Как я могу объединить два коммита в один, если я уже начал ребаз?

1159

Я пытаюсь объединить 2 коммита в 1, поэтому я следовал «коммитам сдвига с ребазой» из git ready .

Я побежал

git rebase --interactive HEAD~2

В результирующем редакторе я изменяю pickна squashи затем сохраняю-выход, но перебазирование завершается неудачно с ошибкой

Не может "сквош" без предыдущего коммита

Теперь, когда дерево работы достигло этого состояния, у меня возникли проблемы с восстановлением.

Команда git rebase --interactive HEAD~2не выполняется с:

Интерактивный ребаз уже начался

и git rebase --continueтерпит неудачу с

Не может "сквош" без предыдущего коммита

Майкл
источник
22
Я ударил это тоже. Моя ошибка была вызвана тем, что git rebase -i перечисляет коммиты в обратном порядке в git log; последний коммит на дне!
lmsurprenant
1
также проверьте: git-scm.com/book/en/Git-Tools-Rewriting-History
nha

Ответы:

1734

Резюме

Сообщение об ошибке

Не может "сквош" без предыдущего коммита

означает, что вы, вероятно, пытались «раздавить вниз». Git всегда вытесняет новый коммит в более старый коммит или «вверх», как это видно в интерактивном списке задач перебазирования, то есть в коммит в предыдущей строке. Изменение команды в самой первой строке списка задач на squashвсегда будет приводить к этой ошибке, так как нет ничего для первого коммита, в который нужно попасть.

Исправление

Сначала вернитесь туда, откуда вы начали

$ git rebase --abort

Скажи, что твоя история

$ git log --pretty=oneline
a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c
b76d157d507e819d7511132bdb5a80dd421d854f b
df239176e1a2ffac927d8b496ea00d5488481db5 a

То есть a был первым коммитом, затем b и, наконец, c. После совершения c мы решаем раздавить b и c вместе:

(Примечание. По умолчанию на большинстве платформ git logпоток выводится в пейджер less. Чтобы выйти из пейджера и вернуться в командную строку, нажмите qклавишу.)

Запуск git rebase --interactive HEAD~2дает вам редактор с

pick b76d157 b
pick a931ac7 c

# Rebase df23917..a931ac7 onto df23917
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

(Обратите внимание, что этот список задач находится в обратном порядке по сравнению с выводом git log.)

Изменение b pickна squashприведет к появившейся ошибке, но если вместо этого вы сдавите c в b (новый коммит в более старый или «сдавить вверх»), изменив список задач на

pick   b76d157 b
squash a931ac7 c

и сохранить выход из вашего редактора, вы получите другой редактор, содержимое которого

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

b

# This is the 2nd commit message:

c

Когда вы сохраняете и выходите, содержимое отредактированного файла становится сообщением коммита нового комбинированного коммита:

$ git log --pretty=oneline
18fd73d3ce748f2a58d1b566c03dd9dafe0b6b4f b and c
df239176e1a2ffac927d8b496ea00d5488481db5 a

Примечание о переписывании истории

Интерактивный ребаз переписывает историю. Попытка передать на удаленный компьютер, который содержит старую историю, потерпит неудачу, потому что это не перемотка вперед.

Если ветка, которую вы перебазировали, является темой или тематической веткой, в которой вы работаете самостоятельно , ничего страшного. Перенос в другое хранилище потребует --forceопции, или, в качестве альтернативы, вы можете, в зависимости от разрешений удаленного хранилища, сначала удалить старую ветку, а затем нажать на перебазированную версию. Примеры тех команд, которые потенциально могут разрушить работу, выходят за рамки этого ответа.

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

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

Грег Бэкон
источник
Если я использую rebase для сжатия коммита, создается новый «комбинированный» коммит, содержащий два набора изменений, но хэш другой. оригинальные коммиты также сохраняются в git?
fabsenet
@fabsenet Да и нет. Оригинальные коммиты по-прежнему доступны, но, вероятно, больше не доступны из любого реферата (в зависимости от особенностей вашей истории). Коммиты без ссылок в конечном итоге удаляются через процесс сборки мусора.
Грег Бэкон
я просто играл вокруг ... я сделал, git log hashoftheoldcommitи это сработало, но мне было любопытно увидеть git log --graphсо всеми этими недостижимыми
коммитами
этот сквош - хороший инструмент для организации коммитов до толчка, но если я нажму один коммит, я не смогу его раздавить? Git говорит: Успешно перебазирован и обновлен отделенный заголовок.
Сержио
Я не получаю редактор во втором экземпляре, git bash, кажется, застрял в каком-то процессе. Что делать?
411

Если есть несколько коммитов, вы можете использовать, git rebase -iчтобы раздавить два коммита в один.

Если вы хотите объединить только два коммита, и они являются «самыми последними двумя», следующие команды могут использоваться для объединения двух коммитов в один:

