threads / patch / 22300

patch, 2 partsrev-parse --branches/--tags/--remotes=pattern

Subject: [PATCH 2/2] rev-parse --branches/--tags/--remotes=pattern

## tl;dr

12 messages between Jan 20, 2010 and Jan 22, 2010. Diffs are folded; open one to read it.

replies: 11people: 5as markdown or json

Ilari Liusvaara· Jan 20, 2010, 09:48 UTC · lore

[PATCH 1/2] rev-parse --glob

Add --glob=<glob-pattern> option to rev-parse and everything that accepts its options. This option matches all refs that match given shell glob pattern (complete with some DWIM logic).

Example:
'git log --branches --not --glob=remotes/origin'
To show what you have that origin doesn't.
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
--glob can do everything --namespace did and more (in fact, what
was valid for --namespace is valid for --glob and even means the same
thing). Built on top of master.
 Documentation/git-log.txt          |   11 +++
 Documentation/git-rev-list.txt     |    1 +
 Documentation/git-rev-parse.txt    |    5 ++
 Documentation/rev-list-options.txt |    7 ++
 builtin-rev-parse.c                |    5 ++
 refs.c                             |   46 ++++++++++++
 refs.h                             |    1 +
 revision.c                         |   16 ++++-
 t/t6018-rev-list-glob.sh           |  136 ++++++++++++++++++++++++++++++++++++
 9 files changed, 226 insertions(+), 2 deletions(-)
 create mode 100755 t/t6018-rev-list-glob.sh
Show changes to 9 files +226 −2

Documentation/git-log.txt, Documentation/git-rev-list.txt, Documentation/git-rev-parse.txt, Documentation/rev-list-options.txt, builtin-rev-parse.c, refs.c, refs.h, revision.c, t/t6018-rev-list-glob.sh

diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 3d79de1..0b874e3 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -107,6 +107,17 @@ git log --follow builtin-rev-list.c::
 	those commits that occurred before the file was given its
 	present name.
 
+git log --branches --not --glob=remotes/origin/*::
+
+	Shows all commits that are in any of local branches but not in
+	any of remote tracking branches for 'origin' (what you have that
+	origin doesn't).
+
+git log master --not --glob=remotes/*/master::
+
+	Shows all commits that are in local master but not in any remote
+	repository master branches.
+
 Discussion
 ----------
 
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 3341d1b..33122a3 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -24,6 +24,7 @@ SYNOPSIS
 	     [ \--branches ]
 	     [ \--tags ]
 	     [ \--remotes ]
+	     [ \--glob=glob-pattern ]
 	     [ \--stdin ]
 	     [ \--quiet ]
 	     [ \--topo-order ]
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 82045a2..6eb8c14 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -112,6 +112,11 @@ OPTIONS
 --remotes::
 	Show tag refs found in `$GIT_DIR/refs/remotes`.
 
+--glob=glob-pattern::
+	Show refs matching shell glob pattern `glob-pattern`. If pattern
+	specified lacks leading 'refs/', it is automatically prepended.
+	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
+
 --show-prefix::
 	When the command is invoked from a subdirectory, show the
 	path of the current directory relative to the top-level
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 1f57aed..6d03c17 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -243,6 +243,13 @@ endif::git-rev-list[]
 	Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
 	on the command line as '<commit>'.
 
+--glob=glob-pattern::
+	Pretend as if all the refs matching shell glob `glob-pattern`
+	are listed on the command line as '<commit>'. Leading 'refs/',
+	is automatically prepended if missing. If pattern lacks '?', '*',
+	or '[', '/*' at the end is impiled.
+
+
 ifndef::git-rev-list[]
 --bisect::
 
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 37d0233..a635dde 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -52,6 +52,7 @@ static int is_rev_argument(const char *arg)
 		"--parents",
 		"--pretty",
 		"--remotes",
+		"--glob=",
 		"--sparse",
 		"--tags",
 		"--topo-order",
@@ -577,6 +578,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				for_each_tag_ref(show_reference, NULL);
 				continue;
 			}
+			if (!prefixcmp(arg, "--glob=")) {
+				for_each_glob_ref(show_reference, arg + 7, NULL);
+				continue;
+			}
 			if (!strcmp(arg, "--remotes")) {
 				for_each_remote_ref(show_reference, NULL);
 				continue;
diff --git a/refs.c b/refs.c
index 3e73a0a..5adf69c 100644
--- a/refs.c
+++ b/refs.c
@@ -519,6 +519,14 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
 	return ref;
 }
 
+/* The argument to filter_refs */
+struct ref_filter
+{
+	const char *pattern;
+	each_ref_fn *fn;
+	void *cb_data;
+};
+
 int read_ref(const char *ref, unsigned char *sha1)
 {
 	if (resolve_ref(ref, sha1, 1, NULL))
@@ -545,6 +553,15 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 	return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
 }
 
