Как я могу сбросить или вернуть файл к определенной ревизии?


4519

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

Я сделал git logвместе с a, git diffчтобы найти нужную мне ревизию, но просто не знаю, как вернуть файл в прежнее состояние в прошлом.


11
После возврата не забудьте --cachedпри проверке git diff. ссылка
Джеффри Хейл

5
Я нашел твой вопрос, когда погуглил мой. Но после того, как я прочитал решение, я проверил свой журнал и обнаружил, что я сделал эти изменения как отдельный коммит, поэтому я сделал git revert для этого коммита, а все остальное осталось так, как я хотел. Не решение, просто другой способ сделать это иногда.
sudo97

Ответы:


6140

Предполагая, что хеш коммита вы хотите c5f567:

git checkout c5f567 -- file1/to/restore file2/to/restore

Страница руководства git checkout дает больше информации.

Если вы хотите вернуться к фиксации ранее c5f567, добавьте ~1(где 1 - количество коммитов , которые вы хотите вернуть, это может быть что угодно):

git checkout c5f567~1 -- file1/to/restore file2/to/restore

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


12
@shadowhand: есть ли способ отменить это, так что это версия сразу после?
aliteralmind

16
@aliteralmind: Нет, к сожалению, сокращенная запись истории Git в истории движется только назад.
Грег Хьюгилл

46
Если вы собираетесь использовать название ветви для abcde (например develop), которое вам нужно git checkout develop -- file/to/restore(обратите внимание на двойную черту)
Ohad Schneider

8
@aliteralmind: На самом деле, да, есть способ сделать это: "git log --reverse -1 --ancestry-path yourgitrev..master" и затем использовать соответствующие опции, чтобы просто получить git rev. --ancestry-path "проведет линию" между двумя коммитами, а -1 покажет вам только одну версию, а --reverse обеспечит, чтобы первая отправленная запись была самой старой.
Крис Когдон,

6
Лично я нахожу ГОЛОВУ ^ легче набирать, чем ГОЛОВУ ~ 1 :)
juzzlin

606

Вы можете быстро просмотреть изменения, внесенные в файл, используя команду diff:

git diff <commit hash> <filename>

Затем, чтобы вернуть определенный файл в этот коммит, используйте команду сброса:

git reset <commit hash> <filename>

Возможно, вам придется использовать эту --hardопцию, если у вас есть локальные модификации.

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

git checkout <commit hash>
git checkout -b <new branch name>

Затем вы можете изменить это значение по отношению к основной линии, когда будете готовы объединить эти изменения:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>

7
Команда 'git checkout <commit hash>' вернула мне мою старую версию проекта, именно эту, которую я искал. Спасибо, Крис.
Видур Пандж

48
Чтобы вернуть файл git checkout <commit hash> <filename>работал лучше для меня, чемgit reset
Motti Strom

3
Я хотел раннюю версию одного файла, потому что я перезаписал 150 строк неправильно выбранной копией / вставкой. git checkout <commit hash> <filename>работал на меня. Это не должно быть принятым ответом, ИМХО. git resetне.
Harperville

24
не можете использовать git resetдля сброса одного файла, вы получите ошибкуfatal: Cannot do hard reset with paths
slier

13
Что Слиер сказал: вы не можете git reset --hard <commit hash> <filename>. Это ошибка с тем, fatal: Cannot do hard reset with paths.что сказал Мотти Стром: использованиеgit checkout <commit hash> <filename>
Ястребиный Паркер

366

Вы можете использовать любую ссылку на коммит git, включая SHA-1, если это наиболее удобно. Дело в том, что команда выглядит так:

git checkout [commit-ref] -- [filename]


22
В чем разница между этим ответом, который имеет --, и принятым, который не дает?
2rs2ts

80
В git «-» перед списком файлов говорит git, что все последующие аргументы должны интерпретироваться как имена файлов, а не как имена веток или что-то еще. Иногда это помогает разобраться.
foxxtrot

49
'-' - это не только соглашение git, но и то, что вы можете найти в различных местах командной строки * nix. rm -- -f(удалить файл с именем -f) кажется каноническим примером. Более подробно здесь
Hawkeye Parker

7
Просто добавьте к тому, что сказал @HawkeyeParker, rmкоманда использует getopt (3) для анализа своих аргументов. getoptэто команда для анализа аргументов команды. gnu.org/software/libc/manual/html_node/Getopt.html
Деви

