git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Proof-of-concept patch to remember what the detached HEAD was
@ 2009-10-14  4:44 Daniel Barkalow
  2009-10-14  5:07 ` Junio C Hamano
                   ` (3 more replies)
  0 siblings, 4 replies; 93+ messages in thread
From: Daniel Barkalow @ 2009-10-14  4:44 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

When detaching HEAD (or "browsing history"), the user has specified
the commit with some "extended SHA1" which is not a local
branch. Exactly what that string was is likely to be useful to the
user later. Also, we can detect the user putting work into the history
for the first time (such that it is no longer going to be protected as
uncommitted changes in the working tree) without a branch to hold it
by seeing that there is such a description for the current state
before the commit. (Afterwards, the description should be dropped; it
doesn't make sense to tell the user they checked out "origin/master"
or "d199fb7" when they've now diverged from that remote branch with
local changes or made a different commit.)

The upshot of the messages should be:

 $ git checkout origin/master
 Since you can't actually change "origin/master" yourself, you'll just
 be sightseeing unless you create a local branch to hold new local work.

 $ git branch
 * (not a local branch, but "origin/master")

 $ git commit
 You've been sightseeing "origin/master". The commit can't change that
 value, so your commit isn't held in any branch. If you want to create
 a branch to hold it, here's how.

"git checkout origin/master" should be similar in complexity to
"svn checkout -r 8655"; the difference is that svn won't let you
commit then and git will but you'll need to understand the
implications if you do so. If you don't commit (because you don't want
to make any changes, because you don't think it would be possible, or
because you don't want to worry about what would happen), there's no
meaningful difference, and you don't need to be told.

The messages have to be improved and made more useful.

The effects of "git checkout HEAD", "git checkout origin/master; git 
checkout HEAD^", and "git checkout origin/master; git reset --hard 
origin/next" aren't handled quite right; none of them keep a description, 
but there should always be some description of a detached HEAD unless the 
user has made a commit (and therefore gotten the message about making a 
local branch to put it on).
---
 branch.c                 |   13 +++++++++++++
 branch.h                 |    6 ++++++
 builtin-branch.c         |   13 ++++++++++++-
 builtin-checkout.c       |    8 +++++++-
 builtin-commit.c         |   10 +++++++++-
 t/t3203-branch-output.sh |    2 +-
 t/t7201-co.sh            |    6 ++----
 7 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/branch.c b/branch.c
index 05ef3f5..2c5b6d3 100644
--- a/branch.c
+++ b/branch.c
@@ -194,6 +194,18 @@ void create_branch(const char *head,
 	free(real_ref);
 }
 
+char *get_detached_head_string(void)
+{
+	char *filename = git_path("DETACH_NAME");
+	struct stat st;
+	if (stat(filename, &st) || !S_ISREG(st.st_mode))
+		return NULL;
+	struct strbuf buf = STRBUF_INIT;
+	strbuf_read_file(&buf, filename, st.st_size);
+	strbuf_trim(&buf);
+	return strbuf_detach(&buf, 0);
+}
+
 void remove_branch_state(void)
 {
 	unlink(git_path("MERGE_HEAD"));
@@ -201,4 +213,5 @@ void remove_branch_state(void)
 	unlink(git_path("MERGE_MSG"));
 	unlink(git_path("MERGE_MODE"));
 	unlink(git_path("SQUASH_MSG"));
+	unlink(git_path("DETACH_NAME"));
 }
diff --git a/branch.h b/branch.h
index eed817a..0a30c3a 100644
--- a/branch.h
+++ b/branch.h
@@ -22,6 +22,12 @@ void create_branch(const char *head, const char *name, const char *start_name,
 void remove_branch_state(void);
 
 /*
+ * Returns the string used when detaching HEAD, or NULL if HEAD is not
+ * detached.
+ */
+char *get_detached_head_string(void);
+
+/*
  * Configure local branch "local" to merge remote branch "remote"
  * taken from origin "origin".
  */
diff --git a/builtin-branch.c b/builtin-branch.c
index 9f57992..9ce4127 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -425,7 +425,18 @@ static void show_detached(struct ref_list *ref_list)
 
 	if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
 		struct ref_item item;
-		item.name = xstrdup("(no branch)");
+		char *literal = get_detached_head_string();
+		struct stat st;
+		if (literal) {
+			struct strbuf buf = STRBUF_INIT;
+			strbuf_addstr(&buf, "(no branch, as \"");
+			strbuf_addstr(&buf, literal);
+			strbuf_addstr(&buf, "\")");
+			free(literal);
+			item.name = strbuf_detach(&buf, 0);
+		} else {
+			item.name = xstrdup("(no branch)");
+		}
 		item.len = strlen(item.name);
 		item.kind = REF_LOCAL_BRANCH;
 		item.dest = NULL;
diff --git a/builtin-checkout.c b/builtin-checkout.c
index d050c37..448397d 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -510,11 +510,17 @@ static void update_refs_for_switch(struct checkout_opts *opts,
 			   REF_NODEREF, DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path)
-				fprintf(stderr, "Note: moving to '%s' which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n  git checkout -b <new_branch_name>\n", new->name);
+				fprintf(stderr, "Note: moving to '%s' which isn't a local branch.\nAny commits you may make will not affect the commit with this name.\n", new->name);
 			describe_detached_head("HEAD is now at", new->commit);
 		}
 	}
 	remove_branch_state();
