{"thread":{"id":"126","subject":"\"True\" git merge in git-pasky","startedAt":"2005-04-19T03:51:07Z","lastAt":"2005-04-21T04:04:44Z","messageCount":10,"participants":["Petr Baudis","bert hubert","Francois Romieu","Junio C Hamano","Linus Torvalds"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"780","messageId":"20050419035107.GB5554@pasky.ji.cz","threadId":"126","inReplyTo":null,"subject":"\"True\" git merge in git-pasky","fromName":"Petr Baudis","fromEmail":"pasky@ucw.cz","sentAt":"2005-04-19T03:51:07Z","receivedAt":"2005-04-19T03:51:07Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"  Hello,\n\n  so I've implemented \"true\" git merge in git-pasky, using core git's\nmerging capabilities. It seems to even work. :-)\n\n  I tested it briefly, and even did one non-conflicting and one\nconflicting merge with Linus with this, but I'd like to hear your\ncomments and possibly more testing before releasing it.\n\n  To get the lastest git-pasky, get the tarball at\n\n\thttp://pasky.or.cz/~pasky/dev/git\n\nunpack, build, install, do\n\n\tgit pull\n\nrebuild and reinstall.\n\n\n  The semantics is trivial (and it might get changed so that you would\ndo git update instead of git pull at most of places). If you don't have\na given GIT repository ready yet, do\n\n\tgit init rsync://example.com/repo\n\nin a new directory. It is by default tracking, therefore if you do\n\n\tgit pull\n\nanytime later, git merge will be automatically invoked. If you want to\nprevent this, do\n\n\tgit track\n\nwhich will untrack your tree; the remote branch you were tracking is\ncalled \"origin\", shall you want to pull/merge it later. You might want\nto also merge with someone else. Do\n\n\tgit addremote elsewhere rsync://example.org/another\n\tgit pull elsewhere\n\tgit merge elsewhere\n\n(Note that merge won't pull automatically; you must do that on your own\nif you want to pull.)\n\n\nIf the merge didn't succeed and you have conflicts, don't panic. The\nmerge told you about the conflicts, you can also do\n\n\tgit diff\n\nto see the changes, you'll probably spot the conflict markers. Resolve\nthe conflicts and then simply do\n\n\tgit commit\n\nto commit the pending merge.\n\n\nNow you decided to do a little bit of parallel development and stick\nyour patches not ready for 2.6.12 to a separate tree. That's fine, do\n\n\tgit fork experimental ~/linux-2.6.experimental\n\nand get some coffee. (It takes about 8 minutes here, but I think git\nisn't at fault - it is probably all spent in\n\n\tread-tree $(tree-id)\n\tcheckout-cache -a\n\tupdate-cache --refresh\n\nand you pretty much need to call that.)\n\nThen, do some work there, syncing with your main tree periodically:\n\n\tgit merge master\n\n(that's how your first init'd branch is called). You decide to make it\nmore fun for Linus and push your experimental stuff into your master\ntree. Fine, cd there and do\n\n\tgit merge experimental\n\nand there you go!\n\n\nHave fun,\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nC++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor\n"},{"id":"785","messageId":"20050419054307.GA1528@outpost.ds9a.nl","threadId":"126","inReplyTo":"20050419035107.GB5554@pasky.ji.cz","subject":"Re: \"True\" git merge in git-pasky","fromName":"bert hubert","fromEmail":"ahu@ds9a.nl","sentAt":"2005-04-19T05:43:07Z","receivedAt":"2005-04-19T05:43:07Z","isPatch":false,"sender":{"key":"ahu@ds9a.nl","avatar":null},"body":"On Tue, Apr 19, 2005 at 05:51:07AM +0200, Petr Baudis wrote:\n> \thttp://pasky.or.cz/~pasky/dev/git\n\nI pulled the tar.bz2 and did make:\ngcc -g -O3 -Wall -o merge-cache merge-cache.o libgit.a libgit.a -lssl -lz\ngcc -g -O3 -Wall   -c -o unpack-file.o unpack-file.c\ngcc -g -O3 -Wall -o unpack-file unpack-file.o libgit.a libgit.a -lssl -lz\nmake: commit-id: Command not found\nGenerating gitversion.sh...\n\nIs this bad?\n\n-- \nhttp://www.PowerDNS.com      Open source, database driven DNS Software \nhttp://netherlabs.nl              Open and Closed source services\n"},{"id":"790","messageId":"20050419080949.GA2393@pasky.ji.cz","threadId":"126","inReplyTo":"20050419054307.GA1528@outpost.ds9a.nl","subject":"Re: \"True\" git merge in git-pasky","fromName":"Petr Baudis","fromEmail":"pasky@ucw.cz","sentAt":"2005-04-19T08:09:49Z","receivedAt":"2005-04-19T08:09:49Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"Dear diary, on Tue, Apr 19, 2005 at 07:43:07AM CEST, I got a letter\nwhere bert hubert <ahu@ds9a.nl> told me that...\n> On Tue, Apr 19, 2005 at 05:51:07AM +0200, Petr Baudis wrote:\n> > \thttp://pasky.or.cz/~pasky/dev/git\n> \n> I pulled the tar.bz2 and did make:\n> gcc -g -O3 -Wall -o merge-cache merge-cache.o libgit.a libgit.a -lssl -lz\n> gcc -g -O3 -Wall   -c -o unpack-file.o unpack-file.c\n> gcc -g -O3 -Wall -o unpack-file unpack-file.o libgit.a libgit.a -lssl -lz\n> make: commit-id: Command not found\n> Generating gitversion.sh...\n> \n> Is this bad?\n\nIt will cause a 40-digit hexadecimal number missing in your git help and\ngit version output.\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nStuff: http://pasky.or.cz/\nC++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor\n"},{"id":"911","messageId":"20050419234057.GA14807@electric-eye.fr.zoreil.com","threadId":"126","inReplyTo":"20050419035107.GB5554@pasky.ji.cz","subject":"Re: \"True\" git merge in git-pasky","fromName":"Francois Romieu","fromEmail":"romieu@fr.zoreil.com","sentAt":"2005-04-19T23:40:57Z","receivedAt":"2005-04-19T23:40:57Z","isPatch":false,"sender":{"key":"romieu@fr.zoreil.com","avatar":null},"body":"Petr Baudis <pasky@ucw.cz> :\n[...]\n> Now you decided to do a little bit of parallel development and stick\n> your patches not ready for 2.6.12 to a separate tree. That's fine, do\n> \n> \tgit fork experimental ~/linux-2.6.experimental\n> \n> and get some coffee. (It takes about 8 minutes here, but I think git\n> isn't at fault - it is probably all spent in\n> \n> \tread-tree $(tree-id)\n> \tcheckout-cache -a\n> \tupdate-cache --refresh\n\nTip of the day: cat the whole tree to /dev/null before the fork\n\n--\nUeimor\n"},{"id":"918","messageId":"7vacnumgot.fsf@assigned-by-dhcp.cox.net","threadId":"126","inReplyTo":"20050419035107.GB5554@pasky.ji.cz","subject":"[RFC] Possible strategy cleanup for git add/remove/diff etc.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-04-20T00:32:02Z","receivedAt":"2005-04-20T00:32:02Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I was reading this comment in gitcommit.sh and started\nthinking...\n\n    # We bother with added/removed files here instead of updating\n    # the cache at the time of git(add|rm).sh, since we want to\n    # have the cache in a consistent state representing the tree\n    # as it was the last time we committed. Otherwise, e.g. partial\n    # conflicts would be a PITA since added/removed files would\n    # be committed along automagically as well.\n\nLet's for a moment forget what git-pasky currently does, which\nis not to touch .git/index until the user says \"Ok, let's\ncommit\".  I am wondering if that is the root cause of all the\ntrouble git-pasky needs to go through.  Specifically I think\nhaving to deal with add/remove queue seems to affect not just\ncommit you have that comment above but also with diffs.\n\nI'd like to start from a different premise and see what happens:\n\n - What .git/index records is *not* the state as the last\n   commit.  It is just an cache Cogito uses to speed up access\n   to the user's working tree.  From the user's point of view,\n   it does not even exist.\n\n - The way this hypothetical Cogito uses .git/index is to always\n   reflect add and remove but modification may be out of sync.\n   It is updated lazily when .git/index must match the working\n   tree.  Again, this is invisible to the user.  From the user's\n   point of view, there are only two things: the last commit\n   represented as .git/HEAD and his own working tree.\n\nI call this hypothetical implementation of Cogito \"jit-*\" in the\nfollowing description.  Also this is just to convey the idea, so\nall the error checking (e.g. \"what the user gave jit-merge is\nnot a valid commit id\") and sugarcoating (e.g. tags, symbolic\nforeign repository names instead of rsync URL etc) are omitted.\n\n\n* jit-checkout $commit_id\n\n  This is like \"cvs co\".  Same as what you are doing I suppose.\n\n    committed_tree=$(cat-file commit $commit_id | sed -e 's/^tree //;q')\n    read-tree $committed_tree\n    checkout-cache -f -a\n    echo $commit_id >.git/HEAD\n\n* jit-add files... | jit-remove files...\n\n  Like \"cvs add\".  Here, .git/index is treated as just a cache\n  of the working tree, not the mirror of previous commit.  So\n  unlike git-pasky, jit-* touches .git/index here.\n\n    update-cache --add \"$@\"\n\n    ---\n\n    rm -f \"$@\" ;# this is debatable...\n    update-cache --remove \"$@\"\n\n* jit-diff [files...]\n\n  Like \"cvs diff\".  The user wants to see what's different\n  between his working tree and the last commit.\n\n    case \"$#\" in 0) set x $(show-files --cached); shift ;; esac\n    update-cache --add --remove \"$@\" --refresh\n    current_tree=$(write-tree)\n\n    committed_tree=$(cat-file commit $commit_id | sed -e 's/^tree //;q')\n    diff-tree -r -z $committed_tree $current_tree |\n      filter-output-to-limit-to-given-filelist \"$@\" |\n      parse-diff-tree-output-and-show-real-file-diffs\n\n  Unlike git-pasky, jit-* does not keep the state from the last\n  commit in .git/index.  Instead, .git/index is meant to cache\n  the state of the working tree.  So the first three lines in\n  the above updates .git/index lazily from what is in the\n  working tree for the part that needs to be diffed.  Then it\n  uses helper scripts to filter and parse diff-tree output and\n  generates per-file diffs.  Since add and remove are already\n  recorded in .git/index, it does not have to special case\n  \"uncommitted add\" and such.\n\n* jit-commit\n\n  Like \"cvs commit\".\n\n    set x $(show-files --cached); shift\n    update-cache --add --remove \"$@\"\n\n    current_tree=$(write-tree)\n    next_commit=$(commmit-tree $current_tree -p $(cat .git/HEAD))\n    echo $next_commit >.git/HEAD\n\n  Unlike git-pasky, .git/index already has adds and removes but\n  it does not know about local modifications.  So it runs\n  update-cache to make it match the working tree first, and then\n  does the usual commit thing.  \n\n  The above only allows the whole tree commit.  But allowing\n  single file commit is not that hard:\n\n    (\n        set x $(show-files --cached); shift\n        update-cache --add --remove \"$@\"\n    ) ;# we use subshell to preserve \"$@\" here...\n    current_tree=$(write-tree)\n\n    committed_tree=$(cat-file commit $commit_id | sed -e 's/^tree //;q')\n    read-tree $(committed_tree)\n    update-cache --add --remove \"$@\"\n    next_commit=$(commmit-tree $current_tree -p $(cat .git/HEAD))\n    echo $next_commit >.git/HEAD\n\n    read-tree $current_tree\n\n  The first four lines are to preserve the current tree state.\n  Then we rewind the dircache to the last committed state,\n  update only the named files to bring it to the state the user\n  wanted to commit, and commit.  Once done, we re-read the state\n  to match the user's original intention (e.g. adds recorded in\n  .git/index previously but not committed in this run is\n  preserved).\n\n\n* jit-merge $commit_id\n\n  LIke \"cvs up -j\".  I have working tree which is based on some\n  commit, and I want to merge somebody else's head $commit_id.\n  Stated more exactly: I want to have the result of my changes\n  in my working tree, if I started out from the merge between\n  the commit I am actually based on and $commit_id.\n\n    # First get my changes and stash away in a safe place.\n    jit-diff >,,working-tree-changes-as-patch\n\n    # After the above, we know .git/index matches the working tree, so...\n    current_tree=$(write-tree)\n\n    # Usual 3-way Linus merge.\n    merge_base=$(merge-base $(cat .git/HEAD) $commit_id)\n\n    base_tree=$(cat-file commit $merge_base | sed -e 's/^tree //;q')\n    committed_tree=$(cat-file commit $(cat .git/HEAD) | sed -e 's/^tree //;q')\n    his_tree=$(cat-file commit $commit_id | sed -e 's/^tree //;q')\n\n    read-tree -m $base_tree $committed_tree $his_tree\n    merge-cache three-way-merge-script -a\n\n    # Now our .git/index has the merge result.  Match working\n    # tree to it.\n    checkout-cache -f -a\n\n    # Apply our precious changes.\n    patch <,,working-tree-changes-as-patch\n\n    # Here we need to detect adds and removes and issue\n    # appropriate update-cache --add --remove.\n\n* jit-pull $foreign_repository\n\n  I do not think we need this.  Just rsync but not merge.\n\n\nIt looks quite simple.  I am asking your opinion because I am\nsure you have thought about issues involved through, and the\nabove outline looks simple only because it is missing something\nimportant that you already had to deal with and solved---and the\nsolution looks convoluted to me only because I am not aware of\nthe problem you had to solve.\n\n"},{"id":"924","messageId":"Pine.LNX.4.58.0504191846290.6467@ppc970.osdl.org","threadId":"126","inReplyTo":"7vacnumgot.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC] Possible strategy cleanup for git add/remove/diff etc.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-04-20T01:51:06Z","receivedAt":"2005-04-20T01:51:06Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 19 Apr 2005, Junio C Hamano wrote:\n> \n> Let's for a moment forget what git-pasky currently does, which\n> is not to touch .git/index until the user says \"Ok, let's\n> commit\". \n\nI think git-pasky is wrong.\n\nIt's true that we want to often (almost always) diff against the last \n\"released\" thing, and I actually think git-pasky does what it does because \nI never wrote a tool to diff the current working directory against a \n\"tree\".\n\nAt the same time, I very much worked with a model where you do _not_ have \na traditional \"work file\", but the index really _is_ the \"work file\".\n\n> I'd like to start from a different premise and see what happens:\n> \n>  - What .git/index records is *not* the state as the last\n>    commit.  It is just an cache Cogito uses to speed up access\n>    to the user's working tree.  From the user's point of view,\n>    it does not even exist.\n\nYes. Yes. YES.\n\nThat is indeed the whole point of the index file. In my world-view, the\nindex file does _everything_. It's the staging area (\"work file\"), it's\nthe merging area (\"merge directory\") and it's the cache file (\"stat\ncache\").\n\nI'll immediately write a tool to diff the current working directory \nagainst a tree object, and hopefully that will just make pasky happy with \nthis model too. \n\nIs there any other reason why git-pasky wants to have a work file?\n\n\t\tLinus\n"},{"id":"926","messageId":"7v3btmmco0.fsf@assigned-by-dhcp.cox.net","threadId":"126","inReplyTo":"Pine.LNX.4.58.0504191846290.6467@ppc970.osdl.org","subject":"Re: [RFC] Possible strategy cleanup for git add/remove/diff etc.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-04-20T01:58:55Z","receivedAt":"2005-04-20T01:58:55Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\nLT> Is there any other reason why git-pasky wants to have a work file?\n\nDo you mean \"why does a user wants to check things out in the\nworking directory and make changes, possibly run compile tests\nbefore pushing the result to Linus?\" ;-)  I'm confused what you\nmean by \"a work file\", I guess...\n\n\n"},{"id":"933","messageId":"Pine.LNX.4.58.0504192102140.6467@ppc970.osdl.org","threadId":"126","inReplyTo":"Pine.LNX.4.58.0504191846290.6467@ppc970.osdl.org","subject":"Re: [RFC] Possible strategy cleanup for git add/remove/diff etc.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-04-20T04:32:44Z","receivedAt":"2005-04-20T04:32:44Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 19 Apr 2005, Linus Torvalds wrote:\n> \n> That is indeed the whole point of the index file. In my world-view, the\n> index file does _everything_. It's the staging area (\"work file\"), it's\n> the merging area (\"merge directory\") and it's the cache file (\"stat\n> cache\").\n> \n> I'll immediately write a tool to diff the current working directory \n> against a tree object, and hopefully that will just make pasky happy with \n> this model too. \n\nOk, \"immediately\" took a bit longer than I wanted to, and quite frankly,\nthe end result is not very well tested. It was a bit more complex than I\nwas hoping for to match up the index file against a tree object, since\nunlike the tree<->tree comparison in diff-tree, you have to compare two\ncases where the layout isn't the same.\n\nNo matter. It seems to work to a first approximation, and the result is\nsuch a cool tool that it's worth committing and pushing out immediately. \n\nThe code ain't exactly pretty, but hey, maybe that's just me having higher \nstandards of beauty than most. Or maybe you just shudder at what I \nconsider pretty in the first place, in which case you probably shouldn't \nlook too closely at this one.\n\nWhat the new \"diff-cache\" does is basically emulate \"diff-tree\", except \none of the trees is always the index file.\n\nYou can also choose whether you want to trust the index file entirely\n(using the \"--cached\" flag) or ask the diff logic to show any files that\ndon't match the stat state as being \"tentatively changed\".  Both of these\noperations are very useful indeed.\n\nFor example, let's say that you have worked on your index file, and are\nready to commit. You want to see eactly _what_ you are going to commit is\nwithout having to write a new tree object and compare it that way, and to\ndo that, you just do\n\n\tdiff-cache --cached $(cat .git/HEAD)\n\n(another difference between diff-tree and diff-cache is that the new \ndiff-cache can take a \"commit\" object, and it automatically just extracts \nthe tree information from there).\n\nExample: let's say I had renamed \"commit.c\" to \"git-commit.c\", and I had \ndone an \"upate-cache\" to make that effective in the index file. \n\"show-diff\" wouldn't show anything at all, since the index file matches \nmy working directory. But doing a diff-cache does:\n\n\ttorvalds@ppc970:~/git> diff-cache --cached $(cat .git/HEAD)\n\t-100644 blob    4161aecc6700a2eb579e842af0b7f22b98443f74        commit.c\n\t+100644 blob    4161aecc6700a2eb579e842af0b7f22b98443f74        git-commit.c\n\nSo what the above \"diff-cache\" command line does is to say\n\n   \"show me the differences between HEAD and the current index contents \n    (the ones I'd write with a \"write-tree\")\"\n\nAnd as you can see, the output matches \"diff-tree -r\" output (we always do\n\"-r\", since the index is always fully populated). All the same rules: \"+\"  \nmeans added file, \"-\" means removed file, and \"*\" means changed file. You \ncan trivially see that the above is a rename.\n\nIn fact, \"diff-tree --cached\" _should_ always be entirely equivalent to\nactually doing a \"write-tree\" and comparing that. Except this one is much\nnicer for the case where you just want to check. Maybe you don't want to\ndo the tree.\n\nSo doing a \"diff-cache --cached\" is basically very useful when you are \nasking yourself \"what have I already marked for being committed, and \nwhat's the difference to a previous tree\".\n\nHowever, the \"non-cached\" version takes a different approach, and is\npotentially the even more useful of the two in that what it does can't be\nemulated with a \"write-tree + diff-tree\". Thus that's the default mode.  \nThe non-cached version asks the question\n\n   \"show me the differences between HEAD and the currently checked out \n    tree - index contents _and_ files that aren't up-to-date\"\n\nwhich is obviously a very useful question too, since that tells you what\nyou _could_ commit. Again, the output matches the \"diff-tree -r\" output to\na tee, but with a twist.\n\nThe twist is that if some file doesn't match the cache, we don't have a\nbacking store thing for it, and we use the magic \"all-zero\" sha1 to show\nthat. So let's say that you have edited \"kernel/sched.c\", but have not\nactually done an update-cache on it yet - there is no \"object\" associated\nwith the new state, and you get:\n\n\ttorvalds@ppc970:~/v2.6/linux> diff-cache $(cat .git/HEAD )\n\t*100644->100664 blob    7476bbcfe5ef5a1dd87d745f298b831143e4d77e->0000000000000000000000000000000000000000      kernel/sched.c\n\nie it shows that the tree has changed, and that \"kernel/sched.c\" has is\nnot up-to-date and may contain new stuff. The all-zero sha1 means that to\nget the real diff, you need to look at the object in the working directory\ndirectly rather than do an object-to-object diff.\n\nNOTE! As with other commands of this type, \"diff-cache\" does not actually \nlook at the contents of the file at all. So maybe \"kernel/sched.c\" hasn't \nactually changed, and it's just that you touched it. In either case, it's \na note that you need to upate-cache it to make the cache be in sync.\n\nNOTE 2! You can have a mixture of files show up as \"has been updated\" and\n\"is still dirty in the working directory\" together. You can always tell\nwhich file is in which state, since the \"has been updated\" ones show a\nvalid sha1, and the \"not in sync with the index\" ones will always have the\nspecial all-zero sha1.\n\nI think this should obviate the need for Pasky keeping a separate work \nfile. You can always tell what the difference to the last commit is with \nthis, and you don't need to have a separate file to tell you about what \nyou're supposed to do.\n\n\t\t\tLinus\n"},{"id":"941","messageId":"7voecakmlq.fsf@assigned-by-dhcp.cox.net","threadId":"126","inReplyTo":"Pine.LNX.4.58.0504192102140.6467@ppc970.osdl.org","subject":"Re: [RFC] Possible strategy cleanup for git add/remove/diff etc.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-04-20T06:07:13Z","receivedAt":"2005-04-20T06:07:13Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\n>> I'll immediately write a tool to diff the current working directory \n>> against a tree object, and hopefully that will just make pasky happy with \n>> this model too. \n\nThe model you have always had is that there are three things the\nuser needs to be aware of:\n\n * files in working tree -- this is what you touch with your\n   editor and feed compilers with.\n\n * files in dircache -- update-cache copies from working\n   tree to here, checkout-cache copies from here to working\n   tree.\n\n * committed tree state -- write-tree + commit-tree copies from\n   dircache to this state, read-tree copies from here to\n   dircache.\n\nThe original message I started this thread with suggested that I\nwish if Cogito sugarcoating layer treated the dircache invisible\nto the user by keeping it virtually and lazily in sync with the\nworking tree, as opposed to the way the current git-pasky does,\nwhich is to keep it in sync with the committed state.\n\nBut after thinking about it more, I changed my mind.  With\nsomething like diff-cache available to the user, making aware of\nthe three hierarchy to the user might be cleaner.  \n\nThe workflow becomes:\n \n * Initial read-tree + checkout-cache -f -a; makes the three in\n   sync.\n\n * Hack away.  Makes the working tree drift from dircache.\n\n * show-diff to see what's changed since your last \"checkpoint\".\n   update-cache when happy.  Working tree is in sync with\n   dircache which is the \"staging area\" for my half-baked but\n   still good stuff.  Makes the dircache different from the\n   committed.\n\n * Hack away more.  show-diff does not show your earlier changes\n   anymore.  This is sometimes inconvenient when you want to see\n   what you earlier changed but not committed.  Here comes the\n   new shiny diff-cache to rescue.\n\n * When satisfied with all the changes diff-cache --cached\n   shows, finally, say write-tree + commit-tree.  This makes all\n   three in sync again.\n\nI vaguely recall having heard about some SCM that distinguishes\ncheck-in and commit.  Maybe this two-staged update-cache and\nwrite-tree + commit-tree workflow is similar to it?\n\n"},{"id":"1088","messageId":"7vsm1kg4gz.fsf@assigned-by-dhcp.cox.net","threadId":"126","inReplyTo":"Pine.LNX.4.58.0504192102140.6467@ppc970.osdl.org","subject":"Re: [RFC] Possible strategy cleanup for git add/remove/diff etc.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-04-21T04:04:44Z","receivedAt":"2005-04-21T04:04:44Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":">>>>> \"LT\" == Linus Torvalds <torvalds@osdl.org> writes:\n\nLT> And as you can see, the output matches \"diff-tree -r\" output (we always do\nLT> \"-r\", since the index is always fully populated). All the same rules: \"+\"  \nLT> means added file, \"-\" means removed file, and \"*\" means changed file. You \nLT> can trivially see that the above is a rename.\n\nI do not know if Pasky tools already have something like this\nalready, or not; but just FIY, here is what I use to extract a\n\"patch\" out of a working tree.\n\nUsage:\n\n    $ diff-tree -z [-r] ... | jit-diff-tree-helper [ | less ]\n    $ diff-cache -z ...     | jit-diff-tree-helper [ | less ]\n\nThis would be useful for the merge I described in my initial\nmessage in this thread to take a snapshot of what the user has\ndone since the last commit, to be applied on the result of the\nmerge.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n--- jit-diff-tree-helper\t2005-03-19 15:28:25.000000000 -0800\n+++ jit-diff-tree-helper\t2005-04-20 19:15:32.000000000 -0700\n@@ -0,0 +1,63 @@\n+#!/usr/bin/perl -w\n+\n+use strict;\n+use File::Temp qw(mkstemp);\n+\n+sub cat_file {\n+    my ($sha1, $file) = @_;\n+    unless (defined $sha1) { return \"/dev/null\"; }\n+    if ($sha1 =~ /^0{40}$/) {\n+\topen I, '<', $file;\n+    } else {\n+\tlocal $/; # slurp mode\n+\topen I, \"-|\", \"cat-file\", \"blob\", $sha1\n+\t    or die \"$0: cannot read $sha1\";\n+    }\n+    my ($o, $filename) = mkstemp(\",,jit-diff-tree-helperXXXXXX\");\n+    print $o join(\"\",<I>);\n+    close I\n+\tor die \"$0: closing cat-file pipe from $sha1\";\n+    close $o\n+\tor die \"$0: closing write fd to $filename\";\n+    return $filename;\n+}\n+$/ = \"\\0\";\n+my $rM = \"[0-7]+\";\n+my $rI = \"[0-9a-f]{40}\";\n+while (<STDIN>) {\n+    my ($old, $new, $file);\n+    chomp;\n+    if (/^\\+$rM\\tblob\\t($rI)\\t(.*)$/os) {\n+\t($old, $new, $file) = (undef, $1, $2);\n+    }\n+    elsif (/^-$rM\\tblob\\t($rI)\\t(.*)$/os) {\n+\t($old, $new, $file) = ($1, undef, $2);\n+    }\n+    elsif (/^\\*$rM->$rM\\tblob\\t($rI)->($rI)\\t(.*)$/os) {\n+\t($old, $new, $file) = ($1, $2, $3);\n+    }\n+    else {\n+\tchomp;\n+\tprint STDERR \"warning: $0: ignoring $_\\n\";\n+\tnext;\n+    }\n+    if (@ARGV) {\n+\tmy $matches = 0;\n+\tfor (@ARGV) {\n+\t    my $l = length($_);\n+\t    if ($file eq $_ ||\n+\t\t(substr($file, 0, $l) eq $_ &&\n+\t\t substr($file, $l, 1) eq \"/\")) {\n+\t\t$matches = 1;\n+\t\tlast;\n+\t    }\n+\t}\n+\tnext unless $matches;\n+    }\n+    $old = cat_file $old, $file;\n+    $new = cat_file $new, $file;\n+    system \"diff\", \"-L\", \"l/$file\", \"-L\", \"k/$file\", \"-pu\", $old, $new;\n+    for ($old, $new) {\n+\tunlink $_ if $_ ne '/dev/null';\n+    }\n+}\n\n\n"}]}