git reset --soft "HEAD^"
git commit --amend
user3828059
источник
6
Какой недостаток по сравнению с ребазом? Я считаю, что намного проще в использовании.
Guillaume86
17
Вы не можете присоединиться в произвольном порядке - только последние два коммита .
dr0i
50
@ dr0i Вы можете объединять столько коммитов, сколько хотите, если они являются последними X коммитами, а не где-то посередине. Просто запустите git reset --soft HEAD~10, где 10 - это количество коммитов, которые вы хотите объединить.
Фреганте
2
Это может быть использовано, если у вас не установлен удаленный источник, и у вас есть только два коммита.
Atedja
8
Вы также можете сбросить до определенного коммита, если вы не хотите подсчитывать, сколько их из, HEADс помощью git reset --soft 47b5c5...где 47b5c5...SHA1 ID коммита.
августа
112

Rebase: Вам это не нужно:

Более простой способ для наиболее частых сценариев.

В большинстве случаев:

На самом деле, если все, что вам нужно, это просто объединить несколько недавних коммитов в один, но не нужно drop, rewordи другие операции по перебазированию

Вы можете просто сделать:

git reset --soft "HEAD~n"
  • Предполагая , что ~nэто число фиксаций мягко снимите фиксации (т.е. ~1, ~2...)

Затем используйте следующую команду, чтобы изменить сообщение фиксации.

git commit --amend

который в значительной степени совпадает с длинным диапазоном squashи одним pick.

И это работает для n коммитов, а не только для двух коммитов, как указано выше.

pambda
источник
3
Это хорошо, если вы хотите выполнить некоторую дополнительную очистку, кроме простого сжатия коммитов, например удаления 1 коммита в середине или изменения строки кода.
17
1
Предполагая , что ~nэто число фиксаций мягко оон зафиксированной (то есть ~1, ~2, ...)
Ray
1
Что если я хочу объединить не nпоследние коммиты, а nкоммиты в середине? Могу ли я сделать это легко?
chumakoff
1
Тогда git rebase -iэто то, что вам нужно для squashработы. @chumakoff
Памбда
3
Итак, чтобы присоединиться к nпоследним коммитам в одно, первое использование git reset --soft @~m, гдеm = n - 1
Лукаш Раджчел
55

Сначала вы должны проверить, сколько коммитов у вас есть:

git log

Есть два статуса:

Во-первых, есть только два коммита:

Например:

commit A
commit B

(В этом случае вы не можете использовать git rebase), вам нужно выполнить следующее.

$ git reset --soft HEAD^1

$ git commit --amend

Другое состоит в том, что существует более двух коммитов; Вы хотите объединить коммиты C и D.

Например:

commit A
commit B
commit C
commit D

(при этом условии вы можете использовать git rebase)

git rebase -i B

А потом использовать «сквош». Остальное очень просто. Если вы все еще не знаете, пожалуйста, прочитайте http://zerodie.github.io/blog/2012/01/19/git-rebase-i/

Haimei
источник
Сброс --soft и commit --amend - это единственный способ, который работает, если у вас уже есть процесс перебазирования (и вы выбрали «edit» вместо «squash» для этого коммита). +1
Яцек Лах
1
Слияние первого и только двух коммитов в репозитории, точно мой крайний случай :-)
Крис Хуан-Ливер
1
Пожалуйста, добавьте, что это git push -f origin masterможет быть необходимо.
Ришабх Аграри
33

Предполагая, что вы были в своей собственной ветке темы. Если вы хотите объединить последние 2 коммита в один и выглядеть как герой, разветвите коммит непосредственно перед тем, как вы сделали последние два коммита.

git checkout -b temp_branch HEAD^2

Затем сквош фиксирует другую ветку в этой новой ветке:

git merge branch_with_two_commits --squash

Это внесет изменения, но не совершит их. Так что просто передайте их, и все готово.

git commit -m "my message"

Теперь вы можете объединить эту новую ветку темы с вашей основной веткой.

Homan
источник
5
На самом деле это был самый полезный ответ для меня, потому что он не требовал перебазирования вручную, а просто сводил все коммиты целой ветви в один коммит. Очень хорошо.
Роберт
Спасибо за это! Вот как заставить мерзавца делать то, как я представляю коммиты в виде сквоша в моей голове!
Марьян Венема
Удивительный ответ, намного проще, чем альтернативы
По- видимому, этот ответ не подходит для случая , когда aи cтребует , чтобы быть объединены вместе и сохранить , bкак это.
Талха Ашраф
2
Что-то изменилось в последних версиях git? Когда я пытаюсь git checkout -b combine-last-two-commits "HEAD^2"выполнить первую команду ( ) в git версии 2.17, я получаю сообщение об ошибке:fatal: 'HEAD^2' is not a commit and a branch 'combine-last-two-commits' cannot be created from it
mhucka
23

Вы можете отменить ребаз с

git rebase --abort

и когда вы снова запускаете интерактивную команду rebase 'squash; коммит должен быть ниже пика коммита в списке

Лом Берк
источник
16

Я часто использую git reset --mixed, чтобы вернуть базовую версию перед несколькими коммитами, которые вы хотите объединить, затем я делаю новый коммит, так что вы можете сделать свой коммит самым новым, убедившись, что ваша версия HEAD после того, как вы отправили на сервер.

commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

Если я хочу объединить две коммиты в одну, сначала я использую:

