Знайдіть і відновіть видалений файл у сховищі 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, і це справно працювало.
зорі

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 це два коміти раніше тощо
Nils Luxton

22
У Windows cmd-підказці ^персонаж є символом втечі! Тому на cmd вам потрібно набрати, ^^щоб сказати cmd, що ви хочете отримати один буквальний ^, і щоб ваш не вийшов із чогось іншого після нього. Що відбувається з багатьма людьми, це те, що за ^ним іде пробіл. Тож cmd вважає, що ви уникаєте простору - що дає просто космічний характер. Таким чином, до моменту, коли git отримує аргументи cli, він бачить SHA1і ні SHA1^ . Це дійсно дратує. ~не є символом втечі, тому це все ще працює. (PS. Якщо ви думаєте, що googlers захоче цю інформацію, будь ласка, підкажіть цей коментар)
Alexander Bird

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

Де $commitзначення комітету, яке ви знайшли на кроці 1, наприкладe4cf499627


10
цікаво, на що посилається ~ 1?
tommy chheng

7
@tommy - специфікація тильди подарує вам п’ятого онука названої комітки. Докладніше див. У розділі book.git-scm.com/4_git_treeishes.html .
Роберт Мунтяну

5
це, безумовно, найпростіший та інтуїтивний підхід. git log -- *PartOfMyFileName*. Дякую за$commit~1
bgs

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

34
@Alexar $commit~1означає, що ви повинні додати ім'я коміту . Щось на зразок 1d0c9ef6eb4e39488490543570c31c2ff594426cде $commitє.
Євген

319

Щоб відновити всі видалені файли в папці, введіть наступну команду.

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

1
Куди потрапляють файли? Я не бачу змін.
Вільям Гранд

21
Це, мабуть, найпростіший метод. Його збочення, як важко git, зробило навіть найпростіше завдання.
jww

git checkout - [файл] відновить зміни у [файлі]. Труба замінить [файл] назвою видалених файлів.
Ману

6
ls-filesСуб-команда під рукою, але , здається, не працює для файлів , які були видалені з git rmт в постановці, НЕ кажучи вже зробив, що і запитав ОП.
MarkHu

Це спрацьовувало для відновлення видалених файлів, але як я можу оновити файли, які там змінилися та з'являлися як M myChangedFileпісля git checkout?
libby

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 в іншому випадку. Команда "запустити" 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 автоматизувати поділ, запустивши команду після слова 'run', де команда повинна повернутися 0для goodверсії (див. git help bisectподробиці). Це '[ -e foo.bar ]'типовий вираз для тестування, якщо файл foo.barіснує (реалізація зазвичай знаходиться у файлі, /usr/bin/[який зазвичай є жорстким посиланням /usr/bin/test), і єдині знаки запитання використовуються для того, щоб поставити це все як аргумент єдиного командного рядка.
Мікко Ранталайнен

Чудова ідея. Я спробував такий підхід, і він визначив зобов’язання перед видаленням, але не зобов'язання, яке фактично видалило файл. І в іншому тесті він виявив 2 коміти до видалення.
Михайло Ософський

Божевільний? Можливо. Але бісект - це чудовий спосіб допомогти знайти, де введена помилка, і тому в будь-якому разі це цінне вміння навчитися. Тож, хоча, можливо, це не "правильний" чи найбільш "правильний" спосіб, це все-таки хороша ідея і однозначно коштує +1!
Прифтан

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

@ 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. Ви повинні отримати щось на кшталт:

зробити bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Автор: Олександр Орлов Дата: Чт 12 травня 23:44:27 2011 +0200

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

зробити 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
Це круто! Дякую. Однозначно простіше рішення для тих, хто використовує будь-який IDE JetBrains.
Фабіано Арруда

Як відновити файл, якщо це зображення?
Nodirabegimxonoimim

Шановний @FabianoArruda, IDE 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-Resto 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 

Скажіть, ви знайдете $ коміюючи 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, щоб скасувати видалення, і воно стане
5555

0

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

git checkout <file path with name>

Наведений нижче приклад працює для мене:

git checkout resources/views/usaSchools.blade.php



Видалення вже було здійснено. У цьому випадку потрібно вказати зобов’язання відновити.
sba


-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.