All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: pclouds@gmail.com
Cc: git@vger.kernel.org, gitster@pobox.com, newren@gmail.com,
	peff@peff.net, sbeller@google.com
Subject: [PATCH v3 0/8] fix per-worktree ref iteration in fsck/reflog expire
Date: Sun, 21 Oct 2018 10:08:51 +0200	[thread overview]
Message-ID: <20181021080859.3203-1-pclouds@gmail.com> (raw)
In-Reply-To: <20180929191029.13994-1-pclouds@gmail.com>

v3 changes

- fix incorrect ref reporting (it reported main/HEAD instead of
  main-worktree/HEAD)
- other document typos
- fix strbuf_worktree_ref() producing refs that cannot be handled by
  files-backend.c, e.g. worktrees/foo/refs/heads/master (one day the
  ref store will, but not now)
- make sure the "HEAD" special case in reflog expire still works
  with main-worktree/HEAD and worktrees/xxx/HEAD
- tests added for reflog

Elijah Newren (1):
  fsck: Move fsck_head_link() to get_default_heads() to avoid some
    globals

Nguyễn Thái Ngọc Duy (7):
  refs.c: indent with tabs, not spaces
  Add a place for (not) sharing stuff between worktrees
  refs: new ref types to make per-worktree refs visible to all worktrees
  revision.c: correct a parameter name
  revision.c: better error reporting on ref from different worktrees
  fsck: check HEAD and reflog from other worktrees
  reflog expire: cover reflog from all worktrees

 Documentation/git-reflog.txt           |  7 ++-
 Documentation/git-worktree.txt         | 32 ++++++++++-
 Documentation/gitrepository-layout.txt | 11 +++-
 builtin/fsck.c                         | 68 +++++++++++++++-------
 builtin/reflog.c                       | 46 +++++++++++++--
 path.c                                 |  2 +
 refs.c                                 | 24 +++++++-
 refs.h                                 |  8 ++-
 refs/files-backend.c                   | 42 +++++++++++++-
 revision.c                             | 22 ++++---
 t/t0060-path-utils.sh                  |  2 +
 t/t1410-reflog.sh                      | 15 +++++
 t/t1415-worktree-refs.sh               | 79 ++++++++++++++++++++++++++
 t/t1450-fsck.sh                        | 35 ++++++++++++
 worktree.c                             | 79 +++++++++++++++++++++++++-
 worktree.h                             | 24 ++++++++
 16 files changed, 449 insertions(+), 47 deletions(-)
 create mode 100755 t/t1415-worktree-refs.sh

