{"thread":{"id":"31051","subject":"Feature request: fetch --prune by default","startedAt":"2012-07-19T07:30:59Z","lastAt":"2013-06-20T19:22:54Z","messageCount":52,"participants":["Alexey Muranov","Jeff King","Dan Johnson","Stefan Haller","Konstantin Khomoutov","Junio C Hamano","Johannes Sixt","Michael Haggerty","Nguyen Thai Ngoc Duy","Matthieu Moy","Sam Roberts"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"195311","messageId":"2C63E314-2EF5-4B8E-B96A-5306E317E045@gmail.com","threadId":"31051","inReplyTo":null,"subject":"Feature request: fetch --prune by default","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-19T07:30:59Z","receivedAt":"2012-07-19T07:30:59Z","isPatch":false,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"Hello,\n\ni would like\n\n`git fetch --prune <remote>`\n\nto be the default behavior of\n\n`git fetch <remote>`\n\nIn fact, i think this is the only reasonable behavior.\nKeeping copies of deleted remote branches after `fetch` is more confusing than useful.\n\n(Excuse me if this question has already been discussed.)\n\nThank you.\n\nAlexey Muranov.\n"},{"id":"195328","messageId":"20120719115558.GC29774@sigill.intra.peff.net","threadId":"31051","inReplyTo":"2C63E314-2EF5-4B8E-B96A-5306E317E045@gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-19T11:55:59Z","receivedAt":"2012-07-19T11:55:59Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jul 19, 2012 at 09:30:59AM +0200, Alexey Muranov wrote:\n\n> i would like\n> \n> `git fetch --prune <remote>`\n> \n> to be the default behavior of\n> \n> `git fetch <remote>`\n> \n> In fact, i think this is the only reasonable behavior.\n> Keeping copies of deleted remote branches after `fetch` is more confusing than useful.\n\nI agree it would be much less confusing. However, one downside is that\nwe do not keep reflogs on deleted branches (and nor did the commits in\nremote branches necessarily make it into the HEAD reflog). That makes\n\"git fetch\" a potentially destructive operation (you irrevocably lose\nthe notion of which remote branches pointed where before the fetch, and\nyou open up new commits to immediate pruning by \"gc --auto\".\n\nSo I think it would be a lot more palatable if we kept reflogs on\ndeleted branches. That, in turn, has a few open issues, such as how to\nmanage namespace conflicts (e.g., the fact that a deleted \"foo\" branch\ncan conflict with a new \"foo/bar\" branch).\n\n-Peff\n"},{"id":"195330","messageId":"CAPBPrnsFk-Ww-52W-=qkTK7Yifjowx3tpsELznO4ncmqwfP_Qg@mail.gmail.com","threadId":"31051","inReplyTo":"20120719115558.GC29774@sigill.intra.peff.net","subject":"Re: Feature request: fetch --prune by default","fromName":"Dan Johnson","fromEmail":"computerdruid@gmail.com","sentAt":"2012-07-19T14:03:30Z","receivedAt":"2012-07-19T14:03:30Z","isPatch":false,"sender":{"key":"computerdruid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/34696?v=4"},"body":"On Thu, Jul 19, 2012 at 7:55 AM, Jeff King <peff@peff.net> wrote:\n> On Thu, Jul 19, 2012 at 09:30:59AM +0200, Alexey Muranov wrote:\n>\n>> i would like\n>>\n>> `git fetch --prune <remote>`\n>>\n>> to be the default behavior of\n>>\n>> `git fetch <remote>`\n>>\n>> In fact, i think this is the only reasonable behavior.\n>> Keeping copies of deleted remote branches after `fetch` is more confusing than useful.\n>\n> I agree it would be much less confusing. However, one downside is that\n> we do not keep reflogs on deleted branches (and nor did the commits in\n> remote branches necessarily make it into the HEAD reflog). That makes\n> \"git fetch\" a potentially destructive operation (you irrevocably lose\n> the notion of which remote branches pointed where before the fetch, and\n> you open up new commits to immediate pruning by \"gc --auto\".\n>\n> So I think it would be a lot more palatable if we kept reflogs on\n> deleted branches. That, in turn, has a few open issues, such as how to\n> manage namespace conflicts (e.g., the fact that a deleted \"foo\" branch\n> can conflict with a new \"foo/bar\" branch).\n\nIn the meantime, would it make sense to introduce a configuration\nvariable to request this behavior?\n\nIf so, should it be global?\n\nfetch.prune = always\n\nor per-remote?\n\nremote.<name>.prune = always\n\nThe global option seems to be more in line with what Alexey is looking\nfor, but the per-remote one is similar to the tagopt option, which is\na similar idea.\n\nOf course, this might be just a waste of time to introduce a feature\nno one would use, in which case we obviously should not introduce such\noptions.\n-- \n-Dan\n"},{"id":"195331","messageId":"1knhokc.vqc6kir7kbjmM%lists@haller-berlin.de","threadId":"31051","inReplyTo":"CAPBPrnsFk-Ww-52W-=qkTK7Yifjowx3tpsELznO4ncmqwfP_Qg@mail.gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Stefan Haller","fromEmail":"lists@haller-berlin.de","sentAt":"2012-07-19T15:11:38Z","receivedAt":"2012-07-19T15:11:38Z","isPatch":false,"sender":{"key":"lists@haller-berlin.de","avatar":null},"body":"Dan Johnson <computerdruid@gmail.com> wrote:\n\n> In the meantime, would it make sense to introduce a configuration\n> variable to request this behavior?\n> \n> fetch.prune = always\n> \n> Of course, this might be just a waste of time to introduce a feature\n> no one would use, in which case we obviously should not introduce such\n> options.\n\nI would use it.\n\n\n-- \nStefan Haller\nBerlin, Germany\nhttp://www.haller-berlin.de/\n"},{"id":"195332","messageId":"88300470-AB41-4317-8B97-81DC18FD5899@gmail.com","threadId":"31051","inReplyTo":"20120719115558.GC29774@sigill.intra.peff.net","subject":"Re: Feature request: fetch --prune by default","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-19T16:21:21Z","receivedAt":"2012-07-19T16:21:21Z","isPatch":false,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 19 Jul 2012, at 13:55, Jeff King wrote:\n\n> On Thu, Jul 19, 2012 at 09:30:59AM +0200, Alexey Muranov wrote:\n> \n>> i would like\n>> \n>> `git fetch --prune <remote>`\n>> \n>> to be the default behavior of\n>> \n>> `git fetch <remote>`\n>> \n>> In fact, i think this is the only reasonable behavior.\n>> Keeping copies of deleted remote branches after `fetch` is more confusing than useful.\n> \n> I agree it would be much less confusing. However, one downside is that\n> we do not keep reflogs on deleted branches (and nor did the commits in\n> remote branches necessarily make it into the HEAD reflog). That makes\n> \"git fetch\" a potentially destructive operation (you irrevocably lose\n> the notion of which remote branches pointed where before the fetch, and\n> you open up new commits to immediate pruning by \"gc --auto\".\n\nI do not still understand very well some aspects of Git, like the exact purpose of \"remote tracking branches\" (are they for pull or for push?), so i may be wrong.\nHowever, i thought that a user was not expected to follow the moves of a remote branch of which the user is not an owner: if the user needs to follow the brach and not lose its commits, he/she should create a remote tracking branch.\n\n> So I think it would be a lot more palatable if we kept reflogs on\n> deleted branches. That, in turn, has a few open issues, such as how to\n> manage namespace conflicts (e.g., the fact that a deleted \"foo\" branch\n> can conflict with a new \"foo/bar\" branch).\n\nI prefer to think of a remote branch and its local copy as the same thing, which are physically different only because of current real world/hardware/software limitations, which make it necessary to keep a local cache of remote data.  With this approach, reflogs should be deleted with the branch, and there will be no namespace conflicts.\n\nAlexey."},{"id":"195333","messageId":"CA80E335-AD87-4DFC-9569-A010D3E850C0@gmail.com","threadId":"31051","inReplyTo":"20120719115558.GC29774@sigill.intra.peff.net","subject":"Re: Feature request: fetch --prune by default","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-19T16:40:48Z","receivedAt":"2012-07-19T16:40:48Z","isPatch":false,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 19 Jul 2012, at 13:55, Jeff King wrote:\n\n> I agree it would be much less confusing. However, one downside is that\n> we do not keep reflogs on deleted branches (and nor did the commits in\n> remote branches necessarily make it into the HEAD reflog). That makes\n> \"git fetch\" a potentially destructive operation (you irrevocably lose\n> the notion of which remote branches pointed where before the fetch, and\n> you open up new commits to immediate pruning by \"gc --auto\".\n\nIf i understand correctly, existence of a reflog entry will not stop \"gc\" from removing a commit, will it?\nIn this case, if a remote branch was rebased or reset, commits can be lost anyway, right?\n\nAlexey."},{"id":"195334","messageId":"CAPBPrntB3ixuRFDP5fp8saJoEZvYOEd631Nh2=-WGudB-UK=kw@mail.gmail.com","threadId":"31051","inReplyTo":"CA80E335-AD87-4DFC-9569-A010D3E850C0@gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Dan Johnson","fromEmail":"computerdruid@gmail.com","sentAt":"2012-07-19T16:48:04Z","receivedAt":"2012-07-19T16:48:04Z","isPatch":false,"sender":{"key":"computerdruid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/34696?v=4"},"body":"On Thu, Jul 19, 2012 at 12:40 PM, Alexey Muranov\n<alexey.muranov@gmail.com> wrote:\n> On 19 Jul 2012, at 13:55, Jeff King wrote:\n>\n>> I agree it would be much less confusing. However, one downside is that\n>> we do not keep reflogs on deleted branches (and nor did the commits in\n>> remote branches necessarily make it into the HEAD reflog). That makes\n>> \"git fetch\" a potentially destructive operation (you irrevocably lose\n>> the notion of which remote branches pointed where before the fetch, and\n>> you open up new commits to immediate pruning by \"gc --auto\".\n>\n> If i understand correctly, existence of a reflog entry will not stop \"gc\" from removing a commit, will it?\n> In this case, if a remote branch was rebased or reset, commits can be lost anyway, right?\nFrom the git-gc man page:\ngit gc tries very hard to be safe about the garbage it collects. In\nparticular, it will keep not only objects referenced by your current\nset of branches and tags, but also objects referenced by the index,\nremote-tracking branches, refs saved by git filter-branch in\nrefs/original/, or reflogs (which may reference commits in branches\nthat were later amended or rewound).\n\nSo yes, a reflog entry does stop gc from removing objects, including\ncommits. It will expire old reflog entries (90 days by default)\nthough, so it's not like they will stay around forever.\n-- \n-Dan\n"},{"id":"195335","messageId":"733EACD1-353C-4256-BA8B-DFDCE1C7D6F1@gmail.com","threadId":"31051","inReplyTo":"CAPBPrntB3ixuRFDP5fp8saJoEZvYOEd631Nh2=-WGudB-UK=kw@mail.gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-19T16:51:15Z","receivedAt":"2012-07-19T16:51:15Z","isPatch":false,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 19 Jul 2012, at 18:48, Dan Johnson wrote:\n\n> From the git-gc man page:\n> git gc tries very hard to be safe about the garbage it collects. In\n> particular, it will keep not only objects referenced by your current\n> set of branches and tags, but also objects referenced by the index,\n> remote-tracking branches, refs saved by git filter-branch in\n> refs/original/, or reflogs (which may reference commits in branches\n> that were later amended or rewound).\n> \n> So yes, a reflog entry does stop gc from removing objects, including\n> commits. It will expire old reflog entries (90 days by default)\n> though, so it's not like they will stay around forever.\n\nDan, thanks for the explanation.\n\nAlexey.\n"},{"id":"195342","messageId":"20120719213438.1cc7ca77a9cb3367a3be0539@domain007.com","threadId":"31051","inReplyTo":"88300470-AB41-4317-8B97-81DC18FD5899@gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Konstantin Khomoutov","fromEmail":"flatworm@users.sourceforge.net","sentAt":"2012-07-19T17:34:38Z","receivedAt":"2012-07-19T17:34:38Z","isPatch":false,"sender":{"key":"flatworm@users.sourceforge.net","avatar":null},"body":"On Thu, 19 Jul 2012 18:21:21 +0200\nAlexey Muranov <alexey.muranov@gmail.com> wrote:\n\n[...]\n> I do not still understand very well some aspects of Git, like the\n> exact purpose of \"remote tracking branches\" (are they for pull or for\n> push?), so i may be wrong.\nThis is wery well explained in the Pro Git book, for instance.\nAnd in numerous blog posts etc.\n\n> However, i thought that a user was not\n> expected to follow the moves of a remote branch of which the user is\n> not an owner: if the user needs to follow the brach and not lose its\n> commits, he/she should create a remote tracking branch.\nThis would present another namespacing issue: how would you name the\nbranches you're interested in so that they don't clash with your own\npersonal local branches?  You'd have to invent a scheme which would\nencode the remote's name in a branch name.  But remote branches already\ndo just this.  So you create a remote tracking branch when you intend\nto actually *develop* something on that branch with the final intention\nto push that work back.\n\n> > So I think it would be a lot more palatable if we kept reflogs on\n> > deleted branches. That, in turn, has a few open issues, such as how\n> > to manage namespace conflicts (e.g., the fact that a deleted \"foo\"\n> > branch can conflict with a new \"foo/bar\" branch).\n> \n> I prefer to think of a remote branch and its local copy as the same\n> thing, which are physically different only because of current real\n> world/hardware/software limitations, which make it necessary to keep\n> a local cache of remote data.  With this approach, reflogs should be\n> deleted with the branch, and there will be no namespace conflicts.\nIt appears, the distributed nature of a DVCS did not fully sink into\nyour mindset yet. ;-)\nLooks like you mentally treat a Git remote as a thing being used to\naccess a centralized \"reference\" server which maintains a master copy\nof a repository, of which you happen to also have a local copy.\nThen it's quite logically to think that if someone deleted a branch in\nthe master copy, everyone \"downstream\" should have the same\nremote branch deleted to be in sync with that master copy.\nBut this is not the only way to organize your work.\nYou could fetch from someone else's repository and be interested in\ntheir branch \"foo\", but think what happens when you fetch next time from\nthat repo and see Git happily deleting your local branch thatremote/foo\nsimply because someone with push access deleted that branch from the\nrepo.  This might *not* be what you really want or expect.\n"},{"id":"195345","messageId":"E94B0D74-2BB8-4B3E-BFB9-A2CFE9C2A7BB@gmail.com","threadId":"31051","inReplyTo":"20120719213438.1cc7ca77a9cb3367a3be0539@domain007.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-19T21:20:10Z","receivedAt":"2012-07-19T21:20:10Z","isPatch":false,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 19 Jul 2012, at 19:34, Konstantin Khomoutov wrote:\n\n> On Thu, 19 Jul 2012 18:21:21 +0200\n> Alexey Muranov <alexey.muranov@gmail.com> wrote:\n> \n> [...]\n>> I do not still understand very well some aspects of Git, like the\n>> exact purpose of \"remote tracking branches\" (are they for pull or for\n>> push?), so i may be wrong.\n> This is wery well explained in the Pro Git book, for instance.\n> And in numerous blog posts etc.\n\nI have read the Pro Gut book and numerous blog posts, but i keep forgetting the explanation because it does not make much sense to me:\n\n\"Tracking branches are local branches that have a direct relationship to a remote branch.  If you’re on a tracking branch and type git push, Git automatically knows which server and branch to push to.  Also, running git pull while on one of these branches fetches all the remote references and then automatically merges in the corresponding remote branch.\" etc.\n\nWhy the same \"direct relationship\" for push and pull?  What happens if one of the branches was reset (yes, i know, \"push -f\").  Most importantly, what is the purpose of it? It is natural to expect that you might be pushing to and pulling from different remotes, i can even imagine pulling from more than one.\n\n>> However, i thought that a user was not\n>> expected to follow the moves of a remote branch of which the user is\n>> not an owner: if the user needs to follow the brach and not lose its\n>> commits, he/she should create a remote tracking branch.\n> This would present another namespacing issue: how would you name the\n> branches you're interested in so that they don't clash with your own\n> personal local branches?  You'd have to invent a scheme which would\n> encode the remote's name in a branch name.  But remote branches already\n> do just this.  So you create a remote tracking branch when you intend\n> to actually *develop* something on that branch with the final intention\n> to push that work back.\n\nBut i am not interested in remote branches, they are just fetched automatically when i do \"git fetch\".  You cannot commit to a remote branch, and i think it is not common to checkout them without a \"-b\" option.  If i am interested in them, i name them somehow.  I think this is the only practical way if i do not want to chase reflogs, because the owner of the branch can reset or rebase it anytime.  I do not develop on tracking branches.  In fact, i am not even using \"git pull\".\n\n>>> So I think it would be a lot more palatable if we kept reflogs on\n>>> deleted branches. That, in turn, has a few open issues, such as how\n>>> to manage namespace conflicts (e.g., the fact that a deleted \"foo\"\n>>> branch can conflict with a new \"foo/bar\" branch).\n>> \n>> I prefer to think of a remote branch and its local copy as the same\n>> thing, which are physically different only because of current real\n>> world/hardware/software limitations, which make it necessary to keep\n>> a local cache of remote data.  With this approach, reflogs should be\n>> deleted with the branch, and there will be no namespace conflicts.\n> It appears, the distributed nature of a DVCS did not fully sink into\n> your mindset yet. ;-)\n> Looks like you mentally treat a Git remote as a thing being used to\n> access a centralized \"reference\" server which maintains a master copy\n> of a repository, of which you happen to also have a local copy.\n> Then it's quite logically to think that if someone deleted a branch in\n> the master copy, everyone \"downstream\" should have the same\n> remote branch deleted to be in sync with that master copy.\n> But this is not the only way to organize your work.\n> You could fetch from someone else's repository and be interested in\n> their branch \"foo\", but think what happens when you fetch next time from\n> that repo and see Git happily deleting your local branch thatremote/foo\n> simply because someone with push access deleted that branch from the\n> repo.  This might *not* be what you really want or expect.\n\nBut this is true that the object store of Git can be viewed as a single centralized repository.  The fact that not everybody has access to every object in Git is a limitation and not a benefit.  These are the branches which are individual, and i do not think it is a good habit to treat every reference that was ever fetched with \"git fetch\" as your own, and put reflogs of all fetched remote branches under Git version control :D.\n\nIf i care about \"thatremote/foo\" branch, i \"track\" it, i do not plan to go through reflogs if it is rebased.\n\nAlexey."},{"id":"195346","messageId":"20120719213225.GA20311@sigill.intra.peff.net","threadId":"31051","inReplyTo":"20120719115558.GC29774@sigill.intra.peff.net","subject":"[RFC/PATCH 0/3] reflog graveyard","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-19T21:32:25Z","receivedAt":"2012-07-19T21:32:25Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jul 19, 2012 at 07:55:58AM -0400, Jeff King wrote:\n\n> So I think it would be a lot more palatable if we kept reflogs on\n> deleted branches. That, in turn, has a few open issues, such as how to\n> manage namespace conflicts (e.g., the fact that a deleted \"foo\" branch\n> can conflict with a new \"foo/bar\" branch).\n\nHere is a patch series to address that. I think I have smoothed out most\nof the rough edges, but I wouldn't be surprised if there are some other\ncorner cases. One that I notice is that \"git log -g\" will stop walking\nwhen it hits a null sha1 in the reflog.\n\n  [1/3]: retain reflogs for deleted refs\n  [2/3]: teach sha1_name to look in graveyard reflogs\n  [3/3]: add tests for reflogs of deleted refs\n\n-Peff\n"},{"id":"195347","messageId":"20120719213311.GA20385@sigill.intra.peff.net","threadId":"31051","inReplyTo":"20120719213225.GA20311@sigill.intra.peff.net","subject":"[PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-19T21:33:11Z","receivedAt":"2012-07-19T21:33:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When a ref is deleted, we completely delete its reflog on\nthe spot, leaving very little help for the user to reverse\nthe action. One can sometimes reconstruct the missing\nentries based on the HEAD reflog, but not always; the\ndeleted entries may not have ever been on HEAD (for example,\nin the case of a refs/remotes branch that was pruned). That\nleaves \"git fsck --lost-found\", which can be quite tedious.\n\nInstead, let's keep the reflogs for deleted refs around\nuntil their entries naturally expire according to the\nregular reflog expiration rules.\n\nThis cannot be done by simply leaving the reflog files in\nplace. The ref namespace does not allow D/F conflicts, so a\nref \"foo\" would block the creation of another ref \"foo/bar\",\nand vice versa. This limitation is acceptable for two refs\nto exist simultaneously, but should not have an impact if\none of the refs is deleted.\n\nThis patch moves reflog entries into a special \"graveyard\"\nnamespace, and appends a tilde (~) character, which is\nnot allowed in a valid ref name. This means that the deleted\nreflogs of these refs:\n\n   refs/heads/a\n   refs/heads/a/b\n   refs/heads/a/b/c\n\nwill be stored in:\n\n   logs/graveyard/refs/heads/a~\n   logs/graveyard/refs/heads/a/b~\n   logs/graveyard/refs/heads/a/b/c~\n\nPutting them in the graveyard namespace ensures they will\nnot conflict with live refs, and the tilde prevents D/F\nconflicts within the graveyard namespace.\n\nThe implementation is fairly straightforward, but it's worth\nnoting a few things:\n\n  1. Updates to \"logs/graveyard/refs/heads/foo~\" happen\n     under the ref-lock for \"refs/heads/foo\". So deletion\n     still takes a single lock, and anyone touching the\n     reflog directly needs to reverse the transformation to\n     find the correct lockfile.\n\n  2. We append entries to the graveyard reflog rather than\n     simply renaming the file into place. This means that\n     if you create and delete a branch repeatedly, the\n     graveyard will contain the concatenation of all\n     iterations.\n\n  3. We do not resurrect dead entries when a new ref is\n     created with the same name. However, it would be\n     possible to build an \"undelete\" feature on top of this\n     if one was so inclined.\n\n  4. The for_each_reflog code has been loosened to allow\n     reflogs that do not have a matching ref. In this case,\n     the callback is passed the null_sha1, and callers must\n     be prepared to handle this case (the only caller that\n     cares is the reflog expiration code, which is updated\n     here).\n\nOnly one test needed to be updated; t7701 tries to create\nunreachable objects by deleting branches. Of course that no\nlonger works, which is the intent of this patch. The test\nnow works around it by removing the graveyard logs.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/reflog.c                     |  9 +++--\n refs.c                               | 69 +++++++++++++++++++++++++++++++++---\n refs.h                               |  3 ++\n t/t7701-repack-unpack-unreachable.sh |  5 ++-\n 4 files changed, 79 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex b3c9e27..e79a2ca 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -359,6 +359,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n \tstruct commit *tip_commit;\n \tstruct commit_list *tips;\n \tint status = 0;\n+\tint updateref = cmd->updateref && !is_null_sha1(sha1);\n \n \tmemset(&cb, 0, sizeof(cb));\n \n@@ -367,6 +368,10 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n \t * getting updated.\n \t */\n \tlock = lock_any_ref_for_update(ref, sha1, 0);\n+\tif (!lock && is_null_sha1(sha1))\n+\t\tlock = lock_any_ref_for_update(\n+\t\t\t\tgraveyard_reflog_to_refname(ref),\n+\t\t\t\tsha1, 0);\n \tif (!lock)\n \t\treturn error(\"cannot lock ref '%s'\", ref);\n \tlog_file = git_pathdup(\"logs/%s\", ref);\n@@ -426,7 +431,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n \t\t\tstatus |= error(\"%s: %s\", strerror(errno),\n \t\t\t\t\tnewlog_path);\n \t\t\tunlink(newlog_path);\n-\t\t} else if (cmd->updateref &&\n+\t\t} else if (updateref &&\n \t\t\t(write_in_full(lock->lock_fd,\n \t\t\t\tsha1_to_hex(cb.last_kept_sha1), 40) != 40 ||\n \t\t\t write_str_in_full(lock->lock_fd, \"\\n\") != 1 ||\n@@ -438,7 +443,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n \t\t\tstatus |= error(\"cannot rename %s to %s\",\n \t\t\t\t\tnewlog_path, log_file);\n \t\t\tunlink(newlog_path);\n-\t\t} else if (cmd->updateref && commit_ref(lock)) {\n+\t\t} else if (updateref && commit_ref(lock)) {\n \t\t\tstatus |= error(\"Couldn't set %s\", lock->ref_name);\n \t\t} else {\n \t\t\tadjust_shared_perm(log_file);\ndiff --git a/refs.c b/refs.c\nindex da74a2b..553de77 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -4,6 +4,8 @@\n #include \"tag.h\"\n #include \"dir.h\"\n \n+static void mark_reflog_deleted(struct ref_lock *lock);\n+\n /*\n  * Make sure \"ref\" is something reasonable to have under \".git/refs/\";\n  * We do not like it if:\n@@ -1780,7 +1782,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n \t */\n \tret |= repack_without_ref(refname);\n \n-\tunlink_or_warn(git_path(\"logs/%s\", lock->ref_name));\n+\tmark_reflog_deleted(lock);\n \tinvalidate_ref_cache(NULL);\n \tunlock_ref(lock);\n \treturn ret;\n@@ -2385,9 +2387,8 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data\n \t\t\t} else {\n \t\t\t\tunsigned char sha1[20];\n \t\t\t\tif (read_ref_full(name->buf, sha1, 0, NULL))\n-\t\t\t\t\tretval = error(\"bad ref for %s\", name->buf);\n-\t\t\t\telse\n-\t\t\t\t\tretval = fn(name->buf, sha1, 0, cb_data);\n+\t\t\t\t\thashcpy(sha1, null_sha1);\n+\t\t\t\tretval = fn(name->buf, sha1, 0, cb_data);\n \t\t\t}\n \t\t\tif (retval)\n \t\t\t\tbreak;\n@@ -2552,3 +2553,63 @@ char *shorten_unambiguous_ref(const char *refname, int strict)\n \tfree(short_name);\n \treturn xstrdup(refname);\n }\n+\n+char *refname_to_graveyard_reflog(const char *ref)\n+{\n+\treturn git_path(\"logs/graveyard/%s~\", ref);\n+}\n+\n+char *graveyard_reflog_to_refname(const char *log)\n+{\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\n+\tif (!prefixcmp(log, \"graveyard/\"))\n+\t\tlog += 10;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addstr(&buf, log);\n+\tif (buf.len > 0 && buf.buf[buf.len-1] == '~')\n+\t\tstrbuf_setlen(&buf, buf.len - 1);\n+\n+\treturn buf.buf;\n+}\n+\n+static int copy_reflog_entries(const char *dst, const char *src)\n+{\n+\tint fdi, fdo, status;\n+\n+\tfdi = open(src, O_RDONLY);\n+\tif (fdi < 0)\n+\t\treturn errno == ENOENT ? 0 : -1;\n+\n+\tfdo = open(dst, O_WRONLY | O_APPEND | O_CREAT, 0666);\n+\tif (fdo < 0) {\n+\t\tclose(fdi);\n+\t\treturn -1;\n+\t}\n+\n+\tstatus = copy_fd(fdi, fdo);\n+\tif (close(fdo) < 0)\n+\t\treturn -1;\n+\tif (status < 0 || adjust_shared_perm(dst) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static void mark_reflog_deleted(struct ref_lock *lock)\n+{\n+\tstatic const char msg[] = \"ref deleted\";\n+\tconst char *log = git_path(\"logs/%s\", lock->ref_name);\n+\tchar *grave = refname_to_graveyard_reflog(lock->ref_name);\n+\n+\tif (log_ref_write(lock->ref_name, lock->old_sha1, null_sha1, msg) < 0)\n+\t\twarning(\"unable to update reflog for %s: %s\",\n+\t\t\tlock->ref_name, strerror(errno));\n+\n+\tif (safe_create_leading_directories(grave) < 0 ||\n+\t    copy_reflog_entries(grave, log) < 0)\n+\t\twarning(\"unable to copy reflog entries to graveyard: %s\",\n+\t\t\tstrerror(errno));\n+\n+\tunlink_or_warn(log);\n+}\ndiff --git a/refs.h b/refs.h\nindex d6c2fe2..9d14558 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -111,6 +111,9 @@ int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long,\n  */\n extern int for_each_reflog(each_ref_fn, void *);\n \n+char *refname_to_graveyard_reflog(const char *ref);\n+char *graveyard_reflog_to_refname(const char *log);\n+\n #define REFNAME_ALLOW_ONELEVEL 1\n #define REFNAME_REFSPEC_PATTERN 2\n #define REFNAME_DOT_COMPONENT 4\ndiff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh\nindex b8d4cde..c06b715 100755\n--- a/t/t7701-repack-unpack-unreachable.sh\n+++ b/t/t7701-repack-unpack-unreachable.sh\n@@ -38,7 +38,9 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '\n \tgit show $csha1 &&\n \tgit show $tsha1 &&\n \t# now expire the reflog, while keeping reachable ones but expiring\n-\t# unreachables immediately\n+\t# unreachables immediately; also remove any graveyard reflogs\n+\t# from deleted branches that would keep things reachable\n+\trm -rf .git/logs/graveyard &&\n \ttest_tick &&\n \tsometimeago=$(( $test_tick - 10000 )) &&\n \tgit reflog expire --expire=$sometimeago --expire-unreachable=$test_tick --all &&\n@@ -76,6 +78,7 @@ test_expect_success '-A without -d option leaves unreachable objects packed' '\n \ttest 1 = $(ls -1 .git/objects/pack/pack-*.pack | wc -l) &&\n \tpackfile=$(ls .git/objects/pack/pack-*.pack) &&\n \tgit branch -D transient_branch &&\n+\trm -rf .git/logs/graveyard &&\n \ttest_tick &&\n \tgit repack -A -l &&\n \ttest ! -f \"$fsha1path\" &&\n-- \n1.7.10.5.40.g059818d\n"},{"id":"195348","messageId":"20120719213326.GB20385@sigill.intra.peff.net","threadId":"31051","inReplyTo":"20120719213225.GA20311@sigill.intra.peff.net","subject":"[PATCH 2/3] teach sha1_name to look in graveyard reflogs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-19T21:33:26Z","receivedAt":"2012-07-19T21:33:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The previous commit introduced graveyard reflogs, where the\nreflog for a deleted branch \"foo\" appears in\n\"logs/graveyard/refs/heads/foo~\".\n\nThis patch teaches dwim_log to search for these logs if the\nref does not exist, and teaches read_ref_at to fall back to\nthem when the literal reflog does not exist.  This allows\n\"deleted@{1}\" to refer to the final commit of a deleted\nbranch (either to view or to re-create the branch).  You can\nalso go further back, or refer to the deleted reflog entries\nby time. Accessing deleted@{0} will yield the null sha1.\n\nSimilarly, for_each_reflog_ent learns to fallback to\ngraveyard refs, which allows the reflog walker to work.\nHowever, this is slightly less friendly, as the revision\nparser expects the matching ref to exist before it realizes\nthat we are interested in the reflog. Therefore you must use\n\"git log -g deleted@{1}\" insted of \"git log -g deleted\" to\nwalk a deleted reflog.\n\nIn both cases, we also tighten up the mode-checking when\nopening the reflogs. dwim_log checks that the entry we found\nis a regular file (not a directory) to avoid D/F confusion\n(e.g., you ask for \"foo\" but \"foo/bar\" exists and we find\nthe \"foo\" but it is a directory).\n\nHowever, read_ref_at and for_each_reflog_ent did not do this\ncheck, and relied on earlier parts of the code to have\nverified the log they are about to open. This meant that\neven before this patch, a race condition in changing refs\nbetween dwim_log and the actual read could cause bizarre\nerrors (e.g., read_ref_at would open and try to mmap a\ndirectory). This patch makes it even easier to trigger those\nconditions (because the ref namespace and the fallback\ngraveyard namespace can have D/F ambiguity for a certain\npath). To solve this, we check the mode of the file we open\nand treat it as if it did not exist if it is not a regular\nfile (this is the same way dwim_log handles it).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n refs.c | 46 +++++++++++++++++++++++++++++++++++-----------\n 1 file changed, 35 insertions(+), 11 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 553de77..551a0f9 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1590,9 +1590,16 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)\n \n \t\tmksnpath(path, sizeof(path), *p, len, str);\n \t\tref = resolve_ref_unsafe(path, hash, 1, NULL);\n-\t\tif (!ref)\n-\t\t\tcontinue;\n-\t\tif (!stat(git_path(\"logs/%s\", path), &st) &&\n+\t\tif (!ref) {\n+\t\t\tif (!stat(refname_to_graveyard_reflog(path), &st) &&\n+\t\t\t    S_ISREG(st.st_mode)) {\n+\t\t\t\tit = path;\n+\t\t\t\thashcpy(hash, null_sha1);\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\tcontinue;\n+\t\t}\n+\t\telse if (!stat(git_path(\"logs/%s\", path), &st) &&\n \t\t    S_ISREG(st.st_mode))\n \t\t\tit = path;\n \t\telse if (strcmp(ref, path) &&\n@@ -2201,9 +2208,16 @@ int read_ref_at(const char *refname, unsigned long at_time, int cnt,\n \n \tlogfile = git_path(\"logs/%s\", refname);\n \tlogfd = open(logfile, O_RDONLY, 0);\n-\tif (logfd < 0)\n-\t\tdie_errno(\"Unable to read log '%s'\", logfile);\n-\tfstat(logfd, &st);\n+\tif (logfd < 0 || fstat(logfd, &st) < 0 || !S_ISREG(st.st_mode)) {\n+\t\tconst char *deleted_log = refname_to_graveyard_reflog(refname);\n+\n+\t\tif (logfd >= 0)\n+\t\t\tclose(logfd);\n+\t\tlogfd = open(deleted_log, O_RDONLY);\n+\t\tif (logfd < 0 || fstat(logfd, &st) < 0 || !S_ISREG(st.st_mode))\n+\t\t\tdie_errno(\"Unable to read log '%s'\", logfile);\n+\t\tlogfile = deleted_log;\n+\t}\n \tif (!st.st_size)\n \t\tdie(\"Log %s is empty.\", logfile);\n \tmapsz = xsize_t(st.st_size);\n@@ -2296,18 +2310,28 @@ int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long\n {\n \tconst char *logfile;\n \tFILE *logfp;\n+\tstruct stat st;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n \tlogfile = git_path(\"logs/%s\", refname);\n \tlogfp = fopen(logfile, \"r\");\n-\tif (!logfp)\n-\t\treturn -1;\n+\tif (!logfp || fstat(fileno(logfp), &st) < 0 || !S_ISREG(st.st_mode)) {\n+\t\tlogfile = refname_to_graveyard_reflog(refname);\n+\n+\t\tif (logfp)\n+\t\t\tfclose(logfp);\n+\t\tlogfp = fopen(logfile, \"r\");\n+\t\tif (!logfp)\n+\t\t\treturn -1;\n+\t\tif (fstat(fileno(logfp), &st) < 0 || !S_ISREG(st.st_mode)) {\n+\t\t\tfclose(logfp);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n \n \tif (ofs) {\n-\t\tstruct stat statbuf;\n-\t\tif (fstat(fileno(logfp), &statbuf) ||\n-\t\t    statbuf.st_size < ofs ||\n+\t\tif (st.st_size < ofs ||\n \t\t    fseek(logfp, -ofs, SEEK_END) ||\n \t\t    strbuf_getwholeline(&sb, logfp, '\\n')) {\n \t\t\tfclose(logfp);\n-- \n1.7.10.5.40.g059818d\n"},{"id":"195349","messageId":"20120719213332.GC20385@sigill.intra.peff.net","threadId":"31051","inReplyTo":"20120719213225.GA20311@sigill.intra.peff.net","subject":"[PATCH 3/3] add tests for reflogs of deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-19T21:33:32Z","receivedAt":"2012-07-19T21:33:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"These tests cover the basic functionality of retaining\nreflogs for deleted refs.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n t/t1413-reflog-deletion.sh | 74 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 74 insertions(+)\n create mode 100755 t/t1413-reflog-deletion.sh\n\ndiff --git a/t/t1413-reflog-deletion.sh b/t/t1413-reflog-deletion.sh\nnew file mode 100755\nindex 0000000..e00d038\n--- /dev/null\n+++ b/t/t1413-reflog-deletion.sh\n@@ -0,0 +1,74 @@\n+#!/bin/sh\n+\n+test_description='test retention of reflog after ref deletion'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup deleted branch' '\n+\ttest_tick && echo one >file && git add file && git commit -m one &&\n+\ttest_tick && echo two >file && git add file && git commit -m two &&\n+\tgit checkout -b foo/bar &&\n+\ttest_tick && echo three >file && git add file && git commit -m three &&\n+\tgit checkout master &&\n+\tgit branch -D foo/bar &&\n+\trm -f .git/logs/HEAD\n+'\n+\n+test_expect_success 'branch is no longer accessible' '\n+\ttest_must_fail git rev-parse --verify foo/bar\n+'\n+\n+test_expect_success 'final reflog is null sha1' '\n+\techo $_z40 >expect &&\n+\tgit rev-parse --verify foo/bar@{0} >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'deleted reflog entries are accessible' '\n+\tcat >expect <<-\\EOF &&\n+\tthree\n+\ttwo\n+\tEOF\n+\t{\n+\t\tgit log -1 --format=%s foo/bar@{1}\n+\t\tgit log -1 --format=%s foo/bar@{2}\n+\t} >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'reflog walker can find deleted entries' '\n+\tcat >expect <<-\\EOF &&\n+\tthree\n+\ttwo\n+\tEOF\n+\tgit log -g --format=%s foo/bar@{1} >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'can still create/delete same ref' '\n+\tgit branch foo/bar &&\n+\tgit branch -D foo/bar\n+'\n+\n+test_expect_success 'can still create/delete parent ref' '\n+\tgit branch foo &&\n+\tgit branch -D foo\n+'\n+\n+test_expect_success 'can still create/delete child ref' '\n+\tgit branch foo/bar/baz &&\n+\tgit branch -D foo/bar/baz\n+'\n+\n+test_expect_success 'deleted reflog entries are still reachable' '\n+\t>expect &&\n+\tgit fsck --unreachable >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'deleted reflog entries are expired normally' '\n+\tgit reflog expire --all --expire=now &&\n+\tgit fsck --unreachable >actual &&\n+\ttest_line_count = 3 actual\n+'\n+\n+test_done\n-- \n1.7.10.5.40.g059818d\n"},{"id":"195350","messageId":"81C40C14-0CAD-41E1-AD16-ABA4D9002D00@gmail.com","threadId":"31051","inReplyTo":"E94B0D74-2BB8-4B3E-BFB9-A2CFE9C2A7BB@gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-19T21:57:21Z","receivedAt":"2012-07-19T21:57:21Z","isPatch":false,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"I just want to correct my mistake in what i've just sent:\n\nOn 19 Jul 2012, at 23:20, Alexey Muranov wrote:\n\n> because the owner of the branch can reset or rebase it anytime.  I do not develop on tracking branches.  In fact, i am not even using \"git pull\".\n\n> I do not develop on tracking branches.\n\nOf course i develop on \"tracking\" branches, i just got confused once again by pull/push thing: i develop on branches that track origin, not upstream.\nI think they should be called \"remotely tracked branches\", so there would be \"remote tracking branches\" for pull and \"remotely tracked branches\" for push.\n\nAlexey."},{"id":"195351","messageId":"7515FF5F-2B4F-4CD0-B4A3-D2B1328AE313@gmail.com","threadId":"31051","inReplyTo":"20120719213311.GA20385@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-19T22:23:12Z","receivedAt":"2012-07-19T22:23:12Z","isPatch":true,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"Jeff,\n\ni have no idea about Git source and little idea of how it is working internally, but reading through your message i wonder: wouldn't it be a good idea to timestamp the dead reflogs ?\n\nAlexey.\n\nOn 19 Jul 2012, at 23:33, Jeff King wrote:\n\n> When a ref is deleted, we completely delete its reflog on\n> the spot, leaving very little help for the user to reverse\n> the action. One can sometimes reconstruct the missing\n> entries based on the HEAD reflog, but not always; the\n> deleted entries may not have ever been on HEAD (for example,\n> in the case of a refs/remotes branch that was pruned). That\n> leaves \"git fsck --lost-found\", which can be quite tedious.\n> \n> Instead, let's keep the reflogs for deleted refs around\n> until their entries naturally expire according to the\n> regular reflog expiration rules.\n> \n> This cannot be done by simply leaving the reflog files in\n> place. The ref namespace does not allow D/F conflicts, so a\n> ref \"foo\" would block the creation of another ref \"foo/bar\",\n> and vice versa. This limitation is acceptable for two refs\n> to exist simultaneously, but should not have an impact if\n> one of the refs is deleted.\n> \n> This patch moves reflog entries into a special \"graveyard\"\n> namespace, and appends a tilde (~) character, which is\n> not allowed in a valid ref name. This means that the deleted\n> reflogs of these refs:\n> \n>   refs/heads/a\n>   refs/heads/a/b\n>   refs/heads/a/b/c\n> \n> will be stored in:\n> \n>   logs/graveyard/refs/heads/a~\n>   logs/graveyard/refs/heads/a/b~\n>   logs/graveyard/refs/heads/a/b/c~\n> \n> Putting them in the graveyard namespace ensures they will\n> not conflict with live refs, and the tilde prevents D/F\n> conflicts within the graveyard namespace.\n> \n> The implementation is fairly straightforward, but it's worth\n> noting a few things:\n> \n>  1. Updates to \"logs/graveyard/refs/heads/foo~\" happen\n>     under the ref-lock for \"refs/heads/foo\". So deletion\n>     still takes a single lock, and anyone touching the\n>     reflog directly needs to reverse the transformation to\n>     find the correct lockfile.\n> \n>  2. We append entries to the graveyard reflog rather than\n>     simply renaming the file into place. This means that\n>     if you create and delete a branch repeatedly, the\n>     graveyard will contain the concatenation of all\n>     iterations.\n> \n>  3. We do not resurrect dead entries when a new ref is\n>     created with the same name. However, it would be\n>     possible to build an \"undelete\" feature on top of this\n>     if one was so inclined.\n> \n>  4. The for_each_reflog code has been loosened to allow\n>     reflogs that do not have a matching ref. In this case,\n>     the callback is passed the null_sha1, and callers must\n>     be prepared to handle this case (the only caller that\n>     cares is the reflog expiration code, which is updated\n>     here).\n> \n> Only one test needed to be updated; t7701 tries to create\n> unreachable objects by deleting branches. Of course that no\n> longer works, which is the intent of this patch. The test\n> now works around it by removing the graveyard logs.\n> \n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n> builtin/reflog.c                     |  9 +++--\n> refs.c                               | 69 +++++++++++++++++++++++++++++++++---\n> refs.h                               |  3 ++\n> t/t7701-repack-unpack-unreachable.sh |  5 ++-\n> 4 files changed, 79 insertions(+), 7 deletions(-)\n> \n> diff --git a/builtin/reflog.c b/builtin/reflog.c\n> index b3c9e27..e79a2ca 100644\n> --- a/builtin/reflog.c\n> +++ b/builtin/reflog.c\n> @@ -359,6 +359,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n> \tstruct commit *tip_commit;\n> \tstruct commit_list *tips;\n> \tint status = 0;\n> +\tint updateref = cmd->updateref && !is_null_sha1(sha1);\n> \n> \tmemset(&cb, 0, sizeof(cb));\n> \n> @@ -367,6 +368,10 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n> \t * getting updated.\n> \t */\n> \tlock = lock_any_ref_for_update(ref, sha1, 0);\n> +\tif (!lock && is_null_sha1(sha1))\n> +\t\tlock = lock_any_ref_for_update(\n> +\t\t\t\tgraveyard_reflog_to_refname(ref),\n> +\t\t\t\tsha1, 0);\n> \tif (!lock)\n> \t\treturn error(\"cannot lock ref '%s'\", ref);\n> \tlog_file = git_pathdup(\"logs/%s\", ref);\n> @@ -426,7 +431,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n> \t\t\tstatus |= error(\"%s: %s\", strerror(errno),\n> \t\t\t\t\tnewlog_path);\n> \t\t\tunlink(newlog_path);\n> -\t\t} else if (cmd->updateref &&\n> +\t\t} else if (updateref &&\n> \t\t\t(write_in_full(lock->lock_fd,\n> \t\t\t\tsha1_to_hex(cb.last_kept_sha1), 40) != 40 ||\n> \t\t\t write_str_in_full(lock->lock_fd, \"\\n\") != 1 ||\n> @@ -438,7 +443,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,\n> \t\t\tstatus |= error(\"cannot rename %s to %s\",\n> \t\t\t\t\tnewlog_path, log_file);\n> \t\t\tunlink(newlog_path);\n> -\t\t} else if (cmd->updateref && commit_ref(lock)) {\n> +\t\t} else if (updateref && commit_ref(lock)) {\n> \t\t\tstatus |= error(\"Couldn't set %s\", lock->ref_name);\n> \t\t} else {\n> \t\t\tadjust_shared_perm(log_file);\n> diff --git a/refs.c b/refs.c\n> index da74a2b..553de77 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -4,6 +4,8 @@\n> #include \"tag.h\"\n> #include \"dir.h\"\n> \n> +static void mark_reflog_deleted(struct ref_lock *lock);\n> +\n> /*\n>  * Make sure \"ref\" is something reasonable to have under \".git/refs/\";\n>  * We do not like it if:\n> @@ -1780,7 +1782,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n> \t */\n> \tret |= repack_without_ref(refname);\n> \n> -\tunlink_or_warn(git_path(\"logs/%s\", lock->ref_name));\n> +\tmark_reflog_deleted(lock);\n> \tinvalidate_ref_cache(NULL);\n> \tunlock_ref(lock);\n> \treturn ret;\n> @@ -2385,9 +2387,8 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data\n> \t\t\t} else {\n> \t\t\t\tunsigned char sha1[20];\n> \t\t\t\tif (read_ref_full(name->buf, sha1, 0, NULL))\n> -\t\t\t\t\tretval = error(\"bad ref for %s\", name->buf);\n> -\t\t\t\telse\n> -\t\t\t\t\tretval = fn(name->buf, sha1, 0, cb_data);\n> +\t\t\t\t\thashcpy(sha1, null_sha1);\n> +\t\t\t\tretval = fn(name->buf, sha1, 0, cb_data);\n> \t\t\t}\n> \t\t\tif (retval)\n> \t\t\t\tbreak;\n> @@ -2552,3 +2553,63 @@ char *shorten_unambiguous_ref(const char *refname, int strict)\n> \tfree(short_name);\n> \treturn xstrdup(refname);\n> }\n> +\n> +char *refname_to_graveyard_reflog(const char *ref)\n> +{\n> +\treturn git_path(\"logs/graveyard/%s~\", ref);\n> +}\n> +\n> +char *graveyard_reflog_to_refname(const char *log)\n> +{\n> +\tstatic struct strbuf buf = STRBUF_INIT;\n> +\n> +\tif (!prefixcmp(log, \"graveyard/\"))\n> +\t\tlog += 10;\n> +\n> +\tstrbuf_reset(&buf);\n> +\tstrbuf_addstr(&buf, log);\n> +\tif (buf.len > 0 && buf.buf[buf.len-1] == '~')\n> +\t\tstrbuf_setlen(&buf, buf.len - 1);\n> +\n> +\treturn buf.buf;\n> +}\n> +\n> +static int copy_reflog_entries(const char *dst, const char *src)\n> +{\n> +\tint fdi, fdo, status;\n> +\n> +\tfdi = open(src, O_RDONLY);\n> +\tif (fdi < 0)\n> +\t\treturn errno == ENOENT ? 0 : -1;\n> +\n> +\tfdo = open(dst, O_WRONLY | O_APPEND | O_CREAT, 0666);\n> +\tif (fdo < 0) {\n> +\t\tclose(fdi);\n> +\t\treturn -1;\n> +\t}\n> +\n> +\tstatus = copy_fd(fdi, fdo);\n> +\tif (close(fdo) < 0)\n> +\t\treturn -1;\n> +\tif (status < 0 || adjust_shared_perm(dst) < 0)\n> +\t\treturn -1;\n> +\treturn 0;\n> +}\n> +\n> +static void mark_reflog_deleted(struct ref_lock *lock)\n> +{\n> +\tstatic const char msg[] = \"ref deleted\";\n> +\tconst char *log = git_path(\"logs/%s\", lock->ref_name);\n> +\tchar *grave = refname_to_graveyard_reflog(lock->ref_name);\n> +\n> +\tif (log_ref_write(lock->ref_name, lock->old_sha1, null_sha1, msg) < 0)\n> +\t\twarning(\"unable to update reflog for %s: %s\",\n> +\t\t\tlock->ref_name, strerror(errno));\n> +\n> +\tif (safe_create_leading_directories(grave) < 0 ||\n> +\t    copy_reflog_entries(grave, log) < 0)\n> +\t\twarning(\"unable to copy reflog entries to graveyard: %s\",\n> +\t\t\tstrerror(errno));\n> +\n> +\tunlink_or_warn(log);\n> +}\n> diff --git a/refs.h b/refs.h\n> index d6c2fe2..9d14558 100644\n> --- a/refs.h\n> +++ b/refs.h\n> @@ -111,6 +111,9 @@ int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long,\n>  */\n> extern int for_each_reflog(each_ref_fn, void *);\n> \n> +char *refname_to_graveyard_reflog(const char *ref);\n> +char *graveyard_reflog_to_refname(const char *log);\n> +\n> #define REFNAME_ALLOW_ONELEVEL 1\n> #define REFNAME_REFSPEC_PATTERN 2\n> #define REFNAME_DOT_COMPONENT 4\n> diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh\n> index b8d4cde..c06b715 100755\n> --- a/t/t7701-repack-unpack-unreachable.sh\n> +++ b/t/t7701-repack-unpack-unreachable.sh\n> @@ -38,7 +38,9 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '\n> \tgit show $csha1 &&\n> \tgit show $tsha1 &&\n> \t# now expire the reflog, while keeping reachable ones but expiring\n> -\t# unreachables immediately\n> +\t# unreachables immediately; also remove any graveyard reflogs\n> +\t# from deleted branches that would keep things reachable\n> +\trm -rf .git/logs/graveyard &&\n> \ttest_tick &&\n> \tsometimeago=$(( $test_tick - 10000 )) &&\n> \tgit reflog expire --expire=$sometimeago --expire-unreachable=$test_tick --all &&\n> @@ -76,6 +78,7 @@ test_expect_success '-A without -d option leaves unreachable objects packed' '\n> \ttest 1 = $(ls -1 .git/objects/pack/pack-*.pack | wc -l) &&\n> \tpackfile=$(ls .git/objects/pack/pack-*.pack) &&\n> \tgit branch -D transient_branch &&\n> +\trm -rf .git/logs/graveyard &&\n> \ttest_tick &&\n> \tgit repack -A -l &&\n> \ttest ! -f \"$fsha1path\" &&\n> -- \n> 1.7.10.5.40.g059818d\n> \n"},{"id":"195352","messageId":"7vy5mftm3q.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"20120719213311.GA20385@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-19T22:36:09Z","receivedAt":"2012-07-19T22:36:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> Only one test needed to be updated; t7701 tries to create\n> unreachable objects by deleting branches. Of course that no\n> longer works, which is the intent of this patch. The test\n> now works around it by removing the graveyard logs.\n\nI think the work-around indicates the need for regular users to be\nable to also discover, prune and delete these logs.  Do we have\n\"prune reflog for _this_ ref (or these refs), removing entries that\nare older than this threshold\"?  If so the codepath would need to\nknow about the graveyard and the implementation detail of the tilde\nsuffix so that the end users do not need to know about them.\n\nI like the general direction.  Perhaps a long distant future\ndirection could be to also use the same trick in the ref namespace\nso that we can have 'next' branch itself, and 'next/foo', 'next/bar'\nforks that are based on the 'next' branch at the same time (it\nobviously is a totally unrelated topic)?\n"},{"id":"195353","messageId":"7vtxx3tlyb.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"20120719213326.GB20385@sigill.intra.peff.net","subject":"Re: [PATCH 2/3] teach sha1_name to look in graveyard reflogs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-19T22:39:24Z","receivedAt":"2012-07-19T22:39:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> The previous commit introduced graveyard reflogs, where the\n> reflog for a deleted branch \"foo\" appears in\n> \"logs/graveyard/refs/heads/foo~\".\n>\n> This patch teaches dwim_log to search for these logs if the\n> ref does not exist, and teaches read_ref_at to fall back to\n> them when the literal reflog does not exist.  This allows\n> \"deleted@{1}\" to refer to the final commit of a deleted\n> branch (either to view or to re-create the branch).  You can\n> also go further back, or refer to the deleted reflog entries\n> by time. Accessing deleted@{0} will yield the null sha1.\n>\n> Similarly, for_each_reflog_ent learns to fallback to\n> graveyard refs, which allows the reflog walker to work.\n> However, this is slightly less friendly, as the revision\n> parser expects the matching ref to exist before it realizes\n> that we are interested in the reflog. Therefore you must use\n> \"git log -g deleted@{1}\" insted of \"git log -g deleted\" to\n> walk a deleted reflog.\n>\n> In both cases, we also tighten up the mode-checking when\n> opening the reflogs. dwim_log checks that the entry we found\n> is a regular file (not a directory) to avoid D/F confusion\n> (e.g., you ask for \"foo\" but \"foo/bar\" exists and we find\n> the \"foo\" but it is a directory).\n>\n> However, read_ref_at and for_each_reflog_ent did not do this\n> check, and relied on earlier parts of the code to have\n> verified the log they are about to open. This meant that\n> even before this patch, a race condition in changing refs\n> between dwim_log and the actual read could cause bizarre\n> errors (e.g., read_ref_at would open and try to mmap a\n> directory). This patch makes it even easier to trigger those\n> conditions (because the ref namespace and the fallback\n> graveyard namespace can have D/F ambiguity for a certain\n> path). To solve this, we check the mode of the file we open\n> and treat it as if it did not exist if it is not a regular\n> file (this is the same way dwim_log handles it).\n>\n> Signed-off-by: Jeff King <peff@peff.net>\n\nThis may or may not be related, but I vaguely recall that \"log -g\"\ntraversal hack had a corner case where the walking stops prematurely\nupon seeing a gap (or creation/deletion that has 0{40})?  Do you\nrecall if we have ever dealt with that?\n\nThe patch seems fine from a cursory look.  Thanks.\n"},{"id":"195354","messageId":"500904B0.9030309@viscovery.net","threadId":"31051","inReplyTo":"E94B0D74-2BB8-4B3E-BFB9-A2CFE9C2A7BB@gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-07-20T07:11:44Z","receivedAt":"2012-07-20T07:11:44Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 7/19/2012 23:20, schrieb Alexey Muranov:\n> On 19 Jul 2012, at 19:34, Konstantin Khomoutov wrote:\n> \n>> On Thu, 19 Jul 2012 18:21:21 +0200 Alexey Muranov\n>> <alexey.muranov@gmail.com> wrote:\n>> \n>> [...]\n>>> I do not still understand very well some aspects of Git, like the \n>>> exact purpose of \"remote tracking branches\" (are they for pull or\n>>> for push?), so i may be wrong.\n>> This is wery well explained in the Pro Git book, for instance. And in\n>> numerous blog posts etc.\n> \n> I have read the Pro Gut book and numerous blog posts, but i keep\n> forgetting the explanation because it does not make much sense to me:\n> \n> \"Tracking branches are local branches that have a direct relationship\n> to a remote branch.  If you’re on a tracking branch and type git push,\n> Git automatically knows which server and branch to push to.  Also,\n> running git pull while on one of these branches fetches all the remote\n> references and then automatically merges in the corresponding remote\n> branch.\" etc.\n\nNote the difference between \"tracking branch\" and \"remote tracking\nbranch\"! The \"remote tracking branches\" are the refs in the refs/remotes/\nhierarchy. The \"tracking branches\" are your own local branches that you\nhave created with 'git branch topic thatremote/topic' (or perhaps 'git\ncheckout -b'). The paragraph talks about the latter.\n\n-- Hannes\n"},{"id":"195355","messageId":"068F71AC-DACD-4545-AB21-F911A934DA3E@gmail.com","threadId":"31051","inReplyTo":"500904B0.9030309@viscovery.net","subject":"Re: Feature request: fetch --prune by default","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-20T07:28:08Z","receivedAt":"2012-07-20T07:28:08Z","isPatch":false,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"\nOn 20 Jul 2012, at 09:11, Johannes Sixt wrote:\n\n> Am 7/19/2012 23:20, schrieb Alexey Muranov:\n>> On 19 Jul 2012, at 19:34, Konstantin Khomoutov wrote:\n>> \n>>> On Thu, 19 Jul 2012 18:21:21 +0200 Alexey Muranov\n>>> <alexey.muranov@gmail.com> wrote:\n>>> \n>>> [...]\n>>>> I do not still understand very well some aspects of Git, like the \n>>>> exact purpose of \"remote tracking branches\" (are they for pull or\n>>>> for push?), so i may be wrong.\n>>> This is wery well explained in the Pro Git book, for instance. And in\n>>> numerous blog posts etc.\n>> \n>> I have read the Pro Gut book and numerous blog posts, but i keep\n>> forgetting the explanation because it does not make much sense to me:\n>> \n>> \"Tracking branches are local branches that have a direct relationship\n>> to a remote branch.  If you’re on a tracking branch and type git push,\n>> Git automatically knows which server and branch to push to.  Also,\n>> running git pull while on one of these branches fetches all the remote\n>> references and then automatically merges in the corresponding remote\n>> branch.\" etc.\n> \n> Note the difference between \"tracking branch\" and \"remote tracking\n> branch\"! The \"remote tracking branches\" are the refs in the refs/remotes/\n> hierarchy. The \"tracking branches\" are your own local branches that you\n> have created with 'git branch topic thatremote/topic' (or perhaps 'git\n> checkout -b'). The paragraph talks about the latter.\n\nHannes, thanks for the explanation, so i was confused once again.\n\nVarious blog posts do not make the terminology clear, for example\nhttp://gitready.com/beginner/2009/03/09/remote-tracking-branches.html\nsais that there are only \"two types of branches: local, and remote-tracking\", while i think it depends on perspective.\nThere are in fact\n1. remote,\n2. remote-tracking (which are local!),\n3. truly local:\n  a) which are tracking some remote-tracking(!) branches,\n  b) and which are not tracking.\n\nI think i was also misguided by Konstantin, who wrote that \"you create a remote tracking branch when you intend to actually *develop* something on that branch\" :).\n\n-Alexey."},{"id":"195358","messageId":"50092993.6010203@alum.mit.edu","threadId":"31051","inReplyTo":"20120719213311.GA20385@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-07-20T09:49:07Z","receivedAt":"2012-07-20T09:49:07Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 07/19/2012 11:33 PM, Jeff King wrote:\n> [...]\n> This cannot be done by simply leaving the reflog files in\n> place. The ref namespace does not allow D/F conflicts, so a\n> ref \"foo\" would block the creation of another ref \"foo/bar\",\n> and vice versa. This limitation is acceptable for two refs\n> to exist simultaneously, but should not have an impact if\n> one of the refs is deleted.\n\nThis is a great feature.\n\n> This patch moves reflog entries into a special \"graveyard\"\n> namespace, and appends a tilde (~) character, which is\n> not allowed in a valid ref name. This means that the deleted\n> reflogs of these refs:\n>\n>     refs/heads/a\n>     refs/heads/a/b\n>     refs/heads/a/b/c\n>\n> will be stored in:\n>\n>     logs/graveyard/refs/heads/a~\n>     logs/graveyard/refs/heads/a/b~\n>     logs/graveyard/refs/heads/a/b/c~\n>\n> Putting them in the graveyard namespace ensures they will\n> not conflict with live refs, and the tilde prevents D/F\n> conflicts within the graveyard namespace.\n\nI agree with Junio that long-term, it would be nice to allow references \n\"foo\" and \"foo/bar\" to exist simultaneously.  To get there, we would \nhave to redesign the mapping between reference names and the filenames \nused for the references and for the reflogs.\n\nThe easiest thing would be to mark files and directories differently; \nsomething like\n\n     $GIT_DIR/{,logs/}refs/heads/a/b/c~\n\nor\n\n     $GIT_DIR/{,logs/}refs/heads~/a~/b~/c\n\ni.e., munging either directory or file names to strings that are illegal \nin refnames such that it is unambiguous from the name whether a path is \na file or directory.\n\nAnd *if* we did that, then we wouldn't need a separate \"graveyard\" \nnamespace, would we?  The reflogs for dead references could live among \nthose for living references.\n\nTherefore, I think it would be good if we would choose a convention now \nfor dead reflogs that is compatible with this hoped-for future.\n\nThe first convention, \"logs/refs/heads/a/b/c~\" is not usable because a \nreflog for a dead reference with this name would conflict with a reflog \nfor a live reference \"heads/a\" or \"heads/a/b\" that uses the current \nfilename convention.\n\nBut the second convention, \"logs/refs/heads~/a~/b~/c, cannot conflict \nwith current reflog files.  And it would be a step towards allowing \n\"foo\" and \"foo/bar\" at the same time.  What do you think about using a \nconvention like this instead of the one that you proposed?\n\n\nAnother minor concern is the choice of trailing tilde in the file or \ndirectory names.  Given that emacs creates backup files by appending a \ntilde to the filename, (1) it would be easy to inadvertently create such \nfiles, which git might try to interpret as reflogs and (2) there might \nbe tools that innately \"know\" to skip such files in their processing. \nack-grep, a replacement for grep, is an example that springs to mind.  I \nknow that I have written backup scripts that ignore files matching \"*~\", \nand a garbage-removal script that removes files matching \"*~\".  Probably \nit is less precarious to name directories rather than files with \ntrailing tildes, but either one could be a surprise for sysadmins.\n\nOther possibilities (according to git-check-ref-format(1)):\n\n     refs/.heads/.a/.b/c\n     refs/heads./a./b./c (problematic on some Windows filesystems?)\n     refs/heads../a../b../c\n     refs/heads~dir/a~dir/b~dir/c (or some other suffix)\n     refs/heads..a..b..c (not recommended because it flattens directory \nhierarchy)\n\n> The implementation is fairly straightforward, but it's worth\n> noting a few things:\n>\n>    1. Updates to \"logs/graveyard/refs/heads/foo~\" happen\n>       under the ref-lock for \"refs/heads/foo\". So deletion\n>       still takes a single lock, and anyone touching the\n>       reflog directly needs to reverse the transformation to\n>       find the correct lockfile.\n\nThis should be documented in the code.\n\n>    2. We append entries to the graveyard reflog rather than\n>       simply renaming the file into place. This means that\n>       if you create and delete a branch repeatedly, the\n>       graveyard will contain the concatenation of all\n>       iterations.\n\nGood.\n\n>    3. We do not resurrect dead entries when a new ref is\n>       created with the same name. However, it would be\n>       possible to build an \"undelete\" feature on top of this\n>       if one was so inclined.\n\nNice prospect.\n\n> [...]> diff --git a/refs.c b/refs.c\n> index da74a2b..553de77 100644\n> --- a/refs.c\n> +++ b/refs.c\n> [...]\n> @@ -2552,3 +2553,63 @@ char *shorten_unambiguous_ref(const char *refname, int strict)\n>   \tfree(short_name);\n>   \treturn xstrdup(refname);\n>   }\n> +\n> +char *refname_to_graveyard_reflog(const char *ref)\n> +{\n> +\treturn git_path(\"logs/graveyard/%s~\", ref);\n> +}\n> +\n> +char *graveyard_reflog_to_refname(const char *log)\n> +{\n> +\tstatic struct strbuf buf = STRBUF_INIT;\n> +\n> +\tif (!prefixcmp(log, \"graveyard/\"))\n> +\t\tlog += 10;\n> +\n> +\tstrbuf_reset(&buf);\n> +\tstrbuf_addstr(&buf, log);\n> +\tif (buf.len > 0 && buf.buf[buf.len-1] == '~')\n> +\t\tstrbuf_setlen(&buf, buf.len - 1);\n> +\n> +\treturn buf.buf;\n> +}\n\nGiven the names of these two functions, I was surprised that they aren't \ninverses of each other.\n\nFunction comments would be nice, too, especially for the latter.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"195362","messageId":"20120720142630.GA31791@sigill.intra.peff.net","threadId":"31051","inReplyTo":"7515FF5F-2B4F-4CD0-B4A3-D2B1328AE313@gmail.com","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-20T14:26:43Z","receivedAt":"2012-07-20T14:26:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jul 20, 2012 at 12:23:12AM +0200, Alexey Muranov wrote:\n\n> i have no idea about Git source and little idea of how it is working\n> internally, but reading through your message i wonder: wouldn't it be\n> a good idea to timestamp the dead reflogs ?\n\nEach individual entry in the reflog has its own timestamp, and the\nentries are expired individually over time as \"git gc\" is run. Or did\nyou mean something else?\n\n-Peff\n"},{"id":"195363","messageId":"FF0EC850-7A16-4A26-A0F0-2FF45FF7F009@gmail.com","threadId":"31051","inReplyTo":"20120720142630.GA31791@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-20T14:32:28Z","receivedAt":"2012-07-20T14:32:28Z","isPatch":true,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 20 Jul 2012, at 16:26, Jeff King wrote:\n\n> On Fri, Jul 20, 2012 at 12:23:12AM +0200, Alexey Muranov wrote:\n> \n>> i have no idea about Git source and little idea of how it is working\n>> internally, but reading through your message i wonder: wouldn't it be\n>> a good idea to timestamp the dead reflogs ?\n> \n> Each individual entry in the reflog has its own timestamp, and the\n> entries are expired individually over time as \"git gc\" is run. Or did\n> you mean something else?\n\nYes, sorry, i was not clear, i meant to put dead reflogs into subdirectories yyyy-mm-dd, or maybe yyyy-mm-dd-hhmmss.\n\n-Alexey."},{"id":"195364","messageId":"20120720144337.GA31946@sigill.intra.peff.net","threadId":"31051","inReplyTo":"7vy5mftm3q.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-20T14:43:37Z","receivedAt":"2012-07-20T14:43:37Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jul 19, 2012 at 03:36:09PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > Only one test needed to be updated; t7701 tries to create\n> > unreachable objects by deleting branches. Of course that no\n> > longer works, which is the intent of this patch. The test\n> > now works around it by removing the graveyard logs.\n> \n> I think the work-around indicates the need for regular users to be\n> able to also discover, prune and delete these logs.  Do we have\n> \"prune reflog for _this_ ref (or these refs), removing entries that\n> are older than this threshold\"?  If so the codepath would need to\n> know about the graveyard and the implementation detail of the tilde\n> suffix so that the end users do not need to know about them.\n\nWe do have it: \"git reflog expire --expire=now deleted-branch\" is the\nright way to do it. Unfortunately, it does not work with my patch. The\ndwim_log correctly notes that a reflog exists (because it checks that\nthe \"graveyard\" version of the ref exists), but then expire_reflog does\nnot correctly fallback when opening the log (it usually has to do the\n_reverse_ translation, because it gets the graveyard log name from\nfor_each_reflog, and has to find the correct lock).\n\nI'll fix it in my re-roll, and then have t7701 use it.\n\n> I like the general direction.  Perhaps a long distant future\n> direction could be to also use the same trick in the ref namespace\n> so that we can have 'next' branch itself, and 'next/foo', 'next/bar'\n> forks that are based on the 'next' branch at the same time (it\n> obviously is a totally unrelated topic)?\n\nI would love that, as it would mean we could simply leave the reflogs in\nplace without having a separate graveyard namespace. Which means there\nwouldn't need to be any reflog-specific translation at all, and bugs\nlike the one above wouldn't exist.\n\nBut it would mean that you cannot naively run\n\n  echo $sha1 >.git/refs/heads/foo\n\nanymore. I suspect that the packed-refs conversion rooted out many\nscripts that did not use update-ref and rev-parse to access refs, but\nthe above does still work today. So I suspect there would be some\nfallout. Not to mention that older versions of git would be completely\nbroken, which would mean we need a lengthy deprecation period while\neverybody upgrades to versions of git that support the reading side.\n\n-Peff\n"},{"id":"195365","messageId":"20120720150726.GA2862@sigill.intra.peff.net","threadId":"31051","inReplyTo":"20120720144337.GA31946@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-20T15:07:26Z","receivedAt":"2012-07-20T15:07:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jul 20, 2012 at 10:43:37AM -0400, Jeff King wrote:\n\n> > I think the work-around indicates the need for regular users to be\n> > able to also discover, prune and delete these logs.  Do we have\n> > \"prune reflog for _this_ ref (or these refs), removing entries that\n> > are older than this threshold\"?  If so the codepath would need to\n> > know about the graveyard and the implementation detail of the tilde\n> > suffix so that the end users do not need to know about them.\n> \n> We do have it: \"git reflog expire --expire=now deleted-branch\" is the\n> right way to do it. Unfortunately, it does not work with my patch. The\n> dwim_log correctly notes that a reflog exists (because it checks that\n> the \"graveyard\" version of the ref exists), but then expire_reflog does\n> not correctly fallback when opening the log (it usually has to do the\n> _reverse_ translation, because it gets the graveyard log name from\n> for_each_reflog, and has to find the correct lock).\n> \n> I'll fix it in my re-roll, and then have t7701 use it.\n\nI noticed I ignored the \"discover\" and \"delete\" parts of your paragraph.\nAs far as deletion goes, I think we can ignore it; expiring all entries\nis equivalent.\n\nDiscovery is harder. Certainly these should not show up in normal\nref-listing output. I'd be content to leave them slightly hidden as a\nfirst step, and people who know they are looking for the pre-deletion\ncontents of the \"foo\" branch can access it by name. Probably a second\nstep would be a fancier interface to help with listing and resurrecting\ndead branches, possibly including branch config.\n\nIn other words, I want to focus on getting the ref-level plumbing right,\nand then we can care about the porcelain later.\n\n-Peff\n"},{"id":"195367","messageId":"7vpq7qtpas.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"20120720150726.GA2862@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-20T15:39:23Z","receivedAt":"2012-07-20T15:39:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I noticed I ignored the \"discover\" and \"delete\" parts of your paragraph.\n> As far as deletion goes, I think we can ignore it; expiring all entries\n> is equivalent.\n> ...\n> In other words, I want to focus on getting the ref-level plumbing right,\n> and then we can care about the porcelain later.\n\nYeah, I agree that is a reasonable way forward.  for-each-ref with a\nnew option (--include-dead or something) can wait.\n"},{"id":"195369","messageId":"7vliietp4u.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"20120720144337.GA31946@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-20T15:42:57Z","receivedAt":"2012-07-20T15:42:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> But it would mean that you cannot naively run\n>\n>   echo $sha1 >.git/refs/heads/foo\n>\n> anymore. I suspect that the packed-refs conversion rooted out many\n> scripts that did not use update-ref and rev-parse to access refs, but\n> the above does still work today. So I suspect there would be some\n> fallout. Not to mention that older versions of git would be completely\n> broken, which would mean we need a lengthy deprecation period while\n> everybody upgrades to versions of git that support the reading side.\n\nWe have that \"core.repositoryversion\" thing, so we could treat it\njust like \"update-index --index-version 4\" to make it a \"flag day\nevent for each repository, on the day of end-user's choice\".\n"},{"id":"195370","messageId":"20120720154403.GB2862@sigill.intra.peff.net","threadId":"31051","inReplyTo":"50092993.6010203@alum.mit.edu","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-20T15:44:03Z","receivedAt":"2012-07-20T15:44:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jul 20, 2012 at 11:49:07AM +0200, Michael Haggerty wrote:\n\n> >This patch moves reflog entries into a special \"graveyard\"\n> >namespace, and appends a tilde (~) character, which is\n> >not allowed in a valid ref name. This means that the deleted\n> >reflogs of these refs:\n> >\n> >    refs/heads/a\n> >    refs/heads/a/b\n> >    refs/heads/a/b/c\n> >\n> >will be stored in:\n> >\n> >    logs/graveyard/refs/heads/a~\n> >    logs/graveyard/refs/heads/a/b~\n> >    logs/graveyard/refs/heads/a/b/c~\n> >\n> >Putting them in the graveyard namespace ensures they will\n> >not conflict with live refs, and the tilde prevents D/F\n> >conflicts within the graveyard namespace.\n> \n> I agree with Junio that long-term, it would be nice to allow\n> references \"foo\" and \"foo/bar\" to exist simultaneously.  To get\n> there, we would have to redesign the mapping between reference names\n> and the filenames used for the references and for the reflogs.\n\nYes, I would really like that, as it could make the alternate namespace\ngo away, which is the source of about half the code in my patches (i.e.,\nwe would only need to loosen the reflog reading code to handle reflogs\nthat do not have a matching ref).\n\nBut I fear that the fallouts from that will be much, much larger. Even\nwith just this change, older versions of git will be slightly unhappy\n(e.g., you will get some extra warnings during fsck and reflog\nexpiration about these reflogs). But changing the on-disk representation\nof the refs namespace will mean a totally new representation of locking.\nThat's going to break old versions of git completely, and possibly even\nsome user scripts.\n\n> The easiest thing would be to mark files and directories differently;\n> something like\n> \n>     $GIT_DIR/{,logs/}refs/heads/a/b/c~\n> [...]\n> The first convention, \"logs/refs/heads/a/b/c~\" is not usable because\n> a reflog for a dead reference with this name would conflict with a\n> reflog for a live reference \"heads/a\" or \"heads/a/b\" that uses the\n> current filename convention.\n\nRight. That's what I started with, then created the graveyard hierarchy\nto avoid conflicts between the \"old\" namespace (that cannot handle D/F\nconflicts) and the \"new\" one (that can, because it represents files and\ndirectories differently).\n\n> or\n> \n>     $GIT_DIR/{,logs/}refs/heads~/a~/b~/c\n> \n> i.e., munging either directory or file names to strings that are\n> illegal in refnames such that it is unambiguous from the name whether\n> a path is a file or directory.\n\nThis one can have conflicts in the opposite direction if you don't have\nany directories. E.g., you have $GIT_DIR/foo, a deleted ref, which has\nno tildes because it has no directories in the path. But you want to\ncreate foo/bar under the \"old\" system, which cannot happen (under the\nnew system, it is fine, but the point of this exercise is to overlay the\nold and new systems).\n\nThat may be an OK tradeoff. We are restrictive in what goes into the\ntop-level. Although I notice that you did not mark \"refs\" in the above\nexample. So you could have the same problem with \"refs/stash\", for\nexample. Again, though, we don't tend to have arbitrary data at the\ntop-level (and I think refs/stash gets special cased in a couple places\nalready). So it might be an acceptable limitation.\n\nIf we want to be pedantic, my patch causes conflicts for top-level refs\ncalled \"graveyard\" (although I know we have talked about restricting\ntop-level refs to [A-Z_-], I don't recall if that has actually\nhappened).\n\n> And *if* we did that, then we wouldn't need a separate \"graveyard\"\n> namespace, would we?  The reflogs for dead references could live\n> among those for living references.\n\nRight, assuming the limitation above is OK. But note that it doesn't\nreally save us any code. We still have to convert between refnames and\ngraveyard versions. _Eventually_ if the refnames were all converted,\nthat code could go away.\n\n> But the second convention, \"logs/refs/heads~/a~/b~/c, cannot conflict\n> with current reflog files.  And it would be a step towards allowing\n> \"foo\" and \"foo/bar\" at the same time.  What do you think about using\n> a convention like this instead of the one that you proposed?\n\nI think it's reasonable. As I said, it doesn't save any code _now_, but since\nI am pulling a convention out of thin air, it might as well be one that\nhas a possibility of converging in the future (all other things being\nequal, of course; I do find marking the directories a little uglier to\nread, but that is mostly because of the tilde).\n\n> Another minor concern is the choice of trailing tilde in the file or\n> directory names.  Given that emacs creates backup files by appending\n> a tilde to the filename, (1) it would be easy to inadvertently create\n> such files, which git might try to interpret as reflogs and (2) there\n> might be tools that innately \"know\" to skip such files in their\n> processing. ack-grep, a replacement for grep, is an example that\n> springs to mind.\n\nThe use of \"~\" for backup files was actually something that made me\nchoose it, since these are, after all, backups of the reflog. But they\nare probably more precious than editor backup files, so the special\ntreatment they're given by other programs is probably not desirable.\n\n> Other possibilities (according to git-check-ref-format(1)):\n> \n>     refs/.heads/.a/.b/c\n>     refs/heads./a./b./c (problematic on some Windows filesystems?)\n>     refs/heads../a../b../c\n>     refs/heads~dir/a~dir/b~dir/c (or some other suffix)\n>     refs/heads..a..b..c (not recommended because it flattens\n> directory hierarchy)\n\nI don't like leading-dot, because those files are also often skipped by\ndirectory traversal of some programs (and certainly they are confusing\nto work with if you try to use \"ls\" to debug your $GIT_DIR/logs\ndirectory). Trailing dot is less ugly to me, but I do wonder about its\nspecial meaning as an extension separator. Double-dots just look gross.\n\nNote that we have a few other magic characters available, too. Colon is\nprobably the least offensive (metacharacters like *, ?, and [ just make\nthings unnecessarily painful for shell users).\n\nSo I think a suffix like \":d\" is probably the least horrible.\n\n-Peff\n"},{"id":"195376","messageId":"20120720155032.GC2862@sigill.intra.peff.net","threadId":"31051","inReplyTo":"7vliietp4u.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-20T15:50:32Z","receivedAt":"2012-07-20T15:50:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jul 20, 2012 at 08:42:57AM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > But it would mean that you cannot naively run\n> >\n> >   echo $sha1 >.git/refs/heads/foo\n> >\n> > anymore. I suspect that the packed-refs conversion rooted out many\n> > scripts that did not use update-ref and rev-parse to access refs, but\n> > the above does still work today. So I suspect there would be some\n> > fallout. Not to mention that older versions of git would be completely\n> > broken, which would mean we need a lengthy deprecation period while\n> > everybody upgrades to versions of git that support the reading side.\n> \n> We have that \"core.repositoryversion\" thing, so we could treat it\n> just like \"update-index --index-version 4\" to make it a \"flag day\n> event for each repository, on the day of end-user's choice\".\n\nTrue. The code to handle both cases would be pretty nasty, though,\nmostly because we do not isolate the filesystem calls at all right now\n(i.e., there are a lot of calls to git_path(\"logs/%s\", refname) in the\ncode. Which is probably not too bad, but there are a lot of implicit\nreverse-conversions (e.g., walking the hierarchy and assuming that the\npath you find is a refname).\n\nIf we are seriously considering doing this for the full refs namespace\nanytime soon, then I'd be tempted to hold off the reflog graveyard until\nthen.  The code would be a lot simpler and less error-prone if we\ndidn't have to convert between the namespaces (you would simply not get\nthe reflog retention behavior in the old repositoryformatversion).\n\n-Peff\n"},{"id":"195378","messageId":"20120720155341.GD2862@sigill.intra.peff.net","threadId":"31051","inReplyTo":"7vtxx3tlyb.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/3] teach sha1_name to look in graveyard reflogs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-20T15:53:41Z","receivedAt":"2012-07-20T15:53:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jul 19, 2012 at 03:39:24PM -0700, Junio C Hamano wrote:\n\n> > Similarly, for_each_reflog_ent learns to fallback to\n> > graveyard refs, which allows the reflog walker to work.\n> > However, this is slightly less friendly, as the revision\n> > parser expects the matching ref to exist before it realizes\n> > that we are interested in the reflog. Therefore you must use\n> > \"git log -g deleted@{1}\" insted of \"git log -g deleted\" to\n> > walk a deleted reflog.\n> \n> This may or may not be related, but I vaguely recall that \"log -g\"\n> traversal hack had a corner case where the walking stops prematurely\n> upon seeing a gap (or creation/deletion that has 0{40})?  Do you\n> recall if we have ever dealt with that?\n\n>From my tests, I think it is probably still broken (if you do a delete,\ncreate, delete sequence on a branch and then walk the reflog, it stops\nprematurely at the 0{40} sha1).\n\nBut what _should_ it show for such an entry? There is no commit to show\nin the reflog walker, but it would still be nice to say \"BTW, there was\na deletion even here\". Obviously just skipping it and showing the next\nentry would be better than the current behavior of stopping the\ntraversal, but I feel like there must be some better behavior.\n\n-Peff\n"},{"id":"195380","messageId":"50098826.4010409@kdbg.org","threadId":"31051","inReplyTo":"50092993.6010203@alum.mit.edu","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2012-07-20T16:32:38Z","receivedAt":"2012-07-20T16:32:38Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 20.07.2012 11:49, schrieb Michael Haggerty:\n> Other possibilities (according to git-check-ref-format(1)):\n> \n>     refs/.heads/.a/.b/c\n>     refs/heads./a./b./c (problematic on some Windows filesystems?)\n\nYes. Probably all filesystems.\n\n>     refs/heads../a../b../c\n\nSame here.\n\n>     refs/heads~dir/a~dir/b~dir/c (or some other suffix)\n>     refs/heads..a..b..c (not recommended because it flattens directory\n> hierarchy)\n\n-- Hannes\n"},{"id":"195381","messageId":"5009892E.9010808@kdbg.org","threadId":"31051","inReplyTo":"20120720154403.GB2862@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2012-07-20T16:37:02Z","receivedAt":"2012-07-20T16:37:02Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 20.07.2012 17:44, schrieb Jeff King:\n> So I think a suffix like \":d\" is probably the least horrible.\n\nNot so. It does not work on Windows :-( in the expected way. Trying to\nopen a file with a colon-separated suffix either opens a resource fork\non NTFS or fails with \"invalid path\".\n\n-- Hannes\n"},{"id":"195382","messageId":"20120720170913.GA14057@sigill.intra.peff.net","threadId":"31051","inReplyTo":"5009892E.9010808@kdbg.org","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-20T17:09:13Z","receivedAt":"2012-07-20T17:09:13Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jul 20, 2012 at 06:37:02PM +0200, Johannes Sixt wrote:\n\n> Am 20.07.2012 17:44, schrieb Jeff King:\n> > So I think a suffix like \":d\" is probably the least horrible.\n> \n> Not so. It does not work on Windows :-( in the expected way. Trying to\n> open a file with a colon-separated suffix either opens a resource fork\n> on NTFS or fails with \"invalid path\".\n\nBleh. It seems that we did too good a job in coming up with a list of\ndisallowed ref characters; they really are things you don't want in your\nfilenames at all. :)\n\n-Peff\n"},{"id":"195438","messageId":"B0A78CAD-49FA-4E03-86C0-1AA4023E60B7@gmail.com","threadId":"31051","inReplyTo":"20120720170913.GA14057@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-22T11:03:05Z","receivedAt":"2012-07-22T11:03:05Z","isPatch":true,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 20 Jul 2012, at 19:09, Jeff King wrote:\n\n> On Fri, Jul 20, 2012 at 06:37:02PM +0200, Johannes Sixt wrote:\n> \n>> Am 20.07.2012 17:44, schrieb Jeff King:\n>>> So I think a suffix like \":d\" is probably the least horrible.\n>> \n>> Not so. It does not work on Windows :-( in the expected way. Trying to\n>> open a file with a colon-separated suffix either opens a resource fork\n>> on NTFS or fails with \"invalid path\".\n> \n> Bleh. It seems that we did too good a job in coming up with a list of\n> disallowed ref characters; they really are things you don't want in your\n> filenames at all. :)\n\nHow about using '@' as an escape character ?\n\n-Alexey.\n"},{"id":"195439","messageId":"6F148977-4F57-47FF-B43B-0997694F3891@gmail.com","threadId":"31051","inReplyTo":"20120720154403.GB2862@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-22T11:10:55Z","receivedAt":"2012-07-22T11:10:55Z","isPatch":true,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 20 Jul 2012, at 17:44, Jeff King wrote:\n\n> On Fri, Jul 20, 2012 at 11:49:07AM +0200, Michael Haggerty wrote:\n> \n>>> This patch moves reflog entries into a special \"graveyard\"\n>>> namespace, and appends a tilde (~) character, which is\n>>> not allowed in a valid ref name. This means that the deleted\n>>> reflogs of these refs:\n>>> \n>>>   refs/heads/a\n>>>   refs/heads/a/b\n>>>   refs/heads/a/b/c\n>>> \n>>> will be stored in:\n>>> \n>>>   logs/graveyard/refs/heads/a~\n>>>   logs/graveyard/refs/heads/a/b~\n>>>   logs/graveyard/refs/heads/a/b/c~\n>>> \n>>> Putting them in the graveyard namespace ensures they will\n>>> not conflict with live refs, and the tilde prevents D/F\n>>> conflicts within the graveyard namespace.\n\nSorry if this idea is stupid or if i miss something, but how about putting deleted reflogs for\n\nrefs/heads/a\nrefs/heads/a/b\nrefs/heads/a/b/c\n\nto\n\nrefs/heads/a@yyyy-mm-dd-hhmmss\nrefs/heads/a/b@yyyy-mm-dd-hhmmss\nrefs/heads/a/b/c@yyyy-mm-dd-hhmmss\n\nwith the time they were deleted?\n\n-Alexey.\n"},{"id":"195440","messageId":"E02FA075-94B6-40A0-81E3-26968C1C18AC@gmail.com","threadId":"31051","inReplyTo":"6F148977-4F57-47FF-B43B-0997694F3891@gmail.com","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-22T11:12:45Z","receivedAt":"2012-07-22T11:12:45Z","isPatch":true,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"\nOn 22 Jul 2012, at 13:10, Alexey Muranov wrote:\n\n> Sorry if this idea is stupid or if i miss something, but how about putting deleted reflogs for\n> \n> refs/heads/a\n> refs/heads/a/b\n> refs/heads/a/b/c\n> \n> to\n> \n> refs/heads/a@yyyy-mm-dd-hhmmss\n> refs/heads/a/b@yyyy-mm-dd-hhmmss\n> refs/heads/a/b/c@yyyy-mm-dd-hhmmss\n> \n> with the time they were deleted?\n> \n> -Alexey.\n\nSorry, i meant to:\n\nlogs/refs/heads/a@yyyy-mm-dd-hhmmss\nlogs/refs/heads/a/b@yyyy-mm-dd-hhmmss\nlogs/refs/heads/a/b/c@yyyy-mm-dd-hhmmss\n\n-Alexey."},{"id":"195441","messageId":"20120722131448.GA16104@sigill.intra.peff.net","threadId":"31051","inReplyTo":"6F148977-4F57-47FF-B43B-0997694F3891@gmail.com","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-22T13:14:48Z","receivedAt":"2012-07-22T13:14:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jul 22, 2012 at 01:10:55PM +0200, Alexey Muranov wrote:\n\n> >>>   refs/heads/a\n> >>>   refs/heads/a/b\n> >>>   refs/heads/a/b/c\n> >>> \n> >>> will be stored in:\n> >>> \n> >>>   logs/graveyard/refs/heads/a~\n> >>>   logs/graveyard/refs/heads/a/b~\n> >>>   logs/graveyard/refs/heads/a/b/c~\n> >>> \n> >>> Putting them in the graveyard namespace ensures they will\n> >>> not conflict with live refs, and the tilde prevents D/F\n> >>> conflicts within the graveyard namespace.\n> \n> Sorry if this idea is stupid or if i miss something, but how about putting deleted reflogs for\n> \n> refs/heads/a\n> refs/heads/a/b\n> refs/heads/a/b/c\n> \n> to\n> \n> refs/heads/a@yyyy-mm-dd-hhmmss\n> refs/heads/a/b@yyyy-mm-dd-hhmmss\n> refs/heads/a/b/c@yyyy-mm-dd-hhmmss\n> \n> with the time they were deleted?\n\nI like the readability of the resulting file names, but it has three\nproblems:\n\n  1. \"@\" is allowed in ref names, so you may be conflicting with\n     existing refs. You could fix that by using \"@{...}\", which is\n     disallowed. E.g., refs/heads/a@{yyyy-mm-dd-hhmmss}.\n\n  2. It makes lookup slightly more expensive, because to find a reflog\n     for \"refs/heads/a\", I have to scan \"logs/refs/heads\" looking for\n     any matching entries of the form \"a@{.*}\". This is probably not a\n     huge deal in practice, though it does make the code more complex.\n\n  3. Most importantly, it does not resolve D/F conflicts (it has the\n     same problem as \"logs/refs/heads/a~\"). If you delete \"foo/bar\", you\n     will end up with \"logs/refs/heads/foo/bar@{...}\". That will prevent\n     D/F conflicts with a new branch \"foo/bar/baz\", but will still have\n     a problem with just \"foo\".\n\n     You need to either mark each directory to avoid the conflict\n     (Michael suggested something like \"refs/heads~/foo~/bar\"), or\n     you need to put the deleted logs into a separate hierarchy (I used\n     \"logs/graveyard\" in my patch).\n\n-Peff\n"},{"id":"195443","messageId":"61E46C90-5C8F-4E11-8CB7-0A05F1D62A8A@gmail.com","threadId":"31051","inReplyTo":"20120722131448.GA16104@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-22T14:40:14Z","receivedAt":"2012-07-22T14:40:14Z","isPatch":true,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 22 Jul 2012, at 15:14, Jeff King wrote:\n\n>  3. Most importantly, it does not resolve D/F conflicts (it has the\n>     same problem as \"logs/refs/heads/a~\"). If you delete \"foo/bar\", you\n>     will end up with \"logs/refs/heads/foo/bar@{...}\". That will prevent\n>     D/F conflicts with a new branch \"foo/bar/baz\", but will still have\n>     a problem with just \"foo\".\n\nUnfortunately i do not really follow this, because i have not seen any directories in \"logs/refs/heads/\", i only saw files named after local branches there. I do not know how directories are used there.\n\n-Alexey."},{"id":"195446","messageId":"20120722155056.GA22641@sigill.intra.peff.net","threadId":"31051","inReplyTo":"61E46C90-5C8F-4E11-8CB7-0A05F1D62A8A@gmail.com","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-22T15:50:56Z","receivedAt":"2012-07-22T15:50:56Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jul 22, 2012 at 04:40:14PM +0200, Alexey Muranov wrote:\n\n> >  3. Most importantly, it does not resolve D/F conflicts (it has the\n> >     same problem as \"logs/refs/heads/a~\"). If you delete \"foo/bar\", you\n> >     will end up with \"logs/refs/heads/foo/bar@{...}\". That will prevent\n> >     D/F conflicts with a new branch \"foo/bar/baz\", but will still have\n> >     a problem with just \"foo\".\n> \n> Unfortunately i do not really follow this, because i have not seen any\n> directories in \"logs/refs/heads/\", i only saw files named after local\n> branches there. I do not know how directories are used there.\n\nThe user is free to have branch names with slashes, in which case they\nare represented in the filesystem as directories. Even without using\nslashes in your branch names, you already have subdirectories in\nrefs/remotes.\n\n-Peff\n"},{"id":"195455","messageId":"7vzk6rplfk.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"20120720155341.GD2862@sigill.intra.peff.net","subject":"Re: [PATCH 2/3] teach sha1_name to look in graveyard reflogs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-22T20:53:19Z","receivedAt":"2012-07-22T20:53:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> But what _should_ it show for such an entry? There is no commit to show\n> in the reflog walker, but it would still be nice to say \"BTW, there was\n> a deletion even here\". Obviously just skipping it and showing the next\n> entry would be better than the current behavior of stopping the\n> traversal, but I feel like there must be some better behavior.\n\nLike showing an entry that says \"Ref deleted here\", which should be\neasy to do by creating a phoney commit object and inserting it to\nthe queue the reflog walker uses, I would guess.\n"},{"id":"195849","messageId":"CACsJy8BtcvuW2HKPSki7meyHMsvpLS0b8QG5M_083HEwy=-9EQ@mail.gmail.com","threadId":"31051","inReplyTo":"20120720170913.GA14057@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-07-26T12:47:06Z","receivedAt":"2012-07-26T12:47:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Jul 21, 2012 at 12:09 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Jul 20, 2012 at 06:37:02PM +0200, Johannes Sixt wrote:\n>\n>> Am 20.07.2012 17:44, schrieb Jeff King:\n>> > So I think a suffix like \":d\" is probably the least horrible.\n>>\n>> Not so. It does not work on Windows :-( in the expected way. Trying to\n>> open a file with a colon-separated suffix either opens a resource fork\n>> on NTFS or fails with \"invalid path\".\n>\n> Bleh. It seems that we did too good a job in coming up with a list of\n> disallowed ref characters; they really are things you don't want in your\n> filenames at all. :)\n\nSo we haven't found any way to present both branches \"foo\" and\n\"foo/bar\" on file system at the same time. How about when we a new\nbranch introduces such a conflict, we push the new branch directly to\npacked-refs? If we need either of them on a separate file, for fast\nupdate for example, then we unpack just one and repack all refs that\nconflict with it. Attempting to update two conflict branches in\nparallel may impact performance, but I don't think that happens often.\n-- \nDuy\n"},{"id":"195863","messageId":"10DD3DE0-E554-4BE3-A20B-FDBC73219646@gmail.com","threadId":"31051","inReplyTo":"CACsJy8BtcvuW2HKPSki7meyHMsvpLS0b8QG5M_083HEwy=-9EQ@mail.gmail.com","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-26T16:26:45Z","receivedAt":"2012-07-26T16:26:45Z","isPatch":true,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 26 Jul 2012, at 14:47, Nguyen Thai Ngoc Duy wrote:\n\n> So we haven't found any way to present both branches \"foo\" and\n> \"foo/bar\" on file system at the same time. How about when we a new\n> branch introduces such a conflict, we push the new branch directly to\n> packed-refs? If we need either of them on a separate file, for fast\n> update for example, then we unpack just one and repack all refs that\n> conflict with it. Attempting to update two conflict branches in\n> parallel may impact performance, but I don't think that happens often.\n> -- \n> Duy\n\nHow about simply deprecating \"/\" in branch name?\n\n-Alexey.\n"},{"id":"195865","messageId":"vpq8ve6qxui.fsf@bauges.imag.fr","threadId":"31051","inReplyTo":"10DD3DE0-E554-4BE3-A20B-FDBC73219646@gmail.com","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-07-26T16:41:09Z","receivedAt":"2012-07-26T16:41:09Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Alexey Muranov <alexey.muranov@gmail.com> writes:\n\n> On 26 Jul 2012, at 14:47, Nguyen Thai Ngoc Duy wrote:\n>\n>> So we haven't found any way to present both branches \"foo\" and\n>> \"foo/bar\" on file system at the same time. How about when we a new\n>> branch introduces such a conflict, we push the new branch directly to\n>> packed-refs? If we need either of them on a separate file, for fast\n>> update for example, then we unpack just one and repack all refs that\n>> conflict with it. Attempting to update two conflict branches in\n>> parallel may impact performance, but I don't think that happens often.\n>> -- \n>> Duy\n>\n> How about simply deprecating \"/\" in branch name?\n\nErr, it's not like nobody's using this feature (Junio does a heavy use\nof it in particular) ...\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"195867","messageId":"20120726165949.GB13942@sigill.intra.peff.net","threadId":"31051","inReplyTo":"vpq8ve6qxui.fsf@bauges.imag.fr","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-26T16:59:49Z","receivedAt":"2012-07-26T16:59:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jul 26, 2012 at 06:41:09PM +0200, Matthieu Moy wrote:\n\n> > How about simply deprecating \"/\" in branch name?\n> \n> Err, it's not like nobody's using this feature (Junio does a heavy use\n> of it in particular) ...\n\nNot to mention git itself, as it splits up the refs/remotes hierarchy\ninto subdirectories. I think deprecating \"/\" is out of the question.\n\n-Peff\n"},{"id":"195870","messageId":"91CA9DC6-FBAE-410F-A182-E83FBA769AC6@gmail.com","threadId":"31051","inReplyTo":"20120726165949.GB13942@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Alexey Muranov","fromEmail":"alexey.muranov@gmail.com","sentAt":"2012-07-26T17:24:40Z","receivedAt":"2012-07-26T17:24:40Z","isPatch":true,"sender":{"key":"alexey.muranov@gmail.com","avatar":"https://gravatar.com/avatar/15ecd60a4d31cd6b05b9db40739c8c0db30049d238dd6dd26a0c4dd876577648?d=mp&s=160"},"body":"On 26 Jul 2012, at 18:59, Jeff King wrote:\n\n> Not to mention git itself, as it splits up the refs/remotes hierarchy\n> into subdirectories. I think deprecating \"/\" is out of the question.\n> \n> -Peff\n\nOk, i guess you know better than me, my vision of Git is probably still too simplistic.\n\n-Alexey."},{"id":"195874","messageId":"7vehny8lgm.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"20120720170913.GA14057@sigill.intra.peff.net","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-26T17:46:01Z","receivedAt":"2012-07-26T17:46:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Fri, Jul 20, 2012 at 06:37:02PM +0200, Johannes Sixt wrote:\n>\n>> Am 20.07.2012 17:44, schrieb Jeff King:\n>> > So I think a suffix like \":d\" is probably the least horrible.\n>> \n>> Not so. It does not work on Windows :-( in the expected way. Trying to\n>> open a file with a colon-separated suffix either opens a resource fork\n>> on NTFS or fails with \"invalid path\".\n>\n> Bleh. It seems that we did too good a job in coming up with a list of\n> disallowed ref characters; they really are things you don't want in your\n> filenames at all. :)\n\nWhy do no need to even worry about ~ vs : vs whatever in the first\nplace?\n\nWith a flag-day per repository \"core.repositoryformatversion = 1\",\nyou do not have to worry about mixture of old-style refs and new\nones, so refs/heads/next-d/log could be a topic branch 'next/log'\nthat is based on an integration branch 'next' branch that physically\nresides at refs/heads/next-f or an entry refs/heads/next in packed\nrefs.  Only the API functions in refs.c should care, no?\n"},{"id":"195876","messageId":"20120726175248.GA15928@sigill.intra.peff.net","threadId":"31051","inReplyTo":"7vehny8lgm.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-26T17:52:48Z","receivedAt":"2012-07-26T17:52:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jul 26, 2012 at 10:46:01AM -0700, Junio C Hamano wrote:\n\n> > Bleh. It seems that we did too good a job in coming up with a list of\n> > disallowed ref characters; they really are things you don't want in your\n> > filenames at all. :)\n> \n> Why do no need to even worry about ~ vs : vs whatever in the first\n> place?\n> \n> With a flag-day per repository \"core.repositoryformatversion = 1\",\n> you do not have to worry about mixture of old-style refs and new\n> ones, so refs/heads/next-d/log could be a topic branch 'next/log'\n> that is based on an integration branch 'next' branch that physically\n> resides at refs/heads/next-f or an entry refs/heads/next in packed\n> refs.  Only the API functions in refs.c should care, no?\n\nI think the point was that Michael wanted to select a standard that\ncould be used for graveyard reflogs _now_, but which would eventually\nmatch the format we use for active refs. And that requires a character\nthat is not valid in a refname.\n\nGiven that the change of format for actives refs would require a flag\nday, keeping the graveyard scheme mixable with the current ref rules may\nnot be worth caring about, though.\n\n-Peff\n"},{"id":"197134","messageId":"7vzk5uxvzl.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"CAPBPrnsFk-Ww-52W-=qkTK7Yifjowx3tpsELznO4ncmqwfP_Qg@mail.gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-08-16T23:22:54Z","receivedAt":"2012-08-16T23:22:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dan Johnson <computerdruid@gmail.com> writes:\n\n> On Thu, Jul 19, 2012 at 7:55 AM, Jeff King <peff@peff.net> wrote:\n> ...\n>> So I think it would be a lot more palatable if we kept reflogs on\n>> deleted branches. That, in turn, has a few open issues, such as how to\n>> manage namespace conflicts (e.g., the fact that a deleted \"foo\" branch\n>> can conflict with a new \"foo/bar\" branch).\n>\n> In the meantime, would it make sense to introduce a configuration\n> variable to request this behavior?\n>\n> If so, should it be global?\n>\n> fetch.prune = always\n>\n> or per-remote?\n>\n> remote.<name>.prune = always\n>\n> The global option seems to be more in line with what Alexey is looking\n> for, but the per-remote one is similar to the tagopt option, which is\n> a similar idea.\n>\n> Of course, this might be just a waste of time to introduce a feature\n> no one would use, in which case we obviously should not introduce such\n> options.\n\nI was reading through the backlog today and noticed that this topic\nveered into the \"reflog graveyard\" tangent.  I wasn't involved in\nthe main topic, but I think having both configuration variables,\nremote.<remote>.prune taking precedence over fetch.prune, as long as\nwe make sure \"fetch --no-prune\" will override any configured\ndefault, is not a bad thing per-se.\n\nAs long as the users who elect to use this feature are aware of the\npruning of the refs and logs, that is, but \"branch [-r] -d\" has been\nthe way to lose both the branch and its log for a long time, so I do\nnot see a big issue there, either.\n\nThe log graveyard is an independently interesting idea, which I may\nping separately, but I consider it pretty much orthogonal to this\nparticular topic.\n"},{"id":"197135","messageId":"7vvcgixvrw.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"068F71AC-DACD-4545-AB21-F911A934DA3E@gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-08-16T23:27:31Z","receivedAt":"2012-08-16T23:27:31Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alexey Muranov <alexey.muranov@gmail.com> writes:\n\n> On 20 Jul 2012, at 09:11, Johannes Sixt wrote:\n> ...\n>> Note the difference between \"tracking branch\" and \"remote tracking\n>> branch\"! The \"remote tracking branches\" are the refs in the refs/remotes/\n>> hierarchy. The \"tracking branches\" are your own local branches that you\n>> have created with 'git branch topic thatremote/topic' (or perhaps 'git\n>> checkout -b'). The paragraph talks about the latter.\n>\n> Hannes, thanks for the explanation, so i was confused once again.\n>\n> Various blog posts do not make the terminology clear, for example\n> http://gitready.com/beginner/2009/03/09/remote-tracking-branches.html\n> sais that there are only \"two types of branches: local, and remote-tracking\"...\n> ...\n> I think i was also misguided by Konstantin, who wrote that \"you\n> create a remote tracking branch when you intend to actually\n> *develop* something on that branch\" :).\n\nI was re-reading the backlog today, and saw this topic fizzled out.\n\nWe obviously cannot fix third-party documentation that teach lies to\npeople, but is there something we can do to improve our own\ndocumentation with respect to this confusion?\n\nAs I wrote it elsewhere, I try to avoid the bareword \"tracking\" in\ngeneral, and call the local branch you build on something like \"your\n'next' branch that forked from origin/next remote tracking branch\"\nmyself.  Perhaps we can start from checking the documentation with\nsuch a phrasing discipline?\n"},{"id":"197136","messageId":"7vr4r6xvom.fsf@alter.siamese.dyndns.org","threadId":"31051","inReplyTo":"7vy5mftm3q.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/3] retain reflogs for deleted refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-08-16T23:29:29Z","receivedAt":"2012-08-16T23:29:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I like the general direction.  Perhaps a long distant future\n> direction could be to also use the same trick in the ref namespace\n> so that we can have 'next' branch itself, and 'next/foo', 'next/bar'\n> forks that are based on the 'next' branch at the same time (it\n> obviously is a totally unrelated topic)?\n\nI notice that I was responsible for making this topic veer in the\nwrong direction by bringing up a new feature \"having 'next' and\n'next/bar' at the same time\" which nobody asked.  Perhaps we can\ndrop that for now to simplify the scope of the topic, to bring the\nlog graveyard back on track?\n"},{"id":"197510","messageId":"20120821065113.GB3238@sigill.intra.peff.net","threadId":"31051","inReplyTo":"7vzk5uxvzl.fsf@alter.siamese.dyndns.org","subject":"Re: Feature request: fetch --prune by default","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-08-21T06:51:14Z","receivedAt":"2012-08-21T06:51:14Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Aug 16, 2012 at 04:22:54PM -0700, Junio C Hamano wrote:\n\n> > In the meantime, would it make sense to introduce a configuration\n> > variable to request this behavior?\n> >\n> > If so, should it be global?\n> >\n> > fetch.prune = always\n> >\n> > or per-remote?\n> >\n> > remote.<name>.prune = always\n> >\n> > The global option seems to be more in line with what Alexey is looking\n> > for, but the per-remote one is similar to the tagopt option, which is\n> > a similar idea.\n> >\n> > Of course, this might be just a waste of time to introduce a feature\n> > no one would use, in which case we obviously should not introduce such\n> > options.\n> \n> I was reading through the backlog today and noticed that this topic\n> veered into the \"reflog graveyard\" tangent.  I wasn't involved in\n> the main topic, but I think having both configuration variables,\n> remote.<remote>.prune taking precedence over fetch.prune, as long as\n> we make sure \"fetch --no-prune\" will override any configured\n> default, is not a bad thing per-se.\n> \n> As long as the users who elect to use this feature are aware of the\n> pruning of the refs and logs, that is, but \"branch [-r] -d\" has been\n> the way to lose both the branch and its log for a long time, so I do\n> not see a big issue there, either.\n> \n> The log graveyard is an independently interesting idea, which I may\n> ping separately, but I consider it pretty much orthogonal to this\n> particular topic.\n\nYeah, I think that is sensible. As long as the _default_ is not to\nprune, and people are making a conscious choice to prune, I don't see a\nproblem at all.\n\nThe log graveyard is orthogonal to the proposed option, but I think it\nwould be a necessary step before flipping the default for that option to\n\"true\".\n\n-Peff\n"},{"id":"221505","messageId":"1371756174612-7590048.post@n2.nabble.com","threadId":"31051","inReplyTo":"CAPBPrnsFk-Ww-52W-=qkTK7Yifjowx3tpsELznO4ncmqwfP_Qg@mail.gmail.com","subject":"Re: Feature request: fetch --prune by default","fromName":"Sam Roberts","fromEmail":"vieuxtech@gmail.com","sentAt":"2013-06-20T19:22:54Z","receivedAt":"2013-06-20T19:22:54Z","isPatch":false,"sender":{"key":"vieuxtech@gmail.com","avatar":null},"body":"I would use the config feature to turn on --prune for fetch, and was\nsurprised that it wasn't available - I hit this thread because I figured I\nsomehow missed it in the config docs.\n\nHaving both global and local settings seems nice.\n\n\n\n--\nView this message in context: http://git.661346.n2.nabble.com/Feature-request-fetch-prune-by-default-tp7563241p7590048.html\nSent from the git mailing list archive at Nabble.com.\n"}]}