I have a simple repository and I need to have a branch checked out in a different directory. For that I use git worktree. Normally git worktree add -B hello ../hello master creates a new branch called hello from master and checks it out under ../hello.
That is actually exactly what I am looking for, except for that the directory already exists. According to the documentation -B is supposed to ignore that and just use the files that are in the directory, but it still complains with:
$ git worktree add -B hello ../hello master
Preparing worktree (resetting branch 'hello'; was at 0465e02)
fatal: '../hello' already exists
Why does the command I use not work? See quote from the docs below. Do I misinterpret the docs?
Quote from the documentation:
By default, -b refuses to create a new branch if it already exists. -B overrides this safeguard, resetting < new-branch > to < branch >
This is a basic misunderstanding of the -B option. It tells Git that it is OK for the branch name—in this case, hello—to exist. It is never OK for the added work-tree directory—in this case, ../hello—to already exist.
(A lot of this you already know, based on your question—this is for other readers.)
First: git worktree add adds a new work-tree, not just a directory. The added work-tree comes with an added index: there is one Git index per work-tree. When the git worktree add operation succeeds, you will find, inside the added work-tree, a file named .git that contains the path to the original Git repository. You can cd ../hello or otherwise navigate to the added work-tree and work in it the same way that you would work in your main work-tree. The added work-tree is yours to play with as you like; its Git index is independent of the Git index for your main work-tree.
Second: a branch like master, in Git, is just a name—which you can change at any time, or even delete entirely with no significant effect in some cases1—that lets you find a particular commit. It's the commits, not the branch names, that matter. The commits hold files, and each commit has its own unique true name, which is its hash ID. A branch name, though, just holds the hash ID of one existing commit.
Now:
$ git worktree add -B hello ../hello master
This tells Git to create a new directory named ../hello. This new directory, which should be outside your current work-tree (and probably is given ../hello as the path), needs to not exist. Since ../hello actually does exist, that's why this step fails.
The -B hello part of this git worktree add command tells it that, after successfully creating this new empty directory and then checking out some commit into that work-tree—we'll get to which particular commit in a moment—it should do a transactional operation that makes the branch name hello come into existence (if it did not exist before), or now identify (if it did exist before), the commit you select.
The adjective transactional here means that this step should either succeed entirely, so that branch name hello exists with the right commit, or appear not to have happened at all: if there was already a branch name hello it will remain undisturbed.
The message:
(resetting branch 'hello'; was at 0465e02)
tells you that the branch name did exist, and did point to (contain the hash ID of) the commit whose abbreviated hash ID is 0465e02. Since the actual command ultimately failed, the branch name hello should continue to exist and continue to point to commit 0465e02.
Last, the master part of the git worktree add command supplied the hash ID of the commit that Git was supposed to check out into the added work-tree. If you run:
git rev-parse master
Git will print out the hash ID that was obtained by this step (assuming the name master has not yet been modified to identify some other commit, that is).
The thing as a whole failed, as we already noted, because ../hello already exists (and probably contains files—some Git operations allow directories to exist already as long as they are empty and I don't recall offhand if git worktree add was one in the past, is now, or will be one of them in the future). To fix this, just pick some other name, or—if none of the files in ../hello are valuable—remove ../hello entirely.
1For the deletion of a branch name to be insignificant, you need to be able to find the commit that you would have found via that branch name, by some other means. You also need to not be bothered by git config deleting configuration information (such as the branch's upstream) that is stored under that branch name in the Git configuration file, nor by the reflog for the branch being deleted immediately. (There is some vague speculation about retaining the reflogs for "branch undelete" operations someday, but it has not gone anywhere for a long time.)
Total hack but this works for me because git just tracks filenames:
Assuming I am in, say, ~/code/ and I have a directory project which is my code:
# Let's say I did a poor man's git clone:
$ cp -r project my-poc
# Now I changed some stuff in my-poc and I want to add it
# as a worktree of project, and assuming I don't care about
# the git history in my-poc
$ mv my-poc temp-name
$ (cd project && git worktree add ../my-poc)
$ rm -rf temp-name/.git
$ mv my-poc/.git temp-name
$ rm -rf my-poc
$ mv temp-name my-poc
Basically I just create a fresh worktree and move my existing directory into it. Nothing mind blowing but it seems to answer the question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With