Знайдіть, коли файл видалено в Git


1034

У мене є сховище Git з n комітами.

У мене є потрібний мені файл, який раніше знаходився в сховищі, і я раптом шукаю і думаю "О! Куди пішов цей файл?"

Чи є (серія) Git команд (и), які підкажуть мені, що "файл true_needed.txt був видалений під час здійснення n-13"?

Іншими словами, не дивлячись на кожну окрему команду і не знаючи, що в моєму репортажі Git є зміна кожного файлу, чи можу я швидко знайти останню команду, яка має цей файл, щоб я міг отримати його назад?



2
Посилання, яким поділився Педро, отримала відповідь на моє запитання: як знайти видалений файл, коли ти не пам’ятаєш шлях.
Гордон Бін

Відповіді:


1126

git log --full-history -- [file path] показує зміни файлу, працюйте, навіть якщо файл був видалений.

Приклад:

git log --full-history  -- myfile

Якщо ви хочете бачити лише останню команду, яка видалила файл, використовуйте додатково -1, наприклад, git log --full-history -1 -- [file path]

Див. Розділ " Коміт", який видалив файл


16
чи можна шукати шаблони? Я забув ціле ім’я файлу = (можливо, можна отримати журнал усіх видалень?
wutzebaer

6
знайшов його тут: stackoverflow.com/questions/6017987 / ...
wutzebaer

6
Зверніть увагу, якщо ви використовуєте PowerShell, дефіси потрібно уникати: git log '-' [шлях до файлу]. Сподіваємось, це може комусь іншому скреготати зубами.
А. Вілсон

68
Мені вдалося здійснити пошук, git log -- */<<filename>>.<<file extension>>не знаючи весь шлях до файлу.
Том Говард

2
Квадратні дужки @MERose є як заповнювач для реального шляху до файлу.
Еміль Бержерон

229

Коротка відповідь:

git log --full-history -- your_file

покаже всі комісії в історії вашого репо, включаючи об'єднання, які торкнулися your_file. Останній (верхній) - той, який видалив файл.

Деякі пояснення:

--full-historyПрапор тут важливий. Без цього Git виконує "спрощення історії", коли ви запитуєте його для журналу файлу. Документи висвітлюють деталі про те, як саме це працює, і мені не вистачає зернистості та сміливості, необхідних для того, щоб спробувати це зрозуміти з вихідного коду, але в документах git-log є багато що:

Режим за замовчуванням

Спрощує історію до найпростішої історії, що пояснює остаточний стан дерева. Найпростіший, оскільки він обрізає деякі бічні гілки, якщо кінцевий результат однаковий (тобто злиття гілок з однаковим вмістом)

Це, очевидно, стосується того, коли файл, історію якого ми хочемо видалити , оскільки найпростіша історія, що пояснює остаточний стан видаленого файлу, не є історією . Чи існує ризик того, що git logбез --full-historyпростого будете стверджувати, що файл ніколи не створювався? На жаль, так. Ось демонстрація:

mark@lunchbox:~/example$ git init
Initialised empty Git repository in /home/mark/example/.git/
mark@lunchbox:~/example$ touch foo && git add foo && git commit -m "Added foo"
[master (root-commit) ddff7a7] Added foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo
mark@lunchbox:~/example$ git checkout -b newbranch
Switched to a new branch 'newbranch'
mark@lunchbox:~/example$ touch bar && git add bar && git commit -m "Added bar"
[newbranch 7f9299a] Added bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git rm foo && git commit -m "Deleted foo"
rm 'foo'
[master 7740344] Deleted foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 foo
mark@lunchbox:~/example$ git checkout newbranch
Switched to branch 'newbranch'
mark@lunchbox:~/example$ git rm bar && git commit -m "Deleted bar"
rm 'bar'
[newbranch 873ed35] Deleted bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git merge newbranch
Already up-to-date!
Merge made by the 'recursive' strategy.
mark@lunchbox:~/example$ git log -- foo
commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log -- bar
mark@lunchbox:~/example$ git log --full-history -- foo
commit 2463e56a21e8ee529a59b63f2c6fcc9914a2b37c
Merge: 7740344 873ed35
Author: Mark Amery 
Date:   Tue Jan 12 22:51:36 2016 +0000

    Merge branch 'newbranch'

commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log --full-history -- bar
commit 873ed352c5e0f296b26d1582b3b0b2d99e40d37c
Author: Mark Amery 
Date:   Tue Jan 12 22:51:29 2016 +0000

    Deleted bar

commit 7f9299a80cc9114bf9f415e1e9a849f5d02f94ec
Author: Mark Amery 
Date:   Tue Jan 12 22:50:38 2016 +0000

    Added bar

Зверніть увагу, як git log -- barна термінальному дампі вгорі виникла буквально відсутність виводу; Git "спрощує" історію до вигадки, де її barніколи не було. git log --full-history -- barз іншого боку, дає нам створений документ barі зобов'язання, яке його видалило.

Щоб було зрозуміло: це питання не просто теоретичне. Я лише заглянув у документи і виявив --full-historyпрапор, оскільки git log -- some_fileне вдався до мене в реальному сховищі, де я намагався відстежувати видалений файл. Спрощення історії іноді може бути корисним, коли ви намагаєтесь зрозуміти, яким чином існуючий на даний момент файл перебуває у його поточному стані, але при спробі відстежити видалення файлів , швидше за все, вас перекрутить, приховавши зобов’язання, про яке ви дбаєте. . Завжди використовуйте --full-historyпрапор для цього випадку використання.


4
Зауважте, що це лише пошукова історія, що стосується поточної гілки (а не "вся історія репо"), тобто, якщо файл ще не видалений у поточній гілці, але він був у іншій гілці, цей запис не знайде. Ви повинні знаходитись у будь-якій гілці, де файл уже видалено . Можливо, це очевидно, коли думаєш про це, але спочатку мене це вигнало.
Анентроп

1
Ця відповідь працює. Але з самого git logвиводу, зовсім не очевидно, що останній комірок видалив файл. Я також спробував git log --name-status --full-history -- file_nameі git log -p --stat --full-history -- file_name, але жодним чином прямо не вказує, що файл видалено в останньому комітеті. Це здається помилкою.
Martin_W

@Martin_ATS mkdir somedir && cd somedir && git init && touch foo && git add foo && git commit -m "Added foo" && git checkout -b newbranch && touch bar && git add bar && git commit -m "Added bar" && git checkout master && git rm foo && git commit -m "Deleted foo" && git checkout newbranch && git rm bar && git commit -m "Deleted bar" && git checkout master && git merge newbranch && git log --name-status --full-history -- barвключає D barі A barдля мене в журнал вихід з Git 2.12.2. Ви не бачите цих рядків у висновку? Яка у вас версія?
Марк Амері

git version 2.15.1Так, ваша командна послідовність звітує D barі A bar. Можливо, моє питання стосується історії мого файлу. Я простежував історію .htaccessфайлу, який отримав gitignore'ed та видалений. Нарешті я зрозумів це і додав файл назад. Коли я включаю --name-statusв git logкоманду, я бачу два A .htaccessзаписи (оскільки я додав її ще в останньому комітеті), але ні D .htaccess. Тож здається, що в деяких випадках, навіть якщо файл був вилучений із сховища, git logявний D file_nameзапис не відображатиметься .
Martin_W

@Martin_ATS Цікаво. Цікаво, чи, можливо, .htaccessдодано в команду X, але потім вони не включені в комісію злиття, яка принесла X до master? Це єдине, що я можу придумати, що я міг би стверджувати, що повинен виглядати як файл, доданий і ніколи не видалений, і все ще не присутній. Було б цікаво спробувати розібратися в MCVE, а потім з'ясувати, чи це помилка Git, а якщо ні, чи можна підробити мою відповідь, щоб обробити ваш випадок.
Марк

84

Журнал Git, але вам потрібно встановити префікс --

Наприклад:

dan-mac:test dani$ git log file1.txt
fatal: ambiguous argument 'file1.txt': unknown revision or path not in the working tree.

dan-mac:test dani$ git log -- file1.txt
 commit 0f7c4e1c36e0b39225d10b26f3dea40ad128b976
 Author: Daniel Palacio <danpal@gmail.com>
 Date:   Tue Jul 26 23:32:20 2011 -0500

 foo

31

Я щойно додав тут рішення (чи є спосіб у git перелічити всі видалені файли у сховищі?) Для пошуку комітетів видалених файлів за допомогою regexp:

git log --diff-filter=D --summary | sed -n '/^commit/h;/\/some_dir\//{G;s/\ncommit \(.*\)/ \1/gp}'

Це повертає все видалене в каталозі з назвою some_dir(каскадно). Будь-який sed regexp там, де \/some_dir\/є, буде робити.

OSX (завдяки @triplee та @keif)

git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }

1
Приємно. Деякі невідповідність башу в OS X:sed: 1: "/^commit/h;/\/some_dir\ ...": bad flag in substitute command: '}'
Brent Faust

@BrentFoust Це ясна річ, я не можу перевірити, що ... спробуйте додати пробіл в кінці (після дужок, але перед єдиною цитатою), сторінка man в Інтернеті про це не зрозуміла ...
estani

Приємна пропозиція. Але додавання пробілу перед єдиною цитатою не допомогло. Не було місця перед закриттям дужки.
Brent Faust

1
BSD / OSX sed, мабуть, не завжди хороший з крапками з комою як роздільниками команд. Спробуйте змінити їх на нові рядки або переключитися наsed -n -e '/^commit/h' -e '\:/some_dir/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
трійчатко

1
Я перевірив це і git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }працював на мені на OSX.
keif

21

Ви можете знайти останню команду, яка видалила файл наступним чином:

git rev-list -n 1 HEAD -- [file_path]

Додаткову інформацію можна отримати тут


11
Первинне схвальне рішення не працювало для мене, але це було.
Нік Хайнер

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.