From: Pratik Karki <predatoramigo@xxxxxxxxx> 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@xxxxxxxxx> Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> --- builtin/merge-base.c | 81 ++++---------------------------------------- commit.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ commit.h | 2 ++ 3 files changed, 89 insertions(+), 75 deletions(-) diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 08d91b1f0c..790ceaeed6 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -110,54 +110,12 @@ static int handle_is_ancestor(int argc, const char **argv) return 1; } -struct rev_collect { - struct commit **commit; - int nr; - int alloc; - unsigned int initial : 1; -}; - -static void add_one_commit(struct object_id *oid, struct rev_collect *revs) -{ - struct commit *commit; - - if (is_null_oid(oid)) - return; - - commit = lookup_commit(the_repository, oid); - if (!commit || - (commit->object.flags & TMP_MARK) || - parse_commit(commit)) - return; - - ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc); - revs->commit[revs->nr++] = commit; - commit->object.flags |= TMP_MARK; -} - -static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *ident, timestamp_t timestamp, - int tz, const char *message, void *cbdata) -{ - struct rev_collect *revs = cbdata; - - if (revs->initial) { - revs->initial = 0; - add_one_commit(ooid, revs); - } - add_one_commit(noid, revs); - return 0; -} - static int handle_fork_point(int argc, const char **argv) { struct object_id oid; char *refname; + struct commit *derived, *fork_point; const char *commitname; - struct rev_collect revs; - struct commit *derived; - struct commit_list *bases; - int i, ret = 0; switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) { case 0: @@ -173,41 +131,14 @@ static int handle_fork_point(int argc, const char **argv) die("Not a valid object name: '%s'", commitname); derived = lookup_commit_reference(the_repository, &oid); - memset(&revs, 0, sizeof(revs)); - revs.initial = 1; - for_each_reflog_ent(refname, collect_one_reflog_ent, &revs); - if (!revs.nr && !get_oid(refname, &oid)) - add_one_commit(&oid, &revs); + fork_point = get_fork_point(refname, derived); - for (i = 0; i < revs.nr; i++) - revs.commit[i]->object.flags &= ~TMP_MARK; - - bases = get_merge_bases_many_dirty(derived, revs.nr, revs.commit); - - /* - * There should be one and only one merge base, when we found - * a common ancestor among reflog entries. - */ - if (!bases || bases->next) { - ret = 1; - goto cleanup_return; - } - - /* And the found one must be one of the reflog entries */ - for (i = 0; i < revs.nr; i++) - if (&bases->item->object == &revs.commit[i]->object) - break; /* found */ - if (revs.nr <= i) { - ret = 1; /* not found */ - goto cleanup_return; - } - - printf("%s\n", oid_to_hex(&bases->item->object.oid)); + if (!fork_point) + return 1; -cleanup_return: - free_commit_list(bases); - return ret; + printf("%s\n", oid_to_hex(&fork_point->object.oid)); + return 0; } int cmd_merge_base(int argc, const char **argv, const char *prefix) diff --git a/commit.c b/commit.c index 30d1af2b20..a3fc77a4eb 100644 --- a/commit.c +++ b/commit.c @@ -17,6 +17,7 @@ #include "sha1-lookup.h" #include "wt-status.h" #include "advice.h" +#include "refs.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); @@ -958,6 +959,86 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co return result; } +struct rev_collect { + struct commit **commit; + int nr; + int alloc; + unsigned int initial : 1; +}; + +static void add_one_commit(struct object_id *oid, struct rev_collect *revs) +{ + struct commit *commit; + + if (is_null_oid(oid)) + return; + + commit = lookup_commit(the_repository, oid); + if (!commit || + (commit->object.flags & TMP_MARK) || + parse_commit(commit)) + return; + + ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc); + revs->commit[revs->nr++] = commit; + commit->object.flags |= TMP_MARK; +} + +static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid, + const char *ident, timestamp_t timestamp, + int tz, const char *message, void *cbdata) +{ + struct rev_collect *revs = cbdata; + + if (revs->initial) { + revs->initial = 0; + add_one_commit(ooid, revs); + } + add_one_commit(noid, revs); + return 0; +} + +struct commit *get_fork_point(const char *refname, struct commit *commit) +{ + struct object_id oid; + struct rev_collect revs; + struct commit_list *bases; + int i; + struct commit *ret = NULL; + + memset(&revs, 0, sizeof(revs)); + revs.initial = 1; + for_each_reflog_ent(refname, collect_one_reflog_ent, &revs); + + if (!revs.nr && !get_oid(refname, &oid)) + add_one_commit(&oid, &revs); + + for (i = 0; i < revs.nr; i++) + revs.commit[i]->object.flags &= ~TMP_MARK; + + bases = get_merge_bases_many(commit, revs.nr, revs.commit); + + /* + * There should be one and only one merge base, when we found + * a common ancestor among reflog entries. + */ + if (!bases || bases->next) + goto cleanup_return; + + /* And the found one must be one of the reflog entries */ + for (i = 0; i < revs.nr; i++) + if (&bases->item->object == &revs.commit[i]->object) + break; /* found */ + if (revs.nr <= i) + goto cleanup_return; + + ret = bases->item; + +cleanup_return: + free_commit_list(bases); + return ret; +} + struct commit_list *get_octopus_merge_bases(struct commit_list *in) { struct commit_list *i, *j, *k, *ret = NULL; diff --git a/commit.h b/commit.h index da0db36eba..b34240017f 100644 --- a/commit.h +++ b/commit.h @@ -211,6 +211,8 @@ extern struct commit_list *get_octopus_merge_bases(struct commit_list *in); /* To be used only when object flags after this call no longer matter */ extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos); +struct commit *get_fork_point(const char *refname, struct commit *commit); + /* largest positive number a signed 32-bit integer can contain */ #define INFINITE_DEPTH 0x7fffffff -- gitgitgadget