From 63d564b300274ec71a274f9b672366d07ae5620a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 20 Nov 2009 02:00:40 -0800 Subject: [PATCH 1/5] read_revision_from_stdin(): use strbuf It is so 2005 (and Linus ;-) to have a fixed 1000-byte buffer that reads from the user. Let's use strbuf to unlimit the input length. Signed-off-by: Junio C Hamano --- revision.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/revision.c b/revision.c index 9fc4e8d3818..d56387fe65e 100644 --- a/revision.c +++ b/revision.c @@ -955,19 +955,21 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, void read_revisions_from_stdin(struct rev_info *revs) { - char line[1000]; + struct strbuf sb; - while (fgets(line, sizeof(line), stdin) != NULL) { - int len = strlen(line); - if (len && line[len - 1] == '\n') - line[--len] = '\0'; + strbuf_init(&sb, 1000); + while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) { + int len = sb.len; + if (len && sb.buf[len - 1] == '\n') + sb.buf[--len] = '\0'; if (!len) break; - if (line[0] == '-') + if (sb.buf[0] == '-') die("options not supported in --stdin mode"); - if (handle_revision_arg(line, revs, 0, 1)) - die("bad revision '%s'", line); + if (handle_revision_arg(sb.buf, revs, 0, 1)) + die("bad revision '%s'", sb.buf); } + strbuf_release(&sb); } static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what) From 8b3dce565084c89ceb19f7ccf0fe22ffd365f7fd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 3 Nov 2009 06:59:18 -0800 Subject: [PATCH 2/5] Teach --stdin option to "log" family Move the logic to read revs from standard input that rev-list knows about from it to revision machinery, so that all the users of setup_revisions() can feed the list of revs from the standard input when "--stdin" is used on the command line. Allow some users of the revision machinery that want different semantics from the "--stdin" option to disable it by setting an option in the rev_info structure. This also cleans up the kludge made to bundle.c via cut and paste. Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 2 +- builtin-blame.c | 1 + builtin-diff-tree.c | 1 + builtin-rev-list.c | 7 ------- bundle.c | 12 ++---------- revision.c | 15 +++++++++++++-- revision.h | 4 ++-- 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index bf66116d61a..88ad405111d 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -243,12 +243,12 @@ endif::git-rev-list[] Pretend as if all the refs in `$GIT_DIR/refs/remotes` are listed on the command line as ''. -ifdef::git-rev-list[] --stdin:: In addition to the '' listed on the command line, read them from the standard input. +ifdef::git-rev-list[] --quiet:: Don't print anything to standard output. This form diff --git a/builtin-blame.c b/builtin-blame.c index 7512773b401..b0aa5305079 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2352,6 +2352,7 @@ parse_done: die_errno("cannot stat path '%s'", path); } + revs.disable_stdin = 1; setup_revisions(argc, argv, &revs, NULL); memset(&sb, 0, sizeof(sb)); diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 79cedb72c44..2380c21951f 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -104,6 +104,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ opt->abbrev = 0; opt->diff = 1; + opt->disable_stdin = 1; argc = setup_revisions(argc, argv, opt, NULL); while (--argc > 0) { diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 42cc8d8872a..f6a56f34e18 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -306,7 +306,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) struct rev_info revs; struct rev_list_info info; int i; - int read_from_stdin = 0; int bisect_list = 0; int bisect_show_vars = 0; int bisect_find_all = 0; @@ -349,12 +348,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) bisect_show_vars = 1; continue; } - if (!strcmp(arg, "--stdin")) { - if (read_from_stdin++) - die("--stdin given twice?"); - read_revisions_from_stdin(&revs); - continue; - } usage(rev_list_usage); } diff --git a/bundle.c b/bundle.c index df95e151e2e..e04ab497111 100644 --- a/bundle.c +++ b/bundle.c @@ -204,7 +204,6 @@ int create_bundle(struct bundle_header *header, const char *path, int i, ref_count = 0; char buffer[1024]; struct rev_info revs; - int read_from_stdin = 0; struct child_process rls; FILE *rls_fout; @@ -256,15 +255,8 @@ int create_bundle(struct bundle_header *header, const char *path, /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "--stdin")) { - if (read_from_stdin++) - die("--stdin given twice?"); - read_revisions_from_stdin(&revs); - continue; - } - return error("unrecognized argument: %s'", argv[i]); - } + if (argc > 1) + return error("unrecognized argument: %s'", argv[1]); object_array_remove_duplicates(&revs.pending); diff --git a/revision.c b/revision.c index d56387fe65e..f5b735fc141 100644 --- a/revision.c +++ b/revision.c @@ -953,7 +953,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, return 0; } -void read_revisions_from_stdin(struct rev_info *revs) +static void read_revisions_from_stdin(struct rev_info *revs) { struct strbuf sb; @@ -1229,7 +1229,7 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, */ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def) { - int i, flags, left, seen_dashdash; + int i, flags, left, seen_dashdash, read_from_stdin; /* First, search for "--" */ seen_dashdash = 0; @@ -1247,6 +1247,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch /* Second, deal with arguments and options */ flags = 0; + read_from_stdin = 0; for (left = i = 1; i < argc; i++) { const char *arg = argv[i]; if (*arg == '-') { @@ -1285,6 +1286,16 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->no_walk = 0; continue; } + if (!strcmp(arg, "--stdin")) { + if (revs->disable_stdin) { + argv[left++] = arg; + continue; + } + if (read_from_stdin++) + die("--stdin given twice?"); + read_revisions_from_stdin(revs); + continue; + } opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv); if (opts > 0) { diff --git a/revision.h b/revision.h index b6421a64321..f64637bcbdc 100644 --- a/revision.h +++ b/revision.h @@ -83,6 +83,8 @@ struct rev_info { use_terminator:1, missing_newline:1, date_mode_explicit:1; + unsigned int disable_stdin:1; + enum date_mode date_mode; unsigned int abbrev; @@ -128,8 +130,6 @@ struct rev_info { #define REV_TREE_DIFFERENT 3 /* Mixed changes */ /* revision.c */ -void read_revisions_from_stdin(struct rev_info *revs); - typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *); extern volatile show_early_output_fn_t show_early_output; From 5486ef0e6d159a8971742fd2464b9656f5457fda Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 20 Nov 2009 02:33:28 -0800 Subject: [PATCH 3/5] setup_revisions(): do not call get_pathspec() too early This is necessary because we will later allow pathspecs to be fed from the standard input, and pathspecs taken from the command line (and converted via get_pathspec() already) in revs->prune_data too early gets in the way when we want to append from the standard input. Signed-off-by: Junio C Hamano --- revision.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/revision.c b/revision.c index f5b735fc141..4410a45a023 100644 --- a/revision.c +++ b/revision.c @@ -1230,6 +1230,7 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def) { int i, flags, left, seen_dashdash, read_from_stdin; + const char **prune_data = NULL; /* First, search for "--" */ seen_dashdash = 0; @@ -1240,7 +1241,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch argv[i] = NULL; argc = i; if (argv[i + 1]) - revs->prune_data = get_pathspec(revs->prefix, argv + i + 1); + prune_data = argv + i + 1; seen_dashdash = 1; break; } @@ -1321,12 +1322,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch for (j = i; j < argc; j++) verify_filename(revs->prefix, argv[j]); - revs->prune_data = get_pathspec(revs->prefix, - argv + i); + prune_data = argv + i; break; } } + if (prune_data) + revs->prune_data = get_pathspec(revs->prefix, prune_data); + if (revs->def == NULL) revs->def = def; if (revs->show_merge) From 60da8b15c1b77706e0701cccef2d534a1c3825ad Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 20 Nov 2009 02:50:21 -0800 Subject: [PATCH 4/5] Make --stdin option to "log" family read also pathspecs Similar to the command line arguments, after giving zero or more revs, you can feed a line "--" and then feed pathspecs one at a time. With this ( echo ^maint echo -- echo Documentation ) | git log --stat --oneline --stdin master -- t lists commits that touch Documentation/ or t/ between maint and master. Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 4 +- revision.c | 72 ++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 88ad405111d..b44fdd9f015 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -246,7 +246,9 @@ endif::git-rev-list[] --stdin:: In addition to the '' listed on the command - line, read them from the standard input. + line, read them from the standard input. If a '--' separator is + seen, stop reading commits and start reading paths to limit the + result. ifdef::git-rev-list[] --quiet:: diff --git a/revision.c b/revision.c index 4410a45a023..8750c20e07d 100644 --- a/revision.c +++ b/revision.c @@ -953,9 +953,38 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, return 0; } -static void read_revisions_from_stdin(struct rev_info *revs) +static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb, const char ***prune_data) +{ + const char **prune = *prune_data; + int prune_nr; + int prune_alloc; + + /* count existing ones */ + if (!prune) + prune_nr = 0; + else + for (prune_nr = 0; prune[prune_nr]; prune_nr++) + ; + prune_alloc = prune_nr; /* not really, but we do not know */ + + while (strbuf_getwholeline(sb, stdin, '\n') != EOF) { + int len = sb->len; + if (len && sb->buf[len - 1] == '\n') + sb->buf[--len] = '\0'; + ALLOC_GROW(prune, prune_nr+1, prune_alloc); + prune[prune_nr++] = xstrdup(sb->buf); + } + if (prune) { + ALLOC_GROW(prune, prune_nr+1, prune_alloc); + prune[prune_nr] = NULL; + } + *prune_data = prune; +} + +static void read_revisions_from_stdin(struct rev_info *revs, const char ***prune) { struct strbuf sb; + int seen_dashdash = 0; strbuf_init(&sb, 1000); while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) { @@ -964,11 +993,18 @@ static void read_revisions_from_stdin(struct rev_info *revs) sb.buf[--len] = '\0'; if (!len) break; - if (sb.buf[0] == '-') + if (sb.buf[0] == '-') { + if (len == 2 && sb.buf[1] == '-') { + seen_dashdash = 1; + break; + } die("options not supported in --stdin mode"); + } if (handle_revision_arg(sb.buf, revs, 0, 1)) die("bad revision '%s'", sb.buf); } + if (seen_dashdash) + read_pathspec_from_stdin(revs, &sb, prune); strbuf_release(&sb); } @@ -1220,6 +1256,34 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, ctx->argc -= n; } +static void append_prune_data(const char ***prune_data, const char **av) +{ + const char **prune = *prune_data; + int prune_nr; + int prune_alloc; + + if (!prune) { + *prune_data = av; + return; + } + + /* count existing ones */ + for (prune_nr = 0; prune[prune_nr]; prune_nr++) + ; + prune_alloc = prune_nr; /* not really, but we do not know */ + + while (*av) { + ALLOC_GROW(prune, prune_nr+1, prune_alloc); + prune[prune_nr++] = *av; + av++; + } + if (prune) { + ALLOC_GROW(prune, prune_nr+1, prune_alloc); + prune[prune_nr] = NULL; + } + *prune_data = prune; +} + /* * Parse revision information, filling in the "rev_info" structure, * and removing the used arguments from the argument list. @@ -1294,7 +1358,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (read_from_stdin++) die("--stdin given twice?"); - read_revisions_from_stdin(revs); + read_revisions_from_stdin(revs, &prune_data); continue; } @@ -1322,7 +1386,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch for (j = i; j < argc; j++) verify_filename(revs->prefix, argv[j]); - prune_data = argv + i; + append_prune_data(&prune_data, argv + i); break; } } From d21fc9342cd82bb48bfbf9f54024ae2d4e16a9a3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 23 Nov 2009 00:49:47 -0800 Subject: [PATCH 5/5] Add trivial tests for --stdin option to log family Signed-off-by: Junio C Hamano --- t/t6017-rev-list-stdin.sh | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100755 t/t6017-rev-list-stdin.sh diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh new file mode 100755 index 00000000000..f1c32dba423 --- /dev/null +++ b/t/t6017-rev-list-stdin.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# +# Copyright (c) 2009, Junio C Hamano +# + +test_description='log family learns --stdin' + +. ./test-lib.sh + +check () { + for cmd in rev-list "log --stat" + do + for i in "$@" + do + printf "%s\n" $i + done >input && + test_expect_success "check $cmd $*" ' + git $cmd $(cat input) >expect && + git $cmd --stdin actual && + sed -e "s/^/input /" input && + sed -e "s/^/output /" expect && + test_cmp expect actual + ' + done +} + +them='1 2 3 4 5 6 7' + +test_expect_success setup ' + ( + for i in 0 $them + do + for j in $them + do + echo $i.$j >file-$j && + git add file-$j || exit + done && + test_tick && + git commit -m $i || exit + done && + for i in $them + do + git checkout -b side-$i master~$i && + echo updated $i >file-$i && + git add file-$i && + test_tick && + git commit -m side-$i || exit + done + ) +' + +check master +check side-1 ^side-4 +check side-1 ^side-7 -- +check side-1 ^side-7 -- file-1 +check side-1 ^side-7 -- file-2 +check side-3 ^side-4 -- file-3 +check side-3 ^side-2 +check side-3 ^side-2 -- file-1 + +test_done