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> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Ilari Liusvaara committed
Jan 20, 2010 at 11:48 UTC
d08bae7e221727e26baab984b792854b842130d7
9 files changed
+223
-2
Documentation/git-log.txt
+11
@@ -107,6 +107,17 @@ git log --follow builtin-rev-list.c::
107
those commits that occurred before the file was given its
108
present name.
109
110
+git log --branches --not --glob=remotes/origin/*::
111
+
112
+ Shows all commits that are in any of local branches but not in
113
+ any of remote tracking branches for 'origin' (what you have that
114
+ origin doesn't).
115
+
116
+git log master --not --glob=remotes/*/master::
117
+
118
+ Shows all commits that are in local master but not in any remote
119
+ repository master branches.
120
+
121
Discussion
122
----------
123
Documentation/git-rev-list.txt
+1
@@ -24,6 +24,7 @@ SYNOPSIS
24
[ \--branches ]
25
[ \--tags ]
26
[ \--remotes ]
27
+ [ \--glob=glob-pattern ]
28
[ \--stdin ]
29
[ \--quiet ]
30
[ \--topo-order ]
Documentation/git-rev-parse.txt
+5
@@ -112,6 +112,11 @@ OPTIONS
112
--remotes::
113
Show tag refs found in `$GIT_DIR/refs/remotes`.
114
115
+--glob=glob-pattern::
116
+ Show refs matching shell glob pattern `glob-pattern`. If pattern
117
+ specified lacks leading 'refs/', it is automatically prepended.
118
+ If pattern lacks '?', '*', or '[', '/*' at the end is impiled.
119
+
120
--show-prefix::
121
When the command is invoked from a subdirectory, show the
122
path of the current directory relative to the top-level
Documentation/rev-list-options.txt
+7
@@ -243,6 +243,13 @@ endif::git-rev-list[]
243
Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed
244
on the command line as '<commit>'.
245
246
+--glob=glob-pattern::
247
+ Pretend as if all the refs matching shell glob `glob-pattern`
248
+ are listed on the command line as '<commit>'. Leading 'refs/',
249
+ is automatically prepended if missing. If pattern lacks '?', '*',
250
+ or '[', '/*' at the end is impiled.
251
+
252
+
253
ifndef::git-rev-list[]
254
--bisect::
255
builtin-rev-parse.c
+5
@@ -52,6 +52,7 @@ static int is_rev_argument(const char *arg)
52
"--parents",
53
"--pretty",
54
"--remotes",
55
+ "--glob=",
56
"--sparse",
57
"--tags",
58
"--topo-order",
@@ -577,6 +578,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
578
for_each_tag_ref(show_reference, NULL);
579
continue;
580
}
581
+ if (!prefixcmp(arg, "--glob=")) {
582
+ for_each_glob_ref(show_reference, arg + 7, NULL);
583
+ continue;
584
+ }
585
if (!strcmp(arg, "--remotes")) {
586
for_each_remote_ref(show_reference, NULL);
587
continue;
refs.c
+45
@@ -519,6 +519,13 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
519
return ref;
520
}
521
522
+/* The argument to filter_refs */
523
+struct ref_filter {
524
+ const char *pattern;
525
+ each_ref_fn *fn;
526
+ void *cb_data;
527
+};
528
+
529
int read_ref(const char *ref, unsigned char *sha1)
530
{
531
if (resolve_ref(ref, sha1, 1, NULL))
@@ -545,6 +552,15 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
552
return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
553
}
554
555
+static int filter_refs(const char *ref, const unsigned char *sha, int flags,
556
+ void *data)
557
+{
558
+ struct ref_filter *filter = (struct ref_filter *)data;
559
+ if (fnmatch(filter->pattern, ref, 0))
560
+ return 0;
561
+ return filter->fn(ref, sha, flags, filter->cb_data);
562
+}
563
+
564
int peel_ref(const char *ref, unsigned char *sha1)
565
{
566
int flag;
@@ -674,6 +690,35 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
690
return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
691
}
692
693
+int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
694
+{
695
+ struct strbuf real_pattern = STRBUF_INIT;
696
+ struct ref_filter filter;
697
+ const char *has_glob_specials;
698
+ int ret;
699
+
700
+ if (prefixcmp(pattern, "refs/"))
701
+ strbuf_addstr(&real_pattern, "refs/");
702
+ strbuf_addstr(&real_pattern, pattern);
703
+
704
+ has_glob_specials = strpbrk(pattern, "?*[");
705
+ if (!has_glob_specials) {
706
+ /* Append impiled '/' '*' if not present. */
707
+ if (real_pattern.buf[real_pattern.len - 1] != '/')
708
+ strbuf_addch(&real_pattern, '/');
709
+ /* No need to check for '*', there is none. */
710
+ strbuf_addch(&real_pattern, '*');
711
+ }
712
+
713
+ filter.pattern = real_pattern.buf;
714
+ filter.fn = fn;
715
+ filter.cb_data = cb_data;
716
+ ret = for_each_ref(filter_refs, &filter);
717
+
718
+ strbuf_release(&real_pattern);
719
+ return ret;
720
+}
721
+
722
int for_each_rawref(each_ref_fn fn, void *cb_data)
723
{
724
return do_for_each_ref("refs/", fn, 0,
refs.h
+1
@@ -25,6 +25,7 @@ extern int for_each_tag_ref(each_ref_fn, void *);
25
extern int for_each_branch_ref(each_ref_fn, void *);
26
extern int for_each_remote_ref(each_ref_fn, void *);
27
extern int for_each_replace_ref(each_ref_fn, void *);
28
+extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
29
30
/* can be used to learn about broken ref and symref */
31
extern int for_each_rawref(each_ref_fn, void *);
revision.c
+14
-2
@@ -699,12 +699,18 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,
699
return 0;
700
}
701
702
+static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
703
+ unsigned flags)
704
+{
705
+ cb->all_revs = revs;
706
+ cb->all_flags = flags;
707
+}
708
+
709
static void handle_refs(struct rev_info *revs, unsigned flags,
710
int (*for_each)(each_ref_fn, void *))
711
{
712
struct all_refs_cb cb;
706
- cb.all_revs = revs;
707
- cb.all_flags = flags;
713
+ init_all_refs_cb(&cb, revs, flags);
714
for_each(handle_one_ref, &cb);
715
}
716
@@ -1352,6 +1358,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
1358
handle_refs(revs, flags, for_each_remote_ref);
1359
continue;
1360
}
1361
+ if (!prefixcmp(arg, "--glob=")) {
1362
+ struct all_refs_cb cb;
1363
+ init_all_refs_cb(&cb, revs, flags);
1364
+ for_each_glob_ref(handle_one_ref, arg + 7, &cb);
1365
+ continue;
1366
+ }
1367
if (!strcmp(arg, "--reflog")) {
1368
handle_reflog(revs, flags);
1369
continue;
t/t6018-rev-list-glob.sh
new
+134
@@ -0,0 +1,134 @@
1
+#!/bin/sh
2
+
3
+test_description='rev-list/rev-parse --glob'
4
+
5
+. ./test-lib.sh
6
+
7
+commit () {
8
+ test_tick &&
9
+ echo $1 > foo &&
10
+ git add foo &&
11
+ git commit -m "$1"
12
+}
13
+
14
+compare () {
15
+ # Split arguments on whitespace.
16
+ git $1 $2 >expected &&
17
+ git $1 $3 >actual &&
18
+ test_cmp expected actual
19
+}
20
+
21
+test_expect_success 'setup' '
22
+
23
+ commit master &&
24
+ git checkout -b subspace/one master &&
25
+ commit one &&
26
+ git checkout -b subspace/two master &&
27
+ commit two &&
28
+ git checkout -b subspace-x master &&
29
+ commit subspace-x &&
30
+ git checkout -b other/three master &&
31
+ commit three &&
32
+ git checkout -b someref master &&
33
+ commit some &&
34
+ git checkout master &&
35
+ commit master2
36
+'
37
+
38
+test_expect_success 'rev-parse --glob=refs/heads/subspace/*' '
39
+
40
+ compare rev-parse "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
41
+
42
+'
43
+
44
+test_expect_success 'rev-parse --glob=heads/subspace/*' '
45
+
46
+ compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace/*"
47
+
48
+'
49
+
50
+test_expect_success 'rev-parse --glob=refs/heads/subspace/' '
51
+
52
+ compare rev-parse "subspace/one subspace/two" "--glob=refs/heads/subspace/"
53
+
54
+'
55
+
56
+test_expect_success 'rev-parse --glob=heads/subspace/' '
57
+
58
+ compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace/"
59
+
60
+'
61
+
62
+test_expect_success 'rev-parse --glob=heads/subspace' '
63
+
64
+ compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace"
65
+
66
+'
67
+
68
+test_expect_success 'rev-parse --glob=heads/subspace/* --glob=heads/other/*' '
69
+
70
+ compare rev-parse "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
71
+
72
+'
73
+
74
+test_expect_success 'rev-parse --glob=heads/someref/* master' '
75
+
76
+ compare rev-parse "master" "--glob=heads/someref/* master"
77
+
78
+'
79
+
80
+test_expect_success 'rev-parse --glob=heads/*' '
81
+
82
+ compare rev-parse "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
83
+
84
+'
85
+
86
+test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
87
+
88
+ compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
89
+
90
+'
91
+
92
+test_expect_success 'rev-list --glob=heads/subspace/*' '
93
+
94
+ compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/*"
95
+
96
+'
97
+
98
+test_expect_success 'rev-list --glob=refs/heads/subspace/' '
99
+
100
+ compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/"
101
+
102
+'
103
+
104
+test_expect_success 'rev-list --glob=heads/subspace/' '
105
+
106
+ compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/"
107
+
108
+'
109
+
110
+test_expect_success 'rev-list --glob=heads/subspace' '
111
+
112
+ compare rev-list "subspace/one subspace/two" "--glob=heads/subspace"
113
+
114
+'
115
+
116
+test_expect_success 'rev-list --glob=heads/someref/* master' '
117
+
118
+ compare rev-list "master" "--glob=heads/someref/* master"
119
+
120
+'
121
+
122
+test_expect_success 'rev-list --glob=heads/subspace/* --glob=heads/other/*' '
123
+
124
+ compare rev-list "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
125
+
126
+'
127
+
128
+test_expect_success 'rev-list --glob=heads/*' '
129
+
130
+ compare rev-list "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
131
+
132
+'
133
+
134
+test_done