merge-base --fork-point: extract libified function

We need this functionality in the builtin rebase. Note: to make this function truly reusable, we have to switch the call get_merges_many_dirty() to get_merges_many() because we want the commit flags to be reset (otherwise, subsequent get_merge_bases() calls would obtain incorrect results). This did not matter when the function was called in `git rev-parse --fork-point` because in that command, the process definitely did not traverse any commits before exiting. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Pratik Karki committed Sep 4, 2018 at 15:00 UTC 103148aad80ee7ab65b902d77d964260cfd3f6ea
3 files changed +89 -75
builtin/merge-base.c
+6 -75
@@ -110,54 +110,12 @@ static int handle_is_ancestor(int argc, const char **argv)
110 return 1;
111 }
112
113 -struct rev_collect {
114 - struct commit **commit;
115 - int nr;
116 - int alloc;
117 - unsigned int initial : 1;
118 -};
119 -
120 -static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
121 -{
122 - struct commit *commit;
123 -
124 - if (is_null_oid(oid))
125 - return;
126 -
127 - commit = lookup_commit(the_repository, oid);
128 - if (!commit ||
129 - (commit->object.flags & TMP_MARK) ||
130 - parse_commit(commit))
131 - return;
132 -
133 - ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
134 - revs->commit[revs->nr++] = commit;
135 - commit->object.flags |= TMP_MARK;
136 -}
137 -
138 -static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
139 - const char *ident, timestamp_t timestamp,
140 - int tz, const char *message, void *cbdata)
141 -{
142 - struct rev_collect *revs = cbdata;
143 -
144 - if (revs->initial) {
145 - revs->initial = 0;
146 - add_one_commit(ooid, revs);
147 - }
148 - add_one_commit(noid, revs);
149 - return 0;
150 -}
151 -
113 static int handle_fork_point(int argc, const char **argv)
114 {
115 struct object_id oid;
116 char *refname;
117 + struct commit *derived, *fork_point;
118 const char *commitname;
157 - struct rev_collect revs;
158 - struct commit *derived;
159 - struct commit_list *bases;
160 - int i, ret = 0;
119
120 switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) {
121 case 0:
@@ -173,41 +131,14 @@ static int handle_fork_point(int argc, const char **argv)
131 die("Not a valid object name: '%s'", commitname);
132
133 derived = lookup_commit_reference(the_repository, &oid);
176 - memset(&revs, 0, sizeof(revs));
177 - revs.initial = 1;
178 - for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
134
180 - if (!revs.nr && !get_oid(refname, &oid))
181 - add_one_commit(&oid, &revs);
135 + fork_point = get_fork_point(refname, derived);
136
183 - for (i = 0; i < revs.nr; i++)
184 - revs.commit[i]->object.flags &= ~TMP_MARK;
185 -
186 - bases = get_merge_bases_many_dirty(derived, revs.nr, revs.commit);
187 -
188 - /*
189 - * There should be one and only one merge base, when we found
190 - * a common ancestor among reflog entries.
191 - */
192 - if (!bases || bases->next) {
193 - ret = 1;
194 - goto cleanup_return;
195 - }
196 -
197 - /* And the found one must be one of the reflog entries */
198 - for (i = 0; i < revs.nr; i++)
199 - if (&bases->item->object == &revs.commit[i]->object)
200 - break; /* found */
201 - if (revs.nr <= i) {
202 - ret = 1; /* not found */
203 - goto cleanup_return;
204 - }
205 -
206 - printf("%s\n", oid_to_hex(&bases->item->object.oid));
137 + if (!fork_point)
138 + return 1;
139
208 -cleanup_return:
209 - free_commit_list(bases);
210 - return ret;
140 + printf("%s\n", oid_to_hex(&fork_point->object.oid));
141 + return 0;
142 }
143
144 int cmd_merge_base(int argc, const char **argv, const char *prefix)
commit.c
+81
@@ -17,6 +17,7 @@
17 #include "sha1-lookup.h"
18 #include "wt-status.h"
19 #include "advice.h"
20 +#include "refs.h"
21
22 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
23
@@ -958,6 +959,86 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
959 return result;
960 }
961
962 +struct rev_collect {
963 + struct commit **commit;
964 + int nr;
965 + int alloc;
966 + unsigned int initial : 1;
967 +};
968 +
969 +static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
970 +{
971 + struct commit *commit;
972 +
973 + if (is_null_oid(oid))
974 + return;
975 +
976 + commit = lookup_commit(the_repository, oid);
977 + if (!commit ||
978 + (commit->object.flags & TMP_MARK) ||
979 + parse_commit(commit))
980 + return;
981 +
982 + ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
983 + revs->commit[revs->nr++] = commit;
984 + commit->object.flags |= TMP_MARK;
985 +}
986 +
987 +static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
988 + const char *ident, timestamp_t timestamp,
989 + int tz, const char *message, void *cbdata)
990 +{
991 + struct rev_collect *revs = cbdata;
992 +
993 + if (revs->initial) {
994 + revs->initial = 0;
995 + add_one_commit(ooid, revs);
996 + }
997 + add_one_commit(noid, revs);
998 + return 0;
999 +}
1000 +
1001 +struct commit *get_fork_point(const char *refname, struct commit *commit)
1002 +{
1003 + struct object_id oid;
1004 + struct rev_collect revs;
1005 + struct commit_list *bases;
1006 + int i;
1007 + struct commit *ret = NULL;
1008 +
1009 + memset(&revs, 0, sizeof(revs));
1010 + revs.initial = 1;
1011 + for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
1012 +
1013 + if (!revs.nr && !get_oid(refname, &oid))
1014 + add_one_commit(&oid, &revs);
1015 +
1016 + for (i = 0; i < revs.nr; i++)
1017 + revs.commit[i]->object.flags &= ~TMP_MARK;
1018 +
1019 + bases = get_merge_bases_many(commit, revs.nr, revs.commit);
1020 +
1021 + /*
1022 + * There should be one and only one merge base, when we found
1023 + * a common ancestor among reflog entries.
1024 + */
1025 + if (!bases || bases->next)
1026 + goto cleanup_return;
1027 +
1028 + /* And the found one must be one of the reflog entries */
1029 + for (i = 0; i < revs.nr; i++)
1030 + if (&bases->item->object == &revs.commit[i]->object)
1031 + break; /* found */
1032 + if (revs.nr <= i)
1033 + goto cleanup_return;
1034 +
1035 + ret = bases->item;
1036 +
1037 +cleanup_return:
1038 + free_commit_list(bases);
1039 + return ret;
1040 +}
1041 +
1042 struct commit_list *get_octopus_merge_bases(struct commit_list *in)
1043 {
1044 struct commit_list *i, *j, *k, *ret = NULL;
commit.h
+2
@@ -211,6 +211,8 @@ extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
211 /* To be used only when object flags after this call no longer matter */
212 extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos);
213
214 +struct commit *get_fork_point(const char *refname, struct commit *commit);
215 +
216 /* largest positive number a signed 32-bit integer can contain */
217 #define INFINITE_DEPTH 0x7fffffff
218