threads / rfc / 33017

RFC patchRecord original ref in detached HEAD

Subject: [PATCH/RFC] Record original ref in detached HEAD

## tl;dr

3 messages between Feb 26, 2013 and Feb 26, 2013. Diffs are folded; open one to read it.

replies: 2people: 2as markdown or json

Nguyễn Thái Ngọc Duy· Feb 26, 2013, 14:28 UTC · lore

Many times I checkout a remote branch, or a tag and after a while I forget remember what ref I checked it out from. Saving the original ref would help (or is it already saved somewhere?).

This exploits the fact that FETCH_HEAD stores extra info after the SHA-1 and at least C Git is prepared to ignore the rest after 40-hex is successfully parsed for all refs, including HEAD. We could use this to store the original ref in detached case, as demonstrated in this patch. "git status" and "git branch" could be modified to show this information later on. I think it should work even with Git 0.99.8 (I have not really verified though).

So far C Git runs well for me. Other implementations like libgit2 or JGit may be stricter and reject modified HEAD. I have to check. But I think HEAD is a local matter and this is not a big deal.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c |  6 ++++--
 refs.c             | 37 ++++++++++++++++++++++++++++++-------
 refs.h             |  5 +++++
 3 files changed, 39 insertions(+), 9 deletions(-)
Show changes to 3 files +39 −9

builtin/checkout.c, refs.c, refs.h

diff --git a/builtin/checkout.c b/builtin/checkout.c
index a9c1b5a..82fccbe 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -594,8 +594,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 	if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
 		/* Nothing to do. */
 	} else if (opts->force_detach || !new->path) {	/* No longer on any branch. */
-		update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
-			   REF_NODEREF, DIE_ON_ERR);
+		update_ref_with_note(msg.buf, "HEAD",
+				     new->commit->object.sha1,
+				     new->path, NULL,
+				     REF_NODEREF, DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path && advice_detached_head)
 				detach_advice(new->name);
diff --git a/refs.c b/refs.c
index 175b9fc..b67c3b1 100644
--- a/refs.c
+++ b/refs.c
@@ -2045,10 +2045,13 @@ static int is_branch(const char *refname)
 	return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
 }
 
-int write_ref_sha1(struct ref_lock *lock,
-	const unsigned char *sha1, const char *logmsg)
+static int write_ref_sha1_with_note(struct ref_lock *lock,
+				    const unsigned char *sha1,
+				    const char *note,
+				    const char *logmsg)
 {
 	static char term = '\n';
+	static char note_separator = '\t';
 	struct object *o;
 
 	if (!lock)
@@ -2071,7 +2074,10 @@ int write_ref_sha1(struct ref_lock *lock,
 		return -1;
 	}
 	if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
-	    write_in_full(lock->lock_fd, &term, 1) != 1
+	    (note &&
+	     (write_in_full(lock->lock_fd, &note_separator, 1) != 1 ||
+	      write_in_full(lock->lock_fd, note, strlen(note)) != strlen(note))) ||
+	     write_in_full(lock->lock_fd, &term, 1) != 1
 		|| close_ref(lock) < 0) {
 		error("Couldn't write %s", lock->lk->filename);
 		unlock_ref(lock);
@@ -2114,6 +2120,13 @@ int write_ref_sha1(struct ref_lock *lock,
 	return 0;
 }
 
+int write_ref_sha1(struct ref_lock *lock,
+		   const unsigned char *sha1,
+		   const char *logmsg)
+{
+	return write_ref_sha1_with_note(lock, sha1, NULL, logmsg);
+}
+
 int create_symref(const char *ref_target, const char *refs_heads_master,
 		  const char *logmsg)
 {
@@ -2411,9 +2424,11 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
 	return retval;
 }
 
-int update_ref(const char *action, const char *refname,
-		const unsigned char *sha1, const unsigned char *oldval,
-		int flags, enum action_on_err onerr)
+int update_ref_with_note(const char *action, const char *refname,
+			 const unsigned char *sha1,
+			 const char *note,
+			 const unsigned char *oldval,
+			 int flags, enum action_on_err onerr)
 {
 	static struct ref_lock *lock;
 	lock = lock_any_ref_for_update(refname, oldval, flags);
@@ -2426,7 +2441,7 @@ int update_ref(const char *action, const char *refname,
 		}
 		return 1;
 	}
-	if (write_ref_sha1(lock, sha1, action) < 0) {
+	if (write_ref_sha1_with_note(lock, sha1, note, action) < 0) {
 		const char *str = "Cannot update the ref '%s'.";
 		switch (onerr) {
 		case MSG_ON_ERR: error(str, refname); break;
@@ -2438,6 +2453,14 @@ int update_ref(const char *action, const char *refname,
 	return 0;
 }
 
+int update_ref(const char *action, const char *refname,
+	       const unsigned char *sha1, const unsigned char *oldval,
+	       int flags, enum action_on_err onerr)
+{
+	return update_ref_with_note(action, refname, sha1, NULL,
+				    oldval, flags, onerr);
+}
+
 struct ref *find_ref_by_name(const struct ref *list, const char *name)
 {
 	for ( ; list; list = list->next)
diff --git a/refs.h b/refs.h
index 1b2e2d3..9fbba2e 100644
--- a/refs.h
+++ b/refs.h
@@ -146,6 +146,11 @@ enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };
 int update_ref(const char *action, const char *refname,
 		const unsigned char *sha1, const unsigned char *oldval,
 		int flags, enum action_on_err onerr);
+int update_ref_with_note(const char *action, const char *refname,
+			 const unsigned char *sha1,
+			 const char *note,
+			 const unsigned char *oldval,
+			 int flags, enum action_on_err onerr);
 
 extern int parse_hide_refs_config(const char *var, const char *value, const char *);
 extern int ref_is_hidden(const char *);
-- 
1.8.1.2.536.gf441e6d
Matthieu Moy· Feb 26, 2013, 14:37 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH/RFC] Record original ref in detached HEAD

Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> Many times I checkout a remote branch, or a tag and after a while I
> forget remember what ref I checked it out from. Saving the original
> ref would help (or is it already saved somewhere?).
It is :-). Try e.g.
  git checkout --detach
  git checkout -
(analogous to "cd -": return back to where you were before)
-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/
Duy Nguyen· Feb 26, 2013, 14:57 UTC · re: Matthieu Moy · lore

Re: [PATCH/RFC] Record original ref in detached HEAD

On Tue, Feb 26, 2013 at 9:37 PM, Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> wrote:

Show 12 quoted lines
> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
>
>> Many times I checkout a remote branch, or a tag and after a while I
>> forget remember what ref I checked it out from. Saving the original
>> ref would help (or is it already saved somewhere?).
>
> It is :-). Try e.g.
>
>   git checkout --detach
>   git checkout -
>
> (analogous to "cd -": return back to where you were before)

Yeah. I always manage to forget reflog when I need it. Time to show the information in "git branch", oh wait I still have a wip about git-branch on pu..

-- 
Duy

← back to recent threads