gitgit-worktree

Why is a suffix automatically appended to the git worktree internal directory name?


When creating a new worktree by git worktree add foo <revision>, in the main worktree .git/worktrees/foo/ is also created. I've encountered a situation several times, where it creates .git/worktrees/foo1/ instead. I think 1 is appended because there are some naming conflicts. I'd like to know why this happens and how to replicate the case. Thanks.

FYI, multiple threads may work in the same main worktree to create different worktrees, and git version 2.31.1 on Ubuntu.


Solution

  • The git worktree add code synthesizes the worktree ID (internal name) in add_worktree, using this loop:

    if (safe_create_leading_directories_const(sb_repo.buf))
            die_errno(_("could not create leading directories of '%s'"),
                      sb_repo.buf);
    
    while (mkdir(sb_repo.buf, 0777)) {
            counter++;
            if ((errno != EEXIST) || !counter /* overflow */)
                    die_errno(_("could not create directory of '%s'"),
                              sb_repo.buf);
            strbuf_setlen(&sb_repo, len);
            strbuf_addf(&sb_repo, "%d", counter);
    }
    name = strrchr(sb_repo.buf, '/') + 1;
    

    The initial sb_repo.buf consists of a "sanitized" name derived from the path, where in your case the path is the name foo added to the Git directory and the worktrees subdirectory ($GIT_DIR/worktrees/foo). The len here is the point where the numeric suffix part goes in. So, if the initial mkdir call fails with EEXIST, the foo part of the sb_repo.buf strbuf is replaced with foo1, foo2, and so on, until the mkdir call either succeeds (returns 0) or fails with something other than EEXIST.

    [Sometimes Git] creates .git/worktrees/foo1/ instead. I think 1 is appended because there are some naming conflicts.

    This would happen if there's already a .git/worktrees/foo, so that the mkdir call returned an error with EEXIST as the error number. That in turn would happen if there were a worktree whose path ended with foo, or something that sanitized to foo.]

    [Note that you can also see random errors with Git repositories when they are stored in a cloud-synced folder (ICloud, Dropbox, etc). The cloud-syncing software reaches into the repository and damages it in its attempt to synchronize it. That's probably not the case here though.]

    (It's a bit curious as to why you care where the worktree-specific files live inside $GIT_DIR. I'm not sure why Git goes to all this effort to make a base name, rather than just generating randomized IDs using mkdtemp or an equivalent: had Git done that, people would not be tempted to poke around inside .git/worktrees as much. To use worktree-specific refs like HEAD and bisect revision names, use git rev-parse or git update-ref, which knows how to do this on its own. There is, I think, a bit of a problem with temporary index files: the index file needs to be per-worktree, and it would be nice to have git rev-parse --git-internal-worktree-path or some such for constructing such names.)