+	if (!new->path && strcmp(new->name, "HEAD")) {
+		FILE *detach_name;
+		detach_name = fopen(git_path("DETACH_NAME"), "w");
+		fprintf(detach_name, "%s\n", new->name);
+		fclose(detach_name);
+	}
 	strbuf_release(&msg);
 	if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
 		report_tracking(new);
diff --git a/builtin-commit.c b/builtin-commit.c
index 200ffda..2ceb951 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -24,6 +24,7 @@
 #include "string-list.h"
 #include "rerere.h"
 #include "unpack-trees.h"
+#include "branch.h"
 
 static const char * const builtin_commit_usage[] = {
 	"git commit [options] [--] <filepattern>...",
@@ -968,6 +969,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct ref_lock *ref_lock;
 	struct commit_list *parents = NULL, **pptr = &parents;
 	struct stat statbuf;
+	char *detached_string;
 	int allow_fast_forward = 1;
 	struct wt_status s;
 
@@ -1089,10 +1091,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		die("cannot update HEAD ref");
 	}
 
+	detached_string = get_detached_head_string();
+
 	unlink(git_path("MERGE_HEAD"));
 	unlink(git_path("MERGE_MSG"));
 	unlink(git_path("MERGE_MODE"));
 	unlink(git_path("SQUASH_MSG"));
+	unlink(git_path("DETACH_NAME"));
 
 	if (commit_index_files())
 		die ("Repository has been updated, but unable to write\n"
@@ -1101,8 +1106,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
 	rerere();
 	run_hook(get_index_file(), "post-commit", NULL);
-	if (!quiet)
+	if (!quiet) {
+		if (detached_string)
+			fprintf(stderr, "\nNote: you had checked out '%s' which isn't a local branch.\nIf you want to create a new branch with this commit, you may do so\n(now or later) by using -b with the checkout command. Example:\n  git checkout -b <new_branch_name>\n\n", detached_string);
 		print_summary(prefix, commit_sha1);
+	}
 
 	return 0;
 }
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 809d1c4..08409cd 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -67,7 +67,7 @@ test_expect_success 'git branch -v shows branch summaries' '
 '
 
 cat >expect <<'EOF'
-* (no branch)
+* (no branch, as "HEAD^0")
   branch-one
   branch-two
   master
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index ebfd34d..0f40589 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -171,10 +171,8 @@ test_expect_success 'checkout to detach HEAD' '
 	git checkout -f renamer && git clean -f &&
 	git checkout renamer^ 2>messages &&
 	(cat >messages.expect <<EOF
-Note: moving to '\''renamer^'\'' which isn'\''t a local branch
-If you want to create a new branch from this checkout, you may do so
-(now or later) by using -b with the checkout command again. Example:
-  git checkout -b <new_branch_name>
+Note: moving to '\''renamer^'\'' which isn'\''t a local branch.
+Any commits you may make will not affect the commit with this name.
 HEAD is now at 7329388... Initial A one, A two
 EOF
 ) &&
-- 
1.6.5.9.ge994f.dirty

^ permalink raw reply related	[flat|nested] 93+ messages in thread

end of thread, other threads:[~2009-10-27 17:59 UTC | newest]