Range-diff against v2:
1:  328a4d1263 ! 1:  fff4cfcc93 refs: new ref types to make per-worktree refs visible to all worktrees
    @@ -24,7 +24,7 @@
         "blah". This syntax coincidentally matches the underlying directory
         structure which makes implementation a bit easier.
     
    -    The main worktree has to be treated specially because well.. it's
    +    The main worktree has to be treated specially because well... it's
         special from the beginning. So HEAD from the main worktree is
         acccessible via the name "main-worktree/HEAD" instead of
         "worktrees/main/HEAD" because "main" could be just another secondary
    @@ -53,9 +53,9 @@
      shared.
      
     +Refs that are per working tree can still be accessed from another
    -+working tree via two special paths main-worktree and worktrees. The
    ++working tree via two special paths, main-worktree and worktrees. The
     +former gives access to per-worktree refs of the main working tree,
    -+while the former to all linked working trees.
    ++while the latter to all linked working trees.
     +
     +For example, main-worktree/HEAD or main-worktree/refs/bisect/good
     +resolve to the same value as the main working tree's HEAD and
    @@ -128,6 +128,14 @@
      diff --git a/refs/files-backend.c b/refs/files-backend.c
      --- a/refs/files-backend.c
      +++ b/refs/files-backend.c
    +@@
    + #include "../object.h"
    + #include "../dir.h"
    + #include "../chdir-notify.h"
    ++#include "worktree.h"
    + 
    + /*
    +  * This backend uses the following flags in `ref_update::flags` for
     @@
      	return refs;
      }
    @@ -137,16 +145,18 @@
     +					      const char *refname)
     +{
     +	const char *real_ref;
    ++	const char *worktree_name;
    ++	int length;
     +
    -+	if (!skip_prefix(refname, "worktrees/", &real_ref))
    -+		BUG("refname %s is not a other-worktree ref", refname);
    -+	real_ref = strchr(real_ref, '/');
    -+	if (!real_ref)
    ++	if (parse_worktree_ref(refname, &worktree_name, &length, &real_ref))
     +		BUG("refname %s is not a other-worktree ref", refname);
    -+	real_ref++;
     +
    -+	strbuf_addf(sb, "%s/%.*slogs/%s", refs->gitcommondir,
    -+		    (int)(real_ref - refname), refname, real_ref);
    ++	if (worktree_name)
    ++		strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir,
    ++			    length, worktree_name, real_ref);
    ++	else
    ++		strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir,
    ++			    real_ref);
     +}
     +
      static void files_reflog_path(struct files_ref_store *refs,
    @@ -157,11 +167,8 @@
      		strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname);
      		break;
     +	case REF_TYPE_OTHER_PSEUDOREF:
    -+		return files_reflog_path_other_worktrees(refs, sb, refname);
     +	case REF_TYPE_MAIN_PSEUDOREF:
    -+		if (!skip_prefix(refname, "main-worktree/", &refname))
    -+			BUG("ref %s is not a main pseudoref", refname);
    -+		/* passthru */
    ++		return files_reflog_path_other_worktrees(refs, sb, refname);
      	case REF_TYPE_NORMAL:
      		strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
      		break;
    @@ -172,7 +179,7 @@
     +	case REF_TYPE_MAIN_PSEUDOREF:
     +		if (!skip_prefix(refname, "main-worktree/", &refname))
     +			BUG("ref %s is not a main pseudoref", refname);
    -+		/* passthru */
    ++		/* fallthrough */
     +	case REF_TYPE_OTHER_PSEUDOREF:
      	case REF_TYPE_NORMAL:
      		strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
    @@ -193,9 +200,9 @@
     +
     +test_expect_success 'ambiguous main-worktree/HEAD' '
     +	mkdir -p .git/refs/heads/main-worktree &&
    -+	test_when_finished rm .git/refs/heads/main-worktree/HEAD &&
    ++	test_when_finished rm -f .git/refs/heads/main-worktree/HEAD &&
     +	cp .git/HEAD .git/refs/heads/main-worktree/HEAD &&
    -+	git rev-parse main-worktree/HEAD 2>warn >/dev/null &&
    ++	git rev-parse main-worktree/HEAD 2>warn &&
     +	grep "main-worktree/HEAD.*ambiguous" warn
     +'
     +
    @@ -207,9 +214,9 @@
     +
     +test_expect_success 'ambiguous worktrees/xx/HEAD' '
     +	mkdir -p .git/refs/heads/worktrees/wt1 &&
    -+	test_when_finished rm .git/refs/heads/worktrees/wt1/HEAD &&
    ++	test_when_finished rm -f .git/refs/heads/worktrees/wt1/HEAD &&
     +	cp .git/HEAD .git/refs/heads/worktrees/wt1/HEAD &&
    -+	git rev-parse worktrees/wt1/HEAD 2>warn >/dev/null &&
    ++	git rev-parse worktrees/wt1/HEAD 2>warn &&
     +	grep "worktrees/wt1/HEAD.*ambiguous" warn
     +'
     +
    @@ -232,3 +239,62 @@
     +'
     +
      test_done
    +
    + diff --git a/worktree.c b/worktree.c
    + --- a/worktree.c
    + +++ b/worktree.c
    +@@
    + 	return ret;
    + }
    + 
    ++int parse_worktree_ref(const char *worktree_ref, const char **name,
    ++		       int *name_length, const char **ref)
    ++{
    ++	if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
    ++		if (!*worktree_ref)
    ++			return -1;
    ++		if (name)
    ++			*name = NULL;
    ++		if (name_length)
    ++			*name_length = 0;
    ++		if (ref)
    ++			*ref = worktree_ref;
    ++		return 0;
    ++	}
    ++	if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
    ++		const char *slash = strchr(worktree_ref, '/');
    ++
    ++		if (!slash || slash == worktree_ref || !slash[1])
    ++			return -1;
    ++		if (name)
    ++			*name = worktree_ref;
    ++		if (name_length)
    ++			*name_length = slash - worktree_ref;
    ++		if (ref)
    ++			*ref = slash + 1;
    ++		return 0;
    ++	}
    ++	return -1;
    ++}
    ++
    + int other_head_refs(each_ref_fn fn, void *cb_data)
    + {
    + 	struct worktree **worktrees, **p;
    +
    + diff --git a/worktree.h b/worktree.h
    + --- a/worktree.h
    + +++ b/worktree.h
    +@@
    + 				     const char *fmt, ...)
    + 	__attribute__((format (printf, 2, 3)));
    + 
    ++/*
    ++ * Parse a worktree ref (i.e. with prefix main-worktree/ or
    ++ * worktrees/) and return the position of the worktree's name and
    ++ * length (or NULL and zero if it's main worktree), and ref.
    ++ *
    ++ * All name, name_length and ref arguments could be NULL.
    ++ */
    ++int parse_worktree_ref(const char *worktree_ref, const char **name,
    ++		       int *name_length, const char **ref);
    + #endif