+static int filter_refs(const char* ref, const unsigned char *sha, int flags,
+	void* data)
+{
+	struct ref_filter *filter = (struct ref_filter*)data;
+	if (fnmatch(filter->pattern, ref, 0))
+		return 0;
+	return filter->fn(ref, sha, flags, filter->cb_data);
+}
+
 int peel_ref(const char *ref, unsigned char *sha1)
 {
 	int flag;
@@ -674,6 +691,35 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 	return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
 }
 
+int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+{
+	struct strbuf real_pattern = STRBUF_INIT;
+	struct ref_filter filter;
+	const char *has_glob_specials;
+	int ret;
+
+	if (prefixcmp(pattern, "refs/"))
+		strbuf_addstr(&real_pattern, "refs/");
+	strbuf_addstr(&real_pattern, pattern);
+
+	has_glob_specials = strpbrk(pattern, "?*[");
+	if (!has_glob_specials) {
+		/* Append impiled '/' '*' if not present. */
+		if (real_pattern.buf[real_pattern.len - 1] != '/')
+			strbuf_addch(&real_pattern, '/');
+		/* No need to check for '*', there is none. */
+		strbuf_addch(&real_pattern, '*');
+	}
+
+	filter.pattern = real_pattern.buf;
+	filter.fn = fn;
+	filter.cb_data = cb_data;
+	ret = for_each_ref(filter_refs, &filter);
+
+	strbuf_release(&real_pattern);
+	return ret;
+}
+
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
 	return do_for_each_ref("refs/", fn, 0,
diff --git a/refs.h b/refs.h
index e141991..78ad173 100644
--- a/refs.h
+++ b/refs.h
@@ -25,6 +25,7 @@ extern int for_each_tag_ref(each_ref_fn, void *);
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
 extern int for_each_replace_ref(each_ref_fn, void *);
+extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
 
 /* can be used to learn about broken ref and symref */
 extern int for_each_rawref(each_ref_fn, void *);
diff --git a/revision.c b/revision.c
index 25fa14d..162b182 100644
--- a/revision.c
+++ b/revision.c
@@ -699,12 +699,18 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,
 	return 0;
 }
 
+static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
+	unsigned flags)
+{
+	cb->all_revs = revs;
+	cb->all_flags = flags;
+}
+
 static void handle_refs(struct rev_info *revs, unsigned flags,
 		int (*for_each)(each_ref_fn, void *))
 {
 	struct all_refs_cb cb;
-	cb.all_revs = revs;
-	cb.all_flags = flags;
+	init_all_refs_cb(&cb, revs, flags);
 	for_each(handle_one_ref, &cb);
 }
 
@@ -1352,6 +1358,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				handle_refs(revs, flags, for_each_remote_ref);
 				continue;
 			}
