Git перебазирует ад

У меня есть репо. Это немного хаотично...

Я могу оставить все как есть и с этого момента просто продолжать использовать правильный поток, но...

Могу ли я хотя бы исправить некоторые сообщения о коммитах и ​​сохранить старые ветки для истории?)

Также будет полезно сохранить старые даты фиксации.

Вам не нужно перемещать коммиты между ветками или создавать новые ветки, чтобы все выглядело так, как будто репо было выполнено в правильном порядке с самого начала, но я не буду вас останавливать, если хотите.

И как правильно закинуть результат в gitlab?

Вот скрипт для создания примера репозитория (запустите его в пустом каталоге):

#!/bin/bash
git init

# how to rebase it?
# need to fix some bad commit messages
# and prefer to keep old commit dates

# main
git branch -m main

# init commit #0
echo '#0 init commit in main' >> file.txt
git add file.txt
git commit --message = "#0 init: all is fine for now"

# commit #1 (problem here)
sleep 1s 
echo 'commit #1 in main' >> file.txt
git add file.txt
git commit --message = "#1 rebase and add a tag for this message"

# branching
# dev
git branch dev
git checkout dev
# feat
git branch feat
git checkout feat

# commit #2
sleep 1s 
echo 'commit #2 in feat' >> file.txt
git add file.txt
git commit --message = "#2 feat: good commit"

# commit #3
sleep 1s 
echo 'commit #3 in feat' >> file.txt
git add file.txt
git commit --message = "#3 feat: good commit"

# commit #4
sleep 1s 
git checkout main
echo 'commit #4 in main' >> file_2.txt
git add file_2.txt
git commit --message = "#4 fix: small fix in main"

# merge #5
sleep 1s 
git checkout feat
git merge main --no-ff --message = "#5 merge: main into feat"

# commit #6
sleep 1s 
echo 'commit #6 in feat' >> file.txt
git add file.txt
git commit --message = "#6 feat: good commit"

# commit #7
sleep 1s 
echo 'commit #7 in feat' >> file.txt
git add file.txt
git commit --message = "#7 rebase and add a tag for this message"

# commit #8
sleep 1s 
echo 'commit #8 in feat' >> file.txt
git add file.txt
git commit --message = "#8 feat: good commit"

# commit #9
sleep 1s 
git checkout main
echo 'commit #9 in main' >> file_2.txt
git add file_2.txt
git commit --message = "#9 style: small style fix in main"

# commit #10
sleep 1s 
git checkout feat
echo 'commit #10 in feat' >> file.txt
git add file.txt
git commit --message = "#10 feat: good commit"

# merge #11
sleep 1s 
git checkout dev
git merge main --no-ff --message = "#11 merge: main into dev"

# merge #12
sleep 1s 
git merge feat --no-ff --message = "#12 merge: feat into dev"

# commit #13
sleep 1s 
echo 'commit #13 in dev' >> file_2.txt
git add file_2.txt
git commit --message = "#13 refactor: good commit"

# commit #14
sleep 1s 
echo 'commit #14 in dev' >> file_2.txt
git add file_2.txt
git commit --message = "#14 rebase and add a tag for this message"

# commit #15
sleep 1s 
echo 'commit #15 in dev' >> file_2.txt
git add file_2.txt
git commit --message = "#15 refactor: good commit"

# merge #16
sleep 1s 
git checkout main
git merge dev --no-ff --message = "#16 merge: dev into main and call it a release_##"

# branching
# hotfix
git branch hotfix
git checkout hotfix

# commit #17
sleep 1s 
echo 'commit #17 in hotfix' >> file_2.txt
git add file_2.txt
git commit --message = "#17 fix: no more issues"

# merge #18
sleep 1s 
git checkout main
git merge hotfix --no-ff --message = "#18 merge: hotfix into main"

# merge #19
sleep 1s 
git checkout dev
git merge hotfix --no-ff --message = "#19 merge: hotfix into dev"

# end
git checkout main

Я попробовал перебазировать:

git rebase --interactive --keep-base --rebase-merges  <SHA #0>

