git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Eric Sunshine <sunshine@sunshineco.com>
To: git@vger.kernel.org
Cc: Jeff King <peff@peff.net>, Duy Nguyen <pclouds@gmail.com>,
	Eric Sunshine <sunshine@sunshineco.com>
Subject: [PATCH 5/9] worktree: disallow adding same path multiple times
Date: Tue, 28 Aug 2018 17:20:22 -0400	[thread overview]
Message-ID: <20180828212026.21989-6-sunshine@sunshineco.com> (raw)
In-Reply-To: <20180828212026.21989-1-sunshine@sunshineco.com>

A given path should only ever be associated with a single registered
worktree. This invariant is enforced by refusing to create a new
worktree at a given path if that path already exists. For example:

    $ git worktree add -q --detach foo
    $ git worktree add -q --detach foo
    fatal: 'foo' already exists

However, the check can be fooled, and the invariant broken, if the
path is missing. Continuing the example:

    $ rm -fr foo
    $ git worktree add -q --detach foo
    $ git worktree list
    ...      eadebfe [master]
    .../foo  eadebfe (detached HEAD)
    .../foo  eadebfe (detached HEAD)

This "corruption" leads to the unfortunate situation in which the
worktree can not be removed:

    $ git worktree remove foo
    fatal: validation failed, cannot remove working tree: '.../foo'
    does not point back to '.git/worktrees/foo'

Nor can the bogus entry be pruned:

    $ git worktree prune -v
    $ git worktree list
    ...      eadebfe [master]
    .../foo  eadebfe (detached HEAD)
    .../foo  eadebfe (detached HEAD)

without first deleting the worktree directory manually:

    $ rm -fr foo
    $ git worktree prune -v
    Removing .../foo: gitdir file points to non-existent location
    Removing .../foo1: gitdir file points to non-existent location
    $ git worktree list
    ...  eadebfe [master]

or by manually deleting the worktree entry in .git/worktrees.

To address this problem, upgrade "git worktree add" validation to
allow worktree creation only if the given path is not already
associated with an existing worktree (even if the path itself is
non-existent), thus preventing such bogus worktree entries from being
created in the first place.

Reported-by: Jeff King <peff@peff.net>
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 builtin/worktree.c      | 25 +++++++++++++++++++++++++
 t/t2025-worktree-add.sh |  7 +++++++
 2 files changed, 32 insertions(+)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index 46c93771e8..1122f27b5f 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -221,8 +221,33 @@ static const char *worktree_basename(const char *path, int *olen)
 
 static void validate_worktree_add(const char *path, const struct add_opts *opts)
 {
+	struct worktree **worktrees;
+	struct worktree *wt;
+	int locked;
+
 	if (file_exists(path) && !is_empty_dir(path))
 		die(_("'%s' already exists"), path);
+
+	worktrees = get_worktrees(0);
+	/*
+	 * find_worktree()'s suffix matching may undesirably find the main
+	 * rather than a linked worktree (for instance, when the basenames
+	 * of the main worktree and the one being created are the same).
+	 * We're only interested in linked worktrees, so skip the main
+	 * worktree with +1.
+	 */
+	wt = find_worktree(worktrees + 1, NULL, path);
+	if (!wt)
+		goto done;
+
+	locked = !!is_worktree_locked(wt);
+	if (locked)
+		die(_("'%s' is a missing but locked worktree;\nuse 'unlock' and 'prune' or 'remove' to clear"), path);
+	else
+		die(_("'%s' is a missing but already registered worktree;\nuse 'prune' or 'remove' to clear"), path);
+
+done:
+	free_worktrees(worktrees);
 }
 
 static int add_worktree(const char *path, const char *refname,
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 07d292317c..67fccc6591 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -552,4 +552,11 @@ test_expect_success '"add" in bare repo invokes post-checkout hook' '
 	test_cmp hook.expect goozy/hook.actual
 '
 
+test_expect_success '"add" an existing but missing worktree' '
+	git worktree add --detach pneu &&
+	test_must_fail git worktree add --detach pneu &&
+	rm -fr pneu &&
+	test_must_fail git worktree add --detach pneu
+'
+
 test_done
-- 
2.19.0.rc1.350.ge57e33dbd1


  parent reply	other threads:[~2018-08-28 21:21 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-28 21:20 [PATCH 0/9] worktree: fix bugs and broaden --force applicability Eric Sunshine
2018-08-28 21:20 ` [PATCH 1/9] worktree: don't die() in library function find_worktree() Eric Sunshine
2018-08-28 21:20 ` [PATCH 2/9] worktree: move delete_git_dir() earlier in file for upcoming new callers Eric Sunshine
2018-08-28 21:20 ` [PATCH 3/9] worktree: generalize delete_git_dir() to reduce code duplication Eric Sunshine
2018-08-30  6:57   ` Jeff King
2018-08-30  8:45     ` Eric Sunshine
2018-08-28 21:20 ` [PATCH 4/9] worktree: prepare for more checks of whether path can become worktree Eric Sunshine
2018-08-28 21:20 ` Eric Sunshine [this message]
2018-08-30  7:28   ` [PATCH 5/9] worktree: disallow adding same path multiple times Jeff King
2018-08-30  8:22     ` Eric Sunshine
2018-08-28 21:20 ` [PATCH 6/9] worktree: teach 'add' to respect --force for registered but missing path Eric Sunshine
2018-08-30  7:36   ` Jeff King
2018-08-30  8:26     ` Eric Sunshine
2018-08-28 21:20 ` [PATCH 7/9] worktree: teach 'move' to override lock when --force given twice Eric Sunshine
2018-08-30  7:38   ` Jeff King
2018-08-30  8:30     ` Eric Sunshine
2018-08-28 21:20 ` [PATCH 8/9] worktree: teach 'remove' " Eric Sunshine
2018-08-30  7:40   ` Jeff King
2018-08-30  8:33     ` Eric Sunshine
2018-08-28 21:20 ` [PATCH 9/9] worktree: delete .git/worktrees if empty after 'remove' Eric Sunshine
2018-08-30  7:54 ` [PATCH 0/9] worktree: fix bugs and broaden --force applicability Jeff King
2018-08-30  8:57   ` Eric Sunshine
2018-08-30  9:04   ` Eric Sunshine
2018-08-30 19:46     ` Jeff King
2018-08-30 20:14       ` Junio C Hamano
2018-08-30 23:49         ` Ramsay Jones
2018-08-31  0:54           ` Jeff King
2018-08-31 21:57             ` Ramsay Jones

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180828212026.21989-6-sunshine@sunshineco.com \
    --to=sunshine@sunshineco.com \
    --cc=git@vger.kernel.org \
    --cc=pclouds@gmail.com \
    --cc=peff@peff.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).