+			if (!prefixcmp(arg, "--glob=")) {
+				struct all_refs_cb cb;
+				init_all_refs_cb(&cb, revs, flags);
+				for_each_glob_ref(handle_one_ref, arg + 7, &cb);
+				continue;
+			}
 			if (!strcmp(arg, "--reflog")) {
 				handle_reflog(revs, flags);
 				continue;
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
new file mode 100755
index 0000000..0d7e4bc
--- /dev/null
+++ b/t/t6018-rev-list-glob.sh
@@ -0,0 +1,136 @@
+#!/bin/sh
+
+test_description='rev-list/rev-parse --glob'
+
+. ./test-lib.sh
+
+
+commit () {
+	test_tick &&
+	echo $1 > foo &&
+	git add foo &&
+	git commit -m "$1"
+}
+
+compare () {
+	# Split arguments on whitespace.
+	git $1 $2 >expected &&
+	git $1 $3 >actual &&
+	test_cmp expected actual
+}
+
+test_expect_success 'setup' '
+
+	commit master &&
+	git checkout -b subspace/one master &&
+	commit one &&
+	git checkout -b subspace/two master &&
+	commit two &&
+	git checkout -b subspace-x master &&
+	commit subspace-x &&
+	git checkout -b other/three master &&
+	commit three &&
+	git checkout -b someref master &&
+	commit some &&
+	git checkout master &&
+	commit master2
+'
+
+test_expect_success 'rev-parse --glob=refs/heads/subspace/*' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/*' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace/*"
+
+'
+
+test_expect_success 'rev-parse --glob=refs/heads/subspace/' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=refs/heads/subspace/"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace/"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace' '
+
+	compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/* --glob=heads/other/*' '
+
+	compare rev-parse "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/someref/* master' '
+
+	compare rev-parse "master" "--glob=heads/someref/* master"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/*' '
+
+	compare rev-parse "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
+
+'
+
+test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/*' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/*"
+
+'
+
+test_expect_success 'rev-list --glob=refs/heads/subspace/' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace' '
+
+	compare rev-list "subspace/one subspace/two" "--glob=heads/subspace"
+
+'
+
+test_expect_success 'rev-list --glob=heads/someref/* master' '
+
+	compare rev-list "master" "--glob=heads/someref/* master"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/* --glob=heads/other/*' '
+
+	compare rev-list "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
+
+'
+
+test_expect_success 'rev-list --glob=heads/*' '
+
+	compare rev-list "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
+
+'
+
+
+test_done
-- 
1.6.6.199.gff4b0
Ilari Liusvaara· Jan 20, 2010, 09:48 UTC · re: Ilari Liusvaara · lore

Since local branch, tags and remote tracking branch namespaces are most often used, add shortcut notations for globbing those in manner similar to --glob option.

With this, one can express the "what I have but origin doesn't?" as:

'git log --branches --not --remotes=origin'
Original-idea-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
---
Builds on top of --glob patch.
 Documentation/git-log.txt          |    4 +-
 Documentation/git-rev-list.txt     |    6 ++--
 Documentation/git-rev-parse.txt    |   19 +++++++---
 Documentation/rev-list-options.txt |   18 +++++++---
 builtin-rev-parse.c                |   18 ++++++++++
 refs.c                             |   12 ++++++-
 refs.h                             |    1 +
 revision.c                         |   18 ++++++++++
 t/t6018-rev-list-glob.sh           |   62 +++++++++++++++++++++++++++++++++++-
 9 files changed, 138 insertions(+), 20 deletions(-)
Show changes to 9 files +138 −14

Documentation/git-log.txt, Documentation/git-rev-list.txt, Documentation/git-rev-parse.txt, Documentation/rev-list-options.txt, builtin-rev-parse.c, refs.c, refs.h, revision.c, t/t6018-rev-list-glob.sh

diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 0b874e3..0acd526 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -107,13 +107,13 @@ git log --follow builtin-rev-list.c::
 	those commits that occurred before the file was given its
 	present name.
 
-git log --branches --not --glob=remotes/origin/*::
+git log --branches --not --remotes=origin::
 
 	Shows all commits that are in any of local branches but not in
 	any of remote tracking branches for 'origin' (what you have that
 	origin doesn't).
 
-git log master --not --glob=remotes/*/master::
+git log master --not --remotes=*/master::
 
 	Shows all commits that are in local master but not in any remote
 	repository master branches.
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 33122a3..f857116 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -21,9 +21,9 @@ SYNOPSIS
 	     [ \--full-history ]
 	     [ \--not ]
 	     [ \--all ]
-	     [ \--branches ]
-	     [ \--tags ]
-	     [ \--remotes ]
+	     [ \--branches[=pattern] ]
+	     [ \--tags=[pattern] ]
+	     [ \--remotes=[pattern] ]
 	     [ \--glob=glob-pattern ]
 	     [ \--stdin ]
 	     [ \--quiet ]
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 6eb8c14..afe8699 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -103,14 +103,21 @@ OPTIONS
 --all::
 	Show all refs found in `$GIT_DIR/refs`.
 
---branches::
-	Show branch refs found in `$GIT_DIR/refs/heads`.
+--branches[=pattern]::
+	Show branch refs found in `$GIT_DIR/refs/heads`. If `pattern`
+	is given, only branches matching given shell glob are shown.
+	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
 
---tags::
-	Show tag refs found in `$GIT_DIR/refs/tags`.
+--tags[=pattern]::
+	Show tag refs found in `$GIT_DIR/refs/tags`. If `pattern`
+	is given, only tags matching given shell glob are shown.
+	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
 
---remotes::
-	Show tag refs found in `$GIT_DIR/refs/remotes`.
+--remotes[=pattern]::
+	Show tag refs found in `$GIT_DIR/refs/remotes`. If `pattern`
+	is given, only remote tracking branches matching given shell glob
+	are shown. If pattern lacks '?', '*', or '[', '/*' at the end is
+	impiled.
 
 --glob=glob-pattern::
 	Show refs matching shell glob pattern `glob-pattern`. If pattern
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 6d03c17..3ef7117 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -228,20 +228,26 @@ endif::git-rev-list[]
 	Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the
 	command line as '<commit>'.
 
---branches::
+--branches[=pattern]::
 
 	Pretend as if all the refs in `$GIT_DIR/refs/heads` are listed
-	on the command line as '<commit>'.
+	on the command line as '<commit>'. If `pattern` is given, limit
+	branches to ones matching given shell glob. If pattern lacks '?',
+	'*', or '[', '/*' at the end is impiled.
 
---tags::
+--tags[=pattern]::
 
 	Pretend as if all the refs in `$GIT_DIR/refs/tags` are listed
-	on the command line as '<commit>'.
+	on the command line as '<commit>'. If `pattern` is given, limit
+	tags to ones matching given shell glob. If pattern lacks '?', '*',
+	or '[', '/*' at the end is impiled.
 
---remotes::
+--remotes[=pattern]::
 
 	Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
-	on the command line as '<commit>'.
+	on the command line as '<commit>'. If `pattern`is given, limit
+	remote tracking branches to ones matching given shell glob.
+	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
 
 --glob=glob-pattern::
 	Pretend as if all the refs matching shell glob `glob-pattern`
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index a635dde..d14fe20 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -41,6 +41,7 @@ static int is_rev_argument(const char *arg)
 		"--all",
 		"--bisect",
 		"--dense",
+		"--branches=",
 		"--branches",
 		"--header",
 		"--max-age=",
@@ -51,9 +52,11 @@ static int is_rev_argument(const char *arg)
 		"--objects-edge",
 		"--parents",
 		"--pretty",
+		"--remotes=",
 		"--remotes",
 		"--glob=",
 		"--sparse",
+		"--tags=",
 		"--tags",
 		"--topo-order",
 		"--date-order",
@@ -570,10 +573,20 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				for_each_ref_in("refs/bisect/good", anti_reference, NULL);
 				continue;
 			}
