git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [RFC/PATCHv8 00/10] git notes

From
Junio C Hamano <gitster@pobox.com>
Date
Nov 20, 2009, 10:46 UTC
Message-ID
<7vk4xl1nkl.fsf@alter.siamese.dyndns.org>
In-Reply-To
<20091120192800.6117@nanako3.lavabit.com>
Nanako Shiraishi <nanako3@lavabit.com> writes:
Show 14 quoted lines
>> Step 0.  Apply as you specified on top of 'next'
>>
>>     $ git checkout next^0
>>     $ git am -s your-10-patches
>>     $ M=$(git describe)
>>
>> Step 1.  Rebase back to the bottom of the old series
>>
>>     $ git checkout next...jh/notes
>
> What do three-dots mean in this command?
>
>>     $ git rebase --onto HEAD next $M
>>     $ N=$(git describe)
Heh, good eyes.
I forgot that I had this toy hidden from the general public.
-- >8 --
Subject: [PATCH] "checkout A...B" switches to the merge base between A and B

When flipping commits around on topic branches, I often end up doing this sequence:

 * Run "log --oneline next..jc/frotz" to find out the first commit
   on 'jc/frotz' branch not yet merged to 'next';
 * Run "checkout $that_commit^" to detach HEAD to the parent of it;
 * Rebuild the series on top of that commit; and
 * "show-branch jc/frotz HEAD" and "diff jc/frotz HEAD" to verify.

Introduce a new syntax to "git checkout" to name the commit to switch to, to make the first two steps easier. When the branch to switch to is specified as A...B (you can omit either A or B but not both, and HEAD is used instead of the omitted side), the merge base between these two commits are computed, and if there is one unique one, we detach the HEAD at that commit.

With this, I can say "checkout next...jc/frotz".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-checkout.c       |    7 +++++--
 cache.h                  |    1 +
 sha1_name.c              |   42 ++++++++++++++++++++++++++++++++++++++++++
 t/t2012-checkout-last.sh |   27 ++++++++++++++++++++++++++-
 4 files changed, 74 insertions(+), 3 deletions(-)
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 64f3a11..7167111 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -690,7 +690,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	 * case 3: git checkout <something> [<paths>]
 	 *
 	 *   With no paths, if <something> is a commit, that is to
-	 *   switch to the branch or detach HEAD at it.
+	 *   switch to the branch or detach HEAD at it.  As a special case,
+	 *   if <something> is A...B (missing A or B means HEAD but you can
+	 *   omit at most one side), and if there is a unique merge base
+	 *   between A and B, A...B names that merge base.
 	 *
 	 *   With no paths, if <something> is _not_ a commit, no -t nor -b
 	 *   was given, and there is a tracking branch whose name is
@@ -716,7 +719,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		if (!strcmp(arg, "-"))
 			arg = "@{-1}";
 
