1
#include "builtin.h"
2
#include "cache.h"
3
#include "commit.h"
4
+#include "refs.h"
5
+#include "diff.h"
6
+#include "revision.h"
7
#include "parse-options.h"
8
9
static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
30
N_("git merge-base [-a|--all] --octopus <commit>..."),
31
N_("git merge-base --independent <commit>..."),
32
N_("git merge-base --is-ancestor <commit> <commit>"),
33
+ N_("git merge-base --fork-point <ref> [<commit>]"),
34
NULL
35
};
36
89
return 1;
90
}
91
92
+struct rev_collect {
93
+ struct commit **commit;
94
+ int nr;
95
+ int alloc;
96
+ unsigned int initial : 1;
97
+};
98
+
99
+static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
100
+{
101
+ struct commit *commit;
102
+
103
+ if (is_null_sha1(sha1))
104
+ return;
105
+
106
+ commit = lookup_commit(sha1);
107
+ if (!commit ||
108
+ (commit->object.flags & TMP_MARK) ||
109
+ parse_commit(commit))
110
+ return;
111
+
112
+ ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
113
+ revs->commit[revs->nr++] = commit;
114
+ commit->object.flags |= TMP_MARK;
115
+}
116
+
117
+static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
118
+ const char *ident, unsigned long timestamp,
119
+ int tz, const char *message, void *cbdata)
120
+{
121
+ struct rev_collect *revs = cbdata;
122
+
123
+ if (revs->initial) {
124
+ revs->initial = 0;
125
+ add_one_commit(osha1, revs);
126
+ }
127
+ add_one_commit(nsha1, revs);
128
+ return 0;
129
+}
130
+
131
+static int handle_fork_point(int argc, const char **argv)
132
+{
133
+ unsigned char sha1[20];
134
+ char *refname;
135
+ const char *commitname;
136
+ struct rev_collect revs;
137
+ struct commit *derived;
138
+ struct commit_list *bases;
139
+ int i, ret = 0;
140
+
141
+ switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) {
142
+ case 0:
143
+ die("No such ref: '%s'", argv[0]);
144
+ case 1:
145
+ break; /* good */
146
+ default:
147
+ die("Ambiguous refname: '%s'", argv[0]);
148
+ }
149
+
150
+ commitname = (argc == 2) ? argv[1] : "HEAD";
151
+ if (get_sha1(commitname, sha1))
152
+ die("Not a valid object name: '%s'", commitname);
153
+
154
+ derived = lookup_commit_reference(sha1);
155
+ memset(&revs, 0, sizeof(revs));
156
+ revs.initial = 1;
157
+ for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
158
+
159
+ for (i = 0; i < revs.nr; i++)
160
+ revs.commit[i]->object.flags &= ~TMP_MARK;
161
+
162
+ bases = get_merge_bases_many(derived, revs.nr, revs.commit, 0);
163
+
164
+ /*
165
+ * There should be one and only one merge base, when we found
166
+ * a common ancestor among reflog entries.
167
+ */
168
+ if (!bases || bases->next) {
169
+ ret = 1;
170
+ goto cleanup_return;
171
+ }
172
+
173
+ /* And the found one must be one of the reflog entries */
174
+ for (i = 0; i < revs.nr; i++)
175
+ if (&bases->item->object == &revs.commit[i]->object)
176
+ break; /* found */
177
+ if (revs.nr <= i) {
178
+ ret = 1; /* not found */
179
+ goto cleanup_return;
180
+ }
181
+
182
+ printf("%s\n", sha1_to_hex(bases->item->object.sha1));
183
+
184
+cleanup_return:
185
+ free_commit_list(bases);
186
+ return ret;
187
+}
188
+
189
int cmd_merge_base(int argc, const char **argv, const char *prefix)
190
{
191
struct commit **rev;
201
N_("list revs not reachable from others"), 'r'),
202
OPT_CMDMODE(0, "is-ancestor", &cmdmode,
203
N_("is the first one ancestor of the other?"), 'a'),
204
+ OPT_CMDMODE(0, "fork-point", &cmdmode,
205
+ N_("find where <commit> forked from reflog of <ref>"), 'f'),
206
OPT_END()
207
};
208
223
if (cmdmode == 'r' || cmdmode == 'o')
224
return handle_octopus(argc, argv, cmdmode == 'r', show_all);
225
226
+ if (cmdmode == 'f') {
227
+ if (argc < 1 || 2 < argc)
228
+ usage_with_options(merge_base_usage, options);
229
+ return handle_fork_point(argc, argv);
230
+ }
231
+
232
if (argc < 2)
233
usage_with_options(merge_base_usage, options);
234