Обрезка Git Commits/Squashing Git История

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

Затем каждые несколько минут/часов/дней я делаю серьезный коммит с настоящим комментарием вроде "Исправлена ​​ошибка 22.55, в 3 раза". Как я могу отделить эти две концепции? Я хотел бы удалить все мои частые коммиты и просто оставить серьезные.

Ответ 1

Использование Squash вместо

Недавно я работал в другой ветке и использовал 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? Меньше путают и немного меньше подвержены ошибкам.

Ответ 2

Отредактированный ответ теперь (во второй половине этой записи) с новым исправлением Git1.7! действие и опция --autosquash для быстрого изменения порядка коммитов и редактирования сообщений.


Во-первых, классический процесс сжатия, как это было сделано до Git1.7.
(Git1.7 имеет тот же процесс, только ускоренный за счет возможности автоматического переупорядочения фиксации в отличие от ручного переупорядочения и более чистых сообщений о сжатии)

Я хотел бы иметь возможность удалить все мои частые проверки и просто оставить серьезные.

Это называется фиксация коммитов.
У вас есть хороший пример "очистки comit" в этой статье о Git:
(Примечание: интерактивная функция rebase появилась с сентября 2007 года и позволяет раздавливать, разбивать, удалять или переупорядочивать коммиты: см. Также страницу GitPro)

Предупреждение: делайте это только с коммитами, которые не были переданы во внешний репозиторий. Если другие основывают работу на коммитах, которые вы собираетесь удалить, может возникнуть множество конфликтов. Просто не переписывайте свою историю, если она была передана другим.

alt text

Последние 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 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.

И если мы посмотрим на историю снова...

alt text


Примечание: для целей "фиксации коммита" Git1.7 (февраль 2010 г.) представил 2 новых элемента (как упомянуто Дастином в комментарии):

  • " git rebase -i " изучил новое действие " fixup ", которое fixup изменение, но не влияет на существующее сообщение журнала.
  • " git rebase -i " также изучил --autosquash которая полезна вместе с новым действием "fixup".

Оба (действие --autosquash опция --autosquash) проиллюстрированы в этой записи блога Thechnosorcery Networks. Эти функции готовятся с июня прошлого года и обсуждаются в декабре прошлого года.

Действие или директива fixup предназначены для уничтожения коммита, который вы бы вручную переупорядочили в списке редактирования коммита rebase --interactive, в то же время игнорируя второе сообщение коммита, что rebase --interactive редактирования сообщения (вы можете просто сохранить его).: сжатый коммит будет иметь только первое сообщение коммита)
Полученное сообщение о коммите будет только первым.

  # s, squash = use commit, but meld into previous commit
  # f, fixup = like "squash", but discard this commit log message

Опция --autosquash предназначена для автоматического выполнения процесса изменения порядка коммитов:

Если вы знаете, с каким коммитом вы хотите что-то раздавить, вы можете сделать это с сообщением " squash! $other_commit_subject ". Затем, если вы запустите @git rebase --interactive --autosquash [email protected], строка будет автоматически установлена как сквош и помещена под коммит с темой $ other_commit_subject.

(На самом деле, squash! Может использовать только начало другого сообщения коммита)

$ vim Foo.txt
$ git commit -am "Change all the 'Bar to 'Foo's"
[topic 8374d8e] Change all the 'Bar to 'Foo's
 1 files changed, 2 insertions(+), 2 deletions(-)
$ vim Bar.txt
$ git commit -am "Change all the 'Foo to 'Bar's"
[topic 2d12ce8] Change all the 'Foo 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 to 'Foo's
squash 259a7e6 squash! Change all the 'Bar's
pick 2d12ce8 Change all the 'Foo to 'Bar's

Редакция сообщения будет:

# This is a combination of 2 commits.
# The first commit message is:

Change all the 'Bar to 'Foo's

# This is the 2nd commit message:

squash! Change all the 'Bar's

Это означает, что по умолчанию вы сохраняете операцию сжатия в сообщении коммита.
Но с исправлением! Директива, вы можете оставить это сжатие "невидимым" в сообщении коммита, но при этом воспользоваться автоматическим переупорядочением фиксации с --autosquash (и тот факт, что ваше второе сообщение коммита основано на первом коммите, с которым вы хотите быть сжато).

pick 8374d8e Change all the 'Bar to 'Foo's
fixup cfc6e54 fixup! Change all the 'Bar's
pick 2d12ce8 Change all the 'Foo to 'Bar's

Сообщение по умолчанию будет:

# This is a combination of 2 commits.
# The first commit message is:

Change all the 'Bar to 'Foo's

# The 2nd commit message will be skipped:

#    fixup! Change all the 'Bar's

Обратите внимание, что fixup! сообщение коммитов уже закомментировано.
Вы можете просто сохранить сообщение как -i, и ваше исходное сообщение будет сохранено.
Очень удобно для включения изменений, когда вы понимаете, что забыли добавить часть более раннего коммита.

Теперь, если вы хотите исправить или раздавить, основываясь на предыдущем коммите, который вы только что сделали, Джейкоб Хелвиг (автор записи в блоге Technosorcery Networks) рекомендует следующие псевдонимы:

[alias]
    fixup = !sh -c 'git commit -m \"fixup! $(git log -1 --format='\\''%s'\\'' [email protected])\"' -
    squash = !sh -c 'git commit -m \"squash! $(git log -1 --format='\\''%s'\\'' [email protected])\"' -

