git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* (unknown), 
@ 2015-09-01  2:13 David Turner
  2015-09-01  2:13 ` [PATCH v5 1/3] refs: clean up common_list David Turner
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: David Turner @ 2015-09-01  2:13 UTC (permalink / raw
  To: git, mhagger, Christian Couder

This version of the patch series squashes in Junio's comment fix and
the xstrndup fix.

In addition, it removes refs/worktree, and just makes refs/bisect
per-worktree.  If we later discover that other things need to be
per-worktree, we can do refs/worktree, but for now, this is
backwards-compatible so we'll do this.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v5 1/3] refs: clean up common_list
  2015-09-01  2:13 (unknown), David Turner
@ 2015-09-01  2:13 ` David Turner
  2015-09-01  2:13 ` [PATCH v5 2/3] path: optimize common dir checking David Turner
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 10+ messages in thread
From: David Turner @ 2015-09-01  2:13 UTC (permalink / raw
  To: git, mhagger, Christian Couder; +Cc: David Turner, Junio C Hamano

Instead of common_list having formatting like ! and /, use a struct to
hold common_list data in a structured form.

We don't use 'exclude' yet; instead, we keep the old codepath that
handles info/sparse-checkout and logs/HEAD.  Later, we will use exclude.

Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 path.c | 58 +++++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 37 insertions(+), 21 deletions(-)

diff --git a/path.c b/path.c
index 95acbaf..f1ffe37 100644
--- a/path.c
+++ b/path.c
@@ -91,35 +91,51 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
 		buf->buf[newlen] = '/';
 }
 
-static const char *common_list[] = {
-	"/branches", "/hooks", "/info", "!/logs", "/lost-found",
-	"/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn",
-	"config", "!gc.pid", "packed-refs", "shallow",
-	NULL
+struct common_dir {
+	/* Not considered garbage for report_linked_checkout_garbage */
+	unsigned ignore_garbage:1;
+	unsigned is_dir:1;
+	/* Not common even though its parent is */
+	unsigned exclude:1;
+	const char *dirname;
+};
+
+struct common_dir common_list[] = {
+	{ 0, 1, 0, "branches" },
+	{ 0, 1, 0, "hooks" },
+	{ 0, 1, 0, "info" },
+	{ 0, 0, 1, "info/sparse-checkout" },
+	{ 1, 1, 0, "logs" },
+	{ 1, 1, 1, "logs/HEAD" },
+	{ 0, 1, 0, "lost-found" },
+	{ 0, 1, 0, "objects" },
+	{ 0, 1, 0, "refs" },
+	{ 0, 1, 0, "remotes" },
+	{ 0, 1, 0, "worktrees" },
+	{ 0, 1, 0, "rr-cache" },
+	{ 0, 1, 0, "svn" },
+	{ 0, 0, 0, "config" },
+	{ 1, 0, 0, "gc.pid" },
+	{ 0, 0, 0, "packed-refs" },
+	{ 0, 0, 0, "shallow" },
+	{ 0, 0, 0, NULL }
 };
 
 static void update_common_dir(struct strbuf *buf, int git_dir_len)
 {
 	char *base = buf->buf + git_dir_len;
-	const char **p;
+	const struct common_dir *p;
 
 	if (is_dir_file(base, "logs", "HEAD") ||
 	    is_dir_file(base, "info", "sparse-checkout"))
 		return;	/* keep this in $GIT_DIR */
-	for (p = common_list; *p; p++) {
-		const char *path = *p;
-		int is_dir = 0;
-		if (*path == '!')
-			path++;
-		if (*path == '/') {
-			path++;
-			is_dir = 1;
-		}
-		if (is_dir && dir_prefix(base, path)) {
+	for (p = common_list; p->dirname; p++) {
+		const char *path = p->dirname;
+		if (p->is_dir && dir_prefix(base, path)) {
 			replace_dir(buf, git_dir_len, get_git_common_dir());
 			return;
 		}
-		if (!is_dir && !strcmp(base, path)) {
+		if (!p->is_dir && !strcmp(base, path)) {
 			replace_dir(buf, git_dir_len, get_git_common_dir());
 			return;
 		}
@@ -129,16 +145,16 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len)
 void report_linked_checkout_garbage(void)
 {
 	struct strbuf sb = STRBUF_INIT;
-	const char **p;
+	const struct common_dir *p;
 	int len;
 
 	if (!git_common_dir_env)
 		return;
 	strbuf_addf(&sb, "%s/", get_git_dir());
 	len = sb.len;
-	for (p = common_list; *p; p++) {
-		const char *path = *p;
-		if (*path == '!')
+	for (p = common_list; p->dirname; p++) {
+		const char *path = p->dirname;
+		if (p->ignore_garbage)
 			continue;
 		strbuf_setlen(&sb, len);
 		strbuf_addstr(&sb, path);
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 2/3] path: optimize common dir checking
  2015-09-01  2:13 (unknown), David Turner
  2015-09-01  2:13 ` [PATCH v5 1/3] refs: clean up common_list David Turner
