У меня есть репо. Это немного хаотично...
Я могу оставить все как есть и с этого момента просто продолжать использовать правильный поток, но...
Могу ли я хотя бы исправить некоторые сообщения о коммитах и сохранить старые ветки для истории?)
Также будет полезно сохранить старые даты фиксации.
Вам не нужно перемещать коммиты между ветками или создавать новые ветки, чтобы все выглядело так, как будто репо было выполнено в правильном порядке с самого начала, но я не буду вас останавливать, если хотите.
И как правильно закинуть результат в 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 (я обновил git до версии 2.44) переместил метки hotfix и feat на новые коммиты, но dev по-прежнему является домом для всех старых коммитов. Мне не нужно хранить старые нефиксированные коммиты.
dev не содержится в main, поэтому перебазирование main никак не повлияет на dev. У вас есть две разошедшиеся ветви. Если вы хотите сохранить «структуру» вашего графика такой, какая она есть, но обновлять только сообщения о фиксации, git filter-branch или git filter-repo являются более подходящими инструментами.
Вы говорите «перемещать коммиты между ветками» — у коммитов нет связанной ветки. Вы можете найти один коммит во многих ветках (или нет, в зависимости от вашей ветки, методов слияния и истории). Коммиты различны и неизменяемы, а ветки — это просто способ ссылки на конкретный коммит и его отдельное дерево истории. Если вы удалите ветку, она не удалит никакие коммиты, на которые ссылается другая ветка. Перечитайте документацию по слиянию и перебазированию, и я рекомендую не вносить изменения непосредственно в основной файл — всегда разветвляйтесь, редактируйте, объединяйте.





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) настолько плохой инструмент, что существует предложение спланировать и задокументировать его возможное удаление.
git filter-repo сделал это для образца репозитория. Спасибо. Но у меня возникли проблемы с обратным вызовом, поэтому я переключился на git filter-repo --replace-message replace_msg.txt и перечислил изменения в файле.
Я все еще отчетливо вижу ветви (пурпурные и зеленые линии). Вы спрашиваете, как обновить ссылки на ветки?
--update-refsделает это. Было бы полезно, если бы вы могли показать «ожидаемый результат»