GIT: добавление патча, сделанного после нескольких коммитов - PullRequest
8 голосов
/ 09 октября 2009

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

1) троллинг, проверка журналов CVS и отправка их в git как полные патчи

A--B--C--D

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

git reset --soft <sha1 ID for commit B>

3) Я копирую изменение и добавляю его для фиксации (B) с помощью

git commit --amend

4) к моему удивлению, дерево теперь читает

A--B

с коммитами (C) и (D) только в рабочем дереве. Их данные пропали из журналов, и я не думаю, что смогу их вернуть. Где я неправ? Является ли мой единственный вариант сделать дополнительный коммит поверх (D), и просто знать, что это действительно часть (B)?

Ответы [ 2 ]

14 голосов
/ 09 октября 2009

Что случилось

Вы имеете в виду изменить, а не добавить, верно? Я собираюсь притвориться, что это было на ветке под названием master, для удобства. Вот как выглядит ваш репозиторий:

A---B' (master)
 \
  \-B---C---D

Коммиты Git явно зависят от их родителей - один и тот же патч поверх другого родителя - это другой коммит.

Как восстановить

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

git checkout master@{1}
git branch oldmaster master@{1}

Предполагается, что это первая предыдущая позиция. Это может быть второй (master@{2}) ... или, если вы знаете, когда это было, вы можете использовать master@{7:35} или master@{23.hours.ago}. Краткое описание этих форм см. В разделе «Определение редакций» man git-rev-parse ( онлайн здесь ).

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

git reflog show master

Это даст вам список предыдущих позиций на master, и вы сможете суметь узнать из описаний, какое вы хотите (или, может быть, попробовать несколько). Вы можете просто скопировать хэши из списка и использовать git checkout или git branch, как указано выше.

Что вы должны были сделать

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

Тем не менее, чтобы сделать этот конкретный вид редактирования истории, вам нужно интерактивное обновление:

git rebase -i master~4 master

master~4 представляет собой коммит за четыре коммита до кончика мастера. Здесь вы можете использовать любую форму, какую захотите - может быть, это другая ветка, может быть хеш коммита - что угодно.

Откроется в редакторе список коммитов, с которыми вы играете:

pick <hash-A>  <message-A>
pick <hash-B>  <message-B>
pick <hash-C>  <message-C>
pick <hash-D>  <message-D>

# Rebase <hash-A^>..<hash-D> onto <hash-A^>
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Текст закомментированной справки довольно понятен. В этом случае вы хотите изменить 'pick' на 'edit' в строке коммита B. сохранить и выйти. Начнется ребазирование, и оно будет приостановлено после применения B, чтобы позволить вам внести изменения. Вы будете делать то, что вам нужно, добавить, использовать git commit --amend, а затем git rebase --continue. Это будет применяться C и D, и все будет готово. Если в середине что-то пойдет не так, используйте git rebase --abort, чтобы вернуться к тому, с чего вы начали.

Перебазировка может быть довольно страшной - например, не удаляйте случайно строки в этом списке! Если вам это пока не удобно, рекомендуется интенсивно использовать gitk и резервные имена ветвей.

1 голос
/ 09 октября 2009

4) к моему удивлению, дерево теперь читает

A - B

На самом деле журнал (история) выглядит так

A - B '

в качестве коммита отличается от предыдущего. Ситуация выглядит следующим образом:

A---B'                         <-- master  <--- HEAD
  \
    \--B---C---D

Цепочка коммитов "B --- C --- D" (с оригинальным B) выглядит потерянной, поскольку нет ссылки, указывающей на D. Теперь ваша ветвь указывает на коммит B '(модифицированный коммит B). Вы все еще можете получить доступ к D через reflog; это будет ГОЛОВА @ {2} я думаю ...

Где я ошибся?

Вы должны были использовать «git rebase --interactive», изменив «pick» рядом с коммитом B на «edit», и раздавить там патч.

Это, или используйте один из интерфейсов управления патчами поверх Git, как StGIT или Guilt .

Их данные удалены из журналов, и я не думаю, что смогу их вернуть.

Взгляните на reflog , то есть "git reflog" или "git log -g". Там вы должны увидеть свои "потерянные" коммиты.

НТН

...