Git 2.23
[gitweb.git] / builtin / merge-base.c
index 57ac472716145d14d29421bf3469f9376295f3c6..e3f8da13b69b8dbe6898bf71ee2b5df206179bd1 100644 (file)
@@ -1,43 +1,50 @@
 #include "builtin.h"
 #include "cache.h"
+#include "config.h"
 #include "commit.h"
+#include "refs.h"
+#include "diff.h"
+#include "revision.h"
 #include "parse-options.h"
+#include "repository.h"
+#include "commit-reach.h"
 
 static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
-       struct commit_list *result;
+       struct commit_list *result, *r;
 
-       result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
+       result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1);
 
        if (!result)
                return 1;
 
-       while (result) {
-               printf("%s\n", sha1_to_hex(result->item->object.sha1));
+       for (r = result; r; r = r->next) {
+               printf("%s\n", oid_to_hex(&r->item->object.oid));
                if (!show_all)
-                       return 0;
-               result = result->next;
+                       break;
        }
 
+       free_commit_list(result);
        return 0;
 }
 
 static const char * const merge_base_usage[] = {
-       N_("git merge-base [-a|--all] <commit> <commit>..."),
-       N_("git merge-base [-a|--all] --octopus <commit>..."),
+       N_("git merge-base [-a | --all] <commit> <commit>..."),
+       N_("git merge-base [-a | --all] --octopus <commit>..."),
        N_("git merge-base --independent <commit>..."),
        N_("git merge-base --is-ancestor <commit> <commit>"),
+       N_("git merge-base --fork-point <ref> [<commit>]"),
        NULL
 };
 
 static struct commit *get_commit_reference(const char *arg)
 {
-       unsigned char revkey[20];
+       struct object_id revkey;
        struct commit *r;
 
-       if (get_sha1(arg, revkey))
+       if (get_oid(arg, &revkey))
                die("Not a valid object name %s", arg);
-       r = lookup_commit_reference(revkey);
+       r = lookup_commit_reference(the_repository, &revkey);
        if (!r)
                die("Not a valid commit name %s", arg);
 
@@ -46,45 +53,47 @@ static struct commit *get_commit_reference(const char *arg)
 
 static int handle_independent(int count, const char **args)
 {
-       struct commit_list *revs = NULL;
-       struct commit_list *result;
+       struct commit_list *revs = NULL, *rev;
        int i;
 
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       result = reduce_heads(revs);
-       if (!result)
+       reduce_heads_replace(&revs);
+
+       if (!revs)
                return 1;
 
-       while (result) {
-               printf("%s\n", sha1_to_hex(result->item->object.sha1));
-               result = result->next;
-       }
+       for (rev = revs; rev; rev = rev->next)
+               printf("%s\n", oid_to_hex(&rev->item->object.oid));
+
+       free_commit_list(revs);
        return 0;
 }
 
 static int handle_octopus(int count, const char **args, int show_all)
 {
        struct commit_list *revs = NULL;
-       struct commit_list *result;
+       struct commit_list *result, *rev;
        int i;
 
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       result = reduce_heads(get_octopus_merge_bases(revs));
+       result = get_octopus_merge_bases(revs);
+       free_commit_list(revs);
+       reduce_heads_replace(&result);
 
        if (!result)
                return 1;
 
-       while (result) {
-               printf("%s\n", sha1_to_hex(result->item->object.sha1));
+       for (rev = result; rev; rev = rev->next) {
+               printf("%s\n", oid_to_hex(&rev->item->object.oid));
                if (!show_all)
-                       return 0;
-               result = result->next;
+                       break;
        }
 
+       free_commit_list(result);
        return 0;
 }
 
@@ -102,41 +111,87 @@ static int handle_is_ancestor(int argc, const char **argv)
                return 1;
 }
 
+static int handle_fork_point(int argc, const char **argv)
+{
+       struct object_id oid;
+       char *refname;
+       struct commit *derived, *fork_point;
+       const char *commitname;
+
+       switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) {
+       case 0:
+               die("No such ref: '%s'", argv[0]);
+       case 1:
+               break; /* good */
+       default:
+               die("Ambiguous refname: '%s'", argv[0]);
+       }
+
+       commitname = (argc == 2) ? argv[1] : "HEAD";
+       if (get_oid(commitname, &oid))
+               die("Not a valid object name: '%s'", commitname);
+
+       derived = lookup_commit_reference(the_repository, &oid);
+
+       fork_point = get_fork_point(refname, derived);
+
+       if (!fork_point)
+               return 1;
+
+       printf("%s\n", oid_to_hex(&fork_point->object.oid));
+       return 0;
+}
+
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
 {
        struct commit **rev;
        int rev_nr = 0;
        int show_all = 0;
-       int octopus = 0;
-       int reduce = 0;
-       int is_ancestor = 0;
+       int cmdmode = 0;
 
        struct option options[] = {
                OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")),
-               OPT_BOOL(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")),
-               OPT_BOOL(0, "independent", &reduce, N_("list revs not reachable from others")),
-               OPT_BOOL(0, "is-ancestor", &is_ancestor,
-                        N_("is the first one ancestor of the other?")),
+               OPT_CMDMODE(0, "octopus", &cmdmode,
+                           N_("find ancestors for a single n-way merge"), 'o'),
+               OPT_CMDMODE(0, "independent", &cmdmode,
+                           N_("list revs not reachable from others"), 'r'),
+               OPT_CMDMODE(0, "is-ancestor", &cmdmode,
+                           N_("is the first one ancestor of the other?"), 'a'),
+               OPT_CMDMODE(0, "fork-point", &cmdmode,
+                           N_("find where <commit> forked from reflog of <ref>"), 'f'),
                OPT_END()
        };
 
        git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
-       if (!octopus && !reduce && argc < 2)
-               usage_with_options(merge_base_usage, options);
-       if (is_ancestor && (show_all || octopus || reduce))
-               die("--is-ancestor cannot be used with other options");
-       if (is_ancestor)
+
+       if (cmdmode == 'a') {
+               if (argc < 2)
+                       usage_with_options(merge_base_usage, options);
+               if (show_all)
+                       die("--is-ancestor cannot be used with --all");
                return handle_is_ancestor(argc, argv);
-       if (reduce && (show_all || octopus))
-               die("--independent cannot be used with other options");
+       }
 
-       if (octopus)
+       if (cmdmode == 'r' && show_all)
+               die("--independent cannot be used with --all");
+
+       if (cmdmode == 'o')
                return handle_octopus(argc, argv, show_all);
-       else if (reduce)
+
+       if (cmdmode == 'r')
                return handle_independent(argc, argv);
 
-       rev = xmalloc(argc * sizeof(*rev));
+       if (cmdmode == 'f') {
+               if (argc < 1 || 2 < argc)
+                       usage_with_options(merge_base_usage, options);
+               return handle_fork_point(argc, argv);
+       }
+
+       if (argc < 2)
+               usage_with_options(merge_base_usage, options);
+
+       ALLOC_ARRAY(rev, argc);
        while (argc-- > 0)
                rev[rev_nr++] = get_commit_reference(*argv++);
        return show_merge_base(rev, rev_nr, show_all);