+			if (!prefixcmp(arg, "--branches=")) {
+				for_each_glob_ref_in(show_reference, arg + 11,
+					"refs/heads/", NULL);
+				continue;
+			}
 			if (!strcmp(arg, "--branches")) {
 				for_each_branch_ref(show_reference, NULL);
 				continue;
 			}
+			if (!prefixcmp(arg, "--tags=")) {
+				for_each_glob_ref_in(show_reference, arg + 7,
+					"refs/tags/", NULL);
+				continue;
+			}
 			if (!strcmp(arg, "--tags")) {
 				for_each_tag_ref(show_reference, NULL);
 				continue;
@@ -582,6 +595,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				for_each_glob_ref(show_reference, arg + 7, NULL);
 				continue;
 			}
+			if (!prefixcmp(arg, "--remotes=")) {
+				for_each_glob_ref_in(show_reference, arg + 10,
+					"refs/remotes/", NULL);
+				continue;
+			}
 			if (!strcmp(arg, "--remotes")) {
 				for_each_remote_ref(show_reference, NULL);
 				continue;
diff --git a/refs.c b/refs.c
index 5adf69c..af78c31 100644
--- a/refs.c
+++ b/refs.c
@@ -691,15 +691,18 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 	return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
 }
 
-int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
+	const char *prefix, void *cb_data)
 {
 	struct strbuf real_pattern = STRBUF_INIT;
 	struct ref_filter filter;
 	const char *has_glob_specials;
 	int ret;
 
-	if (prefixcmp(pattern, "refs/"))
+	if (!prefix && prefixcmp(pattern, "refs/"))
 		strbuf_addstr(&real_pattern, "refs/");
+	else if (prefix)
+		strbuf_addstr(&real_pattern, prefix);
 	strbuf_addstr(&real_pattern, pattern);
 
 	has_glob_specials = strpbrk(pattern, "?*[");
@@ -720,6 +723,11 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 	return ret;
 }
 
+int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+{
+	return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
+}
+
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
 	return do_for_each_ref("refs/", fn, 0,
diff --git a/refs.h b/refs.h
index 78ad173..f7648b9 100644
--- a/refs.h
+++ b/refs.h
@@ -26,6 +26,7 @@ extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
 extern int for_each_replace_ref(each_ref_fn, void *);
 extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
+extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
 
 /* can be used to learn about broken ref and symref */
 extern int for_each_rawref(each_ref_fn, void *);
diff --git a/revision.c b/revision.c
index 162b182..1e9277d 100644
--- a/revision.c
+++ b/revision.c
@@ -1364,6 +1364,24 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				for_each_glob_ref(handle_one_ref, arg + 7, &cb);
 				continue;
 			}
