Могу ли я сделать частичный возврат в GIT

156

Можно ли отменить только один файл или определенные изменения в файле в многофайловой фиксации?

Полная история, которую я передал кучу файлов. Через несколько коммитов кто-то, кто останется безымянным (JACK !!!), скопировал файл в свой репозиторий и зафиксировал несколько файлов, перезаписав некоторые изменения, которые я сделал. Я хочу отменить один файл, который был забит или еще лучше, зайти и отменить два изменения в этом файле. Это должен быть отдельный возвратный коммит, так как он был извлечен.

сцепление
источник
Брайан поставил меня на правильный путь. В итоге я использовал git add --patch, а затем использовал функцию редактирования в patch, чтобы получить то, что я хотел.
Сцепление
2
Я довольно поздно опаздываю к игре, но думаю, что нашел «правильный» ответ .
ntc2
@ ntc2 Как ни странно, принятый ответ сработал для меня намного лучше, чем тот, который вы связали - он создал конфликты слияния, которые я мог разрешить, вместо того, чтобы просто не применять патчи.
Иридайн
@liridayn Ты прочитал весь мой ответ? В разделе «Вариации» я утверждаю, что добавление --3wayприводит к конфликтам слияния вместо того, чтобы не применять патчи.
ntc2

Ответы:

204

Вы можете отменить коммит, не создавая новый, добавив параметр --no-commit. Это оставляет все возвращенные файлы в области подготовки. Оттуда я выполнил бы мягкий сброс и добавил бы изменения, которые я действительно хотел. Для примера рабочего процесса:

git revert <sha-of-bad-commit> --no-commit
git reset   // This gets them out of the staging area
<edit bad file to look like it should, if necessary>
git add <bad-file>
git checkout . // This wipes all the undesired reverts still hanging around in the working copy
git commit
bobDevil
источник
42
После мягкого сброса вы также git add -pможете запустить интерактивный сеанс, который позволяет выборочно добавлять куски файлов вместо целых файлов. (Также есть git reset -pвыборочно нестандартные изменения. Полезно знать, но, вероятно, не то, что вы хотите в этом сценарии.)
Стефан Кохен,
1
Это именно то, что я искал: вернуться в рабочую копию, выбрать фрагменты в интерактивном режиме, зафиксировать. Вы заслуживаете большого, соленого мужского обмана для этого: *
das_weezul
Другим решением вдохновленный этот будет revert, reset, stash -p(заначка «отменяет , что я не хочу , чтобы actualy марки»), addостальные,commit
Кирилл Chapon
82

Вы можете в интерактивном режиме применить старую версию файла с помощью checkoutкоманды.

Например, если вы знаете, COMMITкуда был удален код для добавления, вы можете выполнить следующую команду:

git checkout -p COMMIT^ -- FILE_TO_REVERT

Git предложит вам добавить фрагменты, отсутствующие в текущей версии файла. Вы можете использовать eдля создания патча изменения, прежде чем применить его обратно.

cmcginty
источник
Очень полезно, если вы хотите восстановить только часть файла! В основном, чтобы получить куски из головы,git checkout -p path/to/file
фальстаф
40

Вы можете просто вручную проверить старое, хорошее содержимое файлов, которые вы хотите восстановить, используя git checkout. Например, если вы хотите вернуться my-important-fileк версии, которая была в версии abc123, вы можете сделать

git checkout abc123 -- my-important-file

Теперь у вас есть старое содержимое my-important-fileназад, и вы можете даже отредактировать его, если захотите, и выполнить коммит как обычно, чтобы сделать коммит, который вернет сделанные им изменения. Если есть только некоторые части его коммита, которые вы хотите вернуть, используйте, git add -pчтобы выбрать только несколько фрагментов из патча, который вы делаете.

Брайан Кэмпбелл
источник
19
Это не так revert, вы просто забираете старую версию файла. Правильное revertудаление удалит неверные изменения фиксации в файле, даже если он был изменен несколько раз после этой неудачной фиксации, и вы не хотите терять эти модификации.
Тотор
2
Тотор, посмотри мой ответ. Вы можете использовать «-p» для сохранения изменений. Ответ очень близок к этому на самом деле.
cmcginty
1
Или вы можете объединить это в git checkout -p.
Лео Лам
31

Я нашел способ сделать это в списке рассылки Git:

git show <commit> -- <path> | git apply --reverse

Источник: http://git.661346.n2.nabble.com/Revert-a-single-commit-in-a-single-file-td6064050.html#a6064406

вариации

Эта команда не выполняется (не вызывает изменений), если исправление не применяется корректно, но --3wayвместо этого вы получаете конфликты, которые затем можно разрешить вручную (в этом случае ответ Кейси может быть более практичным):

git show <commit> -- <path> | git apply --reverse --3way

Вы также можете использовать это для частичного возврата нескольких коммитов, например:

git log -S<string> --patch | git apply --reverse

вернуть файлы с изменениями, соответствующими <string>в любом коммите. Это именно то, что мне было нужно в моем случае использования (несколько отдельных коммитов внесли аналогичные изменения в разные файлы, а также изменили другие файлы несвязанными способами, которые я не хотел возвращать).

Если вы diff.noprefix=trueустановили в свой, ~/.gitconfigто вам нужно добавить -p0в git applyкоманду, например,

git show <commit> -- <path> | git apply -p0 --reverse
NTC2
источник
Этот ответ намного лучше принятого: «еще лучше, иди и отмени два изменения в этом файле».
переписано
Я хотел вернуть только часть коммита, поэтому я сделал: (1) git show ... > foo.txt (2) правку, foo.txtчтобы удалить разногласия, которые я не хотел возвращать (3), git apply --reverse foo.txt чтобы отменить разности, все еще перечисленные в foo.txt.
cxw
Добавление --binary --full-index в часть git show также может быть полезным вариантом
Laurynas Biveinis