git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Terminology question about remote branches.

From
Jeff King <peff@peff.net>
Date
Aug 5, 2007, 09:21 UTC
Message-ID
<20070805092115.GA12507@coredump.intra.peff.net>
In-Reply-To
<85tzrfh3yg.fsf@lola.goethe.zz>
On Sat, Aug 04, 2007 at 02:36:07PM +0200, David Kastrup wrote:
Show 6 quoted lines
> And it's actually worse after your explanations.  Previously I
> imagined to have a chance to figure this out on my own, by trying to
> abstract from what I see happening when using the various commands.
> 
> Now I think that I basically have no chance figuring this out on my
> own sufficiently well to be able to improve the documentation.

I see that Lars and others have provided some explanations in my absence, but let me try to lay it out from basics, and hopefully between all of our writing it will make sense. I'm going to try to be as basic as possible, so if I am telling you something you already know, it's not because I think you're stupid, but because I'm trying to be thorough.

Git history is a directed graph of commit objects, with each commit object pointing to its parent (or parents if it is a merge). We have human-readable names pointing into the history as well, which we call refs, and generally store under the "refs/" hierarchy (with a few exceptions, which I will mention in a minute).

There are a few different types of pointers (refs) that are useful to us. They are differentiated by the types of things we want to do with them.

  1. refs that track our ongoing commits. This process involves making
     a new commit object whose parent is the previous value of the ref,
     and then pointing the ref at the new commit. We generally call
     these refs "heads", "local branches", or just "branches", and they
     are stored in "refs/heads/".
  2. refs that point to a single commit and aren't changed. These refs
     are "tags", and we store them in "refs/tags/".
  3. refs that represent a remote repository's local branch. These are
     updated by git-fetch, which simply writes the new value of the
     pointer into our local copy, throwing out the old value.  These are
     called "remote tracking branches" and are stored in
     "refs/remotes/<remote>/<branch>".
  4. Temporary pointers to help out some multi-step operation that we're
     in the middle of. These include FETCH_HEAD and MERGE_HEAD.
There is an order of ref lookup which goes like:
  .git/<name>
  .git/refs/<name>
  .git/refs/tags/<name>
  .git/refs/heads/<name>
  .git/refs/remotes/<name>

It used to be the case (prior to git 1.5) that remote tracking branches and local branches were stored in the same hierarchy (under refs/heads/). This turned out to be problematic for many users, because the operations that you perform on them don't play well together.

For example, let's say you have a branch "origin" representing Junio's "master" branch. You check it out and make a commit. This rewrites the "origin" ref, but it's safe because the new commit points to the old value. Now you want to fetch more work from Junio, so you run "git-fetch". But the value of Junio's ref doesn't ever reference your work. If git-fetch copies the value of Junio's new ref into your "origin", then it will throw away the work that you were done (i.e., no ref will be pointing to it, or to any commit that references it). But if git-fetch doesn't copy the value of Junio's new ref, then how will you get to see his new work?

So it is a mistake to create commits on top of a remote tracking branch. The new strategy is therefore to store them in refs/remotes, which is an indicator that they are purely for remote tracking. If you attempt to git-checkout a branch in refs/remotes, you will get a detached HEAD rather than actually checking out that branch.

Normally "HEAD" is a pointer to a ref in refs/heads/ (which is itself a pointer to a commit object) representing "the current branch." When HEAD becomes detached, we mean that instead of pointing to a branch ref, it points directly to a commit object. So when you do "git-checkout origin/master" (which, unless you have a local branch named "origin/master" will end up looking in refs/remotes/origin/master), it doesn't put "refs/remotes/origin/master" in your HEAD, but rather the _value_ of that ref.

The implication here is that commits you create on a detached HEAD are not stored by any ref except HEAD, and will be lost when you move the HEAD elsewhere (i.e., when you check out a different branch).

Tags are in a similar situation. You don't want to make commits on tags; instead, you want to simply set them to a pre-existing commit. Thus when you check out a tag, you get a detached HEAD (and we know it's a tag because it's in refs/tags, not refs/heads).

So hopefully at this point you understand "local branch" and "remote tracking branch."

To get to the final concept you mentioned, let's take a look at what fetch and pull do.

Let's say you run:
  git-fetch git://git.kernel.org/pub/scm/git/git.git

That stores the value of Junio's current branch (which tends to be "master") into your local FETCH_HEAD (stored in ".git/FETCH_HEAD").

And if you run:
  git-fetch git://git.kernel.org/pub/scm/git/git.git next
Then we store the value of Junio's next branch into your FETCH_HEAD.
And finally, if you run:
  git-fetch \
    git://git.kernel.org/pub/scm/git/git.git \
    next:refs/remotes/junio/next

