ChatGPT解决这个技术问题 Extra ChatGPT

How can I view an old version of a file with Git?

Is there a command in Git to see (either dumped to stdout, or in $PAGER or $EDITOR) a particular version of a particular file?

If you came to this question because you want to check an older version of a binary file (e.g. an image), then better to do a checkout to the old commit, see what you need to see, and then come back to the HEAD. For that, do git checkout <sha1-of-the-commit-you-need>, afterwards, git checkout HEAD

M
MakotoE

You can use git show with a path from the root of the repository (./ or ../ for relative pathing):

$ git show REVISION:path/to/file

Replace REVISION with your actual revision (could be a Git commit SHA, a tag name, a branch name, a relative commit name, or any other way of identifying a commit in Git)

For example, to view the version of file <repository-root>/src/main.c from 4 commits ago, use:

$ git show HEAD~4:src/main.c

Git for Windows requires forward slashes even in paths relative to the current directory. For more information, check out the man page for git-show.


That doesn't actually seem to work -- did you try it? For "git show HEAD:path/to/file.c", I get an "ambiguous argument" error.
Has to be the complete path, from the top of the git repo
If you're on windows, it might be a path separator thing; if I do git show HEAD:dir\subdir\file, I get the anbiguous argument. If I do git show HEAD:dir/subdir/file, it works as expected.
The path you must provide after the : is from the root of the git repository. (this was given below as an answer but I think it was intended as a comment on this answer)
If you want to see it in a Vim split so that they scroll together, I wrote a short blog post showing how to do that.
J
Jim Hunziker

Doing this by date looks like this if the commit happened within the last 90 days:

git show HEAD@{2013-02-25}:./fileInCurrentDirectory.txt

Note that HEAD@{2013-02-25} means "where HEAD was on 2013-02-25" in this repository (using the reflog), not "the last commit before 2013-02-25 in this branch in history".

This is important! It means that, by default, this method only works for history within the last 90 days. Otherwise, you need to do this:

git show $(git rev-list -1 --before="2013-02-26" HEAD):./fileInCurrentDirectory.txt

This command is useful with master instead of HEAD@{2013-02-25}, if you're on a branch
Can you include the time, à la git log --since='2016-04-28 23:59:59 +0100'?
The fact this syntax uses the reflog is important and should be highlighted strongly, because the reflog does not contain all commits. See blog.endpoint.com/2014/05/git-checkout-at-specific-date.html
Something which I missed: there cannot be a space after the colon : and before the filename.
@AliceHeaton This cannot be stressed enough. (Thanks !)
C
Chris Martin

If you like GUIs, you can use gitk:

start gitk with: gitk /path/to/file Choose the revision in the top part of the screen, e.g. by description or date. By default, the lower part of the screen shows the diff for that revision, (corresponding to the "patch" radio button). To see the file for the selected revision: Click on the "tree" radio button. This will show the root of the file tree at that revision. Drill down to your file.


This also works with tig, which is a curses git repo viewer.
@Paul Slocum: May be because this command is not a conventional command, not the built-in of git. I think this command only work for Windows.
Note this only seems to work if you start from the root of your git repository.
If you want to check against a certain revision with gitk you could also use this shortcut: gitk REVISION /path/to/file. This can come in handy when you want to check against a certain version for instance.
sudo apt install gitk for ubuntu
C
Community

You can also specify a commit hash (often also called commit ID) with the git show command.

In a nutshell

git show <commitHash>:/path/to/file

Step by step

Show the log of all the changes for a given file with git log /path/to/file In the list of changes shown, it shows the commit hash such as commit 06c98... (06c98... being the commit hash) Copy the commit hash Run the command git show :/path/to/file using the commit hashof step 3 & the path/to/file of step 1.

Note: adding the ./ when specifying a relative path seems important, i.e. git show b2f8be577166577c59b55e11cfff1404baf63a84:./flight-simulation/src/main/components/nav-horiz.html.


