[PATCH/RFC] Record original ref in detached HEAD

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxx>
---
 builtin/checkout.c |  6 ++++--
 refs.c             | 37 ++++++++++++++++++++++++++++++-------
 refs.h             |  5 +++++
 3 files changed, 39 insertions(+), 9 deletions(-)

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

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]