1

Git fast forward merge is FORWARD in terms of TIME. The pointer goes from older to newer commits.

Example (by ffwd merge master pointer is moved from commit D to commit G):

Before fast-forward merge:

                  master
                    |
        A<--B<--B<--D<--E<--F<--G
                                |
                           new_branch

After fast-forward merge:

                              master
                                |
        A<--B<--B<--D<--E<--F<--G
                                |
                           new_branch

But, since the commit pointers point from newer to older, in terms of these pointers strictly, the branch pointer goes backwards... upstream the commit pointers. So in that sense, it can be labeled a fast-reverse merge. Upstream (the term from ProGit book, chapter on merging) refers to reverse flow, up-the-stream, so explaining upstream flow as a fast forward thing can confuse newcomers. So, it's:

Upstream in terms of commit pointers.

Forward in terms of commit time.

Does this reasoning make sense?

Martin
  • 1,276
  • 1
  • 10
  • 13
  • you are at `D` after `git chectout master ; git merge new_branch` – J-16 SDiZ May 30 '12 at 15:21
  • True. That's clear. It's the terminology that's a bit confusing, so I'm trying to clear it up here... after git merge new_branch, the master pointer goes E, F, G and stops at G. But if you look at the commit pointers, it-s G->F->E->D... so the master pointer goes referse of the commit pointer direction. – Martin May 30 '12 at 15:24
  • no, it don't move master to "G". It just start with "G" and ff to "D". `master` is just a name/label thingy (ref), it is updated after merge, not "move" in steps. – J-16 SDiZ May 30 '12 at 15:28

2 Answers2

2

This would only be true if commits followed the path of the arrows.

I think that's what's confusing you. Remember, the arrows point to where you have come from, not where you're going.

The master pointer moved forward in time and forward along the branch. Remember, HEAD is at the tip of the branch. Anytime you move towards HEAD (assuming it hasn't been moved backwards) you're moving forward in time and commits.

Now, if you wanted to think of it as a linked list, then yes, typically A would be the head, and G would be the tail, EXCEPT.....we know that in Git our HEAD points to G. Besides that, I don't think newbies typically look at it at this low level and try to apply data structure concepts to it.

You're over thinking it.

wadesworld
  • 13,535
  • 14
  • 60
  • 93
  • I'm probably overthinking it. I thought little arrows B->A, C->B, you know, the directon newer->older might confuse visually oriented people like me... but okay. I accept this overthinking statement :-) – Martin May 30 '12 at 16:07
  • Well, it is confusing at first. The natural tendency is to follow the arrows. However, once you realize that the arrows point to parents, not children, it becomes more clear. Not sure there's anyway around that initial learning curve as reversing the arrows while easier to understand would be factually incorrect. – wadesworld May 30 '12 at 16:35
  • Meaning there exist no pointers from parents to children in the linked list? – Martin May 30 '12 at 16:45
  • But never mind this comment, for a question like this, this amount of discussion is more than enough. – Martin May 30 '12 at 17:01
  • @Martin that's correct. Parents do not point to children. This would not work as everytime you made a commit on a new branch, the parent commit would have to add that pointer to it's collection of children. In doing so, the contents of that commit would change. This would change the sha1 of that commit. This, in turn, would invalidate the pointers of the other children that that commit already had. You would not want this! Take a look at Git internals and what each commit is made of exactly and you'll see why having back references is the only logical solution. The integrity of Git's history.. – Adam Dymitruk May 30 '12 at 17:22
  • .. hinges on this idea of a "rolling hash". The ids that are used to point from one commit to another, a commit to a tree, a tree to a blob, a tree to a tree and a tag to a commit **are the hash of the contents**. Changing anything within any of these things, invalidates the pointer. This is why Git's structure is so great for keeping integrity. – Adam Dymitruk May 30 '12 at 17:26
0

No, it don't move master to "G". It just start with "G" and ff to "D". master is just a name/label thingy (called ref). It is updated after merge, not "move" in steps.

To understand this, you can just try a merge with conflict. When the merge is awaiting manual resolve, the branches are still points to their old commit. Just the checkout tree and index are in the indeterminate state.

update: May be you can think in this way:

  1. find the merge base git merge-base D G, this is G
  2. Create a fake, temporary branch at G (Not really a branch, but lets pretend this for a moment).
  3. ff the fake branch to D.
  4. rename the fake branch to master, overwrite the old one.
J-16 SDiZ
  • 26,473
  • 4
  • 65
  • 84
  • Before merge, master points to D. That's the initial situation. How then master starts with G, like you say? – Martin May 30 '12 at 16:15
  • The index start at G; master keeps at D. It don't move master until it finish all steps. Try the conflict merge I suggest, it help you understand better. – J-16 SDiZ May 30 '12 at 16:19
  • In your case, 'master' keeps at D all the time, it never "go backward". If you are brave, try pull the power while it merging.... After reboot, checkout master still give you D. – J-16 SDiZ May 30 '12 at 16:23
  • okay, i made the same arrow mistake.. just pretend the arrow, it still clear some concept stuff – J-16 SDiZ May 30 '12 at 16:45
  • I don't get the 3rd step in the update: ff the fake branch to D... if it's already at G, how can it ff to D? From G to D is reverse... DEFG is fowrard, GFED is reverse. But I'll think about it, maybe even pull out the power... – Martin May 30 '12 at 16:56
  • This is not a clear explanation. Google "git for computer scientists" to get a very clear explanation. Key is, branches are nothing more than references to a particular commit and are stored as 40 character stirngs in `.git/refs`. – Adam Dymitruk May 30 '12 at 17:29