Git squash совершает коммит в середине ветки

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

У меня есть :

A -- B -- C -- D -- E -- F -- G
|                             |
master                        dev
origin/master

Я хочу раздавить это в

A -- H -- E -- F -- G
|                   |
master              dev
origin/master

Где H эквивалентно B -- C -- D. И я хочу иметь возможность указать сообщение фиксации H. A — это последний отправленный коммит, поэтому все последующие коммиты можно переписать, не нарушая работу сервера. Идея состоит в том, чтобы очистить историю, прежде чем я перемотаю вперед master.

Как я могу это сделать ?

PS: обратите внимание, что в моем случае у меня на самом деле гораздо больше, чем 3 коммита для сквоша в середине, но если я могу сделать это с 3, я должен сделать это с большим количеством.

PPS: Кроме того, если возможно, я бы предпочел решение, при котором E, F и G остаются нетронутыми (в основном в отношении даты фиксации).


person deadbeef    schedule 18.08.2016    source источник
comment
Возможный дубликат объединения нескольких коммитов перед отправкой в ​​Git   -  person Bryce Drew    schedule 18.08.2016


Ответы (1)


Вы можете выполнить интерактивную перебазировку и вручную выбрать коммиты, которые вы хотите раздавить. Это перепишет историю вашей ветки dev, но, поскольку вы не отправляли эти коммиты, не должно быть никаких негативных последствий, кроме того, что может произойти на вашем собственном компьютере.

Начните со следующего:

git checkout dev
git rebase -i HEAD~6

Это должно открыть окно, показывающее вам следующий список из 7 коммитов, возвращаясь на 6 шагов назад от HEAD вашей ветки dev:

pick 07c5abd message for commit A
pick dl398cn message for commit B
pick 93nmcdu message for commit C
pick lst28e4 message for commit D
pick 398nmol message for commit E
pick 9kml38d message for commit F
pick 02jmdmp message for commit G

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

pick 07c5abd message for commit A
pick dl398cn new commit message for "H" goes here
squash 93nmcdu message for commit C
squash lst28e4 message for commit D
pick 398nmol message for commit E
pick 9kml38d message for commit F
pick 02jmdmp message for commit G

Обратите внимание на то, что происходит выше. Набрав squash, вы указываете Git объединить этот коммит с коммитом над ним, то есть коммитом, который был непосредственно перед им. Итак, это говорит о том, чтобы сжать коммит D назад в коммит C, а затем сжать C в B, оставив вам только один коммит для коммитов B, C и D. Остальные коммиты остаются как есть.

Сохраните файл (: wq в Git Bash в Windows), и перебазирование будет завершено. Имейте в виду, что вы можете получить конфликты слияния из-за этого, как и следовало ожидать, но в их разрешении нет ничего особенного, и вы можете продолжать, как и при обычном перебазировании или слиянии.

Если вы проверите ветку после перебазирования, вы заметите, что коммиты E, F и G теперь имеют новые хэши, даты и т. д. Это потому, что эти коммиты фактически были заменены новыми коммитами. Причина этого в том, что вы переписали историю, и поэтому коммиты в целом уже не могут быть такими, как были раньше.

person Tim Biegeleisen    schedule 18.08.2016
comment
Сработало как шарм, спасибо! Я не ожидал, что это будет так просто. Однако есть ли способ сделать то же самое, не переписывая последние 3 коммита? Я заметил, что git создал 3 новых фиксации (с датой фиксации, являющейся текущей датой). - person deadbeef; 18.08.2016
comment
@deadbeef Если вы немного обдумаете свой вопрос, вы поймете, почему ответ должен быть отрицательным. Поскольку вы переписали то, что произошло до коммитов E, F и G, сами эти три коммита переписаны и фактически являются новыми коммитами. - person Tim Biegeleisen; 18.08.2016
comment
Я некоторое время размышлял и понял, что это, вероятно, невозможно, да :-). Однако я нашел git rebase --committer-date-is-author-date, который в основном делает то, что я хочу (сохраняет исходную дату фиксации). - person deadbeef; 18.08.2016
comment
Кроме того, как я уже упоминал, у меня было гораздо больше, чем 3 коммита для сквоша, поэтому я использовал git rebase -i <commit_sha> вместо того, что вы упомянули, что было проще, чем подсчет всех этих коммитов. - person deadbeef; 18.08.2016
comment
@deadbeef Я не знал о опции сохранения даты фиксации, но хэши коммитов изменились. Это добросовестные новые коммиты. Хорошо, что вы не делаете этого ни с какими опубликованными коммитами. - person Tim Biegeleisen; 18.08.2016
comment
Нашел в гугле. Я попробовал это при первой перебазировке, но это не сработало. Однако я запустил его после, и он изменил все коммиты после commit_sha. Так что в моем случае выполнение git rebase --committer-date-is-author-date sha_of_H сделало то, что я хотел. - person deadbeef; 18.08.2016