Re: [PATCH] worktree: repair copied repository and linked worktrees

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Eric Sunshine <ericsunshine@xxxxxxxxxxx> writes:

> +	inferred_backlink = infer_backlink(realdotgit.buf);
>  	backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
>  	if (err == READ_GITFILE_ERR_NOT_A_FILE) {
>  		fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
>  		goto done;
>  	} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
> -		if (!(backlink = infer_backlink(realdotgit.buf))) {
> +		if (inferred_backlink) {
> +			/*
> +			 * Worktree's .git file does not point at a repository
> +			 * but we found a .git/worktrees/<id> in this
> +			 * repository with the same <id> as recorded in the
> +			 * worktree's .git file so make the worktree point at
> +			 * the discovered .git/worktrees/<id>. (Note: backlink
> +			 * is already NULL, so no need to free it first.)
> +			 */
> +			backlink = inferred_backlink;
> +			inferred_backlink = NULL;
> +		} else {
>  			fn(1, realdotgit.buf, _("unable to locate reposito
ry; .git file does not reference a repository"), cb_data);
>  			goto done;
>  		}

Moving the `infer_backlink()` call outside of the error condition caused me to
discover a bug in my own refactor of infer_backlink() on my relative paths series.
Turns out I needed to clear the `inferred` path if an error occurred.
This is OK.

> +	/*
> +	 * If we got this far, either the worktree's .git file pointed at a
> +	 * valid repository (i.e. read_gitfile_gently() returned success) or
> +	 * the .git file did not point at a repository but we were able to
> +	 * infer a suitable new value for the .git file by locating a
> +	 * .git/worktrees/<id> in *this* repository corresponding to the <id>
> +	 * recorded in the worktree's .git file.
> +	 *
> +	 * However, if, at this point, inferred_backlink is non-NULL (i.e. we
> +	 * found a suitable .git/worktrees/<id> in *this* repository) *and* the
> +	 * worktree's .git file points at a valid repository *and* those two
> +	 * paths 
differ, then that indicates that the user probably *copied*
> +	 * the main and linked worktrees to a new location as a unit rather
> +	 * than *moving* them. Thus, the copied worktree's .git file actually
> +	 * points at the .git/worktrees/<id> in the *original* repository, not
> +	 * in the "copy" repository. In this case, point the "copy" worktree's
> +	 * .git file at the "copy" repository.
> +	 */
> +	if (inferred_backlink && fspathcmp(backlink, inferred_backlink)) {
> +		free(backlink);
> +		backlink = inferred_backlink;
> +		inferred_backlink = NULL;
> +	}

This edge case seems to primarily be an artifact of using absolute paths to link
the worktrees. The test case you provided passes on my relative path series even
with this condition commented out. However, we do still want to support absolute
paths to maintain backwards compatibility so I think we should try to detect
this edge case if we can.

Unfortunately, this change now "repairs" (i.e., breaks) worktr
ees from other
repositories if the worktree_id happens to be identical between the repositories.
For example, the following test passes on `master` (with absolute paths) and on
my relative path series (relative paths without your changes integrated), but it
fails on your patch (and my relative patch series with your changes integrated):

    test_expect_success 'does not repair worktrees from another repo' '
        test_when_finished "rm -rf repo1 repo2" &&
        mkdir -p repo1 &&
        git -C repo1 init main &&
        test_commit -C repo1/main nothing &&
        git -C repo1/main worktree add ../linked &&
        cp repo1/main/.git/worktrees/linked/gitdir repo1/main.expect &&
        cp repo1/linked/.git repo1/linked.expect &&
        mkdir -p repo2 &&
        git -C repo2 init main &&
        test_commit -C repo2/main nothing &&
        git -C repo2/main worktree add ../linked &&
        cp repo2/main/.git/worktrees/linked/gitdir repo2/main.expect &&
        
cp repo2/linked/.git repo2/linked.expect &&
        git -C repo1/main worktree repair ../.. &&
        test_cmp repo1/main.expect repo1/main/.git/worktrees/linked/gitdir &&
        test_cmp repo1/linked.expect repo1/linked/.git &&
        test_cmp repo2/main.expect repo2/main/.git/worktrees/linked/gitdir &&
        test_cmp repo2/linked.expect repo2/linked/.git &&
        git -C repo2/main worktree repair ../../repo1/linked &&
        test_cmp repo1/main.expect repo1/main/.git/worktrees/linked/gitdir &&
        test_cmp repo1/linked.expect repo1/linked/.git &&
        test_cmp repo2/main.expect repo2/main/.git/worktrees/linked/gitdir &&
        test_cmp repo2/linked.expect repo2/linked/.git
    '

Granted, this is likely a rare edge case, but I would not expect trying to repair
a valid worktree from another repository to change the pointers. I think a good
solution to this would be to introduce a unique hash on the worktree_id when
creating a worktree to guarantee uniqu
eness across repositories, e.g.,:

    repo1/main/.git/worktrees/linked-6a4b5d/gitdir:
        /path/to/repo1/linked/.git
    repo1/linked/.git:
        gitdir: /path/to/repo1/main/worktrees/linked-6a4b5d

    repo2/main/.git/worktrees/linked-9ab26fe/gitdir:
        /path/to/repo2/linked/.git
    repo2/linked/.git:
        gitdir: /path/to/repo2/main/worktrees/linked-9ab26fe

This would allow us to detect this copied case while not "repairing" valid
worktrees from other repositories with the same linked worktree directory name.


Best,

- Caleb

Attachment: signature.asc
Description: OpenPGP digital signature


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux