2009-03-13 6 views
44

Я внесла изменения в скрипт и совершил его. Затем я сделал несколько других изменений и перебросил их в удаленный репозиторий и т. Д.Отменить изменение в git (не переписывая историю)

Тогда я понял, что первое изменение, о котором я упоминал, было глупо, и хочу отменить его. Могу ли я «не использовать», что совершить, без ручной копирования/вставки diff?

В качестве примера: у меня есть два файла, a.py и b.py:

Commit 1: 
I delete a function in a.py 

Commit 2: 
I change a few lines in b.py 

Commit 3: 
I change the docstring in a.py 

Можно ли отменить эту функцию удаления, и сделать вид, как «совершить 4» (а не удаление совершить 1)

+13

Ой, это «осознано», а не «осознано»! (Я не американец ..) – dbr

+3

Ваше правописание было исправлено людьми, которые используют ярды и ноги для измерения вещей ... ха-ха – FaddishWorm

ответ

57

Да, вы можете использовать git revert. См. git manual section on this для получения дополнительной информации.

Суть в том, что вы можете сказать:

git revert 4f4k2a 

Где 4f4k2a это идентификатор фиксации вы хотите отменить, и он будет пытаться отменить его.

+0

Ага, конечно же, спасибо! – dbr

+0

BTW! Он удалит этот commit 1 объект – aatifh

+0

Хм, он, похоже, не удалил revert'd commit .. – dbr

31

Просто комментарий:

git revert aCommit 

делает Возвратить все совершить (как в «всех файлов частью фиксации»):
вычисляет обратный патч, применяет его на ГОЛОВЫ и совершить.

Так две проблемы здесь (первый один легко решается):

  • он всегда совершает, так что вы можете добавить -no-commit вариант: «git revert --no-commit aCommit»: это полезно при отмене более один эффект совершает ваш индекс в строке.
  • он не применяется к определенному файлу (что, если вы a.py были частью коммита с 1000 другими изменениями, которые вы, возможно, не захотите возвращать)?
    Для этого, если вы хотите, чтобы извлечь определенные файлы, как они были в другой коммит, вы должны увидеть git-checkout, в частности синтаксис git checkout <commit> <filename> (это не совсем то, что вам нужно в этом случае, хотя)

Easy Git (Elijah Newren) попытался принести больше «полного возврата» на Git Mailing list; но без особого успеха:

Люди иногда хотят «вернуть изменения».

Теперь, это может быть:

  • изменения между 32 и 29 пересмотры назад,
  • это может быть все изменения с момента последней фиксации,
  • это может быть изменения, так как 3 совершает назад , или
  • это может быть только одна конкретная фиксация.
  • пользователь может захотеть подмножество таких реверсии, чтобы только конкретные файлы,

(eg revert является documented here, но я не уверен, что она является частью текущего распределения, например, хотя)

, но все это сводится к «возвращению изменений» в конце.

eg revert --since HEAD~3 # Undo all changes since HEAD~3 
eg revert --in HEAD~8  # much like git revert HEAD~8, but nocommit by default 
eg revert --since HEAD foo.py # Undo changes to foo.py since last commit 
eg revert foo.py    # Same as above 
eg revert --in trial~7 bar.c baz. # Undo changes made in trial~7 to bar.[ch] 

Являются ли эти виды «возвращаясь данных» действительно настолько различны, что должны должны быть разные команды, или что некоторые из этих операций не должна поддерживаться простой командой REVERT?
Конечно, большинство пользователей большую часть времени, вероятно, будут использовать форму «» , но я не вижу вреда в поддержке дополнительных возможностей.

Также ... есть ли что-нибудь фундаментальное, что бы сохранить ядро ​​git от принятия такого поведения?

Илия

Примечание: совершает по умолчанию не имеет смысла для обобщенной revert команды, и «git revert REVISION» ошибка будет с инструкциями (говорит пользователю добавить --in флаг).


