Как раздавить два непоследовательных коммита?

183

Я немного новичок во всей функции ребазинга в git. Допустим, я сделал следующие коммиты:

A -> B -> C -> D

После этого я понимаю, что Dсодержит исправление, которое зависит от добавленного нового кода A, и что эти коммиты принадлежат друг другу. Как мне раздавить Aи Dвместе и оставить Bи в Cодиночестве?

Ник Рейман
источник

Ответы:

258

Вы можете запустить git rebase --interactiveи изменить порядок D до B и раздавить D в A.

Git откроет редактор, и вы увидите такой файл, например: git rebase --interactive HEAD~4

pick aaaaaaa Commit A
pick bbbbbbb Commit B
pick ccccccc Commit C
pick ddddddd Commit D

# Rebase aaaaaaa..ddddddd onto 1234567 (4 command(s))
#
# 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
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Теперь вы меняете файл так, как он выглядит:

pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C

И теперь git объединит изменения A и D в один коммит, а затем поместит B и C. Если вы не хотите сохранять сообщение фиксации D, вместо этого squashвы должны использовать fixupключевое слово. Для получения дополнительной информации fixupвы можете обратиться к git rebaseдокументации или проверить этот вопрос, который имеет несколько хороших ответов.

Rudi
источник
5
Первоначально я читал это как «перебазировать D на A, раздавить D в A, затем перебазировать B на DA». Из ответа не ясно, что это можно сделать, переупорядочив строки в текстовом редакторе.
Виктор Сергиенко
3
Если ваша ветка локальная, вы получите There is no tracking information for the current branchошибку при перебазировании. В этом случае вам нужно указать количество коммитов, с которыми вы хотите работать, например:git rebase -i HEAD~4 . Смотрите этот ответ .
Johndodo
1
Я использую интерактивный режим (git rebase -i) в течение многих лет, я просто понял, что это может быть изменить . Спасибо 🤟🏻
CalvinChe
43

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

git log --oneline -4

D commit_message_for_D
C commit_message_for_C
B commit_message_for_B
A commit_message_for_A

git rebase --interactive

pick D commit_message_for_D
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A

Тип i(Переведите VIM в режим вставки)

Измените список так, чтобы он выглядел следующим образом (вам не нужно удалять или включать сообщение фиксации). Не опечатка squash! :

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

Введите Escзатем ZZ(Сохранить и выйти из VIM)

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

commit_message_for_D

# This is the 2nd commit message:

commit_message_for_A

Тип i

Измените текст так, как вы хотите, чтобы выглядело новое сообщение о коммите. Я рекомендую это описание изменений в коммите Aи D:

new_commit_message_for_A_and_D

Введите EscтогдаZZ

git log --oneline -4

E new_commit_message_for_A_and_D
C commit_message_for_C
B commit_message_for_B

git show E

(You should see a diff showing a combination of changes from A and D)

Вы создали новый коммит E. Коммиты Aи Dбольше не в вашей истории, но не ушли. Вы все еще можете восстановить их в этот момент и на некоторое время git rebase --hard D(git rebase --hard уничтожит все локальные изменения! ).

Nate
источник
3

Для тех, кто использует SourceTree :

Убедитесь, что вы еще не выдвинули коммиты.

  1. Репозиторий> Интерактивная перебазировка ...
  2. Перетащите D (новый коммит), чтобы он был прямо над A (более старый коммит)
  3. Убедитесь, что коммит D выделен
  4. щелчок Squash with previous
Адам Джонс
источник
1

Интерактивная перебазировка работает хорошо, пока у вас не будет большой ветки функций с 20-30 коммитами и / или парой слияний от мастера или / и исправления конфликтов, пока вы коммитили в своей ветке. Даже при нахождении моих фиксаций через историю и замену pickс squashне работали здесь. Поэтому я искал другой путь и нашел эту статью . Я сделал свои изменения, чтобы работать над этим на отдельной ветке:

git checkout master
git fetch
git pull
git merge branch-name
git reset origin/master
git branch -D branch-name
git checkout -b branch-name
git add --all
#Do some commit
git push -f --set-upstream origin branch-name

До этого я получил свой запрос на получение с ~ 30 коммитами с 2-3 слияниями от master + исправление конфликтов. И после этого я получил четкий пиар с одним коммитом.

PS вот скрипт bash для этого в автомоде.

Алексей Гузенко
источник
Первое решение в этой статье действительно хорошее, спасибо за ссылку
Hoody
-1

$ git checkout master

$ git log --oneline

D
C
B
A

$ git rebase --onto HEAD ^^^ HEAD ^

$ git log --oneline

D
A
Хлопок
источник
Я думаю ты имеешь ввиду --oneline? И похоже, что вы бросили, Cа Bэто не то, что намеревался ОП.
bstpierre
Не работал для меня Он переместил мою ГЛАВУ и мастера вниз к A, но не слил D в A ( git show A), и D, C и B были потеряны в моем ref-log. Пришлось git rebase Dвернуться.
конец