Найти и восстановить удаленный файл в репозитории Git


2802

Скажем, я в Git-хранилище. Я удаляю файл и фиксирую это изменение. Я продолжаю работать и делаю еще коммиты. Затем я обнаружил, что мне нужно восстановить этот файл.

Я знаю, что могу извлечь файл, используя git checkout HEAD^ foo.bar, но я не знаю, когда этот файл был удален.

  1. Какой самый быстрый способ найти коммит, который удалил данное имя файла?
  2. Какой самый простой способ вернуть этот файл в мою рабочую копию?

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


39
обратите внимание, что предыдущий комментарий отвечает на вопрос в заголовке, а не в теле - это включает в себя выяснение, когда файл был удален.
avdgaag

8
Чтобы найти коммит, файл был удален в:git log --diff-filter=D -- path/to/file
titaniumdecoy



54
@hhh git checkout deletedFileбудет восстанавливать, deletedFileесли он был удален, но это удаление еще не было поставлено или зафиксировано . Это не то, о чем здесь спрашивается; этот вопрос о том, как восстановить файл, удаление которого было совершено много коммитов назад.
Марк Амери

Ответы:


3150

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

git rev-list -n 1 HEAD -- <file_path>

Затем извлеките версию перед фиксацией, используя ^символ caret ( ):

git checkout <deleting_commit>^ -- <file_path>

Или в одной команде, если $fileречь идет о файле.

git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

Если вы используете zsh и у вас включена опция EXTENDED_GLOB, символ каретки не будет работать. Вы можете использовать ~1вместо этого.

git checkout $(git rev-list -n 1 HEAD -- "$file")~1 -- "$file"

94
Сложнее всего проверить коммит ДО, используя суффикс ^. Спасибо.
Кристиан Удард

4
По некоторым причинам, это не будет работать в Zsh. ± git checkout $(git rev-list -n 1 HEAD "spec/Sporkfile_example.rb")^ -- "spec/Sporkfile_example.rb" zsh: no matches found: b71c152d8f38dcd23ad7600a93f261a7252c59e9^ Я переключился на bash, и все работало нормально.
zoras

20
Из командной строки Windows я получил ошибку. error: pathspec <filename> did not match any file(s) known to git., Решение было использовать git bash.
donturner

56
Я думаю, что @zoras zsh имеет свое собственное расширение для '^', но вы можете использовать альтернативный синтаксис '~ 1': git checkout <deleting-commit>~1 -- <file-path> ~ X позволяет вам указать X коммитов перед указанным коммитом, поэтому ~ 1 - это коммит до, ~ 2 два коммита до и т. д.
Нильс Лакстон,

22
В командной строке Windows, ^символ является escape-символом! Поэтому в cmd вы должны набрать, ^^чтобы сообщить cmd, что вы хотите использовать один литерал ^ и что после него вы не избежите чего-то еще. Что происходит со многими людьми, так это то, что за ^ними следует пробел. Таким образом, cmd думает, что вы выходите из пробела - что дает просто пробел. Таким образом, к тому времени, когда git получает аргументы cli, он видит SHA1и не видит SHA1^. Это действительно раздражает. ~это не спасательный персонаж, поэтому это все еще работает. (PS. Если вы думаете, что гуглерам понадобится эта информация, пожалуйста, проголосуйте за этот комментарий)
Александр Берд

875
  1. Используйте git log --diff-filter=D --summaryдля получения всех коммитов, которые удалили файлы и файлы удалены;
  2. Используйте git checkout $commit~1 path/to/file.extдля восстановления удаленного файла.

Где $commitзначение коммита, которое вы нашли на шаге 1, напримерe4cf499627


10
Любопытно, что означает ~ 1?
Томми Ченг

7
@tommy - спецификация тильды даст вам n-го внука именованного коммита. Смотрите book.git-scm.com/4_git_treeishes.html для более подробной информации.
Роберт Мунтяну

5
это, безусловно, самый простой и интуитивно понятный подход. git log -- *PartOfMyFileName*, Спасибо за$commit~1
BGS