Thread overview: 93+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-10-14  4:44 [PATCH] Proof-of-concept patch to remember what the detached HEAD was Daniel Barkalow
2009-10-14  5:07 ` Junio C Hamano
2009-10-14  5:08 ` Jeff King
2009-10-14 10:33   ` Johannes Schindelin
2009-10-14 15:39     ` Jeff King
2009-10-14 18:34       ` Junio C Hamano
2009-10-14 18:40         ` Jeff King
2009-10-14 15:56   ` Daniel Barkalow
2009-10-14 18:56 ` Jay Soffian
2009-10-14 19:15   ` Daniel Barkalow
2009-10-14 20:18     ` Nicolas Pitre
2009-10-14 20:37       ` Daniel Barkalow
2009-10-14 20:42       ` Junio C Hamano
2009-10-14 20:48         ` Nicolas Pitre
2009-10-14 22:34           ` Junio C Hamano
2009-10-14 23:09             ` Jeff King
2009-10-14 23:34               ` Nicolas Pitre
2009-10-15  0:56                 ` Junio C Hamano
2009-10-15  1:47                   ` Jeff King
2009-10-15  3:08                     ` Nicolas Pitre
2009-10-15  4:21                       ` Jeff King
2009-10-16  1:04                       ` Johannes Schindelin
2009-10-16  1:36                         ` Nicolas Pitre
2009-10-16  2:07                           ` Johannes Schindelin
2009-10-16  2:45                             ` Nicolas Pitre
2009-10-16  2:56                             ` Junio C Hamano
2009-10-17  7:24                             ` Sean Estabrooks
2009-10-26 22:22                               ` Johannes Schindelin
2009-10-27  3:41                                 ` Nanako Shiraishi
2009-10-27 10:33                                   ` Making Git easy to use -- without RTFM, was " Johannes Schindelin
2009-10-27 17:58                                     ` Avery Pennarun
2009-10-16  0:53                   ` Johannes Schindelin
2009-10-16  3:00                     ` Junio C Hamano
2009-10-15  7:36               ` James Pickens
2009-10-15 12:54                 ` Jakub Narebski
2009-10-15 14:11                   ` Björn Steinbrink
2009-10-15 19:03                   ` Nicolas Pitre
2009-10-15 15:36                 ` Daniel Barkalow
2009-10-15 16:29                   ` Michael J Gruber
2009-10-15 19:07                   ` Nicolas Pitre
2009-10-15 19:22                     ` Daniel Barkalow
2009-10-15 22:56                       ` Thomas Rast
2009-10-15 18:51                 ` Nicolas Pitre
2009-10-15 19:52                   ` Junio C Hamano
2009-10-15 21:26                     ` Jeff King
2009-10-15 21:54                       ` Junio C Hamano
2009-10-15 22:08                         ` Junio C Hamano
2009-10-15 23:16                           ` Nicolas Pitre
2009-10-15 23:47                           ` James Pickens
2009-10-16  0:34                             ` Nicolas Pitre
2009-10-16  0:43                             ` Johannes Schindelin
2009-10-16  5:03                             ` Björn Steinbrink
2009-10-15 22:16                         ` Jeff King
2009-10-15 22:17                           ` Jeff King
2009-10-16  4:29                         ` Björn Steinbrink
2009-10-16  6:02                           ` Daniel Barkalow
2009-10-16  8:27                             ` Björn Steinbrink
2009-10-16 15:44                             ` Nicolas Pitre
2009-10-15 19:31                 ` Daniel Barkalow
2009-10-15 20:34                   ` Junio C Hamano
2009-10-15 21:35                     ` Daniel Barkalow
2009-10-15 21:48                       ` Nicolas Pitre
2009-10-16 12:15                   ` Julian Phillips
2009-10-16 14:30                     ` Björn Steinbrink
2009-10-16 17:31                       ` Julian Phillips
2009-10-16 18:29                         ` Daniel Barkalow
2009-10-16 19:05                         ` Junio C Hamano
2009-10-16 19:48                           ` Julian Phillips
2009-10-16 20:19                             ` Nicolas Pitre
2009-10-17 15:15                               ` Julian Phillips
2009-10-17 17:04                                 ` Björn Steinbrink
2009-10-17 17:35                                   ` Julian Phillips
2009-10-17 17:48                                     ` Björn Steinbrink
2009-10-17 22:28                                       ` Julian Phillips
2009-10-16 20:19                             ` Junio C Hamano
2009-10-17 15:32                               ` Julian Phillips
2009-10-17 17:43                                 ` Junio C Hamano
2009-10-17 22:19                                   ` Julian Phillips
2009-10-17  7:55                         ` Björn Steinbrink
2009-10-17  8:11                           ` Junio C Hamano
2009-10-17  8:40                             ` Björn Steinbrink
2009-10-17  9:04                               ` Junio C Hamano
2009-10-17 17:07                                 ` James Pickens
2009-10-17 19:41                                 ` Björn Steinbrink
2009-10-18 22:47                                   ` Junio C Hamano
2009-10-19  8:44                                     ` Björn Steinbrink
2009-10-17 15:02                           ` Julian Phillips
2009-10-14 23:52         ` Eric Raible
2009-10-16 22:36 ` Christoph Bartoschek
2009-10-17  7:43   ` Junio C Hamano
2009-10-17  8:19     ` Björn Steinbrink
2009-10-17 17:42       ` Junio C Hamano
2009-10-17 20:35     ` Daniel Barkalow

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).