From ff2549dc9af3fffa8a8285418601d9eab94de7b7 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 26 Aug 2008 19:11:44 +0200 Subject: [PATCH 1/3] bash completion: Hide more plumbing commands git still shows way too many commands, some of them are clearly plumbing. This patch hides the plumbing commands liberally (that is, in special cases, users still might want to call one of the hidden commands, a *normal* workflow should never involve these, though - and if it does, we have a UI problem anyway). Signed-off-by: Petr Baudis Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 89858c237e..1154ae1ac8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -386,7 +386,9 @@ __git_porcelain_commands () cat-file) : plumbing;; check-attr) : plumbing;; check-ref-format) : plumbing;; + checkout-index) : plumbing;; commit-tree) : plumbing;; + count-objects) : infrequent;; cvsexportcommit) : export;; cvsimport) : import;; cvsserver) : daemon;; @@ -395,6 +397,7 @@ __git_porcelain_commands () diff-index) : plumbing;; diff-tree) : plumbing;; fast-import) : import;; + fast-export) : export;; fsck-objects) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; @@ -404,6 +407,10 @@ __git_porcelain_commands () index-pack) : plumbing;; init-db) : deprecated;; local-fetch) : plumbing;; + lost-found) : infrequent;; + ls-files) : plumbing;; + ls-remote) : plumbing;; + ls-tree) : plumbing;; mailinfo) : plumbing;; mailsplit) : plumbing;; merge-*) : plumbing;; @@ -428,6 +435,7 @@ __git_porcelain_commands () runstatus) : plumbing;; sh-setup) : internal;; shell) : daemon;; + show-ref) : plumbing;; send-pack) : plumbing;; show-index) : plumbing;; ssh-*) : transport;; @@ -442,6 +450,8 @@ __git_porcelain_commands () upload-archive) : plumbing;; upload-pack) : plumbing;; write-tree) : plumbing;; + var) : infrequent;; + verify-pack) : infrequent;; verify-tag) : plumbing;; *) echo $i;; esac From f88d225feb117f0429540da90b2552e198fa2e82 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Sep 2008 17:28:59 -0700 Subject: [PATCH 2/3] diff --cumulative is a sub-option of --dirstat The option used to be implemented as if it is a totally independent one, but "git diff --cumulative" would not mean anything without "--dirstat". This makes --cumulative imply --dirstat. Signed-off-by: Junio C Hamano --- diff.c | 9 ++++++--- diff.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/diff.c b/diff.c index 7b4300a74a..b3a7da70a5 100644 --- a/diff.c +++ b/diff.c @@ -1072,7 +1072,7 @@ static void show_dirstat(struct diff_options *options) dir.alloc = 0; dir.nr = 0; dir.percent = options->dirstat_percent; - dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; + dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE); changed = 0; for (i = 0; i < q->nr; i++) { @@ -2298,6 +2298,7 @@ void diff_setup(struct diff_options *options) options->break_opt = -1; options->rename_limit = -1; options->dirstat_percent = 3; + DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE); options->context = 3; options->change = diff_change; @@ -2470,8 +2471,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_SHORTSTAT; else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent)) options->output_format |= DIFF_FORMAT_DIRSTAT; - else if (!strcmp(arg, "--cumulative")) - options->output_format |= DIFF_FORMAT_CUMULATIVE; + else if (!strcmp(arg, "--cumulative")) { + options->output_format |= DIFF_FORMAT_DIRSTAT; + DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE); + } else if (!strcmp(arg, "--check")) options->output_format |= DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) diff --git a/diff.h b/diff.h index 50fb5ddb0b..7f53bebf33 100644 --- a/diff.h +++ b/diff.h @@ -31,7 +31,6 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_FORMAT_PATCH 0x0010 #define DIFF_FORMAT_SHORTSTAT 0x0020 #define DIFF_FORMAT_DIRSTAT 0x0040 -#define DIFF_FORMAT_CUMULATIVE 0x0080 /* These override all above */ #define DIFF_FORMAT_NAME 0x0100 @@ -64,6 +63,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_OPT_CHECK_FAILED (1 << 16) #define DIFF_OPT_RELATIVE_NAME (1 << 17) #define DIFF_OPT_IGNORE_SUBMODULES (1 << 18) +#define DIFF_OPT_DIRSTAT_CUMULATIVE (1 << 19) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) From a4d7d2c6dbc9e1294034171c31d87b24cf3b492e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 4 Sep 2008 22:15:02 -0700 Subject: [PATCH 3/3] log --author/--committer: really match only with name part When we tried to find commits done by AUTHOR, the first implementation tried to pattern match a line with "^author .*AUTHOR", which later was enhanced to strip leading caret and look for "^author AUTHOR" when the search pattern was anchored at the left end (i.e. --author="^AUTHOR"). This had a few problems: * When looking for fixed strings (e.g. "git log -F --author=x --grep=y"), the regexp internally used "^author .*x" would never match anything; * To match at the end (e.g. "git log --author='google.com>$'"), the generated regexp has to also match the trailing timestamp part the commit header lines have. Also, in order to determine if the '$' at the end means "match at the end of the line" or just a literal dollar sign (probably backslash-quoted), we would need to parse the regexp ourselves. An earlier alternative tried to make sure that a line matches "^author " (to limit by field name) and the user supplied pattern at the same time. While it solved the -F problem by introducing a special override for matching the "^author ", it did not solve the trailing timestamp nor tail match problem. It also would have matched every commit if --author=author was asked for, not because the author's email part had this string, but because every commit header line that talks about the author begins with that field name, regardleses of who wrote it. Instead of piling more hacks on top of hacks, this rethinks the grep machinery that is used to look for strings in the commit header, and makes sure that (1) field name matches literally at the beginning of the line, followed by a SP, and (2) the user supplied pattern is matched against the remainder of the line, excluding the trailing timestamp data. Signed-off-by: Junio C Hamano --- grep.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ grep.h | 7 +++++++ revision.c | 21 ++++---------------- t/t7002-grep.sh | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 17 deletions(-) diff --git a/grep.c b/grep.c index f67d6716ea..706351197f 100644 --- a/grep.c +++ b/grep.c @@ -2,6 +2,19 @@ #include "grep.h" #include "xdiff-interface.h" +void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat) +{ + struct grep_pat *p = xcalloc(1, sizeof(*p)); + p->pattern = pat; + p->origin = "header"; + p->no = 0; + p->token = GREP_PATTERN_HEAD; + p->field = field; + *opt->pattern_tail = p; + opt->pattern_tail = &p->next; + p->next = NULL; +} + void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t) { @@ -247,16 +260,53 @@ static int fixmatch(const char *pattern, char *line, regmatch_t *match) } } +static int strip_timestamp(char *bol, char **eol_p) +{ + char *eol = *eol_p; + int ch; + + while (bol < --eol) { + if (*eol != '>') + continue; + *eol_p = ++eol; + ch = *eol; + *eol = '\0'; + return ch; + } + return 0; +} + +static struct { + const char *field; + size_t len; +} header_field[] = { + { "author ", 7 }, + { "committer ", 10 }, +}; + static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx) { int hit = 0; int at_true_bol = 1; + int saved_ch = 0; regmatch_t pmatch[10]; if ((p->token != GREP_PATTERN) && ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD))) return 0; + if (p->token == GREP_PATTERN_HEAD) { + const char *field; + size_t len; + assert(p->field < ARRAY_SIZE(header_field)); + field = header_field[p->field].field; + len = header_field[p->field].len; + if (strncmp(bol, field, len)) + return 0; + bol += len; + saved_ch = strip_timestamp(bol, &eol); + } + again: if (!opt->fixed) { regex_t *exp = &p->regexp; @@ -298,6 +348,8 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol goto again; } } + if (p->token == GREP_PATTERN_HEAD && saved_ch) + *eol = saved_ch; return hit; } diff --git a/grep.h b/grep.h index d252dd25f8..59b3f871ea 100644 --- a/grep.h +++ b/grep.h @@ -17,12 +17,18 @@ enum grep_context { GREP_CONTEXT_BODY, }; +enum grep_header_field { + GREP_HEADER_AUTHOR = 0, + GREP_HEADER_COMMITTER, +}; + struct grep_pat { struct grep_pat *next; const char *origin; int no; enum grep_pat_token token; const char *pattern; + enum grep_header_field field; regex_t regexp; }; @@ -74,6 +80,7 @@ struct grep_opt { }; extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t); +extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *); extern void compile_grep_patterns(struct grep_opt *opt); extern void free_grep_patterns(struct grep_opt *opt); extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size); diff --git a/revision.c b/revision.c index 36291b6b86..270294af83 100644 --- a/revision.c +++ b/revision.c @@ -953,22 +953,9 @@ static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token append_grep_pattern(&revs->grep_filter, ptn, "command line", 0, what); } -static void add_header_grep(struct rev_info *revs, const char *field, const char *pattern) +static void add_header_grep(struct rev_info *revs, enum grep_header_field field, const char *pattern) { - char *pat; - const char *prefix; - int patlen, fldlen; - - fldlen = strlen(field); - patlen = strlen(pattern); - pat = xmalloc(patlen + fldlen + 10); - prefix = ".*"; - if (*pattern == '^') { - prefix = ""; - pattern++; - } - sprintf(pat, "^%s %s%s", field, prefix, pattern); - add_grep(revs, pat, GREP_PATTERN_HEAD); + append_header_grep_pattern(&revs->grep_filter, field, pattern); } static void add_message_grep(struct rev_info *revs, const char *pattern) @@ -1154,9 +1141,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg * Grepping the commit log */ else if (!prefixcmp(arg, "--author=")) { - add_header_grep(revs, "author", arg+9); + add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9); } else if (!prefixcmp(arg, "--committer=")) { - add_header_grep(revs, "committer", arg+12); + add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12); } else if (!prefixcmp(arg, "--grep=")) { add_message_grep(revs, arg+7); } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) { diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh index c8b4f65f38..5e359cb561 100755 --- a/t/t7002-grep.sh +++ b/t/t7002-grep.sh @@ -22,6 +22,7 @@ test_expect_success setup ' mkdir t && echo test >t/t && git add file x y z t/t && + test_tick && git commit -m initial ' @@ -113,4 +114,54 @@ do done +test_expect_success 'log grep setup' ' + echo a >>file && + test_tick && + GIT_AUTHOR_NAME="With * Asterisk" \ + GIT_AUTHOR_EMAIL="xyzzy@frotz.com" \ + git commit -a -m "second" && + + echo a >>file && + test_tick && + git commit -a -m "third" + +' + +test_expect_success 'log grep (1)' ' + git log --author=author --pretty=tformat:%s >actual && + ( echo third ; echo initial ) >expect && + test_cmp expect actual +' + +test_expect_success 'log grep (2)' ' + git log --author=" * " -F --pretty=tformat:%s >actual && + ( echo second ) >expect && + test_cmp expect actual +' + +test_expect_success 'log grep (3)' ' + git log --author="^A U" --pretty=tformat:%s >actual && + ( echo third ; echo initial ) >expect && + test_cmp expect actual +' + +test_expect_success 'log grep (4)' ' + git log --author="frotz\.com>$" --pretty=tformat:%s >actual && + ( echo second ) >expect && + test_cmp expect actual +' + +test_expect_success 'log grep (5)' ' + git log --author=Thor -F --grep=Thu --pretty=tformat:%s >actual && + ( echo third ; echo initial ) >expect && + test_cmp expect actual +' + +test_expect_success 'log grep (6)' ' + git log --author=-0700 --pretty=tformat:%s >actual && + >expect && + test_cmp expect actual + +' + test_done