From: Junio C Hamano Date: Thu, 17 Jul 2008 00:10:28 +0000 (-0700) Subject: Merge branch 'maint' X-Git-Tag: v1.6.0-rc0~44 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/88bbda08d7b9503862a8fb8846d78c67825e5e3d?ds=inline;hp=-c Merge branch 'maint' * maint: Start preparing 1.5.6.4 release notes git fetch-pack: do not complain about "no common commits" in an empty repo rebase-i: keep old parents when preserving merges t7600-merge: Use test_expect_failure to test option parsing Fix buffer overflow in prepare_attr_stack Fix buffer overflow in git diff Fix buffer overflow in git-grep git-cvsserver: fix call to nonexistant cleanupWorkDir() Documentation/git-cherry-pick.txt et al.: Fix misleading -n description Conflicts: RelNotes --- 88bbda08d7b9503862a8fb8846d78c67825e5e3d diff --combined Documentation/git-cherry-pick.txt index a691173ba1,4ef5af4ca9..50fb3d5d54 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@@ -7,7 -7,7 +7,7 @@@ git-cherry-pick - Apply the change intr SYNOPSIS -------- -'git-cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] +'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] DESCRIPTION ----------- @@@ -19,12 -19,12 +19,12 @@@ OPTION ------- :: Commit to cherry-pick. - For a more complete list of ways to spell commits, see + For a more complete list of ways to spell commits, see the "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1]. -e:: --edit:: - With this option, `git-cherry-pick` will let you edit the commit + With this option, 'git-cherry-pick' will let you edit the commit message prior to committing. -x:: @@@ -58,14 -58,14 +58,14 @@@ Usually the command automatically creates a commit with a commit log message stating which commit was cherry-picked. This flag applies the change necessary - to cherry-pick the named commit to your working tree, - but does not make the commit. In addition, when this - option is used, your working tree does not have to match + to cherry-pick the named commit to your working tree + and the index, but does not make the commit. In addition, + when this option is used, your index does not have to match the HEAD commit. The cherry-pick is done against the - beginning state of your working tree. + beginning state of your index. + This is useful when cherry-picking more than one commits' - effect to your working tree in a row. + effect to your index in a row. -s:: --signoff:: diff --combined Documentation/git-revert.txt index 5411edca96,f7f4bd4689..271850f511 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@@ -7,7 -7,7 +7,7 @@@ git-revert - Revert an existing commi SYNOPSIS -------- -'git-revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] +'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] DESCRIPTION ----------- @@@ -24,7 -24,7 +24,7 @@@ OPTION -e:: --edit:: - With this option, `git-revert` will let you edit the commit + With this option, 'git-revert' will let you edit the commit message prior to committing the revert. This is the default if you run the command from a terminal. @@@ -37,22 -37,22 +37,22 @@@ relative to the specified parent. --no-edit:: - With this option, `git-revert` will not start the commit + With this option, 'git-revert' will not start the commit message editor. -n:: --no-commit:: Usually the command automatically creates a commit with - a commit log message stating which commit was reverted. - This flag applies the change necessary to revert the - named commit to your working tree, but does not make the - commit. In addition, when this option is used, your - working tree does not have to match the HEAD commit. - The revert is done against the beginning state of your - working tree. + a commit log message stating which commit was + reverted. This flag applies the change necessary + to revert the named commit to your working tree + and the index, but does not make the commit. In addition, + when this option is used, your index does not have to match + the HEAD commit. The revert is done against the + beginning state of your index. + This is useful when reverting more than one commits' - effect to your working tree in a row. + effect to your index in a row. -s:: --signoff:: diff --combined diff.c index 6a39b393f3,386de826d3..a07812c5c7 --- a/diff.c +++ b/diff.c @@@ -531,9 -531,9 +531,9 @@@ static void emit_add_line(const char *r else { /* Emit just the prefix, then the rest. */ emit_line(ecbdata->file, set, reset, line, ecbdata->nparents); - (void)check_and_emit_line(line + ecbdata->nparents, - len - ecbdata->nparents, ecbdata->ws_rule, - ecbdata->file, set, reset, ws); + ws_check_emit(line + ecbdata->nparents, + len - ecbdata->nparents, ecbdata->ws_rule, + ecbdata->file, set, reset, ws); } } @@@ -826,12 -826,12 +826,12 @@@ static void show_stats(struct diffstat_ /* Sanity: give at least 5 columns to the graph, * but leave at least 10 columns for the name. */ - if (width < name_width + 15) { - if (name_width <= 25) - width = name_width + 15; - else - name_width = width - 15; - } + if (width < 25) + width = 25; + if (name_width < 10) + name_width = 10; + else if (width < name_width + 15) + name_width = width - 15; /* Find the longest filename and max number of changes */ reset = diff_get_color_opt(options, DIFF_RESET); @@@ -924,8 -924,7 +924,8 @@@ total = add + del; } show_name(options->file, prefix, name, len, reset, set); - fprintf(options->file, "%5d ", added + deleted); + fprintf(options->file, "%5d%s", added + deleted, + added + deleted ? " " : ""); show_graph(options->file, '+', add, add_c, reset); show_graph(options->file, '-', del, del_c, reset); fprintf(options->file, "\n"); @@@ -1132,85 -1131,42 +1132,85 @@@ static void free_diffstat_info(struct d struct checkdiff_t { struct xdiff_emit_state xm; const char *filename; - int lineno, color_diff; + int lineno; + struct diff_options *o; unsigned ws_rule; unsigned status; - FILE *file; + int trailing_blanks_start; }; +static int is_conflict_marker(const char *line, unsigned long len) +{ + char firstchar; + int cnt; + + if (len < 8) + return 0; + firstchar = line[0]; + switch (firstchar) { + case '=': case '>': case '<': + break; + default: + return 0; + } + for (cnt = 1; cnt < 7; cnt++) + if (line[cnt] != firstchar) + return 0; + /* line[0] thru line[6] are same as firstchar */ + if (firstchar == '=') { + /* divider between ours and theirs? */ + if (len != 8 || line[7] != '\n') + return 0; + } else if (len < 8 || !isspace(line[7])) { + /* not divider before ours nor after theirs */ + return 0; + } + return 1; +} + static void checkdiff_consume(void *priv, char *line, unsigned long len) { struct checkdiff_t *data = priv; - const char *ws = diff_get_color(data->color_diff, DIFF_WHITESPACE); - const char *reset = diff_get_color(data->color_diff, DIFF_RESET); - const char *set = diff_get_color(data->color_diff, DIFF_FILE_NEW); + int color_diff = DIFF_OPT_TST(data->o, COLOR_DIFF); + const char *ws = diff_get_color(color_diff, DIFF_WHITESPACE); + const char *reset = diff_get_color(color_diff, DIFF_RESET); + const char *set = diff_get_color(color_diff, DIFF_FILE_NEW); char *err; if (line[0] == '+') { unsigned bad; data->lineno++; - bad = check_and_emit_line(line + 1, len - 1, - data->ws_rule, NULL, NULL, NULL, NULL); + if (!ws_blank_line(line + 1, len - 1, data->ws_rule)) + data->trailing_blanks_start = 0; + else if (!data->trailing_blanks_start) + data->trailing_blanks_start = data->lineno; + if (is_conflict_marker(line + 1, len - 1)) { + data->status |= 1; + fprintf(data->o->file, + "%s:%d: leftover conflict marker\n", + data->filename, data->lineno); + } + bad = ws_check(line + 1, len - 1, data->ws_rule); if (!bad) return; data->status |= bad; err = whitespace_error_string(bad); - fprintf(data->file, "%s:%d: %s.\n", data->filename, data->lineno, err); + fprintf(data->o->file, "%s:%d: %s.\n", + data->filename, data->lineno, err); free(err); - emit_line(data->file, set, reset, line, 1); - (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, - data->file, set, reset, ws); - } else if (line[0] == ' ') + emit_line(data->o->file, set, reset, line, 1); + ws_check_emit(line + 1, len - 1, data->ws_rule, + data->o->file, set, reset, ws); + } else if (line[0] == ' ') { data->lineno++; - else if (line[0] == '@') { + data->trailing_blanks_start = 0; + } else if (line[0] == '@') { char *plus = strchr(line, '+'); if (plus) data->lineno = strtol(plus, NULL, 10) - 1; else die("invalid diff"); + data->trailing_blanks_start = 0; } } @@@ -1583,9 -1539,8 +1583,9 @@@ static void builtin_diffstat(const cha static void builtin_checkdiff(const char *name_a, const char *name_b, const char *attr_path, - struct diff_filespec *one, - struct diff_filespec *two, struct diff_options *o) + struct diff_filespec *one, + struct diff_filespec *two, + struct diff_options *o) { mmfile_t mf1, mf2; struct checkdiff_t data; @@@ -1597,18 -1552,13 +1597,18 @@@ data.xm.consume = checkdiff_consume; data.filename = name_b ? name_b : name_a; data.lineno = 0; - data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); + data.o = o; data.ws_rule = whitespace_rule(attr_path); - data.file = o->file; if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); + /* + * All the other codepaths check both sides, but not checking + * the "old" side here is deliberate. We are checking the newly + * introduced changes, and as long as the "new" side is text, we + * can and should check what it introduces. + */ if (diff_filespec_is_binary(two)) goto free_and_return; else { @@@ -1622,12 -1572,6 +1622,12 @@@ ecb.outf = xdiff_outf; ecb.priv = &data; xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + + if (data.trailing_blanks_start) { + fprintf(o->file, "%s:%d: ends with blank lines.\n", + data.filename, data.trailing_blanks_start); + data.status = 1; /* report errors */ + } } free_and_return: diff_free_filespec_data(one); @@@ -3412,9 -3356,8 +3412,8 @@@ int diff_result_code(struct diff_option void diff_addremove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, - const char *base, const char *path) + const char *concatpath) { - char concatpath[PATH_MAX]; struct diff_filespec *one, *two; if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode)) @@@ -3436,9 -3379,6 +3435,6 @@@ addremove = (addremove == '+' ? '-' : addremove == '-' ? '+' : addremove); - if (!path) path = ""; - sprintf(concatpath, "%s%s", base, path); - if (options->prefix && strncmp(concatpath, options->prefix, options->prefix_length)) return; @@@ -3459,9 -3399,8 +3455,8 @@@ void diff_change(struct diff_options *o unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, - const char *base, const char *path) + const char *concatpath) { - char concatpath[PATH_MAX]; struct diff_filespec *one, *two; if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode) @@@ -3474,8 -3413,6 +3469,6 @@@ tmp = old_mode; old_mode = new_mode; new_mode = tmp; tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c; } - if (!path) path = ""; - sprintf(concatpath, "%s%s", base, path); if (options->prefix && strncmp(concatpath, options->prefix, options->prefix_length)) diff --combined git-rebase--interactive.sh index da79a24568,e3f65bd880..e63a864c7b --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@@ -10,28 -10,14 +10,28 @@@ # The original idea comes from Eric W. Biederman, in # http://article.gmane.org/gmane.comp.version-control.git/22407 -USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose] - [--onto ] [])' +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-rebase [-i] [options] [--] [] +git-rebase [-i] (--continue | --abort | --skip) +-- + Available options are +v,verbose display a diffstat of what changed upstream +onto= rebase onto given branch instead of upstream +p,preserve-merges try to recreate merges instead of ignoring them +s,strategy= use the given merge strategy +m,merge always used (no-op) +i,interactive always used (no-op) + Actions: +continue continue rebasing process +abort abort rebasing process and restore original branch +skip skip current patch and continue rebasing process +" -OPTIONS_SPEC= . git-sh-setup require_work_tree -DOTEST="$GIT_DIR/.dotest-merge" +DOTEST="$GIT_DIR/rebase-merge" TODO="$DOTEST"/git-rebase-todo DONE="$DOTEST"/done MSG="$DOTEST"/message @@@ -39,8 -25,10 +39,8 @@@ SQUASH_MSG="$DOTEST"/message-squas REWRITTEN="$DOTEST"/rewritten PRESERVE_MERGES= STRATEGY= +ONTO= VERBOSE= -test -d "$REWRITTEN" && PRESERVE_MERGES=t -test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" -test -f "$DOTEST"/verbose && VERBOSE=t GIT_CHERRY_PICK_HELP=" After resolving the conflicts, mark the corrected paths with 'git add ', and @@@ -174,6 -162,8 +174,8 @@@ pick_one_preserving_merges () new_parents="$new_parents $new_p" ;; esac + else + new_parents="$new_parents $p" fi done case $fast_forward in @@@ -378,27 -368,10 +380,27 @@@ do_rest () done } +# check if no other options are set +is_standalone () { + test $# -eq 2 -a "$2" = '--' && + test -z "$ONTO" && + test -z "$PRESERVE_MERGES" && + test -z "$STRATEGY" && + test -z "$VERBOSE" +} + +get_saved_options () { + test -d "$REWRITTEN" && PRESERVE_MERGES=t + test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" + test -f "$DOTEST"/verbose && VERBOSE=t +} + while test $# != 0 do case "$1" in --continue) + is_standalone "$@" || usage + get_saved_options comment_for_reflog continue test -d "$DOTEST" || die "No interactive rebase running" @@@ -431,8 -404,6 +433,8 @@@ do_rest ;; --abort) + is_standalone "$@" || usage + get_saved_options comment_for_reflog abort git rerere clear @@@ -450,8 -421,6 +452,8 @@@ exit ;; --skip) + is_standalone "$@" || usage + get_saved_options comment_for_reflog skip git rerere clear @@@ -459,7 -428,7 +461,7 @@@ output git reset --hard && do_rest ;; - -s|--strategy) + -s) case "$#,$1" in *,*=*) STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; @@@ -470,26 -439,25 +472,26 @@@ shift ;; esac ;; - -m|--merge) + -m) # we use merge anyway ;; - -C*) - die "Interactive rebase uses merge, so $1 does not make sense" - ;; - -v|--verbose) + -v) VERBOSE=t ;; - -p|--preserve-merges) + -p) PRESERVE_MERGES=t ;; - -i|--interactive) + -i) # yeah, we know ;; - ''|-h) - usage + --onto) + shift + ONTO=$(git rev-parse --verify "$1") || + die "Does not point to a valid commit: $1" ;; - *) + --) + shift + test $# -eq 1 -o $# -eq 2 || usage test -d "$DOTEST" && die "Interactive rebase already started" @@@ -498,6 -466,15 +500,6 @@@ comment_for_reflog start - ONTO= - case "$1" in - --onto) - ONTO=$(git rev-parse --verify "$2") || - die "Does not point to a valid commit: $2" - shift; shift - ;; - esac - require_clean_work_tree UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" @@@ -574,7 -551,6 +576,7 @@@ EO has_action "$TODO" || die_abort "Nothing to do" + git update-ref ORIG_HEAD $HEAD output git checkout $ONTO && do_rest ;; esac diff --combined revision.c index 846af7bd20,8dc3ca7bf6..3897fec531 --- a/revision.c +++ b/revision.c @@@ -10,7 -10,6 +10,7 @@@ #include "grep.h" #include "reflog-walk.h" #include "patch-ids.h" +#include "decorate.h" volatile show_early_output_fn_t show_early_output; @@@ -260,7 -259,7 +260,7 @@@ static int tree_difference = REV_TREE_S static void file_add_remove(struct diff_options *options, int addremove, unsigned mode, const unsigned char *sha1, - const char *base, const char *path) + const char *fullpath) { int diff = REV_TREE_DIFFERENT; @@@ -286,7 -285,7 +286,7 @@@ static void file_change(struct diff_opt unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, - const char *base, const char *path) + const char *fullpath) { tree_difference = REV_TREE_DIFFERENT; DIFF_OPT_SET(options, HAS_CHANGES); @@@ -413,26 -412,10 +413,26 @@@ static void try_to_simplify_commit(stru commit->object.flags |= TREESAME; } -static int add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list) +static void insert_by_date_cached(struct commit *p, struct commit_list **head, + struct commit_list *cached_base, struct commit_list **cache) +{ + struct commit_list *new_entry; + + if (cached_base && p->date < cached_base->item->date) + new_entry = insert_by_date(p, &cached_base->next); + else + new_entry = insert_by_date(p, head); + + if (cache && (!*cache || p->date < (*cache)->item->date)) + *cache = new_entry; +} + +static int add_parents_to_list(struct rev_info *revs, struct commit *commit, + struct commit_list **list, struct commit_list **cache_ptr) { struct commit_list *parent = commit->parents; unsigned left_flag; + struct commit_list *cached_base = cache_ptr ? *cache_ptr : NULL; if (commit->object.flags & ADDED) return 0; @@@ -462,7 -445,7 +462,7 @@@ if (p->object.flags & SEEN) continue; p->object.flags |= SEEN; - insert_by_date(p, list); + insert_by_date_cached(p, list, cached_base, cache_ptr); } return 0; } @@@ -487,7 -470,7 +487,7 @@@ p->object.flags |= left_flag; if (!(p->object.flags & SEEN)) { p->object.flags |= SEEN; - insert_by_date(p, list); + insert_by_date_cached(p, list, cached_base, cache_ptr); } if(revs->first_parent_only) break; @@@ -628,7 -611,7 +628,7 @@@ static int limit_list(struct rev_info * if (revs->max_age != -1 && (commit->date < revs->max_age)) obj->flags |= UNINTERESTING; - if (add_parents_to_list(revs, commit, &list) < 0) + if (add_parents_to_list(revs, commit, &list, NULL) < 0) return -1; if (obj->flags & UNINTERESTING) { mark_parents_uninteresting(commit); @@@ -927,23 -910,6 +927,23 @@@ int handle_revision_arg(const char *arg return 0; } +void read_revisions_from_stdin(struct rev_info *revs) +{ + char line[1000]; + + while (fgets(line, sizeof(line), stdin) != NULL) { + int len = strlen(line); + if (len && line[len - 1] == '\n') + line[--len] = '\0'; + if (!len) + break; + if (line[0] == '-') + die("options not supported in --stdin mode"); + if (handle_revision_arg(line, revs, 0, 1)) + die("bad revision '%s'", line); + } +} + static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what) { if (!revs->grep_filter) { @@@ -990,226 -956,6 +990,226 @@@ static void add_ignore_packed(struct re revs->ignore_packed[num] = NULL; } +static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, + int *unkc, const char **unkv) +{ + const char *arg = argv[0]; + + /* pseudo revision arguments */ + if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") || + !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") || + !strcmp(arg, "--reflog") || !strcmp(arg, "--not") || + !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk")) + { + unkv[(*unkc)++] = arg; + return 0; + } + + if (!prefixcmp(arg, "--max-count=")) { + revs->max_count = atoi(arg + 12); + } else if (!prefixcmp(arg, "--skip=")) { + revs->skip_count = atoi(arg + 7); + } else if ((*arg == '-') && isdigit(arg[1])) { + /* accept -, like traditional "head" */ + revs->max_count = atoi(arg + 1); + } else if (!strcmp(arg, "-n")) { + if (argc <= 1) + return error("-n requires an argument"); + revs->max_count = atoi(argv[1]); + return 2; + } else if (!prefixcmp(arg, "-n")) { + revs->max_count = atoi(arg + 2); + } else if (!prefixcmp(arg, "--max-age=")) { + revs->max_age = atoi(arg + 10); + } else if (!prefixcmp(arg, "--since=")) { + revs->max_age = approxidate(arg + 8); + } else if (!prefixcmp(arg, "--after=")) { + revs->max_age = approxidate(arg + 8); + } else if (!prefixcmp(arg, "--min-age=")) { + revs->min_age = atoi(arg + 10); + } else if (!prefixcmp(arg, "--before=")) { + revs->min_age = approxidate(arg + 9); + } else if (!prefixcmp(arg, "--until=")) { + revs->min_age = approxidate(arg + 8); + } else if (!strcmp(arg, "--first-parent")) { + revs->first_parent_only = 1; + } else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) { + init_reflog_walk(&revs->reflog_info); + } else if (!strcmp(arg, "--default")) { + if (argc <= 1) + return error("bad --default argument"); + revs->def = argv[1]; + return 2; + } else if (!strcmp(arg, "--merge")) { + revs->show_merge = 1; + } else if (!strcmp(arg, "--topo-order")) { + revs->lifo = 1; + revs->topo_order = 1; + } else if (!strcmp(arg, "--date-order")) { + revs->lifo = 0; + revs->topo_order = 1; + } else if (!prefixcmp(arg, "--early-output")) { + int count = 100; + switch (arg[14]) { + case '=': + count = atoi(arg+15); + /* Fallthrough */ + case 0: + revs->topo_order = 1; + revs->early_output = count; + } + } else if (!strcmp(arg, "--parents")) { + revs->rewrite_parents = 1; + revs->print_parents = 1; + } else if (!strcmp(arg, "--dense")) { + revs->dense = 1; + } else if (!strcmp(arg, "--sparse")) { + revs->dense = 0; + } else if (!strcmp(arg, "--show-all")) { + revs->show_all = 1; + } else if (!strcmp(arg, "--remove-empty")) { + revs->remove_empty_trees = 1; + } else if (!strcmp(arg, "--no-merges")) { + revs->no_merges = 1; + } else if (!strcmp(arg, "--boundary")) { + revs->boundary = 1; + } else if (!strcmp(arg, "--left-right")) { + revs->left_right = 1; + } else if (!strcmp(arg, "--cherry-pick")) { + revs->cherry_pick = 1; + revs->limited = 1; + } else if (!strcmp(arg, "--objects")) { + revs->tag_objects = 1; + revs->tree_objects = 1; + revs->blob_objects = 1; + } else if (!strcmp(arg, "--objects-edge")) { + revs->tag_objects = 1; + revs->tree_objects = 1; + revs->blob_objects = 1; + revs->edge_hint = 1; + } else if (!strcmp(arg, "--unpacked")) { + revs->unpacked = 1; + free(revs->ignore_packed); + revs->ignore_packed = NULL; + revs->num_ignore_packed = 0; + } else if (!prefixcmp(arg, "--unpacked=")) { + revs->unpacked = 1; + add_ignore_packed(revs, arg+11); + } else if (!strcmp(arg, "-r")) { + revs->diff = 1; + DIFF_OPT_SET(&revs->diffopt, RECURSIVE); + } else if (!strcmp(arg, "-t")) { + revs->diff = 1; + DIFF_OPT_SET(&revs->diffopt, RECURSIVE); + DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE); + } else if (!strcmp(arg, "-m")) { + revs->ignore_merges = 0; + } else if (!strcmp(arg, "-c")) { + revs->diff = 1; + revs->dense_combined_merges = 0; + revs->combine_merges = 1; + } else if (!strcmp(arg, "--cc")) { + revs->diff = 1; + revs->dense_combined_merges = 1; + revs->combine_merges = 1; + } else if (!strcmp(arg, "-v")) { + revs->verbose_header = 1; + } else if (!strcmp(arg, "--pretty")) { + revs->verbose_header = 1; + get_commit_format(arg+8, revs); + } else if (!prefixcmp(arg, "--pretty=")) { + revs->verbose_header = 1; + get_commit_format(arg+9, revs); + } else if (!strcmp(arg, "--graph")) { + revs->topo_order = 1; + revs->rewrite_parents = 1; + revs->graph = graph_init(revs); + } else if (!strcmp(arg, "--root")) { + revs->show_root_diff = 1; + } else if (!strcmp(arg, "--no-commit-id")) { + revs->no_commit_id = 1; + } else if (!strcmp(arg, "--always")) { + revs->always_show_header = 1; + } else if (!strcmp(arg, "--no-abbrev")) { + revs->abbrev = 0; + } else if (!strcmp(arg, "--abbrev")) { + revs->abbrev = DEFAULT_ABBREV; + } else if (!prefixcmp(arg, "--abbrev=")) { + revs->abbrev = strtoul(arg + 9, NULL, 10); + if (revs->abbrev < MINIMUM_ABBREV) + revs->abbrev = MINIMUM_ABBREV; + else if (revs->abbrev > 40) + revs->abbrev = 40; + } else if (!strcmp(arg, "--abbrev-commit")) { + revs->abbrev_commit = 1; + } else if (!strcmp(arg, "--full-diff")) { + revs->diff = 1; + revs->full_diff = 1; + } else if (!strcmp(arg, "--full-history")) { + revs->simplify_history = 0; + } else if (!strcmp(arg, "--relative-date")) { + revs->date_mode = DATE_RELATIVE; + } else if (!strncmp(arg, "--date=", 7)) { + revs->date_mode = parse_date_format(arg + 7); + } else if (!strcmp(arg, "--log-size")) { + revs->show_log_size = 1; + } + /* + * Grepping the commit log + */ + else if (!prefixcmp(arg, "--author=")) { + add_header_grep(revs, "author", arg+9); + } else if (!prefixcmp(arg, "--committer=")) { + add_header_grep(revs, "committer", arg+12); + } else if (!prefixcmp(arg, "--grep=")) { + add_message_grep(revs, arg+7); + } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) { + if (revs->grep_filter) + revs->grep_filter->regflags |= REG_EXTENDED; + } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) { + if (revs->grep_filter) + revs->grep_filter->regflags |= REG_ICASE; + } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) { + if (revs->grep_filter) + revs->grep_filter->fixed = 1; + } else if (!strcmp(arg, "--all-match")) { + if (revs->grep_filter) + revs->grep_filter->all_match = 1; + } else if (!prefixcmp(arg, "--encoding=")) { + arg += 11; + if (strcmp(arg, "none")) + git_log_output_encoding = xstrdup(arg); + else + git_log_output_encoding = ""; + } else if (!strcmp(arg, "--reverse")) { + revs->reverse ^= 1; + } else if (!strcmp(arg, "--children")) { + revs->children.name = "children"; + revs->limited = 1; + } else { + int opts = diff_opt_parse(&revs->diffopt, argv, argc); + if (!opts) + unkv[(*unkc)++] = arg; + return opts; + } + + return 1; +} + +void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, + const struct option *options, + const char * const usagestr[]) +{ + int n = handle_revision_opt(revs, ctx->argc, ctx->argv, + &ctx->cpidx, ctx->out); + if (n <= 0) { + error("unknown option `%s'", ctx->argv[0]); + usage_with_options(usagestr, options); + } + ctx->argv += n; + ctx->argc -= n; +} + /* * Parse revision information, filling in the "rev_info" structure, * and removing the used arguments from the argument list. @@@ -1219,7 -965,12 +1219,7 @@@ */ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def) { - int i, flags, seen_dashdash, show_merge; - const char **unrecognized = argv + 1; - int left = 1; - int all_match = 0; - int regflags = 0; - int fixed = 0; + int i, flags, left, seen_dashdash; /* First, search for "--" */ seen_dashdash = 0; @@@ -1235,13 -986,58 +1235,13 @@@ break; } - flags = show_merge = 0; - for (i = 1; i < argc; i++) { + /* Second, deal with arguments and options */ + flags = 0; + for (left = i = 1; i < argc; i++) { const char *arg = argv[i]; if (*arg == '-') { int opts; - if (!prefixcmp(arg, "--max-count=")) { - revs->max_count = atoi(arg + 12); - continue; - } - if (!prefixcmp(arg, "--skip=")) { - revs->skip_count = atoi(arg + 7); - continue; - } - /* accept -, like traditional "head" */ - if ((*arg == '-') && isdigit(arg[1])) { - revs->max_count = atoi(arg + 1); - continue; - } - if (!strcmp(arg, "-n")) { - if (argc <= i + 1) - die("-n requires an argument"); - revs->max_count = atoi(argv[++i]); - continue; - } - if (!prefixcmp(arg, "-n")) { - revs->max_count = atoi(arg + 2); - continue; - } - if (!prefixcmp(arg, "--max-age=")) { - revs->max_age = atoi(arg + 10); - continue; - } - if (!prefixcmp(arg, "--since=")) { - revs->max_age = approxidate(arg + 8); - continue; - } - if (!prefixcmp(arg, "--after=")) { - revs->max_age = approxidate(arg + 8); - continue; - } - if (!prefixcmp(arg, "--min-age=")) { - revs->min_age = atoi(arg + 10); - continue; - } - if (!prefixcmp(arg, "--before=")) { - revs->min_age = approxidate(arg + 9); - continue; - } - if (!prefixcmp(arg, "--until=")) { - revs->min_age = approxidate(arg + 8); - continue; - } + if (!strcmp(arg, "--all")) { handle_refs(revs, flags, for_each_ref); continue; @@@ -1258,14 -1054,265 +1258,14 @@@ handle_refs(revs, flags, for_each_remote_ref); continue; } - if (!strcmp(arg, "--first-parent")) { - revs->first_parent_only = 1; - continue; - } if (!strcmp(arg, "--reflog")) { handle_reflog(revs, flags); continue; } - if (!strcmp(arg, "-g") || - !strcmp(arg, "--walk-reflogs")) { - init_reflog_walk(&revs->reflog_info); - continue; - } if (!strcmp(arg, "--not")) { flags ^= UNINTERESTING; continue; } - if (!strcmp(arg, "--default")) { - if (++i >= argc) - die("bad --default argument"); - def = argv[i]; - continue; - } - if (!strcmp(arg, "--merge")) { - show_merge = 1; - continue; - } - if (!strcmp(arg, "--topo-order")) { - revs->lifo = 1; - revs->topo_order = 1; - continue; - } - if (!strcmp(arg, "--date-order")) { - revs->lifo = 0; - revs->topo_order = 1; - continue; - } - if (!prefixcmp(arg, "--early-output")) { - int count = 100; - switch (arg[14]) { - case '=': - count = atoi(arg+15); - /* Fallthrough */ - case 0: - revs->topo_order = 1; - revs->early_output = count; - continue; - } - } - if (!strcmp(arg, "--parents")) { - revs->rewrite_parents = 1; - revs->print_parents = 1; - continue; - } - if (!strcmp(arg, "--dense")) { - revs->dense = 1; - continue; - } - if (!strcmp(arg, "--sparse")) { - revs->dense = 0; - continue; - } - if (!strcmp(arg, "--show-all")) { - revs->show_all = 1; - continue; - } - if (!strcmp(arg, "--remove-empty")) { - revs->remove_empty_trees = 1; - continue; - } - if (!strcmp(arg, "--no-merges")) { - revs->no_merges = 1; - continue; - } - if (!strcmp(arg, "--boundary")) { - revs->boundary = 1; - continue; - } - if (!strcmp(arg, "--left-right")) { - revs->left_right = 1; - continue; - } - if (!strcmp(arg, "--cherry-pick")) { - revs->cherry_pick = 1; - revs->limited = 1; - continue; - } - if (!strcmp(arg, "--objects")) { - revs->tag_objects = 1; - revs->tree_objects = 1; - revs->blob_objects = 1; - continue; - } - if (!strcmp(arg, "--objects-edge")) { - revs->tag_objects = 1; - revs->tree_objects = 1; - revs->blob_objects = 1; - revs->edge_hint = 1; - continue; - } - if (!strcmp(arg, "--unpacked")) { - revs->unpacked = 1; - free(revs->ignore_packed); - revs->ignore_packed = NULL; - revs->num_ignore_packed = 0; - continue; - } - if (!prefixcmp(arg, "--unpacked=")) { - revs->unpacked = 1; - add_ignore_packed(revs, arg+11); - continue; - } - if (!strcmp(arg, "-r")) { - revs->diff = 1; - DIFF_OPT_SET(&revs->diffopt, RECURSIVE); - continue; - } - if (!strcmp(arg, "-t")) { - revs->diff = 1; - DIFF_OPT_SET(&revs->diffopt, RECURSIVE); - DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE); - continue; - } - if (!strcmp(arg, "-m")) { - revs->ignore_merges = 0; - continue; - } - if (!strcmp(arg, "-c")) { - revs->diff = 1; - revs->dense_combined_merges = 0; - revs->combine_merges = 1; - continue; - } - if (!strcmp(arg, "--cc")) { - revs->diff = 1; - revs->dense_combined_merges = 1; - revs->combine_merges = 1; - continue; - } - if (!strcmp(arg, "-v")) { - revs->verbose_header = 1; - continue; - } - if (!strcmp(arg, "--pretty")) { - revs->verbose_header = 1; - get_commit_format(arg+8, revs); - continue; - } - if (!prefixcmp(arg, "--pretty=")) { - revs->verbose_header = 1; - get_commit_format(arg+9, revs); - continue; - } - if (!strcmp(arg, "--graph")) { - revs->topo_order = 1; - revs->rewrite_parents = 1; - revs->graph = graph_init(revs); - continue; - } - if (!strcmp(arg, "--root")) { - revs->show_root_diff = 1; - continue; - } - if (!strcmp(arg, "--no-commit-id")) { - revs->no_commit_id = 1; - continue; - } - if (!strcmp(arg, "--always")) { - revs->always_show_header = 1; - continue; - } - if (!strcmp(arg, "--no-abbrev")) { - revs->abbrev = 0; - continue; - } - if (!strcmp(arg, "--abbrev")) { - revs->abbrev = DEFAULT_ABBREV; - continue; - } - if (!prefixcmp(arg, "--abbrev=")) { - revs->abbrev = strtoul(arg + 9, NULL, 10); - if (revs->abbrev < MINIMUM_ABBREV) - revs->abbrev = MINIMUM_ABBREV; - else if (revs->abbrev > 40) - revs->abbrev = 40; - continue; - } - if (!strcmp(arg, "--abbrev-commit")) { - revs->abbrev_commit = 1; - continue; - } - if (!strcmp(arg, "--full-diff")) { - revs->diff = 1; - revs->full_diff = 1; - continue; - } - if (!strcmp(arg, "--full-history")) { - revs->simplify_history = 0; - continue; - } - if (!strcmp(arg, "--relative-date")) { - revs->date_mode = DATE_RELATIVE; - continue; - } - if (!strncmp(arg, "--date=", 7)) { - revs->date_mode = parse_date_format(arg + 7); - continue; - } - if (!strcmp(arg, "--log-size")) { - revs->show_log_size = 1; - continue; - } - - /* - * Grepping the commit log - */ - if (!prefixcmp(arg, "--author=")) { - add_header_grep(revs, "author", arg+9); - continue; - } - if (!prefixcmp(arg, "--committer=")) { - add_header_grep(revs, "committer", arg+12); - continue; - } - if (!prefixcmp(arg, "--grep=")) { - add_message_grep(revs, arg+7); - continue; - } - if (!strcmp(arg, "--extended-regexp") || - !strcmp(arg, "-E")) { - regflags |= REG_EXTENDED; - continue; - } - if (!strcmp(arg, "--regexp-ignore-case") || - !strcmp(arg, "-i")) { - regflags |= REG_ICASE; - continue; - } - if (!strcmp(arg, "--fixed-strings") || - !strcmp(arg, "-F")) { - fixed = 1; - continue; - } - if (!strcmp(arg, "--all-match")) { - all_match = 1; - continue; - } - if (!prefixcmp(arg, "--encoding=")) { - arg += 11; - if (strcmp(arg, "none")) - git_log_output_encoding = xstrdup(arg); - else - git_log_output_encoding = ""; - continue; - } - if (!strcmp(arg, "--reverse")) { - revs->reverse ^= 1; - continue; - } if (!strcmp(arg, "--no-walk")) { revs->no_walk = 1; continue; @@@ -1275,13 -1322,13 +1275,13 @@@ continue; } - opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); + opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv); if (opts > 0) { i += opts - 1; continue; } - *unrecognized++ = arg; - left++; + if (opts < 0) + exit(128); continue; } @@@ -1305,18 -1352,21 +1305,18 @@@ } } - if (revs->grep_filter) { - revs->grep_filter->regflags |= regflags; - revs->grep_filter->fixed = fixed; - } - - if (show_merge) + if (revs->def == NULL) + revs->def = def; + if (revs->show_merge) prepare_show_merge(revs); - if (def && !revs->pending.nr) { + if (revs->def && !revs->pending.nr) { unsigned char sha1[20]; struct object *object; unsigned mode; - if (get_sha1_with_mode(def, sha1, &mode)) - die("bad default revision '%s'", def); - object = get_reference(revs, def, sha1, 0); - add_pending_object_with_mode(revs, object, def, mode); + if (get_sha1_with_mode(revs->def, sha1, &mode)) + die("bad default revision '%s'", revs->def); + object = get_reference(revs, revs->def, sha1, 0); + add_pending_object_with_mode(revs, object, revs->def, mode); } /* Did the user ask for any diff output? Run the diff! */ @@@ -1350,13 -1400,12 +1350,13 @@@ die("diff_setup_done failed"); if (revs->grep_filter) { - revs->grep_filter->all_match = all_match; compile_grep_patterns(revs->grep_filter); } if (revs->reverse && revs->reflog_info) die("cannot combine --reverse with --walk-reflogs"); + if (revs->rewrite_parents && revs->children.name) + die("cannot combine --parents and --children"); /* * Limitations on the graph functionality @@@ -1370,26 -1419,6 +1370,26 @@@ return left; } +static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child) +{ + struct commit_list *l = xcalloc(1, sizeof(*l)); + + l->item = child; + l->next = add_decoration(&revs->children, &parent->object, l); +} + +static void set_children(struct rev_info *revs) +{ + struct commit_list *l; + for (l = revs->commits; l; l = l->next) { + struct commit *commit = l->item; + struct commit_list *p; + + for (p = commit->parents; p; p = p->next) + add_child(revs, p->item, commit); + } +} + int prepare_revision_walk(struct rev_info *revs) { int nr = revs->pending.nr; @@@ -1418,8 -1447,6 +1418,8 @@@ return -1; if (revs->topo_order) sort_in_topological_order(&revs->commits, revs->lifo); + if (revs->children.name) + set_children(revs); return 0; } @@@ -1431,12 -1458,10 +1431,12 @@@ enum rewrite_result static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp) { + struct commit_list *cache = NULL; + for (;;) { struct commit *p = *pp; if (!revs->limited) - if (add_parents_to_list(revs, p, &revs->commits) < 0) + if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0) return rewrite_one_error; if (p->parents && p->parents->next) return rewrite_one_ok; @@@ -1499,11 -1524,6 +1499,11 @@@ static int commit_match(struct commit * commit->buffer, strlen(commit->buffer)); } +static inline int want_ancestry(struct rev_info *revs) +{ + return (revs->rewrite_parents || revs->children.name); +} + enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) { if (commit->object.flags & SHOWN) @@@ -1524,13 -1544,13 +1524,13 @@@ /* Commit without changes? */ if (commit->object.flags & TREESAME) { /* drop merges unless we want parenthood */ - if (!revs->rewrite_parents) + if (!want_ancestry(revs)) return commit_ignore; /* non-merge - always ignore it */ if (!commit->parents || !commit->parents->next) return commit_ignore; } - if (revs->rewrite_parents && rewrite_parents(revs, commit) < 0) + if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0) return commit_error; } return commit_show; @@@ -1560,7 -1580,7 +1560,7 @@@ static struct commit *get_revision_1(st if (revs->max_age != -1 && (commit->date < revs->max_age)) continue; - if (add_parents_to_list(revs, commit, &revs->commits) < 0) + if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) return NULL; } diff --combined t/t7600-merge.sh index d4cf6289a4,daf45b7ffa..9ab5bdacc4 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@@ -221,37 -221,13 +221,13 @@@ test_expect_success 'setup' test_debug 'gitk --all' --test_expect_success 'test option parsing' ' - if git merge -$ c1 - then - echo "[OOPS] -$ accepted" - false - fi && - if git merge --no-such c1 - then - echo "[OOPS] --no-such accepted" - false - fi && - if git merge -s foobar c1 - then - echo "[OOPS] -s foobar accepted" - false - fi && - if git merge -s=foobar c1 - then - echo "[OOPS] -s=foobar accepted" - false - fi && - if git merge -m - then - echo "[OOPS] missing commit msg accepted" - false - fi && - if git merge - then - echo "[OOPS] missing commit references accepted" - false - fi ++test_expect_failure 'test option parsing' ' + test_must_fail git merge -$ c1 && + test_must_fail git merge --no-such c1 && + test_must_fail git merge -s foobar c1 && + test_must_fail git merge -s=foobar c1 && + test_must_fail git merge -m && + test_must_fail git merge ' test_expect_success 'merge c0 with c1' ' @@@ -465,51 -441,11 +441,51 @@@ test_expect_success 'merge log message git merge --no-log c2 && git show -s --pretty=format:%b HEAD >msg.act && verify_diff msg.nolog msg.act "[OOPS] bad merge log message" && + git merge --log c3 && git show -s --pretty=format:%b HEAD >msg.act && + verify_diff msg.log msg.act "[OOPS] bad merge log message" && + + git reset --hard HEAD^ && + git config merge.log yes && + git merge c3 && + git show -s --pretty=format:%b HEAD >msg.act && verify_diff msg.log msg.act "[OOPS] bad merge log message" ' test_debug 'gitk --all' +test_expect_success 'merge c1 with c0, c2, c0, and c1' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + git merge c0 c2 c0 c1 && + verify_merge file result.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c0, c2, c0, and c1' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + git merge c0 c2 c0 c1 && + verify_merge file result.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + +test_expect_success 'merge c1 with c1 and c2' ' + git reset --hard c1 && + git config branch.master.mergeoptions "" && + test_tick && + git merge c1 c2 && + verify_merge file result.1-5 && + verify_parents $c1 $c2 +' + +test_debug 'gitk --all' + test_done