Возврат части фиксации с помощью git

151

Я хочу отменить конкретную фиксацию в git. К сожалению, наша организация по-прежнему использует CVS в качестве стандарта, поэтому, когда я возвращаюсь в CVS, несколько коммитов git объединяются в один. В этом случае я бы хотел выделить исходный git commit, но это невозможно.

Есть ли подход, подобный тому git add --patch, который позволил бы мне выборочно редактировать различия, чтобы решить, какие части фиксации нужно отменить?

скипоппи
источник
Другие решения здесь , но с упором на ограничение частичного Revert к конкретным файлам.
ntc2

Ответы:

234

Используйте опцию --no-commit( -n) git revert, затем отключите изменения, затем используйте git add --patch:

$ git revert -n $bad_commit    # Revert the commit, but don't commit the changes
$ git reset HEAD .             # Unstage the changes
$ git add --patch .            # Add whatever changes you want
$ git commit                   # Commit those changes

Примечание. Файлы, которые вы добавляете с помощью git add --patch, - это файлы, которые вы хотите восстановить, а не файлы, которые вы хотите сохранить.

мипади
источник
13
Возможно, стоит добавить последнюю требуемую команду для тех, кто не очень знаком с git: после фиксации, git reset --hardчтобы отменить другие изменения, от которых вы не хотели возвращаться.
tremby
15
git reset --hardопасен для новичков, так как может потерять требуемые правки. Вместо того, чтобы привыкнуть git status, это намекает на git checkout -- FILE..более безопасное возвращение вещей.
Тино
Какое зияющее упущение git; git revertнадо просто --patchпоспорить.
Kaz
@Kaz: git revertиспользуется для отмены всех коммитов . Вы можете использовать git checkout -pдля интерактивного выбора битов для восстановления.
mipadi
1
Я также хотел бы добавить (возможно) очевидное, что сначала сохраните вашу работу . Либо commitсначала, либо stash, потом попробуй revert.
Фелипе Альварес
39

Я успешно использовал следующее.

Сначала верните полную фиксацию (поместите ее в индекс), но не фиксируйте.

git revert -n <sha1>  # -n is short for --no-commit

Затем интерактивно удалите отмененные ХОРОШИЕ изменения из индекса.

git reset -p          # -p is short for --patch  

Затем зафиксируйте обратную разницу плохих изменений

git commit -m "Partially revert <sha1>..."

Наконец, возвращенные ХОРОШИЕ изменения (которые были отменены командой сброса) все еще находятся в рабочем дереве. Их нужно очистить. Если в рабочем дереве не осталось других незафиксированных изменений, это можно сделать с помощью

git reset --hard
user1338062
источник
5
Разве это не лучшая альтернатива принятому ответу (который использует reset HEAD .), потому что он не требует окончательной очистки рабочего каталога?
Стивен Лу
2
Этот ответ лучше, потому что reset -pон короче, чем reset HEADследует add -p. Но он все еще требует очистки, так как «хорошие» блоки, которые были сброшены, все еще находятся в рабочем каталоге после фиксации.
Chiel ten Brinke
Ответ на этот вопрос не выше , потому что в интерактивном режиме удаления изменений вы хотите часто запутанным и подверженным ошибкам --- особенно , если любой из них требуют редактирования.
Kaz
5

Лично я предпочитаю эту версию, которая повторно использует автоматически сгенерированное сообщение фиксации и дает пользователю возможность отредактировать и вставить слово «частично» перед окончательной фиксацией.

# generate a revert commit
# note the hash printed to console on success
git revert --no-edit <hash to revert>

# undo that commit, but not its changes to the working tree
# (reset index to commit-before-last; that is, one graph entry up from HEAD)
git reset HEAD~1

# interactively add reversions
git add -p

# commit with pre-filled message
git commit -c <hash from revert commit, printed to console after first command>

# reset the rest of the current directory's working tree to match git
# this will reapply the excluded parts of the reversion to the working tree
# you may need to change the paths to be checked out
# be careful not to accidentally overwrite unsaved work
git checkout -- .
jeffcook2150
источник
4

Решение:

git revert --no-commit <commit hash>
git reset -p        # every time choose 'y' if you want keep the change, otherwise choose 'n'
git commit -m "Revert ..."
git checkout -- .   # Don't forget to use it.
Кшиштоф Качмарский
источник
Если бы вы сказали, чем оно отличается от общепринятого, это поможет людям
CharlesB
1
@Krzysztof Почему важно оформить заказ в конце и почему это решение отличается от решения user1338062?
Мартин
3

Другой альтернативой (если ваша текущая версия файла не слишком далеко от версии, которую вы пытаетесь вернуть) является получение хеша фиксации непосредственно перед той, которую вы хотите частично вернуть (из git log). Тогда ваша команда станет:

$ git checkout -p <hash_preceding_commit_to_revert> -- file/you/want/to/fix.ext

Это действительно изменяет файлы в вашем рабочем дереве, но не создает никаких коммитов, поэтому, если вы действительно набираете вес, вы можете просто начать заново git reset --hard -- file/you/want/to/fix.ext.

Вальф
источник
1

Вы можете использовать git-revert -n, а затем использовать add --patch для выбора фрагментов.

Уильям Перселл
источник