Peter Hagervall | baffc0e | 2007-07-15 01:14:45 +0200 | [diff] [blame] | 1 | #include "builtin.h" |
Linus Torvalds | 6683463 | 2005-04-17 12:18:17 -0700 | [diff] [blame] | 2 | #include "cache.h" |
Daniel Barkalow | b5039db | 2005-04-18 11:39:48 -0700 | [diff] [blame] | 3 | #include "commit.h" |
Junio C Hamano | d96855f | 2013-10-23 16:47:32 -0700 | [diff] [blame] | 4 | #include "refs.h" |
| 5 | #include "diff.h" |
| 6 | #include "revision.h" |
Pierre Habouzit | e5d1a4d | 2008-10-02 14:59:19 +0200 | [diff] [blame] | 7 | #include "parse-options.h" |
Linus Torvalds | 6683463 | 2005-04-17 12:18:17 -0700 | [diff] [blame] | 8 | |
Christian Couder | 53eda89 | 2008-07-30 07:04:14 +0200 | [diff] [blame] | 9 | static int show_merge_base(struct commit **rev, int rev_nr, int show_all) |
Johannes Schindelin | 52cab8a | 2006-06-29 15:16:46 +0200 | [diff] [blame] | 10 | { |
Christian Couder | 53eda89 | 2008-07-30 07:04:14 +0200 | [diff] [blame] | 11 | struct commit_list *result; |
| 12 | |
Junio C Hamano | 2ce406c | 2014-10-30 12:20:44 -0700 | [diff] [blame] | 13 | result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1); |
Johannes Schindelin | 52cab8a | 2006-06-29 15:16:46 +0200 | [diff] [blame] | 14 | |
| 15 | if (!result) |
| 16 | return 1; |
| 17 | |
Junio C Hamano | 9585e40 | 2005-08-23 21:08:59 -0700 | [diff] [blame] | 18 | while (result) { |
brian m. carlson | f2fd076 | 2015-11-10 02:22:28 +0000 | [diff] [blame] | 19 | printf("%s\n", oid_to_hex(&result->item->object.oid)); |
Junio C Hamano | 9585e40 | 2005-08-23 21:08:59 -0700 | [diff] [blame] | 20 | if (!show_all) |
| 21 | return 0; |
Johannes Schindelin | 52cab8a | 2006-06-29 15:16:46 +0200 | [diff] [blame] | 22 | result = result->next; |
Junio C Hamano | 9585e40 | 2005-08-23 21:08:59 -0700 | [diff] [blame] | 23 | } |
Johannes Schindelin | 52cab8a | 2006-06-29 15:16:46 +0200 | [diff] [blame] | 24 | |
Junio C Hamano | 9585e40 | 2005-08-23 21:08:59 -0700 | [diff] [blame] | 25 | return 0; |
Linus Torvalds | 6683463 | 2005-04-17 12:18:17 -0700 | [diff] [blame] | 26 | } |
| 27 | |
Pierre Habouzit | e5d1a4d | 2008-10-02 14:59:19 +0200 | [diff] [blame] | 28 | static const char * const merge_base_usage[] = { |
Alex Henrie | 9c9b4f2 | 2015-01-13 00:44:47 -0700 | [diff] [blame] | 29 | N_("git merge-base [-a | --all] <commit> <commit>..."), |
| 30 | N_("git merge-base [-a | --all] --octopus <commit>..."), |
Nguyễn Thái Ngọc Duy | f037dbf | 2012-08-20 19:32:22 +0700 | [diff] [blame] | 31 | N_("git merge-base --independent <commit>..."), |
Junio C Hamano | 34f5130 | 2012-09-11 11:35:26 -0700 | [diff] [blame] | 32 | N_("git merge-base --is-ancestor <commit> <commit>"), |
Junio C Hamano | d96855f | 2013-10-23 16:47:32 -0700 | [diff] [blame] | 33 | N_("git merge-base --fork-point <ref> [<commit>]"), |
Pierre Habouzit | e5d1a4d | 2008-10-02 14:59:19 +0200 | [diff] [blame] | 34 | NULL |
| 35 | }; |
Junio C Hamano | 9585e40 | 2005-08-23 21:08:59 -0700 | [diff] [blame] | 36 | |
Christian Couder | df57acc | 2008-07-29 07:42:53 +0200 | [diff] [blame] | 37 | static struct commit *get_commit_reference(const char *arg) |
| 38 | { |
| 39 | unsigned char revkey[20]; |
| 40 | struct commit *r; |
| 41 | |
| 42 | if (get_sha1(arg, revkey)) |
| 43 | die("Not a valid object name %s", arg); |
| 44 | r = lookup_commit_reference(revkey); |
| 45 | if (!r) |
| 46 | die("Not a valid commit name %s", arg); |
| 47 | |
| 48 | return r; |
| 49 | } |
| 50 | |
Junio C Hamano | e2f5df4 | 2013-12-30 11:37:49 -0800 | [diff] [blame] | 51 | static int handle_independent(int count, const char **args) |
Jonathan Nieder | aa8f98c | 2010-08-17 02:01:15 -0500 | [diff] [blame] | 52 | { |
| 53 | struct commit_list *revs = NULL; |
| 54 | struct commit_list *result; |
| 55 | int i; |
| 56 | |
Junio C Hamano | e2f5df4 | 2013-12-30 11:37:49 -0800 | [diff] [blame] | 57 | for (i = count - 1; i >= 0; i--) |
| 58 | commit_list_insert(get_commit_reference(args[i]), &revs); |
| 59 | |
| 60 | result = reduce_heads(revs); |
| 61 | if (!result) |
| 62 | return 1; |
| 63 | |
| 64 | while (result) { |
brian m. carlson | f2fd076 | 2015-11-10 02:22:28 +0000 | [diff] [blame] | 65 | printf("%s\n", oid_to_hex(&result->item->object.oid)); |
Junio C Hamano | e2f5df4 | 2013-12-30 11:37:49 -0800 | [diff] [blame] | 66 | result = result->next; |
| 67 | } |
| 68 | return 0; |
| 69 | } |
| 70 | |
| 71 | static int handle_octopus(int count, const char **args, int show_all) |
| 72 | { |
| 73 | struct commit_list *revs = NULL; |
| 74 | struct commit_list *result; |
| 75 | int i; |
Jonathan Nieder | a1e0ad7 | 2010-08-17 02:01:54 -0500 | [diff] [blame] | 76 | |
| 77 | for (i = count - 1; i >= 0; i--) |
Jonathan Nieder | aa8f98c | 2010-08-17 02:01:15 -0500 | [diff] [blame] | 78 | commit_list_insert(get_commit_reference(args[i]), &revs); |
Jonathan Nieder | a1e0ad7 | 2010-08-17 02:01:54 -0500 | [diff] [blame] | 79 | |
Junio C Hamano | 8f29299 | 2013-12-30 11:58:54 -0800 | [diff] [blame] | 80 | result = reduce_heads(get_octopus_merge_bases(revs)); |
Jonathan Nieder | aa8f98c | 2010-08-17 02:01:15 -0500 | [diff] [blame] | 81 | |
| 82 | if (!result) |
| 83 | return 1; |
| 84 | |
| 85 | while (result) { |
brian m. carlson | f2fd076 | 2015-11-10 02:22:28 +0000 | [diff] [blame] | 86 | printf("%s\n", oid_to_hex(&result->item->object.oid)); |
Jonathan Nieder | aa8f98c | 2010-08-17 02:01:15 -0500 | [diff] [blame] | 87 | if (!show_all) |
| 88 | return 0; |
| 89 | result = result->next; |
| 90 | } |
| 91 | |
| 92 | return 0; |
| 93 | } |
| 94 | |
Junio C Hamano | 5907cda | 2012-08-30 14:52:20 -0700 | [diff] [blame] | 95 | static int handle_is_ancestor(int argc, const char **argv) |
| 96 | { |
| 97 | struct commit *one, *two; |
| 98 | |
| 99 | if (argc != 2) |
| 100 | die("--is-ancestor takes exactly two commits"); |
| 101 | one = get_commit_reference(argv[0]); |
| 102 | two = get_commit_reference(argv[1]); |
| 103 | if (in_merge_bases(one, two)) |
| 104 | return 0; |
| 105 | else |
| 106 | return 1; |
| 107 | } |
| 108 | |
Junio C Hamano | d96855f | 2013-10-23 16:47:32 -0700 | [diff] [blame] | 109 | struct rev_collect { |
| 110 | struct commit **commit; |
| 111 | int nr; |
| 112 | int alloc; |
| 113 | unsigned int initial : 1; |
| 114 | }; |
| 115 | |
| 116 | static void add_one_commit(unsigned char *sha1, struct rev_collect *revs) |
| 117 | { |
| 118 | struct commit *commit; |
| 119 | |
| 120 | if (is_null_sha1(sha1)) |
| 121 | return; |
| 122 | |
| 123 | commit = lookup_commit(sha1); |
| 124 | if (!commit || |
| 125 | (commit->object.flags & TMP_MARK) || |
| 126 | parse_commit(commit)) |
| 127 | return; |
| 128 | |
| 129 | ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc); |
| 130 | revs->commit[revs->nr++] = commit; |
| 131 | commit->object.flags |= TMP_MARK; |
| 132 | } |
| 133 | |
| 134 | static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, |
| 135 | const char *ident, unsigned long timestamp, |
| 136 | int tz, const char *message, void *cbdata) |
| 137 | { |
| 138 | struct rev_collect *revs = cbdata; |
| 139 | |
| 140 | if (revs->initial) { |
| 141 | revs->initial = 0; |
| 142 | add_one_commit(osha1, revs); |
| 143 | } |
| 144 | add_one_commit(nsha1, revs); |
| 145 | return 0; |
| 146 | } |
| 147 | |
| 148 | static int handle_fork_point(int argc, const char **argv) |
| 149 | { |
| 150 | unsigned char sha1[20]; |
| 151 | char *refname; |
| 152 | const char *commitname; |
| 153 | struct rev_collect revs; |
| 154 | struct commit *derived; |
| 155 | struct commit_list *bases; |
| 156 | int i, ret = 0; |
| 157 | |
| 158 | switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) { |
| 159 | case 0: |
| 160 | die("No such ref: '%s'", argv[0]); |
| 161 | case 1: |
| 162 | break; /* good */ |
| 163 | default: |
| 164 | die("Ambiguous refname: '%s'", argv[0]); |
| 165 | } |
| 166 | |
| 167 | commitname = (argc == 2) ? argv[1] : "HEAD"; |
| 168 | if (get_sha1(commitname, sha1)) |
| 169 | die("Not a valid object name: '%s'", commitname); |
| 170 | |
| 171 | derived = lookup_commit_reference(sha1); |
| 172 | memset(&revs, 0, sizeof(revs)); |
| 173 | revs.initial = 1; |
| 174 | for_each_reflog_ent(refname, collect_one_reflog_ent, &revs); |
| 175 | |
Jeff King | 4f21454 | 2016-10-12 16:10:40 -0400 | [diff] [blame] | 176 | if (!revs.nr && !get_sha1(refname, sha1)) |
| 177 | add_one_commit(sha1, &revs); |
| 178 | |
Junio C Hamano | d96855f | 2013-10-23 16:47:32 -0700 | [diff] [blame] | 179 | for (i = 0; i < revs.nr; i++) |
| 180 | revs.commit[i]->object.flags &= ~TMP_MARK; |
| 181 | |
Junio C Hamano | 2ce406c | 2014-10-30 12:20:44 -0700 | [diff] [blame] | 182 | bases = get_merge_bases_many_dirty(derived, revs.nr, revs.commit); |
Junio C Hamano | d96855f | 2013-10-23 16:47:32 -0700 | [diff] [blame] | 183 | |
| 184 | /* |
| 185 | * There should be one and only one merge base, when we found |
| 186 | * a common ancestor among reflog entries. |
| 187 | */ |
| 188 | if (!bases || bases->next) { |
| 189 | ret = 1; |
| 190 | goto cleanup_return; |
| 191 | } |
| 192 | |
| 193 | /* And the found one must be one of the reflog entries */ |
| 194 | for (i = 0; i < revs.nr; i++) |
| 195 | if (&bases->item->object == &revs.commit[i]->object) |
| 196 | break; /* found */ |
| 197 | if (revs.nr <= i) { |
| 198 | ret = 1; /* not found */ |
| 199 | goto cleanup_return; |
| 200 | } |
| 201 | |
brian m. carlson | f2fd076 | 2015-11-10 02:22:28 +0000 | [diff] [blame] | 202 | printf("%s\n", oid_to_hex(&bases->item->object.oid)); |
Junio C Hamano | d96855f | 2013-10-23 16:47:32 -0700 | [diff] [blame] | 203 | |
| 204 | cleanup_return: |
| 205 | free_commit_list(bases); |
| 206 | return ret; |
| 207 | } |
| 208 | |
Junio C Hamano | 71dfbf2 | 2007-01-09 00:50:02 -0800 | [diff] [blame] | 209 | int cmd_merge_base(int argc, const char **argv, const char *prefix) |
Linus Torvalds | 6683463 | 2005-04-17 12:18:17 -0700 | [diff] [blame] | 210 | { |
Christian Couder | 53eda89 | 2008-07-30 07:04:14 +0200 | [diff] [blame] | 211 | struct commit **rev; |
| 212 | int rev_nr = 0; |
Junio C Hamano | 71dfbf2 | 2007-01-09 00:50:02 -0800 | [diff] [blame] | 213 | int show_all = 0; |
Junio C Hamano | 16e57ae | 2013-10-23 16:10:25 -0700 | [diff] [blame] | 214 | int cmdmode = 0; |
Linus Torvalds | 6683463 | 2005-04-17 12:18:17 -0700 | [diff] [blame] | 215 | |
Pierre Habouzit | e5d1a4d | 2008-10-02 14:59:19 +0200 | [diff] [blame] | 216 | struct option options[] = { |
Stefan Beller | d5d09d4 | 2013-08-03 13:51:19 +0200 | [diff] [blame] | 217 | OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")), |
Junio C Hamano | 16e57ae | 2013-10-23 16:10:25 -0700 | [diff] [blame] | 218 | OPT_CMDMODE(0, "octopus", &cmdmode, |
| 219 | N_("find ancestors for a single n-way merge"), 'o'), |
| 220 | OPT_CMDMODE(0, "independent", &cmdmode, |
| 221 | N_("list revs not reachable from others"), 'r'), |
| 222 | OPT_CMDMODE(0, "is-ancestor", &cmdmode, |
| 223 | N_("is the first one ancestor of the other?"), 'a'), |
Junio C Hamano | d96855f | 2013-10-23 16:47:32 -0700 | [diff] [blame] | 224 | OPT_CMDMODE(0, "fork-point", &cmdmode, |
| 225 | N_("find where <commit> forked from reflog of <ref>"), 'f'), |
Pierre Habouzit | e5d1a4d | 2008-10-02 14:59:19 +0200 | [diff] [blame] | 226 | OPT_END() |
| 227 | }; |
| 228 | |
Johannes Schindelin | ef90d6d | 2008-05-14 18:46:53 +0100 | [diff] [blame] | 229 | git_config(git_default_config, NULL); |
Stephen Boyd | 3778292 | 2009-05-23 11:53:12 -0700 | [diff] [blame] | 230 | argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); |
Jonathan Nieder | aa8f98c | 2010-08-17 02:01:15 -0500 | [diff] [blame] | 231 | |
Junio C Hamano | 16e57ae | 2013-10-23 16:10:25 -0700 | [diff] [blame] | 232 | if (cmdmode == 'a') { |
| 233 | if (argc < 2) |
| 234 | usage_with_options(merge_base_usage, options); |
| 235 | if (show_all) |
| 236 | die("--is-ancestor cannot be used with --all"); |
| 237 | return handle_is_ancestor(argc, argv); |
| 238 | } |
| 239 | |
| 240 | if (cmdmode == 'r' && show_all) |
| 241 | die("--independent cannot be used with --all"); |
| 242 | |
Junio C Hamano | d5d1678 | 2014-01-10 10:33:32 -0800 | [diff] [blame] | 243 | if (cmdmode == 'o') |
Junio C Hamano | e2f5df4 | 2013-12-30 11:37:49 -0800 | [diff] [blame] | 244 | return handle_octopus(argc, argv, show_all); |
Junio C Hamano | d5d1678 | 2014-01-10 10:33:32 -0800 | [diff] [blame] | 245 | |
| 246 | if (cmdmode == 'r') |
Junio C Hamano | e2f5df4 | 2013-12-30 11:37:49 -0800 | [diff] [blame] | 247 | return handle_independent(argc, argv); |
Junio C Hamano | 16e57ae | 2013-10-23 16:10:25 -0700 | [diff] [blame] | 248 | |
Junio C Hamano | d96855f | 2013-10-23 16:47:32 -0700 | [diff] [blame] | 249 | if (cmdmode == 'f') { |
| 250 | if (argc < 1 || 2 < argc) |
| 251 | usage_with_options(merge_base_usage, options); |
| 252 | return handle_fork_point(argc, argv); |
| 253 | } |
| 254 | |
Junio C Hamano | 16e57ae | 2013-10-23 16:10:25 -0700 | [diff] [blame] | 255 | if (argc < 2) |
| 256 | usage_with_options(merge_base_usage, options); |
Jonathan Nieder | aa8f98c | 2010-08-17 02:01:15 -0500 | [diff] [blame] | 257 | |
Jeff King | b32fa95 | 2016-02-22 17:44:25 -0500 | [diff] [blame] | 258 | ALLOC_ARRAY(rev, argc); |
Pierre Habouzit | e5d1a4d | 2008-10-02 14:59:19 +0200 | [diff] [blame] | 259 | while (argc-- > 0) |
| 260 | rev[rev_nr++] = get_commit_reference(*argv++); |
Christian Couder | 53eda89 | 2008-07-30 07:04:14 +0200 | [diff] [blame] | 261 | return show_merge_base(rev, rev_nr, show_all); |
Linus Torvalds | 6683463 | 2005-04-17 12:18:17 -0700 | [diff] [blame] | 262 | } |