Изменение порядка коммитов

108

В настоящее время я работаю над веткой и хочу, чтобы некоторые коммиты слились с другими ветвями:

    a-b-c-d-e-f-g (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)
   \
    x-x-x-x-x (branchB)

(Буквы обозначают коммиты, а «x» - нерелевантные коммиты.)

Однако я заметил, что было бы неплохо объединить несколько коммитов. Я хочу «объединить» коммиты a, d, e и g в один патч и передать его мастеру. Коммиты b и f должны идти как одна фиксация в BranchB. Есть ли хороший мерзкий способ добиться этого?

Питер
источник
5
В двух уже приведенных ответах упоминается правильная команда, но я думаю, что с такой простой задачей, как эта, стоит иметь настоящие инструкции здесь, в StackOverflow, поэтому я опубликовал некоторые. (Я удивлен, что не могу найти хороший предыдущий вопрос для ссылки.)
Cascabel

Ответы:

128

Команда, которую вы ищете, - это git rebase, в частности, -i/--interactiveопция.

Я собираюсь предположить, что вы хотите оставить фиксацию c в ветке A, и что вы действительно имеете в виду, что хотите переместить другие коммиты в другие ветки, а не слияние, поскольку слияние выполняется просто. Начнем с манипулирования веткой A.

git rebase -i <SHA1 of commit a>^ branchA

Это ^означает предыдущую фиксацию, поэтому эта команда говорит, что нужно перебазировать ветку A, используя фиксацию перед «a» в качестве основы. Git представит вам список коммитов в этом диапазоне. Измените их порядок и скажите git раздавить подходящие:

pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f

Теперь история должна выглядеть так:

    c - [a+d+e+g] - [b+f] (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)

Теперь давайте возьмем только что сжатый коммит b + f для branchB.

git checkout branchB
git cherry-pick branchA  # cherry-pick one commit, the tip of branchA

И то же самое для a + d + e + g для master:

git checkout master
git cherry-pick branchA^

Наконец, обновите branchA, чтобы он указывал на c:

git branch -f branchA branchA^^

Теперь у нас должно быть:

    c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
   /
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
   \
    x-x-x-x-x-[b+f] (branchB)

Обратите внимание, что если у вас было несколько коммитов, которые вы хотели перемещать между ветвями, вы можете снова использовать rebase (не интерактивно):

# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB

Наконец, отказ от ответственности: вполне возможно изменить порядок коммитов таким образом, чтобы некоторые из них перестали применяться чисто. Это могло произойти из-за того, что вы выбрали неверный порядок (установка патча перед фиксацией, представляющая исправленную функцию); в этом случае вы захотите отменить rebase ( git rebase --abort). В противном случае вам придется разумно исправить конфликты (как и в случае конфликтов слияния), добавить исправления, а затем запустить, git rebase --continueчтобы двигаться дальше. Эти инструкции также содержатся в сообщении об ошибке, которое печатается при возникновении конфликта.

Каскабель
источник
диаграмма после git branch -f branchA branchA^^неправильная? А именно, не следует ли branchA указывать на c в этой точке, чтобы первая строка диаграммы была, c (branchA)а не c - [a+d+e+g] - [b+f] (branchA)?
ntc2 05
@enoksrd: Да, это ошибка, но в вашем редактировании были ошибки. Я исправлю это, когда у меня будет возможность.
Cascabel
Вместо того, чтобы делать, git branch -f branchA branchA^^почему вы не можете просто сделать в git reset --hard <sha1 of c>ветке A?
Михаил Васильев
20
git rebase -i HEAD~3

Где 3количество коммитов, которые нужно переупорядочить ( источник ).

Это откроет vi , в котором перечислены коммиты от самых старых (вверху) до самых новых (внизу).
Теперь измените порядок строк, сохраните и выйдите из редактора.

ddpпереместит текущую строку вниз
ddkP переместит текущую строку вверх ( источник )

Нелу
источник
9

git rebase --interactive это та команда, которую вы хотите.

Пример:

Текущий статус таков:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git status
On branch master
nothing to commit, working tree clean
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
a6e3c6a (HEAD -> master) Fourth commit
9a24b81 Third commit
7bdfb68 Second commit
186d1e0 First commit

Я хочу изменить порядок 9a24b81коммитов (Третья фиксация) и 7bdfb68(Вторая фиксация). Для этого я сначала нахожу коммит перед первым коммитом, который мы хотим изменить . Это фиксация 186d1e0(первая фиксация).

В git rebase --interactive COMMIT-BEFORE-FIRST-COMMIT-WE-WANT-TO-CHANGEэтом случае команда для выполнения :

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git rebase --interactive 186d1e0

В редакторе по умолчанию откроется файл со следующим содержимым:

pick 7bdfb68 Second commit
pick 9a24b81 Third commit
pick a6e3c6a Fourth commit

# Rebase 186d1e0..a6e3c6a onto 186d1e0 (3 commands)
#
# 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
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#

Обратите внимание, что порядок коммитов в файле противоположен тому, который используется для журнала git. В журнале git самая последняя фиксация находится вверху. В этом файле самая последняя фиксация находится внизу.

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

pick 9a24b81 Third commit
pick 7bdfb68 Second commit
pick a6e3c6a Fourth commit

Команда pickв начале каждой строки означает «использовать (т.е. включить) эту фиксацию», и когда я сохраню временный файл с командами rebase и выйду из редактора, git выполнит команды и обновит репозиторий и рабочий каталог:

$ git rebase --interactive 186d1e0
Successfully rebased and updated refs/heads/master.
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
ba48fc9 (HEAD -> master) Fourth commit
119639c Second commit
9716581 Third commit
186d1e0 First commit

Обратите внимание на переписанную историю коммитов.

Ссылки:

кодовая лента
источник
1
Пожалуйста, добавьте соответствующие детали из вашей ссылки, чтобы дать полный и самостоятельный ответ. ТАК неодобрительно относится к "ответам только по ссылкам". В частности, как можно использовать git rebase --interactiveдля достижения цели OP?
SherylHohman
Второй ссылки сейчас нет.
devsaw
Добавлен контент, чтобы сделать ответ полным и автономным. Убрана вторая ссылка.
codeape
-1

git rebase - это то, что вы хотите. Обратите внимание на опцию --interactive.


источник
5
Пожалуйста, добавьте соответствующие детали из вашей ссылки, чтобы дать полный и самостоятельный ответ. ТАК неодобрительно относится к "ответам только по ссылкам".
SherylHohman