Тримминг Git Commits/Squashing Git History
Я проверяю свой код в Git-ветке каждые несколько минут, и комментарии заканчиваются такими вещами, как "Все сломано, начиная снова" и другие нелепости.
Затем каждые несколько минут / часов / дней я делаю серьезный коммит с реальным комментарием вроде: "Исправлена ошибка № 22.55, третий раз". Как я могу разделить эти два понятия? Я хотел бы иметь возможность удалить все мои частые коммиты и просто оставить серьезные.
5 ответов
Вместо этого используйте сквош
Недавно я работал в другой ветке и использую squash
, Другая ветка называется temp, а потом я использую git merge temp --squash
перенести его в реальную ветвь, которая отправляется на сервер.
Рабочий процесс примерно такой, если я работаю в Ticket65252
:
git branch -d temp #remove old temp bbranch
git checkout -b temp
# work work work, committing all the way
git checkout Ticket65252
git merge temp --squash
git commit -m "Some message here"
Преимущества перед использованием rebase
? Путь менее сложный.
Преимущества перед использованием reset --hard
а потом reset --soft
? Менее запутанный и немного менее подвержен ошибкам.
Отредактированный ответ теперь (во второй половине этой записи) с новым исправлением Git1.7! действие и --autosquash
опция для быстрого изменения порядка и редактирования сообщений.
Во-первых, классический процесс сжатия, как это было сделано до Git1.7.
(Git1.7 имеет тот же процесс, только ускоренный за счет возможности автоматического переупорядочения коммитов в отличие от ручного переупорядочения и более чистых сообщений о сжатии)
Я хотел бы иметь возможность удалить все мои частые проверки и просто оставить серьезные.
Это называется фиксация коммитов.
У вас есть хороший пример "очистки comit" в этой статье о Git:
(Примечание: интерактивная функция rebase появилась с сентября 2007 года и позволяет раздавливать, разбивать, удалять или переупорядочивать коммиты: см. Также страницу GitPro)
Предостережение: делайте это только с коммитами, которые не были переданы во внешний репозиторий. Если другие основывают свою работу на коммитах, которые вы собираетесь удалить, может возникнуть множество конфликтов. Только не переписывайте свою историю, если она была передана другим.
http://www.gitready.com/images/squash1.png
Последние 4 коммита были бы намного счастливее, если бы они были свернуты вместе
$ git rebase -i HEAD~4
pick 01d1124 Adding license
pick 6340aaa Moving license into its own file
pick ebfd367 Jekyll has become self-aware.
pick 30e0ccb Changed the tagline in the binary, too.
# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
перебазировать, используя последние четыре коммита, откуда
HEAD
это сHEAD~4
,
Мы просто собираемся раздавить все в один коммит.
Таким образом, изменение первых четырех строк файла на это поможет:
pick 01d1124 Adding license
squash 6340aaa Moving license into its own file
squash ebfd367 Jekyll has become self-aware.
squash 30e0ccb Changed the tagline in the binary, too.
В основном это говорит Git объединить все четыре коммита в первый коммит в списке. Как только это будет сделано и сохранено, появится другой редактор со следующим:
# This is a combination of 4 commits.
# The first commit's message is:
Adding license
# This is the 2nd commit message:
Moving license into its own file
# This is the 3rd commit message:
Jekyll has become self-aware.
# This is the 4th commit message:
Changed the tagline in the binary, too.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths...
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: LICENSE
# modified: README.textile
# modified: Rakefile
# modified: bin/jekyll
#
Поскольку мы объединяем так много коммитов, Git позволяет вам изменять сообщение нового коммита, основываясь на остальных коммитах, вовлеченных в процесс. Отредактируйте сообщение, как считаете нужным, затем сохраните и выйдите.
Как только это будет сделано, ваши коммиты будут успешно раздавлены!
Created commit 0fc4eea: Creating license file, and making jekyll self-aware.
4 files changed, 27 insertions(+), 30 deletions(-)
create mode 100644 LICENSE
Successfully rebased and updated refs/heads/master.
И если мы посмотрим на историю снова...
http://www.gitready.com/images/squash2.png
Примечание: для целей "фиксации сжатия" в Git1.7 (февраль 2010 г.) введено 2 новых элемента (как упомянуто Dustin в комментарии):
- "
git rebase -i
"узнал новое действие"fixup
msgstr "это отменяет изменение, но не влияет на существующее сообщение журнала.- "
git rebase -i
"также узнал--autosquash
опция, которая полезна вместе с новым действием "fixup".
Оба (исправление действий и --autosquash
Опция) показаны в этой записи блога Thechnosorcery Networks. Эти функции готовятся с июня прошлого года и обсуждаются в декабре прошлого года.
fixup
действие или директива предназначены для сжатия коммита, который вы бы вручную переупорядочили в списке редактирования коммита rebase --interactive
игнорируя второе сообщение коммита, что ускорит этап редактирования сообщения (вы можете просто сохранить его: сжатый коммит будет иметь только первое сообщение коммита)
Полученное сообщение о коммите будет только первым.
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
--autosquash
Опция о том, чтобы сделать процесс переупорядочения коммитов автоматически для вас:
Если вы знаете, какой коммит вы хотите раздавить, вы можете передать это сообщением "
squash! $other_commit_subject
". Тогда если вы запустите@git rebase --interactive --autosquash commitish@
строка будет автоматически установлена как сквош и помещена под коммитом с темой $ other_commit_subject.
(На самом деле, squash!
можно использовать только начало другого сообщения коммита)
$ vim Foo.txt
$ git commit -am "Change all the 'Bar's to 'Foo's"
[topic 8374d8e] Change all the 'Bar's to 'Foo's
1 files changed, 2 insertions(+), 2 deletions(-)
$ vim Bar.txt
$ git commit -am "Change all the 'Foo's to 'Bar's"
[topic 2d12ce8] Change all the 'Foo's to 'Bar's
1 files changed, 1 insertions(+), 1 deletions(-)
$ vim Foo.txt
$ git commit -am "squash! Change all the 'Bar's"
[topic 259a7e6] squash! Change all the 'Bar's
1 files changed, 2 insertions(+), 1 deletions(-)
Увидеть? Здесь третий коммит использует только начало первого сообщения коммита.
rebase --interactive --autosquash
переместит сдавленный коммит ниже соответствующего:
pick 8374d8e Change all the 'Bar's to 'Foo's
squash 259a7e6 squash! Change all the 'Bar's
pick 2d12ce8 Change all the 'Foo's to 'Bar's
Редакция сообщения будет:
# This is a combination of 2 commits.
# The first commit's message is:
Change all the 'Bar's to 'Foo's
# This is the 2nd commit message:
squash! Change all the 'Bar's
То есть по умолчанию вы сохраняете операцию сжатия, записанную в сообщении фиксации.
Но с исправлением! Директива, вы можете сохранить эту "сквош" "невидимой" в сообщении коммита, при этом получая выгоду от автоматического переупорядочения коммита с --autosquash
опция (и тот факт, что ваше второе сообщение о коммите основано на первом коммите, с которым вы хотите получить сжатие).
pick 8374d8e Change all the 'Bar's to 'Foo's
fixup cfc6e54 fixup! Change all the 'Bar's
pick 2d12ce8 Change all the 'Foo's to 'Bar's
Сообщение по умолчанию будет:
# This is a combination of 2 commits.
# The first commit's message is:
Change all the 'Bar's to 'Foo's
# The 2nd commit message will be skipped:
# fixup! Change all the 'Bar's
Обратите внимание, что
fixup!
Сообщение коммита уже закомментировано.
Вы можете просто сохранить сообщение как есть, и ваше оригинальное сообщение будет сохранено.
Очень удобно для включения изменений, когда вы понимаете, что забыли добавить часть предыдущего коммита.
Теперь, если вы хотите исправить или раздавить, основываясь на предыдущем коммите, который вы только что сделали, Джейкоб Хелвиг (автор записи в блоге Technosorcery Networks) рекомендует следующие псевдонимы:
[alias]
fixup = !sh -c 'git commit -m \"fixup! $(git log -1 --format='\\''%s'\\'' $@)\"' -
squash = !sh -c 'git commit -m \"squash! $(git log -1 --format='\\''%s'\\'' $@)\"' -
И для выполнения интерактивной перебазировки, которая всегда выигрывает от автоматического изменения порядка коммитов, предназначенных для раздавливания:
[alias]
ri = rebase --interactive --autosquash
Обновление для Git 2.18 (Q2 2018): " git rebase -i
"иногда оставляют промежуточный" # This is a combination of N commits
msgstr "сообщение предназначено для потребления человеком внутри редактора в конечном результате в определенных угловых случаях, что было исправлено.
Смотрите коммит 15ef693, коммит dc4b5bc, коммит e12a7ef, коммит d5bc6f2 (27 апреля 2018 г.) от Johannes Schindelin ( dscho
)
(Объединено Юнио С Хамано - gitster
- в коммите 4a3bf32, 23 мая 2018 года)
rebase --skip
: очистить сообщение о коммите после неудачного исправления / сквошаВо время серии команд fixup/squash интерактивное перебазирование создает сообщение фиксации с комментариями. Это будет представлено пользователю в редакторе, если хотя бы одна из этих команд была
squash
,В любом случае сообщение о фиксации будет в конечном итоге очищено, удалив все эти промежуточные комментарии, на последнем этапе такой цепочки исправлений / сквоша.
Однако, если последняя команда fixup/squash в такой цепочке завершается неудачно с конфликтами слияния, и если пользователь затем решает пропустить ее (или преобразовать в чистое рабочее дерево, а затем продолжить перебазирование), текущий код не сможет очистить отправить сообщение.
Этот коммит исправляет это поведение.
Исправление немного сложнее, чем кажется на первый взгляд, потому что речь идет не только о том,
git rebase --skip
исправление или сквош. Также речь идет об удалении пропущенного сообщения о фиксации / сквоша из накопленного сообщения о коммите. И это также вопрос о том, должны ли мы позволить пользователю редактировать окончательное сообщение о коммите или нет ("Был ли сквош в цепочке, который не был пропущен?").Например, в этом случае мы хотим исправить сообщение коммита, но не открывать его в редакторе:
pick <- succeeds fixup <- succeeds squash <- fails, will be skipped
Это где недавно представленный
current-fixups
Файл очень удобен. Беглый взгляд, и мы можем определить, был ли не пропущенный сквош. Нам нужно только следить за тем, чтобы они были актуальными в отношении пропущенных команд fixup/squash. В качестве бонуса мы можем даже избежать ненужных фиксаций, например, когда была только одна фиксация, и она не удалась и была пропущена.Исправить только ту ошибку, из-за которой окончательное сообщение о фиксации не было очищено должным образом, но без исправления остальных, было бы сложнее, чем исправить все за один раз, следовательно, эта фиксация объединяет больше, чем одну проблему.
Git 2.19 (Q3 2018) исправляет ошибку: git rebase -i
"сказано объединить два или более коммитов в один, он пометил сообщение журнала для каждого коммита своим номером.
Он правильно назвал первый "1-й коммит", но следующий был " commit #1
", который был один на один (!).
Смотрите коммит dd2e36e (15 августа 2018 г.) от Филиппа Вуда ( phillipwood
)
(Объединено Юнио С Хамано - gitster
- в коммите 36fd1e8, 20 августа 2018 г.)
rebase -i
: исправить нумерацию в сообщении сквошаCommit e12a7ef ("
rebase -i
Ручка "комбинация<n>
совершает "сGETTEXT_POISON
", 2018-04-27, Git 2.18) изменил способ, которым отдельные сообщения о фиксации помечаются, когда согласование происходит вместе.
При этом была введена регрессия, когда нумерация сообщений отключена одним. Этот коммит исправляет это и добавляет тест для нумерации.
Использование Soft Reset вместо Rebase для Squash GIT History
Я думаю, что длина ответов VonC говорит о многом - в буквальном смысле - о том, как сложно git rebase
является. Это мое продолжение другого ответа на мой вопрос.
- У вас есть ветка
ticket-201
что вы разветвились отmaster
, Вы хотите сделать вид, что все коммиты изticket-201
никогда не случалось, но вы делали всю работу за один раз. - Мягкий сброс к точке ветвления с помощью
git reset --soft hash
гдеhash
должен быть хеш коммита, который находится вticket-201
лог. - Зафиксируйте ваши изменения, используя add, затем commit. Теперь история ветки будет иметь только первый коммит и новый с новым материалом.
Составление историй из произвольных комитетов в разных ветвях
С помощью сброса вы можете переписать историю так, как считаете нужным, хотя ваши изменения потеряют всю прелесть правильной отметки времени. Предполагая, что вас это не волнует (может быть, времени / даты в ваших файлах будет достаточно), или если вы хотите поиграть с коммитами по ходу дела, вы можете выполнить следующие шаги:
- Оформить заказ на новую ветку на
commit0
(представьте, что это хеш)git checkout -b new-history commit0
- Теперь вы можете получить файлы из
commit5
:git reset --hard commit5
- Вернитесь к своей индексной точке:
git reset --soft commit0
- Фиксация, и это будет второй коммит в ветке.
Эта идея проста, эффективна и гибка.
С git версии 2.xx и новее простой способ — клонировать репозиторий в новое место с помощью --depth NN, использовать число выше, чем последний коммит, который может быть у ваших пользователей, а затем просто зафиксировать изменение и отправить его. назад.
Только что сохраненный репо будет иметь « Начальную фиксацию » с коммитами NN.
На этом этапе вы можете удалить свой старый репо
Используйте git rebase -i, чтобы собрать и сдавить ваши коммиты вместе.