tags:

views:

38

answers:

3

In git, I can specify the previous revision by saying HEAD^ or HEAD~1. What about going the other way? Suppose I'm on revision X, and I do git checkout X^. How do I go back?

Something like git checkout X+?

A: 

I don't think this is possible, since a Git commit only stores its parent commit, but not its children.

Imagine a commit would store its children. What would happen if you would create several branches off this commit, so multiple commits have this commit as parent? What would then be "HEAD+"? This is ambiguous and wrong.

Speaking in what I know from data structures: Git stores history as a single-linked list, whereas you operation would need a doubly-linked list.

fat-lobyte
It's a little worse than that - see my answer about the ambiguity even if parents knew about children.
Jefromi
+1  A: 

You can't really do precisely that. History in git is a directed acyclic graph - each commit contains references to its parents, but parents don't have references to their children.

The problem here should become obvious when you think about a commit you created multiple branches from. Which "next commit" do you mean? With parents, you can number off (a normal merge commit has a first and second parent), but how do you do that with children? Even if you know what branch you're trying to be on (e.g. you've checked out master~4 and now you want to look at master~3) it's not well-defined - you could be in a situation like this:

- X (HEAD) - o - o - o - Y (master)
   \                    /
    o - o - o ----------

That said, in simple cases, you could do something like this:

git checkout $(git rev-list HEAD..master | tail -n 1)

Clearly that'll work fine with linear history. With merges... rev-list works from present to past in the history, following it backward. I believe it follows the first parent first, so the thing printed last will be the commit after HEAD found by following all last parents.

Edit: That does assume that you know which branch you want to move forward toward. If you don't... well, you're pretty much stuck looking on all refs for commits which have the current HEAD as parent - probably grepping the output of git rev-parse:

git rev-list --all --children | grep ^$(git rev-parse HEAD)

Then grab the other SHA1 from the line (use awk, whatever). You'll have to manually inspect or make an arbitrary choice if there are multiple results though...

Jefromi
A: 

As far as I can tell there's no symbolic way to refer to the children of a commit.

The best I can give you just now is the --children option of git rev-list. Here I ask for the four most recent commits to be pretty-printed, plus information on their children. Note that on the "commit" line of each entry (other than the most recent) there is an extra commit number, which specifies the child of that node. You could grab that either manually or through some shell scripting to get to the child.

$ git rev-list --children --pretty HEAD~3...

commit 20dba296ad1d48ec90f9319e2c13b245e849f698
Author: Somebody <____@____.net>
Date:   Thu May 6 19:10:38 2010 -0400

    Support for complex trig/pow.

commit 9d42b5bac1721a847a39c25672e577c7101c8ff0 20dba296ad1d48ec90f9319e2c13b245e849f698
Author: Somebody <____@____.net>
Date:   Wed May 5 21:55:07 2010 -0400

    Fix doc formatting warning.

commit 72bed3baa9df71cb224dfa8388b5969d50f5567c 9d42b5bac1721a847a39c25672e577c7101c8ff0
Merge: b8244cb61491c9cdb83d36e57f8eb49773e44f6b 899c3dd3f9f419f200b84ca0abe59d7ac3d5bb53
Author: Somebody <____@____.net>
Date:   Wed May 5 21:54:59 2010 -0400

    Merge branch 'master' 

commit 899c3dd3f9f419f200b84ca0abe59d7ac3d5bb53 72bed3baa9df71cb224dfa8388b5969d50f5567c
Author: Somebody <____@____.net>
Date:   Wed May 5 21:19:11 2010 -0400

    Fix link
kwatford