One of the things that confuses many new git users is how branches work between a local repository and a remote. This is always something I have to explain in my git training classes, so I figure that explaining it here might be a good idea.
I’m going to use git’s own repository for this demonstration, because it doesn’t have that many branches. So, let’s clone that:
kate@deadmooncow ~/example$ git clone \ git://git.kernel.org/pub/scm/git/git.git Initialized empty Git repository in /Users/kate/example/git/.git/ remote: Counting objects: 127827, done. remote: Compressing objects: 100% (30677/30677), done. remote: Total 127827 (delta 95536), reused 127502 (delta 95329) Receiving objects: 100% (127827/127827), 24.76 MiB | 13.42 MiB/s, done. Resolving deltas: 100% (95536/95536), done. kate@deadmooncow ~/example$ cd git kate@deadmooncow ~/example/git(master)$ git branch * master
Hmmm … isn’t a clone supposed to be a complete copy of the original? Where are all the branches? They’re there, as we can see with “git branch -a”:
kate@deadmooncow ~/example/git(master)$ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/html remotes/origin/maint remotes/origin/man remotes/origin/master remotes/origin/next remotes/origin/pu remotes/origin/todo
Ah, there they are!
What’s going on here? Git really has three kinds of branches, two of which are devoted to maintaining the relationship between a clone and the original repository (which git refers to as “origin” by default). These are: what I like to call purely local branches, which exist only in the clone; tracking branches, which are local working copies of remote branches; and remotes, which are local copies of the remote branches as they exist(ed) on the remote.
Conceptually (the actual implementation is a little more complicated), when you clone a repository, git copies all of the branches into an area called “remotes/origin”, creates a local tracking branch of “master” to track remotes/origin/master, and checks it out. When you type just “git branch”, it shows you all of your local branches, whether they track a remote branch or not (I’ll get to purely local branches is a second), so on a freshly cloned repository, you will normally see just “master”.
You can make local working copies of any of the remote branches easily enough:
kate@deadmooncow ~/example/git(master)$ git checkout -b maint origin/maint Branch maint set up to track remote branch maint from origin. Switched to a new branch 'maint'
Alternatively, you can make a purely local branch of your own, one that doesn’t exist on the original repository:
kate@deadmooncow ~/example/git(maint)$ git checkout master Switched to branch 'master' kate@deadmooncow ~/example/git(master)$ git branch my-local-branch kate@deadmooncow ~/example/git(master)$ git checkout my-local-branch# two step way: Switched to branch 'my-local-branch' # one step way: kate@deadmooncow ~/example/git(maint)$ git checkout master Switched to branch 'master' kate@deadmooncow ~/example/git(my-local-branch)$ git checkout -b local-branch Switched to a new branch 'local-branch'
Both of these branches are based on master. Now, ‘git branch’ shows these branches, plus my two tracking branches:
kate@deadmooncow ~/example/git(local-branch)$ git branch * local-branch maint master my-local-branch
(The ‘*’ next to ‘local-branch’ indicates the currently checked-out branch; my prompt also indicates the branch I’m on).
When you make changes to a tracking branch, such as master, and commit, your changes are stored only in the local branch ‘master’, not in ‘remotes/origin/master’. This is because ‘remotes/origin/master’ always reflects the state of the remote repository’s ‘master’ at the last time that you fetched from the remote repo. When you run “git fetch”, it updates all of the ‘origin’ branches with the latest content from the remote repo. You can then use “git merge” to merge those changes with your local tracking branch (this example is fake because no one pushed to git while I was writing this ):
kate@deadmooncow ~/example/git(master)$ git fetch kate@deadmooncow ~/example/git(local-branch)$ git checkout master Switched to branch 'master' kate@deadmooncow ~/example/git(master)$ git merge origin/master Updating 87b5054..2a10b71 Fast-forward Documentation/config.txt | 7 + Documentation/diff-options.txt | 6 +- Documentation/git-fmt-merge-msg.txt | 16 ++- Documentation/git-format-patch.txt | 2 +- Documentation/git-read-tree.txt | 6 +- : : t/t7810-grep.sh | 29 ++++- t/t9001-send-email.sh | 36 +++++ tree-diff.c | 29 ++-- userdiff.c | 17 +++ 59 files changed, 1405 insertions(+), 317 deletions(-) create mode 100644 contrib/git-shell-commands/README create mode 100755 contrib/git-shell-commands/help create mode 100755 contrib/git-shell-commands/list create mode 100755 t/t3032-merge-recursive-options.sh create mode 100644 t/t4013/diff.log_-GF_-p_--pickaxe-all_master create mode 100644 t/t4013/diff.log_-GF_-p_master create mode 100644 t/t4013/diff.log_-GF_master
This sequence (fetch, then merge) is so common that git abbreviates it as “git pull”. One gotcha with “git pull,” however, is that the merge only happens in the currently checked-out branch. This bites even experienced users, who sometimes wonder why the changes they thought they pulled aren’t showing up in their tracking branch.
Here’s a diagram that shows this set-up:
The purely local branches have no equivalent on the remote. You can turn one into a remote branch by pushing it to the origin:
kate@deadmooncow ~/example/git(master)$ git checkout my-local-branch Switched to branch 'my-local-branch' kate@deadmooncow ~/example/git(my-local-branch)$ git push origin my-local-branch : : kate@deadmooncow ~/example/git(my-local-branch)$ git branch --set-upstream my-local-branch origin/my-local-branch
The last command is a handy feature in new versions (>1.7, I believe) of git to let you make your local branch track the new remote branch you just created. (This is not done automatically.)
I hope this helps you wrap your brain around git branches.