Skip to content

Commit

Permalink
log: add option to search for header or body
Browse files Browse the repository at this point in the history
Summary:
This change adds a new option to `git log` that allows users to search
for commits that match either the author or the commit message. This is
useful for finding commits that were either authored or co-authored by a
specific person.

Currently, the best way to find a commit either authored or co-authored
by a specific person is to use

```sh
$ echo \
    $(git log --author=Torvalds --pretty="%cd,%H\n" --date=iso-strict) \
    $(git log --grep="Co-authored-by: .*Torvalds" --pretty="%cd,%H\n" --date=iso-strict) \
| sort -n --reverse \
| awk -F, '{print $2}' \
| tr '\n' '\t' \
| xargs git show --stat --stdin
```

This is a bit of a pain, so this change adds a new option to `git log`.
Now finding either authors or co-authors is as simple as

```sh
$ git log --author=Torvalds --grep=Torvalds --match-header-or-grep
```

Test plan:
1. create commit authored by A and co-authored-by B
2. create commit authored by B
3. run
```sh
$ git log --author=B --grep="Co-authored-by: B" --match-header-or-grep
```
4. expect to see both commits

Signed-off-by: Max 👨🏽‍💻 Coplan <[email protected]>
  • Loading branch information
vegerot committed Apr 7, 2024
1 parent 7774cfe commit c7f14e5
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 4 deletions.
8 changes: 8 additions & 0 deletions Documentation/rev-list-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ endif::git-rev-list[]
Limit the commits output to ones that match all given `--grep`,
instead of ones that match at least one.

--match-header-or-grep::
Limit the commits output to ones that match either header patterns
(`--author`, `--committer`, or `--grep-reflog`) or `--grep`, instead
of ones that match both the header and grep patterns
+
For example, `--author=me --grep=Co-authored-by: me` limits to commits either
authored or co-authored by me.

--invert-grep::
Limit the commits output to ones with a log message that do not
match the pattern specified with `--grep=<pattern>`.
Expand Down
2 changes: 1 addition & 1 deletion contrib/completion/git-completion.bash
Original file line number Diff line number Diff line change
Expand Up @@ -2170,7 +2170,7 @@ __git_log_gitk_options="
# Options that go well for log and shortlog (not gitk)
__git_log_shortlog_options="
--author= --committer= --grep=
--all-match --invert-grep
--all-match --invert-grep --match-header-or-grep
"
# Options accepted by log and show
__git_log_show_options="
Expand Down
4 changes: 2 additions & 2 deletions grep.c
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ void compile_grep_patterns(struct grep_opt *opt)

if (!opt->pattern_expression)
opt->pattern_expression = header_expr;
else if (opt->all_match)
else if (opt->all_match || opt->match_header_or_grep)
opt->pattern_expression = grep_splice_or(header_expr,
opt->pattern_expression);
else
Expand Down Expand Up @@ -1829,7 +1829,7 @@ int grep_source(struct grep_opt *opt, struct grep_source *gs)
opt->body_hit = 0;
grep_source_1(opt, gs, 1);

if (opt->all_match && !chk_hit_marker(opt->pattern_expression))
if (!opt->match_header_or_grep && opt->all_match && !chk_hit_marker(opt->pattern_expression))
return 0;
if (opt->no_body_match && opt->body_hit)
return 0;
Expand Down
1 change: 1 addition & 0 deletions grep.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ struct grep_opt {
int count;
int word_regexp;
int all_match;
int match_header_or_grep;
int no_body_match;
int body_hit;
#define GREP_BINARY_DEFAULT 0
Expand Down
2 changes: 2 additions & 0 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -2646,6 +2646,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE;
} else if (!strcmp(arg, "--all-match")) {
revs->grep_filter.all_match = 1;
} else if (!strcmp(arg, "--match-header-or-grep")) {
revs->grep_filter.match_header_or_grep = 1;
} else if (!strcmp(arg, "--invert-grep")) {
revs->grep_filter.no_body_match = 1;
} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
Expand Down
26 changes: 25 additions & 1 deletion t/t7810-grep.sh
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,14 @@ test_expect_success 'log --grep --author uses intersection' '
test_cmp expect actual
'

test_expect_success 'log --grep --author --match-header-or-grep uses union' '
# grep matches only third and fourth
# author matches only initial and third
git log --author="A U Thor" --grep=r --match-header-or-grep --format=%s >actual &&
test_write_lines fourth third initial >expect &&
test_cmp expect actual
'

test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' '
# grep matches initial and second but not third
# author matches only initial and third
Expand All @@ -971,7 +979,23 @@ test_expect_success 'log --grep --grep --author takes union of greps and interse
test_cmp expect actual
'

test_expect_success 'log ---all-match -grep --author --author still takes union of authors and intersects with grep' '
test_expect_success 'log --author --grep --grep --match-header-or-grep takes union of greps and author' '
# grep matches initial and second but not third
# author matches only initial and third
git log --author="A U Thor" --grep=second --grep=initial --match-header-or-grep --format=%s >actual &&
test_write_lines third second initial >expect &&
test_cmp expect actual
'

test_expect_success 'log --author --grep --grep --all-match --match-header-or-grep still takes union of greps and author' '
# grep matches initial and second but not third
# author matches only initial and third
git log --author="A U Thor" --grep=second --grep=initial --all-match --match-header-or-grep --format=%s >actual &&
test_write_lines third second initial >expect &&
test_cmp expect actual
'

test_expect_success 'log --all-match --grep --author --author still takes union of authors and intersects with grep' '
# grep matches only initial and third
# author matches all but second
git log --all-match --author="Thor" --author="Night" --grep=i --format=%s >actual &&
Expand Down

0 comments on commit c7f14e5

Please sign in to comment.