diff options
Diffstat (limited to 'builtin/merge-base.c')
| -rw-r--r-- | builtin/merge-base.c | 109 | 
1 files changed, 109 insertions, 0 deletions
| diff --git a/builtin/merge-base.c b/builtin/merge-base.c index d39c91023e..a2923235e1 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -1,6 +1,9 @@  #include "builtin.h"  #include "cache.h"  #include "commit.h" +#include "refs.h" +#include "diff.h" +#include "revision.h"  #include "parse-options.h"  static int show_merge_base(struct commit **rev, int rev_nr, int show_all) @@ -27,6 +30,7 @@ static const char * const merge_base_usage[] = {  	N_("git merge-base [-a|--all] --octopus <commit>..."),  	N_("git merge-base --independent <commit>..."),  	N_("git merge-base --is-ancestor <commit> <commit>"), +	N_("git merge-base --fork-point <ref> [<commit>]"),  	NULL  }; @@ -85,6 +89,103 @@ 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(unsigned char *sha1, struct rev_collect *revs) +{ +	struct commit *commit; + +	if (is_null_sha1(sha1)) +		return; + +	commit = lookup_commit(sha1); +	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(unsigned char *osha1, unsigned char *nsha1, +				  const char *ident, unsigned long timestamp, +				  int tz, const char *message, void *cbdata) +{ +	struct rev_collect *revs = cbdata; + +	if (revs->initial) { +		revs->initial = 0; +		add_one_commit(osha1, revs); +	} +	add_one_commit(nsha1, revs); +	return 0; +} + +static int handle_fork_point(int argc, const char **argv) +{ +	unsigned char sha1[20]; +	char *refname; +	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]), sha1, &refname)) { +	case 0: +		die("No such ref: '%s'", argv[0]); +	case 1: +		break; /* good */ +	default: +		die("Ambiguous refname: '%s'", argv[0]); +	} + +	commitname = (argc == 2) ? argv[1] : "HEAD"; +	if (get_sha1(commitname, sha1)) +		die("Not a valid object name: '%s'", commitname); + +	derived = lookup_commit_reference(sha1); +	memset(&revs, 0, sizeof(revs)); +	revs.initial = 1; +	for_each_reflog_ent(refname, collect_one_reflog_ent, &revs); + +	for (i = 0; i < revs.nr; i++) +		revs.commit[i]->object.flags &= ~TMP_MARK; + +	bases = get_merge_bases_many(derived, revs.nr, revs.commit, 0); + +	/* +	 * 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", sha1_to_hex(bases->item->object.sha1)); + +cleanup_return: +	free_commit_list(bases); +	return ret; +} +  int cmd_merge_base(int argc, const char **argv, const char *prefix)  {  	struct commit **rev; @@ -100,6 +201,8 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)  			    N_("list revs not reachable from others"), 'r'),  		OPT_CMDMODE(0, "is-ancestor", &cmdmode,  			    N_("is the first one ancestor of the other?"), 'a'), +		OPT_CMDMODE(0, "fork-point", &cmdmode, +			    N_("find where <commit> forked from reflog of <ref>"), 'f'),  		OPT_END()  	}; @@ -120,6 +223,12 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)  	if (cmdmode == 'r' || cmdmode == 'o')  		return handle_octopus(argc, argv, cmdmode == 'r', show_all); +	if (cmdmode == 'f') { +		if (argc < 1 || 2 < argc) +			usage_with_options(merge_base_usage, options); +		return handle_fork_point(argc, argv); +	} +  	if (argc < 2)  		usage_with_options(merge_base_usage, options); | 
