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

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

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

git-show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8 my_file.txt

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

По сути, я ищу что-то вроде svn cat.

12 ответов

Решение

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

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

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

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

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

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

git show HEAD^^:./test.py

)


До 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, и просто выдает дамп этот объект на стандартный вывод.


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

См. Коммит 3214594, коммит 7bcf341 (09 сентября 2016 г.), коммит 7bcf341 (09 сентября 2016 г.) и коммит b9e62f6, коммит 16dcc29 (24 августа 2016 г.) от Johannes Schindelin ( dscho )
(Объединено Юнио С Хамано - gitster - в коммите 7889ed2, 21 сентября 2016 г.)

cat-file: служба поддержки --textconv / --filters в пакетном режиме

Даже если " git hash-objects", который является инструментом, позволяющим взять поток данных внутри файловой системы и поместить его в хранилище объектов Git, позволил выполнять преобразования" из внешнего мира в Git"(например, преобразования в конце строки и применение чистого -filter), и с самого начала у него была функция, включенная по умолчанию. git cat-file ", который берет объект из хранилища объектов Git и извлекает его для потребления внешним миром, ему не хватало эквивалентного механизма для запуска"Git-to-external-world"

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

Смотрите коммит cc0ea7c (21 сентября 2017 г.) Джеффа Кинга ( peff )
(Объединено Юнио С Хамано - gitster - в коммите bfbc2fc, 28 сентября 2017 г.)

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

git checkout 08618129e66127921fbfcbc205a06153c92622fe path/to/file.txt

или же

git checkout mybranchname path/to/file.txt

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

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

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:full/repo/path/to/my_file.txt

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

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

где:

  • HASH - номер хэша Git-ревизии SHA-1
  • 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
git checkout {SHA1} -- filename

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

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

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

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

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

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

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

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

git diff --name-only --diff-filter=D $commit~1 $commit | xargs git checkout $commit~1

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

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

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

      $ 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'

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

      $ git show HEAD:link
test

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

      $ git show HEAD:test
tree HEAD:test

foo

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

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

  • Обратите внимание, на какой ветке вы находитесь: git branch
  • Оформить предыдущий коммит вы хотите: git checkout 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8
  • Скопируйте файл, который вы хотите во временную папку
  • Оформите ветку, с которой вы начали: git checkout theBranchYouNoted
  • Скопируйте файл, который вы поместили во временную папку
  • Зафиксируйте изменения в git: git commit -m "added file ?? from previous commit"

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

В Visual Studio с установленным расширением Git:

  1. Git -> Просмотр истории веток
  2. Щелкните правой кнопкой мыши фиксацию, в которой был изменен файл, затем выберите «Просмотреть сведения о фиксации».
  3. В списке изменений справа нажмите на интересующий файл. Вы увидите параллельное сравнение всего содержимого файла до и после изменения. Просто выберите все, скопируйте содержимое файла и вставьте в любое место.
Другие вопросы по тегам