+			if (!prefixcmp(arg, "--branches=")) {
+				struct all_refs_cb cb;
+				init_all_refs_cb(&cb, revs, flags);
+				for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
+				continue;
+			}
+			if (!prefixcmp(arg, "--tags=")) {
+				struct all_refs_cb cb;
+				init_all_refs_cb(&cb, revs, flags);
+				for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
+				continue;
+			}
+			if (!prefixcmp(arg, "--remotes=")) {
+				struct all_refs_cb cb;
+				init_all_refs_cb(&cb, revs, flags);
+				for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
+				continue;
+			}
 			if (!strcmp(arg, "--reflog")) {
 				handle_reflog(revs, flags);
 				continue;
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 0d7e4bc..bf85e0c 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -33,7 +33,9 @@ test_expect_success 'setup' '
 	git checkout -b someref master &&
 	commit some &&
 	git checkout master &&
-	commit master2
+	commit master2 &&
+	git tag foo/bar master &&
+	git update-ref refs/remotes/foo/baz master
 '
 
 test_expect_success 'rev-parse --glob=refs/heads/subspace/*' '
@@ -66,6 +68,24 @@ test_expect_success 'rev-parse --glob=heads/subspace' '
 
 '
 
+test_expect_success 'rev-parse --branches=subspace/*' '
+
+	compare rev-parse "subspace/one subspace/two" "--branches=subspace/*"
+
+'
+
+test_expect_success 'rev-parse --branches=subspace/' '
+
+	compare rev-parse "subspace/one subspace/two" "--branches=subspace/"
+
+'
+
+test_expect_success 'rev-parse --branches=subspace' '
+
+	compare rev-parse "subspace/one subspace/two" "--branches=subspace"
+
+'
+
 test_expect_success 'rev-parse --glob=heads/subspace/* --glob=heads/other/*' '
 
 	compare rev-parse "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
@@ -84,6 +104,18 @@ test_expect_success 'rev-parse --glob=heads/*' '
 
 '
 
+test_expect_success 'rev-parse --tags=foo' '
+
+	compare rev-parse "foo/bar" "--tags=foo"
+
+'
+
+test_expect_success 'rev-parse --remotes=foo' '
+
+	compare rev-parse "foo/baz" "--remotes=foo"
+
+'
+
 test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
 
 	compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
@@ -114,6 +146,23 @@ test_expect_success 'rev-list --glob=heads/subspace' '
 
 '
 
+test_expect_success 'rev-list --branches=subspace/*' '
+
+	compare rev-list "subspace/one subspace/two" "--branches=subspace/*"
+
+'
+
+test_expect_success 'rev-list --branches=subspace/' '
+
+	compare rev-list "subspace/one subspace/two" "--branches=subspace/"
+
+'
+
+test_expect_success 'rev-list --branches=subspace' '
+
+	compare rev-list "subspace/one subspace/two" "--branches=subspace"
+
+'
 test_expect_success 'rev-list --glob=heads/someref/* master' '
 
 	compare rev-list "master" "--glob=heads/someref/* master"
@@ -132,5 +181,16 @@ test_expect_success 'rev-list --glob=heads/*' '
 
 '
 
+test_expect_success 'rev-list --tags=foo' '
+
+	compare rev-list "foo/bar" "--tags=foo"
+
+'
+
+test_expect_success 'rev-list --remotes=foo' '
+
+	compare rev-list "foo/baz" "--remotes=foo"
+
+'
 
 test_done
-- 
1.6.6.199.gff4b0
Junio C Hamano· Jan 20, 2010, 10:18 UTC · re: Ilari Liusvaara · lore

Re: [PATCH 2/2] rev-parse --branches/--tags/--remotes=pattern

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:
Show 12 quoted lines
> Since local branch, tags and remote tracking branch namespaces are
> most often used, add shortcut notations for globbing those in
> manner similar to --glob option.
>
> With this, one can express the "what I have but origin doesn't?"
> as:
>
> 'git log --branches --not --remotes=origin'
>
> Original-idea-by: Johannes Sixt <j6t@kdbg.org>
> Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
> ---
Nice.
Johannes Sixt· Jan 21, 2010, 12:51 UTC · re: Ilari Liusvaara · lore

Re: [PATCH 2/2] rev-parse --branches/--tags/--remotes=pattern

Works for me, thank you very much!
Junio, kindly squash in these speeling fixes in both patches:
sed -i 's/impiled/implied/g' `git diff HEAD^ --name-only Documentation/`
-- Hannes
Michael J Gruber· Jan 21, 2010, 13:51 UTC · re: Johannes Sixt · lore

Re: [PATCH 2/2] rev-parse --branches/--tags/--remotes=pattern

Johannes Sixt venit, vidit, dixit 21.01.2010 13:51:
Show 6 quoted lines
> Works for me, thank you very much!
> 
> Junio, kindly squash in these speeling fixes in both patches:
> 
> sed -i 's/impiled/implied/g' `git diff HEAD^ --name-only Documentation/`
> 

Also, the option descriptions are kind of staccato English right now, lacking a few "the" here and there. Should we fix this or simply leave it at that?

Michael
Thomas Rast· Jan 21, 2010, 17:27 UTC · re: Michael J Gruber · lore

Re: [PATCH 2/2] rev-parse --branches/--tags/--remotes=pattern

Thanks Ilari for implementing my wishes :-)
On Thursday 21 January 2010 14:51:41 Michael J Gruber wrote:
Show 11 quoted lines
> Johannes Sixt venit, vidit, dixit 21.01.2010 13:51:
> > Works for me, thank you very much!
> > 
> > Junio, kindly squash in these speeling fixes in both patches:
> > 
> > sed -i 's/impiled/implied/g' `git diff HEAD^ --name-only Documentation/`
> > 
> 
> Also, the option descriptions are kind of staccato English right now,
> lacking a few "the" here and there. Should we fix this or simply leave
> it at that?

Maybe the --branches, --tags and --remotes could be consolidated a bit, as in the draft patch below. If --all took a pattern, it could be included there too, but that would probably look too weird.

