Найти, когда файл был удален в Git

1035

У меня есть Git-репозиторий с n коммитами.

У меня есть файл, который мне нужен, и который раньше находился в хранилище, и который я вдруг ищу и думаю: «О! Куда этот файл пошел?»

Есть ли (серия) команда (-ы) Git, которая сообщит мне, что «файл действительно_needed.txt был удален при коммите n-13»?

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

JohnMetta
источник
5
Возможный дубликат Как найти удаленный файл в истории коммитов?
Педро Роло
2
Ссылка, которую поделил Педро, содержала ответ на мой вопрос: как найти удаленный файл, если вы не помните путь.
Гордон Бин

Ответы:

1127

git log --full-history -- [file path] показывает изменения файла, работают, даже если файл был удален.

Пример:

git log --full-history  -- myfile

Если вы хотите видеть только последний коммит, который удалил файл, используйте -1, например, git log --full-history -1 -- [file path]

См. Какой коммит удалил файл

vogella
источник
16
Можно ли искать шаблоны? Я забыл полное имя файла = (может быть, можно получить журнал всех удалений?
wutzebaer
6
нашел его здесь: stackoverflow.com/questions/6017987/…
wutzebaer
6
Обратите внимание: если вы используете PowerShell, дефисы должны быть экранированы: git log '-' [путь к файлу]. Надеюсь, это может кто-то еще скрежетать зубами.
А. Уилсон
68
Я смог выполнить поиск, git log -- */<<filename>>.<<file extension>>не зная полного пути к файлу.
Том Ховард,
2
@ Более квадратные скобки используются в качестве заполнителя для реального пути к файлу.
Эмиль Бержерон
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
Обратите внимание, что при этом выполняется поиск только в истории, относящейся к текущей ветви (не «всей истории репо») ... т.е., если файл еще не удален в текущей ветви, но был в другой ветви, он не найдет коммит удаления. Вы должны быть в любой ветке, где файл уже был удален . Может быть, это очевидно, когда я думаю об этом, но поначалу меня это застало.
Anentropic
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 перечислить все удаленные файлы в хранилище?) Для поиска коммитов удаленных файлов с помощью регулярного выражения:

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 }
estani
источник
1
Ницца. Некоторые несоответствия под Bash в OS X:sed: 1: "/^commit/h;/\/some_dir\ ...": bad flag in substitute command: '}'
Брент Фауст
@BrentFoust Жаль, что я не могу это проверить ... попробуйте добавить пробел в конце (после фигурных скобок, но до одинарной кавычки), страница руководства в Интернете не совсем ясно об этом ...
estani
Хорошее предложение. Но добавление пробела перед одинарной кавычкой не помогло. Также не было пробела перед закрывающей скобкой.
Брент Фауст
1
BSD / OSX sed, видимо, не всегда хороши с точками с запятой в качестве разделителей команд. Попробуйте изменить их на новые строки или переключиться наsed -n -e '/^commit/h' -e '\:/some_dir/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
tripleee
1
Я проверял это и git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }работал для меня на OSX.
Кейф
22

Вы можете найти последний коммит, который удалил файл, следующим образом:

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

Дополнительная информация доступна здесь

Акиф
источник
11
Первичное решение по голосованию не сработало для меня, но это сработало.
Ник
0

Пытаться:

git log --stat | grep file
Эрик Вудрафф
источник