Search code examples
gitgit-subtree

What is the technique/process for doing a temporary git checkout into another directory of the current checkout without messing up my current checkout


The scenario is that I'm in the middle of a task, in a feature branch, and then I've been asked to do a quick hot fix, or do some tests on another branch.

And so, one approach is to close up my current work, either stashing (and hope I remember that I've stashed it), or commit it to my local repo, with then the option of pushing that with a "WIP" prefix (Work In Progress).

All of which are really annoying and can take time.

What I would like to happen is that I run something like:

git fetch
git something to clone the required branch, or create a new branch for the required hotfix into a separate directory with that directory not needing to have the full .git folder
cd to other folder
do the work
git add .
git commit -m 'Some sarky comment that no one reads'
git push
cd back to my main checkout
git something to make it forget all about the other directory and maybe even delete it!
and just carry without needing to unstash or any other waste of time stuff

Of course, this could all be wrapped up into a shell function or script, and so be simplified.

I had thought sub-trees where the thing, but I couldn't get the subtree created.

As an example, working with app and wanting to look at master:

$ git subtree add -d --prefix=../app-master/ origin master
command: {add}
quiet: {}
dir: {../app-master}
opts: {origin master}

git fetch origin master
From bitbucket.org:digitickets/app
 * branch                  master     -> FETCH_HEAD
Adding ../app-master as 'b03a1ea06e26cfae0d49483c9027ecd6aab210c8'...
error: invalid path '../app-master/.babelrc'

At this point in time, the ../app-master directory doesn't exist.

If I create that directory:

$ git subtree add -d --prefix=../app-master/ origin master
prefix '../app-master' already exists.

The error that git-subtree is making is in response to:

git read-tree --prefix=../app-master b03a1ea06e26cfae0d49483c9027ecd6aab210c8

which sort of suggests that the content of ../app-master should already be populated.

And so, I think I've misunderstood what Git's SubTrees are all about or that I'm attempting to misuse them and so not going to work.

Any suggestions?


Solution

  • TL;DR

    Use git worktree.

    Long-ish

    A Git repository fundamentally consists of:

    • a database of Git's commits and other internal Git objects, indexed by OIDs (Object IDs) or hash IDs;
    • a database of names—branch and tag names for instance—so that humans don't have to memorize big, ugly, random-looking hash IDs; and
    • for a usable repository, in which you can get work done, one (1) working tree or work-tree where you do your work.

    For servers that simply receive git push requests, we drop the last item: we create a so-called bare repository that has no working tree. This is specifically so that nobody can get any work done in the non-existent working tree. See also:

    But, as you've experienced, this leaves us with a kind of a gap: what happens when we want to have more than one working tree so that we can work on more than one feature at a time?

    The standard historical answer to this question was always "make another clone". If having two clones—origin over on GitHub, say, and the one on your laptop where you're working—is good, wouldn't three clones be better? It's really better when that third clone is on Bob's machine instead of yours and Bob is the one doing the other feature. But even if it's just on your own machine, you can always make another clone. Each (non-bare) clone you make has a working tree, providing you with a place to get work done.

    For many years, this was the state of things in Git. Some people objected somewhat, now and then: "why should I pay for the cost (in network using and/or disk space on my laptop, for instance) for another clone?" But on the other hand, in the early Aughties or whatever we call the 2005-ish time period these days, a "big project" was a often just a couple of megabytes. These days, an accidental phone butt-dial-texted photo uses ten or a hundred times that space and nobody cares. On the other hand, now we have some bigger projects, where we clone a couple dozen gigabytes or whatever before we even start. So people complain as much or more now, about running out of space to hold all these clones.

    What if we could create a second working tree for the original clone? Well, we can. Since Git version 2.5, Git has supported the git worktree command. (It has a particularly bad bug in the initial implementation, fixed in Git 2.15, with further smaller fixes since then, so try to use a more recent Git version when using git worktree.) Using git worktree, we can add as many extra working trees as we like to any given Git repository.

    There is a peculiar constraint that occurs because these added repositories share the names database (branch and tag names and the like): each added working tree must use either a detached HEAD, or a branch name that no other working tree is using. Each added working tree gets its own separate HEAD reference, its own index / staging-area, and some other private special-case items (e.g., git bisect refs), so that, in general, except for the "different branch name" constraint and the fact that these working trees share a single underlying objects database as well (which saves disk space), this is otherwise like making extra clones.

    You can remove an added working tree with git worktree remove (new in Git 2.17) or by simply removing the entire added working tree with an OS-level removal tool and running git worktree prune (original to Git 2.5). There are some other minor considerations to be aware of, so read the documentation thoroughly.