Как получить отдельный файл из определенной ревизии в Git?

У меня есть репозиторий Git, и я хотел бы посмотреть, как выглядели некоторые файлы несколько месяцев назад. Я нашел ревизию на тот момент; это 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8. Мне нужно посмотреть, как выглядит один файл, а также сохранить его как («новый») файл.

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

git-show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8 my_file.txt

Однако эта команда показывает разницу, а не содержимое файла. Я знаю, что позже могу использовать что-то вроде PAGER=cat и перенаправить вывод в файл, но я не знаю, как добраться до фактического содержимого файла.

В основном я ищу что-то вроде svn cat.


person Milan Babuškov    schedule 04.03.2009    source источник
comment
Ключ здесь: git show (бесполезно) использует другой синтаксис с двоеточием. git show 2c7cf:my_file.txt   -  person Steve Bennett    schedule 30.05.2012
comment
Чтобы уточнить, приведенная выше команда просит git показать два отдельных объекта, ревизию и файл. В принятом ниже ответе, в котором между двумя элементами используется двоеточие, запрашивается конкретный файл определенной версии.   -  person jhclark    schedule 09.07.2012
comment
В * nix вам не нужен PAGER, просто перенаправление вывода оболочки с помощью >   -  person Konstantin Pelepelin    schedule 20.11.2013
comment
возможный дубликат Is есть ли быстрая команда git, чтобы увидеть старую версию файла?   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 10.09.2014
comment
У Checat есть важный комментарий для тех, кто хочет экспортировать контент в какой-нибудь файл. Вам понадобится что-то вроде этого: git show {sha}: my_file.txt ›old_my_file.txt   -  person ormurin    schedule 18.09.2014
comment
Судя по названию вопроса, это кажется правильным ответом. Судя по остальному содержанию вопроса, этот ответ кажется наиболее полезным. Мне потребовалось несколько минут, чтобы понять, что эти две вещи кажутся довольно разными, надеюсь, этот комментарий сэкономит время другим.   -  person rbatt    schedule 05.12.2015
comment
@rbatt Нет, больше нет: это будет (сегодня, 2019) git restore, а не git checkout. См. мой отредактированный ответ.   -  person VonC    schedule 06.11.2019
comment
Если вы ищете ответ о получении всех файлов в подкаталоге в версии истории Git, прочтите это: stackoverflow.com/a / 62342847/94148   -  person aleung    schedule 12.06.2020


Ответы (11)


Использование git show

Чтобы завершить свой собственный ответ, синтаксис действительно

git show object
git show $REV:$FILE
git show somebranch:from/the/root/myfile.txt
git show HEAD^^^:test/test.py

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

  1. имя ветки (как предложено ясень)
  2. HEAD + x количество ^ символов
  3. Хеш SHA1 данной ревизии
  4. Первые несколько (возможно, 5) символов данного хэша SHA1

Совет. Важно помнить, что при использовании git show всегда указывайте путь от корня репозитория, а не текущую позицию в каталоге.

(Хотя Майк Морарти упоминает, что, по крайней мере, с git 1.7.5.4, вы можете указать относительный путь, поместив ./ в начале пути. Например:

git show HEAD^^:./test.py

)

Использование git restore

В Git 2.23+ (август 2019 г.) вы также можете использовать git restore < / strong>, который заменяет запутанную команду git checkout

git restore -s <SHA1>     -- afile
git restore -s somebranch -- afile

Это восстановит в рабочем дереве только файл, который присутствует в исходном (-s) коммите SHA1 или ветке somebranch.
Чтобы восстановить также индекс:

git restore -s <SHA1> -SW -- afile

(-SW: сокращение от --staged --worktree)


Как указано в комментарии от starwarswii

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

Например. ты можешь сделать:

git show 1234:path/to/file.txt > new.txt 
git show 1234~:path/to/file.txt > old.txt

затем сравните их.


Использование низкоуровневых сантехнических команд git

До git1.5.x это было сделано с помощью сантехники:

git ls-tree <rev>
показать список из одного или нескольких объектов blob в коммите

git cat-file blob <file-SHA1>
cat файл, поскольку он был зафиксирован в определенной ревизии (аналогично svn cat). используйте git ls-tree, чтобы получить значение заданного файла-sha1

git cat-file -p $(git-ls-tree $REV $file | cut -d " " -f 3 | cut -f 1)::

git-ls-tree перечисляет идентификатор объекта для $file в ревизии $REV, он вырезается из вывода и используется в качестве аргумента для git-cat-file, который на самом деле должен называться git-cat-object, и просто выгружает этот объект в stdout.


