{"thread":{"id":"8687","subject":"[PATCH] cvsimport: test case for severe branch import problem","startedAt":"2007-06-23T12:51:47Z","lastAt":"2007-06-25T08:03:02Z","messageCount":20,"participants":["Steffen Prohaska","Johannes Schindelin","Alex Riesen","Johannes Sixt"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"45608","messageId":"11826031071213-git-send-email-prohaska@zib.de","threadId":"8687","inReplyTo":null,"subject":"[PATCH] cvsimport: test case for severe branch import problem","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-23T12:51:47Z","receivedAt":"2007-06-23T12:51:47Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"The result of importing branches using git-cvsimport depends\non the time of the first commit to the cvs branch. If the first\ncommit is done after other commits to the cvs trunk the result\nof cvsimport may be wrong. git-cvsimport creates a wrong history.\n\nThe problem is quite severe because merging such a wrongly imported\nbranch by git may be successful without reporting any problem but the\nresults are wrong. The result may differ from what a simple cvs merge\n(cvs up -j) yields.\n\nThis test script creates two cvs repositories and imports both to\ngit. The first cvs repository has the 'wrong' order of commits and\nyields an error. The result of a merge in cvs and a merge in git\ndiffers. The second cvs repository has the 'right' order and the\nimport to git runs as expected and merging yields the same results\nin cvs and git.\n\nThe conclusion is you must not rely on the existing cvsimport for\ntracking cvs branches. The history of such branches may be plain wrong.\nGit may display different patches than cvs would do. Merging cvs\ntopic branches may yield completely wrong results. One obvious thing\nthat may happen is that a merge reverts changes commited to the cvs\ntrunk before the first commit to the cvs branch. And this would happen\nwithout any indication by git. Everything would seem to run smoothely.\n\nThis conclusion should be stated in bold at appropriate places in\nthe documentation.\n\nIt's a pity because merging is what git is especially good at. But\nas long as git-cvsimport may create the wrong history you can't use\ngit to merge cvs topic branches without double checking every detail,\nwhich makes this approach unfeasable.\n\nSigned-off-by: Steffen Prohaska <prohaska@zib.de>\n---\n t/t9600-cvsimport.sh |  184 ++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 184 insertions(+), 0 deletions(-)\n create mode 100755 t/t9600-cvsimport.sh\n\ndiff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh\nnew file mode 100755\nindex 0000000..180cd8a\n--- /dev/null\n+++ b/t/t9600-cvsimport.sh\n@@ -0,0 +1,184 @@\n+\n+test_description='CVS import'\n+\n+. ./test-lib.sh\n+\n+cvs >/dev/null 2>&1\n+if test $? -ne 1\n+then\n+    test_expect_success 'skipping git-cvsimport tests, cvs not found' :\n+    test_done\n+    exit\n+fi\n+\n+CVSROOT=$(pwd)/cvsrootwrong\n+CVSWORK=$(pwd)/cvsworkwrong\n+CVSIMPORTED=$(pwd)/cvsimportedwrong\n+export CVSROOT CVSWORK\n+\n+cvspscache=\"$HOME/.cvsps/$(echo $CVSROOT | sed -e 's%/%#%g')#src\"\n+rm -f $cvspscache\n+\n+rm -rf \"$CVSROOT\" \"$CVSWORK\"\n+mkdir \"$CVSROOT\" &&\n+cvs init &&\n+mkdir \"$CVSROOT/src\"\n+cvs -Q co -d \"$CVSWORK\" src &&\n+rm -rf .git || \n+exit 1\n+\n+# sleeps are needed to fight cvsps' fuzz\n+test_expect_success \\\n+\t'initial cvs commits, tag, and branch' \\\n+\t'( cd \"$CVSWORK\" &&\n+\t   echo \"a: line 1\" >>a.txt &&\n+\t   cvs add a.txt &&\n+\t   cvs commit -m \"cvs commit 1\" &&\n+\t   sleep 2 &&\n+\t   echo \"a: line 2\" >>a.txt &&\n+\t   echo \"a: line 3\" >>a.txt &&\n+\t   echo \"a: line 4\" >>a.txt &&\n+\t   echo \"a: line 5\" >>a.txt &&\n+\t   cvs commit -m \"cvs commit 2\" &&\n+\t   cvs tag split &&\n+\t   cvs tag -b branch &&\n+\t   sleep 2\n+\t)'\n+\n+test_expect_success \\\n+\t'importing to git' \\\n+\t'git-cvsimport -v -a -i -k -u -z 1 -a -C $CVSIMPORTED -o cvshead src'\n+\n+test_expect_success \\\n+\t'cvs commit' \\\n+\t'( cd \"$CVSWORK\" &&\n+\t   echo \"a: line 6\" >>a.txt &&\n+\t   cvs commit -m \"cvs commit 2\" &&\n+\t   sleep 2\n+\t)'\n+\n+test_expect_success \\\n+\t'cvs commit' \\\n+\t'( cd \"$CVSWORK\" &&\n+\t   echo \"a: line 7\" >>a.txt &&\n+\t   cvs commit -m \"cvs commit 3\" &&\n+\t   sleep 2\n+\t)'\n+\n+test_expect_success \\\n+\t'importing to git' \\\n+\t'git-cvsimport -v -a -i -k -u -z 1 -a -C $CVSIMPORTED -o cvshead src'\n+\n+test_expect_success \\\n+\t'cvs commit' \\\n+\t'( cd \"$CVSWORK\" &&\n+\t   cvs up -r branch &&\n+\t   sed -e 's/2/B/' a.txt >t &&\n+\t   mv t a.txt &&\n+\t   cvs commit -m \"cvs commit on branch\" &&\n+\t   cvs up -A &&\n+\t   sleep 2\n+\t)'\n+\n+test_expect_success \\\n+\t'importing to git' \\\n+\t'git-cvsimport -v -a -i -k -u -z 1 -a -C $CVSIMPORTED -o cvshead src'\n+\n+test_expect_success \\\n+\t'merging' \\\n+    '( cd \"$CVSWORK\" &&\n+\t   cvs up -j branch\n+\t ) &&\n+\t (\n+\t   cd \"$CVSIMPORTED\" &&\n+\t   git-checkout cvshead &&\n+\t   git-merge branch\n+\t ) &&\n+\t diff -q \"$CVSWORK/a.txt\" \"$CVSIMPORTED/a.txt\"'\n+\n+CVSROOT=$(pwd)/cvsrootright\n+CVSWORK=$(pwd)/cvsworkright\n+CVSIMPORTED=$(pwd)/cvsimportedright\n+export CVSROOT CVSWORK\n+\n+cvspscache=\"$HOME/.cvsps/$(echo $CVSROOT | sed -e 's%/%#%g')#src\"\n+rm -f $cvspscache\n+\n+rm -rf \"$CVSROOT\" \"$CVSWORK\"\n+mkdir \"$CVSROOT\" &&\n+cvs init &&\n+mkdir \"$CVSROOT/src\"\n+cvs -Q co -d \"$CVSWORK\" src &&\n+rm -rf .git || \n+exit 1\n+\n+# sleeps are needed to fight cvsps' fuzz\n+test_expect_success \\\n+\t'initial cvs commits, tag, and branch' \\\n+\t'( cd \"$CVSWORK\" &&\n+\t   echo \"a: line 1\" >>a.txt &&\n+\t   cvs add a.txt &&\n+\t   cvs commit -m \"cvs commit 1\" &&\n+\t   sleep 2 &&\n+\t   echo \"a: line 2\" >>a.txt &&\n+\t   echo \"a: line 3\" >>a.txt &&\n+\t   echo \"a: line 4\" >>a.txt &&\n+\t   echo \"a: line 5\" >>a.txt &&\n+\t   cvs commit -m \"cvs commit 2\" &&\n+\t   cvs tag split &&\n+\t   cvs tag -b branch &&\n+\t   sleep 2\n+\t)'\n+\n+test_expect_success \\\n+\t'importing to git' \\\n+\t'git-cvsimport -v -a -i -k -u -z 1 -a -C $CVSIMPORTED -o cvshead src'\n+\n+test_expect_success \\\n+\t'cvs commit' \\\n+\t'( cd \"$CVSWORK\" &&\n+\t   cvs up -r branch &&\n+\t   sed -e 's/2/B/' a.txt >t &&\n+\t   mv t a.txt &&\n+\t   cvs commit -m \"cvs commit on branch\" &&\n+\t   cvs up -A &&\n+\t   sleep 2\n+\t)'\n+\n+test_expect_success \\\n+\t'importing to git' \\\n+\t'git-cvsimport -v -a -i -k -u -z 1 -a -C $CVSIMPORTED -o cvshead src'\n+\n+test_expect_success \\\n+\t'cvs commit' \\\n+\t'( cd \"$CVSWORK\" &&\n+\t   echo \"a: line 6\" >>a.txt &&\n+\t   cvs commit -m \"cvs commit 2\" &&\n+\t   sleep 2\n+\t)'\n+\n+test_expect_success \\\n+\t'cvs commit' \\\n+\t'( cd \"$CVSWORK\" &&\n+\t   echo \"a: line 7\" >>a.txt &&\n+\t   cvs commit -m \"cvs commit 3\" &&\n+\t   sleep 2\n+\t)'\n+\n+test_expect_success \\\n+\t'importing to git' \\\n+\t'git-cvsimport -v -a -i -k -u -z 1 -a -C $CVSIMPORTED -o cvshead src'\n+\n+test_expect_success \\\n+\t'merging' \\\n+    '( cd \"$CVSWORK\" &&\n+\t   cvs up -j branch\n+\t ) &&\n+\t (\n+\t   cd \"$CVSIMPORTED\" &&\n+\t   git-checkout cvshead &&\n+\t   git-merge branch\n+\t ) &&\n+\t diff -q \"$CVSWORK/a.txt\" \"$CVSIMPORTED/a.txt\"'\n+\n+test_done\n-- \n1.5.2.2.315.gc649a\n"},{"id":"45610","messageId":"1BD13366-B4BD-4630-9046-49567A345CBC@zib.de","threadId":"8687","inReplyTo":"11826031071213-git-send-email-prohaska@zib.de","subject":"Re: [PATCH] cvsimport: test case for severe branch import problem","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-23T13:26:08Z","receivedAt":"2007-06-23T13:26:08Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"\nOn Jun 23, 2007, at 2:51 PM, Steffen Prohaska wrote:\n\n> The conclusion is you must not rely on the existing cvsimport for\n> tracking cvs branches. The history of such branches may be plain  \n> wrong.\n> Git may display different patches than cvs would do. Merging cvs\n> topic branches may yield completely wrong results. One obvious thing\n> that may happen is that a merge reverts changes commited to the cvs\n> trunk before the first commit to the cvs branch. And this would happen\n> without any indication by git. Everything would seem to run smoothely.\n\nThis is not only a theoretical problem but I am experiencing it on a\nreal-world repository right now. I have a topic branch that branches\noff from the wrong commit in git.\n\nIs there an easy way to fix this? I know the right commit the branch\nshould have as its parent. How can I move it there? git-rebase is not  \nthe\nright command because the patches derived from my branch are already  \nwrong.\nI would only need to attach the first commit to a different parent.\n\n\tSteffen\n"},{"id":"45618","messageId":"11826268772950-git-send-email-prohaska@zib.de","threadId":"8687","inReplyTo":"1BD13366-B4BD-4630-9046-49567A345CBC@zib.de","subject":"[PATCH] transplant: move a series of commits to a different parent","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-23T19:27:57Z","receivedAt":"2007-06-23T19:27:57Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"git-transplant.sh <onto> <from> <to>\n\ntransplant starts with the contents of <onto> and puts on top of\nit the contents of files if they are touched by the series of\ncommits <from>..<to>.  If a commit touches a file the content of\nthis file is taken as it is in the commit. No merging is\nperformed. Original authors, commiters, and commit messages are\npreserved.\n\nWarning: this is just a quick hack to solve _my_ problem.\n- No error checking is performed.\n- Removal of files is not handled.\n- Whitespace in filename is not handled.\n- The index is left in dirty state.\n- No branch is created for the result.\n- The script is not integrated with git's shell utilities.\n\nSigned-off-by: Steffen Prohaska <prohaska@zib.de>\n---\n git-transplant.sh |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 60 insertions(+), 0 deletions(-)\n create mode 100755 git-transplant.sh\n\nThis script seems to solved the problem for me. I can place\nthe topic branch imported from cvs to the right place.\n\nWhat do you think? Is this a sane way to handle the situation?\n\n    Steffen\n\ndiff --git a/git-transplant.sh b/git-transplant.sh\nnew file mode 100755\nindex 0000000..3320071\n--- /dev/null\n+++ b/git-transplant.sh\n@@ -0,0 +1,60 @@\n+#!/bin/sh\n+\n+[[ $# == 3 ]] || { echo \"$0 <onto> <from> <to>\"; exit 1; }\n+onto=$(git-rev-parse $1)\n+from=$(git-rev-parse $2)\n+to=$(git-rev-parse $3)\n+\n+# copied from git-filter-branch.sh \n+set_ident () {\n+    lid=\"$(echo \"$1\" | tr \"A-Z\" \"a-z\")\"\n+    uid=\"$(echo \"$1\" | tr \"a-z\" \"A-Z\")\"\n+    pick_id_script='\n+        /^'$lid' /{\n+            s/'\\''/'\\''\\\\'\\'\\''/g\n+            h\n+            s/^'$lid' \\([^<]*\\) <[^>]*> .*$/\\1/\n+            s/'\\''/'\\''\\'\\'\\''/g\n+            s/.*/export GIT_'$uid'_NAME='\\''&'\\''/p\n+\n+            g\n+            s/^'$lid' [^<]* <\\([^>]*\\)> .*$/\\1/\n+            s/'\\''/'\\''\\'\\'\\''/g\n+            s/.*/export GIT_'$uid'_EMAIL='\\''&'\\''/p\n+\n+            g\n+            s/^'$lid' [^<]* <[^>]*> \\(.*\\)$/\\1/\n+            s/'\\''/'\\''\\'\\'\\''/g\n+            s/.*/export GIT_'$uid'_DATE='\\''&'\\''/p\n+\n+            q\n+        }\n+    '\n+\n+    LANG=C LC_ALL=C sed -ne \"$pick_id_script\"\n+    # Ensure non-empty id name.\n+    echo \"[ -n \\\"\\$GIT_${uid}_NAME\\\" ] || export GIT_${uid}_NAME=\\\"\\${GIT_${uid}_EMAIL%%@*}\\\"\"\n+}\n+\n+parent=$onto\n+git-read-tree --reset $parent\n+\n+for commit in $(git-rev-list --reverse $from..$to)\n+do\n+    echo \"rewriting commit $commit...\"\n+    git-diff-tree -r $commit | grep ^: | cut -b 9-15,57-97,100- |\n+    while read mode sha path \n+    do\n+        echo \" $mode $sha $path\"\n+        git-update-index --add --cacheinfo $mode $sha $path\n+    done\n+\n+    eval \"$(git-cat-file commit $commit |set_ident AUTHOR)\"\n+    eval \"$(git-cat-file commit $commit |set_ident COMMITTER)\"\n+\n+    parent=$(git-cat-file commit $commit |  sed -e '1,/^$/d' | git-commit-tree $(git-write-tree) -p $parent)\n+    echo \"... new commit $parent\"\n+done\n+\n+echo \"\"\n+echo \"new head is $parent\"\n-- \n1.5.2.2.315.gc649a\n"},{"id":"45624","messageId":"Pine.LNX.4.64.0706232151480.4059@racer.site","threadId":"8687","inReplyTo":"11826268772950-git-send-email-prohaska@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-23T20:54:30Z","receivedAt":"2007-06-23T20:54:30Z","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, Steffen Prohaska wrote:\n\n> git-transplant.sh <onto> <from> <to>\n> \n> transplant starts with the contents of <onto> and puts on top of\n> it the contents of files if they are touched by the series of\n> commits <from>..<to>.\n\nThis reeks of rebase.\n\nIOW, I suspect that it does almost the same as\n\n\tgit checkout <to>\n\tgit rebase -s ours --onto <onto> <from>^\n\nCiao,\nDscho\n"},{"id":"45625","messageId":"81b0412b0706231404hc8b4bc4xd5bc51c733d8ea69@mail.gmail.com","threadId":"8687","inReplyTo":"11826268772950-git-send-email-prohaska@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-06-23T21:04:51Z","receivedAt":"2007-06-23T21:04:51Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"On 6/23/07, Steffen Prohaska <prohaska@zib.de> wrote:\n> git-transplant.sh <onto> <from> <to>\n>\n> transplant starts with the contents of <onto> and puts on top of\n> it the contents of files if they are touched by the series of\n> commits <from>..<to>.  If a commit touches a file the content of\n> this file is taken as it is in the commit. No merging is\n> performed. Original authors, commiters, and commit messages are\n> preserved.\n>\n> Warning: this is just a quick hack to solve _my_ problem.\n> - No error checking is performed.\n> - Removal of files is not handled.\n> - Whitespace in filename is not handled.\n> - The index is left in dirty state.\n> - No branch is created for the result.\n> - The script is not integrated with git's shell utilities.\n\n# detached head\ngit checkout $(git rev-parse onto) && git format-patch --stdout\n--full-index from..to|git am -3\n"},{"id":"45643","messageId":"F2287087-FE29-4858-A273-49BC5A1C2894@zib.de","threadId":"8687","inReplyTo":"Pine.LNX.4.64.0706232151480.4059@racer.site","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-24T06:55:35Z","receivedAt":"2007-06-24T06:55:35Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"\nOn Jun 23, 2007, at 10:54 PM, Johannes Schindelin wrote:\n\n>> git-transplant.sh <onto> <from> <to>\n>>\n>> transplant starts with the contents of <onto> and puts on top of\n>> it the contents of files if they are touched by the series of\n>> commits <from>..<to>.\n>\n> This reeks of rebase.\n>\n> IOW, I suspect that it does almost the same as\n>\n> \tgit checkout <to>\n> \tgit rebase -s ours --onto <onto> <from>^\n\nIt doesn't do anything useful for me. In fact it seems as if it\ndid nothing.\n\nI tried your proposal:\n  - rebase says 'Changes from <onto> to <onto>',\n  - then it rewinds to <onto>,\n  - next it says several time 'Already applied: ...' with increasing\n    patch numbers,\n  - then 'All done',\n  - The result is the same as if I executed 'git reset --hard <onto>'.\n\nI thought about something similar before I wrote transplant.\nHonestly, I didn't understand what rebase would do combined with ours.\n\n\tSteffen\n"},{"id":"45645","messageId":"9C4338DC-0C4A-4981-9EC9-2417513F629D@zib.de","threadId":"8687","inReplyTo":"81b0412b0706231404hc8b4bc4xd5bc51c733d8ea69@mail.gmail.com","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-24T07:08:16Z","receivedAt":"2007-06-24T07:08:16Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"\nOn Jun 23, 2007, at 11:04 PM, Alex Riesen wrote:\n\n>> git-transplant.sh <onto> <from> <to>\n>>\n>> transplant starts with the contents of <onto> and puts on top of\n>> it the contents of files if they are touched by the series of\n>> commits <from>..<to>.  If a commit touches a file the content of\n>> this file is taken as it is in the commit. No merging is\n>> performed. Original authors, commiters, and commit messages are\n>> preserved.\n>>\n> [...]\n> # detached head\n> git checkout $(git rev-parse onto) && git format-patch --stdout\n> --full-index from..to|git am -3\n\nNo. This one tries to apply the _changes_ between from..to. What I\nneed is the resulting _content_ of files modified between from..to.\n\nThe _changes_ are already wrong because they are relative to the\nhistory. But the history was messed up by git-cvsimport, as I tried to\nexplaine in my first mail in this thread. So the changes derived\nfrom the wrong history are useless.\n\ntransplant only checks if a file is modified by a commit. If it is\nit takes the _content_ of the file in that commit. The changes from\nthe parent commit, which you can find by format-patch, do not matter.\n\nI believe it's more like git-filter-branch, but I wasn't yet abel to\ntell git-filter-branch how to do the job.\n\n\tSteffen\n"},{"id":"45649","messageId":"20070624082027.GA2467@steel.home","threadId":"8687","inReplyTo":"9C4338DC-0C4A-4981-9EC9-2417513F629D@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-06-24T08:20:27Z","receivedAt":"2007-06-24T08:20:27Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Steffen Prohaska, Sun, Jun 24, 2007 09:08:16 +0200:\n> \n> On Jun 23, 2007, at 11:04 PM, Alex Riesen wrote:\n> \n> >>git-transplant.sh <onto> <from> <to>\n> >>\n> >>transplant starts with the contents of <onto> and puts on top of\n> >>it the contents of files if they are touched by the series of\n> >>commits <from>..<to>.  If a commit touches a file the content of\n> >>this file is taken as it is in the commit. No merging is\n> >>performed. Original authors, commiters, and commit messages are\n> >>preserved.\n> >>\n> >[...]\n> ># detached head\n> >git checkout $(git rev-parse onto) && git format-patch --stdout\n> >--full-index from..to|git am -3\n> \n> No. This one tries to apply the _changes_ between from..to. What I\n> need is the resulting _content_ of files modified between from..to.\n> \n\nAch, yes. I should have read your message a bit more closely. There is\na term for that \"contents of files\", BTW: \"repository state\".\n\n> I believe it's more like git-filter-branch, but I wasn't yet abel to\n> tell git-filter-branch how to do the job.\n\nI suspect git-filter-branch can be both.\n"},{"id":"45650","messageId":"20070624082906.GB2467@steel.home","threadId":"8687","inReplyTo":"11826268772950-git-send-email-prohaska@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-06-24T08:29:06Z","receivedAt":"2007-06-24T08:29:06Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Steffen Prohaska, Sat, Jun 23, 2007 21:27:57 +0200:\n> +for commit in $(git-rev-list --reverse $from..$to)\n> +do\n> +    echo \"rewriting commit $commit...\"\n> +    git-diff-tree -r $commit | grep ^: | cut -b 9-15,57-97,100- |\n> +    while read mode sha path \n> +    do\n> +        echo \" $mode $sha $path\"\n> +        git-update-index --add --cacheinfo $mode $sha $path\n> +    done\n\nWhy not just read-tree for every commit? It is not like you're\nmodifying the repository in any way, just changing parenthood. That'd\nsolve the problem with deletions.\nSo it should be enough to read-tree the repo state for each and every\nsource commit into the index (and you can just use a temporary index\nfile for that, see GIT_INDEX_FILE). Than just commit the index.\n"},{"id":"45653","messageId":"4D530871-0CBE-4F9A-99B7-ECF0F4656D85@zib.de","threadId":"8687","inReplyTo":"20070624082906.GB2467@steel.home","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-24T09:05:29Z","receivedAt":"2007-06-24T09:05:29Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"\nOn Jun 24, 2007, at 10:29 AM, Alex Riesen wrote:\n\n> Steffen Prohaska, Sat, Jun 23, 2007 21:27:57 +0200:\n>> +for commit in $(git-rev-list --reverse $from..$to)\n>> +do\n>> +    echo \"rewriting commit $commit...\"\n>> +    git-diff-tree -r $commit | grep ^: | cut -b 9-15,57-97,100- |\n>> +    while read mode sha path\n>> +    do\n>> +        echo \" $mode $sha $path\"\n>> +        git-update-index --add --cacheinfo $mode $sha $path\n>> +    done\n>\n> Why not just read-tree for every commit? It is not like you're\n> modifying the repository in any way, just changing parenthood. That'd\n> solve the problem with deletions.\n> So it should be enough to read-tree the repo state for each and every\n> source commit into the index (and you can just use a temporary index\n> file for that, see GIT_INDEX_FILE). Than just commit the index.\n\nI am changing the repository.\n\nI only modify the index for files that have changes in $commit. Their\ncontent gets replaced by the content from the commit. I'm leaving\nall other files untouched.\n\nThis creates a new series of commits that starts from the repository\nstate of <onto> and has mixed in files only if they are changed in\nthe series of commits from..to. These files are just replaced. I'm not\ntrying to merge changes but just replace the whole file.\n\nOpposed to that, read-tree would modify the content of _all_ files.\n\n\nHere's the situation before transplant\n\n           o--Y--3\n          /\n   1--X--2--o--o--o\n\nSay at X the file x.txt got modified. At Y the file y.txt got modified.\n3 has both modifications.\n\nNow I do transplant 1 2 3, which yields\n\n     o--Y--4\n    /\n   1--X--2--o--o--o\n\ny.txt is identical in 3 and 4 but x.txt is identical in 1 (!) and 4.\nHence 3 and 4 are different. The changes to x.txt in commit X got\neliminated from the history. 4 is a mixture of 1 and the repository\nstate of files at 3 that got modified between 2 and 3. Changes between\n1 and 2 got eliminated from the history.\n\nThis is exactly what I want to achieve. The content of the files on\nbranch 3 is correct for all files that were committed after 2. But\nbecause 2 is the wrong branching point all the content originating\nfrom commits between 1 and 2 is wrong. Files committed between\n2 and 3 have the right content but the branch needs to be attached\nat 1.\n\n\tSteffen\n"},{"id":"45654","messageId":"20070624093017.GC2467@steel.home","threadId":"8687","inReplyTo":"4D530871-0CBE-4F9A-99B7-ECF0F4656D85@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-06-24T09:30:17Z","receivedAt":"2007-06-24T09:30:17Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Steffen Prohaska, Sun, Jun 24, 2007 11:05:29 +0200:\n> >Steffen Prohaska, Sat, Jun 23, 2007 21:27:57 +0200:\n> >>+for commit in $(git-rev-list --reverse $from..$to)\n> >>+do\n> >>+    echo \"rewriting commit $commit...\"\n> >>+    git-diff-tree -r $commit | grep ^: | cut -b 9-15,57-97,100- |\n> >>+    while read mode sha path\n> >>+    do\n> >>+        echo \" $mode $sha $path\"\n> >>+        git-update-index --add --cacheinfo $mode $sha $path\n> >>+    done\n> >\n> >Why not just read-tree for every commit? It is not like you're\n> >modifying the repository in any way, just changing parenthood. That'd\n> >solve the problem with deletions.\n> >So it should be enough to read-tree the repo state for each and every\n> >source commit into the index (and you can just use a temporary index\n> >file for that, see GIT_INDEX_FILE). Than just commit the index.\n> \n> I am changing the repository.\n> \n\nNo, you don't.\n\n> I only modify the index for files that have changes in $commit. Their\n> content gets replaced by the content from the commit. I'm leaving\n> all other files untouched.\n\nNo, you don't modify anything. Ever tried to run git-status after your\nscript finished? Tried to understand what the output means?\n\n> This creates a new series of commits that starts from the repository\n> state of <onto> and has mixed in files only if they are changed in\n> the series of commits from..to. These files are just replaced. I'm not\n> trying to merge changes but just replace the whole file.\n> \n> Opposed to that, read-tree would modify the content of _all_ files.\n\nNo, it wouldn't (unless you run git-read-tree -u, and I fail to see\nwhy would you want that). You probably confuse git-read-tree with\ngit-checkout-index.\n\n> This is exactly what I want to achieve. The content of the files on\n> branch 3 is correct for all files that were committed after 2. But\n> because 2 is the wrong branching point all the content originating\n> from commits between 1 and 2 is wrong. Files committed between\n> 2 and 3 have the right content but the branch needs to be attached\n> at 1.\n\nThis misses merges (see git-rev-list --parents), but does the job for\nlinear history:\n\n    export GIT_INDEX_FILE=\"$(git rev-parse --git-dir)/tr.idx\"\n    parent=$(git rev-parse \"$onto\")\n    git rev-list --reverse \"$from..$to\" | while read c\n    do\n\trm -f \"$GIT_INDEX_FILE\"\n\tgit read-tree $c || break;\n\t# Authorship information here\n\tparent=$(git cat-file commit $c | \\\n\t    sed -e '1,/^$/d' | \\\n\t    git commit-tree $(git write-tree) -p \"$parent\")\n\techo \"Commit $parent\"\n    done\n"},{"id":"45660","messageId":"Pine.LNX.4.64.0706241123170.4059@racer.site","threadId":"8687","inReplyTo":"9C4338DC-0C4A-4981-9EC9-2417513F629D@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-06-24T10:26:11Z","receivedAt":"2007-06-24T10:26:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 24 Jun 2007, Steffen Prohaska wrote:\n\n> On Jun 23, 2007, at 11:04 PM, Alex Riesen wrote:\n> \n> > # detached head\n> > git checkout $(git rev-parse onto) && git format-patch --stdout\n> > --full-index from..to|git am -3\n> \n> No. This one tries to apply the _changes_ between from..to. What I need \n> is the resulting _content_ of files modified between from..to.\n\nOh! But the commit messages do no longer correspond to their patches, do \nthey?\n\nExample:\n\nIn \"onto\", you have a sorely needed bugfix in main.c. In \"from\", you have \nnot. Then you do your transplant, and all of a sudden, the \nfirst transplanted commit _undoes_ that bugfix (because you take the \ncontents at face value), but the commit message _cannot_ say so, or even \nwhy.\n\nIMHO this makes no sense (and that is why I misunderstood it as being a \nrebase).\n\nCiao,\nDscho\n"},{"id":"45661","messageId":"B1169070-341A-444A-ACCE-872C5E00A05A@zib.de","threadId":"8687","inReplyTo":"Pine.LNX.4.64.0706241123170.4059@racer.site","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-24T10:45:00Z","receivedAt":"2007-06-24T10:45:00Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"\nOn Jun 24, 2007, at 12:26 PM, Johannes Schindelin wrote:\n\n>\n> Oh! But the commit messages do no longer correspond to their  \n> patches, do\n> they?\n>\n> Example:\n>\n> In \"onto\", you have a sorely needed bugfix in main.c. In \"from\",  \n> you have\n> not. Then you do your transplant, and all of a sudden, the\n> first transplanted commit _undoes_ that bugfix (because you take the\n> contents at face value), but the commit message _cannot_ say so, or  \n> even\n> why.\n>\n> IMHO this makes no sense (and that is why I misunderstood it as  \n> being a\n> rebase).\n\nIt doesn't make sense on a sane repository.\n\nI need the script to fix an insane, broken repository that was generated\nby git-cvsimport [1]. cvsimport created commits attached to the wrong  \nparent\nin the first place. So the patches derived from this history are wrong.\nThey are different from the patches that you'd expect from the cvs  \nrepository.\nThe commit messages and the patches did never correspond. My script  \nfixes\nthis relationship. Only after I transplanted the branch the commits and\ntheir messages match.\n\nThe following is a different illustration of the same problem.  \nSuppose you copy\na file and modify its contents somewhere else. Now you checkout the  \n_wrong_\nbranch, but do not recognize, copy the file back and commit it. You'd be\nwriting a message as if the right branch would have been checked out.  \nThis\nmessage describes what you believe you did. But because of the wrong  \nbranch\nyou may have done something completely different to the repository.  \nYou may\nfor example have reverted earlier changes. Your commit doesn't make  \nany sense\nbefore you transplanted it to the right branch. You don't want to  \napply a\npatch to the right branch but transplant the content of your file.\n\n\tSteffen\n\n[1] http://article.gmane.org/gmane.comp.version-control.git/50736\n"},{"id":"45676","messageId":"EA80EB51-97F9-4C5F-BEF7-C8C1BF39B8D2@zib.de","threadId":"8687","inReplyTo":"20070624093017.GC2467@steel.home","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-24T17:13:42Z","receivedAt":"2007-06-24T17:13:42Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"\nOn Jun 24, 2007, at 11:30 AM, Alex Riesen wrote:\n\n>> I am changing the repository.\n>>\n>\n> No, you don't.\n\nMagically, the script solved my problem by creating a new, corrected\nbranch that is different from the original one. I didn't run any\nother script. I promise, I ran the script that I sent in the patch.\n\n\n>> I only modify the index for files that have changes in $commit. Their\n>> content gets replaced by the content from the commit. I'm leaving\n>> all other files untouched.\n>\n> No, you don't modify anything. Ever tried to run git-status after your\n> script finished?\n\nYes.\n\n> Tried to understand what the output means?\n\nNot really. I instead reset the index to a controlled state.\n\n\tSteffen\n"},{"id":"45682","messageId":"20070624183552.GA2374@steel.home","threadId":"8687","inReplyTo":"EA80EB51-97F9-4C5F-BEF7-C8C1BF39B8D2@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-06-24T18:35:52Z","receivedAt":"2007-06-24T18:35:52Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Steffen Prohaska, Sun, Jun 24, 2007 19:13:42 +0200:\n> >>I am changing the repository.\n> >\n> >No, you don't.\n> \n> Magically, the script solved my problem by creating a new, corrected\n> branch that is different from the original one. I didn't run any\n> other script. I promise, I ran the script that I sent in the patch.\n\nI meant: \"it does not change the working directory\". It is irrelevant,\nwas a bit of confusion on my part.\n\nYour script works, just it can be made simplier: no need for diff,\nit'll only hurt perfomance and complicates things. And you don't have\nto care about additions/deletions, and it is trivially extensible to\nsupport merges, and the current index is untouched - your user can\ncontinue working in predictable environment.\n\nAs to perfomance: read-tree doesn't actually _read_ the blobs to\npopulate index, just the trees. And diff-tree has do do the same, but\nalso _compare_ two trees recursively: more work, more memory needed.\n\nBTW, Johannes moved that ident code you copied from git-filter-branch\ninto its own shell file, so it can be sourced and trivially reused.\n"},{"id":"45692","messageId":"6382A127-7A81-4ADE-B2C1-1FCCBE9EA239@zib.de","threadId":"8687","inReplyTo":"20070624183552.GA2374@steel.home","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-24T20:54:21Z","receivedAt":"2007-06-24T20:54:21Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"\nOn Jun 24, 2007, at 8:35 PM, Alex Riesen wrote:\n\n> Your script works, just it can be made simplier: no need for diff,\n> it'll only hurt perfomance and complicates things. And you don't have\n> to care about additions/deletions, and it is trivially extensible to\n> support merges, and the current index is untouched - your user can\n> continue working in predictable environment.\n\nI understand that I can leave the default index untouched by using\na different index. I knew that this must be possible somehow but was\ntoo lazy to find out how. Thanks for the details.\n\nI don't see how I can avoid tree diffs. As I pointed out earlier I need\nto mix the tree of the base commit of the newly built branch with\nfiles that were changed in the series of commits that I'm transplanting.\n\nJust taking the whole tree from the commits I'm transplanting is  \n_wrong_.\nI need to only take files that were touched by a commit. The tree of\nthe tip of the resulting branch can be quite different from the tree\nof the tip of the original branch.\n\n> As to perfomance: read-tree doesn't actually _read_ the blobs to\n> populate index, just the trees. And diff-tree has do do the same, but\n> also _compare_ two trees recursively: more work, more memory needed.\n\nI'm still pretty convinced that I need tree diffs (not file diffs!).\n\n> BTW, Johannes moved that ident code you copied from git-filter-branch\n> into its own shell file, so it can be sourced and trivially reused.\n\nI see, thanks.\n\nAnyway, the script worked for me and I still think it may be useful for\nfixing broken repositories resulting from a wrong cvsimport. I would\nprobably improve many details if someone else considered my work useful.\nBut up to now it seems as if I failed to explain, why the script would\nbe needed in the first place.\n\nHowever, the best way would be to fix git-cvsimport to handle branches\ncorrectly independently of the time of the first commit to a branch;\nand avoid insane, broken repositories altogether.\n\n\tSteffen\n"},{"id":"45701","messageId":"20070624222039.GB2374@steel.home","threadId":"8687","inReplyTo":"6382A127-7A81-4ADE-B2C1-1FCCBE9EA239@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-06-24T22:20:39Z","receivedAt":"2007-06-24T22:20:39Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Steffen Prohaska, Sun, Jun 24, 2007 22:54:21 +0200:\n> I don't see how I can avoid tree diffs. As I pointed out earlier I need\n> to mix the tree of the base commit of the newly built branch with\n> files that were changed in the series of commits that I'm transplanting.\n\nHmm...\n\n> Just taking the whole tree from the commits I'm transplanting is  \n> _wrong_.\n> I need to only take files that were touched by a commit. The tree of\n> the tip of the resulting branch can be quite different from the tree\n> of the tip of the original branch.\n\nI see.\n\n> >As to perfomance: read-tree doesn't actually _read_ the blobs to\n> >populate index, just the trees. And diff-tree has do do the same, but\n> >also _compare_ two trees recursively: more work, more memory needed.\n> \n> I'm still pretty convinced that I need tree diffs (not file diffs!).\n\n\"git-read-tree --reset\" does an in-index merge (just discards unmerged\nentries), so it still is better then git-diff-tree. But remove that\nunlink, so that the previuos tree is not discarded and do a\n\"git-read-tree $onto\" before starting the loop.\n\n> Anyway, the script worked for me and I still think it may be useful for\n> fixing broken repositories resulting from a wrong cvsimport. I would\n> probably improve many details if someone else considered my work useful.\n> But up to now it seems as if I failed to explain, why the script would\n> be needed in the first place.\n> \n> However, the best way would be to fix git-cvsimport to handle branches\n> correctly independently of the time of the first commit to a branch;\n> and avoid insane, broken repositories altogether.\n\nYou still better make it work properly wrt deleted files.\nAnd you have to be careful not to hit a real content conflict.\n"},{"id":"45744","messageId":"467F6BB0.4C7F041E@eudaptics.com","threadId":"8687","inReplyTo":"9C4338DC-0C4A-4981-9EC9-2417513F629D@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Johannes Sixt","fromEmail":"j.sixt@eudaptics.com","sentAt":"2007-06-25T07:16:00Z","receivedAt":"2007-06-25T07:16:00Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Steffen Prohaska wrote:\n> \n> On Jun 23, 2007, at 11:04 PM, Alex Riesen wrote:\n> \n> >> git-transplant.sh <onto> <from> <to>\n> >>\n> >> transplant starts with the contents of <onto> and puts on top of\n> >> it the contents of files if they are touched by the series of\n> >> commits <from>..<to>.  If a commit touches a file the content of\n> >> this file is taken as it is in the commit. No merging is\n> >> performed. Original authors, commiters, and commit messages are\n> >> preserved.\n> >>\n> > [...]\n> > # detached head\n> > git checkout $(git rev-parse onto) && git format-patch --stdout\n> > --full-index from..to|git am -3\n> \n> No. This one tries to apply the _changes_ between from..to. What I\n> need is the resulting _content_ of files modified between from..to.\n\nInstall a graft that points the parent of \"from\" to \"onto\", then run\ngit-filter-branch. Like so:\n\n $ echo \"$(git-rev-parse from^0) $(git-rev-parse onto^0)\" \\\n        > .git/info/grafts\n $ git-filter-branch new-to onto..to\n\n(no filters necessary).\n\n-- Hannes\n"},{"id":"45747","messageId":"21FC6948-6969-47A4-9D6E-EFD9D98FC361@zib.de","threadId":"8687","inReplyTo":"467F6BB0.4C7F041E@eudaptics.com","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2007-06-25T07:49:45Z","receivedAt":"2007-06-25T07:49:45Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"\nOn Jun 25, 2007, at 9:16 AM, Johannes Sixt wrote:\n\n> Steffen Prohaska wrote:\n>>\n>> On Jun 23, 2007, at 11:04 PM, Alex Riesen wrote:\n>>\n>>>> git-transplant.sh <onto> <from> <to>\n>>>>\n>>>> transplant starts with the contents of <onto> and puts on top of\n>>>> it the contents of files if they are touched by the series of\n>>>> commits <from>..<to>.  If a commit touches a file the content of\n>>>> this file is taken as it is in the commit. No merging is\n>>>> performed. Original authors, commiters, and commit messages are\n>>>> preserved.\n>>>>\n>>> [...]\n>>> # detached head\n>>> git checkout $(git rev-parse onto) && git format-patch --stdout\n>>> --full-index from..to|git am -3\n>>\n>> No. This one tries to apply the _changes_ between from..to. What I\n>> need is the resulting _content_ of files modified between from..to.\n>\n> Install a graft that points the parent of \"from\" to \"onto\", then run\n> git-filter-branch. Like so:\n\nI stumbled over the grafts yesterday and thought that this may be a  \nway...\n\n>  $ echo \"$(git-rev-parse from^0) $(git-rev-parse onto^0)\" \\\n>     > .git/info/grafts\n>  $ git-filter-branch new-to onto..to\n\n... ok this is interesting: git-filter-branch will take the info from\ngrafts to create its new commits. The new commits will contain all\ninformation independent of the grafts file, right?\n\n... but, I'm not convinced that this is what I need. I need to mix the\ntree of onto with the files _modified_ between from..to. Taking _all_\nfiles from the commits between from..to is _wrong_. And I think that\nis what the command you proposed would do.\n\nMy feeling is that repairing a repository resulting from a broken\ncvsimport is a bit more complex than what the existing tools provide\nout-of-the box.\n\n\tSteffen\n"},{"id":"45753","messageId":"467F76B6.2D814459@eudaptics.com","threadId":"8687","inReplyTo":"21FC6948-6969-47A4-9D6E-EFD9D98FC361@zib.de","subject":"Re: [PATCH] transplant: move a series of commits to a different parent","fromName":"Johannes Sixt","fromEmail":"j.sixt@eudaptics.com","sentAt":"2007-06-25T08:03:02Z","receivedAt":"2007-06-25T08:03:02Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Steffen Prohaska wrote:\n> \n> On Jun 25, 2007, at 9:16 AM, Johannes Sixt wrote:\n> >  $ echo \"$(git-rev-parse from^0) $(git-rev-parse onto^0)\" \\\n> >     > .git/info/grafts\n> >  $ git-filter-branch new-to onto..to\n> \n> ... ok this is interesting: git-filter-branch will take the info from\n> grafts to create its new commits. The new commits will contain all\n> information independent of the grafts file, right?\n\nYes.\n\n> ... but, I'm not convinced that this is what I need. I need to mix the\n> tree of onto with the files _modified_ between from..to. Taking _all_\n> files from the commits between from..to is _wrong_. And I think that\n> is what the command you proposed would do.\n\nMy proposed commands won't do what you need. They just change\nparenthood, but not the tree snapshots that they represent.\n\n-- Hannes\n"}]}