3
git checkout $commit~1 filenameсинтаксис работает идеально подходят для отдельных файлов, а также работает для целых каталогов. т.е. , чтобы восстановить все удаленные изображения в ./images из ша 12345: git checkout 12345~1 images. спасибо за этот ответ!
noinput

34
@Alexar $commit~1означает, что вы должны добавить имя коммита. Что-то вроде 1d0c9ef6eb4e39488490543570c31c2ff594426cгде $commitесть.
Евгений

319

Чтобы восстановить все эти удаленные файлы в папке, введите следующую команду.

git ls-files -d | xargs git checkout --

1
Куда попадают файлы? Я не вижу изменений.
Уильям Гранд

21
Это, наверное, самый простой способ. Его извращение, как трудно git, сделал даже самую простую задачу.
1913 года

git checkout - [файл] вернет изменения в [файл]. Канал заменит [файл] именем удаленных файлов.
Ману

6
ls-filesСуб-команда под рукой, но , кажется, не работает для файлов , которые были удалены с git rmт в постановке, не говоря уже совершил, что и спросил ОП.
MarkHu

Это сработало для восстановления удаленных файлов, но как я могу обновить файлы, которые были изменены и отображаются как M myChangedFileпосле git checkout?
Либби

124

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

git checkout HEAD -- path/to/file.ext


93

Если вы ненормальный, используйте git-bisect. Вот что нужно сделать:

git bisect start
git bisect bad
git bisect good <some commit where you know the file existed>

Теперь пришло время запустить автоматический тест. Команда оболочки '[ -e foo.bar ]'вернет 0, если foo.barсуществует, и 1 в противном случае. Команда «run» git-bisectбудет использовать бинарный поиск, чтобы автоматически найти первый коммит, где тест не пройден. Он начинается на полпути через заданный диапазон (от хорошего до плохого) и разрезает его пополам в зависимости от результата указанного теста.

git bisect run '[ -e foo.bar ]'

Теперь вы в коммите, который удалил его. Отсюда вы можете вернуться в будущее и использовать, git-revertчтобы отменить изменения,

git bisect reset
git revert <the offending commit>

или вы можете вернуться на один коммит и вручную осмотреть повреждения:

git checkout HEAD^
cp foo.bar /tmp
git bisect reset
cp /tmp/foo.bar .

2
Не могли бы вы уточнить git bisect run '[ -e foo.bar ]'?
Avdgaag

Вы также можете использовать хорошее и плохое вручную, если это не может быть проверено автоматически. Смотрите справочную страницу.
Джош Ли

