gittagsvcs-checkout

Git - Switching between tags


I have an issue when i'm trying to check out a different tag on the same commit.

This becomes an issue in an application where i check where the detached head is at.

I believe this is because the commit is tagged with both 4.1 and 4.1.1 but i need the head to explicitly say "HEAD detached at 4.1" when i'm checking out tags/4.1 from the 4.1.1 tag not display the same "HEAD detached at 4.1.1" text.

Unable to update detached head from 4.1 -> 4.1.1

4.1.1 -> 4.1

Unable to update detached head from 4.1.1 -> 4.1

4.1.1 -> 4.1

In the last checkout i cannot switch from 4.1 to 4.1.1, probably because the HEAD is already at the commit, is there someway to force the "HEAD detached at N" without like checking out the master branch in this case and then checking out the tag.

Since this is supposed to be used in production when there might be a few users online i want the checkout to be as smooth and clear (where the head is detached) as possible.


Solution

  • TL;DR: apparently, you would have to cheat. I don't recommend doing this, but show how it would work, below.

    In the last checkout I cannot switch from 4.1 to 4.1.1, probably because the HEAD is already at the commit ...

    More or less, yes. It's more accurate to say that "switching" from 4.1 to 4.1.1 is not switching anything at all, because 4.1 and 4.1.1 are the same commit. Git takes a shortcut and does not write anything at all, because nothing needs to be written. Well, that is, except:

    is there someway to force the "HEAD detached at N" without like checking out the master branch in this case and then checking out the tag.

    The "detached at / from" that git status reports is based on a bread-crumb trail left behind in the HEAD reflog. When you run git checkout X for some X and Git updates HEAD, Git first saves the current value of HEAD into the reflog for HEAD, along with a comment, such as this one:

    8858448bb4 HEAD@{0}: checkout: moving from pu to master
    

    (pu is the "proposed update" or "pickup" branch in the Git repository for Git itself, so the above entry is the result of me doing git checkout pu, which created pu from origin/pu and gave it the value 8265814db9543fbaf50c4db8133671ce64dc1ae4, and then git checkout master. The motion from master to the newly created pu made HEAD@{1}, whose comment is checkout: moving from master to pu.)

    When HEAD is detached, what git status does is to root through the reflog for HEAD to try to find which branch or tag you had explicitly checked-out before you got to whatever commit you are on right now. If the detached-HEAD commit you are on right now matches the value of that branch or tag name, you are detached at Y where Y comes from the comment. If the detached-HEAD commit you are on right now doesn't match (or maybe is not @{0}), git status reports detached from Y instead of detached at Y.

    So, if you could get the reflog updated, you could get git status to report something new. The obvious way to do that is git update-ref, but I tried it. Here is what happened. First, we set up the situation:

    $ git checkout v2.18.0
    [snip]
    HEAD is now at 53f9a3e157 Git 2.18
    $ git tag haha
    $ git checkout haha
    HEAD is now at 53f9a3e157 Git 2.18
    $ git reflog | head -1
    53f9a3e157 HEAD@{0}: checkout: moving from master to v2.18.0
    

    and of course git status reports "detached at v2.18.0". So:

    $ git update-ref -m "moving from v2.18.0 to haha" HEAD HEAD
    $ git reflog | head -1
    53f9a3e157 HEAD@{0}: checkout: moving from master to v2.18.0
    

    Alas, git update-ref also took a short-cut and did not bother to update the reflog. (Perhaps the git checkout haha did invoke the reflog update code, but that took the shortcut.) So, it's time to cheat—note that I'm not advising that anyone actually do this!—by knowing that a HEAD reflog update just adds a line to .git/logs/HEAD:

    $ ed .git/logs/HEAD 
    16108
    $t$
    $s/from master to v2.18.0/from v2.18.0 to haha/ 
    w
    16281
    q
    $ git reflog | head -2
    53f9a3e157 HEAD@{0}: checkout: moving from v2.18.0 to haha
    53f9a3e157 HEAD@{1}: checkout: moving from master to v2.18.0
    $ git status
    HEAD detached at haha
    nothing to commit, working tree clean
    

    The $t$ command duplicated the last line; then $s/from master to v2.18.0/from v2.18.0 to haha/ replaced the comment with a new comment. So now git status reports what we want it to report.

    It's pretty clearly a bug that git update-ref short-circuits the update. It might also be an important or useful optimization, but if so, there should be some flag to enable the optimization or force the update, based on whichever default is deemed more important. The bug would be irrelevant except for this whole git status relies on the reflog thing.