942

I sort of want the equivalent of cd - for git. If I am in branch master and I checkout foo, I would love to be able to type something like git checkout - to go back to master, and be able to type it again to return to foo.

Does anything like this exist? Would it be hard to implement?

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Matt Briggs
  • 41,224
  • 16
  • 95
  • 126

9 Answers9

1598

From the release notes for 1.6.2

@{-1} is a way to refer to the last branch you were on. This is
accepted not only where an object name is expected, but anywhere a branch name is expected and acts as if you typed the branch name.
E.g. git branch --track mybranch @{-1}, git merge @{-1}, and
git rev-parse --symbolic-full-name @{-1} would work as expected.

and

git checkout - is a shorthand for git checkout @{-1}.

To see the list of previous checkouts:

i=0; while [ $? -eq 0 ]; do i=$((i+1)); echo -n "$i. "; git rev-parse --symbolic-full-name @{-$i} 2> /dev/null; done

This Bash one-liner script is not perfect but it should work for most cases. Note that sometimes the number may skip.

Tip: You can add it to .bashrc as a function.

ADTC
  • 8,999
  • 5
  • 68
  • 93
Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94
  • 67
    wow, I totally should have just tried it! figured - was a shell-ism, and that if the functionality was in git, it would be something different – Matt Briggs Aug 26 '11 at 16:17
  • 9
    This does not work well when you checkout a commit SHA twice, in which case @{-1} points to where you were before the first checkout.. – user716468 Mar 01 '13 at 00:32
  • 1
    I'm using ZSH, and I had to wrap @{-1} in quotes. Otherwise git choked: `error: pathspec '@-' did not match any file(s) known to git. error: pathspec '@1' did not match any file(s) known to git.` – Murphy Randle Feb 07 '14 at 23:12
  • 1
    I'm using fish, and also had to wrap `@{-1}` in quotes. `git checkout -` worked just fine, though. – Cypress Frankenfeld Mar 15 '16 at 19:42
  • Note that this does NOT work everywhere a branch name is expected, for example `git push` will not deduce `@{-1}` as a ref name. You must still manually type it. – void.pointer Mar 25 '16 at 14:37
  • `git checkout -` doesn't always work as I expect. For example sometimes when I use it I get `already on branch `. Any idea why? – tba May 05 '16 at 12:03
  • 37
    Extra kudos for the pedagogy used: Show the general facility, then mention the compact but less general shorthand! … I quite often use `@{u}`, which is the upstream branch of the current branch. It's very useful for e.g. `git log @{u}..` which lists upstream commits that aren't pulled yet. And the converse `git log ..@{u}` which is just the local commits that aren't pushed yet. – Benjohn Nov 23 '16 at 09:03
  • Is there a way to use the @{-N} annotation to list the branches without having to checkout ? – sashok_bg Nov 10 '19 at 08:43
  • @Benjohn Your examples are reversed, i.e., `git log @{u}..` gives you local commits that aren't pushed yet and `git log ..@{u}` gives you commits that aren't pulled yet, but then it requires that `git fetch` on the upstream branch is already done for this to work. – haridsv Dec 11 '20 at 06:47
317

The simplest way of doing this nowadays is:

git checkout -

... which is an alias of:

git checkout @{-1}

git checkout minus

If you want to know more about this, I wrote an entire article about it here: Checkout The Previous Branch In Git.

