Home > other >  Why does extracting a specific git tag to a /tmp directory mess up the working tree?
Why does extracting a specific git tag to a /tmp directory mess up the working tree?

Time:02-11

Because I wanted to look at the files in the last release, tagged v2.1, to run it in isolation from my working directory, I created a tmp directory

mkdir /tmp/lastRelease

and extracted the last release in that directory

git --work-tree=/tmp/lastRelease checkout tags/v2.1 -- .

The files are indeed extracted just fine to /tmp/lastRelease.

But now when I run git status in the working directory I find that very many files (all the files modified since tag v2.1?) appear as staged for commit, and another long list of files (that are in HEAD) appears as not staged for commit.

Was this last command the right way to extract a specific tag? How do I reset to HEAD? (The obvious git checkout HEAD doesn't modify what git status says, even though the files are there.)

CodePudding user response:

It doesn't (do anything to your current working tree). What it does effectively damage / mess-up is the (singular) index. (It also will affect HEAD.)

Each non-bare Git repository has one working tree, one index, and one HEAD. That one index is used by git checkout or git switch; that one HEAD is set by it. When you run:

git --work-tree=<path> checkout ...

your Git extracts the specified commit—the ... part—and writes it through the index into the working tree.

Normally, the working tree to which your git checkout or git switch would write is the (singular) working tree. But with --work-tree you temporarily override that one working tree with one different working tree—so that is the working tree to which this Git operation writes. But you have not changed the (singular) index, nor the (singular) HEAD, and git checkout or git switch will write to those as well.

The HEAD keeps track of the current commit by containing:

  • a raw hash ID, or
  • the name of the current branch (with the branch name itself then containing the commit hash ID).

The index holds your proposed next commit, which starts out matching the current commit—so that when you switch commits, Git first removes the existing checked-out commit's files (as recorded in the index and stored in the existing in the working tree), and then plugs in the target commit's files (to the index and working tree). Using --work-tree to re-point the working tree, without first cleaning out and removing the current working tree and index, leaves them out of sync.

If you don't mind the effect on HEAD, and are otherwise using this git checkout or git switch for a special purpose, consider setting GIT_INDEX_FILE to the name of a temporary file that does not exist now, and that you will delete once the git checkout or git switch finishes.

If you do mind the effect on HEAD, or intend to do this for more general purposes, consider not using git --work-tree checkout like this at all: instead, make sure your Git is at least 2.15, and begin using git worktree. See the git worktree documentation for more about git worktree.

CodePudding user response:

This is only a partial answer. I don't know why the working directory gets messed up when extracting to an unrelated directory, but it's possible to return to a sane state—the one where the repo was just before the git --work-tree... command—by running:

git reset --hard HEAD

CodePudding user response:

What you wanted to do was either

git archive v2.1 | tar Cx /path/to/tmp/tree

or

git worktree add /path/to/tmp/tree v2.1

or

git clone -sb v2.3 . /path/to/tmp/tree

or even

scratch=`mktemp -d`
GIT_INDEX_FILE=$PWD/.git/scratch-index git --work-tree=$scratch read-tree -um v2.1

because as @torek points out git checkout is a convenience command, built for "standard" workflows where your work trees are for content you're actively tracking; the index is Git's manifest for what you last checked out or added there, and HEAD is its ancestor commit.

  •  Tags:  
  • git
  • Related