ChatGPT解决这个技术问题 Extra ChatGPT

How to checkout in Git by date?

I am working on a regression in the source code. I'd like to tell Git: "checkout the source based on a parameterized date/time". Is this possible?

I also have staged changes in my current view that I don't want to lose. Ideally, I would like to toggle back and forth between the current source, and some version I'm interested in based on a previous date.

Just in case you don't know about it, git bisect is pretty great for finding regressions. I would say, use the {1 year ago} syntax like Andy said, to find a known-good commit, then use that as your initial git bisect good point.
I feel like this is a good use case for tags.

b
bebbo

To keep your current changes

You can keep your work stashed away, without commiting it, with git stash. You would than use git stash pop to get it back. Or you can (as carleeto said) git commit it to a separate branch.

Checkout by date using rev-parse

You can checkout a commit by a specific date using rev-parse like this:

git checkout 'master@{1979-02-26 18:30:00}'

More details on the available options can be found in the git-rev-parse.

As noted in the comments this method uses the reflog to find the commit in your history. By default these entries expire after 90 days. Although the syntax for using the reflog is less verbose you can only go back 90 days.

Checkout out by date using rev-list

The other option, which doesn't use the reflog, is to use rev-list to get the commit at a particular point in time with:

git checkout `git rev-list -n 1 --first-parent --before="2009-07-27 13:37" master`

Note the --first-parent if you want only your history and not versions brought in by a merge. That's what you usually want.