@ 2015-09-01  2:13 ` David Turner
  2015-10-05  3:00   ` Michael Haggerty
  2015-09-01  2:13 ` [PATCH v5 3/3] refs: make refs/bisect/* per-worktree David Turner
  2015-09-01 16:32 ` making refs/bisect/ per worktree (was Re: (unknown)) Junio C Hamano
  3 siblings, 1 reply; 10+ messages in thread
From: David Turner @ 2015-09-01  2:13 UTC (permalink / raw
  To: git, mhagger, Christian Couder; +Cc: David Turner, Junio C Hamano

Instead of a linear search over common_list to check whether
a path is common, use a trie.  The trie search operates on
path prefixes, and handles excludes.

Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 path.c                | 227 ++++++++++++++++++++++++++++++++++++++++++++++----
 t/t0060-path-utils.sh |   1 +
 2 files changed, 214 insertions(+), 14 deletions(-)

diff --git a/path.c b/path.c
index f1ffe37..c87f44a 100644
--- a/path.c
+++ b/path.c
@@ -121,25 +121,224 @@ struct common_dir common_list[] = {
 	{ 0, 0, 0, NULL }
 };
 
-static void update_common_dir(struct strbuf *buf, int git_dir_len)
+/*
+ * A compressed trie.  A trie node consists of zero or more characters that
+ * are common to all elements with this prefix, optionally followed by some
+ * children.  If value is not NULL, the trie node is a terminal node.
+ *
+ * For example, consider the following set of strings:
+ * abc
+ * def
+ * definite
+ * definition
+ *
+ * The trie would look look like:
+ * root: len = 0, children a and d non-NULL, value = NULL.
+ *    a: len = 2, contents = bc, value = (data for "abc")
+ *    d: len = 2, contents = ef, children i non-NULL, value = (data for "def")
+ *       i: len = 3, contents = nit, children e and i non-NULL, value = NULL
+ *           e: len = 0, children all NULL, value = (data for "definite")
+ *           i: len = 2, contents = on, children all NULL,
+ *              value = (data for "definition")
+ */
+struct trie {
+	struct trie *children[256];
+	int len;
+	char *contents;
+	void *value;
+};
+
+static struct trie *make_trie_node(const char *key, void *value)
 {
-	char *base = buf->buf + git_dir_len;
-	const struct common_dir *p;
+	struct trie *new_node = xcalloc(1, sizeof(*new_node));
+	new_node->len = strlen(key);
+	if (new_node->len) {
+		new_node->contents = xmalloc(new_node->len);
+		memcpy(new_node->contents, key, new_node->len);
+	}
+	new_node->value = value;
+	return new_node;
+}
 
-	if (is_dir_file(base, "logs", "HEAD") ||
-	    is_dir_file(base, "info", "sparse-checkout"))
-		return;	/* keep this in $GIT_DIR */
-	for (p = common_list; p->dirname; p++) {
-		const char *path = p->dirname;
-		if (p->is_dir && dir_prefix(base, path)) {
-			replace_dir(buf, git_dir_len, get_git_common_dir());
-			return;
+/*
+ * Add a key/value pair to a trie.  The key is assumed to be \0-terminated.
+ * If there was an existing value for this key, return it.
+ */
+static void *add_to_trie(struct trie *root, const char *key, void *value)
+{
+	struct trie *child;
+	void *old;
+	int i;
+
+	if (!*key) {
+		/* we have reached the end of the key */
+		old = root->value;
+		root->value = value;
+		return old;
+	}
+
+	for (i = 0; i < root->len; i++) {
+		if (root->contents[i] == key[i])
+			continue;
+
+		/*
+		 * Split this node: child will contain this node's
+		 * existing children.
+		 */
+		child = malloc(sizeof(*child));
+		memcpy(child->children, root->children, sizeof(root->children));
+
+		child->len = root->len - i - 1;
+		if (child->len) {
+			child->contents = xstrndup(root->contents + i + 1,
+						   child->len);
 		}
-		if (!p->is_dir && !strcmp(base, path)) {
-			replace_dir(buf, git_dir_len, get_git_common_dir());
-			return;
+		child->value = root->value;
+		root->value = NULL;
+		root->len = i;
+
+		memset(root->children, 0, sizeof(root->children));
+		root->children[(unsigned char)root->contents[i]] = child;
+
+		/* This is the newly-added child. */
+		root->children[(unsigned char)key[i]] =
+			make_trie_node(key + i + 1, value);
+		return NULL;
+	}
+
+	/* We have matched the entire compressed section */
+	if (key[i]) {
+		child = root->children[(unsigned char)key[root->len]];
+		if (child) {
+			return add_to_trie(child, key + root->len + 1, value);
+		} else {
+			child = make_trie_node(key + root->len + 1, value);
+			root->children[(unsigned char)key[root->len]] = child;
+			return NULL;
 		}
 	}
+
+	old = root->value;
+	root->value = value;
+	return old;
+}
+
+typedef int (*match_fn)(const char *unmatched, void *data, void *baton);
+
+/*
+ * Search a trie for some key.  Find the longest /-or-\0-terminated
+ * prefix of the key for which the trie contains a value.  Call fn
+ * with the unmatched portion of the key and the found value, and
+ * return its return value.  If there is no such prefix, return -1.
+ *
+ * The key is partially normalized: consecutive slashes are skipped.
+ *
+ * For example, consider the trie containing only [refs,
+ * refs/worktree] (both with values).
+ *
+ * | key             | unmatched  | val from node | return value |
+ * |-----------------|------------|---------------|--------------|
+ * | a               | not called | n/a           | -1           |
+ * | refs            | \0         | refs          | as per fn    |
+ * | refs/           | /          | refs          | as per fn    |
+ * | refs/w          | /w         | refs          | as per fn    |
+ * | refs/worktree   | \0         | refs/worktree | as per fn    |
+ * | refs/worktree/  | /          | refs/worktree | as per fn    |
+ * | refs/worktree/a | /a         | refs/worktree | as per fn    |
+ * |-----------------|------------|---------------|--------------|
+ *
+ */
+static int trie_find(struct trie *root, const char *key, match_fn fn,
+		     void *baton)
+{
+	int i;
+	int result;
+	struct trie *child;
+
+	if (!*key) {
+		/* we have reached the end of the key */
+		if (root->value && !root->len)
+			return fn(key, root->value, baton);
+		else
+			return -1;
+	}
+
+	for (i = 0; i < root->len; i++) {
+		/* Partial path normalization: skip consecutive slashes. */
+		if (key[i] == '/' && key[i+1] == '/') {
+			key++;
+			continue;
+		}
+		if (root->contents[i] != key[i])
+			return -1;
+	}
+
+	/* Matched the entire compressed section */
+	key += i;
+	if (!*key)
+		/* End of key */
+		return fn(key, root->value, baton);
+
+	/* Partial path normalization: skip consecutive slashes */
+	while (key[0] == '/' && key[1] == '/')
+		key++;
+
+	child = root->children[(unsigned char)*key];
+	if (child)
+		result = trie_find(child, key + 1, fn, baton);
+	else
+		result = -1;
+
+	if (result >= 0 || (*key != '/' && *key != 0))
+		return result;
+	if (root->value)
+		return fn(key, root->value, baton);
+	else
+		return -1;
+}
+
+static struct trie common_trie;
+static int common_trie_done_setup;
+
+static void init_common_trie(void)
+{
+	struct common_dir *p;
+
+	if (common_trie_done_setup)
+		return;
+
+	for (p = common_list; p->dirname; p++)
+		add_to_trie(&common_trie, p->dirname, p);
+
+	common_trie_done_setup = 1;
+}
+
+/*
+ * Helper function for update_common_dir: returns 1 if the dir
+ * prefix is common.
+ */
+static int check_common(const char *unmatched, void *value, void *baton)
+{
+	struct common_dir *dir = value;
+
+	if (!dir)
+		return 0;
+
+	if (dir->is_dir && (unmatched[0] == 0 || unmatched[0] == '/'))
+		return !dir->exclude;
+
+	if (!dir->is_dir && unmatched[0] == 0)
+		return !dir->exclude;
+
+	return 0;
+}
+
+static void update_common_dir(struct strbuf *buf, int git_dir_len)
+{
+	char *base = buf->buf + git_dir_len;
+	init_common_trie();
+	if (trie_find(&common_trie, base, check_common, NULL) > 0)
+		replace_dir(buf, git_dir_len, get_git_common_dir());
 }
 
 void report_linked_checkout_garbage(void)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 93605f4..1ca49e1 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -271,6 +271,7 @@ test_git_path GIT_COMMON_DIR=bar objects/bar              bar/objects/bar
 test_git_path GIT_COMMON_DIR=bar info/exclude             bar/info/exclude
 test_git_path GIT_COMMON_DIR=bar info/grafts              bar/info/grafts
 test_git_path GIT_COMMON_DIR=bar info/sparse-checkout     .git/info/sparse-checkout
+test_git_path GIT_COMMON_DIR=bar info//sparse-checkout    .git/info//sparse-checkout
 test_git_path GIT_COMMON_DIR=bar remotes/bar              bar/remotes/bar
 test_git_path GIT_COMMON_DIR=bar branches/bar             bar/branches/bar
 test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master   bar/logs/refs/heads/master
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 3/3] refs: make refs/bisect/* per-worktree
  2015-09-01  2:13 (unknown), David Turner
  2015-09-01  2:13 ` [PATCH v5 1/3] refs: clean up common_list David Turner
  2015-09-01  2:13 ` [PATCH v5 2/3] path: optimize common dir checking David Turner
@ 2015-09-01  2:13 ` David Turner
  2015-09-01 16:32 ` making refs/bisect/ per worktree (was Re: (unknown)) Junio C Hamano
  3 siblings, 0 replies; 10+ messages in thread
From: David Turner @ 2015-09-01  2:13 UTC (permalink / raw
  To: git, mhagger, Christian Couder; +Cc: David Turner, Junio C Hamano

We need the place we stick refs for bisects in progress to not be
shared between worktrees.  So we make the refs/bisect/ hierarchy
per-worktree.

The is_per_worktree_ref function and associated docs learn that
refs/bisect/ is per-worktree, as does the git_path code in path.c

The ref-packing functions learn that per-worktree refs should not be
packed (since packed-refs is common rather than per-worktree).

Since refs/bisect is per-worktree, logs/refs/bisect should be too.

Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/glossary-content.txt |  5 +++--
 path.c                             |  2 ++
 refs.c                             | 32 +++++++++++++++++++++++++++++++-
 t/t0060-path-utils.sh              |  5 +++++
 t/t1400-update-ref.sh              | 19 +++++++++++++++++++
 t/t3210-pack-refs.sh               |  7 +++++++
 6 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 8c6478b..e225974 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -413,8 +413,9 @@ exclude;;
 
 [[def_per_worktree_ref]]per-worktree ref::
 	Refs that are per-<<def_working_tree,worktree>>, rather than
-	global.  This is presently only <<def_HEAD,HEAD>>, but might
-	later include other unusual refs.
+	global.  This is presently only <<def_HEAD,HEAD>> and any refs
+	that start with `refs/bisect/`, but might later include other
+	unusual refs.
 
 [[def_pseudoref]]pseudoref::
 	Pseudorefs are a class of files under `$GIT_DIR` which behave
diff --git a/path.c b/path.c
index c87f44a..26723a9 100644
--- a/path.c
+++ b/path.c
@@ -107,9 +107,11 @@ struct common_dir common_list[] = {
 	{ 0, 0, 1, "info/sparse-checkout" },
 	{ 1, 1, 0, "logs" },
 	{ 1, 1, 1, "logs/HEAD" },
+	{ 0, 1, 1, "logs/refs/bisect" },
 	{ 0, 1, 0, "lost-found" },
 	{ 0, 1, 0, "objects" },
 	{ 0, 1, 0, "refs" },
+	{ 0, 1, 1, "refs/bisect" },
 	{ 0, 1, 0, "remotes" },
 	{ 0, 1, 0, "worktrees" },
 	{ 0, 1, 0, "rr-cache" },
diff --git a/refs.c b/refs.c
index 4e15f60..24401f7 100644
--- a/refs.c
+++ b/refs.c
@@ -304,6 +304,11 @@ struct ref_entry {
 };
 
 static void read_loose_refs(const char *dirname, struct ref_dir *dir);
+static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+					  const char *dirname, size_t len,
+					  int incomplete);
+static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
 
 static struct ref_dir *get_ref_dir(struct ref_entry *entry)
 {
@@ -312,6 +317,24 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
 	dir = &entry->u.subdir;
 	if (entry->flag & REF_INCOMPLETE) {
 		read_loose_refs(entry->name, dir);
+
+		/*
+		 * Manually add refs/bisect, which, being
+		 * per-worktree, might not appear in the directory
+		 * listing for refs/ in the main repo.
+		 */
+		if (!strcmp(entry->name, "refs/")) {
+			int pos = search_ref_dir(dir, "refs/bisect/", 12);
+			if (pos < 0) {
+				struct ref_entry *child_entry;
+				child_entry = create_dir_entry(dir->ref_cache,
+							       "refs/bisect/",
+							       12, 1);
+				add_entry_to_dir(dir, child_entry);
+				read_loose_refs("refs/bisect",
+						&child_entry->u.subdir);
+			}
+		}
 		entry->flag &= ~REF_INCOMPLETE;
 	}
 	return dir;
@@ -2649,6 +2672,8 @@ struct pack_refs_cb_data {
 	struct ref_to_prune *ref_to_prune;
 };
 
+static int is_per_worktree_ref(const char *refname);
+
 /*
  * An each_ref_entry_fn that is run over loose references only.  If
  * the loose reference can be packed, add an entry in the packed ref
@@ -2662,6 +2687,10 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
 	struct ref_entry *packed_entry;
 	int is_tag_ref = starts_with(entry->name, "refs/tags/");
 
+	/* Do not pack per-worktree refs: */
+	if (is_per_worktree_ref(entry->name))
+		return 0;
+
 	/* ALWAYS pack tags */
 	if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
 		return 0;
@@ -2856,7 +2885,8 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
 
 static int is_per_worktree_ref(const char *refname)
 {
-	return !strcmp(refname, "HEAD");
+	return !strcmp(refname, "HEAD") ||
+		starts_with(refname, "refs/bisect/");
 }
 
 static int is_pseudoref_syntax(const char *refname)
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 1ca49e1..627ef85 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -266,6 +266,10 @@ test_expect_success 'setup common repository' 'git --git-dir=bar init'
 test_git_path GIT_COMMON_DIR=bar index                    .git/index
 test_git_path GIT_COMMON_DIR=bar HEAD                     .git/HEAD
 test_git_path GIT_COMMON_DIR=bar logs/HEAD                .git/logs/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisect/foo     .git/logs/refs/bisect/foo
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisec/foo      bar/logs/refs/bisec/foo
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisec          bar/logs/refs/bisec
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisectfoo      bar/logs/refs/bisectfoo
 test_git_path GIT_COMMON_DIR=bar objects                  bar/objects
 test_git_path GIT_COMMON_DIR=bar objects/bar              bar/objects/bar
 test_git_path GIT_COMMON_DIR=bar info/exclude             bar/info/exclude
@@ -276,6 +280,7 @@ test_git_path GIT_COMMON_DIR=bar remotes/bar              bar/remotes/bar
 test_git_path GIT_COMMON_DIR=bar branches/bar             bar/branches/bar
 test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master   bar/logs/refs/heads/master
 test_git_path GIT_COMMON_DIR=bar refs/heads/master        bar/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar refs/bisect/foo          .git/refs/bisect/foo
 test_git_path GIT_COMMON_DIR=bar hooks/me                 bar/hooks/me
 test_git_path GIT_COMMON_DIR=bar config                   bar/config
 test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 97406fa..af1b20d 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1130,4 +1130,23 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches
 )
 '
 
+test_expect_success 'handle per-worktree refs in refs/bisect' '
+	git commit --allow-empty -m "initial commit" &&
+	git worktree add -b branch worktree &&
+	(
+		cd worktree &&
+		git commit --allow-empty -m "test commit"  &&
+		git for-each-ref >for-each-ref.out &&
+		! grep refs/bisect for-each-ref.out &&
+		git update-ref refs/bisect/something HEAD &&
+		git rev-parse refs/bisect/something >../worktree-head &&
+		git for-each-ref | grep refs/bisect/something
+	) &&
+	test_path_is_missing .git/refs/bisect &&
+	test_must_fail git rev-parse refs/bisect/something &&
+	git update-ref refs/bisect/something HEAD &&
+	git rev-parse refs/bisect/something >main-head &&
+	! test_cmp main-head worktree-head
+'
+
 test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 7b5b6d4..db244d2 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -160,6 +160,13 @@ test_expect_success 'pack ref directly below refs/' '
 	test_path_is_missing .git/refs/top
 '
 
+test_expect_success 'do not pack ref in refs/bisect' '
+	git update-ref refs/bisect/local HEAD &&
+	git pack-refs --all --prune &&
+	! grep refs/bisect/local .git/packed-refs >/dev/null &&
+	test_path_is_file .git/refs/bisect/local
+'
+
 test_expect_success 'disable reflogs' '
 	git config core.logallrefupdates false &&
 	rm -rf .git/logs
-- 
2.0.4.315.gad8727a-twtrsrc

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: making refs/bisect/ per worktree (was Re: (unknown))
  2015-09-01  2:13 (unknown), David Turner
                   ` (2 preceding siblings ...)
  2015-09-01  2:13 ` [PATCH v5 3/3] refs: make refs/bisect/* per-worktree David Turner
@ 2015-09-01 16:32 ` Junio C Hamano
  3 siblings, 0 replies; 10+ messages in thread
From: Junio C Hamano @ 2015-09-01 16:32 UTC (permalink / raw
  To: David Turner; +Cc: git, mhagger, Christian Couder

David Turner <dturner@twopensource.com> writes:

> This version of the patch series squashes in Junio's comment fix and
> the xstrndup fix.
>
> In addition, it removes refs/worktree, and just makes refs/bisect
> per-worktree.  If we later discover that other things need to be
> per-worktree, we can do refs/worktree, but for now, this is
> backwards-compatible so we'll do this.

I've already squashed the "make common_list[] static" from Ramsay
into 1/3, so no need to resend for that one.

Let's wait for a few days to see if people spot any more issues,
merge to and cook in 'next' for the remainder of this cycle, and aim
to merge this down to 'master' in the first batch after the upcoming
release.

Thanks.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] path: optimize common dir checking
  2015-09-01  2:13 ` [PATCH v5 2/3] path: optimize common dir checking David Turner
@ 2015-10-05  3:00   ` Michael Haggerty
  2015-10-05 17:22     ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Michael Haggerty @ 2015-10-05  3:00 UTC (permalink / raw
  To: David Turner, git, Christian Couder; +Cc: Junio C Hamano

On 09/01/2015 04:13 AM, David Turner wrote:
> Instead of a linear search over common_list to check whether
> a path is common, use a trie.  The trie search operates on
> path prefixes, and handles excludes.

Here I am, coming late to the discussion as usual. Sorry for that.

I dug into this code yesterday and got all nerd-tingly and started
refactoring and optimizing it. But after I slept on it I realized that
I'm a bit hazy on its justification. A trie is a beautiful data
structure and all, but have there been any benchmarks showing (1) that
this lookup is a bottleneck and (2) that the trie is an improvement on
something simpler, like, say, a sorted string_list? Or is there a
realistic hope that the trie might be generally useful for other purposes?

The latter seems unlikely, at least with the current implementation,
because it is very wasteful of space. It allocates one or two (usually
two) `struct trie` for every single string that is added to it. Each
`struct trie` contains an array of 256 pointers and usually needs two
malloc calls to instantiate. So *each entry* stored in the trie costs
something like 4 kilobytes on a 64-bit system, plus usually 4 calls to
malloc. The large memory footprint, in turn, will cause access
non-locality and might impact the lookup performance. So it is pretty
clear that the current code would be unusable for a large number of strings.

For this particular application, where we only have 19 strings to store,
I suppose we could tolerate the use of approximately 64k of RAM to store
174 characters worth of strings *if* it would bring us big time savings.
But I think we need some evidence of the time savings.

If this lookup is really a bottleneck, I bet there are other
alternatives that are just as fast as this trie and use less code,
especially given that there are only 19 strings that need checking.

With respect to the implementation, it looks correct to me. I would make
the following three suggestions:

* Please document that the `contents` field of `struct trie` is not
NUL-terminated, because that would otherwise be a common assumption. (It
is clearly not NUL-terminated because of the way it is initialized using
xmalloc and memcpy in make_trie_node() and because of the way its length
is adjusted in add_to_trie().)
* But in add_to_trie(), you set `contents` using xstrdup(), which *does*
NUL-terminate the string. It would be more consistent to set it using
xmalloc and memcpy here.
* Please use size_t instead of int for indexing into strings, at least
in the trie_find() function, where the input is likely to be under the
control of users.

If we expected to use the trie for other purposes, then I would suggest
a raft of other improvements. Ask me if you are interested.

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] path: optimize common dir checking
  2015-10-05  3:00   ` Michael Haggerty
@ 2015-10-05 17:22     ` Junio C Hamano
  2015-10-05 20:10       ` David Turner
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2015-10-05 17:22 UTC (permalink / raw
  To: Michael Haggerty; +Cc: David Turner, git, Christian Couder

Michael Haggerty <mhagger@alum.mit.edu> writes:

> For this particular application, where we only have 19 strings to store,
> I suppose we could tolerate the use of approximately 64k of RAM to store
> 174 characters worth of strings *if* it would bring us big time savings.
> But I think we need some evidence of the time savings.
>
> If this lookup is really a bottleneck, I bet there are other
> alternatives that are just as fast as this trie and use less code,
> especially given that there are only 19 strings that need checking.

Very good point.  I agree that we need to know that the dumb linear
scan in the original is on the bottleneck and that any replacement
is an improvement.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] path: optimize common dir checking
  2015-10-05 17:22     ` Junio C Hamano
@ 2015-10-05 20:10       ` David Turner
  2015-10-05 20:36         ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: David Turner @ 2015-10-05 20:10 UTC (permalink / raw
  To: Junio C Hamano; +Cc: Michael Haggerty, git, Christian Couder

On Mon, 2015-10-05 at 10:22 -0700, Junio C Hamano wrote:
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
> > For this particular application, where we only have 19 strings to store,
> > I suppose we could tolerate the use of approximately 64k of RAM to store
> > 174 characters worth of strings *if* it would bring us big time savings.
> > But I think we need some evidence of the time savings.
> >
> > If this lookup is really a bottleneck, I bet there are other
> > alternatives that are just as fast as this trie and use less code,
> > especially given that there are only 19 strings that need checking.
> 
> Very good point.  I agree that we need to know that the dumb linear
> scan in the original is on the bottleneck and that any replacement
> is an improvement.

Just did a tiny bit of microbenchmarking:

The trie code is indeed somewhat faster, but it's not the bottleneck in
the git_path family of functions.  The sprintf stuff takes way more
time.  Most callers don't need this functionality (an append would do).

But this is a benchmark of just git_path.  I don't happen to see any
cases where git_path is taking up an appreciable amount of runtime.

I only added this because Junio requested a speedup.  So I am perfectly
happy to drop this patch from the series.  

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] path: optimize common dir checking
  2015-10-05 20:10       ` David Turner
@ 2015-10-05 20:36         ` Junio C Hamano
  2015-10-05 20:43           ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2015-10-05 20:36 UTC (permalink / raw
  To: David Turner; +Cc: Michael Haggerty, git, Christian Couder

David Turner <dturner@twopensource.com> writes:

> But this is a benchmark of just git_path.  I don't happen to see any
> cases where git_path is taking up an appreciable amount of runtime.
>
> I only added this because Junio requested a speedup.  So I am perfectly
> happy to drop this patch from the series.  

Ok, then let's drop it for now.  Thanks.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/3] path: optimize common dir checking
  2015-10-05 20:36         ` Junio C Hamano
@ 2015-10-05 20:43           ` Junio C Hamano
  0 siblings, 0 replies; 10+ messages in thread
From: Junio C Hamano @ 2015-10-05 20:43 UTC (permalink / raw
  To: David Turner; +Cc: Michael Haggerty, git, Christian Couder

Junio C Hamano <gitster@pobox.com> writes:

> David Turner <dturner@twopensource.com> writes:
>
>> But this is a benchmark of just git_path.  I don't happen to see any
>> cases where git_path is taking up an appreciable amount of runtime.
>>
>> I only added this because Junio requested a speedup.  So I am perfectly
>> happy to drop this patch from the series.  
>
> Ok, then let's drop it for now.  Thanks.

Having said all that, this conversation happened way after I started
today's integration cycle, I am not inclined to redo all of that,
especially with extra tagging involved in the work done today.

So it is in 'master' now.  I'm open to a patch to revert this
commit, though.

Thanks.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2015-10-05 20:43 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-09-01  2:13 (unknown), David Turner
2015-09-01  2:13 ` [PATCH v5 1/3] refs: clean up common_list David Turner
2015-09-01  2:13 ` [PATCH v5 2/3] path: optimize common dir checking David Turner
2015-10-05  3:00   ` Michael Haggerty
2015-10-05 17:22     ` Junio C Hamano
2015-10-05 20:10       ` David Turner
2015-10-05 20:36         ` Junio C Hamano
2015-10-05 20:43           ` Junio C Hamano
2015-09-01  2:13 ` [PATCH v5 3/3] refs: make refs/bisect/* per-worktree David Turner
2015-09-01 16:32 ` making refs/bisect/ per worktree (was Re: (unknown)) Junio C Hamano

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).