Как удалить выбранные записи журнала коммитов из репозитория Git, сохранив их изменения?

241

Я хотел бы удалить выбранные записи журнала фиксации из линейного дерева фиксации, чтобы записи не отображались в журнале фиксации.

Мое дерево коммитов выглядит примерно так:

R--A--B--C--D--E--HEAD

Я хотел бы удалить записи B и C, чтобы они не отображались в журнале фиксации, но изменения от A до D следует сохранить. Может быть, введя один коммит, чтобы B и C стали BC, а дерево выглядит так.

R--A--BC--D--E--HEAD

Или, в идеале, после A следует D напрямую. D 'представляет изменения от A к B, B к C и C к D.

R--A--D'--E--HEAD

Это возможно? если да, то как?

Это довольно новый проект, поэтому на данный момент не имеет ответвлений, поэтому также не объединяется.

xk0der
источник
@ xk0der: здесь «правильный» термин. rebaseможет удалить старые / создать новые коммиты. Я не знаю, что означает «фиксировать записи в журнале».
Jfs
@JFSebastian Я не вижу проблемы с «фиксацией журнала» - логом всех коммитов. И я хотел удалить несколько записей из журнала - при сохранении реальных изменений (коммитов).
xk0der
@ xk0der: коммиты git являются адресно-контентными, т. е. если вы изменяете что-либо в коммите, например, в его лог-сообщении; Вы создаете новый коммит. Вы можете прочитать коммит мерзавца без мерзавца и убедиться в этом сами .
JFS
@JFSebastian - Спасибо за ссылки - я знаю это - Но действительно ли эта техническая специфика меняет проблему, с которой я столкнулся, и как я ее выдвинул? Я думаю, нет. В конце: я хотел удалить «сообщения журнала фиксации» - не удаляя «изменения фиксации» - пожалуйста, перечитайте мой вопрос - особенно второй абзац. Чтобы добавить больше git logпоказывает "журнал регистрации" git-scm.com/docs/git-log . И я хотел избавиться от двух записей из этого журнала - не изменений.
xk0der

Ответы:

273

git-rebase (1) делает именно это.

$ git rebase -i HEAD~5

git awsome-ness [git rebase --interactive] содержит пример.

  1. Не используйте git-rebaseдля публичных (удаленных) коммитов.
  2. Убедитесь, что ваш рабочий каталог чист ( commitили stashваши текущие изменения).
  3. Запустите приведенную выше команду. Он запускает ваш $EDITOR.
  4. Заменить pickдо Cи Dна squash. Он объединит C и D в B. Если вы хотите удалить коммит, просто удалите его строку.

Если вы потерялись, введите:

$ git rebase --abort  
JFS
источник
Спасибо за быстрый ответ. Так я могу оформить заказ А и сделать ребаз, что-то вроде git rebase -i D [A]?
xk0der
3
Как мы можем сделать это на удаленных репо?
Eray
6
@Eray: только push -fтвои изменения. Не делай этого, если ты не работаешь один.
Jfs
2
@ ripper234: я исправил ссылки на git-rebaseруководство пользователя и машину обратного хода для поста в блоге.
JFS
75
# detach head and move to D commit
git checkout <SHA1-for-D>

# move HEAD to A, but leave the index and working tree as for D
git reset --soft <SHA1-for-A>

# Redo the D commit re-using the commit message, but now on top of A
git commit -C <SHA1-for-D>

# Re-apply everything from the old D onwards onto this new place 
git rebase --onto HEAD <SHA1-for-D> master
CB Bailey
источник
Это тоже работает и помогло мне понять, что такое программный сброс. Конечно, «верный» ответ тоже правильный и короче, но спасибо и за этот ответ.
cgp
41

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

git rebase --onto commit-id^ commit-id

Обратите внимание, что это на самом деле удаляет изменения, внесенные коммитом.

Rado
источник
7
Дополнительный заголовок в этой команде приведет к тому, что перебазирование закончится «отсоединенным заголовком», что нежелательно. Это должно быть опущено.
Морозный
3
Это отменяет изменения, введенные моим коммит-идентификатором, ОП хочет сохранить изменения, просто раздавив коммиты.
CB Bailey
1
-1 потому что он не выполняет то, что просил ОП (скорее, он уничтожает то, что он явно хотел сохранить).
Эмиль Стирке
1
Хотя он не выполняет то, о чем просил ОП, это было именно то, что мне было нужно, поэтому +1 за полезный ответ.
Эдвинс
20