@Rocky Can you give us more details Rocky? What are you entering on the command line and why do you say it's not working? Are you getting an error message?
@Rocky: The problem is that the parameter needs to be enclosed in quotes, else bash separates the arguments at spaces. Try git co 'master@{2 days ago}'.
Note: depending on how far back you go, this may not work because it uses the reflog (which expires after some time). You will see 'warning: Log for 'master' only goes back to...'. Rocky's solution will work always. git checkout git rev-list -n 1 --before="2009-07-27 13:37" master
I edited your answer because backticks are deprecated and difficult to read. Subshells $(...) are preferred.
@Andy Happy 40th birthday, Andy! (assuming that's what 1979-02-26 meant :))
H
Holger Bille

Andy's solution does not work for me. Here I found another way:

git checkout `git rev-list -n 1 --before="2009-07-27 13:37" master`

Git: checkout by date


When I execute above command I get error: unknown switch `n' any ideas how to get around this?
@Tim Run it in two parts, first run the git rev-list and only after that run the git checkout with the commit you get from the first command.
Instead of -n 1 you should use --max-count=1
You should explain what the need for the options are. Why -n 1? What is the difference to not passing it?
This does not work on GIT for Windows at all! (GIT version 2.30.1). It looks like rev-list command always ignoring any of such input cmdline options. :-\
C
Carl

Looks like you need something along the lines of this: Git checkout based on date

In other words, you use rev-list to find the commit and then use checkout to actually get it.

If you don't want to lose your staged changes, the easiest thing would be to create a new branch and commit them to that branch. You can always switch back and forth between branches.

Edit: The link is down, so here's the command:

git checkout `git rev-list -n 1 --before="2009-07-27 13:37" master`

Great link! So git checkout branch@{date} stops working when the reflog expires, but you can use git checkout `git rev-list -n 1 --before="2009-07-27 13:37" master`.
Z
Zombo

To those who prefer a pipe to command substitution

git rev-list -n1 --before=2013-7-4 master | xargs git checkout

B
BartoszKP

In my case the -n 1 option doesn't work. On Windows I've found that the following sequence of commands works fine:

git rev-list -1 --before="2012-01-15 12:00" master

This returns the appropriate commit's SHA for the given date, and then:

git checkout SHA

D
Danyal Sandeelo

The git rev-parse solution proposed by @Andy works fine if the date you're interested is the commit's date. If however you want to checkout based on the author's date, rev-parse won't work, because it doesn't offer an option to use that date for selecting the commits. Instead, you can use the following.

git checkout $(
  git log --reverse --author-date-order --pretty=format:'%ai %H' master |
  awk '{hash = $4} $1 >= "2016-04-12" {print hash; exit 0 }
)

(If you also want to specify the time use $1 >= "2016-04-12" && $2 >= "11:37" in the awk predicate.)


Z
Zombo

Going further with the rev-list option, if you want to find the most recent merge commit from your master branch into your production branch (as a purely hypothetical example):

git checkout `git rev-list -n 1 --merges --first-parent --before="2012-01-01" production`

I needed to find the code that was on the production servers as of a given date. This found it for me.


a
antlersoft

If you want to be able to return to the precise version of the repository at the time you do a build it is best to tag the commit from which you make the build.

The other answers provide techniques to return the repository to the most recent commit in a branch as of a certain time-- but they might not always suffice. For example, if you build from a branch, and later delete the branch, or build from a branch that is later rebased, the commit you built from can become "unreachable" in git from any current branch. Unreachable objects in git may eventually be removed when the repository is compacted.

Putting a tag on the commit means it never becomes unreachable, no matter what you do with branches afterwards (barring removing the tag).


Although this does not give me the answer I look for, it does deserve a good mention for pointing out an aspect not mentioned so far. This could be the source of problems that prevent you from reaching the right version.
V
VonC

Reminder, since Git 2.23 (Aug. 2019)

switch to a new branch from a past commit by date: git switch -c newBranch $(git rev-list -n1 --before=yyyy-mm-dd main)

restore a file at a past date in the current working tree git restore -SW -s $(git rev-list -n1 --before=yyyy-mm-dd main) -- path/to/file

Do not use the old, obsolete and confusing git checkout command in 2021/2022: git switch and git restore describe exactly what they are doing (working with only branches for git switch, and only with files for git restore)

Do not use ``: prefer the more modern $(command substitution) syntax over obsolescent backtick syntax.


M
Murali Krishna Regandla

To create a new branch from the checkout, use -b option with new branch name.

git checkout -b 19July2021 `git rev-list -n1 --before=2021-7-19 master`

You can avoid git switch, when you are in 'detached HEAD' state


L
Luca C.
git rev-list -n 1 --before="2009-07-27 13:37" origin/master

take the printed string (for instance XXXX) and do:

git checkout XXXX

Isn't this a duplicate of @bartoszkp answer? just adding the reference to origin, should be a comment on the other answer...
yes, in fact almost, just clearifing what to copy for who does not know what SHA is (like me), in my case that text was not clear and this is my code after founding the solution, not copied, in fact you can see the options are sligtly different too
t
tyoc213

You only need a little change if you hit the limit of reflog (the date you cloned the repo or 90 days a go of history it seems from other notes)

git checkout `git rev-list -1 --before="Jan 17 2020" HEAD`

And you can also use

git checkout `git rev-list -1 --before="Jan 17 2020 8:06 UTC-8" HEAD`

it will checkout the previous commit related to the date or the date-time you enter, see that you can use modifiers for the date, I guess if you dont use UTC+-N it just uses UTC time.

See that I only changed master to HEAD, it seems to work even if you dont have reflog to the date you want to check!!!


J
James Andariese

Here's an alias version that I use to checkout by relative time:

[alias]
    parsedate = "!set -x ;arg1=\"$1\" && shift && which gdate &> /dev/null && gdate -d \"$arg1\" || date -d \"$arg1\""
    codate = "!d=\"$(git parsedate \"$1\")\" && shift && git checkout \"$(git rev-list -n1 --first-parent --before=\"$d\" HEAD)\""

This allows you to checkout your changes from 2 months ago into a new branch named test using the following command:

git codate '2 months ago' -b test

This won't work on a shallow clone so unshallow it first with:

git fetch --unshallow

If you get an unshallow error, you might find help from the excellent answer at git fatal: error in object: unshallow

Requires GNU coreutils and works on Linux and macOS using brew.

Install on macos using brew install coreutils.


M
Matt Faus

If you want to check out a single file as of a specific date, you can pass the filepath to both commands.

git checkout `git rev-list -n 1 --before="2009-07-27 13:37" master FILEPATH` FILEPATH