Friday, 10 October 2008

Git: how remotes work

One of the difficult things for a git beginner to understand is how remote branches work.

Basically, as git is a distributed version control system, every developer has a full and independent repository. So, how can you pass changes around?

In the examples below, we'll consider a remote repository, that we'll call origin, and a local one (that we'll call local). The remote repository has one branch, called master, that has been cloned as origin/master on the local repository. Moreover, the local repository has one local branch, called master as well (but it doesn't need to be), which is set up to track changes that happen on origin/master. Note that, as origin/master is a remote branch, it cannot be checked out -- only master can.

The fetch operation (command git fetch) copies the latest commits from the master on origin to origin/master, and updates the HEAD of the origin/master branch:

git fetch origin
The circles on the schema (click for a larger version) represent commits, and the arrows are the parent->child relationship between commits. The labels indicate the various HEADs (or branches). It is to be noted that a branch is nothing more than a label following the HEAD of a series of commits.

At the end of this operation, origin/master matches the master branch on the origin, but master on the local repository is still behind. We need to use the git merge command to make master point at the same commit than origin/master:
git merge origin/master
This kind of merge is called a fast-forward because no actual merging of changes is involved. No new commit is created; we have just moved a HEAD forward in history. And that's fast.

Now, what happens if you committed a change on your master on local? Nothing changes for the fetch; the two new commits are still created from origin's master so origin/master matches it exactly:
git fetch origin
However, on local, master and origin/master have bifurcated. To reunite them, you'll need to use git merge, that will create another commit, and make master point to it:
git merge origin/master
The new commit (in orange) is a merge commit: it has two parents. (If conflicts happens, git will ask you to resolve them.)

Ah, but now, your master has two more commits that the origin's master. And you surely want to share your changes with your fellow developers. That's where the git push command is useful:
git push origin

git push will start by copying your two commits to the origin, and ask it to update its master to point at the same location than yours. At the end of the operation, both commit trees should match exactly. Note that git push will refuse to push if your origin/master is not up to date.

The "origin" argument to git fetch and git push is optional; git will use all remotes if you don't specify one.

Finally, a note: as fetch and merge are often done together, a git command combines both: git pull. It's smarter than the addition of the two commands, because it knows how to look in your git config what remote branch is actually tracked by your current local branch, and merge from there -- so you don't even need to type the name of origin/master for the merge.

Next time, we'll speak about rebasing.


Alok said...

This is a good explanation.

It would be nice if you could also link to any relevant documentation/howto from the official site. Or if it does not exist, maybe contribute ... ?

sez said...

this is very helpful; thanks!

Xandrani said...

This is highly confusing!

It seems that you have to already understand how git works to understand your explanation! Sorry but I would consider myself intelligent but your explanation is unintelligible.

Thanks for trying though!

Reece said...

Your images have no white background, so they're impossible to read the arrows when you click them because the lightbox-type popup has a very dark background.

rob said...

"Highly confusing" ???
Its the first time someone has been able to explain so I can understand wtf "Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded." really means. A big thank you.

rwhirn said...

@Reece and anyone else:

right click on the popup and select "View Image". This will show the enlarged image with the white background.

Nxjlup said...

Is "origin" just your local representation of the remote branch? So, there's 3 instances: a) local branch b) remote branch c) your local branch representation of the remote branch?