{"thread":{"id":"32211","subject":"Operations on unborn branch","startedAt":"2012-11-27T17:25:52Z","lastAt":"2012-12-05T12:58:20Z","messageCount":18,"participants":["Martin von Zweigbergk","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"203998","messageId":"CANiSa6isDKAgxHWqh5XiQ-adT3-ASFtvAshp028DTcotjQxzmQ@mail.gmail.com","threadId":"32211","inReplyTo":null,"subject":"Operations on unborn branch","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-27T17:25:52Z","receivedAt":"2012-11-27T17:25:52Z","isPatch":false,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"While looking at how to handle \"git rebase --root\", I noticed that\n\"git cherry-pick\" fails with the following when run on an unborn\nbranch:\n\nerror: You do not have a valid HEAD\nfatal: cherry-pick failed\n\nI can not see any reason that it shouldn't work. \"git cherry-pick -n\"\ndoes work. (For rebase, \"git cherry-pick --ff\" would be used, and I\nthink that should also work on an unborn branch.)\n\nAlso, \"git reset\" doesn't work on an unborn branch and I can not see\nany reason that it shouldn't work. This was also asked on stack\noverflow [1], and of course the solution is to use \"git rm --cached\",\nbut doesn't mean that \"git reset\" shouldn't work.\n\nI have very limited time to work on git these days, so if anyone else\nwould like to work on any of this, I would be very happy. I _might_\ntake some time to fix the cherry-pick issue.\n\nBtw, every time I run into problems like these with the treatment of\nroot commits, I can't help but wonder how things would look if git had\nalways had a single root commit (naturally with some dummy user,\ntimestamp etc to ensure sameness across repos). With my limited\nknowledge, it seems like that would complicate a few things, but\nsimplify a lot of things (maybe I'm biased because of the things I\nhave happened to work on?). Has anyone spent some time seriously\nthinking about this? I suppose it would be hard to introduce\nbackward-compatibly, and maybe this is very unrealistic even for git\n2.0, but I would be curious to hear what others think.\n\nMartin\n\n[1] http://stackoverflow.com/questions/3894808/new-git-repository-and-already-git-reset-does-not-work\n"},{"id":"204020","messageId":"7vd2yyn685.fsf@alter.siamese.dyndns.org","threadId":"32211","inReplyTo":"CANiSa6isDKAgxHWqh5XiQ-adT3-ASFtvAshp028DTcotjQxzmQ@mail.gmail.com","subject":"Re: Operations on unborn branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-27T20:25:30Z","receivedAt":"2012-11-27T20:25:30Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Martin von Zweigbergk <martinvonz@gmail.com> writes:\n\n> simplify a lot of things (maybe I'm biased because of the things I\n> have happened to work on?)\n\nYes.  Do not waste time on it.\n"},{"id":"204023","messageId":"CANiSa6g2UQyvOWS+nuc6y=wzfFTVJ6G8OK14KOs5DJ3f-n5vOw@mail.gmail.com","threadId":"32211","inReplyTo":"7vd2yyn685.fsf@alter.siamese.dyndns.org","subject":"Re: Operations on unborn branch","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-27T20:39:44Z","receivedAt":"2012-11-27T20:39:44Z","isPatch":false,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Tue, Nov 27, 2012 at 12:25 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Martin von Zweigbergk <martinvonz@gmail.com> writes:\n>\n>> simplify a lot of things (maybe I'm biased because of the things I\n>> have happened to work on?)\n>\n> Yes.  Do not waste time on it.\n\nYes, no way I would waste time on that; I was mostly just curious.\n\nWhat I might spend time on is to fix cherry-pick.\n"},{"id":"204138","messageId":"7vd2yyi4l1.fsf@alter.siamese.dyndns.org","threadId":"32211","inReplyTo":"CANiSa6g2UQyvOWS+nuc6y=wzfFTVJ6G8OK14KOs5DJ3f-n5vOw@mail.gmail.com","subject":"Re: Operations on unborn branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-28T07:12:10Z","receivedAt":"2012-11-28T07:12:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Martin von Zweigbergk <martinvonz@gmail.com> writes:\n\n> On Tue, Nov 27, 2012 at 12:25 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Martin von Zweigbergk <martinvonz@gmail.com> writes:\n>>\n>>> simplify a lot of things (maybe I'm biased because of the things I\n>>> have happened to work on?)\n>>\n>> Yes.  Do not waste time on it.\n>\n> Yes, no way I would waste time on that; I was mostly just curious.\n\nYou have to special case the edges whichever way you go.  You can\nalways add such a fixed parent commit whenever you create a new root\ncommit, but then the codepath that currently relies on the real root\ncommit not having any parent start needing to notice if the parent\nis the fixed fake commit and exclude it from thee history.  Or you\ncan create a new root commit as parent-less like we currently do,\nand any history examination do not have to special case \"ah, I\nthought there is a parent commit, but that turns out to be the fake\none, so I need to ignore it.\"  Creation of a root commit is a one-time\noperation in any sane history; if we have to have special cases\nsomewhere anyway, it is better to have them in these one-time\noperation codepaths.\n"},{"id":"204274","messageId":"1354213975-17866-1-git-send-email-martinvonz@gmail.com","threadId":"32211","inReplyTo":"CANiSa6isDKAgxHWqh5XiQ-adT3-ASFtvAshp028DTcotjQxzmQ@mail.gmail.com","subject":"[RFC/PATCH 0/2] Fix \"git reset\" on unborn branch","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-29T18:32:53Z","receivedAt":"2012-11-29T18:32:53Z","isPatch":true,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"I decided to address this before \"cherry-pick on unborn branch\". RFC\nmostly because I'm not sure about the user interface. When we have\nagreed on that, I will add documentation.\n\nMartin von Zweigbergk (2):\n  reset: learn to reset to tree\n  reset: learn to reset on unborn branch\n\n builtin/reset.c                     | 73 ++++++++++++++++++++++---------------\n t/t1512-rev-parse-disambiguation.sh |  4 --\n t/t7102-reset.sh                    | 26 +++++++++++++\n t/t7106-reset-unborn-branch.sh      | 52 ++++++++++++++++++++++++++\n 4 files changed, 122 insertions(+), 33 deletions(-)\n create mode 100755 t/t7106-reset-unborn-branch.sh\n\n-- \n1.8.0.1.240.ge8a1f5a\n"},{"id":"204275","messageId":"1354213975-17866-2-git-send-email-martinvonz@gmail.com","threadId":"32211","inReplyTo":"1354213975-17866-1-git-send-email-martinvonz@gmail.com","subject":"[RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-29T18:32:54Z","receivedAt":"2012-11-29T18:32:54Z","isPatch":true,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"In cases where HEAD is not supposed to be updated, there is no reason\nthat \"git reset\" should require a commit, a tree should be enough. So\nmake \"git reset $rev^{tree}\" work just like \"git reset $rev\", except\nthat the former will not update HEAD (since there is no commit to\npoint it to).\n\nDisallow --soft with trees, since that is about updating only HEAD.\n\nBy not updating HEAD, \"git reset $rev^{tree}\" behaves quite like \"git\nreset $rev .\". One might therefore consider requiring a path when\nusing reset with a tree to make that similarity more obvious. However,\na future commit will make \"git reset\" work on an unborn branch by\ninterpreting it as \"git reset $empty_tree\" and it would seem\nunintuitive to the user to say \"git reset .\" on an unborn\nbranch. Requiring a path would also make \"git reset --hard $tree\"\ndisallowed.\n\nThis commit effectively undoes some of commit 13243c2 (reset: the\ncommand takes committish, 2012-07-03). The command line argument is\nnow required to be an unambiguous treeish.\n\n---\n\nMy implementation of lookup_commit_or_tree looks a little clunky. I'm\nnot very familiar with the API. Suggestions welcome.\n\nWhy is the \"HEAD is now at ...\" message printed only for --hard reset?\nAfter all, HEAD is updated for all types of reset not involving paths.\n\n builtin/reset.c                     | 67 +++++++++++++++++++++----------------\n t/t1512-rev-parse-disambiguation.sh |  4 ---\n t/t7102-reset.sh                    | 26 ++++++++++++++\n 3 files changed, 65 insertions(+), 32 deletions(-)\n\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex 915cc9f..cec9874 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -225,6 +225,21 @@ static void die_if_unmerged_cache(int reset_type)\n \n }\n \n+static struct object *lookup_commit_or_tree(const char *rev) {\n+\tunsigned char sha1[20];\n+\tstruct commit *commit;\n+\tstruct tree *tree;\n+\tif (get_sha1_treeish(rev, sha1))\n+\t\tdie(_(\"Failed to resolve '%s' as a valid ref.\"), rev);\n+\tcommit = lookup_commit_reference_gently(sha1, 1);\n+\tif (commit)\n+\t\treturn (struct object *) commit;\n+\ttree = parse_tree_indirect(sha1);\n+\tif (tree)\n+\t\treturn (struct object *) tree;\n+\tdie(_(\"Could not parse object '%s'.\"), rev);\n+}\n+\n int cmd_reset(int argc, const char **argv, const char *prefix)\n {\n \tint i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;\n@@ -232,7 +247,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \tconst char *rev = \"HEAD\";\n \tunsigned char sha1[20], *orig = NULL, sha1_orig[20],\n \t\t\t\t*old_orig = NULL, sha1_old_orig[20];\n-\tstruct commit *commit;\n+\tstruct object *object;\n+\tstruct commit *commit = NULL;\n \tstruct strbuf msg = STRBUF_INIT;\n \tconst struct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n@@ -276,7 +292,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\t * Otherwise, argv[i] could be either <rev> or <paths> and\n \t\t * has to be unambiguous.\n \t\t */\n-\t\telse if (!get_sha1_committish(argv[i], sha1)) {\n+\t\telse if (!get_sha1_treeish(argv[i], sha1)) {\n \t\t\t/*\n \t\t\t * Ok, argv[i] looks like a rev; it should not\n \t\t\t * be a filename.\n@@ -289,19 +305,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n-\tif (get_sha1_committish(rev, sha1))\n-\t\tdie(_(\"Failed to resolve '%s' as a valid ref.\"), rev);\n-\n-\t/*\n-\t * NOTE: As \"git reset $treeish -- $path\" should be usable on\n-\t * any tree-ish, this is not strictly correct. We are not\n-\t * moving the HEAD to any commit; we are merely resetting the\n-\t * entries in the index to that of a treeish.\n-\t */\n-\tcommit = lookup_commit_reference(sha1);\n-\tif (!commit)\n-\t\tdie(_(\"Could not parse object '%s'.\"), rev);\n-\thashcpy(sha1, commit->object.sha1);\n+\tobject = lookup_commit_or_tree(rev);\n+\tif (object->type == OBJ_COMMIT)\n+\t\tcommit = (struct commit*) object;\n+\telse if (reset_type == SOFT)\n+\t\tdie(_(\"--soft requires a commit, which '%s' is not\"), rev);\n+\thashcpy(sha1, object->sha1);\n \n \tif (patch_mode) {\n \t\tif (reset_type != NONE)\n@@ -347,23 +356,25 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\t\tdie(_(\"Could not reset index file to revision '%s'.\"), rev);\n \t}\n \n-\t/* Any resets update HEAD to the head being switched to,\n-\t * saving the previous head in ORIG_HEAD before. */\n-\tif (!get_sha1(\"ORIG_HEAD\", sha1_old_orig))\n-\t\told_orig = sha1_old_orig;\n-\tif (!get_sha1(\"HEAD\", sha1_orig)) {\n-\t\torig = sha1_orig;\n-\t\tset_reflog_message(&msg, \"updating ORIG_HEAD\", NULL);\n-\t\tupdate_ref(msg.buf, \"ORIG_HEAD\", orig, old_orig, 0, MSG_ON_ERR);\n+\tif (commit) {\n+\t\t/* Any resets update HEAD to the head being switched to,\n+\t\t * saving the previous head in ORIG_HEAD before. */\n+\t\tif (!get_sha1(\"ORIG_HEAD\", sha1_old_orig))\n+\t\t\told_orig = sha1_old_orig;\n+\t\tif (!get_sha1(\"HEAD\", sha1_orig)) {\n+\t\t\torig = sha1_orig;\n+\t\t\tset_reflog_message(&msg, \"updating ORIG_HEAD\", NULL);\n+\t\t\tupdate_ref(msg.buf, \"ORIG_HEAD\", orig, old_orig, 0, MSG_ON_ERR);\n+\t\t}\n+\t\telse if (old_orig)\n+\t\t\tdelete_ref(\"ORIG_HEAD\", old_orig, 0);\n+\t\tset_reflog_message(&msg, \"updating HEAD\", rev);\n+\t\tupdate_ref_status = update_ref(msg.buf, \"HEAD\", sha1, orig, 0, MSG_ON_ERR);\n \t}\n-\telse if (old_orig)\n-\t\tdelete_ref(\"ORIG_HEAD\", old_orig, 0);\n-\tset_reflog_message(&msg, \"updating HEAD\", rev);\n-\tupdate_ref_status = update_ref(msg.buf, \"HEAD\", sha1, orig, 0, MSG_ON_ERR);\n \n \tswitch (reset_type) {\n \tcase HARD:\n-\t\tif (!update_ref_status && !quiet)\n+\t\tif (commit && !update_ref_status && !quiet)\n \t\t\tprint_new_head_line(commit);\n \t\tbreak;\n \tcase SOFT: /* Nothing else to do. */\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 6b3d797..bc1e40c 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -121,10 +121,6 @@ test_expect_success 'git log takes only commit-ish' '\n \tgit log 000000000\n '\n \n-test_expect_success 'git reset takes only commit-ish' '\n-\tgit reset 000000000\n-'\n-\n test_expect_success 'first tag' '\n \t# create one tag 0000000000f8f\n \tgit tag -a -m j7cp83um v1.0.0\ndiff --git a/t/t7102-reset.sh b/t/t7102-reset.sh\nindex b096dc8..d723ef5 100755\n--- a/t/t7102-reset.sh\n+++ b/t/t7102-reset.sh\n@@ -491,4 +491,30 @@ test_expect_success 'disambiguation (4)' '\n \ttest ! -f secondfile\n '\n \n+test_expect_success 'reset to tree does not update HEAD' '\n+\tgit reset --hard HEAD &&\n+\trev_before=$(git rev-parse HEAD) &&\n+\tgit reset HEAD^^{tree} &&\n+\ttest $(git rev-parse HEAD) == $rev_before\n+'\n+\n+test_expect_success 'reset to tree' '\n+\t# for simpler tests, drop last commit containing added files\n+\tgit reset --hard HEAD^ &&\n+\tgit reset HEAD^^{tree} &&\n+\tgit diff --cached HEAD^ --exit-code &&\n+\tgit diff HEAD --exit-code\n+'\n+\n+test_expect_success 'reset --hard to tree' '\n+\tgit reset --hard &&\n+\tgit reset --hard HEAD^^{tree} &&\n+\tgit diff --cached HEAD^ --exit-code &&\n+\tgit diff HEAD^ --exit-code\n+'\n+\n+test_expect_success 'reset to tree not allowed with --soft}' '\n+\ttest_must_fail git reset --soft HEAD^^{tree}\n+'\n+\n test_done\n-- \n1.8.0.1.240.ge8a1f5a\n"},{"id":"204276","messageId":"1354213975-17866-3-git-send-email-martinvonz@gmail.com","threadId":"32211","inReplyTo":"1354213975-17866-1-git-send-email-martinvonz@gmail.com","subject":"[RFC/PATCH 2/2] reset: learn to reset on unborn branch","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-29T18:32:55Z","receivedAt":"2012-11-29T18:32:55Z","isPatch":true,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"When run on an unborn branch, \"git reset\" currently fails with:\n\n  fatal: Failed to resolve 'HEAD' as a valid ref.\n\nFix this by interpreting it as a reset to the empty tree.\n\nIf --patch is given, we currently pass the revision specifier, as\ngiven on the command line, to interactive_reset(). On an unborn\nbranch, HEAD can of course not be resolved, so we need to pass the\nsha1 of the empty tree to interactive_reset() as well. This is fine\nsince interactive_reset only needs the parameter to be a treeish and\ndoesn't use it for display purposes.\n\n---\n\nIs it correct that interactive_reset does not use the revision\nspecifier for display purposes? Or, worse, that it requires it to be a\ncommit in some cases? I tried it and didn't see any problem.\n\n builtin/reset.c                | 10 +++++---\n t/t7106-reset-unborn-branch.sh | 52 ++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 59 insertions(+), 3 deletions(-)\n create mode 100755 t/t7106-reset-unborn-branch.sh\n\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex cec9874..3845225 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -229,7 +229,10 @@ static struct object *lookup_commit_or_tree(const char *rev) {\n \tunsigned char sha1[20];\n \tstruct commit *commit;\n \tstruct tree *tree;\n-\tif (get_sha1_treeish(rev, sha1))\n+\tif (!strcmp(rev, \"HEAD\") && get_sha1(\"HEAD\", sha1)) {\n+\t\t// unborn branch: reset to empty tree\n+\t\thashcpy(sha1, EMPTY_TREE_SHA1_BIN);\n+\t} else if (get_sha1_treeish(rev, sha1))\n \t\tdie(_(\"Failed to resolve '%s' as a valid ref.\"), rev);\n \tcommit = lookup_commit_reference_gently(sha1, 1);\n \tif (commit)\n@@ -292,7 +295,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\t * Otherwise, argv[i] could be either <rev> or <paths> and\n \t\t * has to be unambiguous.\n \t\t */\n-\t\telse if (!get_sha1_treeish(argv[i], sha1)) {\n+\t\telse if (!strcmp(argv[i], \"HEAD\") ||\n+\t\t\t !get_sha1_treeish(argv[i], sha1)) {\n \t\t\t/*\n \t\t\t * Ok, argv[i] looks like a rev; it should not\n \t\t\t * be a filename.\n@@ -315,7 +319,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \tif (patch_mode) {\n \t\tif (reset_type != NONE)\n \t\t\tdie(_(\"--patch is incompatible with --{hard,mixed,soft}\"));\n-\t\treturn interactive_reset(rev, argv + i, prefix);\n+\t\treturn interactive_reset(sha1_to_hex(sha1), argv + i, prefix);\n \t}\n \n \t/* git reset tree [--] paths... can be used to\ndiff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh\nnew file mode 100755\nindex 0000000..67d45be\n--- /dev/null\n+++ b/t/t7106-reset-unborn-branch.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+\n+test_description='git reset should work on unborn branch'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo a >a &&\n+\techo b >b\n+'\n+\n+test_expect_success 'reset' '\n+\tgit add a b &&\n+\tgit reset &&\n+\ttest \"$(git ls-files)\" == \"\"\n+'\n+\n+test_expect_success 'reset HEAD' '\n+\trm .git/index &&\n+\tgit add a b &&\n+\tgit reset HEAD &&\n+\ttest \"$(git ls-files)\" == \"\"\n+'\n+\n+test_expect_success 'reset $file' '\n+\trm .git/index &&\n+\tgit add a b &&\n+\tgit reset a &&\n+\ttest \"$(git ls-files)\" == \"b\"\n+'\n+\n+test_expect_success 'reset -p' '\n+\trm .git/index &&\n+\tgit add a &&\n+\techo y | git reset -p &&\n+\ttest \"$(git ls-files)\" == \"\"\n+'\n+\n+test_expect_success 'reset --soft not allowed' '\n+\trm .git/index &&\n+\tgit add a &&\n+\ttest_must_fail git reset --soft\n+'\n+\n+test_expect_success 'reset --hard' '\n+\trm .git/index &&\n+\tgit add a &&\n+\tgit reset --hard &&\n+\ttest \"$(git ls-files)\" == \"\" &&\n+\ttest_path_is_missing a\n+'\n+\n+test_done\n-- \n1.8.0.1.240.ge8a1f5a\n"},{"id":"204277","messageId":"7v4nk8qmaj.fsf@alter.siamese.dyndns.org","threadId":"32211","inReplyTo":"1354213975-17866-2-git-send-email-martinvonz@gmail.com","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-29T18:47:00Z","receivedAt":"2012-11-29T18:47:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Martin von Zweigbergk <martinvonz@gmail.com> writes:\n\n> In cases where HEAD is not supposed to be updated, there is no reason\n> that \"git reset\" should require a commit, a tree should be enough. So\n> make \"git reset $rev^{tree}\" work just like \"git reset $rev\", except\n> that the former will not update HEAD (since there is no commit to\n> point it to).\n\nThat is a horrible design I have to nack, unless you require\npathspec.  You cannot tell what \"git reset $sha1\" would do without\nchecking the type of the object $sha1 refers to.  If you do this\nonly when pathspec is present, then the design is very reasonable.\n\n> Disallow --soft with trees, since that is about updating only HEAD.\n\nLikewise.\n"},{"id":"204282","messageId":"CANiSa6j2sriXaGr0yH9kMrxDEvKHsjNPX_Exbc2_6ecnPYdroQ@mail.gmail.com","threadId":"32211","inReplyTo":"7v4nk8qmaj.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-29T19:04:30Z","receivedAt":"2012-11-29T19:04:30Z","isPatch":true,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Thu, Nov 29, 2012 at 10:47 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Martin von Zweigbergk <martinvonz@gmail.com> writes:\n>\n>> In cases where HEAD is not supposed to be updated, there is no reason\n>> that \"git reset\" should require a commit, a tree should be enough. So\n>> make \"git reset $rev^{tree}\" work just like \"git reset $rev\", except\n>> that the former will not update HEAD (since there is no commit to\n>> point it to).\n>\n> That is a horrible design I have to nack, unless you require\n> pathspec.  You cannot tell what \"git reset $sha1\" would do without\n> checking the type of the object $sha1 refers to.  If you do this\n> only when pathspec is present, then the design is very reasonable.\n\nVery good point. Thanks! I now see that \"git checkout\" also requires a\npath when given a tree.\n\nSo then \"git reset\" on an unborn branch would imply \"git reset\n$empty_tree -- .\" instead. And \"git reset --hard $tree\" would not be\nallowed. And the intersection of these -- \"git reset --hard\" on and\nunborn branch -- would also not work. Would the correct fix be to\nfirst make \"git reset --hard -- $path\" work (*sigh*)? I have never\nunderstood why that doesn't (shouldn't) work.\n"},{"id":"204285","messageId":"7vzk20p6ik.fsf@alter.siamese.dyndns.org","threadId":"32211","inReplyTo":"7v4nk8qmaj.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-29T19:13:07Z","receivedAt":"2012-11-29T19:13:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Martin von Zweigbergk <martinvonz@gmail.com> writes:\n>\n>> In cases where HEAD is not supposed to be updated, there is no reason\n>> that \"git reset\" should require a commit, a tree should be enough. So\n>> make \"git reset $rev^{tree}\" work just like \"git reset $rev\", except\n>> that the former will not update HEAD (since there is no commit to\n>> point it to).\n>\n> That is a horrible design I have to nack, unless you require\n> pathspec.  You cannot tell what \"git reset $sha1\" would do without\n> checking the type of the object $sha1 refers to.  If you do this\n> only when pathspec is present, then the design is very reasonable.\n\nThe above applies to an _arbitrary_ $sha1.\n\nAllowing \"reset $tree -- $pathspec\" is a very good addition in the\nsame sense that \"git checkout $tree -- $pathspec\" is useful.  These\ntwo commands, \"reset\" and \"checkout\", share that the source we grab\nthe blobs out of only need to be a tree and does not have to be a\ncommit, and the only difference between them is where the blobs we\ngrabbed out of that tree go, either only to the index or to both the\nindex and the working tree.\n\nBut I do not think it is connected, at least at the level the end\nusers perceive, to the issue of \"reset\" issued while on an unborn\nbranch.\n\nIf you limit the scope of the behaviour change exposed to the end\nusers so that you would make\n\n\t$ git reset [HEAD]\n\nact as a short-hand for\n\n\t$ rm -f $GIT_DIR/index\n\nwhen HEAD points at an unborn branch, and similarly make\n\n\t$ git reset --hard [HEAD]\n\nact as a short-hand for\n\n\t$ rm -f $GIT_DIR/index\n        $ git clean -f -d\n\nin such a case, I do not think it is unreasonable at all.\n\nIn such a case,\n\n\t$ git reset --soft [HEAD]\n\nwould become just a no-op.  Earlier you were on an unborn branch,\nand after \"reset --soft\", nothing changes.\n\nHmm?\n"},{"id":"204291","messageId":"7vmwy0p5f6.fsf@alter.siamese.dyndns.org","threadId":"32211","inReplyTo":"CANiSa6j2sriXaGr0yH9kMrxDEvKHsjNPX_Exbc2_6ecnPYdroQ@mail.gmail.com","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-29T19:36:45Z","receivedAt":"2012-11-29T19:36:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Martin von Zweigbergk <martinvonz@gmail.com> writes:\n\n> Would the correct fix be to\n> first make \"git reset --hard -- $path\" work (*sigh*)? I have never\n> understood why that doesn't (shouldn't) work.\n\nWhat does it even mean, even when you are on an existing commit, to\nhard reset partially?\n\nPerhaps you looking for \"git checkout $tree -- $path\"?\n"},{"id":"204298","messageId":"CANiSa6hWYsfm0t+s_q7=CcD78yNfpuduxkRc35xW8qDOy97W3g@mail.gmail.com","threadId":"32211","inReplyTo":"7vzk20p6ik.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-29T22:00:16Z","receivedAt":"2012-11-29T22:00:16Z","isPatch":true,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Thu, Nov 29, 2012 at 11:13 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> [...]These\n> two commands, \"reset\" and \"checkout\", share that the source we grab\n> the blobs out of only need to be a tree and does not have to be a\n> commit, and the only difference between them is where the blobs we\n> grabbed out of that tree go, either only to the index or to both the\n> index and the working tree.\n\nSlightly off topic, but another difference (or somehow another aspect\nof the same difference?) that has tripped me up a few times is that\n\"git checkout $rev .\" only affects added and modified files (in $rev\ncompared to HEAD), but \"git reset $rev .\" would also delete deleted\nfiles from the index. I suppose this is also a partial answer to your\nquestion in another message:\n\n> What does it even mean, even when you are on an existing commit, to\n> hard reset partially?\n>\n> Perhaps you looking for \"git checkout $tree -- $path\"?\n\nA more direct answer would be that I would expect \"git reset --hard\n$rev -- .\" to behave like \"git reset --hard $rev\", except that it\nwouldn't update HEAD. It seems to me that that would be similar to how\n\"git reset $rev -- .\" behaves like \"git reset $rev\", except that it\ndoesn't update HEAD. But reset and checkout with and without paths\nstill confuse me after years of using git, so I wouldn't be surprised\nif I'm not making any sense.\n"},{"id":"204342","messageId":"CANiSa6hpztn4gRX+-azuiD2+jrMUcCteqzeXkSdDudsEfz54Mg@mail.gmail.com","threadId":"32211","inReplyTo":"7vd2yyi4l1.fsf@alter.siamese.dyndns.org","subject":"Re: Operations on unborn branch","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-30T16:39:35Z","receivedAt":"2012-11-30T16:39:35Z","isPatch":false,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Tue, Nov 27, 2012 at 11:12 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> You have to special case the edges whichever way you go.  [...]\n\nIf I understand you correctly, you're saying that revision walking\nwould need a different special case. This is the most obvious\ndifference, it seems. \"git show\" would also need different\nspecial-casing. But rebase wouldn't need --root, diff-tree wouldn't\nneed --root, any operations on an unborn branch would just work (incl\nreset, cherry-pick).\n\nAgain, this is hypothetical, so I'll stop the complaining now :-)\n"},{"id":"204343","messageId":"CANiSa6i2f-4jXFUpYV6+fYnpG-tSRRA3cRg_v-v=UrgfwfFz_g@mail.gmail.com","threadId":"32211","inReplyTo":"CANiSa6hWYsfm0t+s_q7=CcD78yNfpuduxkRc35xW8qDOy97W3g@mail.gmail.com","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-11-30T16:45:51Z","receivedAt":"2012-11-30T16:45:51Z","isPatch":true,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Thu, Nov 29, 2012 at 2:00 PM, Martin von Zweigbergk\n<martinvonz@gmail.com> wrote:\n> Slightly off topic, but another difference (or somehow another aspect\n> of the same difference?) that has tripped me up a few times is that\n> \"git checkout $rev .\" only affects added and modified files (in $rev\n> compared to HEAD), but \"git reset $rev .\" would also delete deleted\n> files from the index.\n\nActually, what is the reasoning behind this difference? It almost\nseems like a bug. I think I have just thought it was too obvious to be\na bug before, but thinking more about it, I can't see any reason why\n\"git checkout $rev\" should delete files, but \"git checkout $rev .\"\nshouldn't. I hope I'm just hallucinating or missing something. Can\nsomeone shed some light on this?\n"},{"id":"204370","messageId":"7vd2yunn0e.fsf@alter.siamese.dyndns.org","threadId":"32211","inReplyTo":"CANiSa6i2f-4jXFUpYV6+fYnpG-tSRRA3cRg_v-v=UrgfwfFz_g@mail.gmail.com","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-12-01T09:24:17Z","receivedAt":"2012-12-01T09:24:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Martin von Zweigbergk <martinvonz@gmail.com> writes:\n\n> On Thu, Nov 29, 2012 at 2:00 PM, Martin von Zweigbergk\n> <martinvonz@gmail.com> wrote:\n>> Slightly off topic, but another difference (or somehow another aspect\n>> of the same difference?) that has tripped me up a few times is that\n>> \"git checkout $rev .\" only affects added and modified files...\n\n\"checkout $commit pathspec\" has always been about checking out the\ncontents stored in the paths that match the pathspec from the named\ncommit to the index and also o the working tree.  \"checkout\npathspec\" is similar, but the stuff comes out of the index.\n\nWhen pathspec is \"dir/\", it does not match the directory whose name\nis \"dir\".  The pathspec matches the paths that store blobs under\nthat directory.\n\nIn other words, \"checkout dir/\" (or \"checkout HEAD~4 dir/) has never\nbeen about \"please remove everything in dir/, and then check out\neverything in dir/ from the index (or from HEAD~4)\".  The \"please\nremove everything in dir/\" part is not the job of \"checkout\"; of\ncourse, you can do it as a separate step (e.g. \"rm -fr dir/\").\n"},{"id":"204507","messageId":"CANiSa6iMxzQGM8mZYdfR-drPGgydwVpM5JsQ-8oO09MX5XDH+g@mail.gmail.com","threadId":"32211","inReplyTo":"7vd2yunn0e.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-12-05T03:45:47Z","receivedAt":"2012-12-05T03:45:47Z","isPatch":true,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Sat, Dec 1, 2012 at 1:24 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Martin von Zweigbergk <martinvonz@gmail.com> writes:\n>\n>> On Thu, Nov 29, 2012 at 2:00 PM, Martin von Zweigbergk\n>> <martinvonz@gmail.com> wrote:\n>>> Slightly off topic, but another difference (or somehow another aspect\n>>> of the same difference?) that has tripped me up a few times is that\n>>> \"git checkout $rev .\" only affects added and modified files...\n>\n> \"checkout $commit pathspec\" has always been about ...\n\nI suppose the \"has always been\" is meant to say that it's hard to\nchange at this point, not that it's more intuitive the way it works..?\n\n> ...checking out the\n> contents stored in the paths that match the pathspec from the named\n> commit to the index and also o the working tree.\n\nI think I have always thought that \"git checkout $commit $pathspec\"\nwould replace the section(s) of the tree defined by $pathspec. (I'm\nusing \"tree\" in the more general sense here, as I'm understood the\nindex is not stored as a tree.)\n\n> When pathspec is \"dir/\", it does not match the directory whose name\n> is \"dir\".  The pathspec matches the paths that store blobs under\n> that directory.\n\nAh, right. Unlike \"git reset dir/\", IIUC.\n\nMore importantly, when is it desirable not to delete deleted entries?\nI find it much easier to imagine uses a \"git checkout $commit\n$pathspec\" that does delete deleted entries. It seems like this must\nhave been discussed in depth before, so feel free to point me to an\nold thread.\n\nIf it doesn't seem too strange to you and others if I make \"git reset\n--hard [$commit] $pathspec\" work just like had expected \"git checkout\n$commit $pathspec\", I might look into that when I get some time.\n\n> ...The \"please\n> remove everything in dir/\" part is not the job of \"checkout\"; of\n> course, you can do it as a separate step (e.g. \"rm -fr dir/\").\n\n\"rm -rf dir/\" would of course delete everything in there, including\ne.g. build artifacts....\n"},{"id":"204509","messageId":"7vtxs1kq4z.fsf@alter.siamese.dyndns.org","threadId":"32211","inReplyTo":"CANiSa6iMxzQGM8mZYdfR-drPGgydwVpM5JsQ-8oO09MX5XDH+g@mail.gmail.com","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-12-05T05:46:20Z","receivedAt":"2012-12-05T05:46:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Martin von Zweigbergk <martinvonz@gmail.com> writes:\n\n> More importantly, when is it desirable not to delete deleted entries?\n\nWhen I am trying to check out contents of Documentation/ directory\nas of an older edition because we made mistakes updating the files\nin recent versions, with \"git checkout v1.9.0 Documentation/\", for\nexample.  Perhaps somebody had this bright idea of reformatting our\ndocs with \"= Newer Style =\" section headers, replacing the underline\nstyle, and we found our toolchain depend on the underline style, or\nsomething.  The new files in the same directory added since v1.9.0\nmay share the same mistake as the files whose recent such changes I\nam nuking with this operation, but that does not mean I want to\nretype the contents of them from scratch; I'd rather keep them\naround so that I can fix them up by hand.\n\nI would have to say that it is more common; I do not recall a time I\nwanted to replace everything in a directory (and only there without\ntouching other parts of the tree) with an old version, removing new\nones.  \"git checkout [$commit] $paths\" is still an operation to help\nme build new history forward starting from HEAD, and is not about\nstart building on top of the old $commit.  Losing the work I've done\nto the files that did not exist in $commit:$paths is almost always\n*not* what I would expect to happen with the command.\n"},{"id":"204522","messageId":"CANiSa6iirgUjiLMo-wkaT7B_vdaK6s5gQ-CfTqPnDGj_GarWsw@mail.gmail.com","threadId":"32211","inReplyTo":"7vtxs1kq4z.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH 1/2] reset: learn to reset to tree","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2012-12-05T12:58:20Z","receivedAt":"2012-12-05T12:58:20Z","isPatch":true,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Tue, Dec 4, 2012 at 9:46 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Martin von Zweigbergk <martinvonz@gmail.com> writes:\n>\n>> More importantly, when is it desirable not to delete deleted entries?\n>\n> When I am trying to check out contents of Documentation/ directory\n> as of an older edition because we made mistakes updating the files\n> in recent versions, with \"git checkout v1.9.0 Documentation/\", for\n> example.  Perhaps somebody had this bright idea of reformatting our\n> docs with \"= Newer Style =\" section headers, replacing the underline\n> style, and we found our toolchain depend on the underline style, or\n> something.  The new files in the same directory added since v1.9.0\n> may share the same mistake as the files whose recent such changes I\n> am nuking with this operation, but that does not mean I want to\n> retype the contents of them from scratch; I'd rather keep them\n> around so that I can fix them up by hand.\n\nI think I follow, but why, then, would you not have the save problem\nwith hunks *within* files that have been added in the new version? Or\nis the only change to Documentation/ since the \"broken\" commit that a\nnew file has been added? That seems like a rather narrow use case in\nthat case? \"git checkout -p\" seems more generally useful (whether that\ncommand deleted deleted files or not). It feels like I'm missing\nsomething...\n\n> I would have to say that it is more common; I do not recall a time I\n> wanted to replace everything in a directory (and only there without\n> touching other parts of the tree) with an old version, removing new\n> ones.\n\nIt has happened a few times for me. I think this usually happens when\nI realize that I had a better solution for some subsystem (under some\npath) in some other branch (perhaps from a previous attempt at the\nsame problem) or an in older commit. Knowing that \"git checkout $rev\n$path\" doesn't do what I expect and that \"git reset --hard $rev $path\"\nis not allowed, I think I would usually do \"git reset $rev $path &&\ngit checkout $path\".\n"}]}