Code

revision.c: introduce --min-parents and --max-parents options
[git.git] / revision.c
index 7e82efd9324e84582732485c53c6c25a24c29997..8540a5d2f104d6f26113406be58fa380ec612519 100644 (file)
@@ -444,15 +444,15 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
        commit->object.flags |= TREESAME;
 }
 
-static void insert_by_date_cached(struct commit *p, struct commit_list **head,
+static void commit_list_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);
+               new_entry = commit_list_insert_by_date(p, &cached_base->next);
        else
-               new_entry = insert_by_date(p, head);
+               new_entry = commit_list_insert_by_date(p, head);
 
        if (cache && (!*cache || p->date < (*cache)->item->date))
                *cache = new_entry;
@@ -494,7 +494,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                        if (p->object.flags & SEEN)
                                continue;
                        p->object.flags |= SEEN;
-                       insert_by_date_cached(p, list, cached_base, cache_ptr);
+                       commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                return 0;
        }
@@ -521,7 +521,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                p->object.flags |= left_flag;
                if (!(p->object.flags & SEEN)) {
                        p->object.flags |= SEEN;
-                       insert_by_date_cached(p, list, cached_base, cache_ptr);
+                       commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
                }
                if (revs->first_parent_only)
                        break;
@@ -535,6 +535,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
        int left_count = 0, right_count = 0;
        int left_first;
        struct patch_ids ids;
+       unsigned cherry_flag;
 
        /* First count the commits on the left and on the right */
        for (p = list; p; p = p->next) {
@@ -576,6 +577,9 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
                commit->util = add_commit_patch_id(commit, &ids);
        }
 
+       /* either cherry_mark or cherry_pick are true */
+       cherry_flag = revs->cherry_mark ? PATCHSAME : SHOWN;
+
        /* Check the other side */
        for (p = list; p; p = p->next) {
                struct commit *commit = p->item;
@@ -598,7 +602,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
                if (!id)
                        continue;
                id->seen = 1;
-               commit->object.flags |= SHOWN;
+               commit->object.flags |= cherry_flag;
        }
 
        /* Now check the original side for seen ones */
@@ -610,7 +614,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
                if (!ent)
                        continue;
                if (ent->seen)
-                       commit->object.flags |= SHOWN;
+                       commit->object.flags |= cherry_flag;
                commit->util = NULL;
        }
 
@@ -733,6 +737,23 @@ static struct commit_list *collect_bottom_commits(struct commit_list *list)
        return bottom;
 }
 
+/* Assumes either left_only or right_only is set */
+static void limit_left_right(struct commit_list *list, struct rev_info *revs)
+{
+       struct commit_list *p;
+
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+
+               if (revs->right_only) {
+                       if (commit->object.flags & SYMMETRIC_LEFT)
+                               commit->object.flags |= SHOWN;
+               } else  /* revs->left_only is set */
+                       if (!(commit->object.flags & SYMMETRIC_LEFT))
+                               commit->object.flags |= SHOWN;
+       }
+}
+
 static int limit_list(struct rev_info *revs)
 {
        int slop = SLOP;
@@ -785,9 +806,12 @@ static int limit_list(struct rev_info *revs)
                show(revs, newlist);
                show_early_output = NULL;
        }
-       if (revs->cherry_pick)
+       if (revs->cherry_pick || revs->cherry_mark)
                cherry_pick_list(newlist, revs);
 
+       if (revs->left_only || revs->right_only)
+               limit_left_right(newlist, revs);
+
        if (bottom) {
                limit_to_ancestry(bottom, newlist);
                free_commit_list(bottom);
@@ -820,12 +844,12 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_flags = flags;
 }
 
