{"thread":{"id":"8688","subject":"[PATCH] Add git-save script","startedAt":"2007-06-23T13:02:15Z","lastAt":"2007-06-27T09:38:05Z","messageCount":12,"participants":["Nanako Shiraishi","Johannes Schindelin","Bill Lear","Junio C Hamano","しらいしななこ"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"45609","messageId":"20070623220215.6117@nanako3.bluebottle.com","threadId":"8688","inReplyTo":null,"subject":"[PATCH] Add git-save script","fromName":"Nanako Shiraishi","fromEmail":"nanako3@bluebottle.com","sentAt":"2007-06-23T13:02:15Z","receivedAt":"2007-06-23T13:02:15Z","isPatch":true,"sender":{"key":"nanako3@lavabit.com","avatar":"https://gravatar.com/avatar/3777b9e201c5883a62b1a6fdf7c53f2d712d1d80989146063ea861e33aad72a8?d=mp&s=160"},"body":"When my boss has something to show me and I have to update, for some reason I\nam always in the middle of doing something else, and git pull command refuses\nto work in such a case.\n\nI wrote this little script to save the changes I made, perform the update,\nand then come back to where I was, but on top of the updated commit.\n\nHere is how to use my script:\n\n    $ git save\n    $ git pull\n    $ git save restore\n\nSigned-off-by: Nanako Shiraishi <nanako3@bluebottle.com>\n\ndiff --git a/git-save.sh b/git-save.sh\nnew file mode 100755\nindex 0000000..b45652e\n--- /dev/null\n+++ b/git-save.sh\n@@ -0,0 +1,74 @@\n+#! /bin/sh\n+\n+. git-sh-setup\n+require_work_tree\n+\n+function save_work () {\n+\tsave=$( (\n+\t\ti_tree=$(git-write-tree)\n+\n+\t\tTMP=$GIT_DIR/.git-save-tmp\n+\t\tGIT_INDEX_FILE=$TMP\n+\t\texport GIT_INDEX_FILE\n+\n+\t\tgit-read-tree $i_tree\n+\t\tgit-add -u\n+\t\tw_tree=$(git-write-tree)\n+\t\trm $TMP\n+\t\tgit-read-tree --prefix=base/ HEAD^{tree}\n+\t\tgit-read-tree --prefix=indx/ $i_tree\n+\t\tgit-read-tree --prefix=work/ $w_tree\n+\t\tgit-write-tree\n+\t\trm $TMP\n+\t) )\n+\n+\thead=$(git-log --abbrev-commit --pretty=oneline -n 1 HEAD)\n+\tif branch=$(git symbolic-ref -q HEAD); then\n+\t\tbranch=${branch#refs/heads/}\n+\telse\n+\t\tbranch='(no branch)'\n+\tfi &&\n+\tmsg=$(printf 'WIP on %s \"%s\"' \"$branch\" \"$head\")\n+\n+\tsaved=$(printf '%s' \"$msg\" | git-commit-tree \"$save\")\n+\tgit update-ref -m \"$msg\" refs/heads/saved $saved\n+\tprintf 'Saved %s\\n' \"$msg\" >&2\n+}\n+\n+function list_save () {\n+\tgit-log --abbrev-commit --pretty=oneline -g \"$@\" saved\n+}\n+\n+function show_save () {\n+\tsave=$(git rev-parse --verify --default saved \"$1\")\n+\tgit diff-tree --stat $save:base $save:work\n+}\n+\n+function restore_save () {\n+\tsave=$(git rev-parse --verify --default saved \"$1\")\n+\th_tree=$(git rev-parse --verify $save:base)\n+\ti_tree=$(git rev-parse --verify $save:indx)\n+\tw_tree=$(git rev-parse --verify $save:work)\n+\n+\tgit-merge-recursive $h_tree -- HEAD^{tree} $w_tree\n+}\n+\n+if [ \"$1\" = list ]; then\n+\tshift\n+\tif [ \"$#\" = 0 ]; then\n+\t\tset x -n 10\n+\t\tshift\n+\tfi\n+\tlist_save \"$@\"\n+elif [ \"$1\" = show ]; then\n+\tshift\n+\tshow_save \"$@\"\n+elif [ \"$1\" = restore ]; then\n+\tshift\n+\trestore_save \"$@\"\n+else\n+\tsave_work\n+\tgit reset --hard\n+fi\n+\n+\n-- \nしらいし ななこ http://ivory.ap.teacup.com/nanako3/\n\n----------------------------------------------------------------------\nGet a free email address with REAL anti-spam protection.\nhttp://www.bluebottle.com\n"},{"id":"45611","messageId":"Pine.LNX.4.64.0706231605160.4059@racer.site","threadId":"8688","inReplyTo":"20070623220215.6117@nanako3.bluebottle.com","subject":"Re: [PATCH] Add git-save script","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-23T15:05:22Z","receivedAt":"2007-06-23T15:05:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 23 Jun 2007, Nanako Shiraishi wrote:\n\n> Here is how to use my script:\n> \n>     $ git save\n>     $ git pull\n>     $ git save restore\n\nThis use case has been discussed often, under the name \"git-stash\".\n\nCiao,\nDscho\n"},{"id":"45612","messageId":"18045.14200.611958.870171@lisa.zopyra.com","threadId":"8688","inReplyTo":"Pine.LNX.4.64.0706231605160.4059@racer.site","subject":"Re: [PATCH] Add git-save script","fromName":"Bill Lear","fromEmail":"rael@zopyra.com","sentAt":"2007-06-23T15:08:40Z","receivedAt":"2007-06-23T15:08:40Z","isPatch":true,"sender":{"key":"rael@zopyra.com","avatar":"https://gravatar.com/avatar/c4f2d2790ca3828d3b4e7dfebabf61d2fe94fd82fa49cdac2a5295dd2d46a874?d=mp&s=160"},"body":"On Saturday, June 23, 2007 at 16:05:22 (+0100) Johannes Schindelin writes:\n>Hi,\n>\n>On Sat, 23 Jun 2007, Nanako Shiraishi wrote:\n>\n>> Here is how to use my script:\n>> \n>>     $ git save\n>>     $ git pull\n>>     $ git save restore\n>\n>This use case has been discussed often, under the name \"git-stash\".\n\nWas git-stash added as a patch yet?  If not, do you think Nanako's\npatch does not address the concerns that were raised in the\ndiscussion you refer to?\n\n\nBill\n"},{"id":"45613","messageId":"Pine.LNX.4.64.0706231612460.4059@racer.site","threadId":"8688","inReplyTo":"18045.14200.611958.870171@lisa.zopyra.com","subject":"Re: [PATCH] Add git-save script","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-23T15:13:13Z","receivedAt":"2007-06-23T15:13:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 23 Jun 2007, Bill Lear wrote:\n\n> On Saturday, June 23, 2007 at 16:05:22 (+0100) Johannes Schindelin writes:\n> >Hi,\n> >\n> >On Sat, 23 Jun 2007, Nanako Shiraishi wrote:\n> >\n> >> Here is how to use my script:\n> >> \n> >>     $ git save\n> >>     $ git pull\n> >>     $ git save restore\n> >\n> >This use case has been discussed often, under the name \"git-stash\".\n> \n> Was git-stash added as a patch yet?  If not, do you think Nanako's\n> patch does not address the concerns that were raised in the\n> discussion you refer to?\n\nObviously, I prefer my approach of installing an alias.\n\nCiao,\nDscho\n"},{"id":"45621","messageId":"7vmyyq2zrz.fsf@assigned-by-dhcp.pobox.com","threadId":"8688","inReplyTo":"20070623220215.6117@nanako3.bluebottle.com","subject":"Re: [PATCH] Add git-save script","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-06-23T20:15:28Z","receivedAt":"2007-06-23T20:15:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nanako Shiraishi <nanako3@bluebottle.com> writes:\n\n> I wrote this little script to save the changes I made, perform the update,\n> and then come back to where I was, but on top of the updated commit.\n>\n> Here is how to use my script:\n>\n>     $ git save\n>     $ git pull\n>     $ git save restore\n\nHeh, I earlier said that I'd start looking at the \"stash\", but\nhaven't done anything about it so far (I was busy with the day\njob).  More honestly, I was being lazy, and sometimes lazy are\nrewarded, with a contribution from a new git hacker.\n\nI think this is more or less in the direction I was planning to\nhead to; thanks for the patch.\n\nHaving said that, I have a few comments.  Style, robustness,\nand portability.\n\n> diff --git a/git-save.sh b/git-save.sh\n> new file mode 100755\n> index 0000000..b45652e\n> --- /dev/null\n> +++ b/git-save.sh\n> @@ -0,0 +1,74 @@\n> +#! /bin/sh\n\nStyle.  \"#!/bin/sh\" (no space after she-bang).\n\n> +\n> +. git-sh-setup\n> +require_work_tree\n> +\n> +function save_work () {\n\nNo noiseword \"function\" for portability.\n\nWhen you say \"#!/bin/sh\", you are writing for the family of\ngeneric Bourne shells, not specifically for korn nor bash.  For\nexample, dash is a fine POSIX shell, but does not grok function\nnoiseword.  When in doubt, please stay away from things not in\nPOSIX (e.g. function, [[, ]], ${parameter//pattern/string/}).\n\n> +\tsave=$( (\n> +\t\ti_tree=$(git-write-tree)\n> +\n> +\t\tTMP=$GIT_DIR/.git-save-tmp\n> +\t\tGIT_INDEX_FILE=$TMP\n> +\t\texport GIT_INDEX_FILE\n> +\n> +\t\tgit-read-tree $i_tree\n> +\t\tgit-add -u\n> +\t\tw_tree=$(git-write-tree)\n> +\t\trm $TMP\n> +\t\tgit-read-tree --prefix=base/ HEAD^{tree}\n> +\t\tgit-read-tree --prefix=indx/ $i_tree\n> +\t\tgit-read-tree --prefix=work/ $w_tree\n> +\t\tgit-write-tree\n> +\t\trm $TMP\n> +\t) )\n\nHmph.\n\nI see many problems here, although the basic idea is sound and\n\"interesting\".  I especially like the use of three trees in a\nsingle commit, without any parenthood.\n\n - $GIT_DIR could contain shell metachararcters / whitespace, so\n   could $TMP as well.  Always quote such variables, or you risk\n   a surprise from \"rm\".\n\n - You probably would not want to create a new \"save\" if your\n   working tree and the index are clean.  To test for the\n   condition, you can do something like:\n\n\tgit-diff --quiet --cached && git-diff --quiet\n\n - I generally prefer cleaning temporary files with \"rm -f\", and\n   also set a trap upon exit, like this:\n\n\t# near the beginning of the script\n\tTMP=\"$GIT_DIR/.git-save-tmp-$$\"\n\ttrap 'rm -f \"$TMP\"' 0 \n\t... and then much later ...\n\n\tsave=$( (\n\t\tGIT_INDEX_FILE=$TMP\n\t\texport GIT_INDEX_FILE\n                ...\n\t) )\n\n - I wonder what happens if any of the commands in the above\n   sequence errors out.  For example, I think you will get a\n   failure from the first git-write-tree if you are in the\n   middle of a merge.  You would want to error out as soon as\n   you see a problem.\n\n - Although you keep a separate tree for the index (before the\n   \"git add -u\" to grab the working tree changes) in the saved\n   data, it does not seem to be used.  It _might_ make sense to\n   replace \"git add -u\" with \"git add .\" so that work/ tree\n   contains even untracked (but not ignored) files, and on the\n   restore side unstage the paths that appear in work/ but not\n   in indx/.  I dunno.\n\n> +\n> +\thead=$(git-log --abbrev-commit --pretty=oneline -n 1 HEAD)\n> +\tif branch=$(git symbolic-ref -q HEAD); then\n> +\t\tbranch=${branch#refs/heads/}\n> +\telse\n> +\t\tbranch='(no branch)'\n> +\tfi &&\n\nMinor style.  Please don't write \"; then\\n\".  Line-up \"then\",\n\"elif\", \"else\", and \"fi\"; it is much easier to read that way.\n\n> +\tmsg=$(printf 'WIP on %s \"%s\"' \"$branch\" \"$head\")\n> +\n> +\tsaved=$(printf '%s' \"$msg\" | git-commit-tree \"$save\")\n> +\tgit update-ref -m \"$msg\" refs/heads/saved $saved\n> +\tprintf 'Saved %s\\n' \"$msg\" >&2\n> +}\n> +\n> +function list_save () {\n> +\tgit-log --abbrev-commit --pretty=oneline -g \"$@\" saved\n> +}\n\nI really like the way reflog is used for this.  Your saves do\nnot necessarily tied to a single branch (so there are no\nparent-child relation between each save entry), but you can\nstill identify what each of the save is about because your log\nmessage has branch name in it.\n\nThe sha1 at the beginning of oneline output, although they are\nabbreviated, seem not very useful in this context, by the way.\nI presume your intended use case is to view \"git save list\"\noutput, and say \"git save restore saved@{2}\" to apply the one\nthird from the top.  For that use, the commit object name is\nirrelevant.  Maybe sed it out like this to make it even\nprettier?\n\n\tgit-log --pretty=oneline -g \"$@\" saved\n        sed -e 's/^[0-9a-f]* //'\n\n> +function show_save () {\n> +\tsave=$(git rev-parse --verify --default saved \"$1\")\n> +\tgit diff-tree --stat $save:base $save:work\n> +}\n\nNice, but after trying this myself a bit, I seriously wished for\n\"git save show -p\", so I did it myself, like this:\n\n\tshow_save () {\n\t\tflags=$(git rev-parse --no-revs --flags \"$@\")\n\t\tif test -z \"$flags\"\n\t\tthen\n\t\t\tflags=--stat\n\t\tfi\n\t\tsave=$(git rev-parse --revs-only --no-flags --default saved \"$@\")\n\t\tgit diff-tree $flags $save:base $save:work\n\t}\n\nIt's a dense (and ancient style) plumbing code so needs a bit of\nexplanation:\n\n - The first git-rev-parse looks at \"$@\", discards everything\n   that are not options and discards object names.  So 'git save\n   show -p some' will give you \"-p\" in flags.\n\n - The second one discards flags, and grabs 'some' out of '-p\n   some', or defaults to \"saved\".\n\n> +function restore_save () {\n> +\tsave=$(git rev-parse --verify --default saved \"$1\")\n> +\th_tree=$(git rev-parse --verify $save:base)\n> +\ti_tree=$(git rev-parse --verify $save:indx)\n> +\tw_tree=$(git rev-parse --verify $save:work)\n> +\n> +\tgit-merge-recursive $h_tree -- HEAD^{tree} $w_tree\n> +}\n\nThe same \"robustness\" comments for the save_work function apply\nhere.  You probably do not want to restore on a dirty tree; the\nintended use case is \"stash away, pull, then restore\", so I\nthink it is Ok to assume that you will only be restoring on a\nclean state (and it would make the implementation simpler).\n\nThe three-way merge is done correctly here, and I would imagine\nthe users would feel the UI quite natural _if_ this merge\nconflicts.  \"git diff\" would show only the conflicted paths\n(because the updates coming from the old working tree is placed\nin the index for cleanly merged paths), editing a conflicted\nfile and \"git add $that_path\" would resolve.  That's exactly the\nsame workflow for a conflicted merge.\n\nHowever, I think it is a bit counterintuitive to update the\nworking tree change to the index if the merge did not conflict.\nIt might be better to run an equivalent of \"git reset\", so that\nafter \"git save restore\", the user can say \"git diff\" (not \"git\ndiff HEAD\") to view the local changes.  Perhaps...\n\n\tif git-merge-recursive ...\n\tthen\n        \t# Cleanly merged..\n\t\tadded=\"$TMP\" &&\n                git-diff --cached --name-only --diff-filter=A >\"$added\" &&\n\t\tgit-read-tree HEAD &&\n                git-update-index --add --stdin <\"$added\" ||\n\t\t\tdie \"Cannot unstage local modification\"\n\t\tgit-update-index --refresh\n\tfi\n\nThe --diff-filter dance is so that we do not \"forget\" newly\nadded files when we do \"read-tree HEAD\".\n\n> +\n> +if [ \"$1\" = list ]; then\n> +\tshift\n> +\tif [ \"$#\" = 0 ]; then\n> +\t\tset x -n 10\n> +\t\tshift\n> +\tfi\n> +\tlist_save \"$@\"\n> +elif [ \"$1\" = show ]; then\n> +\tshift\n> +\tshow_save \"$@\"\n> +elif [ \"$1\" = restore ]; then\n> +\tshift\n> +\trestore_save \"$@\"\n> +else\n> +\tsave_work\n> +\tgit reset --hard\n\nI am not absolutely sure if \"git reset --hard\" belongs here.\nYou can certainly type one less command in your example sequence\n(\"stash; pull; restore\").  But I suspect there may be a case\nthat would be more useful if \"git save\" did not do the reset\nitself.  I dunno....\n\n> +fi\n> +\n> +\n\nStyle.  This is a perfect place to use \"case/esac\".\n\n\tcase \"$1\" in\n\tlist)\n        \tdo this thing\n                ;;\n\t...\n\t*)\n        \tdo default thing\n                ;;\n        esac\n\nMuch easier to read, isn't it?\n"},{"id":"45662","messageId":"200706241044.l5OAiMCB012492@mi0.bluebottle.com","threadId":"8688","inReplyTo":"7vmyyq2zrz.fsf@assigned-by-dhcp.pobox.com","subject":"Re: [PATCH] Add git-save script","fromName":"Nanako Shiraishi","fromEmail":"nanako3@bluebottle.com","sentAt":"2007-06-24T10:43:15Z","receivedAt":"2007-06-24T10:43:15Z","isPatch":true,"sender":{"key":"nanako3@lavabit.com","avatar":"https://gravatar.com/avatar/3777b9e201c5883a62b1a6fdf7c53f2d712d1d80989146063ea861e33aad72a8?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Nanako Shiraishi <nanako3@bluebottle.com> writes:\n>\n>> +function save_work () {\n>\n> No noiseword \"function\" for portability.\n>\n> When you say \"#!/bin/sh\", you are writing for the family of\n> generic Bourne shells, not specifically for korn nor bash.  For\n> example, dash is a fine POSIX shell, but does not grok function\n> noiseword.  When in doubt, please stay away from things not in\n> POSIX (e.g. function, [[, ]], ${parameter//pattern/string/}).\n\nIs there a good reference you can point me?\n\n>  - $GIT_DIR could contain shell metachararcters / whitespace, so\n>    could $TMP as well.  Always quote such variables, or you risk\n>    a surprise from \"rm\".\n>\n>  - You probably would not want to create a new \"save\" if your\n>    working tree and the index are clean.  To test for the\n>    condition, you can do something like:\n>\n> \tgit-diff --quiet --cached && git-diff --quiet\n\nI did not think of these problems, but I understand now.\n\n>  - Although you keep a separate tree for the index (before the\n>    \"git add -u\" to grab the working tree changes) in the saved\n>    data, it does not seem to be used.  It _might_ make sense to\n>    replace \"git add -u\" with \"git add .\" so that work/ tree\n>    contains even untracked (but not ignored) files, and on the\n>    restore side unstage the paths that appear in work/ but not\n>    in indx/.  I dunno.\n\nAt first I wanted to do git-add . instead of git-add -u, but then I\nbecame worried that will add files that are not interesting such as\ntemporary files.\n\n>> +\n>> +\thead=$(git-log --abbrev-commit --pretty=oneline -n 1 HEAD)\n>> +\tif branch=$(git symbolic-ref -q HEAD); then\n>> +\t\tbranch=${branch#refs/heads/}\n>> +\telse\n>> +\t\tbranch='(no branch)'\n>> +\tfi &&\n>\n> Minor style.  Please don't write \"; then\\n\".  Line-up \"then\",\n> \"elif\", \"else\", and \"fi\"; it is much easier to read that way.\n\nI see.\n\n> Nice, but after trying this myself a bit, I seriously wished for\n> \"git save show -p\", so I did it myself, like this:\n>\n> \tshow_save () {\n> \t\tflags=$(git rev-parse --no-revs --flags \"$@\")\n> \t\tif test -z \"$flags\"\n> \t\tthen\n> \t\t\tflags=--stat\n> \t\tfi\n> \t\tsave=$(git rev-parse --revs-only --no-flags --default saved \"$@\")\n> \t\tgit diff-tree $flags $save:base $save:work\n> \t}\n>\n> It's a dense (and ancient style) plumbing code so needs a bit of\n> explanation:\n>\n>  - The first git-rev-parse looks at \"$@\", discards everything\n>    that are not options and discards object names.  So 'git save\n>    show -p some' will give you \"-p\" in flags.\n>\n>  - The second one discards flags, and grabs 'some' out of '-p\n>    some', or defaults to \"saved\".\n\nI did not know about these commands.  I will study the manual pages\nand I will re-submit my patch after adding these.\n\n-- \nしらいし ななこ http://ivory.ap.teacup.com/nanako3/\n\n----------------------------------------------------------------------\nGet a free email account with anti spam protection.\nhttp://www.bluebottle.com\n"},{"id":"45687","messageId":"7vbqf5t9vm.fsf@assigned-by-dhcp.cox.net","threadId":"8688","inReplyTo":"200706241044.l5OAiMCB012492@mi0.bluebottle.com","subject":"Re: [PATCH] Add git-save script","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-06-24T19:45:01Z","receivedAt":"2007-06-24T19:45:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nanako Shiraishi <nanako3@bluebottle.com> writes:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> ...\n>> When you say \"#!/bin/sh\", you are writing for the family of\n>> generic Bourne shells, not specifically for korn nor bash.  For\n>> example, dash is a fine POSIX shell, but does not grok function\n>> noiseword.  When in doubt, please stay away from things not in\n>> POSIX (e.g. function, [[, ]], ${parameter//pattern/string/}).\n>\n> Is there a good reference you can point me?\n\nI usually look at this:\n\n    http://www.opengroup.org/onlinepubs/000095399/\n\nand click on \"Shell and Utilities volume (XCU)\", and compare it\nwith \"man bash\".\n\n> At first I wanted to do git-add . instead of git-add -u, but then I\n> became worried that will add files that are not interesting such as\n> temporary files.\n\nAs long as .gitignore is set up properly to ignore such\ngenerated files, that shouldn't be a problem.\n"},{"id":"45735","messageId":"200706250632.l5P6Wu6A028140@mi0.bluebottle.com","threadId":"8688","inReplyTo":"Pine.LNX.4.64.0706231605160.4059@racer.site","subject":"Re: [PATCH] Add git-save script","fromName":"しらいしななこ","fromEmail":"nanako3@bluebottle.com","sentAt":"2007-06-25T06:32:53Z","receivedAt":"2007-06-25T06:32:53Z","isPatch":true,"sender":{"key":"nanako3@lavabit.com","avatar":"https://gravatar.com/avatar/3777b9e201c5883a62b1a6fdf7c53f2d712d1d80989146063ea861e33aad72a8?d=mp&s=160"},"body":"Quoting Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n\n> Hi,\n> \n> On Sat, 23 Jun 2007, Nanako Shiraishi wrote:\n> \n> > Here is how to use my script:\n> > \n> >     $ git save\n> >     $ git pull\n> >     $ git save restore\n> \n> This use case has been discussed often, under the name \"git-stash\".\n> \n> Ciao,\n> Dscho\n\nThank you for your comments.  Do you suggest I rename the script to git-stash and re-submit after fixing according to Junio's comments?\n\n-- \nNanako Shiraishi\nhttp://ivory.ap.teacup.com/nanako3/\n\n----------------------------------------------------------------------\nFinally - A spam blocker that actually works.\nhttp://www.bluebottle.com\n"},{"id":"45745","messageId":"Pine.LNX.4.64.0706250844170.4059@racer.site","threadId":"8688","inReplyTo":"200706250632.l5P6Wu6B028140@mi0.bluebottle.com","subject":"Re: [PATCH] Add git-save script","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-25T07:45:17Z","receivedAt":"2007-06-25T07:45:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 25 Jun 2007, ã~A~Wã~B~Iã~A~Dã~A~Wã~Aªã~Aªã~A~S wrote:\n\n> Quoting Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n> \n> > On Sat, 23 Jun 2007, Nanako Shiraishi wrote:\n> > \n> > > Here is how to use my script:\n> > > \n> > >     $ git save\n> > >     $ git pull\n> > >     $ git save restore\n> > \n> > This use case has been discussed often, under the name \"git-stash\".\n> > \n> > Ciao,\n> > Dscho\n> \n> Thank you for your comments.  Do you suggest I rename the script to \n> git-stash and re-submit after fixing according to Junio's comments?\n\nYes.\n\nCiao,\nDscho\n"},{"id":"45750","messageId":"7vir9c1nek.fsf@assigned-by-dhcp.pobox.com","threadId":"8688","inReplyTo":"200706250632.l5P6Wu6A028140@mi0.bluebottle.com","subject":"Re: [PATCH] Add git-save script","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-06-25T07:52:35Z","receivedAt":"2007-06-25T07:52:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"しらいしななこ  <nanako3@bluebottle.com> writes:\n\n> Quoting Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n>\n>> On Sat, 23 Jun 2007, Nanako Shiraishi wrote:\n>> \n>> > Here is how to use my script:\n>> > \n>> >     $ git save\n>> >     $ git pull\n>> >     $ git save restore\n>> \n>> This use case has been discussed often, under the name \"git-stash\".\n>\n> Thank you for your comments.  Do you suggest I rename the\n> script to git-stash and re-submit after fixing according to\n> Junio's comments?\n\n        Side note: get into the habit of trimming the quote to\n        remove \"Hi,\" and signatures, please.\n\nBecause many people on the list (not just me, as I was not\ninitially involved in the \"wish: pulling into dirty tree\"\ndiscussion myself) already used the word \"stash\" for the concept\nof the operation, I think it would give continuity to our\ndiscussion if you used that word.\n\nI have to also say that the \"restore\" subcommand had a certain\n\"Huh?\" factor when I first saw the above \"git save restore\"\ncommand line.  I would have said \"git stash\" to save away, and\n\"git stash apply\" to propagate the changes to updated HEAD.\n\nThat would make the set of subcommands to:\n\n\t$ git stash\t\t-- to save away\n\t$ git stash list\t-- to get the list\n        $ git stash show [$it]\t-- to view a single stash\n        $ git stash apply [$it]\t-- to apply changes from a stash\n\nRight now you seem to use reflog so that \"list\" gives saved@{$n}\nand the user is expected to pick from it and say something like\n\"git stash show saved@{2}\", but I suspect that there is some\nroom for UI improvements.\n\n * Is comandeering a branch name \"saved\" for the purpose of the\n   stash command a right thing to do?\n\n   Perhaps we would want to use refs/stash/ hierarchy;\n\n * Is it more convenient to have a single stash that holds\n   changes you make anywhere, or is it better to have one stash\n   per branch?\n\n   Unlike StGIT and guilt, that are systems to manage patches\n   for longer term, I think the concept of stash is more geared\n   towards quickly stashing away local changes while you have to\n   'get distracted\", as you described in your commit log\n   message.  So in that sense, I think anything elaborate like\n   one stash per branch is a mistake, and a single stash that is\n   quickly aged and pruned automatically when reflog expires, as\n   you implemented, is the right approach.  Taken together with\n   the previous point, I would actually suggest refs/stash as\n   the refname.\n\n * If we settle on the design of having a single stash per\n   repository, the name of whatever the ref we use to implement\n   the stash should not have to be spelled out by the end user\n   (e.g. saved@{2} should not be necessary---the user should be\n   able to say \"the stash marked with letter '2' in 'git stash\n   list' output).\n\nSo perhaps as a UI improvement, \"git stash list\" should show\nthem just numbered (strip away saved@{$N} part down to just\n\"$N:\" or something), and \"git stash show\" and \"git stash apply\"\nshould take that number.\n\nI think \"git stash\" is usable standalone, but in the particular\nuse case you mentioned, I was planning to follow Linus's\nsuggestion of stashing and unstashing automatically inside \"git\npull\" (most likely in \"git merge\", which is the underlying\ncommand to do the actual merging part), when the pull results in\na fast-forward situation.  But that would be a separate change\nthat uses \"git stash\" command.\n"},{"id":"45866","messageId":"7vsl8euryx.fsf@assigned-by-dhcp.pobox.com","threadId":"8688","inReplyTo":"7vmyyq2zrz.fsf@assigned-by-dhcp.pobox.com","subject":"Re: [PATCH] Add git-save script","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-06-27T01:05:42Z","receivedAt":"2007-06-27T01:05:42Z","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> Nanako Shiraishi <nanako3@bluebottle.com> writes:\n> ...\n>> +\tsave=$( (\n>> +\t\ti_tree=$(git-write-tree)\n>> +\n>> +\t\tTMP=$GIT_DIR/.git-save-tmp\n>> +\t\tGIT_INDEX_FILE=$TMP\n>> +\t\texport GIT_INDEX_FILE\n>> +\n>> +\t\tgit-read-tree $i_tree\n>> +\t\tgit-add -u\n>> +\t\tw_tree=$(git-write-tree)\n>> +\t\trm $TMP\n>> +\t\tgit-read-tree --prefix=base/ HEAD^{tree}\n>> +\t\tgit-read-tree --prefix=indx/ $i_tree\n>> +\t\tgit-read-tree --prefix=work/ $w_tree\n>> +\t\tgit-write-tree\n>> +\t\trm $TMP\n>> +\t) )\n> ...\n>  - Although you keep a separate tree for the index (before the\n>    \"git add -u\" to grab the working tree changes) in the saved\n>    data, it does not seem to be used.  It _might_ make sense to\n>    replace \"git add -u\" with \"git add .\" so that work/ tree\n>    contains even untracked (but not ignored) files, and on the\n>    restore side unstage the paths that appear in work/ but not\n>    in indx/.  I dunno.\n\nI would like to take the last part of the sentence back.\n\nI do not think \"git add .\" makes much sense.  The only case that\n\"add .\" vs \"add -u\" may make a difference is the files that are\nso new to your working tree that you haven't even added them to\nthe index.  But they would not be in your current HEAD, and they\nwould not have been added in the commit you are pulling, and if\nthat is not the case, git-merge would complain and bail out as\nusual anyway.  I replied to your other message with something\nabout \".gitignore\", but not everybody uses \"ignore\" mechanism\neffectively, and \"git add .\" indeed would end up sucking all the\nirrelevent cruft to w_tree for them, only to be extracted back\n(because HEAD, i_tree and whichever commit you will later be\napplying the stash to are not likely to have them).  Maybe we\ncould argue that those people with nonoptimal \"ignore\" list\ndeserve it, but I do not think it is worth the potential added\naggravation.\n\nBy the way, I initially liked the clever use of a single commit\nthat records the three trees in it, but now I am beginning to\nreally hate it.  I am suspecting that the primary reason you did\nit that way was to reduce the number of commit objects used for\nthis purpose.  I personally do not think it is something we\nwould want to optimize for.\n\nI think it would make much more sense to represent a stash like\nthis:\n\n              .------o commit to represent index state\n             /        \\\n     ---o---o----------o\n            HEAD       commit to represent worktree state\n\nThat is, \"index\" and \"worktree\" state are represented as one\ncommit each, both are direct child of the HEAD, with an added\ntwist of the latter being also a child of the former.\n\nThis has an interesting side effect that I can do this:\n\n\t$ git show stash\n\nto view what was in HEAD, index and working tree at the same\ntime (you can use \"git stash show\" to view the changes between\nHEAD and worktree as before).\n\nBeing able to view this \"evil merge\" (this is a deliberately\nevil merge, as you typically have huge differences between the\nindex and worktree when you need to stash) may not be that\ninteresting in the real life, but I think this is a good\ndemonstration of the reason why it is better to use more\nstraightforward ancestry structure to represent the stash.  By\nnot using a custom tree structure that is only understood by\n\"git stash\", the user can use existing tools that operate on\nnormal ancestry chains.\n\n>> ...\n>> +\tsave_work\n>> +\tgit reset --hard\n>\n> I am not absolutely sure if \"git reset --hard\" belongs here.\n> You can certainly type one less command in your example sequence\n> (\"stash; pull; restore\").  But I suspect there may be a case\n> that would be more useful if \"git save\" did not do the reset\n> itself.  I dunno....\n\nI now think \"git reset --hard\" here is fine.\n"},{"id":"45900","messageId":"200706270938.l5R9c583019275@mi0.bluebottle.com","threadId":"8688","inReplyTo":"7vsl8euryx.fsf@assigned-by-dhcp.pobox.com","subject":"Re: [PATCH] Add git-save script","fromName":"しらいしななこ","fromEmail":"nanako3@bluebottle.com","sentAt":"2007-06-27T09:38:05Z","receivedAt":"2007-06-27T09:38:05Z","isPatch":true,"sender":{"key":"nanako3@lavabit.com","avatar":"https://gravatar.com/avatar/3777b9e201c5883a62b1a6fdf7c53f2d712d1d80989146063ea861e33aad72a8?d=mp&s=160"},"body":"Quoting Junio C Hamano <gitster@pobox.com>:\n\n> I think it would make much more sense to represent a stash like\n> this:\n> \n>               .------o commit to represent index state\n>              /        \\\n>      ---o---o----------o\n>             HEAD       commit to represent worktree state\n> \n> That is, \"index\" and \"worktree\" state are represented as one\n> commit each, both are direct child of the HEAD, with an added\n> twist of the latter being also a child of the former.\n\nI do not know if I understand you correctly.\nDo you mean that I should create a stash this way?\n\n i_tree=$(git-write-tree)\n i_commit=$(echo index | git-commit-tree $i_tree -p HEAD)\n w_tree=$( what I did to create w_tree in my previous patch )\n w_commit=$(echo $msg | git-commit-tree $w_tree -p HEAD -p $i_commit)\n\nand when unstashing the stash, I should:\n\n git-merge-recursive $stash^^{tree} -- $stash^^{tree} $stash^{tree}\n\nI think I can make it work, but if that is not what you meant, please let me know.\n\n> I am not absolutely sure if \"git reset --hard\" belongs here.\n> > You can certainly type one less command in your example sequence\n> > (\"stash; pull; restore\").  But I suspect there may be a case\n> > that would be more useful if \"git save\" did not do the reset\n> > itself.  I dunno....\n> \n> I now think \"git reset --hard\" here is fine.\n\nI see.\n\nI will try to update and resend my patch this weekend.\n\n-- \nNanako Shiraishi\nhttp://ivory.ap.teacup.com/nanako3/\n\n----------------------------------------------------------------------\nGet a free email address with REAL anti-spam protection.\nhttp://www.bluebottle.com\n"}]}