then we store Junio's next branch in our FETCH_HEAD, but _also_ store it in the remote tracking branch refs/remotes/junio/next.

The second argument to fetch is called a "refspec", and you can have several of them. Because all of that gets tedious to type, we have the concept of configured remotes, which are a shorthand for a URL and associated refspecs. When you use "git-clone", it creates the following config:

  [remote "origin"]
    url = git://git.kernel.org/pub/scm/git/git.git
    fetch = +refs/heads/*:refs/remotes/origin/*

The refspec in this case uses a wildcard to get _all_ of Junio's branches, and store them by name under refs/remotes/origin. So we can just say "git-fetch origin" and it will populate our FETCH_HEAD as well as the refs/remotes/origin directory.

If we call "git-fetch" without any arguments, it will look up the default remote in our configuration. git-clone also creates the following config:

  [branch "master"]
    remote = origin
    merge = refs/heads/master

which means "if we are on the master branch, default fetches to the remote 'origin'". I will explain the merge line in a minute.

So now we look at git-pull. Recall that a pull is basically a fetch followed by a merge. If you run:

  git-pull git://git.kernel.org/pub/scm/git/git.git

it will do a git-fetch of that URL (grabbing the current branch), followed by a git-merge of FETCH_HEAD.

If you run:
  git-pull origin master

it will fetch the "origin" remote, putting all branches into the FETCH_HEAD (and storing them in refs/remotes/origin as a side effect). Then it will pick the branch named 'master' out of FETCH_HEAD, and merge it.

If you run:
  git-pull origin

it will fetch origin as usual, but which branch gets merged? The answer is the value of the "merge" field in the 'branch "master"' section of your config (which is generally created by git-clone).

And of course, if you run:
  git-pull

it will look up the remote to fetch in the config (just as "git-fetch" would), and then find the branch to merge in the config.

So how do we this configuration set up? There are three ways:
  1. Edit your .git/config by hand. :)
  2. git-clone sets up a master/origin relationship, adding both the
     "remote" and "branch" sections.
  3a. Using git-remote, you can add new "remote" sections easily.
  3b. Using the --track option to git-branch, you can set up the
      "branch" section automatically.

What you had called a "remote-tracking branch" is really a branch for which the 'branch "<name>"' config section has been set up (which could come about in several ways). I haven't really seen a term such as "remote-tracking branch" for this in use in the documentation (and I think we both agree that is not the right term because of its confusing similarity to remote tracking branches, discussed above).

I know that was a pretty long email, but hopefully you understand the context of the terms a bit more, and I didn't bore you too much. ;) Feel free to ask questions if there are parts that are unclear.

-Peff
Previous: David KastrupNext: David Kastrup
Message 19 of 46 in “Terminology question about remote branches.”
  1. David KastrupAug 4, 2007
  2. Jeff KingAug 4, 2007
  3. David KastrupAug 4, 2007
  4. Lars HjemliAug 4, 2007
  5. David KastrupAug 4, 2007
  6. Lars HjemliAug 4, 2007
  7. David KastrupAug 4, 2007
  8. David KastrupAug 4, 2007
  9. Lars HjemliAug 4, 2007
  10. David KastrupAug 4, 2007
  11. Lars HjemliAug 4, 2007
  12. Jeff KingAug 5, 2007
  13. Julian PhillipsAug 4, 2007
  14. David KastrupAug 4, 2007
  15. Julian PhillipsAug 4, 2007
  16. David KastrupAug 4, 2007
  17. Theodore TsoAug 4, 2007
  18. David KastrupAug 5, 2007
  19. Jeff KingAug 5, 2007
  20. David KastrupAug 5, 2007
  21. Jeff KingAug 5, 2007
  22. David KastrupAug 5, 2007
  23. Jeff KingAug 5, 2007
  24. Jakub NarebskiAug 4, 2007
  25. SeanAug 4, 2007
  26. David KastrupAug 4, 2007
  27. SeanAug 4, 2007
  28. David KastrupAug 4, 2007
  29. Jeff KingAug 5, 2007
  30. Jeff KingAug 5, 2007
  31. Steffen ProhaskaAug 5, 2007
  32. Jeff KingAug 5, 2007
  33. David KastrupAug 5, 2007
  34. Jeff KingAug 5, 2007
  35. David KastrupAug 5, 2007
  36. Jeff KingAug 5, 2007
  37. Theodore TsoAug 5, 2007
  38. David KastrupAug 5, 2007
  39. Randal L. SchwartzAug 5, 2007
  40. SeanAug 5, 2007
  41. Jeff KingAug 5, 2007
  42. Junio C HamanoAug 5, 2007
  43. Steffen ProhaskaAug 5, 2007
  44. Julian PhillipsAug 5, 2007
  45. David KastrupAug 5, 2007
  46. Julian PhillipsAug 5, 2007

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.