Я ожидал, что он перестроит все ветки и просто обновит сообщения... Но он сохраняет все старые коммиты и добавляет копии всего поверх ветки main. И похоже, что у этой копии нет ветвей — все находится внутри main.

И можно восстановить дату фиксации после перебазирования с помощью:

git rebase --committer-date-is-author-date <SHA ##>

(один за другим... за ~40 коммитов)

Я все еще отчетливо вижу ветви (пурпурные и зеленые линии). Вы спрашиваете, как обновить ссылки на ветки? --update-refs делает это. Было бы полезно, если бы вы могли показать «ожидаемый результат»

knittl 08.05.2024 13:06

Я хочу, чтобы оно было более или менее таким же, как дерево ввода, но с обновленными сообщениями. --update-refs (я обновил git до версии 2.44) переместил метки hotfix и feat на новые коммиты, но dev по-прежнему является домом для всех старых коммитов. Мне не нужно хранить старые нефиксированные коммиты.

IO_Nox 08.05.2024 13:55
dev не содержится в main, поэтому перебазирование main никак не повлияет на dev. У вас есть две разошедшиеся ветви. Если вы хотите сохранить «структуру» вашего графика такой, какая она есть, но обновлять только сообщения о фиксации, git filter-branch или git filter-repo являются более подходящими инструментами.
knittl 08.05.2024 15:40

Вы говорите «перемещать коммиты между ветками» — у коммитов нет связанной ветки. Вы можете найти один коммит во многих ветках (или нет, в зависимости от вашей ветки, методов слияния и истории). Коммиты различны и неизменяемы, а ветки — это просто способ ссылки на конкретный коммит и его отдельное дерево истории. Если вы удалите ветку, она не удалит никакие коммиты, на которые ссылается другая ветка. Перечитайте документацию по слиянию и перебазированию, и я рекомендую не вносить изменения непосредственно в основной файл — всегда разветвляйтесь, редактируйте, объединяйте.

Paul Hodges 08.05.2024 16:21
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
105
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

git rebase — не тот инструмент, который можно здесь использовать. Rebase предназначен для получения списка коммитов (из одной ссылки) и создания их копий, потенциально меняя их родителя. Если вы хотите обновить только сообщения коммитов, но сохранить структуру своей истории, лучше подойдут git filter-branch или git filter-repo.

Неясно, как именно вы хотите «исправить свои сообщения», но --msg-filter из filter-branch получает исходное сообщение на стандартный ввод и должен вывести новое сообщение на стандартный вывод. Вы можете передать в фильтр любой двоичный файл, сценарий оболочки или функцию, включая sed, awk, файл Python или написанную вами собственную программу.

git filter-branch --msg-filter '
msg = "$(cat)" # get original message
if printf "%s\n" "$msg" | grep -q "#7"; then
  echo "#7 reworded message" # reword message
else
  printf "%s\n" "$msg" # keep message
fi
' -- --all

Если у вас есть теги, которые вы хотите перенести в новую историю и нуждаетесь в обновлении, ставьте --tag-name-filter cat.


С filter-repo вы должны использовать опцию --message-callback '...', которая принимает код Python, который будет телом функции – он должен возвращать новое/обновленное сообщение, например

git filter-repo --message-callback '
  if b"#7" in message:
    return "#7 reworded message" # reword message
  else:
    return message # keep message

Используйте любую сложную логику, которая вам нужна.


Потенциально более простая альтернатива — git replace, которая использует механизм замены, чтобы «скрыть» коммиты и вместо этого показать их замещающие коммиты из замещающих ссылок. Вы можете сделать коммиты замены постоянными с помощью filter-branch.

git replace --edit commit-7 # edit commit 7
git replace --edit ... # edit more commits
git replace --edit ...
git filter-branch -- --all # make replacements permanent

Обратите внимание, что git-filter-branch(1) настолько плохой инструмент, что существует предложение спланировать и задокументировать его возможное удаление.

Guildenstern 10.05.2024 12:36
git filter-repo сделал это для образца репозитория. Спасибо. Но у меня возникли проблемы с обратным вызовом, поэтому я переключился на git filter-repo --replace-message replace_msg.txt и перечислил изменения в файле.
IO_Nox 13.05.2024 10:49

Другие вопросы по теме