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


2,256

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

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

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

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

+229

'$ мерзавец фотографии deletedFile', никто не ясно заявил об этом ?! Ответ на заголовок для будущих googlers ... 15 дек. 112011-12-15 15:23:18

+27

Обратите внимание, что предыдущий комментарий отвечает на вопрос в заголовке, а не в теле, который включает в себя поиск _when_ файл был удален. 16 дек. 112011-12-16 16:02:10

+5

Чтобы найти фиксацию, файл был удален в: 'git log --diff-filter = D - path/to/file' 16 мар. 122012-03-16 21:28:02

  0

Связанный: [Как вы отбрасываете неустановленные изменения в git?] (Http: // stackoverflow. ком/кв/52704/456814). 28 апр. 142014-04-28 05:16:56

  0

Связанный: [Как найти удаленный файл в истории фиксации] (http://stackoverflow.com/questions/7203515/how-to-locate-a-deleted-file-in-the-commit-history) 03 ноя. 142014-11-03 14:14:47

  0

Связанный : [как восстановить все удаленные файлы сразу] (http://stackoverflow.com/questions/21820507/how-do-i-reset-all-deleted-files/) 20 дек. 142014-12-20 21:50:04

  0

@hhh не работает, я получаю ошибку: pathspec ./src/main/resources/file 'не соответствует ни одному файлу, известному git. 26 май. 162016-05-26 08:39:17

+22

@hhh 'git checkout deletedFile' удалит' deletedFile', если он был удален, но это удаление * еще не было выполнено или не выполнено *.Это не тот вопрос, который здесь задают; этот вопрос касается того, как восстановить файл, чье удаление было совершено за последнее время. 15 апр. 172017-04-15 10:33:57

  0

Я отказываюсь голосовать за этот вопрос на том основании, что он был поклонником Rush. 2112 по-прежнему v. 26 сен. 172017-09-26 16:19:34

2,583

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

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

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

git checkout <deleting_commit>^ -- <file_path> 

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

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

Сложный бит - это проверить фиксацию ПЕРЕД, используя суффикс ^. Благодарю. 26 апр. 102010-04-26 14:40:13

+25

Что такое^в конце? 22 апр. 112011-04-22 19:32:15

+18

@ Ranman: Это означает «первый родитель». 22 апр. 112011-04-22 19:38:10

+3

По какой-то причине это не будет работать в zsh. '± мерзавец Checkout $ (мерзавец преподобным список -n 1 ГОЛОВА "спецификации/Sporkfile_example.rb") не^- "Спецификация/Sporkfile_example.rb" ЗШ найдено ни одного совпадения: b71c152d8f38dcd23ad7600a93f261a7252c59e9 ^' Я переключился но он работал нормально. 28 фев. 122012-02-28 03:45:55

+1

По какой-то причине мне пришлось использовать git checkout <deleting_commit> ^^ - <file_path> - возможно, в командной строке Windows 16 май. 122012-05-16 17:17:19

+14

Из командной строки Windows я получил ошибку. 'error: pathspec <filename> не соответствует ни одному файлу, известному git.'. Решение заключалось в использовании git bash. 26 июл. 122012-07-26 18:07:37

+45

@zoras ЗШ имеет собственное расширение на «^» Я верю, но вы можете использовать альтернативный синтаксис «~ 1»: 'GIT проверку <удаление фиксации> ~ 1 - <file-path>' ~ X позволяет чтобы указать, что X совершает транзакции до указанного коммита, поэтому ~ 1 - это фиксация до, ~ 2 - две коммиты до и т. д. 10 сен. 122012-09-10 15:07:45

+2

@zoras Вы также можете избежать^с \^ 16 янв. 132013-01-16 19:32:12

  0

Кстати, если вы используете рыбу, эта команда сломается и требует побегов или просто запускает оболочку bash. 10 июн. 132013-06-10 22:58:14

  0

Клиент командной строки github на основе Powershell ответил фатальным: неоднозначный аргумент <file_path>: неизвестная версия или путь не в рабочем дереве. Используйте «-», чтобы отделить пути от изменений, например: 'мерзавец <command> [<revision> ...] - [<file> ...] Ответ @Robert Мунтяну работал для меня. 11 июн. 132013-06-11 07:40:13

  0

@ Нильс Лукстон благодарит. по какой-то причине нотация карата не работала для меня, когда на платформе win7, но тильда. 09 фев. 142014-02-09 20:03:08

  0

Стоит отметить, что синтаксис одной строки не работает под Windows.Поскольку это очень популярное Q & A (и первая запись в Google), предложит обновить ответ, чтобы сказать это (+ заменен на ~ 1, упомянутый выше). 26 фев. 142014-02-26 09:57:03

  0

Что такое двойные тире, '--', для? 11 сен. 142014-09-11 20:38:04

+3

В командной строке windows cmd символ '^' является символом escape! Поэтому на cmd вам нужно набрать '^^', чтобы сообщить cmd, что вы хотите один литерал ^, и что вы не избегаете что-то еще после него. То, что происходит со многими людьми, заключается в том, что за '^' следует пробел. Итак, cmd думает, что вы избегаете пространства, которое дает просто пространственный символ. Таким образом, к моменту, когда git получает аргументы cli, он видит 'SHA1' и ** не **' SHA1^'. Это очень раздражает. '~' не является escape-символом, поэтому он все еще работает. (PS, если вы думаете, что googlers захотят эту информацию, пожалуйста, воздержитесь от этого комментария) 18 сен. 152015-09-18 14:48:29

  0

большое спасибо, все еще не понимаем, почему мы проверяем родителя коммита? что значит быть родителем фиксации? 11 мар. 162016-03-11 09:28:24

  0

Что делать, если в commit было удалено несколько файлов, и я хочу получить их все? просто повторите команду несколько раз? 23 июл. 162016-07-23 20:59:45

+1

@MuhammadBabar Родитель фиксации, удаляющий нужный файл, является последним фиксатором, содержащим этот файл, поэтому гарантированно будет самой последней версией этого файла. «Родительская» номенклатура проистекает из структурирования коммитов git в ** [дерево] (http://www.gitguys.com/topics/git-object-tree) **, в котором commits являются узлами, а commit предшествует другому commit - это родительский узел этого дочернего узла. (_Compsci 101, вы преследуете меня по-прежнему.) 19 авг. 162016-08-19 00:40:29

  0

Для кого-то другого, интересующегося двойными тире, '--', это отделить имена файлов от git-опций. Это не строго необязательно, если вы не укажете имена файлов, которые действительно выглядят как git. 19 май. 172017-05-19 10:22:36

  0

Эта третья команда является ключом. Благодаря! 15 июн. 172017-06-15 23:38:07

+2

Опасайтесь, что вы должны запустить это из корня вашего репозитория, потому что иначе путь к файлу не будет распознан. Я трачу несколько минут, пока не понял, что я нахожусь во вложенной папке, поэтому это не работает. 10 ноя. 172017-11-10 10:44:23

  0

Вторая команда бросает мне абсурдную ошибку «не удалось создать файл www/index.php: Permission denied», хотя я запускаю консоль в качестве администратора. 13 фев. 182018-02-13 16:58:47


80

Если вы с ума сошли, используйте 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]''? 04 июн. 092009-06-04 22:53:15

  0

Вы также можете использовать хорошее и плохое вручную, если это невозможно проверить автоматически. См. Страницу управления bisect. 04 июн. 092009-06-04 23:00:28

+1

Это не самое простое решение, но оно впечатляет. Спасибо, что написали. 05 июн. 092009-06-05 15:19:37

+1

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


687
  1. Использование git log --diff-filter=D --summary для получить все коммиты, которые удалили файлы и удалили файлы;
  2. Чтобы восстановить удаленный файл, используйте git checkout $commit~1 filename.
+3

Любопытно, к чему относится ~ 1? 22 июл. 112011-07-22 17:41:45

+4

@tommy - спецификация тильды даст вам n-й внук именованного коммита. Подробнее см. Http://book.git-scm.com/4_git_treeishes.html. 23 июл. 112011-07-23 12:00:07

+4

Это самый простой и интуитивно понятный подход. 'git log - * PartOfMyFileName *'. Спасибо за '$ commit ~ 1' 10 апр. 132013-04-10 23:25:05

  0

Я не знаю, что такое' $ commit', вместо этого я использовал 'HEAD'. Однако это сработало для меня. 04 ноя. 132013-11-04 13:50:44

+2

Синтаксис 'git checkout $ commit ~ 1 filename' отлично подходит для отдельных файлов, а также работает для целых каталогов. т.е.: восстановить все удаленные изображения в ./images от sha 12345: 'git checkout 12345 ~ 1 images'. спасибо за этот ответ! 03 июн. 142014-06-03 04:59:25

+26

@Alexar '$ commit ~ 1' означает, что вы должны добавить имя фиксации. Что-то вроде '1d0c9ef6eb4e39488490543570c31c2ff594426c', где' $ commit' есть. 07 апр. 152015-04-07 06:27:35

  0

@Eugene Не должен ли идентификатор commit быть первым до того, как файл был удален? Кажется, если вы поместили идентификатор фиксации, который файл был удален в git, его не найти. 16 окт. 172017-10-16 12:04:57

+1

Гораздо более общий и полезный, чем принятый ответ 29 ноя. 172017-11-29 21:06:17

+1

Если вам просто нужно проверить содержимое файла: 'git show $ commit ~ 1: $ path' 21 дек. 172017-12-21 13:32:32


293

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

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

Куда отправляются файлы? Я не вижу никаких изменений. 13 сен. 132013-09-13 14:36:43

+13

Это, наверное, самый простой способ. Его извращенный, как сложный «git» сделал даже самую простую задачу. 19 окт. 132013-10-19 04:12:44

  0

какие средства -? 14 апр. 152015-04-14 20:46:24

  0

git checkout - [файл] вернет изменения в [файл]. Труба заменит [файл] на имя удаленных файлов. 17 апр. 152015-04-17 13:02:57

  0

именно то, что я хотел, восстанавливает все файлы в репозитории 19 янв. 172017-01-19 18:38:23

  0

Подкоманда 'ls-files' удобна, но, похоже, не работает для файлов, которые были удалены с помощью' git rm', т.е. поставленных, не говоря уже о том, что и требовал ОП. 17 ноя. 172017-11-17 08:47:28


19

У меня есть this solution.

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

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

commit bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Author: Alexander Orlov Date: Thu May 12 23:44:27 2011 +0200

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

commit 3ea4e3af253ac6fd1691ff6bb89c964f54802302 Author: Alexander Orlov Date: Thu May 12 22:10:22 2011 +0200

. Теперь, используя совершить bfe68bd117e1091c96d2976c99b3bcc8310bebe7 ID сделать:

git checkout bfe68bd117e1091c96d2976c99b3bcc8310bebe7^1 yourDeletedFile.java 

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

  0

Это тот же подход, что и принятый ответ, но с некоторыми другими способами найти удаление фиксации. Мне все еще нравится подход, принятый в принятом ответе, но это хорошие альтернативы. Благодаря! 14 мар. 122012-03-14 10:22:13

  0

Я предполагаю, что сначала проверяю удаленный файл, а затем (не меняя его), совершая его, не создает * копию * файла. Правильно? (Мне нужно сделать это с изображениями, а копии сделают репозиторий больше) 14 июл. 162016-07-14 09:22:00


2

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

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

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) 

27

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

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

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

+1

Это не сработало для меня. После проверки я получил 'error: pathspec 'foo' не соответствует ни одному файлу, известному git.' Я убедился, что имя файла верное. Git версия 2.7.0 27 фев. 162016-02-27 01:35:14

  0

-1; это не верно. Эти команды отменит удаление, которое * еще не было зафиксировано * (первый отключает удаление, если он поставлен, а второй отбрасывает неустановленные изменения в файл), но вы утверждаете, что они будут восстанавливать a * commit * удаление файла, что просто неверно и с ошибкой, как в комментарии к @ wisbucky выше. 15 апр. 172017-04-15 11:10:27

  0

@MarkAmery Действительно, я думаю, что эта команда хорошо зарекомендовала себя для тех разработчиков, которые не сделали явной постановки для фиксации удаленных файлов с помощью 'git add -A', но так, чтобы восстановленный файл все еще находился в незастроенной стадии. 20 апр. 172017-04-20 17:08:36


9

Во многих случаях может быть полезно использовать 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 

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

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


3
[email protected]bsd:~/work/git$ rm slides.tex 
[email protected]:~/work/git$ git pull 
Already up-to-date. 
[email protected]:~/work/git$ ls slides.tex 
ls: slides.tex: No such file or directory 

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

[email protected]:~/work/git$ git checkout 
D  .slides.tex.swp 
D  slides.tex 
[email protected]:~/work/git$ git checkout slides.tex 
[email protected]:~/work/git$ ls slides.tex 
slides.tex 
+1

Вопрос состоял в том, чтобы восстановить файл после его удаления и внесено изменение. Этот ответ касается восстановления файла, который был удален только в рабочем каталоге. 06 авг. 132013-08-06 11:25:27

  0

Это правда, и это было то, что я искал. 18 фев. 142014-02-18 15:47:30


58

Мой новый любимый псевдоним, основанный на bonyiii «s answer (upvoted), и мой собственный ответ о "Pass an argument to a Git alias command":

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 

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


Robert Dailey предлагает in the comments следующий псевдоним:

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

И jegan добавляет in the comments:

For setting the alias from the command line, I used this command:

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

Это восстанавливает всю фиксацию, а не только запрашиваемый файл. 28 май. 132013-05-28 17:18:32

+5

Вот мой псевдоним, прекрасно работает: 'restore-file =! Git checkout $ (git rev-list -n 1 HEAD -« $ 1 »)^-« $ 1 »' 12 мар. 142014-03-12 22:27:14

  0

@RobertDailey Это выглядит великолепно!Я включил ваш псевдоним в ответ для большей видимости. 13 мар. 142014-03-13 07:50:23

  0

git: 'restore' не является командой git. 02 ноя. 162016-11-02 02:50:29

  0

@resultsway было бы, если бы вы сначала установили 'git config alias.restore'. 02 ноя. 162016-11-02 07:09:01

+1

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

  0

@jegan Спасибо. Я включил ваш комментарий в ответ для большей наглядности. 10 окт. 172017-10-10 06:04:15

  0

'Не удалось выполнить расширение псевдонима 'restore'; '! git' не является командой git' 30 окт. 172017-10-30 08:57:58

  0

@KarlMorrison В каком OS/shell вы печатаете это? 30 окт. 172017-10-30 09:22:03

  0

Mac (Bash)! Ifyou просто поместил некоторую информацию, чтобы это было Linux-решение, было бы неплохо ^^ 30 окт. 172017-10-30 09:40:26

  0

@KarlMorrison Не уверен, но вы можете хотя бы отредактировать глобальную конфигурацию git (git config -global -edit) и вставить в нее строку 'restore =! git checkout $ (git rev-list -n 1 HEAD -" $ 1 ")^-" $ 1 "' 30 окт. 172017-10-30 12:03:57


13
git checkout /path/to/deleted.file 
+7

Не будет работать с момента делегирования удаления. 06 авг. 132013-08-06 11:24:06

  0

Этот вариант для моей ситуации (удаленный непреднамеренно) был самым простым решением. 07 апр. 152015-04-07 14:06:22


4

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

файлы были добавлены в

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, мне нужно было восстановить файлы, но затем отключить их. У меня было сотни файлов для восстановления, набирая вещи вручную для каждого файла, так как в других примерах было слишком медленно.


91

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

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

+1

Спасибо. Меня устраивает. Восстановите файл, который я еще не совершил. 11 июл. 172017-07-11 18:21:46

  0

Это простейший и лучший. 27 ноя. 172017-11-27 06:27:37


2

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

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

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


32

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

Список всех фиксаций для этого файла.

git log -- path/to/file 

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

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

Да. Это было правильное решение для меня! Простой и понятный 06 июн. 172017-06-06 07:12:34

  0

Просто использовал это решение и не было фиксации для удаления. Однако мне удалось восстановить файл, используя последний код фиксации. 26 сен. 172017-09-26 16:18:37

  0

+10 для разъяснения 'second to last commit'! 29 ноя. 172017-11-29 21:03:15


19

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

git checkout -- . 

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

git checkout <file_path> 

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


8

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 checkout этого пути файла от последнего коммита, где этот файл существует. source


1

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

Список оборванных совершает

git fsck --lost-found

Осмотрите каждый оборванных фиксации

git reset --hard <commit id>

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

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

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


-1
$ git log --diff-filter=D --summary | grep "delete" | sort