{"thread":{"id":"1214","subject":"[RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","startedAt":"2005-07-12T19:05:52Z","lastAt":"2005-07-13T21:54:35Z","messageCount":15,"participants":["Chris Wedgwood","Junio C Hamano","Matthias Urlichs","Linus Torvalds"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"6054","messageId":"20050712190552.GA7178@taniwha.stupidest.org","threadId":"1214","inReplyTo":null,"subject":"[RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2005-07-12T19:05:52Z","receivedAt":"2005-07-12T19:05:52Z","isPatch":true,"sender":{"key":"cw@f00f.org","avatar":null},"body":"Sometimes (often actually) I do:\n\n          cp -Rl tree1 tree2    # new tree with implied CoW semantics\n          cd tree2\n          cg-update             # or similar\n\nthe latter well frob .git/HEAD or similar by doing echo foo > bar\nwhich obviously breaks the intended CoW semantics.\n\nHow would people feel about something like the patch below be?  (RFC\npurposes only, please eyeball this before blidnly applying anything.\nI may have missed spots or goofed, you never know).\n\n\n\ndiff --git a/cg-Xlib b/cg-Xlib\n--- a/cg-Xlib\n+++ b/cg-Xlib\n@@ -21,6 +21,12 @@ usage() {\n \tdie \"usage: $USAGE\"\n }\n \n+echo_to_file() {\n+    TMPFILE=`mktemp \"$2.tmp-XXXXXXX\"`\n+    echo \"$1\" > \"$TMPFILE\"\n+    mv \"$TMPFILE\" \"$2\"\n+}\n+\n pager () {\n \tlocal line\n \t# Invoke pager only if there's any actual output\n@@ -101,7 +107,7 @@ tree_timewarp () {\n \tfi\n \n \tgit-read-tree -m \"$branch\" || die \"$branch: bad commit\"\n-\t[ \"$no_head_update\" ] || echo \"$branch\" > $_git/HEAD\n+\t[ \"$no_head_update\" ] || echo_to_file \"$branch\" $_git/HEAD\n \n \t# Kill gone files\n \tgit-diff-tree -z -r $base $branch | xargs -0 bash -c '\ndiff --git a/cg-branch-add b/cg-branch-add\n--- a/cg-branch-add\n+++ b/cg-branch-add\n@@ -50,4 +50,4 @@ mkdir -p $_git/branches\n [ -s \"$_git/branches/$name\" ] && die \"branch already exists\"\n [ -s \"$_git/refs/heads/$name\" ] && echo \"warning: I already have head of this branch\" >&2\n \n-echo \"$location\" >$_git/branches/$name\n+echo_to_file \"$location\" $_git/branches/$name\ndiff --git a/cg-branch-chg b/cg-branch-chg\n--- a/cg-branch-chg\n+++ b/cg-branch-chg\n@@ -14,4 +14,4 @@ location=$2\n ([ \"$name\" ] && [ \"$location\" ]) || usage\n \n [ -s \"$_git/branches/$name\" ] || die \"branch does not exist\"\n-echo \"$location\" >$_git/branches/$name\n+echo_to_file \"$location\" $_git/branches/$name\ndiff --git a/cg-commit b/cg-commit\n--- a/cg-commit\n+++ b/cg-commit\n@@ -331,7 +331,7 @@ fi\n \n if [ \"$newhead\" ]; then\n \techo \"Committed as $newhead.\"\n-\techo $newhead >$_git/HEAD\n+\techo_to_file $newhead $_git/HEAD\n \t[ \"$merging\" ] && rm $_git/merging $_git/merging-sym $_git/merge-base\n \n \t# Trigger the postcommit hook\ndiff --git a/cg-init b/cg-init\n--- a/cg-init\n+++ b/cg-init\n@@ -44,7 +44,7 @@ mkdir $_git/branches\n touch $_git/refs/heads/master\n \n if [ \"$uri\" ]; then\n-\techo \"$uri\" >$_git/branches/origin\n+\techo_to_file \"$uri\" $_git/branches/origin\n \tcg-pull origin || die \"pull failed\"\n \n \tcp $_git/refs/heads/origin $_git/refs/heads/master\ndiff --git a/cg-seek b/cg-seek\n--- a/cg-seek\n+++ b/cg-seek\n@@ -44,8 +44,8 @@ fi\n \n if [ \"$seek_mode\" = \"away\" ]; then\n \trm $_git/HEAD\n-\techo \"$dstcommit\" >$_git/HEAD\n-\t[ -s $_git/blocked ] || echo \"seeked from $_git_head\" >$_git/blocked\n+\techo_to_file \"$dstcommit\" $_git/HEAD\n+\t[ -s $_git/blocked ] || echo_to_file \"seeked from $_git_head\" $_git/blocked\n else\n \trm $_git/HEAD\n \tln -s \"refs/heads/$_git_head\" $_git/HEAD\ndiff --git a/cg-tag b/cg-tag\n--- a/cg-tag\n+++ b/cg-tag\n@@ -79,5 +79,5 @@ SIGEND\n \n \trm -rf \"$tagdir\"\n else\n-\techo \"$id\" >$_git/refs/tags/$name\n+\techo_to_file \"$id\" $_git/refs/tags/$name\n fi\ndiff --git a/git-branch-script b/git-branch-script\n--- a/git-branch-script\n+++ b/git-branch-script\n@@ -8,4 +8,4 @@ rev=$(git-rev-parse --verify --default H\n [ -z \"$branchname\" ] && die \"git branch: I want a branch name\"\n [ -e \"$GIT_DIR/refs/heads/$branchname\" ] && die \"$branchname already exists\"\n \n-echo $rev > \"$GIT_DIR/refs/heads/$branchname\"\n+echo_to_file $rev \"$GIT_DIR/refs/heads/$branchname\"\ndiff --git a/git-checkout-script b/git-checkout-script\n--- a/git-checkout-script\n+++ b/git-checkout-script\n@@ -67,7 +67,7 @@ fi\n #\n if [ \"$?\" -eq 0 ]; then\n \tif [ \"$newbranch\" ]; then\n-\t\techo $new > \"$GIT_DIR/refs/heads/$newbranch\"\n+\t\techo_to_file $new \"$GIT_DIR/refs/heads/$newbranch\"\n \t\tbranch=\"$newbranch\"\n \tfi\n \t[ \"$branch\" ] && ln -sf \"refs/heads/$branch\" \"$GIT_DIR/HEAD\"\ndiff --git a/git-commit-script b/git-commit-script\n--- a/git-commit-script\n+++ b/git-commit-script\n@@ -97,7 +97,7 @@ grep -v '^#' < .editmsg | git-stripspace\n [ -s .cmitmsg ] && \n \ttree=$(git-write-tree) &&\n \tcommit=$(cat .cmitmsg | git-commit-tree $tree $PARENTS) &&\n-\techo $commit > \"$GIT_DIR/HEAD\" &&\n+\techo_to_file $commit \"$GIT_DIR/HEAD\" &&\n \trm -f -- \"$GIT_DIR/MERGE_HEAD\"\n ret=\"$?\"\n rm -f .cmitmsg .editmsg\ndiff --git a/git-fetch-script b/git-fetch-script\n--- a/git-fetch-script\n+++ b/git-fetch-script\n@@ -30,4 +30,4 @@ rsync://*)\n \t;;\n esac || exit 1\n git-rev-parse --verify \"$head\" > /dev/null || exit 1\n-echo \"$head\" > \"$GIT_DIR/$destination\"\n+echo_to_file \"$head\" \"$GIT_DIR/$destination\"\ndiff --git a/git-rebase-script b/git-rebase-script\n--- a/git-rebase-script\n+++ b/git-rebase-script\n@@ -21,7 +21,7 @@ case \"$#\" in\n esac\n \n git-read-tree -m -u $junio $linus &&\n-echo \"$linus\" >\"$GIT_DIR/HEAD\" || exit\n+echo_to_file \"$linus\" \"$GIT_DIR/HEAD\" || exit\n \n tmp=.rebase-tmp$$\n fail=$tmp-fail\ndiff --git a/git-resolve-script b/git-resolve-script\n--- a/git-resolve-script\n+++ b/git-resolve-script\n@@ -24,8 +24,8 @@ if [ -z \"$head\" -o -z \"$merge\" -o -z \"$m\n fi\n \n dropheads\n-echo $head > \"$GIT_DIR\"/ORIG_HEAD\n-echo $merge > \"$GIT_DIR\"/LAST_MERGE\n+echo_to_file $head \"$GIT_DIR\"/ORIG_HEAD\n+echo_to_file $merge \"$GIT_DIR\"/LAST_MERGE\n \n common=$(git-merge-base $head $merge)\n if [ -z \"$common\" ]; then\n@@ -40,7 +40,7 @@ fi\n if [ \"$common\" == \"$head\" ]; then\n \techo \"Updating from $head to $merge.\"\n \tgit-read-tree -u -m $head $merge || exit 1\n-\techo $merge > \"$GIT_DIR\"/HEAD\n+\techo_to_file $merge \"$GIT_DIR\"/HEAD\n \tgit-diff-tree -p $head $merge | git-apply --stat\n \tdropheads\n \texit 0\n@@ -52,13 +52,13 @@ if [ $? -ne 0 ]; then\n \techo \"Simple merge failed, trying Automatic merge\"\n \tgit-merge-cache -o git-merge-one-file-script -a\n \tif [ $? -ne 0 ]; then\n-\t\techo $merge > \"$GIT_DIR\"/MERGE_HEAD\n+\t\techo_to_file $merge \"$GIT_DIR\"/MERGE_HEAD\n \t\tdie \"Automatic merge failed, fix up by hand\"\n \tfi\n \tresult_tree=$(git-write-tree) || exit 1\n fi\n result_commit=$(echo \"$merge_msg\" | git-commit-tree $result_tree -p $head -p $merge)\n echo \"Committed merge $result_commit\"\n-echo $result_commit > \"$GIT_DIR\"/HEAD\n+echo_to_file $result_commit \"$GIT_DIR\"/HEAD\n git-diff-tree -p $head $result_commit | git-apply --stat\n dropheads\n"},{"id":"6060","messageId":"loom.20050712T233332-364@post.gmane.org","threadId":"1214","inReplyTo":"20050712190552.GA7178@taniwha.stupidest.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Junio C Hamano","fromEmail":"junkio@twinsun.com","sentAt":"2005-07-12T21:37:00Z","receivedAt":"2005-07-12T21:37:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Chris Wedgwood <cw <at> f00f.org> writes:\n\n>  if [ \"$newhead\" ]; then\n>  \techo \"Committed as $newhead.\"\n> -\techo $newhead >$_git/HEAD\n> +\techo_to_file $newhead $_git/HEAD\n>  \t[ \"$merging\" ] && rm $_git/merging $_git/merging-sym $_git/merge-base\n\nGood intentions, but wouldn't the above clobber symlinked HEAD?\n\nNot a fundamental flaw, though.  You need to see if it is a symlink,\nreadlink it (repeatedly until you get a regular file or dangling symlink\ntarget that does not exist --- immediately after git-init-db has such a\nHEAD) and run your echo_to_file on the link target.\n"},{"id":"6068","messageId":"20050713045338.GA19819@taniwha.stupidest.org","threadId":"1214","inReplyTo":"loom.20050712T233332-364@post.gmane.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2005-07-13T04:53:38Z","receivedAt":"2005-07-13T04:53:38Z","isPatch":true,"sender":{"key":"cw@f00f.org","avatar":null},"body":"On Tue, Jul 12, 2005 at 09:37:00PM +0000, Junio C Hamano wrote:\n\n> >  if [ \"$newhead\" ]; then\n> >  \techo \"Committed as $newhead.\"\n> > -\techo $newhead >$_git/HEAD\n> > +\techo_to_file $newhead $_git/HEAD\n> >  \t[ \"$merging\" ] && rm $_git/merging $_git/merging-sym $_git/merge-base\n>\n> Good intentions, but wouldn't the above clobber symlinked HEAD?\n\nYeah, but I would argue that is the right thing to do.\n\nSymlink'd trees don't really make sense to me (they seem fragile and\nsomewhat pointless) but perhaps I'm missing something?\n\n> Not a fundamental flaw, though.  You need to see if it is a symlink,\n> readlink it (repeatedly until you get a regular file or dangling\n> symlink target that does not exist --- immediately after git-init-db\n> has such a HEAD) and run your echo_to_file on the link target.\n\nI don't see how that's helpful.  The idea of echo_to_file is to make\nthe metadata self-contained to the subtree --- so we don't want to\nhave external links (possibly) pointing outside of it.\n"},{"id":"6070","messageId":"pan.2005.07.13.07.03.26.398212@smurf.noris.de","threadId":"1214","inReplyTo":"20050713045338.GA19819@taniwha.stupidest.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Matthias Urlichs","fromEmail":"smurf@smurf.noris.de","sentAt":"2005-07-13T07:03:26Z","receivedAt":"2005-07-13T07:03:26Z","isPatch":true,"sender":{"key":"matthias@urlichs.de","avatar":"https://gravatar.com/avatar/2708905af227313eba6f2b2ae0f7d0259b5ac5d71baef58fe5a13c699ce0bbf0?d=mp&s=160"},"body":"Hi, Chris Wedgwood wrote:\n\n> Symlink'd trees don't really make sense to me (they seem fragile and\n> somewhat pointless) but perhaps I'm missing something?\n\nYou are ;-)  the tree itsels is no symlinked, but HEAD points to\nrefs/heads/<branch> by default.\n\nDon't clobber that, please.\n\n-- \nMatthias Urlichs   |   {M:U} IT Design @ m-u-it.de   |  smurf@smurf.noris.de\nDisclaimer: The quote was selected randomly. Really. | http://smurf.noris.de\n - -\nThough many hands make light work, too many cooks spoil the broth.\n"},{"id":"6081","messageId":"20050713185339.GA9260@taniwha.stupidest.org","threadId":"1214","inReplyTo":"pan.2005.07.13.07.03.26.398212@smurf.noris.de","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2005-07-13T18:53:39Z","receivedAt":"2005-07-13T18:53:39Z","isPatch":true,"sender":{"key":"cw@f00f.org","avatar":null},"body":"On Wed, Jul 13, 2005 at 09:03:26AM +0200, Matthias Urlichs wrote:\n\n> You are ;-) the tree itsels is no symlinked, but HEAD points to\n> refs/heads/<branch> by default.\n\nThanks for pointing that out.  I honestly never noticed that.\n\nHow about the following?\n\n    echo_to_file() {\n\tlocal DEST=\"$2\"\n\tlocal count=1\n\tlocal RET\n\n\t# follow symlinks until we run out or end up with something\n\t# dangling\n\twhile [ -L \"$DEST\" ] ; do\n\t    local NLINK=`readlink \"$DEST\"`\n\n\t    if [ ! -e \"$NLINK\" ] ; then\n\t\t# dangling link, just poke as-is\n\t\techo \"$1\" > \"$DEST\"\n\t\treturn $?\n\t    fi\n\n\t    # follow link\n\t    if [ -L \"$NLINK\" ] ; then\n\t\tcount=$((\"$count\" + 1))\n\t\tif [ \"$count\" -gt 5 ] ; then\n\t\t    echo \"Too many symlinks when resolving $2\" 1>&2\n\t\t    return 1\n\t\tfi\n\t\tDEST=\"$NLINK\"\n\t    fi\n\tdone\n\n\t# end up with a regular file\n\tlocal TMPFILE=`mktemp \"$DEST.tmp-XXXXXXX\"`\n\techo \"$1\" > \"$TMPFILE\"\n\tRET=\"$?\"\n\tmv -f \"$TMPFILE\" \"$DEST\"\n\treturn \"$RET\"\n    }\n\n\nObvious changes from last time are:\n\n  - mark things local\n\n  - follow links, bailing out if it takes too long\n\n  - if the link is dangling, just use as-is\n\n  - when doing the 'replace' at the end use -f because it's quite\n    possible someone will have done cp -Rl ~otheruser/tree here and mv\n    *might* bitch if the UID is different?\n\n  - return (some) errors (just because, it's not like cogito checks\n    for these)\n"},{"id":"6084","messageId":"Pine.LNX.4.58.0507131256490.17536@g5.osdl.org","threadId":"1214","inReplyTo":"20050713185339.GA9260@taniwha.stupidest.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-07-13T20:05:02Z","receivedAt":"2005-07-13T20:05:02Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 13 Jul 2005, Chris Wedgwood wrote:\n\n> On Wed, Jul 13, 2005 at 09:03:26AM +0200, Matthias Urlichs wrote:\n> \n> > You are ;-) the tree itsels is no symlinked, but HEAD points to\n> > refs/heads/<branch> by default.\n> \n> Thanks for pointing that out.  I honestly never noticed that.\n> \n> How about the following?\n\nThis is really complicated, for no good reason.\n\nThe _object_ directories should be linked, but it's really wrong to link \nthe \"refs/\" directories and expect them to have COW behaviour.\n\nI've tried to make most of the git tools write the refs objects \"safely\" \ntoo, ie things like \"git-receive-pack\" (the receiving end of a \n\"git-send-pack\") will write the new ref to a lock-file and then do a \n\"rename()\" to set it. That is COW-safe, but the thing is, it's incredibly \npainful for many other operations, and I won't guarantee that git in \ngeneral is always going to be COW-safe wrt all the git files.\n\nFor example, the \"git clone -l\" behaviour will _only_ link the objects \nsubdirectory. The rest is copied. Unless there's a bug there somewhere.\n\n\t\tLinus\n"},{"id":"6085","messageId":"20050713200724.GN9915@kiste.smurf.noris.de","threadId":"1214","inReplyTo":"20050713185339.GA9260@taniwha.stupidest.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Matthias Urlichs","fromEmail":"smurf@smurf.noris.de","sentAt":"2005-07-13T20:07:24Z","receivedAt":"2005-07-13T20:07:24Z","isPatch":true,"sender":{"key":"matthias@urlichs.de","avatar":"https://gravatar.com/avatar/2708905af227313eba6f2b2ae0f7d0259b5ac5d71baef58fe5a13c699ce0bbf0?d=mp&s=160"},"body":"Hi,\n\nChris Wedgwood:\n> How about the following?\n> \nUmmm...\n\n> \t    local NLINK=`readlink \"$DEST\"`\n> \n> \t    if [ ! -e \"$NLINK\" ] ; then\n\nYou lose if the link is relative and the symlink is not in the current\ndirectory. You also lose on systems where the empty filename is\nsynonymous with the current directory.\n\nYou'd need to do something along the lines of\n\n\tif [ -n \"$NLINK\" ] ; then\n\t\tcase \"$NLINK\" in\n\t\t/*) ;;\n\t\t*) NLINK=\"$(dirname \"$DEST\")/$NLINK\" ;;\n\t\tesac\n\tfi\n\nfirst.\n\n> \t\t# dangling link, just poke as-is\n> \t\techo \"$1\" > \"$DEST\"\n\nYou should remove \"DEST first. Otherwise, under Linux, you'll magically\ncreate the file the symlink points to, which may not be what you want to\ndo.\n\n-- \nMatthias Urlichs   |   {M:U} IT Design @ m-u-it.de   |  smurf@smurf.noris.de\nDisclaimer: The quote was selected randomly. Really. | http://smurf.noris.de\n - -\nCustom does often reason overrule And only serves for reason to the fool.\n\t\t\t\t\t-- John Wilmot, Earl of Rochester\n"},{"id":"6090","messageId":"20050713203647.GA11403@taniwha.stupidest.org","threadId":"1214","inReplyTo":"20050713200724.GN9915@kiste.smurf.noris.de","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2005-07-13T20:36:47Z","receivedAt":"2005-07-13T20:36:47Z","isPatch":true,"sender":{"key":"cw@f00f.org","avatar":null},"body":"On Wed, Jul 13, 2005 at 10:07:24PM +0200, Matthias Urlichs wrote:\n\n> You lose if the link is relative and the symlink is not in the\n> current directory.\n\nCogito doesn't create such links in my (limited_ experience.  Why\nwould anyone else do that?\n\n> You also lose on systems where the empty filename is synonymous with\n> the current directory.\n\nWell, return code from readlink should be tested there.\n\n\n[...]\n\n> You should remove \"DEST first. Otherwise, under Linux, you'll\n> magically create the file the symlink points to, which may not be\n> what you want to do.\n\nThat behavior *is* desirable in this case.\n\n\nAnyhow, the fact this is so complicated and getting more so makes me\nthink it's misdirected :-(\n"},{"id":"6089","messageId":"20050713204458.GB11403@taniwha.stupidest.org","threadId":"1214","inReplyTo":"Pine.LNX.4.58.0507131256490.17536@g5.osdl.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2005-07-13T20:44:58Z","receivedAt":"2005-07-13T20:44:58Z","isPatch":true,"sender":{"key":"cw@f00f.org","avatar":null},"body":"On Wed, Jul 13, 2005 at 01:05:02PM -0700, Linus Torvalds wrote:\n\n> This is really complicated, for no good reason.\n\nWhat should I be doing?\n\n> The _object_ directories should be linked, but it's really wrong to link \n> the \"refs/\" directories and expect them to have COW behaviour.\n\nI'm confused.\n\nThis function in only used to update various files, one of which is\n.git/HEAD\n\nThe complexity I added was to deal with a situation where we have\n\n  tree1/\n\t.git/HEAD -> refs/head/master\n\nand I do \"cp -Rl tree1 tree2\" giving me:\n\n  tree2/\n\t.git/HEAD -> refs/head/master\n\n  and .git/refs/head/master is hardlinked between both trees.\n\nCogito will do \"echo <foo> > .git/HEAD\" which update *both* tree1 and\ntree2 which is not what I want.  So the idea is to readlink and do the\nupdate using echo + rename to get the CoW semantics I desire.\n\n\nYes, weird things happen if you have out-of-treel links but I suspect\nif you do that with cogito you've probably lost anyhow.\n\n> I've tried to make most of the git tools write the refs objects\n> \"safely\" too, ie things like \"git-receive-pack\" (the receiving end\n> of a \"git-send-pack\") will write the new ref to a lock-file and then\n> do a \"rename()\" to set it.\n\nYes, git is probably fine.  Cogito isn't.\n\n> That is COW-safe, but the thing is, it's incredibly painful for many\n> other operations, and I won't guarantee that git in general is\n> always going to be COW-safe wrt all the git files.\n\nWhat operations is this painful for?\n"},{"id":"6095","messageId":"Pine.LNX.4.58.0507131402210.17536@g5.osdl.org","threadId":"1214","inReplyTo":"20050713204458.GB11403@taniwha.stupidest.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-07-13T21:05:35Z","receivedAt":"2005-07-13T21:05:35Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 13 Jul 2005, Chris Wedgwood wrote:\n> \n> The complexity I added was to deal with a situation where we have\n> \n>   tree1/\n> \t.git/HEAD -> refs/head/master\n> \n> and I do \"cp -Rl tree1 tree2\" giving me:\n> \n>   tree2/\n> \t.git/HEAD -> refs/head/master\n> \n>   and .git/refs/head/master is hardlinked between both trees.\n\nAND THAT IS WRONG.\n\nYou shouldn't hardlink whole trees. Git will not guarantee that it breaks \nthe links.\n\nRight now, if you go a \"git commit\", and a number of other core git \noperations, it will use \">\" to overwrite the HEAD. You're screwed.\n\n> > That is COW-safe, but the thing is, it's incredibly painful for many\n> > other operations, and I won't guarantee that git in general is\n> > always going to be COW-safe wrt all the git files.\n> \n> What operations is this painful for?\n\nYour script was tens of lines of code, and it was BUGGY.\n\nAnd now you ask what it's painful for?\n\nGuys, if you want to mess up code, the #1 rule is:\n - make complex code to handle insane situations\n\nYou're well on your way to do that.\n\nDon't hardlink git trees. It's a bug to do so. It will eventually change \nthe other git tree.\n\nHardlink git _object_ directories. That's a totally different thing.\n\n\t\tLinus\n"},{"id":"6096","messageId":"20050713211106.GA12047@taniwha.stupidest.org","threadId":"1214","inReplyTo":"Pine.LNX.4.58.0507131402210.17536@g5.osdl.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2005-07-13T21:11:06Z","receivedAt":"2005-07-13T21:11:06Z","isPatch":true,"sender":{"key":"cw@f00f.org","avatar":null},"body":"On Wed, Jul 13, 2005 at 02:05:35PM -0700, Linus Torvalds wrote:\n\n> >   and .git/refs/head/master is hardlinked between both trees.\n>\n> AND THAT IS WRONG.\n\nOK, I was more-or-less assuming that 'cp -Rl tree1 tree2' was always a\nvalid thing to do.\n\nClearly if it's not then all of this is somewhat moot.\n\n> You shouldn't hardlink whole trees. Git will not guarantee that it\n> breaks the links.\n\nI can live with that, until now though I wasn't aware that hardlinked\ntrees were a bad-thing.\n\n> Your script was tens of lines of code, and it was BUGGY.\n\nYeah well, I'm a retard.\n\n> Hardlink git _object_ directories. That's a totally different thing.\n\nI was under the misconception that hardlink entire trees was\npermissible.\n\nI would however like to be able to hardlink more than just the\n_object_ directory --- hardlinking the source is quite nice too.\nMight that be considered safe?  (I'm of course assuming that editors\ndo write + rename when saving their buffers).\n"},{"id":"6098","messageId":"Pine.LNX.4.58.0507131410430.17536@g5.osdl.org","threadId":"1214","inReplyTo":"20050713185339.GA9260@taniwha.stupidest.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-07-13T21:23:18Z","receivedAt":"2005-07-13T21:23:18Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 13 Jul 2005, Chris Wedgwood wrote:\n> \n> How about the following?\n> \n>     echo_to_file() {\n> \tlocal DEST=\"$2\"\n> \tlocal count=1\n> \tlocal RET\n> \n> \t# follow symlinks until we run out or end up with something\n> \t# dangling\n> \twhile [ -L \"$DEST\" ] ; do\n\nIf you really want to do something like this, then you should do it like \nthis:\n\n\tupdate_head() {\n\t\thead=\"$GIT_DIR/HEAD\"\n\t\tnewvalue=$(git-rev-parse --verify \"$1\"^0) || exit\n\t\tif [ -L \"$head\" ]; then\n\t\t\thead=$(readlink \"$head\")\n\t\t\tcase \"$head\" in\n\t\t\trefs/heads/*)\n\t\t\t\thead=\"$GIT_DIR\"/\"$head\"\n\t\t\t\t;;\n\t\t\t*)\n\t\t\t\texit 1\n\t\t\t\t;;\n\t\t\tesac\n\t\tfi\n\t\techo $newvalue > \"$head.lock\" && rename \"$head.lock\" \"$head\"\n\t}\n\nwhich is at least slightly simpler, and might even work thanks to that.\n\nYour was buggy for several reasons:\n\n - following multiple links is _wrong_, since the next-level link could \n   actually be a symlink to another tree entirely (if somebody is crazy \n   enough to use \"cp -Rl\" to copy trees, then why not \"cp -Rs\"?)\n\n   Ergo: you should only follow a symlink if it points to refs/heads, and \n   anything else would be a BUG.\n\n - you got relative links wrong.\n\nNow, admittedly the above is totally untested too, so I'll probably have \ntypos etc in there. But the basic point stands: don't go for complexity. \nGo for one _particular_ case.\n\n\t\tLinus\n"},{"id":"6100","messageId":"Pine.LNX.4.58.0507131425080.17536@g5.osdl.org","threadId":"1214","inReplyTo":"20050713211106.GA12047@taniwha.stupidest.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-07-13T21:33:43Z","receivedAt":"2005-07-13T21:33:43Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 13 Jul 2005, Chris Wedgwood wrote:\n> \n> I would however like to be able to hardlink more than just the\n> _object_ directory --- hardlinking the source is quite nice too.\n> Might that be considered safe?  (I'm of course assuming that editors\n> do write + rename when saving their buffers).\n\nHmm.. I don't think it's necessarily wrong, although as you say, your\neditor had better DTRT. That said, even if your editor doesn't, at least\nyou won't corrupt your git archive, although you might have surprising\nchanges creep into the other side..\n\nBut I've not verified that every git program does the right thing...\n\nSearch-search-search.. Indeed: at least \"git-apply\" seems to modify the\nfile in place. That may be the only one, though - and should be easily\nfixed by adding an \"unlink()\" (and make it use O_EXCL just to make it \neasy to grep to verify that we always use O_EXCL with O_WR.*).\n\nentry.c should also add O_EXCL (it seems to already do the unlink).\n\nAnybody want to go through and verify it? I think we should support \nhardlinked source trees if we can do so easily, and it _looks_ like we're \npretty close..\n\n\t\tLinus\n"},{"id":"6101","messageId":"20050713215039.GA12882@taniwha.stupidest.org","threadId":"1214","inReplyTo":"Pine.LNX.4.58.0507131425080.17536@g5.osdl.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2005-07-13T21:50:39Z","receivedAt":"2005-07-13T21:50:39Z","isPatch":true,"sender":{"key":"cw@f00f.org","avatar":null},"body":"On Wed, Jul 13, 2005 at 02:33:43PM -0700, Linus Torvalds wrote:\n\n> Hmm.. I don't think it's necessarily wrong, although as you say,\n> your editor had better DTRT.\n\nIt does.  I assume probably everything does but I never really\nchecked.\n\n> That said, even if your editor doesn't, at least you won't corrupt\n> your git archive, although you might have surprising changes creep\n> into the other side..\n\nWell, with hard-linked trees and the nastyness I posted I would test\nwith\n\n\tcp -Rl tree1 tree2\n\tcd tree2\n\t<hack hack hack>\n\tfind ../tree1 -mmin -<n> ... # make sure we didn't mess up original tree\n\nSo far this seems to work for me.\n\n> Search-search-search.. Indeed: at least \"git-apply\" seems to modify\n> the file in place.\n\nI probably just luckily never hit this.\n"},{"id":"6127","messageId":"20050713215435.GB12882@taniwha.stupidest.org","threadId":"1214","inReplyTo":"Pine.LNX.4.58.0507131410430.17536@g5.osdl.org","subject":"Re: [RFC PATCH] cogito --- don't overwrite metadata files in place (breaks CoW use)","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2005-07-13T21:54:35Z","receivedAt":"2005-07-13T21:54:35Z","isPatch":true,"sender":{"key":"cw@f00f.org","avatar":null},"body":"On Wed, Jul 13, 2005 at 02:23:18PM -0700, Linus Torvalds wrote:\n\n> If you really want to do something like this, then you should do it like\n> this:\n>\n> \tupdate_head() {\n\nexcept there are a number of places I need to frob things in Cogito\nand they are not all for the head.  If the head is special and the\nonly candidate sure, that works for me.\n\n>  - following multiple links is _wrong_, since the next-level link could\n>    actually be a symlink to another tree entirely (if somebody is crazy\n>    enough to use \"cp -Rl\" to copy trees, then why not \"cp -Rs\"?)\n\nI already stated that wasn't supposed to work.  If you have symlinks\noutside of your tree with cogito I think there is a *lot* more code\nthat probably needs to be checked.\n\nRemember I used cogito here, not git directly.  That probably means\nthings are invoked very differently for me?\n\n>  - you got relative links wrong.\n\nPossibly, I was more for RFC/concept reasons I posted the patch.  I'm\nclueless when it comes to writing decent or reliable bash sh/bash\nscripts.\n"}]}