Примечание. Начиная с Git 2.11 (4 квартал 2016 г.), вы можете применять фильтр содержимого к выводу git cat-file.

См. совершить 3214594, совершить 7bcf341 (9 сентября 2016 г.), совершить 7bcf341 (09 сентября 2016 г.) и зафиксировать b9e >, совершить 16dcc29 (24 августа 2016 г.) Johannes Schindelin (dscho).
(Объединено Junio ​​C Hamano - gitster - в commit 7889ed2, 21 сентября 2016 г.)

git config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <"
git cat-file --textconv --batch

Примечание: git cat-file --textconv недавно начал segfaulting (2017 г.), который был исправлен в Git 2.15 (4 квартал 2017 г.)

См. commit cc0ea7c (21 сентября 2017 г.) от Джефф Кинг (peff).
(Объединено с помощью Junio ​​C Hamano - gitster - в совершить bfbc a>, 28 сен 2017)

person VonC    schedule 04.03.2009
comment
Как вы сохраняете то, что возвращает git show? По сути, я хочу сохранить его вместо перезаписи незафиксированного локального файла. - person Oscar; 26.06.2012
comment
Без перенаправления нет пути? - person utapyngo; 01.02.2013
comment
@utapyngo я не знаю. - person VonC; 01.02.2013
comment
git checkout [branch | revision] filepath - правильная команда - person Gaui; 06.06.2013
comment
@Gaui, но git checkout заменит ваш файл другой версией, в отличие от git show, что позволяет сохранить его под другим именем, чтобы вы могли получить и увидеть оба (текущая версия и старая версия). Из вопроса неясно, хочет ли OP заменить свою текущую версию на старую. - person VonC; 06.06.2013
comment
Я хотел бы отметить, что ^^^ можно также записать в более общем виде как ~~~ или, лучше, ~3. Использование тильд также имеет то преимущество, что не запускает сопоставление имен файлов некоторых оболочек (например, zsh). - person Eric O Lebigot; 19.06.2013
comment
У меня нет достаточно старого git, чтобы проверить: обрабатывает ли pre-1.5.x git rev-parse синтаксис rev:path? (В более позднем git вы можете git cat-file -p $REV:path. Однако git show работает и для путей к каталогам, поэтому он не просто короче, а обычно ближе к тому, что нужно.) - person torek; 12.01.2014
comment
git show выдал результат, похожий на diff, который вопрос конкретно не хотел. Есть ли более чистый способ, чем git checkout, который перезаписывает файл и изменяет версию, на которую указывает локальный репозиторий? - person dolphus333; 02.07.2017
comment
@ dolphus333 Вы пробовали git show? (git-scm.com/docs/git-show): он показывает содержимое файла, а не вывод, похожий на diff. - person VonC; 02.07.2017
comment
@ dolphus333 То есть, когда вы используете git show с именем файла, он показывает полное содержимое файла. При использовании только с фиксацией он показывает разницу. - person VonC; 03.07.2017
comment
Я использовал git show hash filename (относительный путь), git show hash filename (полный путь), затем, чтобы быть уверенным, что перешел в каталог, в котором находился файл, и использовал git show hash filename (в каталоге). Все три версии вернули один и тот же результат: вывод diff, а не файл. - person dolphus333; 03.07.2017
comment
@ dolphus333 Интересно. Задайте отдельный вопрос с указанием точных набранных команд Git и используемой версии Git: это позволит другим пользователям видеть, что происходит. - person VonC; 04.07.2017
comment
@ dolphus333 Надеюсь, вы уже решили эту проблему, но для справки git show FileName выдает вывод, похожий на diff, но git show HEAD:FileName должен выдавать зафиксированное содержимое файла. - person jmster; 25.10.2017
comment
Это невероятно сложно по сравнению с git checkout [branch | revision] filepath. - person Andrew Koster; 14.11.2019
comment
@AndrewKoster Нет, после тестирования git restore я нахожу его более интуитивным, чем git checkout, который работает с файлами и ветками. Здесь восстановление предназначено только для восстановления файлов. - person VonC; 14.11.2019
comment
В качестве очень конкретного примера, в хранилище паролей вы можете просмотреть предыдущую версию пароля, который вы только что отредактировали, запустив pass git show HEAD~1:yourpasswordname.gpg - person Ale; 07.07.2020
comment
Зачем вообще заморачиваться с `-` в примерах git restore? - person bloody; 27.11.2020
comment
@bloody, чтобы отделить параметр от аргументов пути. Хорошая привычка. Я задокументировал это 11 лет назад: stackoverflow.com/a/1192194/6309 - person VonC; 27.11.2020
comment
Что восстанавливает индекс файла? - person Timo; 06.01.2021
comment
@Timo git restore -s <SHA1> -SW -- afile восстановит и рабочее дерево, и индекс, что означает, что это похоже на восстановление файла, и сделает git add -- file, чтобы добавить то, что вы только что восстановили, в индекс, готовый к фиксации. - person VonC; 06.01.2021
comment
+1 к git show. Он позволяет вам передать содержимое в файл, что отлично, если вы хотите просто быстро сравнить файлы из фиксации. например вы можете сделать: git show 1234:path/to/file.txt > new.txt git show 1234~:path/to/file.txt > old.txt, а затем сравнить их. - person Starwarswii; 01.07.2021
comment
@Starwarswii Хорошее замечание, спасибо. Я включил ваш комментарий в ответ для большей наглядности. - person VonC; 02.07.2021