in case you don't know path to file, use git show <SHA1> --name-only to get it.
this command op - even auto completes from memory - tested on a deleted directory... can't get more op than that gg
in debian, the addition of ./ does not matter for the pathing.
C
Community

In addition to Jim Hunziker's answer,

you can export the file from the revision as,

git show HEAD@{2013-02-25}:./fileInCurrentDirectory.txt > old_fileInCurrentDirectory.txt

Hope this helps :)


s
sachin_ur

To quickly see the differences with older revisions of a file:

git show -1 filename.txt > to compare against the last revision of file git show -2 filename.txt > to compare against the 2nd last revision git show -3 fielname.txt > to compare against the last 3rd last revision


Those commands show the differences with the current version for me but not show the entire file.
It's important to notice that this answer matches the question "How to show differences to given file in recent commits?" instead of "How can I view an old version of a file with Git?" the the original question asks.
The difference is about the : - double colon - between commit-hash and file the commenters mention about the entire file and diff to another older version.
s
sanbor

git log -p will show you not just the commit logs but also the diff of each commit (except merge commits). Then you can press /, enter filename and press enter. Press n or p to go to the next/previous occurrence. This way you will not just see the changes in the file but also the commit information.


Looks like git log -pm would also show merge commits.
You can also run git log -p -- filename.txt to restrain the history to only the desired file.
2
2240

WAY 1: (I prefer this way, no ability to lose uncommitted data)

Find commit id with: git reflog List files from commit git diff-tree --no-commit-id --name-only -r Example: git diff-tree --no-commit-id --name-only -r d2f9ba4 d2f9ba4 is commit id from step 1. Open required file with following command: git show :/path/to/file Example: git show d2f9ba4:Src/Ext/MoreSwiftUI/ListCustom.swift Src/... is file path from step 2.

WAY 2: (Ability to lose uncommitted data)

Find commit id with: git reflog Make hard reset to this commit: git reset --hard %commit ID% Example: git reset --hard c14809fa Make necessary changes and do a new commit into required branch


WARNING: Please be careful with the second method as you will lose all uncommited changes when you do a hard reset!
yes, this is ok with hard reset. that's because of reset is"hard" but not "soft". But you need to do hard because of possibilities of conflicts.
B
Brad Parks

You can use a script like this to dump all the versions of a file to separate files:

e.g.

git_dump_all_versions_of_a_file.sh path/to/somefile.txt

Get the script here as an answer to another similar question


git_root, git_log_short and git_log_message_for_commit are missing.
Good catch! I double posted this answer to 2 different spots, and just removed this one and linked to the other one, where people told me about this before... thanks @mogsie !
This script is very useful!
C
Ciro Santilli Путлер Капут 六四事

Helper to fetch multiple files from a given revision

When trying to resolve merge conflicts, this helper is very useful:

#!/usr/bin/env python3

import argparse
import os
import subprocess

parser = argparse.ArgumentParser()
parser.add_argument('revision')
parser.add_argument('files', nargs='+')
args = parser.parse_args()
toplevel = subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).rstrip().decode()
for path in args.files:
    file_relative = os.path.relpath(os.path.abspath(path), toplevel)
    base, ext = os.path.splitext(path)
    new_path = base + '.old' + ext
    with open(new_path, 'w') as f:
        subprocess.call(['git', 'show', '{}:./{}'.format(args.revision, path)], stdout=f)

GitHub upstream.

Usage:

git-show-save other-branch file1.c path/to/file2.cpp

Outcome: the following contain the alternate versions of the files:

file1.old.c
path/to/file2.old.cpp

This way, you keep the file extension so your editor won't complain, and can easily find the old file just next to the newer one.


@MickeyPerlstein if you can make achieve the same interface with a better implementation, I'm all ears.
maybe i don't understand (and if so, my apologies) but isn't it just : "git show version:./path > new_path " ?
@MickeyPerlstein hi, yes, my command generates that CLI, but it loops over multiple files and produces output name from input, so you don't have to type too much. Nothing revolutionary of course, but convenient.