2
@Honey Да, это то, что я имею в виду, и да, наверное, не совсем обычное. Я видел этот пример в разных местах, возможно, просто чтобы сделать его запоминающимся: rm -f, как известно, страшен / опасен. Но дело в том, что в * nix имя файла может начинаться с «-», и это может привести к путанице в различных интерпретаторах командной строки, которые, когда они увидят «-», ожидают, что параметр команды последует. Это может быть любой файл, начинающийся с '-'; например, "-mySpecialFile".
Ястребиный глаз Паркер

288
git checkout -- foo

Это сбросит fooна ГОЛОВУ. Вы также можете:

git checkout HEAD^ foo

за одну ревизию назад и т. д.


12
Я бы предложил использовать синтаксис, git checkout -- fooчтобы избежать ошибок, если fooчто-то особенное (например, каталог или файл с именем -f). В git, если вы не уверены, всегда ставьте перед всеми файлами и каталогами специальный аргумент --.
Микко Ранталайнен

8
Дополнительное примечание к комментарию Микко: --это не команда git и не специально для git. Это встроенный bash для обозначения конца параметров команды. Вы также можете использовать его со многими другими командами bash.
Матфей

14
@matthaeus также не относится ни к bash, ни к функции оболочки. Это соглашение реализовано во многих различных командах (и поддерживается getopt).
Грег Хьюгилл

2
Нет, --это не встроенное специальное слово в bash. Но это общее соглашение, поддерживаемое многими анализаторами командной строки и используемое многими CLI, включая git.
Эмиль Лундберг

123

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

git checkout HEAD file/to/restore

2
В чем разница между этим (git checkout HEAD file / to / restore) и git reset --hard file / to / restore ???
Мотти Шнеор

2
1) легче запомнить более общий способ 2) не беспокойтесь, нажав Enter перед вводом имени файла
Roman Susi

105

У меня была такая же проблема только сейчас, и я нашел этот ответcommit-ref наиболее простым для понимания ( это значение SHA изменения в журнале, к которому вы хотите вернуться):

git checkout [commit-ref] [filename]

Это поместит старую версию в ваш рабочий каталог, и оттуда вы можете зафиксировать ее, если хотите.


91

Если вы знаете, сколько коммитов нужно вернуть, вы можете использовать:

git checkout master~5 image.png

Это предполагает, что вы находитесь на masterветке, и версия, которую вы хотите, 5 коммитов назад.


80

Я думаю, что нашел это .... от http://www-cs-students.stanford.edu/~blynn/gitmagic/ch02.html

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

Начните с:

$ git log

который показывает вам список последних коммитов и их хэши SHA1.

Далее введите:

$ git reset --hard SHA1_HASH

чтобы восстановить состояние данного коммита и удалить все новые коммиты из записи навсегда.


24
Git никогда ничего не удаляет. Ваши старые коммиты все еще там, но, если на них не будет указана ветка, они больше не будут доступны. git reflog будет показывать их, пока вы не очистите свой репозиторий с помощью git-gc.
Бомба

1
@Bombe: Спасибо за информацию. Я проверил старую версию файла. Прочитав ваш комментарий, я смог использовать «gitref» для поиска частичного хэша SHA1 и использовать «checkout» для возврата к самой последней версии. Другие пользователи git могут найти эту информацию полезной.
Уинстон К. Ян

4
возможно последует agit push --force
bshirley

4
Если у вас есть незафиксированные изменения, вы потеряете их, если выполните git reset --hard
Boklucius

5
@Bombe - «Git никогда ничего не удаляет. Ваши старые коммиты все еще там, но если на них нет подсказки ветки, они больше недоступны». - но такие коммиты обрезаются через некоторое время, поэтому «Git никогда ничего не удаляет» не соответствует действительности.
Bulwersator

61

Это сработало для меня:

git checkout <commit hash> file

Затем внесите изменения:

git commit -a

54

Вы должны быть осторожны, когда говорите «откат». Если раньше у вас была одна версия файла в коммите $ A, а затем позже было сделано два изменения в двух отдельных коммитах $ B и $ C (так что вы видите третью итерацию файла), и если вы скажете " Я хочу вернуться к первому ", вы действительно это имеете в виду?

Если вы хотите избавиться от изменений как второй, так и третьей итерации, это очень просто:

$ git checkout $A file

