{"thread":{"id":"43491","subject":"Re: Is cp -al safe with git?","startedAt":"2006-11-16T18:47:20Z","lastAt":"2006-11-16T19:19:57Z","messageCount":3,"participants":["Junio C Hamano","Linus Torvalds","Johannes Sixt"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"295689","messageId":"ejibnp$mmq$1@sea.gmane.org","threadId":"43491","inReplyTo":null,"subject":"Is cp -al safe with git?","fromName":"Johannes Sixt","fromEmail":"johannes.sixt@telecom.at","sentAt":"2006-11-16T18:47:20Z","receivedAt":"2006-11-16T18:47:20Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"For one reason or another I would like to \"clone\" a local repo including the\nchecked-out working tree with cp -al instead of cg-clone/git-clone, i.e.\nhave all files hard-linked instead of copied.\n\nCan the copies be worked on independently without interference (with the git\ntool set)?\n\nOne thing I noticed is that git-reset or probably git-checkout-index breaks\nlinks of files that need not be changed by the reset. Example:\n\n# make 2 files, commit\n$ mkdir orig && cd orig\n$ git-init-db \ndefaulting to local storage area\n$ echo foo > a && cp a b && git-add a b && git-commit -a -m 1\nCommitting initial tree 99b876dbe094cb7d3850f1abe12b4c5426bb63ea\n\n# 2nd commit modifies only one file:\n$ echo bar > a && git-commit -a -m 2\n\n# create the copy:\n$ cd ..\n$ cp -al orig copy\n$ cd copy\n\n# working files are hard-linked:\n$ ls -l\ntotal 8\n-rw-r--r-- 2 jsixt users 4 Nov 16 19:24 a\n-rw-r--r-- 2 jsixt users 4 Nov 16 19:23 b\n\n# nuke a commit:\n$ git-reset --hard HEAD^\n$ ls -l\ntotal 8\n-rw-r--r-- 1 jsixt users 4 Nov 16 19:24 a\n-rw-r--r-- 1 jsixt users 4 Nov 16 19:24 b\n\nI'd have expected that the hard-link of b remained and only a's link were\nbroken. Does it mean that git-reset writes every single file also for large\ntrees like the kernel? I cannot believe this. Can someone scratch the\ntomatoes off my eyes please?\n\n-- Hannes\n\n"},{"id":"295300","messageId":"Pine.LNX.4.64.0611161108020.3349@woody.osdl.org","threadId":"43491","inReplyTo":"ejibnp$mmq$1@sea.gmane.org","subject":"Re: Is cp -al safe with git?","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-11-16T19:19:21Z","receivedAt":"2006-11-16T19:19:21Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Thu, 16 Nov 2006, Johannes Sixt wrote:\n>\n> For one reason or another I would like to \"clone\" a local repo including the\n> checked-out working tree with cp -al instead of cg-clone/git-clone, i.e.\n> have all files hard-linked instead of copied.\n\nIt works, but I don't think you should depend on it.\n\n> Can the copies be worked on independently without interference (with the git\n> tool set)?\n\nWe _tried_ to make sure it is ok, but since it's not a normal mode of \noperation, I would not guarantee it.\n\n> One thing I noticed is that git-reset or probably git-checkout-index breaks\n> links of files that need not be changed by the reset.\n\nYes and no. They do _not_ actually break links of files that they know \nstay the same, but your example breaks the internal knowledge by using \nthat \"cp -al\". That changes the modification time of the inodes, so git \nthinks that the files _may_ have changed, and when you do a \"git reset\", \nit will overwrite them all.\n\n> Example:\n> \n> # make 2 files, commit\n> $ mkdir orig && cd orig\n> $ git-init-db \n> defaulting to local storage area\n> $ echo foo > a && cp a b && git-add a b && git-commit -a -m 1\n> Committing initial tree 99b876dbe094cb7d3850f1abe12b4c5426bb63ea\n> \n> # 2nd commit modifies only one file:\n> $ echo bar > a && git-commit -a -m 2\n> \n> # create the copy:\n> $ cd ..\n> $ cp -al orig copy\n> $ cd copy\n> \n> # working files are hard-linked:\n> $ ls -l\n> total 8\n> -rw-r--r-- 2 jsixt users 4 Nov 16 19:24 a\n> -rw-r--r-- 2 jsixt users 4 Nov 16 19:23 b\n> \n> # nuke a commit:\n> $ git-reset --hard HEAD^\n> $ ls -l\n> total 8\n> -rw-r--r-- 1 jsixt users 4 Nov 16 19:24 a\n> -rw-r--r-- 1 jsixt users 4 Nov 16 19:24 b\n> \n> I'd have expected that the hard-link of b remained and only a's link were\n> broken. Does it mean that git-reset writes every single file also for large\n> trees like the kernel? I cannot believe this. Can someone scratch the\n> tomatoes off my eyes please?\n\nIf you do a\n\n\tgit update-index --refresh\n\n(or, more easily, a \"git status\", which will do the index refresh for you) \nbefore you do the \"git reset\", you will get:\n\n\t$ ls -l\n\ttotal 8\n\t-rw-r--r-- 1 jsixt users 4 Nov 16 19:24 a\n\t-rw-r--r-- 2 jsixt users 4 Nov 16 19:24 b\n\nlike you want to. The reason \"git reset\" overwrites _both_ files in your \nexample is that the stat() information for those files changed, so \"git \nreset\" thinks they are both dirty and both need to be rewritten.\n\nThat said, I would seriously suggest that you try these things out, and \nrealize that most people do _not_ use the hardlinked approach. For all I \nknow, some piece of git might change some files in-place. I don't _think_ \nwe do, and it would strictly speaking be a bug, but because people don't \nuse it that way, you'd be the guinea pig.\n\nI think we'll happily fix any bugs you find, but that may not make you any \nhappier if the bug corrupted your lifes work ;)\n\nIn general, you might want to use\n\n\tgit clone -l -s\n\ninstead, but that will _not_ hardlink the actual checked-out contents, so \nit's not going to get the kind of sharing you look for. On the other hand, \nespecially with good maintenance (doing \"git repack -l -d -a\" etc), you \nmay end up sharing _more_ that way at least in the repository object \ndatabase (but never in the actual checked-out directories).\n\n"},{"id":"295101","messageId":"7v7ixv9p2a.fsf@assigned-by-dhcp.cox.net","threadId":"43491","inReplyTo":"ejibnp$mmq$1@sea.gmane.org","subject":"Re: Is cp -al safe with git?","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-11-16T19:19:57Z","receivedAt":"2006-11-16T19:19:57Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <johannes.sixt@telecom.at> writes:\n\n> For one reason or another I would like to \"clone\" a local repo including the\n> checked-out working tree with cp -al instead of cg-clone/git-clone, i.e.\n> have all files hard-linked instead of copied.\n>\n> Can the copies be worked on independently without interference (with the git\n> tool set)?\n>\n> One thing I noticed is that git-reset or probably git-checkout-index breaks\n> links of files that need not be changed by the reset. Example:\n>\n> # make 2 files, commit\n> $ mkdir orig && cd orig\n> $ git-init-db \n> defaulting to local storage area\n> $ echo foo > a && cp a b && git-add a b && git-commit -a -m 1\n> Committing initial tree 99b876dbe094cb7d3850f1abe12b4c5426bb63ea\n>\n> # 2nd commit modifies only one file:\n> $ echo bar > a && git-commit -a -m 2\n>\n> # create the copy:\n> $ cd ..\n> $ cp -al orig copy\n> $ cd copy\n>\n> # working files are hard-linked:\n> $ ls -l\n> total 8\n> -rw-r--r-- 2 jsixt users 4 Nov 16 19:24 a\n> -rw-r--r-- 2 jsixt users 4 Nov 16 19:23 b\n>\n> # nuke a commit:\n> $ git-reset --hard HEAD^\n> $ ls -l\n> total 8\n> -rw-r--r-- 1 jsixt users 4 Nov 16 19:24 a\n> -rw-r--r-- 1 jsixt users 4 Nov 16 19:24 b\n>\n> I'd have expected that the hard-link of b remained and only a's link were\n> broken. Does it mean that git-reset writes every single file also for large\n> trees like the kernel? I cannot believe this. Can someone scratch the\n> tomatoes off my eyes please?\n\nMost likely you didn't run \"update-index --refresh\" after \"cp -l\"?\nNot just in the new copied repository but in the original\nrepository I would suspect you would see this.  This is because\nthe index caches ctime and making a new hardlink manipulates the\nfiles' inodes, thus making the cached information stale.\n\n"}]}