Skip to content

Commit d1d3d46

Browse files
committed
Merge branch 'ab/ref-filter-no-contains'
"git tag/branch/for-each-ref" family of commands long allowed to filter the refs by "--contains X" (show only the refs that are descendants of X), "--merged X" (show only the refs that are ancestors of X), "--no-merged X" (show only the refs that are not ancestors of X). One curious omission, "--no-contains X" (show only the refs that are not descendants of X) has been added to them. * ab/ref-filter-no-contains: tag: add tests for --with and --without ref-filter: reflow recently changed branch/tag/for-each-ref docs ref-filter: add --no-contains option to tag/branch/for-each-ref tag: change --point-at to default to HEAD tag: implicitly supply --list given another list-like option tag: change misleading --list <pattern> documentation parse-options: add OPT_NONEG to the "contains" option tag: add more incompatibles mode tests for-each-ref: partly change <object> to <commit> in help tag tests: fix a typo in a test description tag: remove a TODO item from the test suite ref-filter: add test for --contains on a non-commit ref-filter: make combining --merged & --no-merged an error tag doc: reword --[no-]merged to talk about commits, not tips tag doc: split up the --[no-]merged documentation tag doc: move the description of --[no-]merged earlier
2 parents b14f27f + 7505769 commit d1d3d46

14 files changed

+440
-72
lines changed

Documentation/git-branch.txt

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ SYNOPSIS
1010
[verse]
1111
'git branch' [--color[=<when>] | --no-color] [-r | -a]
1212
[--list] [-v [--abbrev=<length> | --no-abbrev]]
13-
[--column[=<options>] | --no-column]
14-
[(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]
13+
[--column[=<options>] | --no-column] [--sort=<key>]
14+
[(--merged | --no-merged) [<commit>]]
15+
[--contains [<commit]] [--no-contains [<commit>]]
1516
[--points-at <object>] [--format=<format>] [<pattern>...]
1617
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
1718
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
@@ -35,11 +36,12 @@ as branch creation.
3536

3637
With `--contains`, shows only the branches that contain the named commit
3738
(in other words, the branches whose tip commits are descendants of the
38-
named commit). With `--merged`, only branches merged into the named
39-
commit (i.e. the branches whose tip commits are reachable from the named
40-
commit) will be listed. With `--no-merged` only branches not merged into
41-
the named commit will be listed. If the <commit> argument is missing it
42-
defaults to `HEAD` (i.e. the tip of the current branch).
39+
named commit), `--no-contains` inverts it. With `--merged`, only branches
40+
merged into the named commit (i.e. the branches whose tip commits are
41+
reachable from the named commit) will be listed. With `--no-merged` only
42+
branches not merged into the named commit will be listed. If the <commit>
43+
argument is missing it defaults to `HEAD` (i.e. the tip of the current
44+
branch).
4345

4446
The command's second form creates a new branch head named <branchname>
4547
which points to the current `HEAD`, or <start-point> if given.
@@ -218,13 +220,19 @@ start-point is either a local or remote-tracking branch.
218220
Only list branches which contain the specified commit (HEAD
219221
if not specified). Implies `--list`.
220222

223+
--no-contains [<commit>]::
224+
Only list branches which don't contain the specified commit
225+
(HEAD if not specified). Implies `--list`.
226+
221227
--merged [<commit>]::
222228
Only list branches whose tips are reachable from the
223-
specified commit (HEAD if not specified). Implies `--list`.
229+
specified commit (HEAD if not specified). Implies `--list`,
230+
incompatible with `--no-merged`.
224231

225232
--no-merged [<commit>]::
226233
Only list branches whose tips are not reachable from the
227-
specified commit (HEAD if not specified). Implies `--list`.
234+
specified commit (HEAD if not specified). Implies `--list`,
235+
incompatible with `--merged`.
228236

229237
<branchname>::
230238
The name of the branch to create or delete.
@@ -301,13 +309,16 @@ If you are creating a branch that you want to checkout immediately, it is
301309
easier to use the git checkout command with its `-b` option to create
302310
a branch and check it out with a single command.
303311

304-
The options `--contains`, `--merged` and `--no-merged` serve three related
305-
but different purposes:
312+
The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
313+
serve four related but different purposes:
306314

307315
- `--contains <commit>` is used to find all branches which will need
308316
special attention if <commit> were to be rebased or amended, since those
309317
branches contain the specified <commit>.
310318

319+
- `--no-contains <commit>` is the inverse of that, i.e. branches that don't
320+
contain the specified <commit>.
321+
311322
- `--merged` is used to find all branches which can be safely deleted,
312323
since those branches are fully contained by HEAD.
313324

Documentation/git-for-each-ref.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ SYNOPSIS
1111
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
1212
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
1313
[--points-at <object>] [(--merged | --no-merged) [<object>]]
14-
[--contains [<object>]]
14+
[--contains [<object>]] [--no-contains [<object>]]
1515