2:  ffdd30f7fc = 2:  e936a0af1e revision.c: correct a parameter name
3:  6809bab191 ! 3:  382c645d73 revision.c: better error reporting on ref from different worktrees
    @@ -87,18 +87,35 @@
      --- a/worktree.c
      +++ b/worktree.c
     @@
    - 	return ret;
    + 	return -1;
      }
      
     +void strbuf_worktree_ref(const struct worktree *wt,
     +			 struct strbuf *sb,
     +			 const char *refname)
     +{
    -+	if (wt && !wt->is_current) {
    -+		if (is_main_worktree(wt))
    -+			strbuf_addstr(sb, "main/");
    -+		else
    -+			strbuf_addf(sb, "worktrees/%s/", wt->id);
    ++	switch (ref_type(refname)) {
    ++	case REF_TYPE_PSEUDOREF:
    ++	case REF_TYPE_PER_WORKTREE:
    ++		if (wt && !wt->is_current) {
    ++			if (is_main_worktree(wt))
    ++				strbuf_addstr(sb, "main-worktree/");
    ++			else
    ++				strbuf_addf(sb, "worktrees/%s/", wt->id);
    ++		}
    ++		break;
    ++
    ++	case REF_TYPE_MAIN_PSEUDOREF:
    ++	case REF_TYPE_OTHER_PSEUDOREF:
    ++		break;
    ++
    ++	case REF_TYPE_NORMAL:
    ++		/*
    ++		 * For shared refs, don't prefix worktrees/ or
    ++		 * main-worktree/. It's not necessary and
    ++		 * files-backend.c can't handle it anyway.
    ++		 */
    ++		break;
     +	}
     +	strbuf_addstr(sb, refname);
     +}
    @@ -141,9 +158,10 @@
      --- a/worktree.h
      +++ b/worktree.h
     @@
    - 				     const char *fmt, ...)
    - 	__attribute__((format (printf, 2, 3)));
    - 
    +  */
    + int parse_worktree_ref(const char *worktree_ref, const char **name,
    + 		       int *name_length, const char **ref);
    ++
     +/*
     + * Return a refname suitable for access from the current ref store.
     + */
    @@ -153,7 +171,7 @@
     +
     +/*
     + * Return a refname suitable for access from the current ref
    -+ * store. The result may be destroyed at the next call.
    ++ * store. The result will be destroyed at the next call.
     + */
     +const char *worktree_ref(const struct worktree *wt,
     +			 const char *refname);
4:  2e13fa8361 = 4:  e2b99ef955 fsck: Move fsck_head_link() to get_default_heads() to avoid some globals
5:  65f1547df3 ! 5:  8fbff370c3 fsck: check HEAD and reflog from other worktrees
    @@ -136,7 +136,7 @@
     +	echo $ZERO_OID >.git/HEAD &&
     +	# avoid corrupt/broken HEAD from interfering with repo discovery
     +	test_must_fail git -C wt fsck 2>out &&
    -+	grep "main/HEAD: detached HEAD points" out
    ++	grep "main-worktree/HEAD: detached HEAD points" out
     +'
     +
     +test_expect_success 'other worktree HEAD link pointing at a funny object' '
