{"thread":{"id":"12653","subject":"optimized checkout+rebase?","startedAt":"2008-03-12T19:10:41Z","lastAt":"2008-03-20T07:53:01Z","messageCount":10,"participants":["Ralf Wildenhues","Johannes Sixt","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"71830","messageId":"20080312191041.GF3198@ins.uni-bonn.de","threadId":"12653","inReplyTo":null,"subject":"optimized checkout+rebase?","fromName":"Ralf Wildenhues","fromEmail":"ralf.wildenhues@gmx.de","sentAt":"2008-03-12T19:10:41Z","receivedAt":"2008-03-12T19:10:41Z","isPatch":false,"sender":{"key":"ralf.wildenhues@gmx.de","avatar":null},"body":"Hello,\n\nthis is a common work pattern:\n\n  git checkout master\n  git pull\n  for branch in $my_topic_branches; do\n    git checkout $branch\n    git rebase master\n    # occasional fixups here...\n  done\n\nNow, it looks to me that one of the first operations of rebase just\nundoes part of the work that the checkout of the branch did.  Well,\n\"undoes\" is the wrong word, what I mean is that it looks like work\nmay be saved by combining the two checkout and the rewinding step.\n\nHas this optimization already been implemented in git by some command\nI've overlooked, and if no, do you agree that there is some optimization\npossible?\n\nThanks,\nRalf\n"},{"id":"71898","messageId":"47D8D5B8.70809@viscovery.net","threadId":"12653","inReplyTo":"20080312191041.GF3198@ins.uni-bonn.de","subject":"Re: optimized checkout+rebase?","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-03-13T07:20:24Z","receivedAt":"2008-03-13T07:20:24Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Ralf Wildenhues schrieb:\n> this is a common work pattern:\n> \n>   git checkout master\n>   git pull\n>   for branch in $my_topic_branches; do\n>     git checkout $branch\n>     git rebase master\n>     # occasional fixups here...\n>   done\n> \n> Now, it looks to me that one of the first operations of rebase just\n> undoes part of the work that the checkout of the branch did.  Well,\n> \"undoes\" is the wrong word, what I mean is that it looks like work\n> may be saved by combining the two checkout and the rewinding step.\n\nWell, what you could do is:\n\n   for branch in $my_topic_branches; do\n     git rebase master $branch\n     # occasional fixups here...\n   done\n\nBut it seems that even then rebase first does the checkout $branch, and\nthen the checkout master right after that. At least the first checkout\nshould be unnecessary because all the revision range computations and\npatch formating should be possible to do without the checkout. (The second\ncheckout is indispensable, of course.)\n\nThis has annoyed me, too, but not so much that I started looking at a fix,\nthough...\n\n-- Hannes\n"},{"id":"72174","messageId":"20080315103954.GD14769@ins.uni-bonn.de","threadId":"12653","inReplyTo":"47D8D5B8.70809@viscovery.net","subject":"Re: optimized checkout+rebase?","fromName":"Ralf Wildenhues","fromEmail":"ralf.wildenhues@gmx.de","sentAt":"2008-03-15T10:39:55Z","receivedAt":"2008-03-15T10:39:55Z","isPatch":false,"sender":{"key":"ralf.wildenhues@gmx.de","avatar":null},"body":"* Johannes Sixt wrote on Thu, Mar 13, 2008 at 08:20:24AM CET:\n> Well, what you could do is:\n> \n>    for branch in $my_topic_branches; do\n>      git rebase master $branch\n>      # occasional fixups here...\n>    done\n> \n> But it seems that even then rebase first does the checkout $branch, and\n> then the checkout master right after that.\n\nExactly.  The amount of typing is not an issue, but the trees are large\nso it's not uncommon that each checkout takes tens of seconds.  No idea\nhow much of that is range computations and patch formatting though.\n\nThanks,\nRalf\n"},{"id":"72195","messageId":"7vk5k3n2ho.fsf_-_@gitster.siamese.dyndns.org","threadId":"12653","inReplyTo":"20080315103954.GD14769@ins.uni-bonn.de","subject":"[PATCH] rebase [--onto O] A B: omit needless checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-15T20:42:11Z","receivedAt":"2008-03-15T20:42:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This teaches \"git rebase [--onto O] A B\" to omit an unnecessary checkout\nof branch B before it goes on.\n\n\"git-rebase\" originally was about rebasing the current branch to somewhere\nelse, and when the extra parameter to name which branch to rebase was\nadded, it defined the semantics to the safest but stupid \"first switch to\nthe named branch and then operate exactly the same way as if we were\nalready on that branch\".\n\nBut the first thing the real part of \"rebase\" does is to reset the work\ntree and the index to the \"onto\" commit.  Which means the \"rebase that\nbranch\" form switched the work tree to the tip of the branch only to\nimmediately switch again to another commit.  This was wasteful.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * Almost untested as I do not use this form very often.  Feedback would\n   be good.\n\n git-rebase.sh |   51 +++++++++++++++++++++++++++++++--------------------\n 1 files changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex ff66af3..9273852 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -309,22 +309,41 @@ then\n \t}\n fi\n \n-# If the branch to rebase is given, first switch to it.\n+# If the branch to rebase is given, that is the branch we will rebase\n+# $branch_name -- branch being rebased, or HEAD (already detached)\n+# $orig_head -- commit object name of tip of the branch before rebasing\n+# $head_name -- refs/heads/<that-branch> or \"detached HEAD\"\n+switch_to=\n case \"$#\" in\n 2)\n+\t# Is it \"rebase other $branchname\" or \"rebase other $commit\"?\n \tbranch_name=\"$2\"\n-\tgit-checkout \"$2\" || usage\n+\tswitch_to=\"$2\"\n+\n+\tif branch=$(git rev-parse --verify \"refs/heads/$2\" 2>/dev/null)\n+\tthen\n+\t\thead_name=\"refs/heads/$2\"\n+\telif branch=$(git rev-parse --verify \"$2\" 2>/dev/null)\n+\tthen\n+\t\thead_name=\"detached HEAD\"\n+\telse\n+\t\tusage\n+\tfi\n \t;;\n *)\n+\t# Do not need to switch branches, we are already on it.\n \tif branch_name=`git symbolic-ref -q HEAD`\n \tthen\n+\t\thead_name=$branch_name\n \t\tbranch_name=`expr \"z$branch_name\" : 'zrefs/heads/\\(.*\\)'`\n \telse\n+\t\thead_name=\"detached HEAD\"\n \t\tbranch_name=HEAD ;# detached\n \tfi\n+\tbranch=$(git rev-parse --verify \"${branch_name}^0\") || exit\n \t;;\n esac\n-branch=$(git rev-parse --verify \"${branch_name}^0\") || exit\n+orig_head=$branch\n \n # Now we are rebasing commits $upstream..$branch on top of $onto\n \n@@ -335,6 +354,8 @@ if test \"$upstream\" = \"$onto\" && test \"$mb\" = \"$onto\" &&\n \t# linear history?\n \t! git rev-list --parents \"$onto\"..\"$branch\" | grep \" .* \" > /dev/null\n then\n+\t# Lazily switch to the target branch if needed...\n+\ttest -z \"$switch_to\" || git checkout \"$switch_to\"\n \techo >&2 \"Current branch $branch_name is up to date.\"\n \texit 0\n fi\n@@ -346,22 +367,11 @@ then\n \tGIT_PAGER='' git diff --stat --summary \"$mb\" \"$onto\"\n fi\n \n-# move to a detached HEAD\n-orig_head=$(git rev-parse HEAD^0)\n-head_name=$(git symbolic-ref HEAD 2> /dev/null)\n-case \"$head_name\" in\n-'')\n-\thead_name=\"detached HEAD\"\n-\t;;\n-*)\n-\tgit checkout \"$orig_head\" > /dev/null 2>&1 ||\n-\t\tdie \"could not detach HEAD\"\n-\t;;\n-esac\n-\n-# Rewind the head to \"$onto\"; this saves our current head in ORIG_HEAD.\n+# Detach HEAD and reset the tree\n echo \"First, rewinding head to replay your work on top of it...\"\n-git-reset --hard \"$onto\"\n+git checkout \"$onto^0\" >/dev/null 2>&1 ||\n+\tdie \"could not detach HEAD\"\n+# git reset --hard \"$onto^0\"\n \n # If the $onto is a proper descendant of the tip of the branch, then\n # we just fast forwarded.\n@@ -374,7 +384,8 @@ fi\n \n if test -z \"$do_merge\"\n then\n-\tgit format-patch -k --stdout --full-index --ignore-if-in-upstream \"$upstream\"..ORIG_HEAD |\n+\tgit format-patch -k --stdout --full-index --ignore-if-in-upstream \\\n+\t\t\"$upstream..$orig_head\" |\n \tgit am $git_am_opt --rebasing --resolvemsg=\"$RESOLVEMSG\" &&\n \tmove_to_original_branch\n \tret=$?\n@@ -397,7 +408,7 @@ echo \"$orig_head\" > \"$dotest/orig-head\"\n echo \"$head_name\" > \"$dotest/head-name\"\n \n msgnum=0\n-for cmt in `git rev-list --reverse --no-merges \"$upstream\"..ORIG_HEAD`\n+for cmt in `git rev-list --reverse --no-merges \"$upstream..$orig_head\"`\n do\n \tmsgnum=$(($msgnum + 1))\n \techo \"$cmt\" > \"$dotest/cmt.$msgnum\"\n-- \n1.5.4.4.718.ga6ccf\n"},{"id":"72197","messageId":"20080315222140.GE24845@ins.uni-bonn.de","threadId":"12653","inReplyTo":"7vk5k3n2ho.fsf_-_@gitster.siamese.dyndns.org","subject":"Re: [PATCH] rebase [--onto O] A B: omit needless checkout","fromName":"Ralf Wildenhues","fromEmail":"ralf.wildenhues@gmx.de","sentAt":"2008-03-15T22:21:41Z","receivedAt":"2008-03-15T22:21:41Z","isPatch":true,"sender":{"key":"ralf.wildenhues@gmx.de","avatar":null},"body":"Hello Junio,\n\n* Junio C Hamano wrote on Sat, Mar 15, 2008 at 09:42:11PM CET:\n> This teaches \"git rebase [--onto O] A B\" to omit an unnecessary checkout\n> of branch B before it goes on.\n\n>  * Almost untested as I do not use this form very often.  Feedback would\n>    be good.\n\nNice, and seems to do exactly what I meant.  Actually the more important\nspeedup gained from this change is that files that have changed in the\nnon-common history of A but not in that of B, preserve their old time\nstamps, so the rebuild of my bugfix topic B after having built master\nis really quick now.\n\nThanks!\nRalf\n"},{"id":"72204","messageId":"7vmyozl3u4.fsf@gitster.siamese.dyndns.org","threadId":"12653","inReplyTo":"20080315222140.GE24845@ins.uni-bonn.de","subject":"Re: [PATCH] rebase [--onto O] A B: omit needless checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-16T03:56:03Z","receivedAt":"2008-03-16T03:56:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Wildenhues <Ralf.Wildenhues@gmx.de> writes:\n\n> Hello Junio,\n>\n> * Junio C Hamano wrote on Sat, Mar 15, 2008 at 09:42:11PM CET:\n>> This teaches \"git rebase [--onto O] A B\" to omit an unnecessary checkout\n>> of branch B before it goes on.\n>\n>>  * Almost untested as I do not use this form very often.  Feedback would\n>>    be good.\n>\n> Nice, and seems to do exactly what I meant.  Actually the more important\n> speedup gained from this change is that files that have changed in the\n> non-common history of A but not in that of B, preserve their old time\n> stamps, so the rebuild of my bugfix topic B after having built master\n> is really quick now.\n\nActually, you would need this on top.  Otherwise\n\n\t$ git rebase master my-topic~3\n\nwould misbehave.\n\n---\n\n git-rebase.sh |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex 9273852..bd55ef6 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -320,7 +320,8 @@ case \"$#\" in\n \tbranch_name=\"$2\"\n \tswitch_to=\"$2\"\n \n-\tif branch=$(git rev-parse --verify \"refs/heads/$2\" 2>/dev/null)\n+\tif git show-ref --verify --quiet -- \"refs/heads/$2\" &&\n+\t   branch=$(git rev-parse --verify \"refs/heads/$2\" 2>/dev/null)\n \tthen\n \t\thead_name=\"refs/heads/$2\"\n \telif branch=$(git rev-parse --verify \"$2\" 2>/dev/null)\n"},{"id":"72392","messageId":"47E10D0D.2080702@viscovery.net","threadId":"12653","inReplyTo":"7vk5k3n2ho.fsf_-_@gitster.siamese.dyndns.org","subject":"Re: [PATCH] rebase [--onto O] A B: omit needless checkout","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-03-19T12:54:37Z","receivedAt":"2008-03-19T12:54:37Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Junio C Hamano schrieb:\n> This teaches \"git rebase [--onto O] A B\" to omit an unnecessary checkout\n> of branch B before it goes on.\n> \n> \"git-rebase\" originally was about rebasing the current branch to somewhere\n> else, and when the extra parameter to name which branch to rebase was\n> added, it defined the semantics to the safest but stupid \"first switch to\n> the named branch and then operate exactly the same way as if we were\n> already on that branch\".\n> \n> But the first thing the real part of \"rebase\" does is to reset the work\n> tree and the index to the \"onto\" commit.  Which means the \"rebase that\n> branch\" form switched the work tree to the tip of the branch only to\n> immediately switch again to another commit.  This was wasteful.\n\nThis works for a frequent use-case of mine:\n\n  $ git rebase master devel\n\nwhere I am already on branch master, and now want to rebase devel on top\nof it.\n\nThe code seems to take care of a lot of corner cases and less frequent\nuse-cases, including using --onto, which I don't feel able to judge about.\n\n> +# git reset --hard \"$onto^0\"\n\nDon't forget to remove this line. ;)\n\n-- Hannes\n"},{"id":"72360","messageId":"7vhcf24j3e.fsf@gitster.siamese.dyndns.org","threadId":"12653","inReplyTo":"47E10D0D.2080702@viscovery.net","subject":"Re: [PATCH] rebase [--onto O] A B: omit needless checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-19T19:19:49Z","receivedAt":"2008-03-19T19:19:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j.sixt@viscovery.net> writes:\n\n> Junio C Hamano schrieb:\n>> This teaches \"git rebase [--onto O] A B\" to omit an unnecessary checkout\n>> of branch B before it goes on.\n>> ...\n>\n> This works for a frequent use-case of mine:\n\n\"Works\" meaning \"does not break\", or \"gives a great performance\nimprovement that it is worth having it early in a tagged release\"?\n\n>   $ git rebase master devel\n>\n> where I am already on branch master, and now want to rebase devel on top\n> of it.\n>\n> The code seems to take care of a lot of corner cases and less frequent\n> use-cases, including using --onto, which I don't feel able to judge about.\n>\n>> +# git reset --hard \"$onto^0\"\n>\n> Don't forget to remove this line. ;)\n"},{"id":"72492","messageId":"47E21510.6020009@viscovery.net","threadId":"12653","inReplyTo":"7vhcf24j3e.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] rebase [--onto O] A B: omit needless checkout","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-03-20T07:41:04Z","receivedAt":"2008-03-20T07:41:04Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Junio C Hamano schrieb:\n> Johannes Sixt <j.sixt@viscovery.net> writes:\n> \n>> Junio C Hamano schrieb:\n>>> This teaches \"git rebase [--onto O] A B\" to omit an unnecessary checkout\n>>> of branch B before it goes on.\n>>> ...\n>> This works for a frequent use-case of mine:\n> \n> \"Works\" meaning \"does not break\", or \"gives a great performance\n> improvement that it is worth having it early in a tagged release\"?\n\nSorry for being so terse. \"Works\", of course, means \"works as expected\",\nand the \"expected\" part of that is that the annoyance is no longer\npresent, which is the unnecessary checkout.\n\nBut given how intrusive the patch is (\"just to remove an unnecessary\ncheckout\"), I'd say this is post-1.5.5 material. Also, the fact that you\nhad to post a fix-up is an indication that there are probably a number of\ncorner cases that need an extended testing period.\n\n-- Hannes\n"},{"id":"72496","messageId":"7vlk4dygpu.fsf@gitster.siamese.dyndns.org","threadId":"12653","inReplyTo":"47E21510.6020009@viscovery.net","subject":"Re: [PATCH] rebase [--onto O] A B: omit needless checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-20T07:53:01Z","receivedAt":"2008-03-20T07:53:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j.sixt@viscovery.net> writes:\n\n> But given how intrusive the patch is (\"just to remove an unnecessary\n> checkout\"), I'd say this is post-1.5.5 material. Also, the fact that you\n> had to post a fix-up is an indication that there are probably a number of\n> corner cases that need an extended testing period.\n\nThe fixup came from a real-world corner case.  I actually do fairly\nesoteric varieties of rebases all the time, starting with my HEAD\ndetached, rebasing --onto a non-branch, explicitly stating where the\nbottom is, etc, etc.\n\nAlthough I am comfortable enough with the patch itself to queue it in\n'next' for my daily use, I do not intend it for 1.5.5 at all.  The merge\nwindow is a discipline, a line must be drawn somewhere, and when the line\nis drawn, it must be honoured.\n"}]}