{"thread":{"id":"2654","subject":"[PATCH] Add git-graft-ripple, a tool for permanently grafting history into a tree.","startedAt":"2005-11-22T20:50:50Z","lastAt":"2005-11-23T17:07:14Z","messageCount":7,"participants":["Ryan Anderson","Linus Torvalds","Andreas Ericsson","Randal L. Schwartz","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"12567","messageId":"11326926501602-git-send-email-ryan@michonline.com","threadId":"2654","inReplyTo":null,"subject":"[PATCH] Add git-graft-ripple, a tool for permanently grafting history into a tree.","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2005-11-22T20:50:50Z","receivedAt":"2005-11-22T20:50:50Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"Enhancements over the original example:\n\n\to Each newly created commit A' references A, and (A^1)' (The first try\n\treferenced A^1 and (A^1)' but not A)\n\n\to Support for incrementally rewriting history is present.\n\nSigned-off-by: Ryan Anderson <ryan@michonline.com>\n\n---\n\n Documentation/git-graft-ripple |   57 +++++++++++++++++\n Makefile                       |    3 +\n git-graft-ripple.perl          |  137 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 196 insertions(+), 1 deletions(-)\n create mode 100644 Documentation/git-graft-ripple\n create mode 100755 git-graft-ripple.perl\n\napplies-to: 92551f12af8cd77f821d066a53c4dd301d1b41f5\n9bfbdd948e6c0c9a8f0383b355e5cbbb42e0a772\ndiff --git a/Documentation/git-graft-ripple b/Documentation/git-graft-ripple\nnew file mode 100644\nindex 0000000..2fa38cb\n--- /dev/null\n+++ b/Documentation/git-graft-ripple\n@@ -0,0 +1,57 @@\n+git-graft-ripple(1)\n+===============\n+\n+NAME\n+----\n+git-graft-ripple - Rewrite history with an additional commitish as a parent.\n+\n+\n+SYNOPSIS\n+--------\n+'git-log <git repository> <branch committish> <graft SHA1> [start commitish]'\n+\n+DESCRIPTION\n+-----------\n+\n+git-graft-ripple rewrites a series of commit objects by adding a new parent to\n+the most ancestral commit, and passing the changed history back up through the\n+revision history.\n+\n+Proceeding in reverse merge order a new commit is created using the following\n+algorithim:\n+\n+\tIf this is the first commit reached, append the graft object name to\n+\tthe parents.\n+\n+\tReplace each parent object name with the object name generated by this\n+\talgorithm earlier.\n+\n+\tAdd the original object name to the parents.\n+\n+If a start commitish is given, the output of git-rev-list is restricted using\n+it. This allows for incremental grafting to occur.\n+\n+Note: No branches are modified by this operation.\n+\n+Files\n+-----\n+\n+GIT_DIR/info/ripple-status contains a mapping between the original object name\n+and the object name generated by this algorithm. When doing incremental\n+grafting, this file is necessary to continue to rename objects. If this is not\n+present, git-graft-ripple will leave the old object name in the list of\n+parents, which should still allow most merges to occur cleanly.\n+\n+\n+Author\n+------\n+Written by Ryan Anderson <ryan@michonline.com>\n+\n+Documentation\n+--------------\n+Documentation by Ryan Anderson.\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\n+\ndiff --git a/Makefile b/Makefile\nindex 1109dc6..984be32 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -97,7 +97,8 @@ SCRIPT_SH = \\\n SCRIPT_PERL = \\\n \tgit-archimport.perl git-cvsimport.perl git-relink.perl \\\n \tgit-shortlog.perl git-fmt-merge-msg.perl \\\n-\tgit-svnimport.perl git-mv.perl git-cvsexportcommit.perl\n+\tgit-svnimport.perl git-mv.perl git-cvsexportcommit.perl \\\n+\tgit-graft-ripple.perl\n \n SCRIPT_PYTHON = \\\n \tgit-merge-recursive.py\ndiff --git a/git-graft-ripple.perl b/git-graft-ripple.perl\nnew file mode 100755\nindex 0000000..b8ec990\n--- /dev/null\n+++ b/git-graft-ripple.perl\n@@ -0,0 +1,137 @@\n+#!/usr/bin/perl\n+# Copyright 2005 Ryan Anderson <ryan@michonline.com>\n+#\n+# GPL v2 (See COPYING)\n+#\n+# This file is licensed under the GPL v2, or a later version\n+# at the discretion of Linus Torvalds.\n+\n+use strict;\n+use warnings;\n+\n+use IPC::Open2;\n+\n+sub git_commit_tree {\n+        my ($tree,$comments,@parents) = @_;\n+\n+        my @cparents;\n+        foreach my $p (@parents) {\n+                push @cparents,\"-p\",$p;\n+        }\n+\n+        my $pid = open2(*Reader, *Writer,\n+\t\t\"git-commit-tree\",$tree,@cparents);\n+        print Writer $comments;\n+        close(Writer);\n+        my $commit = <Reader>;\n+\n+        waitpid $pid, 0;\n+        close(Reader);\n+\n+        chomp $commit;\n+\n+        return $commit;\n+}\n+\n+my ($gittree,$branch,$graft,$start) = @ARGV;\n+chdir($gittree)\n+\tor die sprintf(\"Failed to chdir to '%s': %s\\n\", $ARGV[0], $!);\n+\n+my $range = defined $start ? sprintf(\"%s..%s\",$start,$branch) : $branch;\n+\n+#printf(\"Running git-rev-list --parents --merge-order %s\\n\",$range);\n+open(GRL,\"-|\",\"git-rev-list\",\"--parents\", \"--merge-order\", $range)\n+\tor die \"Failed to run git-rev-list: \" . $!;\n+\n+my %csets;\n+my @revs;\n+while(<GRL>) {\n+\tchomp;\n+\tmy ($commit,@parents) = split;\n+\t$csets{$commit}{parents} = \\@parents;\n+\tpush @revs, $commit;\n+#\tprintf(\"Investigating %s\\n\", $commit);\n+\n+\topen(GCF,\"-|\",\"git-cat-file\",\"commit\",$commit)\n+\t\tor die \"Failed to open git-cat-file: \" . $!;\n+\n+\tmy $in_comments = 0;\n+\twhile(<GCF>) {\n+\t\tchomp;\n+\t\tif ($in_comments) {\n+\t\t\t$csets{$commit}{comments} .= $_ . \"\\n\";\n+\n+\t\t} elsif (m/^tree (.+)$/) {\n+\t\t\t$csets{$commit}{tree} = $1;\n+\t\t\t#printf(\"tree = %s\\n\",$1);\n+\n+\t\t} elsif (m/^parent (.+)$/) {\n+\t\t\t# Do nothing, we already got\n+\t\t\t# the parents from rev-list.\n+\n+\t\t} elsif (m/^(author|committer) (.*) <(.*)> (.*)$/) {\n+\t\t\t#printf(\"%s = %s <%s> at %s\\n\",$1, $2,$3,$4);\n+\t\t\t@{$csets{$commit}{$1}}{qw(name email datetime)}\n+\t\t\t\t= ($2,$3,$4);\n+\n+\t\t} elsif (length == 0) {\n+\t\t\t$in_comments = 1;\n+\t\t\t$csets{$commit}{comments} = \"\";\n+\t\t\tnext;\n+\t\t}\n+\t}\n+\tclose(GCF);\n+\n+}\n+close(GRL);\n+\n+@revs = reverse @revs;\n+shift @revs if defined $start;\n+push @{$csets{$revs[0]}{parents}},$graft;\n+\n+my %newcsets;\n+if (-f \".git/info/ripple-status\") {\n+\topen(RSTATUS,\"<\",\".git/info/ripple-status\")\n+\t\tor die \"Failed to restore status from .git/info/ripple-status: \" . $!;\n+\n+\twhile (<RSTATUS>) {\n+\t\tchomp;\n+\t\tmy ($k,$newcset) = split;\n+\t\t$newcsets{$k} = $newcset;\n+\t}\n+\tclose(RSTATUS);\n+}\n+\n+\n+foreach my $old (@revs) {\n+\tprintf(\"\\nProcessing commit %s\\n\",$old);\n+        $ENV{GIT_AUTHOR_EMAIL} = $csets{$old}{author}{email};\n+        $ENV{GIT_AUTHOR_NAME} = $csets{$old}{author}{name};\n+        $ENV{GIT_AUTHOR_DATE} = $csets{$old}{author}{datetime};\n+        $ENV{GIT_COMMITTER_DATE} = $csets{$old}{committer}{datetime};\n+        $ENV{GIT_COMMITTER_EMAIL} = $csets{$old}{committer}{email};\n+        $ENV{GIT_COMMITTER_NAME} = $csets{$old}{committer}{name};\n+\n+\tmy @parents = ($old);\n+\tforeach my $p (@{$csets{$old}{parents}}) {\n+\t\tif (exists $newcsets{$p}) {\n+\t\t\tpush @parents, $newcsets{$p};\n+\t\t\tprintf(\"\\t%s => %s\\n\",\n+\t\t\t\t$newcsets{$p},$p);\n+\t\t} else {\n+\t\t\tpush @parents, $p;\n+\t\t}\n+\t}\n+        my $commit = git_commit_tree($csets{$old}{tree},\n+\t\t$csets{$old}{comments},@parents);\n+\t$newcsets{$old} = $commit;\n+        printf(\"\\tNew commit: %s\\n\",$newcsets{$old});\n+}\n+\n+open(RSTATUS,\">\",\".git/info/ripple-status\")\n+\tor die \"Failed to save status in .git/info/ripple-status: \" . $!;\n+\n+foreach my $k (keys %newcsets) {\n+\tprintf RSTATUS \"%s\\t%s\\n\", $k, $newcsets{$k};\n+}\n+close(RSTATUS);\n---\n0.99.9.GIT\n"},{"id":"12581","messageId":"Pine.LNX.4.64.0511221652530.13959@g5.osdl.org","threadId":"2654","inReplyTo":"11326926501602-git-send-email-ryan@michonline.com","subject":"Re: [PATCH] Add git-graft-ripple, a tool for permanently grafting history into a tree.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-11-23T00:55:04Z","receivedAt":"2005-11-23T00:55:04Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 22 Nov 2005, Ryan Anderson wrote:\n>\n> Enhancements over the original example:\n> \n> \to Each newly created commit A' references A, and (A^1)' (The first try\n> \treferenced A^1 and (A^1)' but not A)\n> \n> \to Support for incrementally rewriting history is present.\n\nHow about the case of having commits that have pointers to other commits \nin the comments? \n\nFor example, on the kernel do\n\n\tgitk 19842d67340e4a8f616552d344e97fc7452aa37a\n\nand see how gitk highlights the SHA1's in the commit message and makes \nhyperlinks to the commits they point to..\n\n\t\tLinus\n"},{"id":"12597","messageId":"438418CB.30509@op5.se","threadId":"2654","inReplyTo":"Pine.LNX.4.64.0511221652530.13959@g5.osdl.org","subject":"Re: [PATCH] Add git-graft-ripple, a tool for permanently grafting history into a tree.","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2005-11-23T07:22:51Z","receivedAt":"2005-11-23T07:22:51Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Linus Torvalds wrote:\n> \n> On Tue, 22 Nov 2005, Ryan Anderson wrote:\n> \n>>Enhancements over the original example:\n>>\n>>\to Each newly created commit A' references A, and (A^1)' (The first try\n>>\treferenced A^1 and (A^1)' but not A)\n>>\n>>\to Support for incrementally rewriting history is present.\n> \n> \n> How about the case of having commits that have pointers to other commits \n> in the comments? \n> \n> For example, on the kernel do\n> \n> \tgitk 19842d67340e4a8f616552d344e97fc7452aa37a\n> \n> and see how gitk highlights the SHA1's in the commit message and makes \n> hyperlinks to the commits they point to..\n> \n\nFor reference, gitweb does the same.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"12610","messageId":"20051123135150.GA16995@mythryan2.michonline.com","threadId":"2654","inReplyTo":"Pine.LNX.4.64.0511221652530.13959@g5.osdl.org","subject":"Re: [PATCH] Add git-graft-ripple, a tool for permanently grafting history into a tree.","fromName":"Ryan Anderson","fromEmail":"ryan@michonline.com","sentAt":"2005-11-23T13:51:50Z","receivedAt":"2005-11-23T13:51:50Z","isPatch":true,"sender":{"key":"ryan@michonline.com","avatar":null},"body":"On Tue, Nov 22, 2005 at 04:55:04PM -0800, Linus Torvalds wrote:\n> On Tue, 22 Nov 2005, Ryan Anderson wrote:\n> >\n> > Enhancements over the original example:\n> > \n> > \to Each newly created commit A' references A, and (A^1)' (The first try\n> > \treferenced A^1 and (A^1)' but not A)\n> > \n> > \to Support for incrementally rewriting history is present.\n> \n> How about the case of having commits that have pointers to other commits \n> in the comments? \n> \n> For example, on the kernel do\n> \n> \tgitk 19842d67340e4a8f616552d344e97fc7452aa37a\n> \n> and see how gitk highlights the SHA1's in the commit message and makes \n> hyperlinks to the commits they point to..\n\nFor some reason, my gut says that this goes too far.  I'm having a hard\ntime pinning down a way to explain that.\n\nI guess something (untested) like this could do it, right before I call\ngit_commit_tree():\n\n\tmy $found;\n\tdo {\n\t\t$found = 0;\n\t\tif ($csets{$old}{comments} =~ /\\s([a-f0-9]{40})\\s/ &&\n\t\t\texists $newcsets{$1}) {\n\n\t\t\tmy $tcommit = $1;\n\n\t\t\t$found = 1;\n\t\t\t$csets{$old}{comments} =~ s/$tcommit/$newcsets{$tcommit}/g;\n\t\t}\n\t} while ($found);\n\nI'm not entirely convinced this is a good idea, but there it is.\n\nI'll write up a patch later for this (probably switching to GetOpt in\nthe process.)\n\n-- \n\nRyan Anderson\n  sometimes Pug Majere\n"},{"id":"12618","messageId":"861x17pgzg.fsf@blue.stonehenge.com","threadId":"2654","inReplyTo":"20051123135150.GA16995@mythryan2.michonline.com","subject":"Re: [PATCH] Add git-graft-ripple, a tool for permanently grafting history into a tree.","fromName":"Randal L. Schwartz","fromEmail":"merlyn@stonehenge.com","sentAt":"2005-11-23T14:37:55Z","receivedAt":"2005-11-23T14:37:55Z","isPatch":true,"sender":{"key":"merlyn@stonehenge.com","avatar":"https://gravatar.com/avatar/dc528d210743ff0333e6213f9ee7b33b23f1b7bc1f3c5a8c2d819074ecd7ab19?d=mp&s=160"},"body":">>>>> \"Ryan\" == Ryan Anderson <ryan@michonline.com> writes:\n\nRyan> git_commit_tree():\n\nRyan> \tmy $found;\nRyan> \tdo {\nRyan> \t\t$found = 0;\nRyan> \t\tif ($csets{$old}{comments} =~ /\\s([a-f0-9]{40})\\s/ &&\nRyan> \t\t\texists $newcsets{$1}) {\n\nRyan> \t\t\tmy $tcommit = $1;\n\nRyan> \t\t\t$found = 1;\nRyan> \t\t\t$csets{$old}{comments} =~ s/$tcommit/$newcsets{$tcommit}/g;\nRyan> \t\t}\nRyan> \t} while ($found);\n\nI hate artificial booleans.  This is better written as:\n\n  {\n    last unless my ($tcommit) = $csets{$old}{comments} =~ /\\s([a-f0-9]{40})\\s/;\n    last unless exists $newcsets{$tcommit};\n    $csets{$old}{comments} =~ s/$tcommit/$newcsets{$tcommit}/g;\n    redo;\n  }\n\n-- \nRandal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095\n<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>\nPerl/Unix/security consulting, Technical writing, Comedy, etc. etc.\nSee PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!\n"},{"id":"12625","messageId":"Pine.LNX.4.63.0511231638080.8993@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"2654","inReplyTo":"20051123135150.GA16995@mythryan2.michonline.com","subject":"Re: [PATCH] Add git-graft-ripple, a tool for permanently grafting history into a tree.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2005-11-23T15:40:16Z","receivedAt":"2005-11-23T15:40:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 23 Nov 2005, Ryan Anderson wrote:\n\n> For some reason, my gut says that this goes too far.  I'm having a hard\n> time pinning down a way to explain that.\n\nI can pin down what my gut says:\n\n- the old commits still exist, so the links are not invalid\n- the commit message is signed off as is\n- the process to find the correct new SHA1 is not straight-forward, i.e.\n  one would need to introduce a recursion where now suffices a simple\n  linear loop.\n\nCiao,\nDscho\n"},{"id":"12626","messageId":"Pine.LNX.4.64.0511230905570.13959@g5.osdl.org","threadId":"2654","inReplyTo":"20051123135150.GA16995@mythryan2.michonline.com","subject":"Re: [PATCH] Add git-graft-ripple, a tool for permanently grafting history into a tree.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-11-23T17:07:14Z","receivedAt":"2005-11-23T17:07:14Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 23 Nov 2005, Ryan Anderson wrote:\n> \n> I'm not entirely convinced this is a good idea, but there it is.\n\nWell, quite frankly, I think the whole graft-rippling is just wrong (why \ndo it? The grafts are fine, and after you've rippled, you'll never be able \nto merge with somebody who didn't) but if you do it, I think you should \nfix it up properly and have a resulting tree that is at least as good as \nthe original.\n\n\t\tLinus\n"}]}