1
@avdgaag git bisect runговорит Git автоматизировать биективности, выполнив команду следующего слова «запустить» , где команда должна вернуться 0к goodверсии (см git help bisectдля более подробной информации). Это '[ -e foo.bar ]'стандартное выражение для проверки foo.bar, существует ли файл (реализация обычно находится в файле, на /usr/bin/[который обычно /usr/bin/testделается жесткая ссылка), и одинарные кавычки используются для обозначения всего этого как одного аргумента командной строки.
Микко Ранталайнен

Отличная идея. Я попробовал этот подход, и он идентифицировал фиксацию перед удалением, но не фиксацию, которая фактически удаляла файл. И в другом тесте было выявлено 2 коммита до удаления.
Майкл Ософский

Безумный? Может быть. Но bisect - отличный способ помочь найти причину ошибки, поэтому в любом случае это ценный навык. Так что, хотя, может быть, это не «правильный» или «самый правильный» путь, это все же хорошая идея и стоит +1, безусловно!
Pryftan

77

Мой новый любимый псевдоним, основанный на bonyiii «ы ответ (upvoted), и мой собственный ответ на вопрос о„ Передайте аргумент команды Git псевдоним “:

git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'

Я потерял файл, удаленный по ошибке несколько коммитов назад?
Быстро:

git restore my_deleted_file

Кризис предотвращен.

Предупреждение, с Git 2.23 (Q3 2019) поставляется экспериментальная команда с именем git restore(!).
Поэтому переименуйте этот псевдоним (как показано ниже).


Роберт Дейли предлагает в комментариях следующий псевдоним:

restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

И Джеган добавляет в комментариях :

Для установки псевдонима из командной строки я использовал эту команду:

git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" 

7
Это восстанавливает весь коммит, а не только запрошенный файл.
Даниэль Банг

5
Вот мой псевдоним, работает чудесно:restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"
void.pointer

1
@RobertDailey Это выглядит великолепно! Я включил ваш псевдоним в ответ для большей наглядности.
VonC

1
Для установки псевдонима из командной строки я использовал эту команду:git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\""
jegan

2
Expansion of alias 'restore' failed; '!git' is not a git command
Карл Моррисон

55

Если вы знаете имя файла, это простой способ с основными командами:

Перечислите все коммиты для этого файла.

git log -- path/to/file

Последний коммит (самый верхний) - тот, который удалил файл. Так что вам нужно восстановить второй до последнего коммита.

git checkout {second to last commit} -- path/to/file

Просто использовал это решение, и не было никакого коммита для удаления. Я был в состоянии восстановить файл, используя самый последний идентификатор коммита.
Адам

Разве следующий за последним коммит (предыдущий коммит до удаления) не будет содержать самую последнюю версию удаленного файла? Второй-последний (фиксация перед предыдущей фиксацией удаления) может быть безнадежно устаревшей.
Suncat2000

1
Это первое решение, которое я увидел, достаточно простое, поэтому мне не придется возвращаться сюда, чтобы найти его в следующий раз. Может быть.
Eloff

@ Suncat2000 «второй до последнего» означает «предыдущий коммит для удаления», так же как «рядом с последним». en.wiktionary.org/wiki/penultimate#Synonyms
wisbucky

Огромное спасибо за этот ответ !!!!!
Ракеш Бк

29

Чтобы восстановить удаленный и зафиксированный файл:

git reset HEAD some/path
git checkout -- some/path

Он был протестирован на Git версии 1.7.5.4.


1
Это не сработало для меня. После оформления заказа я error: pathspec 'foo' did not match any file(s) known to git.убедился, что имя файла указано правильно. Git версия 2.7.0
wisbucky

-1; это не верно. Эти команды отменят удаление, которое еще не было зафиксировано (первая снимает этап с удаления, если он был подготовлен, а вторая отменяет внесенные без изменений в файл), но вы утверждаете, что они восстановят зафиксированный удаление файла, которое просто не соответствует действительности и завершится с ошибкой, подобной той, что была в комментарии @ wisbucky выше.
Марк Амери

@MarkAmery Действительно, я думаю, что эта команда хорошо работала для тех разработчиков, которые не делали явной подготовки для фиксации удаленных файлов git add -A, но восстановленный файл все еще находился в стадии фиксации.
Федир Рыхтик

25

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

git checkout -- .

но ваши удаленные файлы не вернулись, вы просто выполните следующую команду:

git checkout <file_path>

И до того, ваш файл вернулся.


24

У меня есть это решение .

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

    • git log --grep=*word*
    • git log -Sword
    • git log | grep --context=5 *word*
    • git log --stat | grep --context=5 *word* # рекомендуется, если вы ничего не помните
  2. Вы должны получить что-то вроде:

commit bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Автор: Александр Орлов Дата: чт 12 мая 23:44:27 2011 +0200

replaced deprecated GWT class
- gwtI18nKeySync.sh, an outdated (?, replaced by a Maven goal) I18n generation script

commit 3ea4e3af253ac6fd1691ff6bb89c964f54802302 Автор: Александр Орлов Дата: Чт 12 мая 22:10:22 2011 +0200

3 . Теперь, используя идентификатор фиксации bfe68bd117e1091c96d2976c99b3bcc8310bebe7, сделайте:

git checkout bfe68bd117e1091c96d2976c99b3bcc8310bebe7^1 yourDeletedFile.java

Поскольку идентификатор коммита ссылается на коммит, где файл уже был удален, вам нужно ссылаться на коммит непосредственно перед bfe68b, что можно сделать, добавив ^1. Это значит: дай мне коммит как раз перед bfe68b.


Это тот же подход, что и принятый ответ, но есть еще несколько способов найти коммит удаления. Мне все еще нравится подход, принятый в принятом ответе, но это хорошие альтернативы. Спасибо!
Avdgaag

Я предполагаю, что сначала проверка удаленного файла, а затем (без изменения) его фиксация не создает копию файла. Правильно? (Мне нужно сделать это с изображениями, а копии сделают хранилище больше)
Stonecrusher


12

git undelete path/to/file.ext

  1. Поместите это в свой .bash_profile(или другой соответствующий файл, который загружается при открытии командной оболочки):

    git config --global alias.undelete '!sh -c "git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1" -'
    
  2. Тогда используйте:

    git undelete path/to/file.ext
    

Этот псевдоним сначала проверяет, чтобы найти последний коммит, где существовал этот файл, а затем выполняет Git извлечение пути этого файла из того последнего коммита, где этот файл существовал. Источник .


11

Во многих случаях может быть полезно использовать coreutils (grep, sed и т. Д.) В сочетании с Git. Я уже хорошо знаю эти инструменты, но Git меньше. Если бы я хотел выполнить поиск удаленного файла, я бы сделал следующее:

git log --raw | grep -B 30 $'D\t.*deleted_file.c'

Когда я нахожу ревизию / коммит:

git checkout <rev>^ -- path/to/refound/deleted_file.c

Также как другие заявляли до меня.

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


7

Мне пришлось восстанавливать кучу удаленных файлов из определенного коммита, и мне это удалось с помощью двух команд:

git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git checkout <rev>^ -- 
git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git reset HEAD 

(Обратите внимание на завершающий пробел в конце каждой команды.)

Файлы были добавлены в файл .gitignore и затем очищены с помощью git rm. Мне нужно было восстановить файлы, но затем удалить их. Мне нужно было восстановить сотни файлов, и вводить данные вручную для каждого файла, как в других примерах, было слишком медленно.


7

На самом деле, этот вопрос напрямую касается Git, но кто-то вроде меня работает с такими инструментами с графическим интерфейсом, как WebStorm VCS, кроме знания команд Git CLI.

Я щелкаю правой кнопкой мыши по пути, который содержит удаленный файл, затем захожу в Git и затем нажимаю на Показать историю .

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

Инструменты VCS показывают все изменения, и я вижу все изменения и изменения каждого из них.

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

Затем я выбираю коммиты, которые мой друг удаляет PostAd.jsфайл. теперь смотрите ниже:

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

И теперь я вижу свое желание удалить файл. Я просто дважды щелкаю по имени файла, и оно восстанавливается.

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

Я знаю, что мой ответ - не команды Git, но он быстрый, надежный и простой для начинающих и профессиональных разработчиков. Инструменты WebStorm VCS великолепны и идеально подходят для работы с Git, и для них не нужны никакие другие плагины или инструменты.


1
Это круто! Спасибо. Определенно более простое решение для тех, кто использует любую из сред JetBrains.
Фабиано Арруда

Как нам восстановить файл, если это изображение?
Nodirabegimxonoyim

Уважаемый @FabianoArruda, интегрированные среды разработки JetBrains являются мощными инструментами для разработки. спасибо за ваш прекрасный комментарий.
AmerllicA

Спасибо вам, дорогой @PeterMortensen за издание.
AmerllicA

6

У меня такой же вопрос. Не зная этого, я создал висячий коммит .

Список свисающих коммитов

git fsck --lost-found

Осмотрите каждый висячий коммит

git reset --hard <commit id>

Мои файлы снова появились, когда я перешел на висячий коммит.

git status по причине:

“HEAD detached from <commit id where it detached>”


2
Большое спасибо. Вы помогли мне восстановить тысячи строк кодов.
Рувим

5
user@bsd:~/work/git$ rm slides.tex
user@bsd:~/work/git$ git pull 
Already up-to-date.
user@bsd:~/work/git$ ls slides.tex
ls: slides.tex: No such file or directory

Восстановите удаленный файл:

user@bsd:~/work/git$ git checkout
D       .slides.tex.swp
D       slides.tex
user@bsd:~/work/git$ git checkout slides.tex 
user@bsd:~/work/git$ ls slides.tex
slides.tex

2
Вопрос был о восстановлении файла после того, как он был удален и изменение было зафиксировано. Этот ответ о восстановлении файла, который был удален только в рабочем каталоге.
akaihola

Это правда, и это было то, что я искал.
Hola Soy Edu Feliz Navidad

4

Если вы знаете коммит, который удалил файл (ы), выполните эту команду, где <SHA1_deletion>находится коммит, который удалил файл:

git diff --diff-filter=D --name-only <SHA1_deletion>~1 <SHA1_deletion> | xargs git checkout <SHA1_deletion>~1 --

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


4

Найдите коммит, который удалил ваш файл:

git log --diff-filter=D --oneline -- path/to/file | cut -f -d ' '

Пример вывода:

4711174

Начиная с Git 2.23 фактически есть restoreкоманда. Это все еще эксперимент, но чтобы восстановить что-то, что вы удалили в коммите (в данном случае 4711174), вы можете набрать:

git restore --source=4711174^ path/to/file

Обратите внимание на ^ после идентификатора коммита, так как мы хотим восстановить что-то из коммита до того, который удалил файл.

--sourceАргумент указывает restoreкоманду , где искать файл (ы) для восстановления и может быть любой фиксации и даже индекс.

Смотрите: git-restore doc для git 2.23.0


4

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

Основываясь на превосходном ответе Чарльза Бейли, вот моя строчка:

git co $(git rev-list -n 1 HEAD -- <file_path>)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- <file_path>)~1 head | grep '^D' | cut -f 2)