git reset --mixed 249cf9392da197573a17c8426c282

«249cf9392da197573a17c8426c282» была третьей версией, также является вашей базовой версией до слияния, после чего я делаю новый коммит:

git add .
git commit -m 'some commit message'

Это все, надежда - это другой путь для всех.

К вашему сведению, из git reset --help:

 --mixed
     Resets the index but not the working tree (i.e., the changed files are
     preserved but not marked for commit) and reports what has not been
     updated. This is the default action.
VinceStyling
источник
Я не читал документы по '--mixed', но я уверен, что другие люди читали пост и задавались вопросом о том же: в чем преимущество использования --mixed? Может улучшить ваш пост, чтобы включить фрагмент справочной страницы.
funroll
@funroll Я не знал - очень много смешал, прежде чем написать этот ответ, согласно моему собственному опыту, смешанная операция превратит указанную версию, которую я передаю в качестве аргумента в качестве версии HEAD репозитория, и после этой версии ничего не может быть потеряно, поэтому мы все еще можем справиться с этими изменениями.
VinceStyling
14

$ git rebase --abort

Запустите этот код в любое время, если вы хотите отменить git rebase

$ git rebase -i HEAD~2

Повторно применить последние два коммита. Приведенная выше команда откроет редактор кода

  • [ Последний коммит будет внизу ]. Измените последний коммит на сквош (ы). Так как сквош соединится с предыдущим коммитом.
  • Затем нажмите клавишу esc и введите: wq, чтобы сохранить и закрыть

После: wq вы будете в активном режиме перебазирования

Примечание : вы получите другой редактор, если нет предупреждений / сообщений об ошибках, если есть ошибка или предупреждение, что другой редактор не будет отображаться, вы можете прервать выполнение, $ git rebase --abortесли увидите ошибку или предупреждение, иначе просто продолжите работу$ git rebase --continue

Вы увидите ваше сообщение 2 коммита. Выберите одно или напишите свое собственное сообщение о коммите, сохраните и выйдите [: wq]

Примечание 2: Вам может потребоваться принудительно отправить ваши изменения в удаленное хранилище, если вы запустите команду rebase

$ git push -f

$ git push -f origin master

Гнанасекар С
источник
1
Примечание 2: git push -f origin/masterэто то, что другие ответы отсутствуют. +1
Ришабх Аграри
2

Поскольку я использую git cherry-pickпочти все, для меня это естественно сделать даже здесь.

Учитывая, что я branchXпроверил, и на кончике есть два коммита, из которых я хочу создать один коммит, комбинирующий их контент, я делаю это:

git checkout HEAD^ // Checkout the privious commit
git cherry-pick --no-commit branchX // Cherry pick the content of the second commit
git commit --amend // Create a new commit with their combined content

Если я хочу обновить branchXтакже (и я полагаю, что это недостаток этого метода), я также должен:

git checkout branchX
git reset --hard <the_new_commit>
Мартин Г
источник
1

Если ваша основная ветка git logвыглядит примерно так:

commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

и вы хотите объединить два верхних коммита, просто выполните следующие простые шаги:

  1. Первый, чтобы быть на безопасной стороне извлечения второй последний коммит в отдельной ветке. Вы можете назвать отрасль как угодно.git checkout 77df2a40e53136c7a2d58fd847372 -b merged-commits
  2. Теперь, только вишневого забрать ваши изменения от последней фиксации в этой новой отрасли , как: git cherry-pick -n -x ac72a4308ba70cc42aace47509a5e. (Разрешите конфликты, если они возникнут)
  3. Итак, теперь ваши изменения в последнем коммите есть в вашем втором последнем коммите. Но вам все еще нужно зафиксировать, поэтому сначала добавьте изменения, которые вы только что выбрали, а затем выполните git commit --amend.

Вот и все. Вы можете отправить эту объединенную версию в ветку "объединенные-коммиты", если хотите.

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

git checkout master
git reset --hard origin/master (CAUTION: This command will remove any local changes to your master branch)
git pull
Усман
источник
0

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

Я предполагаю:

  • Вы используете vi в качестве редактора
  • Ваши коммиты в одну строку каждый

Я проверял с git version 2.14.3 (Apple Git-98).


#!/usr/bin/env expect
spawn git rebase -i HEAD~2

# change the second "pick" to "squash"
# down, delete word, insert 's' (for squash), Escape, save and quit
send "jdwis \033:wq\r"

expect "# This is a"

# skip past first commit message (assumed to be one line), delete rest of file
# down 4, delete remaining lines, save and quit
send "4jdG\r:wq\r"

interact
erwaman
источник
Неясно, что делает ваш скрипт.
Бухт
@buhtz Я добавил еще несколько комментариев. Дайте мне знать, если вам все еще непонятно, и если да, то в какой части.
erwaman
До сих пор неясно, что делает ваш scrpit. И expectне описан.
Бухт
@buhtz, какая часть неясна? Я предоставил ссылку на страницу с дополнительной документацией для expect.
erwaman
Общее описание сценария отсутствует. Неясно, что он делает. Не одна часть неясно. Намерение самого сценария неясно.
Бахц