Чтобы расширить ответ Дж. Ф. Себастьяна:

Вы можете использовать git-rebase, чтобы легко вносить всевозможные изменения в историю коммитов.

После запуска git rebase --interactive в вашем $ EDITOR вы получите следующее:

pick 366eca1 This has a huge file
pick d975b30 delete foo
pick 121802a delete bar
# Rebase 57d0b28..121802a onto 57d0b28
#
# 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

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

Я думаю, что выбор просто означает, что вы хотите оставить этот коммит один.

(Пример отсюда )

оборота идбрии
источник
14

Вы можете неинтерактивно удалять B и C в вашем примере с помощью:

git rebase --onto HEAD~5 HEAD~3 HEAD

или символически,

git rebase --onto A C HEAD

Обратите внимание, что изменения в B и C не будут в D; они исчезнут .

Глава
источник
Смотрите здесь для получения дополнительной информации: sethrobertson.github.io/GitFixUm/fixup.html#remove_deep
Макс
3

Еще один способ,

git rebase -i ad0389efc1a79b1f9c4dd6061dca6edc1d5bb78a (C's hash)
and
git push origin master  -f

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

resultsway
источник
2

Я считаю, что этот процесс намного безопаснее и проще для понимания, создав еще одну ветку из SHA1 в A и выбрав нужные изменения, чтобы убедиться, что я удовлетворен тем, как выглядит эта новая ветка. После этого легко удалить старую ветку и переименовать новую.

git checkout <SHA1 of A>
git log #verify looks good
git checkout -b rework
git cherry-pick <SHA1 of D>
....
git log #verify looks good
git branch -D <oldbranch>
git branch -m rework <oldbranch>
Эрик Вудрафф
источник
если вы сделаете это, вы также потеряете коммит E, не так ли? Как я понял, вы удаляете master и переименовываете rework в master (учитывая, что поток ABCDE - это основная ветвь).
Ренан Бандейра
1

Просто собрал ответы всех людей: (я новичок в git plz, использую его только для справки)

git rebase для удаления любых коммитов

мерзавец

-first check from which commit you want to rebase

git rebase -i HEAD ~ 1

-Here i want to rebase on the second last commit- commit count starts from '1')
-this will open the command line editor (called vim editor i guess)

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

pick 0c2236d Добавлена ​​новая строка.

Перебазировать 2a1cd65..0c2236d на 2a1cd65 (1 команда)

#

Команды:

p, pick = использовать коммит

r, reword = использовать коммит, но редактировать коммит

e, edit = использовать фиксацию, но остановиться для внесения изменений

s, squash = использовать коммит, но слиться с предыдущим коммитом

f, fixup = like "squash", но отбросить сообщение журнала этого коммита

x, exec = команда запуска (остаток строки) с использованием оболочки

d, drop = удалить коммит

#

Эти строки можно переупорядочить; они выполняются сверху вниз.

#

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

#

Однако, если вы удалите все, ребаз будет прерван.

#

Обратите внимание, что пустые коммиты закомментированы ~ ~

~
~
~
~
~
~
~
~
~

Здесь измените первую строку в соответствии с вашими потребностями (используя команды, перечисленные выше, например, «drop» для удаления коммита и т. Д.) После завершения редактирования нажмите «: x», чтобы сохранить и выйти из редактора (это только для редактора vim)

А потом

мерзавец

Если это показывает проблему, то вам нужно принудительно отправить изменения на удаленное устройство (ЕГО ОЧЕНЬ КРИТИЧЕСКОЕ: не вызывайте принудительно, если вы работаете в команде)

git push -f origin

Шринат Копаре
источник
-1

Вы можете использовать git cherry-pick для этого. 'cherry-pick' применит коммит к вашей ветке сейчас.

тогда делай

git rebase --hard <SHA1 of A>

затем примените коммиты D и E.

git cherry-pick <SHA1 of D>
git cherry-pick <SHA1 of E>

Это пропустит коммиты B и C. Сказав, что может быть невозможно применить коммит D к ветви без B, так что YMMV.

Рори
источник
2
ОП хочет объединить коммиты B, C, D, а не удалять их изменения.
JFS
3
Я думаю, что вы имели в виду reset --hard, а не rebase --hard(что не существует)
Маурисио Шеффер