Допустим, у вас есть, из 50 совершенных, 20 файлов вы понимаете, что старые совершить X внесены изменения, которые не должны происшедшие.
Небольшая сантехника в порядке.
Что вам нужно это способ перечислить все конкретные файлы, необходимые для вернуться
(как в «чтобы отменить изменения, сделанные в фиксации X, сохраняя при этом все последующие изменения»),
, а затем, для каждого из них:

git-merge-file -p a.py X X^ 

проблема здесь, чтобы восстановить утраченную функцию без облитерирующего всех последующих изменений a.py вы можете захотеть сохранить.
Этот метод когда-то называется «отрицательным слиянием».

С git merge-file <current-file> <base-file> <other-file>means:
включает в себя все изменения, которые ведут от <base-file> к <other-file> в <current-file>, вы можете восстановить удаленные функции, говоря, вы хотите, чтобы включить все изменения)

    .
  • из: X (где функция была удалена)
  • в: X^(предыдущая фиксация Перед X, где функция была все еще там)

Примечание: в «-p» аргумент, который позволяет просматривать первые изменения, ничего не делая на текущий файл. Когда вы уверены, удалите эту опцию.

Примечание: git merge-file является not that simple: вы не можете ссылаться на предыдущие версии файла так же, как это.
(вы бы снова и снова фрустрирующего сообщение: error: Could not stat X)
Вы должны:

git cat-file blob a.py > tmp/ori # current file before any modification 
git cat-file blob HEAD~2:a.py > tmp/X # file with the function deleted 
git cat-file blob HEAD~3:a.py > tmp/F # file with the function which was still there 

git merge-file a.py tmp/X tmp/F # basically a RCS-style merge 
           # note the inversed commit order: X as based, then F 
           # that is why is is a "negative merge" 
diff -u a.py tmp/ori # eyeball the merge result 
git add a.py 
git commit -m "function restored" # and any other changes made from X are preserved! 

Если это будет сделано для большого количества файлов в предыдущей фиксации ... некоторые сценариев в порядке;)

+0

Не думаю, что вам нужна тире в: 'git checkout ' – Paul

5

чтобы отменить изменения, только один файл в коммита, а VonC указал, я бы checkout ветку (мастер или хобота или любой другой), а затем checkout версию файла, который я хотел вернуться и t Reat как новое обязательство:

$ git checkout trunk 
$ git checkout 4f4k2a^ a.py 
$ git add a.py 
$ git diff    #verify I'm only changing what I want; edit as needed 
$ git commit -m 'recover function deleted from a.py in 4f4k2a' 

Там, наверное, команда сантехнические, которая будет делать это непосредственно, но я бы не использовать его, если бы я знал это. Дело не в том, что я не доверяю Git, но я не доверяю себе - я бы не стал доверять тому, что знал, не глядя на то, что было изменено в этом файле в этом деле и с тех пор. И как только я посмотрю, проще просто создать новую фиксацию, отредактировав diff. Возможно, это просто личный стиль работы.

+1

Два комментария: 1/если функция был удален в commit 4f4k2a для a.py, не должен ли вы проверить a.py из * предыдущей * фиксации? (1 * до * 4f4k2a, как в 4f4k2a ^?) 2/Не будет ли это полностью удалить текущую версию a.py? Что делать, если я хочу сохранить последующие изменения? – VonC

+0

Я предпочел бы сделать «отрицательное слияние» с git-merge-file: см. Мой завершенный ответ – VonC

+0

1) Да. Спасибо за исправление другой опечатки. 2) Да. Вот почему мне пришлось бы увидеть разницу. Я бы на самом деле использовал взломанный diffmerge в режиме трехмерного слияния, который показывает мне более полезный diff, с которым я привык работать, но git diff можно использовать. Дело в том, что я уверен, что посмотрел код. – Paul

1

Посмотрите на this git revert question. Кажется, проблема связана с более старыми коммитами, если не последовательной последовательностью, включая самую последнюю фиксацию.

+0

Продвижение, потому что это было полезно, но на самом деле это должен быть комментарий, а не ответ. – tripleee