6:  86326b44b5 ! 6:  2cd501f2ce reflog expire: cover reflog from all worktrees
    @@ -48,12 +48,48 @@
      };
      
      /* Remember to update object flag allocation in object.h */
    +@@
    + 	return 0;
    + }
    + 
    ++static int is_head(const char *refname)
    ++{
    ++	switch (ref_type(refname)) {
    ++	case REF_TYPE_OTHER_PSEUDOREF:
    ++	case REF_TYPE_MAIN_PSEUDOREF:
    ++		if (parse_worktree_ref(refname, NULL, NULL, &refname))
    ++			BUG("not a worktree ref: %s", refname);
    ++		break;
    ++	default:
    ++		break;
    ++	}
    ++	return !strcmp(refname, "HEAD");
    ++}
    ++
    + static void reflog_expiry_prepare(const char *refname,
    + 				  const struct object_id *oid,
    + 				  void *cb_data)
    + {
    + 	struct expire_reflog_policy_cb *cb = cb_data;
    + 
    +-	if (!cb->cmd.expire_unreachable || !strcmp(refname, "HEAD")) {
    ++	if (!cb->cmd.expire_unreachable || is_head(refname)) {
    + 		cb->tip_commit = NULL;
    + 		cb->unreachable_expire_kind = UE_HEAD;
    + 	} else {
     @@
      {
      	struct collected_reflog *e;
      	struct collect_reflog_cb *cb = cb_data;
     +	struct strbuf newref = STRBUF_INIT;
     +
    ++	/*
    ++	 * Avoid collecting the same shared ref multiple times because
    ++	 * they are available via all worktrees.
    ++	 */
    ++	if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
    ++		return 0;
    ++
     +	strbuf_worktree_ref(cb->wt, &newref, ref);
     +	FLEX_ALLOC_STR(e, reflog, newref.buf);
     +	strbuf_release(&newref);
    @@ -94,9 +130,34 @@
     +			if (!all_worktrees && !(*p)->is_current)
     +				continue;
     +			collected.wt = *p;
    -+			for_each_reflog(collect_reflog, &collected);
    ++			refs_for_each_reflog(get_worktree_ref_store(*p),
    ++					     collect_reflog, &collected);
     +		}
     +		free_worktrees(worktrees);
      		for (i = 0; i < collected.nr; i++) {
      			struct collected_reflog *e = collected.e[i];
      			set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
    +
    + diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
    + --- a/t/t1410-reflog.sh
    + +++ b/t/t1410-reflog.sh
    +@@
    + 	)
    + '
    + 
    ++test_expect_success 'expire with multiple worktrees' '
    ++	git init main-wt &&
    ++	(
    ++		cd main-wt &&
    ++		test_tick &&
    ++		test_commit foo &&
    ++		git  worktree add link-wt &&
    ++		test_tick &&
    ++		test_commit -C link-wt foobar &&
    ++		test_tick &&
    ++		git reflog expire --verbose --all --expire=$test_tick &&
    ++		test_must_be_empty .git/worktrees/link-wt/logs/HEAD
    ++	)
    ++'
    ++
    + test_done
-- 
2.19.1.647.g708186aaf9


  parent reply	other threads:[~2018-10-21  8:09 UTC|newest]

Thread overview: 60+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-09-22 18:04 [PATCH 0/8] fix per-worktree ref iteration in fsck/reflog expire Nguyễn Thái Ngọc Duy
2018-09-22 18:04 ` [PATCH 1/8] refs.c: indent with tabs, not spaces Nguyễn Thái Ngọc Duy
2018-09-22 18:04 ` [PATCH 2/8] Add a place for (not) sharing stuff between worktrees Nguyễn Thái Ngọc Duy
2018-09-23  7:51   ` Eric Sunshine
2018-09-25  2:35   ` Stefan Beller
2018-09-25 15:36     ` Duy Nguyen
2018-09-25 16:24       ` Stefan Beller
2018-09-25 16:55         ` Duy Nguyen
2018-09-25 17:56           ` Stefan Beller
2018-09-22 18:04 ` [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees Nguyễn Thái Ngọc Duy
2018-09-23  8:06   ` Eric Sunshine
2018-09-23 13:10     ` Duy Nguyen
2018-09-25  2:48   ` Stefan Beller
2018-09-25 15:49     ` Duy Nguyen
2018-09-25 16:53       ` Stefan Beller
2018-09-25 21:16   ` Junio C Hamano
2018-09-29 18:26     ` Duy Nguyen
2018-10-06 23:20       ` Junio C Hamano
2018-09-22 18:04 ` [PATCH 4/8] revision.c: correct a parameter name Nguyễn Thái Ngọc Duy
2018-09-22 18:04 ` [PATCH 5/8] revision.c: better error reporting on ref from different worktrees Nguyễn Thái Ngọc Duy
2018-09-23  8:25   ` Eric Sunshine
2018-09-23 13:15     ` Duy Nguyen
2018-09-22 18:04 ` [PATCH 6/8] fsck: Move fsck_head_link() to get_default_heads() to avoid some globals Nguyễn Thái Ngọc Duy
2018-09-22 18:04 ` [PATCH 7/8] fsck: check HEAD and reflog from other worktrees Nguyễn Thái Ngọc Duy
2018-09-23  8:41   ` Eric Sunshine
2018-09-29 18:40     ` Duy Nguyen
2018-09-22 18:05 ` [PATCH 8/8] reflog expire: cover reflog from all worktrees Nguyễn Thái Ngọc Duy
2018-09-29 19:10 ` [PATCH v2 0/8] fix per-worktree ref iteration in fsck/reflog expire Nguyễn Thái Ngọc Duy
2018-09-29 19:10   ` [PATCH v2 1/8] refs.c: indent with tabs, not spaces Nguyễn Thái Ngọc Duy
2018-09-29 19:10   ` [PATCH v2 2/8] Add a place for (not) sharing stuff between worktrees Nguyễn Thái Ngọc Duy
2018-09-29 19:10   ` [PATCH v2 3/8] refs: new ref types to make per-worktree refs visible to all worktrees Nguyễn Thái Ngọc Duy
2018-09-30  5:13     ` Eric Sunshine
2018-10-07  1:37     ` Junio C Hamano
2018-09-29 19:10   ` [PATCH v2 4/8] revision.c: correct a parameter name Nguyễn Thái Ngọc Duy
2018-09-29 19:10   ` [PATCH v2 5/8] revision.c: better error reporting on ref from different worktrees Nguyễn Thái Ngọc Duy
2018-09-30  5:25     ` Eric Sunshine
2018-09-29 19:10   ` [PATCH v2 6/8] fsck: Move fsck_head_link() to get_default_heads() to avoid some globals Nguyễn Thái Ngọc Duy
2018-09-29 19:10   ` [PATCH v2 7/8] fsck: check HEAD and reflog from other worktrees Nguyễn Thái Ngọc Duy
2018-09-29 19:10   ` [PATCH v2 8/8] reflog expire: cover reflog from all worktrees Nguyễn Thái Ngọc Duy
2018-09-30  5:36     ` Eric Sunshine
2018-10-02 16:16       ` Duy Nguyen
2018-10-03  7:49         ` Eric Sunshine
2018-10-21  8:08   ` Nguyễn Thái Ngọc Duy [this message]
2018-10-21  8:08     ` [PATCH v3 1/8] refs.c: indent with tabs, not spaces Nguyễn Thái Ngọc Duy
2018-10-21  8:08     ` [PATCH v3 2/8] Add a place for (not) sharing stuff between worktrees Nguyễn Thái Ngọc Duy
2018-10-22  4:28       ` Junio C Hamano
2018-10-29 17:18         ` Duy Nguyen
2018-10-22 10:25       ` SZEDER Gábor
2018-10-21  8:08     ` [PATCH v3 3/8] refs: new ref types to make per-worktree refs visible to all worktrees Nguyễn Thái Ngọc Duy
2018-11-24 19:27       ` Ævar Arnfjörð Bjarmason
2018-11-25  1:19         ` Junio C Hamano
2018-11-25  4:58         ` [PATCH] files-backend.c: fix build error on Solaris Nguyễn Thái Ngọc Duy
2018-11-25 10:19           ` Carlo Arenas
2018-11-25 10:40             ` Duy Nguyen
2018-11-26  4:44             ` Junio C Hamano
2018-10-21  8:08     ` [PATCH v3 4/8] revision.c: correct a parameter name Nguyễn Thái Ngọc Duy
2018-10-21  8:08     ` [PATCH v3 5/8] revision.c: better error reporting on ref from different worktrees Nguyễn Thái Ngọc Duy
2018-10-21  8:08     ` [PATCH v3 6/8] fsck: Move fsck_head_link() to get_default_heads() to avoid some globals Nguyễn Thái Ngọc Duy
2018-10-21  8:08     ` [PATCH v3 7/8] fsck: check HEAD and reflog from other worktrees Nguyễn Thái Ngọc Duy
2018-10-21  8:08     ` [PATCH v3 8/8] reflog expire: cover reflog from all worktrees Nguyễn Thái Ngọc Duy

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

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

  git send-email \
    --in-reply-to=20181021080859.3203-1-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=newren@gmail.com \
    --cc=peff@peff.net \
    --cc=sbeller@google.com \
    /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.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.