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