{"thread":{"id":"37400","subject":"check-ref-format: include refs/ in the argument or to strip it?","startedAt":"2014-08-22T15:41:51Z","lastAt":"2014-08-27T20:53:37Z","messageCount":11,"participants":["Jonathan Nieder","Junio C Hamano","Jeff King","Ronnie Sahlberg","Michael Haggerty"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"248120","messageId":"20140822154151.GK20185@google.com","threadId":"37400","inReplyTo":"047d7b624d36142d46050131f336@google.com","subject":"check-ref-format: include refs/ in the argument or to strip it?","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2014-08-22T15:41:51Z","receivedAt":"2014-08-22T15:41:51Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nMichael Haggerty wrote[1]:\n> Jonathan Nieder wrote:\n\n>> The check-ref-format documentation is pretty unclear, but the\n>> intent is that it would be used like\n>>\n>>\tgit check-ref-format heads/master\n>>\n>> (see the surviving examples in contrib/examples/). That way, it can\n>> enforce the rule (from git-check-ref-format(1))\n>>\n>>\t2. They must contain at least one /. This enforces the presence\n>>\tof a category like heads/, tags/ etc. but the actual names\n>>\tare not restricted.\n[...]\n> Thanks for the explanation and the pointer.\n>\n> I suppose that was a usage that was more popular in the past than\n> now. I can hardly remember anyone talking about references like\n> \"heads/master\" or \"tags/v1\". It seems to me that when somebody wants\n> to be unambiguous they usually use the whole reference name\n> \"refs/heads/master\" or \"refs/tags/v1\". When they want to be succinct\n> they usually use just \"master\" or \"v1\".\n>\n> To me it seems incongruous to have the \"heads/master\" syntax\n> supported at this deep level of plumbing. In most cases, the caller\n> could get the same effect by prepending \"refs/\" to the string and\n> then calling check_refname_format with a new\n> REFNAME_REQUIRE_CATEGORY option that requires both a \"refs/\" prefix\n> and a total of at least *three* levels.\n\nI agree that this piece of UI is pretty weird.  Worse, it's\nunderdocumented.\n\nI wonder if it would make sense to have the check-ref-format commandline\nutility require two slashes when its argument begins with \"refs/\" (still\nrequiring only one slash when it doesn't, for backward compatibility)\nand to start encouraging people to pass refnames with refs/ to it.\n\nThe alternative would be something like the following, which doesn't\nseem too appealing.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n[1] https://code-review.googlesource.com/1017\n\n Documentation/git-check-ref-format.txt | 18 +++++++++---------\n 1 file changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex fc02959..447e9fb 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -15,8 +15,8 @@ SYNOPSIS\n \n DESCRIPTION\n -----------\n-Checks if a given 'refname' is acceptable, and exits with a non-zero\n-status if it is not.\n+Checks if refs/<refname> is an acceptable ref name, and exits\n+with a non-zero status if it is not.\n \n A reference is used in Git to specify branches and tags.  A\n branch head is stored in the `refs/heads` hierarchy, while\n@@ -91,14 +91,14 @@ OPTIONS\n \tcomponents).  The default is `--no-allow-onelevel`.\n \n --refspec-pattern::\n-\tInterpret <refname> as a reference name pattern for a refspec\n-\t(as used with remote repositories).  If this option is\n-\tenabled, <refname> is allowed to contain a single `*`\n-\tin place of a one full pathname component (e.g.,\n-\t`foo/*/bar` but not `foo/bar*`).\n+\tInterpret refs/<refname> as a reference name pattern\n+\tfor a refspec (as used with remote repositories).\n+\tIf this option is enabled, <refname> is allowed\n+\tto contain a single `*` in place of a one full pathname\n+\tcomponent (e.g., `foo/*/bar` but not `foo/bar*`).\n \n --normalize::\n-\tNormalize 'refname' by removing any leading slash (`/`)\n+\tNormalize <refname> by removing any leading slash (`/`)\n \tcharacters and collapsing runs of adjacent slashes between\n \tname components into a single slash.  Iff the normalized\n \trefname is valid then print it to standard output and exit\n@@ -118,7 +118,7 @@ $ git check-ref-format --branch @{-1}\n * Determine the reference name to use for a new branch:\n +\n ------------\n-$ ref=$(git check-ref-format --normalize \"refs/heads/$newbranch\") ||\n+$ ref=$(git check-ref-format --normalize \"heads/$newbranch\") ||\n die \"we do not like '$newbranch' as a branch name.\"\n ------------\n \n-- \n"},{"id":"248133","messageId":"xmqqmwawnzfk.fsf@gitster.dls.corp.google.com","threadId":"37400","inReplyTo":"20140822154151.GK20185@google.com","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-08-22T18:37:19Z","receivedAt":"2014-08-22T18:37:19Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> Michael Haggerty wrote[1]:\n>> Jonathan Nieder wrote:\n>\n>>> The check-ref-format documentation is pretty unclear, but the\n>>> intent is that it would be used like\n>>>\n>>>\tgit check-ref-format heads/master\n>>>\n>>> (see the surviving examples in contrib/examples/). That way, it can\n>>> enforce the rule (from git-check-ref-format(1))\n>>>\n>>>\t2. They must contain at least one /. This enforces the presence\n>>>\tof a category like heads/, tags/ etc. but the actual names\n>>>\tare not restricted.\n> [...]\n>> Thanks for the explanation and the pointer.\n\nI wanted to follow this discussion, especially the ellided [...]\n\"pointer\", but had a hart time finding what \"pointer\" was.\n\nAnyway, the true origin of ONELEVEL as far as I recall was to give\nus a way to say \"in this code path, we also expect to be fed HEAD,\nORIG_HEAD, etc., so please do not subject us to the 'at least one\nslash' rule.\", implication of which is that the 'at least one slash'\nrule was to expect things are 'refs/<anything>' so there will be at\nleast one.  Even back then, that <anything> alone had at least one\nslash (e.g. heads/master), but the intention was *never* that we\nwould forbid <anything> that does not have a slash by feeding\n<anything> part alone to check-ref-format, i.e. things like\n\"refs/stash\" were designed to be allowed.\n\nThat the function does not reject what does not begin with \"refs/\"\nwhen ONELEVEL is not in effect is just being loose.\n"},{"id":"248135","messageId":"20140822184515.GL20185@google.com","threadId":"37400","inReplyTo":"xmqqmwawnzfk.fsf@gitster.dls.corp.google.com","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2014-08-22T18:45:15Z","receivedAt":"2014-08-22T18:45:15Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n\n>                implication of which is that the 'at least one slash'\n> rule was to expect things are 'refs/<anything>' so there will be at\n> least one.  Even back then, that <anything> alone had at least one\n> slash (e.g. heads/master), but the intention was *never* that we\n> would forbid <anything> that does not have a slash by feeding\n> <anything> part alone to check-ref-format, i.e. things like\n> \"refs/stash\" were designed to be allowed.\n\nNow I'm more confused.  Until 5f7b202a (2008-01-01), there was a\ncomment\n\n\t\tif (level < 2)\n\t\t\treturn -2; /* at least of form \"heads/blah\" */\n\nand that behavior has been preserved since the beginning.\n\nWhy do most old callers pass a string that doesn't start with refs/\n(e.g., see the callers in 03feddd6, 2005-10-13)?  Has the intent been\nto relax the requirement since then?\n\nJonathan\n"},{"id":"248169","messageId":"20140823025900.GM20185@google.com","threadId":"37400","inReplyTo":"xmqqmwawnzfk.fsf@gitster.dls.corp.google.com","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2014-08-23T02:59:00Z","receivedAt":"2014-08-23T02:59:00Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n>> Michael Haggerty wrote[1]:\n>>> Jonathan Nieder wrote:\n\n>>>> The check-ref-format documentation is pretty unclear, but the\n>>>> intent is that it would be used like\n>>>>\n>>>>\tgit check-ref-format heads/master\n>>>>\n>>>> (see the surviving examples in contrib/examples/). That way, it can\n>>>> enforce the rule (from git-check-ref-format(1))\n[...]\n>>> Thanks for the explanation and the pointer.\n>\n> I wanted to follow this discussion, especially the ellided [...]\n> \"pointer\", but had a hart time finding what \"pointer\" was.\n\nI missed this question before.\n\nThe discussion happened at https://code-review.googlesource.com/1017.\nIt's easier to see after clicking the 'Expand All' button, but even\nthen it's hard to see the signal in the 'Patch Set <n> was rebased'\nnoise.\n\nThe pointer Michael mentioned was to the git-check-ref-format(1)\nmanpage and old 'check-ref-format' callers that can be found in\ncontrib/examples/.  Sorry for the lack of context.\n\nJonathan\n"},{"id":"248176","messageId":"20140823054646.GA18256@peff.net","threadId":"37400","inReplyTo":"20140822184515.GL20185@google.com","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-08-23T05:46:46Z","receivedAt":"2014-08-23T05:46:46Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Aug 22, 2014 at 11:45:15AM -0700, Jonathan Nieder wrote:\n\n> Junio C Hamano wrote:\n> \n> >                implication of which is that the 'at least one slash'\n> > rule was to expect things are 'refs/<anything>' so there will be at\n> > least one.  Even back then, that <anything> alone had at least one\n> > slash (e.g. heads/master), but the intention was *never* that we\n> > would forbid <anything> that does not have a slash by feeding\n> > <anything> part alone to check-ref-format, i.e. things like\n> > \"refs/stash\" were designed to be allowed.\n> \n> Now I'm more confused.  Until 5f7b202a (2008-01-01), there was a\n> comment\n> \n> \t\tif (level < 2)\n> \t\t\treturn -2; /* at least of form \"heads/blah\" */\n> \n> and that behavior has been preserved since the beginning.\n> \n> Why do most old callers pass a string that doesn't start with refs/\n> (e.g., see the callers in 03feddd6, 2005-10-13)?  Has the intent been\n> to relax the requirement since then?\n\nYeah, this weird \"do not allow refs/foo\" behavior has continually\nconfused me. Coincidentally I just noticed a case today where\n\"pack-refs\" treats \"refs/foo\" specially for no good reason:\n\n  http://thread.gmane.org/gmane.comp.version-control.git/255729\n\nAfter much head scratching over the years, I am of the opinion that\nnobody every really _meant_ to prevent \"refs/foo\", and that code\ncomments like the one you quote above were an attempt to document\nexisting buggy behavior that was really trying to differentiate \"HEAD\"\nfrom \"refs/*\". That's just my opinion, though. :) I'd be happy if all of\nthe special-treatment of \"refs/foo\" went away and check_refname_format\nalways got the full ref.\n\n-Peff\n"},{"id":"248177","messageId":"CAPc5daVnPbP3rseEaZPug21phaF1FozqTFvBnK2EMkNMxkhATw@mail.gmail.com","threadId":"37400","inReplyTo":"20140823054646.GA18256@peff.net","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-08-23T05:50:52Z","receivedAt":"2014-08-23T05:50:52Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Fri, Aug 22, 2014 at 10:46 PM, Jeff King <peff@peff.net> wrote:\n>\n> After much head scratching over the years, I am of the opinion that\n> nobody every really _meant_ to prevent \"refs/foo\"...\n\nYup, that matches my understanding.\n"},{"id":"248245","messageId":"CAL=YDWk5FxnNWaFXJk3t+H0Q_axETmNeb=puuUqhiDBNeQVDGQ@mail.gmail.com","threadId":"37400","inReplyTo":"20140823054646.GA18256@peff.net","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Ronnie Sahlberg","fromEmail":"sahlberg@google.com","sentAt":"2014-08-25T17:43:30Z","receivedAt":"2014-08-25T17:43:30Z","isPatch":false,"sender":{"key":"sahlberg@google.com","avatar":"https://avatars.githubusercontent.com/u/7320636?v=4"},"body":"On Fri, Aug 22, 2014 at 10:46 PM, Jeff King <peff@peff.net> wrote:\n> On Fri, Aug 22, 2014 at 11:45:15AM -0700, Jonathan Nieder wrote:\n>\n>> Junio C Hamano wrote:\n>>\n>> >                implication of which is that the 'at least one slash'\n>> > rule was to expect things are 'refs/<anything>' so there will be at\n>> > least one.  Even back then, that <anything> alone had at least one\n>> > slash (e.g. heads/master), but the intention was *never* that we\n>> > would forbid <anything> that does not have a slash by feeding\n>> > <anything> part alone to check-ref-format, i.e. things like\n>> > \"refs/stash\" were designed to be allowed.\n>>\n>> Now I'm more confused.  Until 5f7b202a (2008-01-01), there was a\n>> comment\n>>\n>>               if (level < 2)\n>>                       return -2; /* at least of form \"heads/blah\" */\n>>\n>> and that behavior has been preserved since the beginning.\n>>\n>> Why do most old callers pass a string that doesn't start with refs/\n>> (e.g., see the callers in 03feddd6, 2005-10-13)?  Has the intent been\n>> to relax the requirement since then?\n>\n> Yeah, this weird \"do not allow refs/foo\" behavior has continually\n> confused me. Coincidentally I just noticed a case today where\n> \"pack-refs\" treats \"refs/foo\" specially for no good reason:\n>\n>   http://thread.gmane.org/gmane.comp.version-control.git/255729\n>\n> After much head scratching over the years, I am of the opinion that\n> nobody every really _meant_ to prevent \"refs/foo\", and that code\n> comments like the one you quote above were an attempt to document\n> existing buggy behavior that was really trying to differentiate \"HEAD\"\n> from \"refs/*\". That's just my opinion, though. :) I'd be happy if all of\n> the special-treatment of \"refs/foo\" went away and check_refname_format\n> always got the full ref.\n>\n\nThere are also a lot of places where we assume that a refs will start\nwith \"refs/heads/\" and not just \"refs/\"\nfor_each_branch_ref(), log_ref_setup() (so no reflogs) is_branch() to\nname a few.\n\nThis makes the api a bit confusing and hard to predict. Which\nfunctions allow refs/foo and which will ignore it?\nAre there any compelling reasons why refs/foo should be allowed?\n"},{"id":"248249","messageId":"20140825182636.GO20185@google.com","threadId":"37400","inReplyTo":"CAL=YDWk5FxnNWaFXJk3t+H0Q_axETmNeb=puuUqhiDBNeQVDGQ@mail.gmail.com","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2014-08-25T18:26:36Z","receivedAt":"2014-08-25T18:26:36Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ronnie Sahlberg wrote:\n> On Fri, Aug 22, 2014 at 10:46 PM, Jeff King <peff@peff.net> wrote:\n\n>> Yeah, this weird \"do not allow refs/foo\" behavior has continually\n>> confused me. Coincidentally I just noticed a case today where\n>> \"pack-refs\" treats \"refs/foo\" specially for no good reason:\n>>\n>>   http://thread.gmane.org/gmane.comp.version-control.git/255729\n>>\n>> After much head scratching over the years, I am of the opinion that\n>> nobody every really _meant_ to prevent \"refs/foo\", and that code\n>> comments like the one you quote above were an attempt to document\n>> existing buggy behavior that was really trying to differentiate \"HEAD\"\n>> from \"refs/*\". That's just my opinion, though. :)\n\nIt's still very puzzling to me.  The comment came at the same time as\nthe behavior, in v0.99.9~120 (git-check-ref-format: reject funny ref\nnames, 2005-10-13).  Before that, the behavior was even stranger ---\nit checked that there was exactly one slash in the argument.\n\nI'm willing to believe we might not want that check any more, though.\n\n[...]\n> There are also a lot of places where we assume that a refs will start\n> with \"refs/heads/\" and not just \"refs/\"\n> for_each_branch_ref(), log_ref_setup() (so no reflogs) is_branch() to\n> name a few.\n\nfor_each_branch_ref is for iterating over local branches, which are\ndefined as refs that start with refs/heads/*.  Likewise, the only\npoint of is_branch is to check whether a ref is under refs/heads/*.\nThat's not an assumption about all refs.\n\nlog_ref_setup implements the policy that there are only reflogs for:\n\n * refs where the reflog was explicitly created (\"git branch\n   --create-reflog\" does this, but for some reason there's no\n   corresponding \"git update-ref --create-reflog\" so people have\n   to use mkdir directly for other refs), plus\n\n * if the '[core] logallrefupdates' configuration is enabled (and it\n   is by default for non-bare repositories), then HEAD, refs/heads/*,\n   refs/notes/*, and refs/remotes/*.\n\nThis is documented in git-config(1) --- see core.logAllRefUpdates.\n\nThat way, when tools internally use other refs (e.g., FETCH_HEAD),\ngit doesn't have to automatically incur the cost of maintaining the\nreflog for those.  What other refs should there be reflogs for?  I\nhaven't thought carefully about this.\n\nIt definitely isn't an assumption that *all* refs will match that\npattern.  But it might be worth changing for other reasons.\n\nJonathan\n"},{"id":"248255","messageId":"xmqqzjesl7gk.fsf@gitster.dls.corp.google.com","threadId":"37400","inReplyTo":"CAL=YDWk5FxnNWaFXJk3t+H0Q_axETmNeb=puuUqhiDBNeQVDGQ@mail.gmail.com","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-08-25T19:01:15Z","receivedAt":"2014-08-25T19:01:15Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ronnie Sahlberg <sahlberg@google.com> writes:\n\n> There are also a lot of places where we assume that a refs will start\n> with \"refs/heads/\" and not just \"refs/\"\n> for_each_branch_ref(), log_ref_setup() (so no reflogs) is_branch() to\n> name a few.\n\nfor-each-BRANCH-ref and is-BRANCH are explicitly about branches and\nit is natural that they will work only on refs that start with\nrefs/heads/, no?  I am not sure about log-ref-setup (\"git grep pu\"\ndoes not find it).\n\n> This makes the api a bit confusing and hard to predict. Which\n> functions allow refs/foo and which will ignore it?  Are there any\n> compelling reasons why refs/foo should be allowed?\n\nThe only one I can think of offhand that we internally use is the\nstash, but that does not give us any assurance that no third-party\ntool is using refs/frotz for its own use X-<.\n"},{"id":"248256","messageId":"20140825190939.GC30953@peff.net","threadId":"37400","inReplyTo":"20140825182636.GO20185@google.com","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-08-25T19:09:39Z","receivedAt":"2014-08-25T19:09:39Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Aug 25, 2014 at 11:26:36AM -0700, Jonathan Nieder wrote:\n\n> It's still very puzzling to me.  The comment came at the same time as\n> the behavior, in v0.99.9~120 (git-check-ref-format: reject funny ref\n> names, 2005-10-13).  Before that, the behavior was even stranger ---\n> it checked that there was exactly one slash in the argument.\n> \n> I'm willing to believe we might not want that check any more, though.\n\nYeah, given that among experiences gits, nobody can figure out a\nmotivation or a history for the feature (and given that it causes\nproblems), I do not see any problem with loosening it.\n\n> That way, when tools internally use other refs (e.g., FETCH_HEAD),\n> git doesn't have to automatically incur the cost of maintaining the\n> reflog for those.  What other refs should there be reflogs for?  I\n> haven't thought carefully about this.\n\nI think you'd in theory want a reflog for anything. The refs in\nrefs/tags are not meant to change, but if they do (e.g., somebody\nforce-pushes a tag to a server) it is nice to have a log of what\nhappened.  I think the same argument could apply to anything in refs/.\n\nI think more ephemeral things (like MERGE_HEAD) tend to be in the root,\noutside of refs. Reflogging those _could_ be useful, but probably isn't\n(and in the case of something like FETCH_HEAD, would not record all of\nthe information anyway).\n\nI wrote the patch below over a year ago and very nearly submitted it. At\nGitHub we use reflogs frequently for auditing and forensics, and I\nwanted to have such an audit trail for everything. However, we ended up\ndoing something a little more invasive that I do not think would be that\ninteresting to upstream (though I am happy to submit a patch if people\nthink otherwise): we maintain a separate \"audit log\" for all refs that\nis never pruned, and lives on even when refs are deleted.\n\n-- >8 --\nSubject: [PATCH] teach core.logallrefupdates an \"always\" mode\n\nWhen core.logallrefupdates is true, we only create a new\nreflog for refs that are under certain well-known\nhierarchies. The reason is that we know that some\nhierarchies (like refs/tags) do not typically change, and\nthat unknown hierarchies might not want reflogs at all\n(e.g., a hypothetical refs/foo might be meant to change\noften and drop old history immediately).\n\nHowever, sometimes it is useful to override this decision\nand simply log for all refs, because the safety and audit\ntrail is more important than the performance implications of\nkeeping the log around.\n\nThis patch introduces a new \"always\" mode for the\ncore.logallrefupdates option which will log updates to\neverything under refs/, regardless where in the hierarchy it\nis (we still will not log things like ORIG_HEAD and\nFETCH_HEAD, which are known to be transient).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nLooking over the code, I am not sure that it actually works as\nadvertised with respect to ORIG_HEAD, etc. That would be easy enough to\nfix, though.\n\n Documentation/config.txt |  8 +++++---\n branch.c                 |  2 +-\n builtin/checkout.c       |  2 +-\n builtin/init-db.c        |  2 +-\n cache.h                  |  9 ++++++++-\n config.c                 |  7 ++++++-\n environment.c            |  2 +-\n refs.c                   | 23 +++++++++++++++++------\n t/t1400-update-ref.sh    | 31 +++++++++++++++++++++++++++++++\n 9 files changed, 71 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c55c22a..27629df 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -405,10 +405,12 @@ core.logAllRefUpdates::\n \t\"$GIT_DIR/logs/<ref>\", by appending the new and old\n \tSHA-1, the date/time and the reason of the update, but\n \tonly when the file exists.  If this configuration\n-\tvariable is set to true, missing \"$GIT_DIR/logs/<ref>\"\n+\tvariable is set to `true`, a missing \"$GIT_DIR/logs/<ref>\"\n \tfile is automatically created for branch heads (i.e. under\n-\trefs/heads/), remote refs (i.e. under refs/remotes/),\n-\tnote refs (i.e. under refs/notes/), and the symbolic ref HEAD.\n+\t`refs/heads/`), remote refs (i.e. under `refs/remotes/`),\n+\tnote refs (i.e. under `refs/notes/`), and the symbolic ref `HEAD`.\n+\tIf it is set to `always`, then a missing reflog is automatically\n+\tcreated for any ref under `refs/`.\n +\n This information can be used to determine what commit\n was the tip of a branch \"2 days ago\".\ndiff --git a/branch.c b/branch.c\nindex 46e8aa8..1d140b7 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -292,7 +292,7 @@ void create_branch(const char *head,\n \t}\n \n \tif (reflog)\n-\t\tlog_all_ref_updates = 1;\n+\t\tlog_all_ref_updates = LOG_REFS_NORMAL;\n \n \tif (forcing)\n \t\tsnprintf(msg, sizeof msg, \"branch: Reset to %s\",\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f71e745..65bc066 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -586,7 +586,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \t\t\t\tchar *ref_name = mkpath(\"refs/heads/%s\", opts->new_orphan_branch);\n \n \t\t\t\ttemp = log_all_ref_updates;\n-\t\t\t\tlog_all_ref_updates = 1;\n+\t\t\t\tlog_all_ref_updates = LOG_REFS_NORMAL;\n \t\t\t\tif (log_ref_setup(ref_name, log_file, sizeof(log_file))) {\n \t\t\t\t\tfprintf(stderr, _(\"Can not do reflog for '%s'\\n\"),\n \t\t\t\t\t    opts->new_orphan_branch);\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 56f85e2..ab0651f 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -264,7 +264,7 @@ static int create_default_files(const char *template_path)\n \t\tconst char *work_tree = get_git_work_tree();\n \t\tgit_config_set(\"core.bare\", \"false\");\n \t\t/* allow template config file to override the default */\n-\t\tif (log_all_ref_updates == -1)\n+\t\tif (log_all_ref_updates == LOG_REFS_UNSET)\n \t\t    git_config_set(\"core.logallrefupdates\", \"true\");\n \t\tif (!starts_with(git_dir, work_tree) ||\n \t\t    strcmp(git_dir + strlen(work_tree), \"/.git\")) {\ndiff --git a/cache.h b/cache.h\nindex fcb511d..79c5ae1 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -603,7 +603,6 @@ extern int minimum_abbrev, default_abbrev;\n extern int ignore_case;\n extern int assume_unchanged;\n extern int prefer_symlink_refs;\n-extern int log_all_ref_updates;\n extern int warn_ambiguous_refs;\n extern int warn_on_object_refname_ambiguity;\n extern int shared_repository;\n@@ -634,6 +633,14 @@ extern int core_preload_index;\n extern int core_apply_sparse_checkout;\n extern int precomposed_unicode;\n \n+enum log_refs_config {\n+\tLOG_REFS_UNSET = -1,\n+\tLOG_REFS_NONE = 0,\n+\tLOG_REFS_NORMAL, /* see should_create_reflog for rules */\n+\tLOG_REFS_ALWAYS\n+};\n+extern enum log_refs_config log_all_ref_updates;\n+\n /*\n  * The character that begins a commented line in user-editable file\n  * that is subject to stripspace.\ndiff --git a/config.c b/config.c\nindex 058505c..7706fdf 100644\n--- a/config.c\n+++ b/config.c\n@@ -703,7 +703,12 @@ static int git_default_core_config(const char *var, const char *value)\n \t}\n \n \tif (!strcmp(var, \"core.logallrefupdates\")) {\n-\t\tlog_all_ref_updates = git_config_bool(var, value);\n+\t\tif (value && !strcasecmp(value, \"always\"))\n+\t\t\tlog_all_ref_updates = LOG_REFS_ALWAYS;\n+\t\telse if (git_config_bool(var, value))\n+\t\t\tlog_all_ref_updates = LOG_REFS_NORMAL;\n+\t\telse\n+\t\t\tlog_all_ref_updates = LOG_REFS_NONE;\n \t\treturn 0;\n \t}\n \ndiff --git a/environment.c b/environment.c\nindex 565f652..3e77237 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -21,7 +21,7 @@ int ignore_case;\n int assume_unchanged;\n int prefer_symlink_refs;\n int is_bare_repository_cfg = -1; /* unspecified */\n-int log_all_ref_updates = -1; /* unspecified */\n+enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;\n int warn_ambiguous_refs = 1;\n int warn_on_object_refname_ambiguity = 1;\n int repository_format_version;\ndiff --git a/refs.c b/refs.c\nindex 27927f2..3b71e01 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2707,7 +2707,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms\n \n \tlock->force_write = 1;\n \tflag = log_all_ref_updates;\n-\tlog_all_ref_updates = 0;\n+\tlog_all_ref_updates = LOG_REFS_NONE;\n \tif (write_ref_sha1(lock, orig_sha1, NULL))\n \t\terror(\"unable to write current sha1 into %s\", oldrefname);\n \tlog_all_ref_updates = flag;\n@@ -2776,17 +2776,28 @@ static int copy_msg(char *buf, const char *msg)\n \treturn cp - buf;\n }\n \n+static int should_create_reflog(const char *refname)\n+{\n+\tswitch (log_all_ref_updates) {\n+\tcase LOG_REFS_ALWAYS:\n+\t\treturn 1;\n+\tcase LOG_REFS_NORMAL:\n+\t\treturn starts_with(refname, \"refs/heads/\") ||\n+\t\t       starts_with(refname, \"refs/remotes/\") ||\n+\t\t       starts_with(refname, \"refs/notes/\") ||\n+\t\t       !strcmp(refname, \"HEAD\");\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n /* This function must set a meaningful errno on failure */\n int log_ref_setup(const char *refname, char *logfile, int bufsize)\n {\n \tint logfd, oflags = O_APPEND | O_WRONLY;\n \n \tgit_snpath(logfile, bufsize, \"logs/%s\", refname);\n-\tif (log_all_ref_updates &&\n-\t    (starts_with(refname, \"refs/heads/\") ||\n-\t     starts_with(refname, \"refs/remotes/\") ||\n-\t     starts_with(refname, \"refs/notes/\") ||\n-\t     !strcmp(refname, \"HEAD\"))) {\n+\tif (should_create_reflog(refname)) {\n \t\tif (safe_create_leading_directories(logfile) < 0) {\n \t\t\tint save_errno = errno;\n \t\t\terror(\"unable to create directory for %s\", logfile);\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 0218e96..7669917 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -983,4 +983,35 @@ test_expect_success 'stdin -z delete refs works with packed and loose refs' '\n \ttest_must_fail git rev-parse --verify -q $c\n '\n \n+test_expect_success 'core.logAllRefUpdates=true does not log refs/foo/' '\n+\ttest_config core.logAllRefUpdates true &&\n+\ttest_commit log-true &&\n+\tgit update-ref -m reflog-message refs/heads/logme HEAD &&\n+\tgit update-ref -m reflog-message refs/foo/logme HEAD &&\n+\t{\n+\t\techo \"refs/heads/logme@{0} reflog-message\"\n+\t} >expect &&\n+\t{\n+\t\tgit log -g -1 --format=\"%gD %gs\" refs/heads/logme &&\n+\t\tgit log -g -1 --format=\"%gD %gs\" refs/foo/logme\n+\t} >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'core.logAllRefUpdates=always logs refs/foo/' '\n+\ttest_config core.logAllRefUpdates always &&\n+\ttest_commit log-always &&\n+\tgit update-ref -m reflog-message refs/heads/logme HEAD &&\n+\tgit update-ref -m reflog-message refs/foo/logme HEAD &&\n+\t{\n+\t\techo \"refs/heads/logme@{0} reflog-message\"\n+\t\techo \"refs/foo/logme@{0} reflog-message\"\n+\t} >expect &&\n+\t{\n+\t\tgit log -g -1 --format=\"%gD %gs\" refs/heads/logme &&\n+\t\tgit log -g -1 --format=\"%gD %gs\" refs/foo/logme\n+\t} >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.1.0.346.ga0367b9\n"},{"id":"248443","messageId":"53FE4551.20108@alum.mit.edu","threadId":"37400","inReplyTo":"20140825190939.GC30953@peff.net","subject":"Re: check-ref-format: include refs/ in the argument or to strip it?","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2014-08-27T20:53:37Z","receivedAt":"2014-08-27T20:53:37Z","isPatch":false,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 08/25/2014 09:09 PM, Jeff King wrote:\n> [...]\n> This patch introduces a new \"always\" mode for the\n> core.logallrefupdates option which will log updates to\n> everything under refs/, regardless where in the hierarchy it\n> is (we still will not log things like ORIG_HEAD and\n> FETCH_HEAD, which are known to be transient).\n> [...]\n\nI like the idea. I would use this setting on all my repos (information\npackrat that I am).\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"}]}