1616
DESCRIPTION
1717
-----------
@@ -69,16 +69,22 @@ OPTIONS
6969

7070
--merged [<object>]::
7171
Only list refs whose tips are reachable from the
72-
specified commit (HEAD if not specified).
72+
specified commit (HEAD if not specified),
73+
incompatible with `--no-merged`.
7374

7475
--no-merged [<object>]::
7576
Only list refs whose tips are not reachable from the
76-
specified commit (HEAD if not specified).
77+
specified commit (HEAD if not specified),
78+
incompatible with `--merged`.
7779

7880
--contains [<object>]::
7981
Only list refs which contain the specified commit (HEAD if not
8082
specified).
8183

84+
--no-contains [<object>]::
85+
Only list refs which don't contain the specified commit (HEAD
86+
if not specified).
87+
8288
--ignore-case::
8389
Sorting and filtering refs are case insensitive.
8490

Documentation/git-tag.txt

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ SYNOPSIS
1212
'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>]
1313
<tagname> [<commit> | <object>]
1414
'git tag' -d <tagname>...
15-
'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
16-
[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
17-
[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]
15+
'git tag' [-n[<num>]] -l [--contains <commit>] [--contains <commit>]
16+
[--points-at <object>] [--column[=<options>] | --no-column]
17+
[--create-reflog] [--sort=<key>] [--format=<format>]
18+
[--[no-]merged [<commit>]] [<pattern>...]
1819
'git tag' -v [--format=<format>] <tagname>...
1920

2021
DESCRIPTION
@@ -82,18 +83,24 @@ OPTIONS
8283

8384
-n<num>::
8485
<num> specifies how many lines from the annotation, if any,
85-
are printed when using -l.
86-
The default is not to print any annotation lines.
87-
If no number is given to `-n`, only the first line is printed.
88-
If the tag is not annotated, the commit message is displayed instead.
89-
90-
-l <pattern>::
91-
--list <pattern>::
92-
List tags with names that match the given pattern (or all if no
93-
pattern is given). Running "git tag" without arguments also
94-
lists all tags. The pattern is a shell wildcard (i.e., matched
95-
using fnmatch(3)). Multiple patterns may be given; if any of
96-
them matches, the tag is shown.
86+
are printed when using -l. Implies `--list`.
87+
+
88+
The default is not to print any annotation lines.
89+
If no number is given to `-n`, only the first line is printed.
90+
If the tag is not annotated, the commit message is displayed instead.
91+
92+
-l::
93+
--list::
94+
List tags. With optional `<pattern>...`, e.g. `git tag --list
95+
'v-*'`, list only the tags that match the pattern(s).
96+
+
97+
Running "git tag" without arguments also lists all tags. The pattern
98+
is a shell wildcard (i.e., matched using fnmatch(3)). Multiple
99+
patterns may be given; if any of them matches, the tag is shown.
100+
+
101+
This option is implicitly supplied if any other list-like option such
102+
as `--contains` is provided. See the documentation for each of those
103+
options for details.
97104

98105
--sort=<key>::
99106
Sort based on the key given. Prefix `-` to sort in
@@ -122,10 +129,23 @@ This option is only applicable when listing tags without annotation lines.
122129

123130
--contains [<commit>]::
124131
Only list tags which contain the specified commit (HEAD if not
125-
specified).
132+
specified). Implies `--list`.
133+
134+
--no-contains [<commit>]::
135+
Only list tags which don't contain the specified commit (HEAD if
136+
not specified). Implies `--list`.
137+
138+
--merged [<commit>]::
139+
Only list tags whose commits are reachable from the specified
140+
commit (`HEAD` if not specified), incompatible with `--no-merged`.
141+
142+
--no-merged [<commit>]::
143+
Only list tags whose commits are not reachable from the specified
144+
commit (`HEAD` if not specified), incompatible with `--merged`.
126145

127146
--points-at <object>::
128-
Only list tags of the given object.
147+
Only list tags of the given object (HEAD if not
148+
specified). Implies `--list`.
129149

130150
-m <msg>::
131151
--message=<msg>::
@@ -173,11 +193,6 @@ This option is only applicable when listing tags without annotation lines.
173193
that of linkgit:git-for-each-ref[1]. When unspecified,
174194
defaults to `%(refname:strip=2)`.
175195

176-
--[no-]merged [<commit>]::
177-
Only list tags whose tips are reachable, or not reachable
178-
if `--no-merged` is used, from the specified commit (`HEAD`
179-
if not specified).
180-
181196
CONFIGURATION
182197
-------------
183198
By default, 'git tag' in sign-with-default mode (-s) will use your