и тогда вы фиксируете результат. Команда спрашивает: «Я хочу извлечь файл из состояния, записанного коммитом $ A».

С другой стороны, вы имели в виду избавление от изменений, внесенных второй итерацией (т. Е. Фиксацией $ B), сохраняя то, что коммит $ C сделал с файлом, вы захотите вернуть $ B

$ git revert $B

Обратите внимание, что тот, кто создал коммит $ B, возможно, не был очень дисциплинированным и мог совершить совершенно несвязанное изменение в том же коммите, и этот возврат может коснуться файлов, отличных от файла, в котором вы видите оскорбительные изменения, поэтому вы можете тщательно проверить результат после выполнения так.


Я сделал это, но тогда «файл журнала git» сказал бы, что я был на исходном коммите, HEAD. Казалось, что «Git Checkout» не удается. Тем не менее, состояние git показывало, что файл действительно был изменен, и "git diff --staged file" будет показывать фактические изменения. Кроме того, «git status» показал, что файл также изменился. Так что не используйте здесь «git log», чтобы отслеживать, какие файлы были изменены.
Фредерик Оллингер

37

Забавно, git checkout fooне будет работать, если рабочая копия находится в каталоге с именем foo; однако, и так git checkout HEAD fooи git checkout ./fooбудет:

$ pwd
/Users/aaron/Documents/work/foo
$ git checkout foo
D   foo
Already on "foo"
$ git checkout ./foo
$ git checkout HEAD foo

26
илиgit checkout -- foo
knittl

32

Вот как это rebaseработает:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>

Предположим, у вас есть

---o----o----o----o  master
    \---A----B       <my branch>

Первые две команды ... совершить git checkout git rebase master

... проверить ветку изменений, которые вы хотите применить к masterветке. Команда rebaseполучает коммиты <my branch>(которые не найдены в master) и повторно применяет их к главе master. Другими словами, родитель первого коммита в <my branch>больше не является предыдущим коммитом в masterистории, а текущим главой master. Две команды такие же как:

git rebase master <my branch>

Возможно, будет проще запомнить эту команду, так как ветки "base" и "modify" явные.

, Окончательный результат истории:

---o----o----o----o   master
                   \----A'----B'  <my branch>

Последние две команды ...

git checkout master
git merge <my branch>

... выполнить ускоренное слияние, чтобы применить все <my branch>изменения master. Без этого шага коммит ребаз не будет добавлен в master. Окончательный результат:

---o----o----o----o----A'----B'  master, <my branch>

masterи <my branch>обе ссылки B'. Также с этого момента можно безопасно удалить <my branch>ссылку.

git branch -d <my branch>

26

Начиная с git v2.23.0, существует новый метод восстановления git, который должен принимать на себя часть git checkoutответственности (даже в принятом ответе упоминается, что git checkoutэто довольно запутанно). Смотрите основные изменения в блоге GitHub .

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

Таким образом, основываясь на ответе Грега Хьюгилла (при условии, что хеш коммита c5f567), команда будет выглядеть так:

git restore --source=c5f567 file1/to/restore file2/to/restore

Или, если вы хотите восстановить содержимое одного коммита до c5f567:

git restore --source=c5f567~1 file1/to/restore file2/to/restore

24

Первая головка сброса для целевого файла

git reset HEAD path_to_file

Вторая проверка этого файла

git checkout -- path_to_file