-		if (get_sha1(arg, rev)) {
+		if (get_sha1_mb(arg, rev)) {
 			if (has_dash_dash)          /* case (1) */
 				die("invalid reference: %s", arg);
 			if (!patch_mode &&
diff --git a/cache.h b/cache.h
index 71a731d..f8df15a 100644
--- a/cache.h
+++ b/cache.h
@@ -703,6 +703,7 @@ extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
 extern int interpret_branch_name(const char *str, struct strbuf *);
+extern int get_sha1_mb(const char *str, unsigned char *sha1);
 
 extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
 extern const char *ref_rev_parse_rules[];
diff --git a/sha1_name.c b/sha1_name.c
index 44bb62d..d5a240c 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -794,6 +794,48 @@ release_return:
 	return retval;
 }
 
+int get_sha1_mb(const char *name, unsigned char *sha1)
+{
+	struct commit *one, *two;
+	struct commit_list *mbs;
+	unsigned char sha1_tmp[20];
+	const char *dots;
+	int st;
+
+	dots = strstr(name, "...");
+	if (!dots)
+		return get_sha1(name, sha1);
+	if (dots == name)
+		st = get_sha1("HEAD", sha1_tmp);
+	else {
+		struct strbuf sb;
+		strbuf_init(&sb, dots - name);
+		strbuf_add(&sb, name, dots - name);
+		st = get_sha1(sb.buf, sha1_tmp);
+		strbuf_release(&sb);
+	}
+	if (st)
+		return st;
+	one = lookup_commit_reference_gently(sha1_tmp, 0);
+	if (!one)
+		return -1;
+
+	if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+		return -1;
+	two = lookup_commit_reference_gently(sha1_tmp, 0);
+	if (!two)
+		return -1;
+	mbs = get_merge_bases(one, two, 1);
+	if (!mbs || mbs->next)
+		st = -1;
+	else {
+		st = 0;
+		hashcpy(sha1, mbs->item->object.sha1);
+	}
+	free_commit_list(mbs);
+	return st;
+}
+
 /*
  * This is like "get_sha1_basic()", except it allows "sha1 expressions",
  * notably "xyz^" for "parent of xyz"
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
index 87b30a2..b44de9d 100755
--- a/t/t2012-checkout-last.sh
+++ b/t/t2012-checkout-last.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='checkout can switch to last branch'
+test_description='checkout can switch to last branch and merge base'
 
 . ./test-lib.sh
 
@@ -91,4 +91,29 @@ test_expect_success 'switch to twelfth from the last' '
 	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
 '
 
+test_expect_success 'merge base test setup' '
+	git checkout -b another other &&
+	echo "hello again" >>world &&
+	git add world &&
+	git commit -m third
+'
+
+test_expect_success 'another...master' '
+	git checkout another &&
+	git checkout another...master &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_expect_success '...master' '
+	git checkout another &&
+	git checkout ...master &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_expect_success 'master...' '
+	git checkout another &&
+	git checkout master... &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
 test_done
-- 
1.6.5.3.342.g14bb9
Previous: Johannes SchindelinNext: Junio C Hamano
Message 19 of 26 in “[RFC/PATCHv8 00/10] git notes”
  1. Johan HerlandNov 20, 2009
  2. 01/10 Notes API: get_commit_notes() -> format_note() + remove the commit restrictionJohan Herland, Nov 20, 2009
  3. 02/10 Notes API: init_notes(): Initialize the notes tree from the given notes refJohan Herland, Nov 20, 2009
  4. 03/10 Notes API: add_note(): Add note objects to the internal notes tree structureJohan Herland, Nov 20, 2009
  5. 04/10 Notes API: get_note(): Return the note annotating the given objectJohan Herland, Nov 20, 2009
  6. 05/10 Notes API: for_each_note(): Traverse the entire notes tree with a callbackJohan Herland, Nov 20, 2009
  7. 06/10 Notes API: Allow multiple concurrent notes trees with new struct notes_treeJohan Herland, Nov 20, 2009
  8. 07/10 Refactor notes concatenation into a flexible interface for combining notesJohan Herland, Nov 20, 2009
  9. 08/10 fast-import: Proper notes tree manipulation using the notes APIJohan Herland, Nov 20, 2009
  10. Shawn O. PearceNov 26, 2009
  11. Johan HerlandNov 26, 2009
  12. Shawn O. PearceNov 26, 2009
  13. 09/10 Rename t9301 to t9350, to make room for more fast-import testsJohan Herland, Nov 20, 2009
  14. 10/10 Add more testcases to test fast-import of notesJohan Herland, Nov 20, 2009
  15. Junio C HamanoNov 20, 2009
  16. Johan HerlandNov 20, 2009
  17. Nanako ShiraishiNov 20, 2009
  18. Johannes SchindelinNov 20, 2009
  19. Junio C HamanoNov 20, 2009
  20. Junio C HamanoNov 20, 2009
  21. Alex RiesenJan 19, 2010
  22. Junio C HamanoJan 19, 2010
  23. Junio C HamanoJan 20, 2010
  24. Johannes SixtJan 20, 2010
  25. Junio C HamanoJan 20, 2010
  26. Alex RiesenJan 20, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.