Show changes to diff +14 −16
diff --git i/Documentation/git-rev-parse.txt w/Documentation/git-rev-parse.txt
index 2bbb566..6fce919 100644
--- i/Documentation/git-rev-parse.txt
+++ w/Documentation/git-rev-parse.txt
@@ -104,29 +104,26 @@ OPTIONS
 	Show all refs found in `$GIT_DIR/refs`.
 
 --branches[=pattern]::
-	Show branch refs found in `$GIT_DIR/refs/heads`. If `pattern`
-	is given, only branches matching given shell glob are shown.
-	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
-
 --tags[=pattern]::
-	Show tag refs found in `$GIT_DIR/refs/tags`. If `pattern`
-	is given, only tags matching given shell glob are shown.
-	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
-
 --remotes[=pattern]::
-	Show tag refs found in `$GIT_DIR/refs/remotes`. If `pattern`
-	is given, only remote tracking branches matching given shell glob
-	are shown. If pattern lacks '?', '*', or '[', '/*' at the end is
-	impiled.
+	Show all branches, tags, or remote-tracking branches,
+	respectively (i.e., refs found in `$GIT_DIR/refs/heads`,
+	`$GIT_DIR/refs/tags`, or `$GIT_DIR/refs/remotes`,
+	respectively).
++
+If a `pattern` is given, only refs matching the given shell glob are
+shown.  If the pattern does not contain a globbing character (`?`,
+`\*`, or `[`), it is anchored by appending `/\*`.
+
+--glob=pattern::
+	Show all refs matching the shell glob pattern `pattern`. If
+	the pattern does not start with `refs/`, this is automatically
+	prepended.  If the pattern does not contain a globbing
+	character (`?`, `\*`, or `[`), it is anchored by appending `/\*`.
 
 --show-toplevel::
 	Show the absolute path of the top-level directory.
 
---glob=glob-pattern::
-	Show refs matching shell glob pattern `glob-pattern`. If pattern
-	specified lacks leading 'refs/', it is automatically prepended.
-	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
-
 --show-prefix::
 	When the command is invoked from a subdirectory, show the
 	path of the current directory relative to the top-level
-- 
Thomas Rast
trast@{inf,student}.ethz.ch
Junio C Hamano· Jan 21, 2010, 20:09 UTC · re: Thomas Rast · lore

Re: [PATCH 2/2] rev-parse --branches/--tags/--remotes=pattern

Thomas Rast <trast@student.ethz.ch> writes:
> Maybe the --branches, --tags and --remotes could be consolidated a
> bit, as in the draft patch below.
Looks sensible.
Show 53 quoted lines
> diff --git i/Documentation/git-rev-parse.txt w/Documentation/git-rev-parse.txt
> index 2bbb566..6fce919 100644
> --- i/Documentation/git-rev-parse.txt
> +++ w/Documentation/git-rev-parse.txt
> @@ -104,29 +104,26 @@ OPTIONS
>  	Show all refs found in `$GIT_DIR/refs`.
>  
>  --branches[=pattern]::
> -	Show branch refs found in `$GIT_DIR/refs/heads`. If `pattern`
> -	is given, only branches matching given shell glob are shown.
> -	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
> -
>  --tags[=pattern]::
> -	Show tag refs found in `$GIT_DIR/refs/tags`. If `pattern`
> -	is given, only tags matching given shell glob are shown.
> -	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
> -
>  --remotes[=pattern]::
> -	Show tag refs found in `$GIT_DIR/refs/remotes`. If `pattern`
> -	is given, only remote tracking branches matching given shell glob
> -	are shown. If pattern lacks '?', '*', or '[', '/*' at the end is
> -	impiled.
> +	Show all branches, tags, or remote-tracking branches,
> +	respectively (i.e., refs found in `$GIT_DIR/refs/heads`,
> +	`$GIT_DIR/refs/tags`, or `$GIT_DIR/refs/remotes`,
> +	respectively).
> ++
> +If a `pattern` is given, only refs matching the given shell glob are
> +shown.  If the pattern does not contain a globbing character (`?`,
> +`\*`, or `[`), it is anchored by appending `/\*`.
> +
> +--glob=pattern::
> +	Show all refs matching the shell glob pattern `pattern`. If
> +	the pattern does not start with `refs/`, this is automatically
> +	prepended.  If the pattern does not contain a globbing
> +	character (`?`, `\*`, or `[`), it is anchored by appending `/\*`.
>  
>  --show-toplevel::
>  	Show the absolute path of the top-level directory.
>  
> ---glob=glob-pattern::
> -	Show refs matching shell glob pattern `glob-pattern`. If pattern
> -	specified lacks leading 'refs/', it is automatically prepended.
> -	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
> -
>  --show-prefix::
>  	When the command is invoked from a subdirectory, show the
>  	path of the current directory relative to the top-level
>
>
> -- 
> Thomas Rast
> trast@{inf,student}.ethz.ch
Thomas Rast· Jan 22, 2010, 00:21 UTC · re: Junio C Hamano · lore

[PATCH] Documentation: improve description of --glob=pattern and friends