marcgg
  • 65,020
  • 52
  • 178
  • 231
  • 3
    Perfect. I was looking up what the git way to do this was so I could create this exact alias. Glad to see it just exists. – Tabitha Jun 23 '16 at 21:56
  • 4
    Is there a way to list the previous branches without checking them out? – Erotemic Jun 08 '17 at 13:56
  • 9
    @Erotemic using bash - `for i in{1..10}; do git rev-parse --symbolic-full-name @{-$i}; done` – Bonsaigin Sep 20 '17 at 14:50
  • Amazing to see the 'git checkout -' alias is a thing! That is exactly the ux I was looking for :) – james_s_tayler Dec 28 '17 at 20:35
  • @Erotemic in powershell it would be `git reflog | ? { $_ -match ': checkout: moving from (.*) to (.*)'} | % { $Matches[1] } `. In bash you have to write something equivalent (get reflog and filter it to lines containing ": checkout:" string and then extract branch name). This is actually what [git does](https://github.com/git/git/blob/cd69ec8cde54af1817630331fc441f493866f0d4/sha1-name.c#L1255) – Mariusz Pawelski Nov 04 '18 at 00:46
  • Sir, my fingers thank you for the amount of typing you just saved me in the years ahead – floroz Mar 23 '22 at 09:47
38

Git version 2.23 introduced the git switch command which you can use to do that (and more). Quoting the official documentation:

Switch to a specified branch. The working tree and the index are updated to match the branch. All new commits will be added to the tip of this branch.

In your specific case you can issue git switch - to go back to the branch you were previously on. You can execute the same command again to return to the first branch.

This command is less confusing and friendly-to-beginners as it addresses a common confusion that arises when using git checkout.

Mike
  • 706
  • 7
  • 16
32

As @Karl points out and from git checkout manual:

As a special case, the "@{-N}" syntax for the N-th last branch checks out the branch (instead of detaching). You may also specify - which is synonymous with "@{-1}".

So both git checkout - and git checkout @{-1} would work in this case

Closest I believe is using the git reflog and parse the latest moving from branch1 to branch2 and git checkout branch1

manojlds
  • 290,304
  • 63
  • 469
  • 417
15

I landed to this question with the same thought to checkout my previous branch. I'm using ohmyz in Mac. Below command helped me.

$ gco -
$ git checkout -
Venkat.R
  • 7,420
  • 5
  • 42
  • 63
  • Just a heads-up that ohmyz is super slow compared to other zsh package managers. I would recommend taking a look at [antigen](https://github.com/zsh-users/antigen) or [zplug](https://github.com/zplug/zplug). – spex May 10 '18 at 16:44
  • 3
    In case it’s not clear, this works because [Oh My Zsh’s Git plugin](https://github.com/robbyrussell/oh-my-zsh/wiki/Plugin:git) defines `gco` as a short way of writing `git checkout`. So `gco -` just calls `git checkout -`. – Rory O'Kane Jan 16 '19 at 23:12
  • offcourse my friend. needless to explain its `git checkout -`. for failsafe updated. – Venkat.R Jan 17 '19 at 02:05
  • @spex antigen uses ohmyzsh anyways for the git plugin, how can that be faster? – Joe Cabezas Jun 29 '20 at 18:10
11

Just adding some more detail to the previous answers to understand the mechanism by which git checkout @{-N} works. It walks the reflog to inspect the checkout history, so if you wanted to implement something similar on your own you should be able to parse the output of git reflog looking for checkout: lines. You can check the implementation in the git source sha1_name.c, specifically the function interpret_nth_prior_checkout.

ddd
  • 111
  • 1
  • 2
2

Here are pointers to the parts of Git’s documentation that describe the git checkout - and git checkout @{-1} solutions given by the other answers:

  • When specifying a Git revision for any command, @{-<n>}, e.g. @{-1} means “the nth branch/commit checked out before the current one.” The documentation for git checkout <branch> reiterates: “You can use the @{-N} syntax to refer to the N-th last branch/commit checked out using git checkout operation.”

  • For the <branch> argument of git checkout, “you may also specify ‘-’ which is synonymous to ‘@{-1}’.”

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
1

The most popular solution is:

git checkout @{-N}

Where N - step count of the branches to move back on the checkout history.

Jackkobec
  • 5,889
  • 34
  • 34
-2

in my case I had switched from master to gh-pages which cause all my components to disappear and get replaced by a file names "static" and others.

git checkout -m master helped