builtin/branch.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
562562
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
563563
FILTER_REFS_REMOTES),
564564
OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
565+
OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
565566
OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
567+
OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")),
566568
OPT__ABBREV(&filter.abbrev),
567569

568570
OPT_GROUP(N_("Specific git-branch actions:")),
@@ -618,7 +620,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
618620
if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
619621
list = 1;
620622

621-
if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr)
623+
if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
624+
filter.no_commit)
622625
list = 1;
623626

624627
if (!!delete + !!rename + !!new_upstream +

builtin/for-each-ref.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
static char const * const for_each_ref_usage[] = {
99
N_("git for-each-ref [<options>] [<pattern>]"),
1010
N_("git for-each-ref [--points-at <object>]"),
11-
N_("git for-each-ref [(--merged | --no-merged) [<object>]]"),
12-
N_("git for-each-ref [--contains [<object>]]"),
11+
N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"),
12+
N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
1313
NULL
1414
};
1515

@@ -43,6 +43,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
4343
OPT_MERGED(&filter, N_("print only refs that are merged")),
4444
OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
4545
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
46+
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
4647
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
4748
OPT_END(),
4849
};

builtin/tag.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
static const char * const git_tag_usage[] = {
2323
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
2424
N_("git tag -d <tagname>..."),
25-
N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]"
25+
N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]"
2626
"\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
2727
N_("git tag -v [--format=<format>] <tagname>..."),
2828
NULL
@@ -424,14 +424,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
424424
OPT_GROUP(N_("Tag listing options")),
425425
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
426426
OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
427+
OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")),
427428
OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
429+
OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
428430
OPT_MERGED(&filter, N_("print only tags that are merged")),
429431
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
430432
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
431433
N_("field name to sort on"), &parse_opt_ref_sorting),
432434
{
433435
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
434-
N_("print only tags of the object"), 0, parse_opt_object_name
436+
N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
437+
parse_opt_object_name, (intptr_t) "HEAD"
435438
},
436439
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
437440
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
@@ -454,8 +457,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
454457
}
455458
create_tag_object = (opt.sign || annotate || msg.given || msgfile);
456459

457-
if (argc == 0 && !cmdmode)
458-
cmdmode = 'l';
460+
if (!cmdmode) {
461+
if (argc == 0)
462+
cmdmode = 'l';
463+
else if (filter.with_commit || filter.no_commit ||
464+
filter.points_at.nr || filter.merge_commit ||
465+
filter.lines != -1)
466+
cmdmode = 'l';
467+
}
459468

460469
if ((create_tag_object || force) && (cmdmode != 0))
461470
usage_with_options(git_tag_usage, options);
@@ -485,13 +494,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
485494
return ret;
486495
}
487496
if (filter.lines != -1)
488-
die(_("-n option is only allowed with -l."));
497+
die(_("-n option is only allowed in list mode"));
489498
if (filter.with_commit)
490-
die(_("--contains option is only allowed with -l."));
499+
die(_("--contains option is only allowed in list mode"));
500+
if (filter.no_commit)
501+
die(_("--no-contains option is only allowed in list mode"));
491502
if (filter.points_at.nr)
492-
die(_("--points-at option is only allowed with -l."));
503+
die(_("--points-at option is only allowed in list mode"));
493504
if (filter.merge_commit)
494-
die(_("--merged and --no-merged option are only allowed with -l"));
505+
die(_("--merged and --no-merged options are only allowed in list mode"));
495506
if (cmdmode == 'd')
496507
return for_each_tag_name(argv, delete_tag, NULL);
497508
if (cmdmode == 'v') {

contrib/completion/git-completion.bash

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,7 +1194,7 @@ _git_branch ()
11941194
--*)
11951195
__gitcomp "
11961196
--color --no-color --verbose --abbrev= --no-abbrev
1197-
--track --no-track --contains --merged --no-merged
1197+
--track --no-track --contains --no-contains --merged --no-merged
11981198
--set-upstream-to= --edit-description --list
11991199
--unset-upstream --delete --move --remotes
12001200
--column --no-column --sort= --points-at
@@ -3023,7 +3023,7 @@ _git_tag ()
30233023
__gitcomp "
30243024
--list --delete --verify --annotate --message --file
30253025
--sign --cleanup --local-user --force --column --sort=
3026-
--contains --points-at --merged --no-merged --create-reflog
3026+
--contains --no-contains --points-at --merged --no-merged --create-reflog
30273027
"
30283028
;;
30293029
esac

parse-options.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,9 @@ extern int parse_opt_passthru_argv(const struct option *, const char *, int);
258258
PARSE_OPT_LASTARG_DEFAULT | flag, \
259259
parse_opt_commits, (intptr_t) "HEAD" \
260260
}
261-
#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, 0)
262-
#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN)
261+
#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG)
262+
#define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
263+
#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
264+
#define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
263265

264266
#endif

0 commit comments

Comments
 (0)