Consolidate the descriptions of --branches, --tags and --remotes a bit, to make it less repetitive. Improve the grammar a bit, and spell out the meaning of the 'append /*' rule.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---

Compared to the earlier draft, I changed "it is anchored" (by appending '/*') to "it is turned into a prefix match", since this is not anchoring in the ^regex$ sense. (I guess 'leading-component match' would be even more accurate, but that makes the sentence unwieldy in my ears.)

 Documentation/git-rev-parse.txt |   32 +++++++++++++++-----------------
 1 files changed, 15 insertions(+), 17 deletions(-)
Show changes to Documentation/git-rev-parse.txt +15 −16
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index e2cb913..e7845d4 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -104,25 +104,23 @@ OPTIONS
 	Show all refs found in `$GIT_DIR/refs`.
 
 --branches[=pattern]::
-	Show branch refs found in `$GIT_DIR/refs/heads`. If `pattern`
-	is given, only branches matching given shell glob are shown.
-	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
-
 --tags[=pattern]::
-	Show tag refs found in `$GIT_DIR/refs/tags`. If `pattern`
-	is given, only tags matching given shell glob are shown.
-	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
-
 --remotes[=pattern]::
-	Show tag refs found in `$GIT_DIR/refs/remotes`. If `pattern`
-	is given, only remote tracking branches matching given shell glob
-	are shown. If pattern lacks '?', '*', or '[', '/*' at the end is
-	impiled.
-
---glob=glob-pattern::
-	Show refs matching shell glob pattern `glob-pattern`. If pattern
-	specified lacks leading 'refs/', it is automatically prepended.
-	If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
+	Show all branches, tags, or remote-tracking branches,
+	respectively (i.e., refs found in `$GIT_DIR/refs/heads`,
+	`$GIT_DIR/refs/tags`, or `$GIT_DIR/refs/remotes`,
+	respectively).
++
+If a `pattern` is given, only refs matching the given shell glob are
+shown.  If the pattern does not contain a globbing character (`?`,
+`\*`, or `[`), it is turned into a prefix match by appending `/\*`.
+
+--glob=pattern::
+	Show all refs matching the shell glob pattern `pattern`. If
+	the pattern does not start with `refs/`, this is automatically
+	prepended.  If the pattern does not contain a globbing
+	character (`?`, `\*`, or `[`), it is turned into a prefix
+	match by appending `/\*`.
 
 --show-toplevel::
 	Show the absolute path of the top-level directory.
-- 
1.6.6.1.534.gf034a
Junio C Hamano· Jan 22, 2010, 00:51 UTC · re: Thomas Rast · lore

Re: [PATCH] Documentation: improve description of --glob=pattern and friends

Thomas Rast <trast@student.ethz.ch> writes:
Show 12 quoted lines
> Consolidate the descriptions of --branches, --tags and --remotes a
> bit, to make it less repetitive.  Improve the grammar a bit, and spell
> out the meaning of the 'append /*' rule.
>
> Signed-off-by: Thomas Rast <trast@student.ethz.ch>
> ---
>
> Compared to the earlier draft, I changed "it is anchored" (by
> appending '/*') to "it is turned into a prefix match", since this is
> not anchoring in the ^regex$ sense.  (I guess 'leading-component
> match' would be even more accurate, but that makes the sentence
> unwieldy in my ears.)

By the way, we may someday want to change that to do FNM_PATHNAME instead of just a flat glob.

Incidentally, that is why I prefer the earlier suggestion to do "check with memcmp() if it is a path-prefix, and if it fails, then give fnmatch(FNM_PATHNAME) a chance". That way, we don't have to say "by appending '/*'" (which is an implementation detail we can change).

It seems that the suggestion was ignored, though.
Junio C Hamano· Jan 21, 2010, 17:58 UTC · re: Johannes Sixt · lore

Re: [PATCH 2/2] rev-parse --branches/--tags/--remotes=pattern

Johannes Sixt <j.sixt@viscovery.net> writes:
Show 5 quoted lines
> Works for me, thank you very much!
>
> Junio, kindly squash in these speeling fixes in both patches:
>
> sed -i 's/impiled/implied/g' `git diff HEAD^ --name-only Documentation/`

Thanks; I've already pushed out what I was preparing before going to bed, so these fixes need to happen in-tree as incremental changes.

Junio C Hamano· Jan 20, 2010, 10:17 UTC · re: Ilari Liusvaara · lore

Re: [PATCH 1/2] rev-parse --glob

Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:
Show 12 quoted lines
> Add --glob=<glob-pattern> option to rev-parse and everything that
> accepts its options. This option matches all refs that match given
> shell glob pattern (complete with some DWIM logic).
>
> Example:
>
> 'git log --branches --not --glob=remotes/origin'
>
> To show what you have that origin doesn't.
>
> Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
> ---

Looks fairly competently done. Does this mean you've abandoned the --namespace series? I don't mind reverting them from 'next'.

Show 6 quoted lines
> +static int filter_refs(const char* ref, const unsigned char *sha, int flags,
> +	void* data)
> +{
> +	struct ref_filter *filter = (struct ref_filter*)data;
> +	if (fnmatch(filter->pattern, ref, 0))
> +		return 0;

In many places (not just refs) we use "If leading path component matches, or fnmatch() says Ok" (e.g. builtin-for-each-ref.c, builtin_grep.c), and in some other places we only use fnmatch(). I think it is better to pick one convention, and adjust the other one to match, and my gut feeling is that "leading path or fnmatch()", while theologically might be messier, would be more useful in practice. And that was why I did my "like this" patch http://article.gmane.org/gmane.comp.version-control.git/137492 that way.

My longer term desire is to consolidate these fnmatch() users to call a single helper function, and then perhaps replace fnmatch() in that helper function with something more powerful. Although I do not personally care too much, people often wanted to have "path/**/leaf-that-is-deep" matcher.

