Search code examples
gitgit-fetch

How should I read the message a git fetch returns?


I just did

get fetch origin feature/8067

and below it I got 3 columns:

 * branch                feature/8067 -> FETCH_HEAD
 * [new branch]          feature/8067 -> origin/feature/8067

Trying to process this...reading from here I just learned that FETCH_HEAD basically means the tip of where I last did a fetch. The file will contain a commit.

Does that [new branch] refer to the new branch created under my refs/remote?

I'm not sure if my reading of the following is correct: feature/8067 -> origin/feature/8067

Is the 2nd column the <nameOfBranchOnRemoteRepo> while the 3rd column is <repoName/nameOfBranchOnRemoteRepo> and it says my fetched remote branch in the refs is pointing to that in the remote?


Solution

  • The fetch output is confusing even to an old hand. Here's how I decode them:

     * branch                feature/8067 -> FETCH_HEAD
     * [new branch]          feature/8067 -> origin/feature/8067
    

    Work each line from right to left:

    • The first line ends with FETCH_HEAD, which means that the reference was deposited into FETCH_HEAD. See the note about FETCH_HEAD below. The arrow is hardcoded (always appears and you can just sort of ignore it); the name feature/8067 is the name of the reference on the remote; and * branch tells you that it was really refs/heads/feature/8067 on the remote, i.e., was a branch. Because this was deposited into FETCH_HEAD there's no additional information available.

    • The second line ends with origin/feature/8067. Your remote-tracking name1 origin/feature/8067 (full name refs/remotes/origin/feature/8067) has been created or updated. As before, we have the arrow and the same name. Then we have * [new branch]: this tells us that origin/feature/8067 did not exist before, and that feature/8067 was—as we already know—a branch name on the remote.

    Here's what I get updating a Git repository for Git:

       ab15ad1a3b..aa25c82427  master      -> origin/master
       ef7435264c..5a294203ad  next        -> origin/next
     + f98c0007ae...e49ac33073 pu          -> origin/pu  (forced update)
       0f4b6a451a..ff8db353a4  todo        -> origin/todo
     * [new tag]               v2.22.0-rc1 -> v2.22.0-rc1
    

    Again we can work right to left:

    • My origin/master got created-or-updated, from their master. The value of my origin/master was ab15ad1a3b but is now aa25c82427. Since there was a value, it's been updated, not created.

    • My origin/next has been created-or-updated, with everything else basically the same as above (modulo the obvious differences).

    • My origin/pu has been updated by force, i.e., this is not a fast-forward and some commits have been deleted. The source was their pu branch and my origin/pu used to be f98c0007ae but is now ff8db353a4. The fact that there are three dots between the two hash IDs—the other lines have only two dots—means that the update was forced, hence not a fast-forward. The plus sign at the very front means that the update was forced. (Clearly, it's pretty important that the update was forced: I have three announcements to that effect!)

    • My origin/todo has been created-or-updated, and by the time we get to the left side, it's obviously an update.

    • My v2.22.0-rc1 has been created-or-updated, from their v2.22.0-rc1; this is a new tag.

    Any time you have a new name (branch, tag, or any other reference), it's by definition a regular non-forced creation and there's no old..new hash available, so the left edge will read * [new whatever].

    FETCH_HEAD is special: all updates get written to .git/FETCH_HEAD, usually wiping out whatever was in it before (but with -a or --append, git fetch will append instead). Each fetched reference results in one line appearing in FETCH_HEAD, giving:

    • the hash ID
    • an optional not-for-merge string
    • the type and name of the reference and its source:

      $ cat .git/FETCH_HEAD
      aa25c82427ae70aebf3b8f970f2afd54e9a2a8c6        branch 'master' of git://...
      [snipped for length]
      

    The one line that is not marked not-for-merge is suitable for the git pull script2 to fish out and pass that hash ID to git merge or git rebase.


    1A remote-tracking name, which most of Git calls a remote-tracking branch, is a reference in your repository whose full name starts with refs/remotes/ and goes on to include the name of the remote, in this case origin, and another slash, and then normally holds the rest of the branch name as seen on that remote.

    A reference is just the generalized name for things that are branches, tags, remote-tracking names, refs/stash, and so on: a string-format name, generally starting with refs/, that remembers one hash ID. For a branch, the one hash ID the name remembers is the commit that Git should consider to be the tip of that branch. For a tag, the one hash ID the name remembers is either the hash ID of the commit, or the hash ID of an annotated tag object that contains additional information (perhaps including a signing key), plus the hash ID of the tagged object (typically a commit though any tag can point to any one of Git's internal object types).

    Git builds remote-tracking names through a refspec. You can supply a refspec when you run git fetch. If you do not supply a refspec, but do supply a remote name like origin, Git fishes the correct refspec from your configuration:

    $ git config --get-all remote.origin.fetch
    +refs/heads/*:refs/remotes/origin/*
    

    A standard configuration for origin always has this exact default refspec, but there are some useful nonstandard configurations, such as the one created by git clone --single-branch. You can also make your own totally bizarre refspecs, though depending on how twisted you get, some combinations will result in a non-functioning git fetch.

    2Well, back when when git pull was a script, anyway. It's been re-coded in C for speed on Windows.