И для выполнения интерактивной перебазировки, которая всегда выигрывает от автоматического изменения порядка коммитов, предназначенных для раздавливания:

[alias]
    ri = rebase --interactive --autosquash

Обновление для Git 2.18 (Q2 2018): " git rebase -i " иногда оставляется промежуточным " # This is a combination of N commits ", предназначенного для использования человеком в редакторе в конечном результате в определенных угловых случаях, который был фиксированный.

См. Коммит 15ef693, коммит dc4b5bc, коммит e12a7ef, коммит d5bc6f2 (27 апреля 2018 г.) от Йоханнеса dscho (dscho).
(Объединено Джунио К Хамано - gitster - в коммите 4a3bf32, 23 мая 2018 г.)

rebase --skip: очистить сообщение о коммите после неудачного исправления/сквоша

Во время серии команд fixup/squash интерактивное перебазирование создает коммит-сообщение с комментариями. Это будет представлено пользователю в редакторе, если хотя бы одна из этих команд была squash.

В любом случае сообщение о фиксации будет в конечном итоге очищено, удалив все эти промежуточные комментарии, на последнем этапе такой цепочки исправлений/сквоша.

Однако, если последняя команда fixup/squash в такой цепочке завершается неудачно с конфликтами слияния, и если пользователь затем решает пропустить ее (или разрешить ее для чистого рабочего дерева, а затем продолжить перебазирование), текущему коду не удается очистить отправить сообщение

Этот коммит исправляет это поведение.

Исправление несколько сложнее, чем кажется на git rebase --skip взгляд, потому что речь идет не только о том, что мы делаем git rebase --skip исправление или сквош. Также речь идет об удалении пропущенного сообщения коммита/сквоша из накопленного сообщения о коммите. И это также вопрос о том, должны ли мы позволить пользователю редактировать окончательное сообщение коммита или нет ("Был ли сквош в цепочке, который не был пропущен?").

Например, в этом случае мы хотим исправить сообщение коммита, но не открывать его в редакторе:

pick  <- succeeds
fixup   <- succeeds
squash  <- fails, will be skipped

Вот где недавно current-fixups файл -i current-fixups очень удобен. Беглый взгляд, и мы можем определить, был ли не пропущенный сквош. Нам нужно только следить за тем, чтобы они были актуальными в отношении пропущенных команд fixup/squash. В качестве бонуса мы можем даже избежать ненужных фиксаций, например, когда была только одна фиксация, и она не удалась и была пропущена.

Исправить только ту ошибку, из-за которой окончательное сообщение о фиксации не было очищено должным образом, но без исправления остальных, было бы сложнее, чем исправить все за один раз, следовательно, эта фиксация объединяет больше, чем одну проблему.


Git 2.19 (Q3 2018) исправляет ошибку: когда " git rebase -i " git rebase -i два или более коммитов в один, он помечает сообщение журнала для каждого коммита своим номером.
Первый корректно назывался "1-й коммит", но следующим был " commit #1 ", который был однозначным (!).

См. Коммит dd2e36e (15 августа 2018 г.) Филиппа Вуда (phillipwood).
(Объединено Юнио К Хамано - gitster - в коммите 36fd1e8, 20 августа 2018 г.)

rebase -i: исправить нумерацию в сообщении сквоша

Коммит e12a7ef (" rebase -i: обрабатывать" комбинацию <n> GETTEXT_POISONGETTEXT_POISON ", 2018-04-27, Git 2.18) изменил способ, которым отдельные сообщения коммитов помечаются при объединении коммитов при сжатии.
При этом была введена регрессия, когда нумерация сообщений отключена одним. Этот коммит исправляет это и добавляет тест для нумерации.

Ответ 3

Использование Soft Reset Вместо Rebase для сквоша GIT История

Я думаю, что длина ответов VonC говорит томами - буквально - о том, насколько сложным является git rebase. Это мое расширение другого ответа на мой вопрос.

  • У вас есть ветвь ticket-201, которую вы разветвляете от master. Вы хотите притвориться, что все коммиты из ticket-201 никогда не происходили, но что вы сделали всю работу за один выстрел.
  • Мягко Reset к точке перехода с помощью git reset --soft hash, где hash должен быть хешем фиксации, который находится в ticket-201 журнале.
  • Зафиксируйте свои изменения, добавив затем commit. Теперь история ветвей будет иметь только первый фиксатор и новый с новым материалом.

Составление историй из произвольных коммитов в разных ветвях

С помощью сброса вы можете переписать историю по своему усмотрению, хотя ваши изменения потеряют очарование правильной метки времени. Предполагая, что вас это не волнует (времени/дат в ваших файлах будет достаточно, возможно?), Или если вы хотите возиться с коммитами по ходу дела, вы можете выполнить следующие действия:

  • Оформить новую ветку на commit0 (притвориться хешем): git checkout -b new-history commit0
  • Теперь вы можете получить файлы из commit5: git reset --hard commit5
  • Вернитесь к своей индексной точке: git reset --soft commit0
  • Commit, и это будет вторая фиксация в ветке.

Эта идея проста, эффективна и гибка.

Ответ 4

Используйте git rebase -i, чтобы выбрать и сквоить свои коммиты вместе.