I don't want you to go there just yet with this series, but at least following the same style everywhere would pave the way to consolidating them.

Show 16 quoted lines
> @@ -674,6 +691,35 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
>  	return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
>  }
>  
> +int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
> +{
> +	struct strbuf real_pattern = STRBUF_INIT;
> +	struct ref_filter filter;
> +	const char *has_glob_specials;
> +	int ret;
> +
> +	if (prefixcmp(pattern, "refs/"))
> +		strbuf_addstr(&real_pattern, "refs/");
> +	strbuf_addstr(&real_pattern, pattern);
> +
> +	has_glob_specials = strpbrk(pattern, "?*[");

Would the need for this code hopefully disappear if we take the "leading path or fnmatch()" check? If we still need this, we probably would want to add a has_glob_special() helper function not as a hardcoded srpbrk(), but in terms of is_glob_special() in git-compat-util.h.

Thanks.
Ilari Liusvaara· Jan 20, 2010, 11:08 UTC · re: Junio C Hamano · lore

Re: [PATCH 1/2] rev-parse --glob

On Wed, Jan 20, 2010 at 02:17:15AM -0800, Junio C Hamano wrote:
> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:
> 
> Looks fairly competently done.  Does this mean you've abandoned the --namespace
> series?  I don't mind reverting them from 'next'.
Pretty much.
 
Show 14 quoted lines
> > +static int filter_refs(const char* ref, const unsigned char *sha, int flags,
> > +	void* data)
> > +{
> > +	struct ref_filter *filter = (struct ref_filter*)data;
> > +	if (fnmatch(filter->pattern, ref, 0))
> > +		return 0;
> 
> In many places (not just refs) we use "If leading path component matches,
> or fnmatch() says Ok" (e.g. builtin-for-each-ref.c, builtin_grep.c), and
> in some other places we only use fnmatch().  I think it is better to pick
> one convention, and adjust the other one to match, and my gut feeling is
> that "leading path or fnmatch()", while theologically might be messier,
> would be more useful in practice.  And that was why I did my "like this"
> patch http://article.gmane.org/gmane.comp.version-control.git/137492 that way.

That pattern of course has been edited before (and the namespace behavoiour is implemented that way).

> My longer term desire is to consolidate these fnmatch() users to call a
> single helper function, and then perhaps replace fnmatch() in that helper
> function with something more powerful.  Although I do not personally care
> too much, people often wanted to have "path/**/leaf-that-is-deep" matcher.
Extended globs? Except that:
'git check-ref-format refs/heads/X+\(a\|b\) ; echo $?' => 0

And '+(a|b)' is wildcard in extended glob (not expressible with standard glob operators).

Extended globbing in gitignore would be useful as some have asked if there is way to use regular expressions there (nope). At least extended globs are equivalent to regular expressions in power.

 
Show 21 quoted lines
> > @@ -674,6 +691,35 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
> >  	return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
> >  }
> >  
> > +int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
> > +{
> > +	struct strbuf real_pattern = STRBUF_INIT;
> > +	struct ref_filter filter;
> > +	const char *has_glob_specials;
> > +	int ret;
> > +
> > +	if (prefixcmp(pattern, "refs/"))
> > +		strbuf_addstr(&real_pattern, "refs/");
> > +	strbuf_addstr(&real_pattern, pattern);
> > +
> > +	has_glob_specials = strpbrk(pattern, "?*[");
> 
> Would the need for this code hopefully disappear if we take the "leading
> path or fnmatch()" check?  If we still need this, we probably would want
> to add a has_glob_special() helper function not as a hardcoded srpbrk(),
> but in terms of is_glob_special() in git-compat-util.h.

'--glob=remotes/origin' and '--glob=remotes/origin*' are not the same. The first gets DWIMed to '--glob=refs/remotes/origin/*'. The usual case for globbing is globbing entiere namespace (name of one remote can be prefix of another).

-Ilari

← back to recent threads