I'm learning about relative commit references and trying to understand the following git log --oneline --graph
output provided in a lesson.
In the lesson it says that given HEAD points to the 9ec05ca
commit, HEAD^^^ (meaning the great-grandparent commit) is the 0c5975a
commit. But it seems to me 4c9749e
should be the great grandparent, if each SHA is a direct descendant of the one below it. Any clarification appreciated.
Commit can have more than one parent. ^ 1 means first parent. ^2 means second parent and do on. Just ^ defaults to ^1 which is first parent.
Similarly ~ give access to traverse up for an ancestor. For example if you want 3 generation up ~3. If you want 7th last commit in same branch favouring first parent while traversing.
Any merge will have two parents with merge to branch becoming first parent. Merge from branch becoming second parent.
setia_'s answer remains the most appropriate but your question is meaningful because a git history can be considered from two distinct perspectives: either you totally view it as a (mathematical) graph in which parent's order is supposed to be non significant, OR you consider a merge operation as the integration of an external branch into a common trunk (i.e. your current branch, very often "master"). This is also the assumption made by git merge
.
This is important not only because browsing an exponentially growing tree rapidly becomes a pain, but also because having a branch "master" that contains a finished product and integrating external submissions is a model followed by many workflows.
Since both these points of view are valid, they cause --first-parent
to be a frequently used option, though not a main command nor a default behavior.
For example, browsing the Linux kernel main repository (for which Git was first designed) with a simple git log --graph
can take up to one minute before displaying something, but git log --graph --first-parent
will show you the main branch and let you observe that it's mainly composed of merges, with occasional direct commits.
Another thing one have to keep it mind is that you can ask to have your commits displayed by chronological order using --date-order
or topological order with --topo-order
. Let's assume you have two distinct branches and alternatively commit to the one or the other before eventually merging them. Depending on the order, the resulting graph would look like one of these :
Chronological order Topological order
* ec9a124 Merge branch 'B' into A * ec9a124 Merge branch 'B' into A
|\ |\
* | e5314f2 Ninth | * e3e2435 Eighth
| * e3e2435 Eighth | * af3bac5 Sixth
* | 308228b Seventh | * 3a2f0b9 Fourth
| * af3bac5 Sixth | * d901c9f Second
* | ab11578 Fifth * | e5314f2 Ninth
| * 3a2f0b9 Fourth * | 308228b Seventh
* | 344bd0f Third * | ab11578 Fifth
| * d901c9f Second * | 344bd0f Third
|/ |/
* 0f029bc First * 0f029bc First
All commits remain on their respective line but the latter is a depth-first search. And since git log
(so does git rev-list
) displays a collection of pre-gathered revisions, this order will be the same whether you're using --graph
or displaying them as a flat list (actually, if not specified, git log
use reverse chronological order by default, and topological one when using --graph
).
Because of that, you can't simply rely on the first row to decide which parent to choose.
This can also cause some older commits to appear before the new ones, which can be very confusing when beginning with Git, and give you the impression that your work has disappeared (until it comes to one's mind to perform a search, then retrieve it buried somewhere in history).