Если вы хотите заменить / перезаписать содержимое файла в текущей ветке содержимым файла из предыдущей фиксации или другой ветки, вы можете сделать это с помощью следующих команд:

git checkout 08618129e66127921fbfcbc205a06153c92622fe path/to/file.txt

or

git checkout mybranchname path/to/file.txt

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

person c-a    schedule 18.11.2010
comment
простейшее решение, и это то, для чего предназначен git-checkout - указание пути означает, что будет извлечен только соответствующий файл. Из справочной страницы git-checkout: git checkout master ~ 2 Makefile - person RichVel; 28.03.2013
comment
Тогда как вернуться в предыдущее состояние перед запуском этой команды? - person Flint; 09.10.2013
comment
@Flint, если вы выходите из состояния HEAD, это будет так же просто, как git checkout HEAD - [полный путь]. - person Tiago Espinha; 07.11.2013
comment
Обратите внимание, что это перезаписывает существующий файл по этому пути, тогда как решение git show SHA1:PATH выводится только на стандартный вывод. - person Flimm; 22.01.2014
comment
Отлично! Я бы не смог понять этого, глядя на git help checkout. Мне нужно было проверить подкаталог с определенной даты, и, используя этот подход, я мог заставить работать следующий синтаксис: git checkout @{YYYY-MM-DD} sub-dir - person haridsv; 25.01.2018
comment
Моя глупая ошибка, но если вы используете окна (sourcetree / mingw), обратите внимание, что разделитель путей - /, а не \ - person BurnsBA; 23.08.2018

