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

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

A -> B -> C -> D

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


person Nik Reiman    schedule 13.10.2010    source источник


Ответы (5)


Вы можете запустить 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 docs или в этот вопрос, на который есть хорошие ответы.

person Rudi    schedule 13.10.2010
comment
Изначально я читал это как «перебазировать D на A, сжать D на A, а затем переустановить B на DA». Из ответа неясно, что это можно сделать, переупорядочив строки в текстовом редакторе. - person Victor Sergienko; 31.05.2017
comment
Если ваша ветка локальная, вы получите There is no tracking information for the current branch ошибку при перебазировании. В этом случае вам нужно указать количество коммитов, с которыми вы хотите работать, например: git rebase -i HEAD~4. См. этот ответ. - person johndodo; 16.05.2018
comment
Я использую интерактивный режим (git rebase -i) в течение многих лет, я только что понял, что его можно переупорядочить. Спасибо ???????? - person CalvinChe; 02.07.2019
comment
Об этом следует сказать жирным шрифтом всем, кто не знаком с git. Никто никогда не говорил мне, что переупорядочивание в rebase творит чудеса. - person hardeep; 24.03.2021

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

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 уничтожит все локальные изменения!).

person Nate    schedule 21.06.2013

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

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

  1. Репозиторий> Интерактивная база ...
  2. Перетащите D (более новая фиксация), чтобы она находилась прямо над A (более старая фиксация)
  3. Убедитесь, что фиксация D выделена
  4. Нажмите Squash with previous
person Adam Johns    schedule 15.04.2016

Интерактивная перебазировка работает хорошо, пока у вас не будет большой функциональной ветки с 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 слияниями от мастера + исправление конфликтов. И после этого получил четкий пиар с одной фиксацией.

P.S. вот скрипт для выполнения этих действий в автоматическом режиме .

person Oleksiy Guzenko    schedule 10.10.2018
comment
Первое решение в этой статье действительно хорошее, спасибо за ссылку - person Hoody; 02.04.2019

$ git checkout master

$ git журнал --oneline

D
C
B
A

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

$ git журнал --oneline

D
A
person Cotton    schedule 13.10.2010
comment
Я думаю, вы имеете в виду --oneline? Похоже, вы сбросили C и B, что не соответствует задумке OP. - person bstpierre; 13.10.2010
comment
У меня не получилось. Он переместил мою HEAD и master в A, но не объединил D в A (git show A), а D, C и B были потеряны в моем журнале ссылок. Пришлось git rebase D вернуться. - person Nate; 21.06.2013