-static void handle_refs(struct rev_info *revs, unsigned flags,
-               int (*for_each)(each_ref_fn, void *))
+static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
+               int (*for_each)(const char *, each_ref_fn, void *))
 {
        struct all_refs_cb cb;
        init_all_refs_cb(&cb, revs, flags);
-       for_each(handle_one_ref, &cb);
+       for_each(submodule, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -921,6 +945,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->min_age = -1;
        revs->skip_count = -1;
        revs->max_count = -1;
+       revs->max_parents = -1;
 
        revs->commit_format = CMIT_FMT_DEFAULT;
 
@@ -1148,6 +1173,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                               int *unkc, const char **unkv)
 {
        const char *arg = argv[0];
+       const char *optarg;
+       int argcount;
 
        /* pseudo revision arguments */
        if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
@@ -1160,11 +1187,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                return 1;
        }
 
-       if (!prefixcmp(arg, "--max-count=")) {
-               revs->max_count = atoi(arg + 12);
+       if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
+               revs->max_count = atoi(optarg);
                revs->no_walk = 0;
-       } else if (!prefixcmp(arg, "--skip=")) {
-               revs->skip_count = atoi(arg + 7);
+               return argcount;
+       } else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
+               revs->skip_count = atoi(optarg);
+               return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
        /* accept -<digit>, like traditional "head" */
                revs->max_count = atoi(arg + 1);
@@ -1178,18 +1207,24 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!prefixcmp(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
                revs->no_walk = 0;
-       } 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 ((argcount = parse_long_opt("max-age", argv, &optarg))) {
+               revs->max_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("since", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("after", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
+               revs->min_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("before", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("until", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
        } else if (!strcmp(arg, "--first-parent")) {
                revs->first_parent_only = 1;
        } else if (!strcmp(arg, "--ancestry-path")) {
@@ -1246,16 +1281,47 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--remove-empty")) {
                revs->remove_empty_trees = 1;
        } else if (!strcmp(arg, "--merges")) {
-               revs->merges_only = 1;
+               revs->min_parents = 2;
        } else if (!strcmp(arg, "--no-merges")) {
-               revs->no_merges = 1;
+               revs->max_parents = 1;
+       } else if (!prefixcmp(arg, "--min-parents=")) {
+               revs->min_parents = atoi(arg+14);
+       } else if (!prefixcmp(arg, "--no-min-parents")) {
+               revs->min_parents = 0;
+       } else if (!prefixcmp(arg, "--max-parents=")) {
+               revs->max_parents = atoi(arg+14);
+       } else if (!prefixcmp(arg, "--no-max-parents")) {
+               revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
                revs->boundary = 1;
        } else if (!strcmp(arg, "--left-right")) {
                revs->left_right = 1;
+       } else if (!strcmp(arg, "--left-only")) {
+               if (revs->right_only)
+                       die("--left-only is incompatible with --right-only"
+                           " or --cherry");
+               revs->left_only = 1;
+       } else if (!strcmp(arg, "--right-only")) {
+               if (revs->left_only)
+                       die("--right-only is incompatible with --left-only");
+               revs->right_only = 1;
+       } else if (!strcmp(arg, "--cherry")) {
+               if (revs->left_only)
+                       die("--cherry is incompatible with --left-only");
+               revs->cherry_mark = 1;
+               revs->right_only = 1;
+               revs->max_parents = 1;
+               revs->limited = 1;
        } else if (!strcmp(arg, "--count")) {
                revs->count = 1;
+       } else if (!strcmp(arg, "--cherry-mark")) {
+               if (revs->cherry_pick)
+                       die("--cherry-mark is incompatible with --cherry-pick");
+               revs->cherry_mark = 1;
+               revs->limited = 1; /* needs limit_list() */
        } else if (!strcmp(arg, "--cherry-pick")) {
+               if (revs->cherry_mark)
+                       die("--cherry-pick is incompatible with --cherry-mark");
                revs->cherry_pick = 1;
                revs->limited = 1;
        } else if (!strcmp(arg, "--objects")) {
@@ -1295,6 +1361,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
        } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+               /*
+                * Detached form ("--pretty X" as opposed to "--pretty=X")
+                * not allowed, since the argument is optional.
+                */
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
@@ -1359,21 +1429,25 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--relative-date")) {
                revs->date_mode = DATE_RELATIVE;
                revs->date_mode_explicit = 1;
-       } else if (!strncmp(arg, "--date=", 7)) {
-               revs->date_mode = parse_date_format(arg + 7);
+       } else if ((argcount = parse_long_opt("date", argv, &optarg))) {
+               revs->date_mode = parse_date_format(optarg);
                revs->date_mode_explicit = 1;
+               return argcount;
        } else if (!strcmp(arg, "--log-size")) {
                revs->show_log_size = 1;
        }
        /*
         * Grepping the commit log
         */
-       else if (!prefixcmp(arg, "--author=")) {
-               add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9);
-       } else if (!prefixcmp(arg, "--committer=")) {
-               add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12);
-       } else if (!prefixcmp(arg, "--grep=")) {
-               add_message_grep(revs, arg+7);
+       else if ((argcount = parse_long_opt("author", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_AUTHOR, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("committer", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_COMMITTER, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
+               add_message_grep(revs, optarg);
+               return argcount;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
@@ -1382,12 +1456,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
-       } else if (!prefixcmp(arg, "--encoding=")) {
-               arg += 11;
-               if (strcmp(arg, "none"))
-                       git_log_output_encoding = xstrdup(arg);
+       } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+               if (strcmp(optarg, "none"))
+                       git_log_output_encoding = xstrdup(optarg);
                else
                        git_log_output_encoding = "";
+               return argcount;
        } else if (!strcmp(arg, "--reverse")) {
                revs->reverse ^= 1;
        } else if (!strcmp(arg, "--children")) {
@@ -1417,14 +1491,14 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
-static int for_each_bad_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/bad", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
 }
 
-static int for_each_good_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/good", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
 }
 
 static void append_prune_data(const char ***prune_data, const char **av)
@@ -1466,6 +1540,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 {
        int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
        const char **prune_data = NULL;
+       const char *submodule = NULL;
+       const char *optarg;
+       int argcount;
+
+       if (opt)
+               submodule = opt->submodule;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -1490,32 +1570,33 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                        int opts;
 
                        if (!strcmp(arg, "--all")) {
-                               handle_refs(revs, flags, for_each_ref);
-                               handle_refs(revs, flags, head_ref);
+                               handle_refs(submodule, revs, flags, for_each_ref_submodule);
+                               handle_refs(submodule, revs, flags, head_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
-                               handle_refs(revs, flags, for_each_branch_ref);
+                               handle_refs(submodule, revs, flags, for_each_branch_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
-                               handle_refs(revs, flags, for_each_bad_bisect_ref);
-                               handle_refs(revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
+                               handle_refs(submodule, revs, flags, for_each_bad_bisect_ref);
+                               handle_refs(submodule, revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
                                revs->bisect = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
-                               handle_refs(revs, flags, for_each_tag_ref);
+                               handle_refs(submodule, revs, flags, for_each_tag_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
-                               handle_refs(revs, flags, for_each_remote_ref);
+                               handle_refs(submodule, revs, flags, for_each_remote_ref_submodule);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--glob=")) {
+                       if ((argcount = parse_long_opt("glob", argv + i, &optarg))) {
                                struct all_refs_cb cb;
+                               i += argcount - 1;
                                init_all_refs_cb(&cb, revs, flags);
-                               for_each_glob_ref(handle_one_ref, arg + 7, &cb);
+                               for_each_glob_ref(handle_one_ref, optarg, &cb);
                                continue;
                        }
                        if (!prefixcmp(arg, "--branches=")) {
@@ -1866,7 +1947,7 @@ int prepare_revision_walk(struct rev_info *revs)
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
-                               insert_by_date(commit, &revs->commits);
+                               commit_list_insert_by_date(commit, &revs->commits);
                        }
                }
                e++;
@@ -1960,10 +2041,15 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
                return commit_ignore;
        if (revs->min_age != -1 && (commit->date > revs->min_age))
                return commit_ignore;
-       if (revs->no_merges && commit->parents && commit->parents->next)
-               return commit_ignore;
-       if (revs->merges_only && !(commit->parents && commit->parents->next))
-               return commit_ignore;
+       if (revs->min_parents || (revs->max_parents >= 0)) {
+               int n = 0;
+               struct commit_list *p;
+               for (p = commit->parents; p; p = p->next)
+                       n++;
+               if ((n < revs->min_parents) ||
+                   ((revs->max_parents >= 0) && (n > revs->max_parents)))
+                       return commit_ignore;
+       }
        if (!commit_match(commit, revs))
                return commit_ignore;
        if (revs->prune && revs->dense) {
@@ -2005,8 +2091,10 @@ static struct commit *get_revision_1(struct rev_info *revs)
                revs->commits = entry->next;
                free(entry);
 
-               if (revs->reflog_info)
+               if (revs->reflog_info) {
                        fake_reflog_parent(revs->reflog_info, commit);
+                       commit->object.flags &= ~(ADDED | SEEN | SHOWN);
+               }
 
                /*
                 * If we haven't done the list limiting, we need to look at
@@ -2208,3 +2296,32 @@ struct commit *get_revision(struct rev_info *revs)
                graph_update(revs->graph, c);
        return c;
 }
+
+char *get_revision_mark(const struct rev_info *revs, const struct commit *commit)
+{
+       if (commit->object.flags & BOUNDARY)
+               return "-";
+       else if (commit->object.flags & UNINTERESTING)
+               return "^";
+       else if (commit->object.flags & PATCHSAME)
+               return "=";
+       else if (!revs || revs->left_right) {
+               if (commit->object.flags & SYMMETRIC_LEFT)
+                       return "<";
+               else
+                       return ">";
+       } else if (revs->graph)
+               return "*";
+       else if (revs->cherry_mark)
+               return "+";
+       return "";
+}
+
+void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
+{
+       char *mark = get_revision_mark(revs, commit);
+       if (!strlen(mark))
+               return;
+       fputs(mark, stdout);
+       putchar(' ');
+}