Вам необходимо указать полный путь к файлу:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:full/repo/path/to/my_file.txt
person Milan Babuškov    schedule 04.03.2009
comment
не обязательно должен быть полный путь. Путь из корневого каталога git (тех, кто зашел в git show --name-only тоже достаточно - person Mohsen; 27.06.2013
comment
Эмм, полный путь от корня репозитория. Взгляните лучше на приведенный мною пример. Перед полной косой чертой нет ведущей косой черты. - person Milan Babuškov; 27.06.2013
comment
К вашему сведению, если вы находитесь в подкаталоге, вы также можете успешно использовать ./filename.ext. - person Traveler; 28.11.2014
comment
Я думаю, дело в том, что если вы находитесь в full/repo/path/to и попробуете: git show 27cf8e84:my_file.txt, вы будете вознаграждены сообщением типа: fatal: Path 'full / repo / path / to / my_file.txt' существует, но не 'my_file.txt'. Вы имели в виду '27cf8e84: full / repo / path / to / my_file.txt' aka '27cf8e84: ./ my_file.txt'? Это похоже на Git мог бы помочь напрямую, но предпочел проявить педантичность. - person Ed Randall; 16.02.2019

Самый простой способ - написать:

git show HASH:file/path/name.ext > some_new_name.ext

куда:

  • HASH - это номер хэша SHA-1 версии Git.
  • file / path / name.ext - это имя файла, который вы ищете.
  • some_new_name.ext - путь и имя, в котором нужно сохранить старый файл.

Пример

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:my_file.txt > my_file.txt.OLD

Будет сохранен my_file.txt из версии 27cf8e как новый файл с именем my_file.txt.OLD.

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

Если вы хотите восстановить удаленный файл, вы можете использовать HASH~1 (одна фиксация перед указанным HASH).

ПРИМЕР:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8~1:deleted_file.txt > deleted_file.txt
person jmarceli    schedule 07.07.2015
comment
Дополнительная информация: вы можете получить HASH, например. с журналом git - person xotix; 30.08.2017
comment
@xotix Спасибо. Я получил всю историю HASH для конкретного файла, используя git log file/path/name.ext - person Sriram Kannan; 04.06.2019

В Windows с помощью Git Bash:

  • в вашем рабочем пространстве измените каталог на папку, в которой находится ваш файл
  • git show cab485c83b53d56846eb883babaaf4dff2f2cc46:./your_file.ext > old.ext
person Alessandro Jacopson    schedule 02.03.2015

И чтобы красиво выгрузить его в файл (по крайней мере, в Windows) - Git Bash:

$ echo "`git show 60d8bdfc:src/services/LocationMonitor.java`" >> LM_60d8bdfc.java

" кавычки необходимы, чтобы сохранить символы новой строки.

person Mr_and_Mrs_D    schedule 01.01.2014
comment
Хороший. +1. Хорошее дополнение к синтаксису git show, о котором я упоминал выше. - person VonC; 01.01.2014
comment
Я действительно не понимаю, зачем вам использовать эхо с кавычками или без них. И я не понимаю, почему вам нужна форма добавления перенаправления вывода. Разве не лучше было бы просто написать: git show 60d8bdfc: src / services / LocationMonitor.java ›LM_60d8bdfc.java. Если по какой-то причине вы действительно хотели принудительно завершить строки в стиле dos, вы могли бы передать его через unix2dos. Но я никогда не находил хоть немного полезным сохранять концы строк dos в Windows, поскольку любые текстовые инструменты, кроме блокнота, которые я использовал в Windows, отлично справляются со строками в стиле unix. - person sootsnoot; 12.06.2014
comment
git show 60d8bdfc: src / services / LocationMonitor.java ›› LM_60d8bdfc.java работал у меня. - person Mike6679; 13.01.2015
comment
@Mike: ты на окнах? - person Mr_and_Mrs_D; 14.01.2015
comment
git show 60d8bdfc:./src/services/LocationMonitor.java > LM_60d8bdfc.java у меня работает в Ubuntu bash. Обратите внимание на ./ после : - person phuclv; 25.10.2016
comment
не используйте двойные кавычки, потому что если символы вашего файла выглядят как переменная оболочки, то есть $ LANG, они будут заменены. @ LưuVĩnhPhúc - это заставка. также не используйте ›› Файл будет добавлен, если он существует, что может привести к ошибкам. - person theguy; 01.12.2017
comment
Судя по комментариям выше, похоже, что $ echo '`git show 60d8bdfc:src/services/LocationMonitor.java`' > LM_60d8bdfc.java - это то, как должна выглядеть команда, чтобы сохранить формат новой строки в Windows. - person SherylHohman; 18.03.2020
comment
@SherylHohman Только сейчас он не запускается git show (если я правильно понимаю). Git Bash - это просто старый старый bash, поэтому git show 60d8bdfc:src/services/LocationMonitor.java > LM_60d8bdfc.java должен работать нормально. - person Ale; 07.07.2020
comment
@Ale, Хм .. Я не уверен, почему я пришел к такому выводу - я неправильно цитировал свои источники. Не знаю, удалялись ли какие-то комментарии или нет. Я смутно помню, что видел дополнительную информацию, возможно, из другого сообщения? Возможно, ты прав. Однако есть некоторые предостережения относительно того, что Git Bash - это просто старый bash. Не все команды bash доступны в git bash. Кроме того, IIRC, я думаю, что в окнах иногда может потребоваться дополнительная косая черта или одинарные кавычки для размещения пробелов в именах каталогов. Я также столкнулся с разницей между использованием одинарных и двойных кавычек. Разве что только для файлов конфигурации? - person SherylHohman; 07.07.2020
comment
@SherylHohman Да, в среде есть некоторые различия, но оболочка (а значит, синтаксис и функции, такие как перенаправление вывода) должны работать так же, как в системах * nix ;-) - person Ale; 09.07.2020

git checkout {SHA1} -- filename

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

person jsina    schedule 28.09.2019
comment
как это не ранжируется выше? - person jouell; 29.04.2021

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

git diff --name-only --diff-filter=D $commit~1 $commit | xargs git checkout $commit~1
person Adrian Gunawan    schedule 24.07.2014

В дополнение ко всем параметрам, перечисленным в других ответах, вы можете использовать git reset с интересующим объектом Git (хеш, ветка, HEAD~x, тег, ...) и путем к вашему файлу:

git reset <hash> /path/to/file

В вашем примере:

git reset 27cf8e8 my_file.txt

Это означает, что он вернет my_file.txt к своей версии при фиксации 27cf8e8 в индексе, оставив его нетронутым (то есть в его текущей версии) в рабочем каталоге.