2

Простой и точный

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

git log 

Допустим, вы нашли $ commitid 1234567 ..., затем

git checkout <$commitid> $fileName

Это восстановит версию файла, которая была в этом коммите.


1

Для лучшего способа сделать это попробуйте.


Сначала найдите идентификатор коммита, который удалил ваш файл. Это даст вам сводку коммитов, которые удалили файлы.

git log --diff-filter = D - резюме

git checkout 84sdhfddbdddf ~ 1

Примечание: 84sdhfddbdddвашcommit id

Благодаря этому вы можете легко восстановить все удаленные файлы.


1

Вы всегда git revertможете сделать свой коммит, который удалил файл. ( Это предполагает, что удаление было единственным изменением в коммите. )

> git log
commit 2994bda49cd97ce49099953fc3f76f7d3c35d1d3
Author: Dave <dave@domain.com>
Date:   Thu May 9 11:11:06 2019 -0700

    deleted readme.md

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

> git revert 2994bd

Сейчас git logпоказывает:

> git log
Author: Dave <dave@domain.com>
Date:   Thu May 9 11:17:41 2019 -0700

    Revert "deleted readme"

    This reverts commit 2994bda49cd97ce49099953fc3f76f7d3c35d1d3.

И readme.mdбыл восстановлен в хранилище.


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

1
Ага! Вы можете сделать последующие коммиты, и все же отменить коммит удаления. Так что если коммит 111 удаляет файл, а коммит 222, 333, 444, добавляет / изменяет вещи, вы все равно можете отменить коммит 111, чтобы отменить удаление, и он станет коммитом 555
Дейв Багданов

0

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

git checkout <file path with name>

Пример ниже работает для меня:

git checkout resources/views/usaSchools.blade.php


Пожалуйста, укажите, в чем проблема
Акбор

Удаление уже зафиксировано. В этом случае вам нужно указать коммит для восстановления.
СБ


-1

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

$ git checkout -- <file>

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

$ git ls-files --deleted

Если удаление было зафиксировано, найдите коммит, где оно произошло, а затем восстановите файл из этого коммита.

$ git rev-list -n 1 HEAD -- <file>
$ git checkout <commit>^ -- <file>

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

$ git log --diff-filter=D --summary

-1

Чтобы восстановить все удаленные файлы с помощью Git, вы также можете сделать:

git checkout $(git ls-files --deleted)

Где git ls-files --deletedперечисляет все удаленные файлы и git checkout $(git command)восстанавливает список файлов в параметре.

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