Junio C Hamano <gitster@xxxxxxxxx> writes: > "Avery Pennarun" <apenwarr@xxxxxxxxx> writes: > ... >> 1) Checking out a remote branch "git checkout origin/master" detaches >> my HEAD, which is kind of bad, since it's such a common thing to want >> to do. > > I do not think it is bad at all. The feature to detach HEAD was designed > for that kind of usage. Start sightseeing, possibly futz with the code, > and even create some snapshot commits, and then: > > * if it starts to take a usable shape, say "git checkout -b my-topic", > from there, to give your exploration a lasting home; or > > * if it doesn't pan out, just discard it with "git checkout -f master" > (or whatever you wanted to switch back to). > > One thing that might help for downstream people would be to be able to say > "I am making 'my-topic' branch out of a detached HEAD, but it really is > meant to be a fork of origin/master that I detached my HEAD from, so > please set up tracking for that one". > > You could force people to say "git checkout -b my-topic origin/master" > from the beginning, but that is very unreasonable and unworkable. When > you are exploring, you more often than not do not know where your quest > would lead to until spending some time. It is quite important to be able > to delay the decision to create a local branch to keep what you did, and > (more importantly) to be able to delay deciding what to name that topic. > > Perhaps "git checkout -b my-topic" from a detached HEAD should inspect the > HEAD reflog to see which remote (or local) branch you came from, and give > that to the --track logic. So here is a patch for discussion, not heavily tested, but: $ git checkout origin/master $ git commit; hack hack hack ... $ git checkout --track -b mybranch sequence should result in mybranch tracking the 'master' branch from the 'origin'. The patch is just a proof of concept; doing this for HEAD reflog that is several megabytes long might take nontrivial amount of time (at least from performance standard of git); if we wanted to go this route, we should add an API to read the reflog entries from more recent to older. branch.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 59 insertions(+), 0 deletions(-) diff --git c/branch.c w/branch.c index b1e59f2..2ec6418 100644 --- c/branch.c +++ w/branch.c @@ -48,6 +48,62 @@ static int should_setup_rebase(const struct tracking *tracking) } /* + * A branch is created out of "HEAD" and we would want tracking; + * go back the reflog to figure out where we really came from. + */ +static int refine_head_one(const char *name, size_t namelen, + struct strbuf *found_ref) +{ + char *real_ref; + unsigned char sha1[20]; + if (dwim_ref(name, namelen, sha1, &real_ref) != 1) + return 0; + strbuf_reset(found_ref); + strbuf_addstr(found_ref, real_ref); + return 0; +} + +static int one_head_ent(unsigned char *osha1, unsigned char *nsha1, + const char *ident, unsigned long timestamp, int zone, + const char *message, void *cbdata) +{ + /* + * Look for signs of HEAD coming from elsewhere. + * + * "checkout: moving from %*s to %s" done by "git checkout" + * "%s: updating HEAD" done by "git reset" + */ + struct strbuf *found_ref = cbdata; + char *cp; + size_t len; + + if (!prefixcmp(message, "checkout: moving from ")) { + cp = strstr(message, " to "); + if (!cp) + return 0; + cp += 4; + len = strlen(cp); + if (cp[len-1] == '\n') + len--; + return refine_head_one(cp, len, found_ref); + } + + cp = strstr(message, ": updating HEAD"); + if (cp && !cp[15]) + return refine_head_one(message, cp - message, found_ref); + return 0; +} + +static const char *refine_head_ref(void) +{ + struct strbuf found = STRBUF_INIT; + + strbuf_addstr(&found, "HEAD"); + for_each_reflog_ent("HEAD", one_head_ent, &found); + return strbuf_detach(&found, NULL); +} + +/* * This is called when new_ref is branched off of orig_ref, and tries * to infer the settings for branch.<new_ref>.{remote,merge} from the * config. @@ -58,6 +114,9 @@ static int setup_tracking(const char *new_ref, const char *orig_ref, char key[1024]; struct tracking tracking; + if (!strcmp(orig_ref, "HEAD")) + orig_ref = refine_head_ref(); + if (strlen(new_ref) > 1024 - 7 - 7 - 1) return error("Tracking not set up: name too long: %s", new_ref); -- 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