Оттуда все очень просто:

  • вы можете сравнить две версии вашего файла с git diff --cached my_file.txt
  • вы можете избавиться от старой версии файла с помощью git restore --staged file.txt (или, до Git v2.23, git reset file.txt), если вы решите, что вам это не нравится
  • вы можете восстановить старую версию с помощью git commit -m "Restore version of file.txt from 27cf8e8" и git restore file.txt (или до Git v2.23, git checkout -- file.txt)
  • вы можете добавлять обновления из старой версии в новую только для некоторых блоков, запустив git add -p file.txt (затем git commit и git restore file.txt).

Наконец, вы даже можете интерактивно выбрать, какие блоки нужно сбросить на самом первом шаге, если вы запустите:

git reset -p 27cf8e8 my_file.txt

Таким образом, git reset с указанием пути дает вам большую гибкость для получения определенной версии файла для сравнения с его текущей извлеченной версией и, если вы решите сделать это, полностью или только для некоторых фрагментов вернуться к этой версии.


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

Конечно, вы все еще можете сделать это после сброса файла с помощью:

git show :file.txt

для вывода на стандартный вывод или

git show :file.txt > file_at_27cf8e8.txt

Но если это все, что вам нужно, запуск git show напрямую с git show 27cf8e8:file.txt, как предлагали другие, конечно, гораздо более прямолинейно.

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

person prosoitos    schedule 13.09.2020
comment
Что делать, если файл находится в другой ветке? - person Chuck; 12.03.2021
comment
Итак, я хочу получить определенную версию определенного файла в другой конкретной ветке. Это возможно? - person Chuck; 12.03.2021
comment
да. Конкретная версия и конкретная ветка: вам просто нужно сначала найти соответствующий хеш, например, git log --graph --oneline --all. Конкретный файл: укажите путь к файлу. - person prosoitos; 12.03.2021
comment
Помните, что это вернет файл к версии, соответствующей хешу в индексе. Не в рабочем дереве. Если вы хотите восстановить эту версию в рабочем дереве, вам нужно запустить git commit и git restore <file>, как я упоминал в ответе. - person prosoitos; 12.03.2021
comment
Спасибо, чувак, ценю это - person Chuck; 16.03.2021

Использование git show $REV:$FILE, как уже указывалось другими, вероятно, является правильным ответом. Я отправляю еще один ответ, потому что, когда я пробовал этот метод, я иногда получал следующую ошибку от git:

fatal: path 'link/foo' exists on disk, but not in 'HEAD'

Проблема возникает, когда часть пути к файлу является символической ссылкой. В таких случаях метод git show $REV:$FILE не будет работать. Действия по воспроизведению:

$ git init .
$ mkdir test
$ echo hello > test/foo
$ ln -s test link
$ git add .
$ git commit -m "initial commit"
$ cat link/foo
hello
$ git show HEAD:link/foo
fatal: path 'link/foo' exists on disk, but not in 'HEAD'

Проблема в том, что такие утилиты, как realpath, здесь не помогают, потому что символическая ссылка может больше не существовать в текущем коммите. Я не знаю хорошего общего решения. В моем случае я знал, что символическая ссылка может существовать только в первом компоненте пути, поэтому я решил проблему, дважды применив метод git show $REV:$FILE. Это работает, потому что, когда git show $REV:$FILE используется в символической ссылке, ее цель печатается:

$ git show HEAD:link
test

В то время как с каталогами команда выводит заголовок, за которым следует содержимое каталога:

$ git show HEAD:test
tree HEAD:test

foo

Итак, в моем случае я просто проверил вывод первого вызова git show $REV:$FILE, и если это была только одна строка, то я заменил первый компонент моего пути результатом, чтобы разрешить символическую ссылку через git.

person josch    schedule 23.12.2020
comment
Интересный альтернативный метод. Проголосовали. - person VonC; 23.12.2020

Получите файл из предыдущей фиксации, проверив предыдущую фиксацию и скопировав файл.

  • Обратите внимание, в какой ветке вы находитесь: git branch
  • Проверьте предыдущий коммит, который вы хотите: git checkout 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8
  • Скопируйте нужный файл во временную папку
  • Оформить заказ в ветке, из которой вы начали: git checkout theBranchYouNoted
  • Скопируйте в файл, который вы поместили во временную папку
  • Зафиксируйте изменения в git: git commit -m "added file ?? from previous commit"
person rocketInABog    schedule 22.07.2011
comment
Я не уверен, почему этот ответ получил столько голосов против. Это определенно некрасиво и явно не лучший способ сделать это. Но это действительно происходит. - person prosoitos; 14.09.2020