{"thread":{"id":"22055","subject":"Question about 'branch -d' safety","startedAt":"2009-12-29T21:54:42Z","lastAt":"2010-07-20T13:34:22Z","messageCount":42,"participants":["Nanako Shiraishi","Nicolas Sebrecht","Junio C Hamano","Clemens Buchacher","Jonathan Nieder","Jakub Narebski","Julian Phillips","Will Palmer","Johannes Sixt","Joshua Jensen","Ævar Arnfjörð Bjarmason","Matthieu Moy"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"130494","messageId":"20091230065442.6117@nanako3.lavabit.com","threadId":"22055","inReplyTo":null,"subject":"Question about 'branch -d' safety","fromName":"Nanako Shiraishi","fromEmail":"nanako3@lavabit.com","sentAt":"2009-12-29T21:54:42Z","receivedAt":"2009-12-29T21:54:42Z","isPatch":false,"sender":{"key":"nanako3@lavabit.com","avatar":"https://gravatar.com/avatar/3777b9e201c5883a62b1a6fdf7c53f2d712d1d80989146063ea861e33aad72a8?d=mp&s=160"},"body":"'git branch -d other' refuses to remove 'other' branch when it isn't merged to the current branch, and it is a safety feature.\n\nBut I often work in the following way in a project with a master branch and some topic branches:\n\n\t% git checkout -b topic origin/topic\n\t% work work work\n\t% git push origin topic\n\t% git checkout master\n\t% git branch -d topic\n\nBecause 'topic' is shared with other people who work on the project, and I don't work on any particular topic all the time, I want to keep my local branches small by removing unused branches. As you can guess, after checking out the master branch, the safety of \"branch -d\" is based on a wrong commit (namely, 'master', that is often behind 'topic') and deletion is refused.\n\nI think the safety feature should check if the branch to be deleted is merged to the remote tracking branch it was forked from, instead of checking the current branch.\n\nWhat do you think?\n\n-- \nNanako Shiraishi\nhttp://ivory.ap.teacup.com/nanako3/\n"},{"id":"130498","messageId":"20091229223123.GA12965@vidovic","threadId":"22055","inReplyTo":"20091230065442.6117@nanako3.lavabit.com","subject":"Re: Question about 'branch -d' safety","fromName":"Nicolas Sebrecht","fromEmail":"nicolas.s.dev@gmx.fr","sentAt":"2009-12-29T22:31:23Z","receivedAt":"2009-12-29T22:31:23Z","isPatch":false,"sender":{"key":"nicolas.s.dev@gmx.fr","avatar":null},"body":"The 30/12/09, Nanako Shiraishi wrote:\n\n> I think the safety feature should check if the branch to be deleted is merged to the remote tracking branch it was forked from, instead of checking the current branch.\n> \n> What do you think?\n\nI think we shouldn't. At first, every repository don't have a remote.\nThis may easily be passed by a \"double check\" with a logical OR between\nthe two statements.\n\nBut even with it, we would hit some foreign workflow. Think: Bob\ndirectly push to Alice and Alice does the same to Bob. I don't use this\nkind of workflow myself but I consider them to be sensible enough to\nhave our attention.\n\nNow, I'm talking about what users may expect from the default behaviour.\nI'm not against a new configuration variable. It would certainly give\nmore granularity to the expectation of \"what is safe for me\".\n\n\n-- \nNicolas Sebrecht\n"},{"id":"130507","messageId":"20091230121238.6117@nanako3.lavabit.com","threadId":"22055","inReplyTo":"20091229223123.GA12965@vidovic","subject":"Re: Question about 'branch -d' safety","fromName":"Nanako Shiraishi","fromEmail":"nanako3@lavabit.com","sentAt":"2009-12-30T03:12:38Z","receivedAt":"2009-12-30T03:12:38Z","isPatch":false,"sender":{"key":"nanako3@lavabit.com","avatar":"https://gravatar.com/avatar/3777b9e201c5883a62b1a6fdf7c53f2d712d1d80989146063ea861e33aad72a8?d=mp&s=160"},"body":"Quoting Nicolas Sebrecht <nicolas.s.dev@gmx.fr>\n\n> The 30/12/09, Nanako Shiraishi wrote:\n>\n>> I think the safety feature should check if the branch to be deleted is merged to the remote tracking branch it was forked from, instead of checking the current branch.\n>> \n>> What do you think?\n>\n> I think we shouldn't. At first, every repository don't have a remote.\n> This may easily be passed by a \"double check\" with a logical OR between\n> the two statements.\n\nSorry, I was unclear. What I meant was that checking with the branch the branch to be deleted was forked from, if and only if such a branch exists. Otherwise, we can keep using the old default behavior to check with the current branch.\n\n> But even with it, we would hit some foreign workflow. Think: Bob\n> directly push to Alice and Alice does the same to Bob. I don't use this\n> kind of workflow myself but I consider them to be sensible enough to\n> have our attention.\n\nHere is what I think about your scenario.\n\nBob directly pushes to Alice and Alice does the same to Bob, both to their refs/remotes/<other person>/ tracking branches, and their own local branches fork from the remote tracking branches that keep track of other person's work. You would still want the check based on that tracking branch, not based on 'master' that has no relationship with the branches they are exchanging.\n\n-- \nNanako Shiraishi\nhttp://ivory.ap.teacup.com/nanako3/\n"},{"id":"130520","messageId":"7vhbr955dz.fsf@alter.siamese.dyndns.org","threadId":"22055","inReplyTo":"20091230121238.6117@nanako3.lavabit.com","subject":"Re: Question about 'branch -d' safety","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-30T06:43:04Z","receivedAt":"2009-12-30T06:43:04Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nanako Shiraishi <nanako3@lavabit.com> writes:\n\n> Quoting Nicolas Sebrecht <nicolas.s.dev@gmx.fr>\n>\n>> The 30/12/09, Nanako Shiraishi wrote:\n>>\n>>> I think the safety feature should check if the branch to be deleted is merged to the remote tracking branch it was forked from, instead of checking the current branch.\n>>> \n>>> What do you think?\n>>\n>> I think we shouldn't. At first, every repository don't have a remote.\n>> This may easily be passed by a \"double check\" with a logical OR between\n>> the two statements.\n>\n> Sorry, I was unclear. What I meant was that checking with the branch the\n> branch to be deleted was forked from, if and only if such a branch\n> exists. Otherwise, we can keep using the old default behavior to check\n> with the current branch.\n\nBack when the original \"safety valve\" was added, there was no mechanical\n\"this branch _always_ merges from/rebases on that other one\" settings.\nThe users were supposed to keep track of the correspondence, and the\ncanonical workflow was \"checkout this && merge that && branch -d that\".\n\nBut I actually think it is quite a natural thing to do in year 2010 to\nchange the safety valve as suggested.\n\nA patch to do so would look like this.\n\n-- >8 --\nbranch -d: base the \"already-merged\" safety on the branch it merges with\n\nWhen a branch is marked to merge with another ref (e.g. local 'next' that\nmerges from and pushes back to origin's 'next', with 'branch.next.merge'\nset to 'refs/heads/next'), it makes little sense to base the \"branch -d\"\nsafety, whose purpose is not to lose commits that are not merged to other\nbranches, on the current branch.  It is much more sensible to check if it\nis merged with the other branch it merges with.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-branch.c  |   64 ++++++++++++++++++++++++++++++++++++++++++++--------\n t/t3200-branch.sh |   26 +++++++++++++++++++++\n 2 files changed, 80 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex c87e63b..d2a35fe 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -93,9 +93,60 @@ static const char *branch_get_color(enum color_branch ix)\n \treturn \"\";\n }\n \n+static int branch_merged(int kind, const char *name,\n+\t\t\t struct commit *rev, struct commit *head_rev)\n+{\n+\t/*\n+\t * This checks whether the merge bases of branch and HEAD (or\n+\t * the other branch this branch builds upon) contains the\n+\t * branch, which means that the branch has already been merged\n+\t * safely to HEAD (or the other branch).\n+\t */\n+\tstruct commit *reference_rev = NULL;\n+\tconst char *reference_name = NULL;\n+\tint merged;\n+\n+\tif (kind == REF_LOCAL_BRANCH) {\n+\t\tstruct branch *branch = branch_get(name);\n+\t\tunsigned char sha1[20];\n+\n+\t\tif (branch &&\n+\t\t    branch->merge &&\n+\t\t    branch->merge[0] &&\n+\t\t    branch->merge[0]->dst &&\n+\t\t    (reference_name =\n+\t\t     resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)\n+\t\t\treference_rev = lookup_commit_reference(sha1);\n+\t}\n+\tif (!reference_rev)\n+\t\treference_rev = head_rev;\n+\n+\tmerged = in_merge_bases(rev, &reference_rev, 1);\n+\n+\t/*\n+\t * After the safety valve is fully redefined to \"check with\n+\t * upstream, if any, otherwise with HEAD\", we should just\n+\t * return the result of the in_merge_bases() above without\n+\t * any of the following code, but during the transition period,\n+\t * a gentle reminder is in order.\n+\t */\n+\tif ((head_rev != reference_rev) &&\n+\t    in_merge_bases(rev, &head_rev, 1) != merged) {\n+\t\tif (merged)\n+\t\t\twarning(\"deleting branch '%s' that has been merged to\\n\"\n+\t\t\t\t\"         '%s', but it is not yet merged to HEAD.\",\n+\t\t\t\tname, reference_name);\n+\t\telse\n+\t\t\twarning(\"not deleting branch '%s' that is not yet merged to\\n\"\n+\t\t\t\t\"         '%s', even though it is merged to HEAD.\",\n+\t\t\t\tname, reference_name);\n+\t}\n+\treturn merged;\n+}\n+\n static int delete_branches(int argc, const char **argv, int force, int kinds)\n {\n-\tstruct commit *rev, *head_rev = head_rev;\n+\tstruct commit *rev, *head_rev = NULL;\n \tunsigned char sha1[20];\n \tchar *name = NULL;\n \tconst char *fmt, *remote;\n@@ -148,15 +199,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)\n \t\t\tcontinue;\n \t\t}\n \n-\t\t/* This checks whether the merge bases of branch and\n-\t\t * HEAD contains branch -- which means that the HEAD\n-\t\t * contains everything in both.\n-\t\t */\n-\n-\t\tif (!force &&\n-\t\t    !in_merge_bases(rev, &head_rev, 1)) {\n-\t\t\terror(\"The branch '%s' is not an ancestor of \"\n-\t\t\t      \"your current HEAD.\\n\"\n+\t\tif (!force && !branch_merged(kinds, bname.buf, rev, head_rev)) {\n+\t\t\terror(\"The branch '%s' is not fully merged.\\n\"\n \t\t\t      \"If you are sure you want to delete it, \"\n \t\t\t      \"run 'git branch -D %s'.\", bname.buf, bname.buf);\n \t\t\tret = 1;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex d59a9b4..e0b7605 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -468,4 +468,30 @@ test_expect_success 'detect misconfigured autosetuprebase (no value)' '\n \tgit config --unset branch.autosetuprebase\n '\n \n+test_expect_success 'attempt to delete a branch without base and unmerged to HEAD' '\n+\tgit checkout my9 &&\n+\tgit config --unset branch.my8.merge &&\n+\ttest_must_fail git branch -d my8\n+'\n+\n+test_expect_success 'attempt to delete a branch merged to its base' '\n+\t# we are on my9 which is the initial commit; traditionally\n+\t# we would not have allowed deleting my8 that is not merged\n+\t# to my9, but it is set to track master that already has my8\n+\tgit config branch.my8.merge refs/heads/master &&\n+\tgit branch -d my8\n+'\n+\n+test_expect_success 'attempt to delete a branch merged to its base' '\n+\tgit checkout master &&\n+\techo Third >>A &&\n+\tgit commit -m \"Third commit\" A &&\n+\tgit branch -t my10 my9 &&\n+\tgit branch -f my10 HEAD^ &&\n+\t# we are on master which is at the third commit, and my10\n+\t# is behind us, so traditionally we would have allowed deleting\n+\t# it; but my10 is set to track my9 that is further behind.\n+\ttest_must_fail git branch -d my10\n+'\n+\n test_done\n"},{"id":"130581","messageId":"20091230210837.GA12935@vidovic","threadId":"22055","inReplyTo":"20091230121238.6117@nanako3.lavabit.com","subject":"Re: Question about 'branch -d' safety","fromName":"Nicolas Sebrecht","fromEmail":"nicolas.s.dev@gmx.fr","sentAt":"2009-12-30T21:08:37Z","receivedAt":"2009-12-30T21:08:37Z","isPatch":false,"sender":{"key":"nicolas.s.dev@gmx.fr","avatar":null},"body":"The 30/12/09, Nanako Shiraishi wrote:\n> Quoting Nicolas Sebrecht <nicolas.s.dev@gmx.fr>\n> \n> > But even with it, we would hit some foreign workflow. Think: Bob\n> > directly push to Alice and Alice does the same to Bob. I don't use this\n> > kind of workflow myself but I consider them to be sensible enough to\n> > have our attention.\n> \n> Here is what I think about your scenario.\n> \n> Bob directly pushes to Alice and Alice does the same to Bob, both to their refs/remotes/<other person>/ tracking branches\n\nWe can't say. They both may have refs/remotes/<same_id> .\n\nBob:\n  $ git branch -d a_branch\n\nNow, Bob has the \"I don't want to loose\" commit known in\nrefs/remotes/<same_id> only.\n\nAlice, some time later:\n  $ git push -f <same_id> <foo>:<a_branch>\n\nBob \"lose\" his commit.\n\n\nI admit it is a very uncommon use case and Bob can still use the reflog.\n\n-- \nNicolas Sebrecht\n"},{"id":"145264","messageId":"20100710065549.GA24296@localhost","threadId":"22055","inReplyTo":"20091230121238.6117@nanako3.lavabit.com","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-10T06:55:49Z","receivedAt":"2010-07-10T06:55:49Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"Hi,\n\nI'm sorry to revive this old thread. But I recently hit this\nbehavior myself, using the workflow described below, and I ended up\ndeleting an unmerged branch with git branch -d.\n\nThis discussion is about v1.7.0-rc0~18^2 (branch -d: base the\n\"already-merged\" safety on the branch it merges with, 2009-12-29),\nwhich teaches 'git branch' to delete a branch with -d, if it is\nmerged into the branch it is tracking, even if it is not merged\ninto any local branch.\n\nOn Wed, Dec 30, 2009 at 12:12:38PM +0900, Nanako Shiraishi wrote:\n> Quoting Nicolas Sebrecht <nicolas.s.dev@gmx.fr>\n> \n> > But even with it, we would hit some foreign workflow. Think: Bob\n> > directly push to Alice and Alice does the same to Bob. I don't use this\n> > kind of workflow myself but I consider them to be sensible enough to\n> > have our attention.\n> \n> Here is what I think about your scenario.\n> \n> Bob directly pushes to Alice and Alice does the same to Bob, both\n> to their refs/remotes/<other person>/ tracking branches, and\n> their own local branches fork from the remote tracking branches\n> that keep track of other person's work. You would still want the\n> check based on that tracking branch, not based on 'master' that\n> has no relationship with the branches they are exchanging.\n\nSo I had a branch 'topic' in two repositories, neither of which was\nin any way authoritative. They were both upstreams to each other.\nAnd the 'topic' branch in each tracked the 'topic' branch in the\nother.\n\nAt some point, I used branch -d to delete branch 'topic' in one\nrepo. Later, I did the same in the other. I then realized that\nafter I do git remote prune origin, the branch will be gone,\nwithout ever having being merged into another branch.\n\nI often use branch -d simply to check if I still forgot something\nabout this branch, or if it has become redundant. With the above\nscenario, I can not rely on branch -d safety any more.\n\nOn the other hand, I do think the new behavior is quite useful in\ngeneral. Maybe the real solution is a reflog for deleted branch\nheads, rather than being too careful about whether or not a branch\ncan be deleted.\n\nClemens\n"},{"id":"145282","messageId":"20100710214050.GA3680@burratino","threadId":"22055","inReplyTo":"20100710065549.GA24296@localhost","subject":"Re: Question about 'branch -d' safety","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-10T21:40:50Z","receivedAt":"2010-07-10T21:40:50Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Clemens Buchacher wrote:\n\n>          Maybe the real solution is a reflog for deleted branch\n> heads, rather than being too careful about whether or not a branch\n> can be deleted.\n\nYes, I think so.  Would there be any bad side effects to this?\n\ndiff --git a/refs.c b/refs.c\nindex b540067..7ed0154 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1084,7 +1084,6 @@ 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 \tinvalidate_cached_refs();\n \tunlock_ref(lock);\n \treturn ret;\n-- \n"},{"id":"145283","messageId":"m3lj9jknlr.fsf@localhost.localdomain","threadId":"22055","inReplyTo":"20100710214050.GA3680@burratino","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-10T21:57:35Z","receivedAt":"2010-07-10T21:57:35Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n> Clemens Buchacher wrote:\n> \n> >          Maybe the real solution is a reflog for deleted branch\n> > heads, rather than being too careful about whether or not a branch\n> > can be deleted.\n> \n> Yes, I think so.  Would there be any bad side effects to this?\n> \n> diff --git a/refs.c b/refs.c\n> index b540067..7ed0154 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -1084,7 +1084,6 @@ 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>  \tinvalidate_cached_refs();\n>  \tunlock_ref(lock);\n>  \treturn ret;\n\nIt's unfortunately not so easy.  The problem you have to solve is D/F\nconflict: if you have 'foo/bar' branch, you can't create 'foo' branch,\nbut after deleting 'foo/bar' you want to be able to create 'foo'\nbranch and reflog for 'foo' branch.\n\nTherefore there were ideas for various \"Attic\"-like areas for reflogs\nfor deleted branches...\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"145284","messageId":"20100710221724.GA3891@burratino","threadId":"22055","inReplyTo":"m3lj9jknlr.fsf@localhost.localdomain","subject":"Re: Question about 'branch -d' safety","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-10T22:17:24Z","receivedAt":"2010-07-10T22:17:24Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jakub Narebski wrote:\n> Jonathan Nieder <jrnieder@gmail.com> writes:\n\n>> +++ b/refs.c\n>> @@ -1084,7 +1084,6 @@ 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>>  \tinvalidate_cached_refs();\n>>  \tunlock_ref(lock);\n>>  \treturn ret;\n>\n> It's unfortunately not so easy.  The problem you have to solve is D/F\n> conflict: if you have 'foo/bar' branch, you can't create 'foo' branch,\n> but after deleting 'foo/bar' you want to be able to create 'foo'\n> branch and reflog for 'foo' branch.\n\nThanks for the pointer.  Here are some more, for the interested.\n\n - http://thread.gmane.org/gmane.comp.version-control.git/144250/focus=145366\n   the dead_refs namespace\n\n - http://thread.gmane.org/gmane.comp.version-control.git/73838/focus=73877 \n   why to make and why not to make the change\n"},{"id":"145312","messageId":"20100711065505.GA19606@localhost","threadId":"22055","inReplyTo":"m3lj9jknlr.fsf@localhost.localdomain","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-11T06:55:05Z","receivedAt":"2010-07-11T06:55:05Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"On Sat, Jul 10, 2010 at 02:57:35PM -0700, Jakub Narebski wrote:\n> \n> It's unfortunately not so easy.  The problem you have to solve is D/F\n> conflict: if you have 'foo/bar' branch, you can't create 'foo' branch,\n> but after deleting 'foo/bar' you want to be able to create 'foo'\n> branch and reflog for 'foo' branch.\n\nI'm going to read up on Jonathan's pointers. But I do not really\nsee the problem above. If the reflog already exists, the new branch\nsimpliy continues using it. So if the branch is re-created, it's as\nif the branch had never been deleted.\n\nOr possibly we add a special reflog entry which points the branch\nto a zero sha to signify that the branch had been deleted.\n\nClemens\n"},{"id":"145315","messageId":"201007110916.29567.jnareb@gmail.com","threadId":"22055","inReplyTo":"20100711065505.GA19606@localhost","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-11T07:16:26Z","receivedAt":"2010-07-11T07:16:26Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Dnia niedziela 11. lipca 2010 08:55, Clemens Buchacher napisał:\n> On Sat, Jul 10, 2010 at 02:57:35PM -0700, Jakub Narebski wrote:\n> > \n> > It's unfortunately not so easy.  The problem you have to solve is D/F\n> > conflict: if you have 'foo/bar' branch, you can't create 'foo' branch,\n> > but after deleting 'foo/bar' you want to be able to create 'foo'\n> > branch and reflog for 'foo' branch.\n> \n> I'm going to read up on Jonathan's pointers. But I do not really\n> see the problem above. If the reflog already exists, the new branch\n> simpliy continues using it. So if the branch is re-created, it's as\n> if the branch had never been deleted.\n\nThe problem is, that when you have 'foo/bar' branch, then you have\n'foo/bar' reflog.  When you delete branch 'foo/bar', but do not delete\n'foo/bar' reflog (only add to it branch deletion event), and then you\nwant to create 'foo' branch, git wouldn't be able to create reflog\nfo 'foo' because of directory / file (D/F) conflict: there is 'foo/'\ndirectory preventing file 'foo' from being created.\n \n> Or possibly we add a special reflog entry which points the branch\n> to a zero sha to signify that the branch had been deleted.\n\nThat is a good idea anyway.\n-- \nJakub Narebski\nPoland\n"},{"id":"145319","messageId":"7414fff60758b3f60d69a1bbed49165e@212.159.54.234","threadId":"22055","inReplyTo":"201007110916.29567.jnareb@gmail.com","subject":"Re: Question about 'branch -d' safety","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-07-11T08:48:12Z","receivedAt":"2010-07-11T08:48:12Z","isPatch":false,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"On Sun, 11 Jul 2010 09:16:26 +0200, Jakub Narebski <jnareb@gmail.com>\nwrote:\n> Dnia niedziela 11. lipca 2010 08:55, Clemens Buchacher napisał:\n>> On Sat, Jul 10, 2010 at 02:57:35PM -0700, Jakub Narebski wrote:\n>> > \n>> > It's unfortunately not so easy.  The problem you have to solve is D/F\n>> > conflict: if you have 'foo/bar' branch, you can't create 'foo'\nbranch,\n>> > but after deleting 'foo/bar' you want to be able to create 'foo'\n>> > branch and reflog for 'foo' branch.\n>> \n>> I'm going to read up on Jonathan's pointers. But I do not really\n>> see the problem above. If the reflog already exists, the new branch\n>> simpliy continues using it. So if the branch is re-created, it's as\n>> if the branch had never been deleted.\n> \n> The problem is, that when you have 'foo/bar' branch, then you have\n> 'foo/bar' reflog.  When you delete branch 'foo/bar', but do not delete\n> 'foo/bar' reflog (only add to it branch deletion event), and then you\n> want to create 'foo' branch, git wouldn't be able to create reflog\n> fo 'foo' because of directory / file (D/F) conflict: there is 'foo/'\n> directory preventing file 'foo' from being created.\n\nYou could just change the reflog code so that if foo is a directory it\nlooks for/creates foo/.reflog (the dot is to prevent a clash with a valid\nbranch name)?\n\n>> Or possibly we add a special reflog entry which points the branch\n>> to a zero sha to signify that the branch had been deleted.\n> \n> That is a good idea anyway.\n\n-- \nJulian\n"},{"id":"145328","messageId":"20100711133730.GA10338@localhost","threadId":"22055","inReplyTo":"201007110916.29567.jnareb@gmail.com","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-11T13:37:30Z","receivedAt":"2010-07-11T13:37:30Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"On Sun, Jul 11, 2010 at 09:16:26AM +0200, Jakub Narebski wrote:\n> \n> The problem is, that when you have 'foo/bar' branch, then you have\n> 'foo/bar' reflog.  When you delete branch 'foo/bar', but do not delete\n> 'foo/bar' reflog (only add to it branch deletion event), and then you\n> want to create 'foo' branch, git wouldn't be able to create reflog\n> fo 'foo' because of directory / file (D/F) conflict: there is 'foo/'\n> directory preventing file 'foo' from being created.\n\nRight, of course. So how about this?\n\nClemens\n\n-->o--\nDate: Sun, 11 Jul 2010 12:37:06 +0200\nSubject: [PATCH/RFC] graveyard for reflogs\n\nInstead of removing reflogs together with refs, keep them around\nand reuse them as a starting point, if a ref of the same name is\ncreated again.\n\nWhen a ref is deleted, a ~ is appended to each directory of the\ncorresponding reflog entry, e.g., refs/heads/branch is renamed to\nrefs~/heads~/branch. Since ~ must not be used for ref names,\ndirectory/file conflicts are thus avoided.\n\nKnown issues:\n\n - The reflog cannot be accessed while the ref does not exist.\n\n - Older git versions will not resurrect the reflog, and therefore\n   leave the renamed reflog behind.\n\n - Breaks t7701, because git-expire tries to lock log entries,\n   which fails because ~ is an illegal character for refs.\n\n - Breaks t9300.\n\nSigned-off-by: Clemens Buchacher <drizzd@aon.at>\n---\n reflog-walk.c   |    9 +++++--\n refs.c          |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n t/t1450-fsck.sh |    1 +\n 3 files changed, 73 insertions(+), 4 deletions(-)\n\ndiff --git a/reflog-walk.c b/reflog-walk.c\nindex 4879615..9415ac8 100644\n--- a/reflog-walk.c\n+++ b/reflog-walk.c\n@@ -228,15 +228,18 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)\n \t\treturn;\n \t}\n \n-\treflog = &commit_reflog->reflogs->items[commit_reflog->recno];\n \tinfo->last_commit_reflog = commit_reflog;\n \tcommit_reflog->recno--;\n-\tcommit_info->commit = (struct commit *)parse_object(reflog->osha1);\n-\tif (!commit_info->commit) {\n+\tif (commit_reflog->recno < 0) {\n \t\tcommit->parents = NULL;\n \t\treturn;\n \t}\n \n+\treflog = &commit_reflog->reflogs->items[commit_reflog->recno];\n+\tcommit_info->commit = (struct commit *)parse_object(reflog->nsha1);\n+\tif (!commit_info->commit)\n+\t\tdie(\"Invalid reflog entry\");\n+\n \tcommit->parents = xcalloc(sizeof(struct commit_list), 1);\n \tcommit->parents->item = commit_info->commit;\n \tcommit->object.flags &= ~(ADDED | SEEN | SHOWN);\ndiff --git a/refs.c b/refs.c\nindex b540067..78c48ad 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1052,6 +1052,70 @@ static int repack_without_ref(const char *refname)\n \treturn commit_lock_file(&packlock);\n }\n \n+static void pronounce_dead(struct strbuf *dead_ref, const char *ref)\n+{\n+\tconst char *p = ref;\n+\twhile (*p) {\n+\t\tif (*p == '/') {\n+\t\t\tint len = p - ref;\n+\t\t\tstrbuf_add(dead_ref, ref, len);\n+\t\t\tif (len > 0)\n+\t\t\t\tstrbuf_addstr(dead_ref, \"~/\");\n+\t\t\tref = p + 1;\n+\t\t}\n+\t\tp++;\n+\t}\n+\tstrbuf_addstr(dead_ref, ref);\n+}\n+\n+static int bury_log(const char *ref)\n+{\n+\tstruct strbuf dead_ref = STRBUF_INIT;\n+\tstruct stat loginfo;\n+\n+\tif (lstat(git_path(\"logs/%s\", ref), &loginfo))\n+\t\treturn 0;\n+\n+\tpronounce_dead(&dead_ref, ref);\n+\n+\tif (S_ISLNK(loginfo.st_mode))\n+\t\treturn error(\"reflog for %s is a symlink\", ref);\n+\n+\tif (safe_create_leading_directories(git_path(\"logs/%s\", dead_ref.buf)))\n+\t\treturn error(\"unable to create directory for %s\", dead_ref.buf);\n+\n+\tif (rename(git_path(\"logs/%s\", ref), git_path(\"logs/%s\", dead_ref.buf)))\n+\t\treturn error(\"unable to move logfile to logs/%s: %s\",\n+\t\t\tdead_ref.buf, strerror(errno));\n+\n+\tstrbuf_release(&dead_ref);\n+\treturn 0;\n+}\n+\n+static int resurrect_log(const char *ref)\n+{\n+\tstruct strbuf dead_ref = STRBUF_INIT;\n+\tstruct stat loginfo;\n+\n+\tpronounce_dead(&dead_ref, ref);\n+\n+\tif (lstat(git_path(\"logs/%s\", dead_ref.buf), &loginfo))\n+\t\treturn 0;\n+\n+\tif (S_ISLNK(loginfo.st_mode))\n+\t\treturn error(\"reflog for %s is a symlink\", dead_ref.buf);\n+\n+\tif (safe_create_leading_directories(git_path(\"logs/%s\", ref)))\n+\t\treturn error(\"unable to create directory for %s\", ref);\n+\n+\tif (rename(git_path(\"logs/%s\", dead_ref.buf), git_path(\"logs/%s\", ref)))\n+\t\treturn error(\"unable to move logfile to logs/%s: %s\",\n+\t\t\tref, strerror(errno));\n+\n+\tstrbuf_release(&dead_ref);\n+\treturn 0;\n+}\n+\n int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n {\n \tstruct ref_lock *lock;\n@@ -1084,7 +1148,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+\tbury_log(lock->ref_name);\n \tinvalidate_cached_refs();\n \tunlock_ref(lock);\n \treturn ret;\n@@ -1384,6 +1448,7 @@ int write_ref_sha1(struct ref_lock *lock,\n \t\tunlock_ref(lock);\n \t\treturn -1;\n \t}\n+\tresurrect_log(lock->ref_name);\n \tinvalidate_cached_refs();\n \tif (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||\n \t    (strcmp(lock->ref_name, lock->orig_ref_name) &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 759cf12..65f160e 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -55,6 +55,7 @@ test_expect_success 'object with bad sha1' '\n \tgrep \"$sha.*corrupt\" out &&\n \trm -f .git/objects/$new &&\n \tgit update-ref -d refs/heads/bogus &&\n+\trm -f .git/logs/refs~/heads~/bogus &&\n \tgit read-tree -u --reset HEAD\n '\n \n-- \n1.7.1.571.gba4d01\n\n"},{"id":"145332","messageId":"7v1vb9hnd9.fsf@alter.siamese.dyndns.org","threadId":"22055","inReplyTo":"20100711133730.GA10338@localhost","subject":"Re: Question about 'branch -d' safety","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-11T18:41:54Z","receivedAt":"2010-07-11T18:41:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Clemens Buchacher <drizzd@aon.at> writes:\n\n> Known issues:\n>\n>  - The reflog cannot be accessed while the ref does not exist.\n>\n>  - Older git versions will not resurrect the reflog, and therefore\n>    leave the renamed reflog behind.\n>\n>  - Breaks t7701, because git-expire tries to lock log entries,\n>    which fails because ~ is an illegal character for refs.\n>\n>  - Breaks t9300.\n\nPerhaps a few obvious ones are missing?\n\n - It is no longer possible to get rid of objects associated with the\n   history of a branch by deleting the branch and then running gc.\n\n - It is no longer possible to trust git that you would start a history of\n   a branch afresh when you create one.  If you happened to have an\n   unrelated branch with the same name in the past, the new branch\n   inherits reflog entries when it shouldn't.\n\nWhat problem are you guys really trying to solve?\n\n\n> diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\n> index 759cf12..65f160e 100755\n> --- a/t/t1450-fsck.sh\n> +++ b/t/t1450-fsck.sh\n> @@ -55,6 +55,7 @@ test_expect_success 'object with bad sha1' '\n>  \tgrep \"$sha.*corrupt\" out &&\n>  \trm -f .git/objects/$new &&\n>  \tgit update-ref -d refs/heads/bogus &&\n> +\trm -f .git/logs/refs~/heads~/bogus &&\n>  \tgit read-tree -u --reset HEAD\n>  '\n\nWhat is this about???\n"},{"id":"145333","messageId":"201007112105.40951.jnareb@gmail.com","threadId":"22055","inReplyTo":"7v1vb9hnd9.fsf@alter.siamese.dyndns.org","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-11T19:05:39Z","receivedAt":"2010-07-11T19:05:39Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Dnia niedziela 11. lipca 2010 20:41, Junio C Hamano napisał:\n> Clemens Buchacher <drizzd@aon.at> writes:\n> \n> > Known issues:\n> >\n> >  - The reflog cannot be accessed while the ref does not exist.\n> >\n> >  - Older git versions will not resurrect the reflog, and therefore\n> >    leave the renamed reflog behind.\n> >\n> >  - Breaks t7701, because git-expire tries to lock log entries,\n> >    which fails because ~ is an illegal character for refs.\n> >\n> >  - Breaks t9300.\n> \n> Perhaps a few obvious ones are missing?\n> \n>  - It is no longer possible to get rid of objects associated with the\n>    history of a branch by deleting the branch and then running gc.\n\nI think that reflog for deleted branches would still follow gc.reflogExpire\nand gr.reflogExpireUnreachable, and that it would be automatically deleted\nwhen it becomes empty.  I don't know if provided implementation assures\nthis.\n\n>  - It is no longer possible to trust git that you would start a history of\n>    a branch afresh when you create one.  If you happened to have an\n>    unrelated branch with the same name in the past, the new branch\n>    inherits reflog entries when it shouldn't.\n\nThat's a valid concern.\n\n> \n> What problem are you guys really trying to solve?\n\nThe problem is that if you delete branch by accident, it might be hard\nto restore it, and its reflog vanishes forever.  And with relaxed\nprotection of 'git branch -d' this might happen more frequently (in the\ncase of circular origin's).\n\n-- \nJakub Narebski\nPoland\n"},{"id":"145334","messageId":"1278885775.2308.27.camel@dreddbeard","threadId":"22055","inReplyTo":"7v1vb9hnd9.fsf@alter.siamese.dyndns.org","subject":"Re: Question about 'branch -d' safety","fromName":"Will Palmer","fromEmail":"wmpalmer@gmail.com","sentAt":"2010-07-11T22:02:55Z","receivedAt":"2010-07-11T22:02:55Z","isPatch":false,"sender":{"key":"wmpalmer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/357044?v=4"},"body":"On Sun, 2010-07-11 at 11:41 -0700, Junio C Hamano wrote:\n> What problem are you guys really trying to solve?\n\nAlmost every porcelain git operation is, in general, \"safe\". Safety is\ndefined as \"will not lose data\". I tried to come up with a list of\n\"unsafe\" commands recently, and others in #git pointed out that a lot of\ncommands could be made safe by inserting \"wait 90 days\" into various\npoints. Other than that, there's reset --hard. That's pretty damn safe,\nin my opinion.\n\nThe reflog is the safety-net which allows us to give users help without\nworrying about (as we might in other VCSs) someone typing a command\nslightly wrong and blowing away their repository. \"Don't worry, you\naren't going to break it\" is a git truism which helps us to encourage\ngit newbies to experiment a little. Even history rewriting is a \"safe\"\noperation.\n\nPersonally, I thought \"git branch -d\" was quite safe enough, as the HEAD\nreflog would still have a copy of the tip, but as was pointed out to me,\nthis would only be there if the branch in question had been checked-out\nrecently, and of course the reflog for that branch would also be\ndestroyed. I don't really mind the second half of that, but if we're\ngoing to solve one problem, why not solve both?\n\nThe main thing which is lost when a ref is deleted (assuming no gc,\nwhich doesn't happen during a delete without anything else happening) is\nthe association between the various objects which will show up during\n\"git fsck\" and the name of the ref which was deleted. Without that loss\nof association the conversation can move from:\n\t\"don't panic! If you haven't done anything else yet, you can type \"git\nfsck\" and go through the list of dangling commits one by one until you\nfind it. Disable automatic garbage collection in the mean-time just to\nbe safe.\"\n\nto (wishful thinking):\n\t\"don't panic! try typing \"git restore-ref -i\" and select the branch you\ndeleted\"\n\n\nWhat's really problem I'm really trying to solve? I suppose part is the\n\"I want porcelain commands to be safe, at least for 30 days\", and the\nrest is that I like having more ways to have a conversation which starts\noff in panic end with \"oh, thanks! git is awesome!\"\n\n-- \n-- Will\n"},{"id":"145391","messageId":"20100712184754.GA18429@localhost","threadId":"22055","inReplyTo":"7v1vb9hnd9.fsf@alter.siamese.dyndns.org","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-12T18:47:55Z","receivedAt":"2010-07-12T18:47:55Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"On Sun, Jul 11, 2010 at 11:41:54AM -0700, Junio C Hamano wrote:\n> \n>  - It is no longer possible to get rid of objects associated with the\n>    history of a branch by deleting the branch and then running gc.\n\nYes, this should be fixed in the final implementation. The reflog\nshould be accessible as usual, and should expire normally, even if\nthe branch has been deleted.\n\n>  - It is no longer possible to trust git that you would start a history of\n>    a branch afresh when you create one.  If you happened to have an\n>    unrelated branch with the same name in the past, the new branch\n>    inherits reflog entries when it shouldn't.\n\nThe reflog does not guarantee any such relatedness with previous\nentries anyways. Doing \"git reset --hard <unrelated-branch>\" is not\nmuch different from deleting the branch and creating a new\nunrelated branch of the same name.\n\nAnd if it's really an issue, we can always make the reflog stop\nlisting entries at the latest branch creation entry, and make\nlisting beyound that optional. (I actually had to change the reflog\nwalker to make it continue beyound branch creation.)\n\n> What problem are you guys really trying to solve?\n\nThe reflog protects you from almost all involuntary loss of\ninformation. Except for recovery after git branch -D and even git\nbranch -d, as per the scenario I described in the original posting\nof this thread.\n\nClemens\n"},{"id":"145404","messageId":"7v39vo8dkn.fsf@alter.siamese.dyndns.org","threadId":"22055","inReplyTo":"20100712184754.GA18429@localhost","subject":"Re: Question about 'branch -d' safety","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-12T23:50:32Z","receivedAt":"2010-07-12T23:50:32Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Clemens Buchacher <drizzd@aon.at> writes:\n\n>> What problem are you guys really trying to solve?\n>\n> The reflog protects you from almost all involuntary loss of\n> information.\n\nAnd you call \"git branch -[dD]\" involuntary?\n\nI am not entirely unsympathetic to add \"git branch --undelete frotz\" to our\nvocabulary, but then we should leave users an easy way to really remove\nthings, and it shouldn't be \"git branch -d --i-really-mean-it frotz\".\n\nIt would probably be more like \"git branch -d frotz\" followed by \"git\nbranch --purge frotz\" or \"git branch --purge \\*\" (if we name the operation\nto remove cruft for undelete \"purge\", that is).\n"},{"id":"145432","messageId":"20100713071317.GA26348@localhost","threadId":"22055","inReplyTo":"7v39vo8dkn.fsf@alter.siamese.dyndns.org","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-13T07:13:17Z","receivedAt":"2010-07-13T07:13:17Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"On Mon, Jul 12, 2010 at 04:50:32PM -0700, Junio C Hamano wrote:\n> \n> I am not entirely unsympathetic to add \"git branch --undelete frotz\" to our\n> vocabulary, but then we should leave users an easy way to really remove\n> things, and it shouldn't be \"git branch -d --i-really-mean-it frotz\".\n> \n> It would probably be more like \"git branch -d frotz\" followed by \"git\n> branch --purge frotz\" or \"git branch --purge \\*\" (if we name the operation\n> to remove cruft for undelete \"purge\", that is).\n\nWhy would we need that? Even now, it is not enough to do \"git\nbranch -D frotz; git gc --prune\". You need to expire the reflog,\nsince HEAD may still have a reference to it. So instead you would\nneed\n\n git branch -D frotz\n git reflog expire --expire=now HEAD frotz\n git gc --prune\n\nPurging a branch becomes just a special case of purging anything\nfrom history.\n\nClemens\n"},{"id":"145433","messageId":"1279008023.2030.11.camel@wpalmer.simply-domain","threadId":"22055","inReplyTo":"20100713071317.GA26348@localhost","subject":"Re: Question about 'branch -d' safety","fromName":"Will Palmer","fromEmail":"wmpalmer@gmail.com","sentAt":"2010-07-13T08:00:23Z","receivedAt":"2010-07-13T08:00:23Z","isPatch":false,"sender":{"key":"wmpalmer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/357044?v=4"},"body":"On Tue, 2010-07-13 at 09:13 +0200, Clemens Buchacher wrote:\n> Why would we need that? Even now, it is not enough to do \"git\n> branch -D frotz; git gc --prune\". You need to expire the reflog,\n> since HEAD may still have a reference to it. So instead you would\n> need\n> \n>  git branch -D frotz\n>  git reflog expire --expire=now HEAD frotz\n>  git gc --prune\n> \n> Purging a branch becomes just a special case of purging anything\n> from history.\n> \n> Clemens\n\nWith that in mind, would it not be enough to simply remove the ref, but\nnot the reflog, and change the logic to let the reflog live on until\neither pruned or all of its entries have expired normally? On branch\ncreation, if a reflog already exists, it would remain untouched. Reflog\nentries would ALWAYS expire using the same expire-time rules, regardless\nof whether or not the ref itself exists, and everything just makes\nsense. Simple to follow, simple to prune, branch -d becomes just as safe\nas any other operation. I like this.\n\nI understand the argument for \"but you may wind up creating an unrelated\nbranch with the same name\", but I can't really think of any cases where\nit would matter, and it would be impossible to guess whether or not a\nnew branch were related to the old one. If we'd really want to take care\nof that sort of thing, we'd need to move into two-dimensional reflog\nterritory, with something like somebranch@{3,5} to mean \"3 reflogs ago,\n5 states ago\", which goes too far and looks ugly.\n"},{"id":"145436","messageId":"4C3C2435.8010500@viscovery.net","threadId":"22055","inReplyTo":"1279008023.2030.11.camel@wpalmer.simply-domain","subject":"Re: Question about 'branch -d' safety","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-07-13T08:30:45Z","receivedAt":"2010-07-13T08:30:45Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 7/13/2010 10:00, schrieb Will Palmer:\n> On Tue, 2010-07-13 at 09:13 +0200, Clemens Buchacher wrote:\n>> Why would we need that? Even now, it is not enough to do \"git\n>> branch -D frotz; git gc --prune\". You need to expire the reflog,\n>> since HEAD may still have a reference to it. So instead you would\n>> need\n>>\n>>  git branch -D frotz\n>>  git reflog expire --expire=now HEAD frotz\n>>  git gc --prune\n>>\n>> Purging a branch becomes just a special case of purging anything\n>> from history.\n> \n> With that in mind, would it not be enough to simply remove the ref, but\n> not the reflog,\n\nWith that in mind, shouldn't it be exactly the other way around, i.e.,\ndump the reflog (the objects are still referenced from HEAD's reflog), but\nkeep the ref around in some attic, just in case the branch is so old that\nits reflog was empty and its objects would otherwise be pruned right away?\n\n-- Hannes\n"},{"id":"145438","messageId":"1279011633.2030.17.camel@wpalmer.simply-domain","threadId":"22055","inReplyTo":"4C3C2435.8010500@viscovery.net","subject":"Re: Question about 'branch -d' safety","fromName":"Will Palmer","fromEmail":"wmpalmer@gmail.com","sentAt":"2010-07-13T09:00:33Z","receivedAt":"2010-07-13T09:00:33Z","isPatch":false,"sender":{"key":"wmpalmer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/357044?v=4"},"body":"On Tue, 2010-07-13 at 10:30 +0200, Johannes Sixt wrote:\n> With that in mind, shouldn't it be exactly the other way around, i.e.,\n> dump the reflog (the objects are still referenced from HEAD's reflog), but\n> keep the ref around in some attic, just in case the branch is so old that\n> its reflog was empty and its objects would otherwise be pruned right away?\n> \n> -- Hannes\n\nIt would probably be more sensible to add a \"deleted\" entry to the\nreflog just prior to removing the ref (yes, this will make HEAD@{1}\nequal to HEAD@{0} in many cases). Keeping the ref itself around in an\nattic doesn't make sense - the reflog can act as a better \"attic\"\nanyway, if we stop deleting nonempty reflogs just because they don't\nhave a live ref associated with them.\n\nHaving a separate \"attic\" just runs into problems of \"what if you delete\nthe ref twice?\" and adds an entirely separate mechanism for tracking\nsomething which we already have a perfectly good method of tracking: the\nprevious state of a ref.\n"},{"id":"145480","messageId":"20100713222105.GA9293@localhost","threadId":"22055","inReplyTo":"1279008023.2030.11.camel@wpalmer.simply-domain","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-13T22:21:05Z","receivedAt":"2010-07-13T22:21:05Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"On Tue, Jul 13, 2010 at 09:00:23AM +0100, Will Palmer wrote:\n> \n> With that in mind, would it not be enough to simply remove the ref, but\n> not the reflog, and change the logic to let the reflog live on until\n> either pruned or all of its entries have expired normally?\n\nThat's the idea. The only reason why this patch renames the reflog\nafter deletion is due to the current directory style layout of refs\nand reflogs. In particular, it is not possible to have refs foo/bar\nand foo at the same time, since that would be a directory/file\nconflict.\n\nI therefore rename reflogs of deleted refs to foo~/bar, for\nexample, which for all intents and purposes is still the reflog of\nfoo/bar, only now it's not in the way of creating a new ref foo.\nThe fact that we rename the ref internally should be transparent to\nthe user.\n\nClemens\n"},{"id":"145721","messageId":"20100717093006.GA11452@localhost","threadId":"22055","inReplyTo":"20100711133730.GA10338@localhost","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-17T09:30:06Z","receivedAt":"2010-07-17T09:30:06Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"Hi,\n\nI am not sure how to proceed with this. It seems like there are\nmany arguments against it, and few for it. So in order to get a\nbetter idea where we stand, I have summarized the arguments so far\nbelow. \n\nI do not know which solution would make everyone happy. But I think\nthere are a few advantages to be considered, and the remaining\nissues are not insurmountable.\n\nClemens\n---\n\nPros and cons for \"undeleting branches\":\n\n+ safety net\n\nIt should not be easy to lose information with git. In most cases\nbranches can be restored from the HEAD reflog, but it can be\ncomplicated if the branch has never been checked out. Even the\ntrivial case is not as obvious as searching for a \"Branch deleted\"\nentry in the reflog.\n\n+ less dependant on git branch -d\n\nSince git branch -d deletes branches which have been merged to a\nremote tracking branch, it does no longer guarantee that the branch\nis still available in history locally, and if the branch is also\ndeleted remotely, running git remote prune removes it entirely. In\nthat sense, it cannot be considered safe any more. Being able to\nrestore deleted branches would make this a non-issue.\n\n+ automatically prune remote tracking branches\n\nOnce it is easy to restore deleted branches, there is no need to\nkeep around remote tracking branches which have been deleted on the\nremote. They can be pruned automatically on git fetch.\n\n- only a convenience\n\nConsidering the many potential issues for this corner case, why\nbother implementing it? Deleted branches can be restored from the\nHEAD reflog anyways.\n\n- implementation complexity\n\nDue to the possibility of D/F conflicts, \"deleted reflogs\" have to\nbe renamed internally. This makes the reflog implementation more\ncomplex.\n\n- user interface complexity\n\nHow to prune deleted branches? Currently, it is enough to do \"git\nbranch -D branch; git gc --prune\" in order to get rid of the branch\nobjects, at least if the HEAD reflog does not contain it or has\nexpired. Consider for example adding a remote, and removing it\nagain. This operation would leave a bunch of deleted branches,\nwhich potentially occupy a lot of disk space.\n\nHow to find and restore deleted branches? If the reflog is used to\nrecord deleted branches, and a new branch of the same name is\ncreated, the reflog contains entries from unrelated branches. [1]\nIf the deleted reflogs are stored in an attic, how do we reference\nthose?\n\n[1] Also, what happens if that new branch is renamed?\n"},{"id":"145739","messageId":"20100718004347.GA8665@burratino","threadId":"22055","inReplyTo":"20100717093006.GA11452@localhost","subject":"Re: Question about 'branch -d' safety","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-07-18T00:43:47Z","receivedAt":"2010-07-18T00:43:47Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Clemens Buchacher wrote:\n\n> I am not sure how to proceed with this.\n\nMaybe: try out the patch locally for a while.  Such an experiment\nwould make it easier to see whether this feature because indispensible\nand how the details should be to work best in practice.\n\nBut be careful!  Once you have learned to rely on the\nmaster@{before.deletion} facility, you might end up trigger-happy with\n‘branch -d’ and get into trouble when faced with copies of git without\nthe safety valve.\n\nTo respond to your philosophical points:\n\n> + safety net\n\nI consider it very good that git is willing to _lose_ information\nrather than get overloaded with it.  On this laptop, if I try\n\n linux$ git fetch git://git.kernel.org/pub/scm/linux/kernel/git/sfr/linux-next.git\n linux$ git checkout FETCH_HEAD\n        ... hack away ...\n\nin a repository with quickly-expiring HEAD reflog, then the pack size\nstays barely comfortable.  Perhaps a person that regularly builds\nshort-lived topic branches off such an unstable branch would be\nsimilarly grateful that the objects do not need to stay long.\n\nSo: this aspect is a minus for me.\n\n> + less dependant on git branch -d\n\nI have not find “log -g” too useful for protection against fat-finger\naccidents (“fsck --lost-found” and “push” take care of that) so it\nseems odd to see safety touted as its main benefit.  What the reflog\nand HEAD@{4} syntax prevent me from losing is a train of thought.\nAfter deleting a branch I have just merged, if I start to wonder about\n“that branch I just deleted”, reading reflog entries to find it\nis the last thing I want to be doing.\n\nIn other words, it is not enough that “branch -d” be safe.  Yes, the\nold branch tip is an ancestor of origin/master, but that does not\nbring me much closer to finding it.  To be able to refer to\nmaster@{5.minutes.ago} directly would be much more convenient.\n\n+.\n\n> + automatically prune remote tracking branches\n\nI’m not comfortable with the idea yet.\n\n0.\n\n> - only a convenience\n> - implementation complexity\n\nYour implementation seemed simple.  I don’t think implementation\ncomplexity is the fatal flaw.\n\n0.\n\n> - user interface complexity\n\nKeeping the reflog would arguably make the syntax for specifying\nrevisions more consistent.\n\nOn the other hand, there are some hard policy questions:\n\n * Should pre-deletion commits expire more quickly?\n\n * What happens when you rename one branch to take on the\n   name of a deleted one?\n\n * What happens when you create a new branch to take the\n   place of a deleted one?\n\n * Should refs outside the refs/heads/ namespace\n   (like refs/bisect/ and refs/original/) use the same\n   policy as refs/heads/?\n\n0.\n"},{"id":"145744","messageId":"201007181355.36691.jnareb@gmail.com","threadId":"22055","inReplyTo":"20100718004347.GA8665@burratino","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-18T11:55:34Z","receivedAt":"2010-07-18T11:55:34Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Sun, 18 Jul 2010, Jonathan Nieder wrote:\n\n> On the other hand, there are some hard policy questions:\n> \n>  * Should pre-deletion commits expire more quickly?\n\nI think you should have an option to configure expiration of reflogs\nof deleted branches separately, just like there is currently a way to\nconfigure longer expiration time of HEAD reflog, or for stash.\n\nWith this on low disk size / low quota machine you would be able to\nturn this feature (this protection) off.\n \n>  * What happens when you rename one branch to take on the\n>    name of a deleted one?\n\nThe same as with D/F conflict.  If you rename branch 'foo' to 'bar',\nyou also rename its reflog, but logs/refs/heads/bar would not conflict\nwith reflog for deleted branch, logs/refs~/heads~/bar (if you had \ndeleted branch 'bar').\n\n>  * What happens when you create a new branch to take the\n>    place of a deleted one?\n\nIt would either be the same as above, or it would get reflog from \ndeleted branch.  The former would be better, if there was in git some \ncommand to resurrect deleted branch.\n \n>  * Should refs outside the refs/heads/ namespace\n>    (like refs/bisect/ and refs/original/) use the same\n>    policy as refs/heads/?\n\nI guess that expire policy should be configured per ref, or per \nnamespace.  Perhaps gc.<pattern>.reflogExpireDeleted ?  But then\ngc.<pattern>.reflogExpireUnreachableDeleted would be long name, as\nif compensating for two-letter section name ;-)\n\n-- \nJakub Narebski\nPoland\n"},{"id":"145780","messageId":"1279484847.8999.22.camel@dreddbeard","threadId":"22055","inReplyTo":"201007181355.36691.jnareb@gmail.com","subject":"Re: Question about 'branch -d' safety","fromName":"Will Palmer","fromEmail":"wmpalmer@gmail.com","sentAt":"2010-07-18T20:27:27Z","receivedAt":"2010-07-18T20:27:27Z","isPatch":false,"sender":{"key":"wmpalmer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/357044?v=4"},"body":"On Sun, 2010-07-18 at 13:55 +0200, Jakub Narebski wrote:\n> On Sun, 18 Jul 2010, Jonathan Nieder wrote:\n> The same as with D/F conflict.  If you rename branch 'foo' to 'bar',\n> you also rename its reflog, but logs/refs/heads/bar would not conflict\n> with reflog for deleted branch, logs/refs~/heads~/bar (if you had \n> deleted branch 'bar').\n\nhaving any kind of suffix like refs~/heads~/bar is just asking for\nsomeone to delete a branch twice.\n\nRegardless of whether or not it would be difficult to implement, I think\nthe ideal (for me) would be:\n 1) existing syntax should work as-is. I like my reflog and don't want\npeople screwing with it ;)\n 2) new syntax should be added for \"some/ref@{..even if it's been\nrenamed or deleted..}\", perhaps an entry in the reflog which points to\nthe \"old name\" / fact that it's been resurrected, for\nmoves/resurrections\n 3) getting rid of something \"for real\" should be a simple command away.\nIf the steps are getting too numerous (delete, expire, /then/ prune?\nAnything else?) then perhaps we just need a \"git shred <ref>\" which\ntakes care of listing out what will be involved, giving you lots of\nchances to abort, etc, and which maybe is less of a sledgehammer than\nthe current method.\n\n\n>From the discussion, I think the things we agree that we all want are:\n 1) For git to not lose data by accident. Maybe there is disagreement as\nto whether or not this already happens. I think the information\ncurrently lost is: a) the name (ie, ease of finding the \"lost\" commit)\nb) the reflog (which I think is utterly lost on delete at the moment?).\nBoth of these are often useless after a delete, but sometimes wanted.\n 2) A straightforward way to restore information which has not been lost\n(again, perhaps there is disagreement as to whether this already exists)\n 3) A way to distinguish between \"the reflog entries of a deleted ref\nwith the same name as our new ref\" and \"the new ref's entries\" (these\nare the \"attic\" discussions, etc) (not applicable to the current\nsituation)\n 4) A way to really get rid of things which are no longer wanted. This\nshould be straightforward and have sane defaults so that, as mentioned,\nadding then removing the wrong remote doesn't leave you with an extra\nrepos' worth of data for the next six months. (obviously this one\nalready exists)\n\nDid I miss anything?\n\n-- \n-- Will\n"},{"id":"145781","messageId":"201007190119.04873.jnareb@gmail.com","threadId":"22055","inReplyTo":"1279484847.8999.22.camel@dreddbeard","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-18T23:19:04Z","receivedAt":"2010-07-18T23:19:04Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Sun, 18 Jul 2010, Will Palmer wrote:\n> On Sun, 2010-07-18 at 13:55 +0200, Jakub Narebski wrote:\n> > On Sun, 18 Jul 2010, Jonathan Nieder wrote:\n\n> > The same as with D/F conflict.  If you rename branch 'foo' to 'bar',\n> > you also rename its reflog, but logs/refs/heads/bar would not conflict\n> > with reflog for deleted branch, logs/refs~/heads~/bar (if you had \n> > deleted branch 'bar').\n> \n> having any kind of suffix like refs~/heads~/bar is just asking for\n> someone to delete a branch twice.\n\nI don't understand what you wanted to say here.  Using the\n\n  $GIT_DIR/logs/refs~/heads~/bar\n\n(and not $GIT_DIR/refs~/heads~/bar) as a reflog for a deleted branch\n'bar' is an implementation detail.  You wouldn't see refs~/heads~/bar\nwhen listing branches... well, perhaps 'git branch --list-deleted'\ncould be used to list deleted branches (by scanning for reflogs).\n \n> Regardless of whether or not it would be difficult to implement, I think\n> the ideal (for me) would be:\n>\n> 1) existing syntax should work as-is. I like my reflog and don't want\n>    people screwing with it ;)\n\nNobody proposes anything different.\n\n> 2) new syntax should be added for \"some/ref@{..even if it's been\n>    renamed or deleted..}\", perhaps an entry in the reflog which points\n>    to the \"old name\" / fact that it's been resurrected, for\n>    moves/resurrections \n\nHere comes hard part: naming ;-)  Or rather chosing API for refering to\ndeleted branches and their reflogs.\n\n> 3) getting rid of something \"for real\" should be a simple command away.\n>    If the steps are getting too numerous (delete, expire, /then/ prune?\n>    Anything else?) then perhaps we just need a \"git shred <ref>\" which\n>    takes care of listing out what will be involved, giving you lots of\n>    chances to abort, etc, and which maybe is less of a sledgehammer than\n>    the current method.\n\nDepending on default safety net, i.e. on default expire time for\nreflogs of deleted branches (perhaps just 1 day, or at most 7 days?),\nit could be as simple as 'git gc'.\n\n> \n> From the discussion, I think the things we agree that we all want are:\n>\n> 1) For git to not lose data by accident. Maybe there is disagreement\n>    as to whether or not this already happens. I think the information\n>    currently lost is: \n>      a) the name (i.e. ease of finding the \"lost\" commit)\n\nUsually HEAD reflog would be enough at least when \"safely\" deleting\nwith 'git branch -d' (and not '-D'), but it is not the case after\nrelaxing rules a bit.\n      \n>      b) the reflog (which I think is utterly lost on delete at the\n>         moment?). \n\nYes it is.  So even if you manage to resurrect branch thanks to HEAD\nreflog or tools like contrib/git-resurrect.sh, the data in reflog would\nbe lost.\n\n> Both of these are often useless after a delete, but sometimes wanted.\n\nTrue.\n\n> 2) A straightforward way to restore information which has not been\n>    lost (again, perhaps there is disagreement as to whether this\n>    already exists)\n\n'git branch <old name> <old SHA-1>' is good, though it is perhaps not\nquite that straighforward.\n\n> 3) A way to distinguish between \"the reflog entries of a deleted ref\n>    with the same name as our new ref\" and \"the new ref's entries\"\n>    (these are the \"attic\" discussions, etc) (not applicable to the\n>    current situation)\n\nThis is a problem of API design.  Probably overloading @{...} yet again\n(@{<n>}, @{<time>}, @{-<n>}, @{upstream}), though using '~' as suffix\n(or ~@{<n>} when refering to reflog of deleted branch) could be\na solution.\n\n> 4) A way to really get rid of things which are no longer wanted. This\n>    should be straightforward and have sane defaults so that, as mentioned,\n>    adding then removing the wrong remote doesn't leave you with an extra\n>    repos' worth of data for the next six months. (obviously this one\n>    already exists)\n\nWell, 'git -c gc.reflogexpire=0 -c gc.reflogexpireunreachable=0 gc --prune=now'\nis a bit mouthfull ;-)\n\n> \n> Did I miss anything?\n\n-- \nJakub Narebski\nPoland\n"},{"id":"145785","messageId":"1279523523.3077.8.camel@dreddbeard","threadId":"22055","inReplyTo":"201007190119.04873.jnareb@gmail.com","subject":"Re: Question about 'branch -d' safety","fromName":"Will Palmer","fromEmail":"wmpalmer@gmail.com","sentAt":"2010-07-19T07:12:03Z","receivedAt":"2010-07-19T07:12:03Z","isPatch":false,"sender":{"key":"wmpalmer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/357044?v=4"},"body":"On Mon, 2010-07-19 at 01:19 +0200, Jakub Narebski wrote:\n> On Sun, 18 Jul 2010, Will Palmer wrote:\n> > On Sun, 2010-07-18 at 13:55 +0200, Jakub Narebski wrote:\n> > having any kind of suffix like refs~/heads~/bar is just asking for\n> > someone to delete a branch twice.\n> \n> I don't understand what you wanted to say here.  Using the\n> \n>   $GIT_DIR/logs/refs~/heads~/bar\n> \n> (and not $GIT_DIR/refs~/heads~/bar) as a reflog for a deleted branch\n> 'bar' is an implementation detail.  You wouldn't see refs~/heads~/bar\n> when listing branches... well, perhaps 'git branch --list-deleted'\n> could be used to list deleted branches (by scanning for reflogs).\n>  \n\ngit branch -d integration\n# git renames refs/heads/integration to refs~/heads~/integration\ngit co -b integration sometopic\n# git creates refs/heads/integration, unrelated to the old one\n(do some work)\n(merge into the main branch)\ngit branch -d integration\n\nNow what?\ngit renames refs/heads/integration to ... what?\n- does the old refs~/heads~/integration get clobbered? If that's ever\nokay, why are we even having this discussion?\n- does the \"old reflog\" stuff get combined? If that's ever okay, why\neven have an extra reflog, instead of just using the reflog we already\nhave?\n- do we move everything else one step down, so refs~/heads~/integration\nbecomes refs~2/heads~2/integration? (ie: 2-dimensional reflog, which\nsounds rather too fancy, to me)\n\n\n-- \n-- Will\n"},{"id":"145787","messageId":"201007191301.22287.jnareb@gmail.com","threadId":"22055","inReplyTo":"1279523523.3077.8.camel@dreddbeard","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-19T11:01:20Z","receivedAt":"2010-07-19T11:01:20Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Mon, 19 Jul 2010, Will Palmer wrote:\n> On Mon, 2010-07-19 at 01:19 +0200, Jakub Narebski wrote:\n> > On Sun, 18 Jul 2010, Will Palmer wrote:\n> > > On Sun, 2010-07-18 at 13:55 +0200, Jakub Narebski wrote:\n\n> > > having any kind of suffix like refs~/heads~/bar is just asking for\n> > > someone to delete a branch twice.\n> > \n> > I don't understand what you wanted to say here.  Using the\n> > \n> >   $GIT_DIR/logs/refs~/heads~/bar\n> > \n> > (and not $GIT_DIR/refs~/heads~/bar) as a reflog for a deleted branch\n> > 'bar' is an implementation detail.  You wouldn't see refs~/heads~/bar\n> > when listing branches... well, perhaps 'git branch --list-deleted'\n> > could be used to list deleted branches (by scanning for reflogs).\n> \n> git branch -d integration\n> # git renames logs/refs/heads/integration to logs/refs~/heads~/integration\n  # git adds deletion event to logs/refs~/heads~/integration reflog\n    (similar to putting creation or rename event in reflog)\n> git co -b integration sometopic\n> # git creates refs/heads/integration, unrelated to the old one\n> (do some work)\n> (merge into the main branch)\n> git branch -d integration\n\nYou touch interesting and important issue.\n\n> \n> Now what?\n> git renames refs/heads/integration to ... what?\n> - does the old refs~/heads~/integration get clobbered? If that's ever\n>   okay, why are we even having this discussion?\n\nThat's one solution.  We give some safety net, but not too much of\nsafety net.\n\n> - does the \"old reflog\" stuff get combined? If that's ever okay, why\n>   even have an extra reflog, instead of just using the reflog we\n>   already have?\n\nThat's another solution.  We have deletion events in reflog to find\nevents for first 'integration', and distinguish them from events for\nsecond (unrelated) 'integration' branch.\n\nWe can't just deal with reflogs of deleted branches by leaving them\nas they are, not moved to logs/refs~/heads~/..., and combining them\nbecause of possibility of D/F conflict (and yet another issue,\ndescribed below).  The reflog for branch 'foo' would block creating\nreflog for branch 'foo/bar'.  Besides I think it's O.K. to require\nmore work to access reflogs for deleted branches, but not good to\nforce more work for reflogs for normal branches.\n\n\nThere is also a variant of the situation you described that makes\nit harder for the 'concatenate reflogs of deleted branches' solution\nto work well, namely:\n\n $ git branch -d foo\n $ git branch -m bar foo   \n $ git branch -d foo\n\nwhere branch 'bar', renamed to 'foo' and then deleted has reflog from\nbefore deletion of first 'foo' branch.\n\n> - do we move everything else one step down, so refs~/heads~/integration\n>   becomes refs~2/heads~2/integration? (ie: 2-dimensional reflog, which\n>   sounds rather too fancy, to me)\n\nThis is yet another solution, although I think the better naming would\nbe to borrow concept of numbered backups, i.e. have\n\n  logs/refs~/heads~/integration~1~\n  logs/refs~/heads~/integration~2~\n  ...\n\nBTW., we can even make git branch ask (prompt for answer) whether to\ndelete old reflog of first 'integration' branch, or keep it in some\nform... unless confiogured to always choose one solution.\n\n\nSigh... this makes eventual solution complicated, but the problem is\nalso complicated...\n\n\nP.S. Wouldn't 'refs~' and 'foo~1~' filenames/pathnames be a problem\non MS Windows / on MS Windows filesystems: NTFS or FAT28^W FAT32?\nOr on HFS+ on MacOS X?\n\n-- \nJakub Narebski\nPoland\n"},{"id":"145800","messageId":"4C448879.4040202@workspacewhiz.com","threadId":"22055","inReplyTo":"1279523523.3077.8.camel@dreddbeard","subject":"Re: Question about 'branch -d' safety","fromName":"Joshua Jensen","fromEmail":"jjensen@workspacewhiz.com","sentAt":"2010-07-19T17:16:41Z","receivedAt":"2010-07-19T17:16:41Z","isPatch":false,"sender":{"key":"jjensen@workspacewhiz.com","avatar":"https://avatars.githubusercontent.com/u/111687?v=4"},"body":"  ----- Original Message -----\nFrom: Will Palmer\nDate: 7/19/2010 1:12 AM\n> git branch -d integration\n> # git renames refs/heads/integration to refs~/heads~/integration\n> git co -b integration sometopic\n> # git creates refs/heads/integration, unrelated to the old one\n> (do some work)\n> (merge into the main branch)\n> git branch -d integration\n>\n> Now what?\n> git renames refs/heads/integration to ... what?\n> - does the old refs~/heads~/integration get clobbered? If that's ever\n> okay, why are we even having this discussion?\n> - does the \"old reflog\" stuff get combined? If that's ever okay, why\n> even have an extra reflog, instead of just using the reflog we already\n> have?\n> - do we move everything else one step down, so refs~/heads~/integration\n> becomes refs~2/heads~2/integration? (ie: 2-dimensional reflog, which\n> sounds rather too fancy, to me\nI was bit by this last week.  I deleted a branch.  A few days later, I \nrealized I needed the branch.  It wasn't in the reflog, so I had to look \nthrough the \"lost\" objects to find it.\n\nMy brain has become muddied with all the ~2 stuff.  Explain again why it \ncan't be as simple as this?\n\ngit branch -d integration\n\ngit reflog\n\n0000001 HEAD@{0}: (fake)checkout: moving from integration to master \n(0000001)\n8000000 HEAD@{1}: branch -d: Deleting integration\n0000001 HEAD@{2}: (fake)checkout: moving from master to integration \n(8000000)\n\ngit checkout -b integration HEAD@{1}  (or 8000000)\n\n-Josh\n"},{"id":"145803","messageId":"7v7hkrpcrk.fsf@alter.siamese.dyndns.org","threadId":"22055","inReplyTo":"20100717093006.GA11452@localhost","subject":"Re: Question about 'branch -d' safety","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-19T18:06:23Z","receivedAt":"2010-07-19T18:06:23Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Clemens Buchacher <drizzd@aon.at> writes:\n\n> Pros and cons for \"undeleting branches\":\n>\n> + safety net\n> It should not be easy to lose information with git.\n\nI am personally not very convinced by this argument when it comes to the\ncases where the user actively asks us to remove something.\n\n> + less dependant on git branch -d\n>\n> Since git branch -d deletes branches which have been merged to a\n> remote tracking branch, it does no longer guarantee that the branch\n> is still available in history locally, and if the branch is also\n> deleted remotely, running git remote prune removes it entirely.\n\nSorry, I have no idea what you are talking about in this paragraph.  I\ncannot read \"Who depends on git branch -d to achieve what\" from your\ndescription.  All I read in the above description is that what the command\ndoes is a good thing.  If you want to keep the remote tracking branches\naround, don't run \"branch -d\".  If you do want to remove the history of a\nbranch, run \"branch -d\".  Isn't it that simple?\n\nPerhaps the missing \"achieve what\" may be that \"output from 'git branch'\nis too cluttered if I don't remove already merged branches as early as\npossible, and forces me to run 'git branch -d' to clean things up too\noften\"?  If that is the issue you are trying to address, perhaps we can\nsolve that in a more direct way, e.g. \"git branch --no-merged\" easier to\naccess?\n\n> - user interface complexity\n>\n> How to prune deleted branches? Currently, it is enough to do \"git\n> branch -D branch; git gc --prune\" in order to get rid of the branch\n> objects, at least if the HEAD reflog does not contain it or has\n> expired. Consider for example adding a remote, and removing it\n> again. This operation would leave a bunch of deleted branches,\n> which potentially occupy a lot of disk space.\n>\n> How to find and restore deleted branches? If the reflog is used to\n> record deleted branches, and a new branch of the same name is\n> created, the reflog contains entries from unrelated branches. [1]\n> If the deleted reflogs are stored in an attic, how do we reference\n> those?\n\nThis is the area I am most concerned about.\n\nIf you take an analogy in say a file server implementation, yes, it should\nnot be easy to lose information there.  But it is and should be easy to\nsay \"rm junk\".  How would people recover from a mistake if they typed a\nwrong filename, \"rm junio\" to lose a precious file \"junio\", when they\nmeant to lose \"junk\"?  They go to backups.  Can't git users do the same?\nAfter all, .git directory is stored on a filesystem of some sort, and\ntaking a backup (you do take backups, don't you?) and picking the stuff\nyou lost from there should be a standard procedure that can be learned\noutside of the context of git.\n\nThis is pretty-much a tangent, but I recall from time to time people\nwonder why the branch namespace is not flat.  If that is a common wish,\nyour \"tilde-suffix all the intermediate path components\" trick could be\nused in later versions of git (perhaps 1.8.X series) to improve the system\nto allow \"maint-1.7.0\" branch and topic branches that forked from it\ne.g. \"maint-1.7.0/fix-frotz\" and \"maint-1.7.0/fix-nitfol\" peacefully\nco-exist.\n"},{"id":"145809","messageId":"20100719192205.GA21744@localhost","threadId":"22055","inReplyTo":"7v7hkrpcrk.fsf@alter.siamese.dyndns.org","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-19T19:22:05Z","receivedAt":"2010-07-19T19:22:05Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"On Mon, Jul 19, 2010 at 11:06:23AM -0700, Junio C Hamano wrote:\n> Clemens Buchacher <drizzd@aon.at> writes:\n> \n> > + less dependant on git branch -d\n> >\n> > Since git branch -d deletes branches which have been merged to a\n> > remote tracking branch, it does no longer guarantee that the branch\n> > is still available in history locally, and if the branch is also\n> > deleted remotely, running git remote prune removes it entirely.\n> \n> Sorry, I have no idea what you are talking about in this paragraph.  I\n> cannot read \"Who depends on git branch -d to achieve what\" from your\n> description.  All I read in the above description is that what the command\n> does is a good thing.  If you want to keep the remote tracking branches\n> around, don't run \"branch -d\".  If you do want to remove the history of a\n> branch, run \"branch -d\".  Isn't it that simple?\n\nSo why do we have \"branch -d\" in the first place? Exactly because I\ndo _not_ want to reomve the history of a branch. I just want to\nremove the reference to it, because it has already been merged and\nis therefore of no interest any more.\n\n> Perhaps the missing \"achieve what\" may be that \"output from 'git branch'\n> is too cluttered if I don't remove already merged branches as early as\n> possible, and forces me to run 'git branch -d' to clean things up too\n> often\"?  If that is the issue you are trying to address, perhaps we can\n> solve that in a more direct way, e.g. \"git branch --no-merged\" easier to\n> access?\n\nThat would only delay the need for cleanup. And when I do the\ncleanup, I have typically forgotten what the branch contains\nexactly. So I like to use \"branch -d\" to find out if the branch\npotentially contains something interesting, and get rid of it\nimmediately if it does not. If that does not work I have no choice\nbut to look at the branch before deleting it blindly.\n\nAnd I used to consider both \"branch -d\" and \"remote prune\" to be\nsafe, because I knew that at some point I would have had to use\n\"branch -D\" in order to actually lose history. Now this is not true\nany more due to the scenario described above. In that sense,\n\"branch -d\" is now pointless, because it's hardly safer than\n\"branch -D\".\n\nSo this process is where a cannot depend on \"branch -d\" any more.\n\n> > - user interface complexity\n> >\n> > How to prune deleted branches? Currently, it is enough to do \"git\n> > branch -D branch; git gc --prune\" in order to get rid of the branch\n> > objects, at least if the HEAD reflog does not contain it or has\n> > expired. Consider for example adding a remote, and removing it\n> > again. This operation would leave a bunch of deleted branches,\n> > which potentially occupy a lot of disk space.\n> >\n> > How to find and restore deleted branches? If the reflog is used to\n> > record deleted branches, and a new branch of the same name is\n> > created, the reflog contains entries from unrelated branches. [1]\n> > If the deleted reflogs are stored in an attic, how do we reference\n> > those?\n> \n> This is the area I am most concerned about.\n\nBelow you explain that we not need this feature, because we can do\nbackups instead. I disagree. But even if I were to agree, why are\nyou concerned about that? Even if you do not use the feature\npersonally, does it do any harm?\n\nThe effort necessary to permanently delete a branch (delete,\nexpire, prune) will stay exactly the same. The reflog syntax can\nalso stay the same. We only really need new commands to list and\nrecreate deleted branches.\n\n> If you take an analogy in say a file server implementation, yes, it should\n> not be easy to lose information there.  But it is and should be easy to\n> say \"rm junk\".  How would people recover from a mistake if they typed a > wrong filename, \"rm junio\" to lose a precious file \"junio\", when they\n> meant to lose \"junk\"?  They go to backups.\n\nBackups are a good measure to protect against loss of large amounts\nof data. They do not protect very well from an accidental \"rm\njunio\", because that only works if junio was already in the latest\nbackup. Protecting against small-scale changes is exactly what a\nversion control system is good at. And they are much superior to\nbackups, because they keep history.\n\n> Can't git users do the same?\n> After all, .git directory is stored on a filesystem of some sort, and\n> taking a backup (you do take backups, don't you?) and picking the stuff\n> you lost from there should be a standard procedure that can be learned\n> outside of the context of git.\n\nI usually use git for my more frequent backups. I put everything\nthat changes a lot and contains much personal work under version\ncontrol.\n\nClemens\n"},{"id":"145810","messageId":"20100719193459.GB21744@localhost","threadId":"22055","inReplyTo":"4C448879.4040202@workspacewhiz.com","subject":"Re: Question about 'branch -d' safety","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-07-19T19:34:59Z","receivedAt":"2010-07-19T19:34:59Z","isPatch":false,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"On Mon, Jul 19, 2010 at 11:16:41AM -0600, Joshua Jensen wrote:\n> \n> 0000001 HEAD@{0}: (fake)checkout: moving from integration to master\n> (0000001)\n> 8000000 HEAD@{1}: branch -d: Deleting integration\n> 0000001 HEAD@{2}: (fake)checkout: moving from master to integration\n> (8000000)\n\nThat does not fit very well with the current definition of the HEAD\nreflog.\n\nBut I do like the idea. We could append branch deletions to\n.git/log/deleted, which otherwise behaves exactly like the HEAD\nreflog.  And we simply do not keep the reflogs of deleted branches.\n\nClemens\n"},{"id":"145811","messageId":"1279568744.3009.14.camel@dreddbeard","threadId":"22055","inReplyTo":"4C448879.4040202@workspacewhiz.com","subject":"Re: Question about 'branch -d' safety","fromName":"Will Palmer","fromEmail":"wmpalmer@gmail.com","sentAt":"2010-07-19T19:45:44Z","receivedAt":"2010-07-19T19:45:44Z","isPatch":false,"sender":{"key":"wmpalmer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/357044?v=4"},"body":"On Mon, 2010-07-19 at 11:16 -0600, Joshua Jensen wrote:\n> My brain has become muddied with all the ~2 stuff.  Explain again why it \n> can't be as simple as this?\n...snip...\n> git checkout -b integration HEAD@{1}  (or 8000000)\n> \n> -Josh\n\nBecause:\n 1) The HEAD reflog is the wrong place to stick things which weren't\nrecently checked-out.\n and 2) the previous tip is currently the easiest-to-recover part of a\ndeleted branch. What's lost is all the reflog data: order of states, and\nhow they were reached.\n\nHowever, I /do/ think it's as simple as \"don't delete the reflog right\naway when you delete a branch\", and other edge-cases and niceties in\nterms of UI (such as ref renaming, resurrection of refs for tracking\nunrelated data, etc) can be taken care of later, if there's actually a\nneed for them.\n\n-- \n-- Will\n"},{"id":"145815","messageId":"201007192236.21786.jnareb@gmail.com","threadId":"22055","inReplyTo":"4C448879.4040202@workspacewhiz.com","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-19T20:36:20Z","receivedAt":"2010-07-19T20:36:20Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Mon, 19 Jul 2010, Joshua Jensen wrote:\n\n> My brain has become muddied with all the ~2 stuff.  Explain again why it \n> can't be as simple as this?\n> \n> git branch -d integration\n> \n> git reflog\n> \n> 0000001 HEAD@{0}: (fake)checkout: moving from integration to master \n> (0000001)\n> 8000000 HEAD@{1}: branch -d: Deleting integration\n> 0000001 HEAD@{2}: (fake)checkout: moving from master to integration \n> (8000000)\n\nBecause 'integration' branch nowadays might have been never checked out,\nand not be in HEAD, even though 'git branch -d integration' works.\nStuffing unrelated events in HEAD reflog, instead of having it be action\nhistory of HEAD symref is IMVHO a bad idea.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"145817","messageId":"201007192240.38681.jnareb@gmail.com","threadId":"22055","inReplyTo":"1279568744.3009.14.camel@dreddbeard","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-19T20:40:37Z","receivedAt":"2010-07-19T20:40:37Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Mon, 19 Jul 2010, Will Palmer wrote:\n> On Mon, 2010-07-19 at 11:16 -0600, Joshua Jensen wrote:\n\n> > My brain has become muddied with all the ~2 stuff.  Explain again why it \n> > can't be as simple as this?\n> ...snip...\n> > git checkout -b integration HEAD@{1}  (or 8000000)\n> > \n> > -Josh\n> \n> Because:\n>  1) The HEAD reflog is the wrong place to stick things which weren't\n> recently checked-out.\n>  and 2) the previous tip is currently the easiest-to-recover part of a\n> deleted branch. What's lost is all the reflog data: order of states, and\n> how they were reached.\n> \n> However, I /do/ think it's as simple as \"don't delete the reflog right\n> away when you delete a branch\", and other edge-cases and niceties in\n> terms of UI (such as ref renaming, resurrection of refs for tracking\n> unrelated data, etc) can be taken care of later, if there's actually a\n> need for them.\n\nThere are at least two issues, which are not niceties, but requirements:\n1.) not deleting reflog for 'foo' when deleting 'foo' branch blocks\n    creating reflog for 'foo/bar' branch because of D/F conflict.\n2.) there is issue of 'git branch -m bar foo', i.e. renaming some other\n    branch to 'foo', a branch which has its own reflog.\n\nBut you are right in that \"moving reflog to attic\" can be postponed till\nneeded.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"145818","messageId":"201007192249.58357.jnareb@gmail.com","threadId":"22055","inReplyTo":"7v7hkrpcrk.fsf@alter.siamese.dyndns.org","subject":"Re: Question about 'branch -d' safety","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-07-19T20:49:57Z","receivedAt":"2010-07-19T20:49:57Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Mon, 19 Jul 2010, Junio C Hamano wrote:\n\n> If you take an analogy in say a file server implementation, yes, it should\n> not be easy to lose information there.  But it is and should be easy to\n> say \"rm junk\".  How would people recover from a mistake if they typed a\n> wrong filename, \"rm junio\" to lose a precious file \"junio\", when they\n> meant to lose \"junk\"?  They go to backups.\n\nFirst, I guess that many people have 'rm' aliased to 'rm -i', so it\nasks for confirmation... though it only moves safety a bit, as \n'rm -f junio' would be still dangerous.\n\nSecond, desktop environment have the notion of trashcan, where files\nto be deleted are moved, and kept for some time or untill trashcan is\nemptied (though it moves safety a bit to people using 'hard delete'\ninstead of moving to trashcan).  Or use undeletefs,\n\n>                                            Can't git users do the same? \n> After all, .git directory is stored on a filesystem of some sort, and\n> taking a backup (you do take backups, don't you?) and picking the stuff\n> you lost from there should be a standard procedure that can be learned\n> outside of the context of git.\n\nPeople expect version control system to protect them more from mistakes.\n\nSidenote: there aere requests in last Git User's Survey (from 2009) to\nhave safety net against losing changes due to \"git reset --hard\", in the\nform of automatically stashing blobs or something.\n\n> \n> This is pretty-much a tangent, but I recall from time to time people\n> wonder why the branch namespace is not flat.  If that is a common wish,\n> your \"tilde-suffix all the intermediate path components\" trick could be\n> used in later versions of git (perhaps 1.8.X series) to improve the system\n> to allow \"maint-1.7.0\" branch and topic branches that forked from it\n> e.g. \"maint-1.7.0/fix-frotz\" and \"maint-1.7.0/fix-nitfol\" peacefully\n> co-exist.\n\nHmmmmm... true, this would make having 'foo' and 'foo/bar' branches \ncoexists, whether they are loose or packed, and whether they have reflogs\nor not.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"145835","messageId":"4C451272.8000802@workspacewhiz.com","threadId":"22055","inReplyTo":"1279568744.3009.14.camel@dreddbeard","subject":"Re: Question about 'branch -d' safety","fromName":"Joshua Jensen","fromEmail":"jjensen@workspacewhiz.com","sentAt":"2010-07-20T03:05:22Z","receivedAt":"2010-07-20T03:05:22Z","isPatch":false,"sender":{"key":"jjensen@workspacewhiz.com","avatar":"https://avatars.githubusercontent.com/u/111687?v=4"},"body":"  ----- Original Message -----\nFrom: Will Palmer\nDate: 7/19/2010 1:45 PM\n> On Mon, 2010-07-19 at 11:16 -0600, Joshua Jensen wrote:\n>> My brain has become muddied with all the ~2 stuff.  Explain again why it\n>> can't be as simple as this?\n> ...snip...\n>> git checkout -b integration HEAD@{1}  (or 8000000)\n>>\n>> -Josh\n> Because:\n>   1) The HEAD reflog is the wrong place to stick things which weren't\n> recently checked-out.\n>   and 2) the previous tip is currently the easiest-to-recover part of a\n> deleted branch. What's lost is all the reflog data: order of states, and\n> how they were reached.\n>\n> However, I /do/ think it's as simple as \"don't delete the reflog right\n> away when you delete a branch\", and other edge-cases and niceties in\n> terms of UI (such as ref renaming, resurrection of refs for tracking\n> unrelated data, etc) can be taken care of later, if there's actually a\n> need for them.\nThanks for the extra clarification.\n\nFor my sake, how is the previous tip the easiest-to-recover part of a \ndeleted branch?  How do you go about finding the lost object?\n\nJosh\n"},{"id":"145844","messageId":"1279607519.3534.14.camel@dreddbeard","threadId":"22055","inReplyTo":"4C451272.8000802@workspacewhiz.com","subject":"Re: Question about 'branch -d' safety","fromName":"Will Palmer","fromEmail":"wmpalmer@gmail.com","sentAt":"2010-07-20T06:31:59Z","receivedAt":"2010-07-20T06:31:59Z","isPatch":false,"sender":{"key":"wmpalmer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/357044?v=4"},"body":"On Mon, 2010-07-19 at 21:05 -0600, Joshua Jensen wrote:\n> For my sake, how is the previous tip the easiest-to-recover part of a \n> deleted branch?  How do you go about finding the lost object?\n> \n> Josh\n\ngit fsck | grep '^dangling commit' | while read dangling commit hash; do\n\tgit log -1 --format='%at %h %s' $hash\ndone | sort -nr | less -FRSX\n\n\n-- \n-- Will\n"},{"id":"145850","messageId":"AANLkTilOhFqhg1ulWEZUceNmrf0djZti9E1Y2QzTRgaN@mail.gmail.com","threadId":"22055","inReplyTo":"7v7hkrpcrk.fsf@alter.siamese.dyndns.org","subject":"Re: Question about 'branch -d' safety","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2010-07-20T13:19:55Z","receivedAt":"2010-07-20T13:19:55Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Mon, Jul 19, 2010 at 18:06, Junio C Hamano <gitster@pobox.com> wrote:\n> Clemens Buchacher <drizzd@aon.at> writes:\n>\n>> Pros and cons for \"undeleting branches\":\n>>\n>> + safety net\n>> It should not be easy to lose information with git.\n>\n> I am personally not very convinced by this argument when it comes to the\n> cases where the user actively asks us to remove something.\n\nI think it's mainly about user interface consistency. When you\n\"delete\" stuff in with git reset (i.e. move the HEAD) it's recorded in\nthe reflog, but this isn't the case with branch deletions.\n\nAs Will Palmer pointed out, being able to tell newbies \"Don't worry,\nyou aren't going to break it\" is a very powerful thing to be able to\ntell newbies and experienced users using Git.\n\nIt encourages experimentation, because you know that even if you botch\nthe rewrite and delete the wrong branch it's easy to recover your work\nfrom the reflog in the morning.\n\nI'd really like something like Joshua Jensen's suggestion for\nrecording a fake branch deletion commit in the reflog.\n"},{"id":"145851","messageId":"vpq630a5lb5.fsf@bauges.imag.fr","threadId":"22055","inReplyTo":"AANLkTilOhFqhg1ulWEZUceNmrf0djZti9E1Y2QzTRgaN@mail.gmail.com","subject":"Re: Question about 'branch -d' safety","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-07-20T13:34:22Z","receivedAt":"2010-07-20T13:34:22Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Mon, Jul 19, 2010 at 18:06, Junio C Hamano <gitster@pobox.com> wrote:\n>> Clemens Buchacher <drizzd@aon.at> writes:\n>>\n>>> Pros and cons for \"undeleting branches\":\n>>>\n>>> + safety net\n>>> It should not be easy to lose information with git.\n>>\n>> I am personally not very convinced by this argument when it comes to the\n>> cases where the user actively asks us to remove something.\n>\n> [...]\n>\n> As Will Palmer pointed out, being able to tell newbies \"Don't worry,\n> you aren't going to break it\" is a very powerful thing to be able to\n> tell newbies and experienced users using Git.\n\nAlso, one of the _very_ cool things with Git is that advance users can\ntell newbies \"don't worry, if you break it, I'll be able to repair\nit\". And advanced users are almost always able to repair beginner's\nmistake after the fact (in my case, it's often about teachers helping\nstudents, but it works also in other situations).\n\n\"branch -d\" (and even more \"branch -D\") deleting the reflog is one of\nthe few exceptions to this rule: dumb users can shoot themselves in\nthe foot (which is normal) but then the doctor cannot undo it easily.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"}]}