{"thread":{"id":"3331","subject":"[PATCH] rebase: allow a hook to refuse rebasing.","startedAt":"2006-02-14T07:53:14Z","lastAt":"2006-02-14T07:53:14Z","messageCount":1,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"16105","messageId":"7v1wy6tlzp.fsf@assigned-by-dhcp.cox.net","threadId":"3331","inReplyTo":null,"subject":"[PATCH] rebase: allow a hook to refuse rebasing.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-14T07:53:14Z","receivedAt":"2006-02-14T07:53:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This lets a hook to interfere a rebase and help prevent certain\nbranches from being rebased by mistake.  A sample hook to show\nhow to prevent a topic branch that has already been merged into\npublish branch.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n * This was done while switching my workflow to more stable\n   \"next\".  A companion script which is more private nature is\n   in the TODO branch, file TO.  I use that one to manage the\n   \"next\" branch, and this sample hook prevents me from rebasing\n   topic branches too eagerly by mistake.\n\n git-rebase.sh               |    9 +++\n templates/hooks--pre-rebase |  150 +++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 159 insertions(+), 0 deletions(-)\n create mode 100644 templates/hooks--pre-rebase\n\n9a111c91b01455ee1ea9f33c60da7ad50d460a7b\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 16d4359..f84160d 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -36,6 +36,15 @@ other=$(git-rev-parse --verify \"$1^0\") |\n # Make sure the branch to rebase is valid.\n head=$(git-rev-parse --verify \"${2-HEAD}^0\") || exit\n \n+# If a hook exists, give it a chance to interrupt\n+if test -x \"$GIT_DIR/hooks/pre-rebase\"\n+then\n+\t\"$GIT_DIR/hooks/pre-rebase\" ${1+\"$@\"} || {\n+\t\techo >&2 \"The pre-rebase hook refused to rebase.\"\n+\t\texit 1\n+\t}\n+fi\n+\n # If the branch to rebase is given, first switch to it.\n case \"$#\" in\n 2)\ndiff --git a/templates/hooks--pre-rebase b/templates/hooks--pre-rebase\nnew file mode 100644\nindex 0000000..981c454\n--- /dev/null\n+++ b/templates/hooks--pre-rebase\n@@ -0,0 +1,150 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Junio C Hamano\n+#\n+\n+publish=next\n+basebranch=\"$1\"\n+if test \"$#\" = 2\n+then\n+\ttopic=\"refs/heads/$2\"\n+else\n+\ttopic=`git symbolic-ref HEAD`\n+fi\n+\n+case \"$basebranch,$topic\" in\n+master,refs/heads/??/*)\n+\t;;\n+*)\n+\texit 0 ;# we do not interrupt others.\n+\t;;\n+esac\n+\n+# Now we are dealing with a topic branch being rebased\n+# on top of master.  Is it OK to rebase it?\n+\n+# Is topic fully merged to master?\n+not_in_master=`git-rev-list --pretty=oneline ^master \"$topic\"`\n+if test -z \"$not_in_master\"\n+then\n+\techo >&2 \"$topic is fully merged to master; better remove it.\"\n+\texit 1 ;# we could allow it, but there is no point.\n+fi\n+\n+# Is topic ever merged to next?  If so you should not be rebasing it.\n+only_next_1=`git-rev-list ^master \"^$topic\" ${publish} | sort`\n+only_next_2=`git-rev-list ^master           ${publish} | sort`\n+if test \"$only_next_1\" = \"$only_next_2\"\n+then\n+\tnot_in_topic=`git-rev-list \"^$topic\" master`\n+\tif test -z \"$not_in_topic\"\n+\tthen\n+\t\techo >&2 \"$topic is already up-to-date with master\"\n+\t\texit 1 ;# we could allow it, but there is no point.\n+\telse\n+\t\texit 0\n+\tfi\n+else\n+\tnot_in_next=`git-rev-list --pretty=oneline ^${publish} \"$topic\"`\n+\tperl -e '\n+\t\tmy $topic = $ARGV[0];\n+\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n+\t\tmy (%not_in_next) = map {\n+\t\t\t/^([0-9a-f]+) /;\n+\t\t\t($1 => 1);\n+\t\t} split(/\\n/, $ARGV[1]);\n+\t\tfor my $elem (map {\n+\t\t\t\t/^([0-9a-f]+) (.*)$/;\n+\t\t\t\t[$1 => $2];\n+\t\t\t} split(/\\n/, $ARGV[2])) {\n+\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n+\t\t\t\tif ($msg) {\n+\t\t\t\t\tprint STDERR $msg;\n+\t\t\t\t\tundef $msg;\n+\t\t\t\t}\n+\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n+\t\t\t}\n+\t\t}\n+\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n+\texit 1\n+fi\n+\n+exit 0\n+\n+################################################################\n+\n+This sample hook safeguards topic branches that have been\n+published from being rewound.\n+\n+The workflow assumed here is:\n+\n+ * Once a topic branch forks from \"master\", \"master\" is never\n+   merged into it again (either directly or indirectly).\n+\n+ * Once a topic branch is fully cooked and merged into \"master\",\n+   it is deleted.  If you need to build on top of it to correct\n+   earlier mistakes, a new topic branch is created by forking at\n+   the tip of the \"master\".  This is not strictly necessary, but\n+   it makes it easier to keep your history simple.\n+\n+ * Whenever you need to test or publish your changes to topic\n+   branches, merge them into \"next\" branch.\n+\n+The script, being an example, hardcodes the publish branch name\n+to be \"next\", but it is trivial to make it configurable via\n+$GIT_DIR/config mechanism.\n+\n+With this workflow, you would want to know:\n+\n+(1) ... if a topic branch has ever been merged to \"next\".  Young\n+    topic branches can have stupid mistakes you would rather\n+    clean up before publishing, and things that have not been\n+    merged into other branches can be easily rebased without\n+    affecting other people.  But once it is published, you would\n+    not want to rewind it.\n+\n+(2) ... if a topic branch has been fully merged to \"master\".\n+    Then you can delete it.  More importantly, you should not\n+    build on top of it -- other people may already want to\n+    change things related to the topic as patches against your\n+    \"master\", so if you need further changes, it is better to\n+    fork the topic (perhaps with the same name) afresh from the\n+    tip of \"master\".\n+\n+Let's look at this example:\n+\n+\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n+\t\t  /       /           /           /\n+\t\t /   a---a---b A     /           /\n+\t\t/   /               /           /\n+\t       /   /   c---c---c---c B         /\n+\t      /   /   /             \\         /\n+\t     /   /   /   b---b C     \\       /\n+\t    /   /   /   /             \\     /\n+    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n+\n+\n+A, B and C are topic branches.\n+\n+ * A has one fix since it was merged up to \"next\".\n+\n+ * B has finished.  It has been fully merged up to \"master\" and \"next\",\n+   and is ready to be deleted.\n+\n+ * C has not merged to \"next\" at all.\n+\n+We would want to allow C to be rebased, refuse A, and encourage\n+B to be deleted.\n+\n+To compute (1):\n+\n+\tgit-rev-list ^master ^topic next\n+\tgit-rev-list ^master        next\n+\n+\tif these match, topic has not merged in next at all.\n+\n+To compute (2):\n+\n+\tgit-rev-list master..topic\n+\n+\tif this is empty, it is fully merged to \"master\".\n-- \n1.2.0.g45dc\n"}]}