4
+1, хотя не уверен в намерении сброса HEAD. Это может или не может быть необходимо. В моей ситуации я хотел вернуть только один конкретный файл к версии в репозитории (которая сохранила оставшиеся локальные изменения без изменений. Мне достаточно было просто
выполнить

Да, мне нужно только запустить 2-ю команду. Like -> shellhacks.com/git-revert-file-to-previous-commit
javaPlease42

22

git-aliases, awk и shell-функции на помощь!

git prevision <N> <filename>

где <N>- количество ревизий файла для отката для файла <filename>.
Например, чтобы оформить немедленную предыдущую ревизию одного файла x/y/z.c, запустите

git prevision -1 x/y/z.c

Как работает git prevision?

Добавьте следующее к вашему gitconfig

[alias]
        prevision = "!f() { git checkout `git log --oneline $2 |  awk -v commit="$1" 'FNR == -commit+1 {print $1}'` $2;} ;f"

Команда в основном

  • выполняет git logна указанном файле и
  • выбирает соответствующий идентификатор в истории файла и
  • выполняет git checkoutкоммит-идентификатор для указанного файла.

По сути, все, что можно было бы сделать вручную в этой ситуации,
заключить в один красивый, эффективный псевдоним - git-prevision.


20

Здесь я должен подключить EasyGit , который является оболочкой, чтобы сделать git более доступным для новичков, не путая бывалых пользователей. Одна из вещей, которые он делает, это дает больше смыслаgit revert . В этом случае вы бы просто сказали:

eg revert foo/bar foo/baz


1
Так и должно быть eg revert --in REVISON -- FILENAME. Это --inважно. Для пользователей Windows: откройте git bash. Выполнить echo %PATH. Первый путь должен быть в вашем пользовательском каталоге, заканчивающийся на bin. Создайте этот путь. Хранить, например, там. Назовите это eg. Не eg.txt.
Коппор

20

В случае, если вы хотите вернуть файл к предыдущей фиксации (и файл, который вы хотите восстановить уже зафиксированную), вы можете использовать

git checkout HEAD^1 path/to/file

или

git checkout HEAD~1 path/to/file

Затем просто подготовьте и зафиксируйте «новую» версию.

Вооружившись знанием, что в случае слияния у коммита может быть два родителя, вы должны знать, что HEAD ^ 1 является первым родителем, а HEAD ~ 1 - вторым родителем.

Любой из них будет работать, если в дереве есть только один родитель.


19

Здесь много предложений, в основном по направлениям git checkout $revision -- $file. Пара неясных альтернатив:

git show $revision:$file > $file

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

git show $revision:$file

или

git show $revision:$file | vim -R -

(OBS: $fileдолжен быть добавлен префикс, ./если это относительный путь для git show $revision:$fileработы)

И тем более странно

git archive $revision $file | tar -x0 > $file

1
Это хорошая альтернатива, если вы не уверены, какую версию коммита вы хотите, и вам нужно «посмотреть» без перезаписи вашего рабочего каталога.
Висбуки

18

Обратите внимание, однако, что git checkout ./fooи git checkout HEAD ./foo это не совсем то же самое; Дело в точке:

$ echo A > foo
$ git add foo
$ git commit -m 'A' foo
Created commit a1f085f: A
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 foo
$ echo B >> foo
$ git add foo
$ echo C >> foo
$ cat foo
A
B
C
$ git checkout ./foo
$ cat foo
A
B
$ git checkout HEAD ./foo
$ cat foo
A

(Второй addэтапы файла в индексе, но это не получить совершено.)

Git checkout ./fooозначает вернуть путь ./fooиз индекса ; добавление HEADуказывает Git вернуть этот путь в индексе к его HEADревизии, прежде чем сделать это.


14

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

У меня есть коммит abc1и после него я сделал несколько (или одну модификацию) файла file.txt.

Теперь скажите, что я что-то испортил в файле file.txtи хочу вернуться к предыдущему коммиту abc1.

1 git checkout file.txt.: это удалит локальные изменения, если они вам не нужны

2 git checkout abc1 file.txt.: это приведет ваш файл к нужной версии

3 git commit -m "Restored file.txt to version abc1".: это совершит ваше возвращение.

  1. git push : это будет толкать все в удаленном хранилище

Между шагами 2 и 3, конечно, вы можете сделать, git statusчтобы понять, что происходит. Обычно вы должны увидеть file.txtуже добавленное, и поэтому нет необходимости git add.


2
Хорошо, так что я думаю, что шаги 1. и 2. являются взаимоисключающими: если abc1 - ваш последний коммит, нет необходимости в 2. и если после abc1 были другие коммиты, вы можете напрямую сделать 2.
Жан Поль

13
  1. Git вернуть файл к определенной фиксации
git checkout Last_Stable_commit_Number -- fileName

2.Git вернуть файл в определенную ветку

git checkout branchName_Which_Has_stable_Commit fileName

11

Чтобы перейти к предыдущей версии коммита, получите номер коммита, скажем eb917a1

git checkout eb917a1 YourFileName

Если вам просто нужно вернуться к последней зафиксированной версии

git reset HEAD YourFileName
git checkout YourFileName

Это просто приведет вас к последнему подтвержденному состоянию файла.



10

Многие ответы здесь претендуют на использование git reset ... <file>или git checkout ... <file>, тем не менее, вы потеряете все изменения <file>в фиксации после фиксации, которую вы хотите отменить.

Если вы хотите отменить изменения от одного коммита git revertтолько для одного файла , как это было бы сделано, но только для одного файла (или, скажем, подмножества файлов коммитов), я предлагаю использовать оба варианта git diffи git applyтак (with <sha>= хэш коммит вы хотите вернуть):

git diff <sha>^ <sha> path/to/file.ext | git apply -R

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

Конечно, это не сработает, если измененные строки были изменены любым коммитом между <sha1>и HEAD(конфликтом).


Это должен быть одобренный ответ. Могу я предложить немного упрощенную версию:git show -p <sha> path/to/file.ext|git apply -R
Amaury D

вы можете использовать <sha>^!вместо<sha>^ <sha>
cambuncious

8

Используйте git logдля получения хеш-ключа для конкретной версии, а затем используйтеgit checkout <hashkey>

Примечание: не забудьте ввести хеш перед последним. Последний хеш указывает вашу текущую позицию (HEAD) и ничего не меняет.


7

Очевидно, что кто-то должен либо написать вразумительную книгу по git, либо git нужно лучше объяснить в документации. Столкнувшись с этой же проблемой, я догадался, что

cd <working copy>
git revert master

отменяет последний коммит, который, кажется, делает.

Ян


7

Вы можете сделать это в 4 этапа:

  1. вернуть весь коммит с файлом, который вы хотите специально отменить - он создаст новый коммит в вашей ветке
  2. soft reset that commit - удаляет коммит и перемещает изменения в рабочую область
  3. отобрать файлы, чтобы восстановить и зафиксировать их
  4. бросить все остальные файлы в вашей рабочей области

Что нужно набрать в своем терминале :

  1. git revert <commit_hash>
  2. git reset HEAD~1
  3. git add <file_i_want_to_revert> && git commit -m 'reverting file'
  4. git checkout .

удачи


разве это не отменяет ВСЕ изменения?
arcee123

1
@ arcee123 Да, но последующий сброс отменяет отмену всех изменений. Проблема в том, что git-revertдействует только на весь репо, поэтому чтобы компенсировать это, мы должны отменить все остальное.
Тимоти

2
Я рекомендую использовать: 1. git revert --no-commit <commit_hash>2. git reset HEADЭто сохраняет дополнительную фиксацию и делает все изменения только в вашем рабочем каталоге.
Тимоти

Ответ @ greg-hewgill лучше и точнее. Этот паршивый и не должен использоваться.
Даниэль Транка

Это именно то, что нужно для истинного возврата определенных файлов. Мне нужно было отменить изменения в нескольких файлах из более ранней фиксации, которая уже была передана в удаленный репозиторий. Я отменил, сбросил и зафиксировал результат: git revert _oldcommit_ --no-commit git reset -- _unchanged1_ _unchanged2_ ... git commit -m "branch without changes to specific files"новая подсказка ветви отразила все изменения, за исключением возвращенных файлов.
Suncat2000

7

Это очень простой шаг. Извлеките файл с требуемым идентификатором фиксации, здесь один идентификатор фиксации ранее, а затем просто выполните команду git commit modify и все готово.

# git checkout <previous commit_id> <file_name>
# git commit --amend

Это очень удобно. Если мы хотим вывести какой-либо файл с любым идентификатором предыдущей фиксации в верхней части фиксации, мы можем легко это сделать.


5
git revert <hash>

Вернет данный коммит. Похоже, вы думаете, что git revertвлияет только на самый последний коммит.

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


5

Если вы делаете неправильный файл в ваших последних коммитах, следуйте инструкциям:

  1. дерево с открытым исходным кодом, измените этот коммит

дерево с открытым исходным кодом

  1. измените строки и найдите ваш коммит, который неверный файл отправил как коммит

введите описание изображения здесь

  1. вы можете увидеть список ваших изменений в этом коммите список файлов в исходном дереве
  2. выберите его, а затем нажмите на ... кнопки справа ... щелкните обратный файл
  3. затем вы можете увидеть его на вкладке статуса файла внизу слева и нажать кнопку «untage»:

вкладка состояния файла

  1. откройте код Visual Studio и вернитесь обратно, зафиксировав удаленные файлы
  2. после всех них вы можете увидеть результаты вашего последнего коммита в исходном дереве

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.