{"thread":{"id":"15639","subject":"[PATCH 1/3] Prepare for non-interactive merge-preserving rebase","startedAt":"2008-09-23T20:57:26Z","lastAt":"2008-10-15T08:07:06Z","messageCount":13,"participants":["Andreas Ericsson","Stephen Haberman","SZEDER Gábor","Johannes Sixt","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"91423","messageId":"48D95836.6040200@op5.se","threadId":"15639","inReplyTo":null,"subject":"[PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2008-09-23T20:57:26Z","receivedAt":"2008-09-23T20:57:26Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"This patch adds two tests (really three, but one of\nthem just handles setup) which we currently expect\nto fail.\n\nOne of them tests \"git rebase -p\", without the -i flag,\nto make sure it works without phony editors and suchlike.\n\nThe other tests \"git pull --rebase --preserve-merges\"\nto make sure that the same functionality exists there.\n\nThe test was originally written by Stephen Habermann\n<stephen@exigencecorp.com> but has been significantly\nmodified since its creation.\n\nSigned-off-by: Andreas Ericsson <ae@op5.se>\n---\n\nStephen, I had to modify the tests a bit to get them to work with\nhow I implemented the merge-preserving rebase, and also to remove\na lot of the cruft that was previously in there. Hope you're ok\nwith the attribution in the commit message.\n\n t/t3409-rebase-preserve-merges.sh |   68 +++++++++++++++++++++++++++++++++++++\n 1 files changed, 68 insertions(+), 0 deletions(-)\n create mode 100644 t/t3409-rebase-preserve-merges.sh\n\ndiff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh\nnew file mode 100644\nindex 0000000..532b220\n--- /dev/null\n+++ b/t/t3409-rebase-preserve-merges.sh\n@@ -0,0 +1,68 @@\n+#!/bin/sh\n+#\n+# Copyright(C) 2008 Stephen Habermann & Andreas Ericsson\n+#\n+test_description='git rebase -p should preserve merges\n+\n+This test runs various incantations of \"git rebase -p\" and checks\n+that merges are properly carried along\n+'\n+. ./test-lib.sh\n+\n+GIT_AUTHOR_EMAIL=bogus_email_address\n+export GIT_AUTHOR_EMAIL\n+\n+#echo 'Setting up:\n+#\n+#A1--A2  <-- origin/master\n+# \\   \\\n+#  B1--M  <-- topic\n+#   \\\n+#    B2  <-- origin/topic\n+#\n+#'\n+\n+test_expect_success 'setup for merge-preserving rebase' \\\n+\t'echo First > A &&\n+\tgit add A &&\n+\tgit-commit -m \"Add A1\" &&\n+\tgit checkout -b topic &&\n+\techo Second > B &&\n+\tgit add B &&\n+\tgit-commit -m \"Add B1\" &&\n+\tgit checkout -f master &&\n+\techo Third >> A &&\n+\tgit-commit -a -m \"Modify A2\" &&\n+\n+\tgit clone ./. clone1 &&\n+\tcd clone1 &&\n+\tgit checkout -b topic origin/topic &&\n+\tgit merge origin/master &&\n+\tcd ..\n+\n+\tgit clone ./. clone2\n+\tcd clone2 &&\n+\tgit checkout -b topic origin/topic &&\n+\tgit merge origin/master &&\n+\tcd .. &&\n+\n+\tgit checkout topic &&\n+\techo Fourth >> B &&\n+\tgit commit -a -m \"Modify B2\"\n+'\n+\n+test_expect_failure 'git pull --rebase -p on moved topic' '\n+\tcd clone1 &&\n+\tgit pull --rebase --preserve-merges &&\n+\ttest $(git rev-list --all --pretty=oneline | grep \"Modify A\" | wc -l) = 1\n+'\n+\n+test_expect_failure 'rebase -p merge on moved topic' '\n+\tcd ../clone2 &&\n+\tgit fetch &&\n+\tgit rebase -p origin/topic &&\n+\ttest 1 = $(git rev-list --all --pretty=oneline | grep \"Modify A\" | wc -l) &&\n+\ttest 1 = $(git rev-list --all --pretty=oneline | grep \"Merge commit\" | wc -l)\n+'\n+\n+test_done\n-- \n1.6.0.2.307.gc4275.dirty\n"},{"id":"91432","messageId":"20080923162211.d4b15373.stephen@exigencecorp.com","threadId":"15639","inReplyTo":"48D95836.6040200@op5.se","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Stephen Haberman","fromEmail":"stephen@exigencecorp.com","sentAt":"2008-09-23T21:22:11Z","receivedAt":"2008-09-23T21:22:11Z","isPatch":true,"sender":{"key":"stephen@exigencecorp.com","avatar":"https://gravatar.com/avatar/23b93ad70a06ce53505f17ddba65176edbcfb6588e7a4c1a2dca04aaf0a6aff1?d=mp&s=160"},"body":"\n> Stephen, I had to modify the tests a bit to get them to work with how\n> I implemented the merge-preserving rebase, and also to remove a lot of\n> the cruft that was previously in there. Hope you're ok with the\n> attribution in the commit message.\n\nNo problem, it looks great.\n\nThis is awesome. Thanks for the insanely short turnaround. The\nGIT_EDITOR=: hack is neat. I did not think it would be that simple.\n\n- Stephen\n"},{"id":"91435","messageId":"48D95FE1.30200@op5.se","threadId":"15639","inReplyTo":"20080923162211.d4b15373.stephen@exigencecorp.com","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2008-09-23T21:30:09Z","receivedAt":"2008-09-23T21:30:09Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Stephen Haberman wrote:\n>> Stephen, I had to modify the tests a bit to get them to work with how\n>> I implemented the merge-preserving rebase, and also to remove a lot of\n>> the cruft that was previously in there. Hope you're ok with the\n>> attribution in the commit message.\n> \n> No problem, it looks great.\n> \n> This is awesome. Thanks for the insanely short turnaround.\n\n\nIt requires a bit of testing though. All the t/t34* tests pass with\nall the patches applied, and some manual tries worked just fine too,\nbut if you wanna give it a twirl where you work, that'd be great.\n\n\n> The\n> GIT_EDITOR=: hack is neat. I did not think it would be that simple.\n> \n\nActually, you should be able to use vanilla \"git-rebase -i -p\" without\ngetting an editor by doing something like this:\n\nGIT_EDITOR=: git rebase -i -p\n\nbut recommending a hack like that to work around a UI deficiency didn't\nreally appeal to me. If Junio doesn't like the patches though, you could\ntry using that.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"91444","messageId":"20080924001027.GA19264@neumann","threadId":"15639","inReplyTo":"48D95FE1.30200@op5.se","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"SZEDER Gábor","fromEmail":"szeder@fzi.de","sentAt":"2008-09-24T00:10:27Z","receivedAt":"2008-09-24T00:10:27Z","isPatch":true,"sender":{"key":"szeder@fzi.de","avatar":null},"body":"Hi Andreas,\n\nFirst of all, thanks for the work!\n\nOn Tue, Sep 23, 2008 at 11:30:09PM +0200, Andreas Ericsson wrote:\n> It requires a bit of testing though. All the t/t34* tests pass with\n> all the patches applied, and some manual tries worked just fine too,\n> but if you wanna give it a twirl where you work, that'd be great.\nUnfortunately in my example workflow[1] posted earlier today your\npatch series does not work in the way I would like it to behave.\n\nThe following DAG is created by the commands below:\n\n  -A---B      master\n    \\\n     C---M    topic\n      \\ /\n       D\n\n  git init\n  echo 1 >foo\n  git add foo\n  git commit -m 'first on master'       # A\n  echo 2 >>foo\n  git commit -m 'second on master' foo  # B\n  git checkout -b topic HEAD^\n  echo 1 >bar\n  git add bar\n  git commit -m 'first on topic'        # C\n  git checkout -b subtopic\n  echo 1 >baz\n  git add baz\n  git commit -m 'first on subtopic'     # D\n  git checkout topic\n  git merge --no-ff subtopic            # M\n\nIf I now execute 'git rebase -p master topic', I get the following:\n\n  -A---B            master\n    \\   \\\n     \\   C'---M'    topic\n      \\      /\n       C----D\n\nBut I would rather like to have the following:\n\n  -A---B            master\n        \\\n         C'---M'    topic\n          \\  /\n           D'\n\nWould such a behaviour possible at all?\n\n\nThanks,\nGábor\n\n\n[1] http://article.gmane.org/gmane.comp.version-control.git/96548\n"},{"id":"91448","messageId":"48D9DB66.3010501@viscovery.net","threadId":"15639","inReplyTo":"20080924001027.GA19264@neumann","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-09-24T06:17:10Z","receivedAt":"2008-09-24T06:17:10Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"SZEDER Gábor schrieb:\n>   -A---B      master\n>     \\\n>      C---M    topic\n>       \\ /\n>        D\n...\n> If I now execute 'git rebase -p master topic', I get the following:\n> \n>   -A---B            master\n>     \\   \\\n>      \\   C'---M'    topic\n>       \\      /\n>        C----D\n> \n> But I would rather like to have the following:\n> \n>   -A---B            master\n>         \\\n>          C'---M'    topic\n>           \\  /\n>            D'\n> \n> Would such a behaviour possible at all?\n\nI think that rebase -i -p was meant to deal only with the situation where\nthe merged-in branch is from outside the topic branch. What you want is\ncalled \"git-sequencer\". Search the archives.\n\n-- Hannes\n"},{"id":"91450","messageId":"48D9E898.1020305@op5.se","threadId":"15639","inReplyTo":"20080924001027.GA19264@neumann","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2008-09-24T07:13:28Z","receivedAt":"2008-09-24T07:13:28Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"SZEDER Gábor wrote:\n> Hi Andreas,\n> \n> First of all, thanks for the work!\n> \n> On Tue, Sep 23, 2008 at 11:30:09PM +0200, Andreas Ericsson wrote:\n>> It requires a bit of testing though. All the t/t34* tests pass with\n>> all the patches applied, and some manual tries worked just fine too,\n>> but if you wanna give it a twirl where you work, that'd be great.\n> Unfortunately in my example workflow[1] posted earlier today your\n> patch series does not work in the way I would like it to behave.\n> \n> The following DAG is created by the commands below:\n> \n>   -A---B      master\n>     \\\n>      C---M    topic\n>       \\ /\n>        D\n> \n>   git init\n>   echo 1 >foo\n>   git add foo\n>   git commit -m 'first on master'       # A\n>   echo 2 >>foo\n>   git commit -m 'second on master' foo  # B\n>   git checkout -b topic HEAD^\n>   echo 1 >bar\n>   git add bar\n>   git commit -m 'first on topic'        # C\n>   git checkout -b subtopic\n>   echo 1 >baz\n>   git add baz\n>   git commit -m 'first on subtopic'     # D\n>   git checkout topic\n>   git merge --no-ff subtopic            # M\n> \n> If I now execute 'git rebase -p master topic', I get the following:\n> \n>   -A---B            master\n>     \\   \\\n>      \\   C'---M'    topic\n>       \\      /\n>        C----D\n> \n> But I would rather like to have the following:\n> \n>   -A---B            master\n>         \\\n>          C'---M'    topic\n>           \\  /\n>            D'\n> \n> Would such a behaviour possible at all?\n> \n\nSee Johannes Sixt's reply (git sequencer).\nWhat I provided was a hack to access existing functionality in a way\nthat was previously not possible. While that can be neat in itself,\nthe patch series doesn't alter how the merge-preserving rebase works\nin the slightest.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"91744","messageId":"48DE7386.2080808@op5.se","threadId":"15639","inReplyTo":"20080923162211.d4b15373.stephen@exigencecorp.com","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2008-09-27T17:55:18Z","receivedAt":"2008-09-27T17:55:18Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Stephen Haberman wrote:\n>> Stephen, I had to modify the tests a bit to get them to work with how\n>> I implemented the merge-preserving rebase, and also to remove a lot of\n>> the cruft that was previously in there. Hope you're ok with the\n>> attribution in the commit message.\n> \n> No problem, it looks great.\n> \n> This is awesome. Thanks for the insanely short turnaround. The\n> GIT_EDITOR=: hack is neat. I did not think it would be that simple.\n> \n\nStephen, are you using this in production? How's it turning out?\n\nShawn, I haven't seen this in any of your branches. Overlooked or\ndropped? I think 1-2 are probably master material, while I'm not\nso sure about 3/3. Would you prefer a re-send that turns it into\na 2-patch series, adding each test with the functionality it tests?\n\nLet me know how you want it and I'll work something up tomorrow\nmorning, gmt + 1.\n\nThanks\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"91747","messageId":"20080927142007.a75a31a0.stephen@exigencecorp.com","threadId":"15639","inReplyTo":"48DE7386.2080808@op5.se","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Stephen Haberman","fromEmail":"stephen@exigencecorp.com","sentAt":"2008-09-27T19:20:07Z","receivedAt":"2008-09-27T19:20:07Z","isPatch":true,"sender":{"key":"stephen@exigencecorp.com","avatar":"https://gravatar.com/avatar/23b93ad70a06ce53505f17ddba65176edbcfb6588e7a4c1a2dca04aaf0a6aff1?d=mp&s=160"},"body":"\n> Stephen, are you using this in production?\n\nKind of--I have not distributed a patched version of pull. But I have\nwritten test cases on our side and manually executing `GIT_EDITOR=:\ngit rebase -i -p` works very well.\n\nPast occurrences aside, no one has needed to rebase a local merge yet.\n\n> How's it turning out?\n\nI think it's great, but the primary problem will be getting devs to\nactually remember to use it. E.g. I don't think they will type out:\n\n    git pull --rebase --preserve-rebase\n\nEvery time they pull. And they definitely don't do our current hack:\n\n    git fetch\n    GIT_EDITOR=: git rebase -i -p\n\nI do have a wrapper shell script for people to use, but it hasn't seen\nwide adoption yet. We have a draconian hook script that tries to\ndetect merges that should have been rebases and reject them, but\nit's disabled for tweaking right now--when it gets turned back on,\nI think more people will use the script.\n\nIn the long term, having \"branch.name.preservemerges\" and\n\"branch.autosetuppreservemerges\" config options to parallel the\n\"branch.name.rebase\" option and get us back to just \"git pull\"\nwould be great.\n\nI've been meaning to submit patches for these two config options--I\nfigure I can hunt down how \"branch.name.rebase\" works and do the\nappropriate copy/paste, but I haven't dedicated any time to it yet.\n\nThanks,\nStephen\n"},{"id":"91855","messageId":"20080929160153.GK17584@spearce.org","threadId":"15639","inReplyTo":"48DE7386.2080808@op5.se","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-09-29T16:01:53Z","receivedAt":"2008-09-29T16:01:53Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Andreas Ericsson <ae@op5.se> wrote:\n>\n> Shawn, I haven't seen this in any of your branches. Overlooked or\n> dropped? I think 1-2 are probably master material, while I'm not\n> so sure about 3/3. Would you prefer a re-send that turns it into\n> a 2-patch series, adding each test with the functionality it tests?\n\nThanks for the reminder.  It just got lost in the shuffle.  I dragged\nthem out of the archives and will queue into this morning's update,\nso no need for a resend.\n\n-- \nShawn.\n"},{"id":"91857","messageId":"48E0FC86.3040001@op5.se","threadId":"15639","inReplyTo":"20080929160153.GK17584@spearce.org","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2008-09-29T16:04:22Z","receivedAt":"2008-09-29T16:04:22Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Shawn O. Pearce wrote:\n> Andreas Ericsson <ae@op5.se> wrote:\n>> Shawn, I haven't seen this in any of your branches. Overlooked or\n>> dropped? I think 1-2 are probably master material, while I'm not\n>> so sure about 3/3. Would you prefer a re-send that turns it into\n>> a 2-patch series, adding each test with the functionality it tests?\n> \n> Thanks for the reminder.  It just got lost in the shuffle.  I dragged\n> them out of the archives and will queue into this morning's update,\n> so no need for a resend.\n> \n\nHold off on that if you haven't already applied them. I just noticed\nsomething strange in passing 15 minutes ago that I need to investigate\na bit more. I need to get home now though, so I won't have time to\ntest it further until later tonight.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"91858","messageId":"20080929161106.GL17584@spearce.org","threadId":"15639","inReplyTo":"48E0FC86.3040001@op5.se","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-09-29T16:11:06Z","receivedAt":"2008-09-29T16:11:06Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Andreas Ericsson <ae@op5.se> wrote:\n> Shawn O. Pearce wrote:\n>> Andreas Ericsson <ae@op5.se> wrote:\n>>> Shawn, I haven't seen this in any of your branches. Overlooked or\n>>> dropped? \n>>\n>> Thanks for the reminder.  It just got lost in the shuffle.\n>\n> Hold off on that if you haven't already applied them. I just noticed\n> something strange in passing 15 minutes ago that I need to investigate\n> a bit more. I need to get home now though, so I won't have time to\n> test it further until later tonight.\n\nIts only in a topic branch right now.  I'll schedule them into 'pu'\ntoday just so they are available, but I'll be happy to replace any\n(or all) of the patches when you come up with something better.\n\n-- \nShawn.\n"},{"id":"92090","messageId":"20081001152707.2225137f.stephen@exigencecorp.com","threadId":"15639","inReplyTo":"48D95836.6040200@op5.se","subject":"[PATCH] Add branch.autosetuppreservemerges and branch.<name>.preservemerges.","fromName":"Stephen Haberman","fromEmail":"stephen@exigencecorp.com","sentAt":"2008-10-01T20:27:07Z","receivedAt":"2008-10-01T20:27:07Z","isPatch":true,"sender":{"key":"stephen@exigencecorp.com","avatar":"https://gravatar.com/avatar/23b93ad70a06ce53505f17ddba65176edbcfb6588e7a4c1a2dca04aaf0a6aff1?d=mp&s=160"},"body":"Signed-off-by: Stephen Haberman <stephen@exigencecorp.com>\n---\n\nThis builds on top of Andreas's work on `git rebase -p`. I\nbasically copy/pasted how autosetuprebase works so that, if\nappropriately configured, `git pull` will do the \"right thing\"\nfor our environment, i.e. rebasing and preserving merges.\n\nI'm not sure how to handle patches on patches, so apologies\nif I did this wrong. Let me know if there are things I should\nbe doing differently.\n\nThanks.\n\n Documentation/config.txt          |   20 ++++++++\n branch.c                          |   20 ++++++++\n cache.h                           |    9 ++++\n config.c                          |   15 ++++++\n environment.c                     |    1 +\n git-pull.sh                       |   10 +++-\n t/t3200-branch.sh                 |   89 ++++++++++++++++++++++++++++--------\n t/t3409-rebase-preserve-merges.sh |   65 ++++++++++++++++----------\n 8 files changed, 182 insertions(+), 47 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex bea867d..0abf1b8 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -438,6 +438,21 @@ branch.autosetuprebase::\n \tbranch to track another branch.\n \tThis option defaults to never.\n \n+branch.autosetuppreservemerges::\n+\tWhen a new branch is created with 'git-branch' or 'git-checkout'\n+\tthat tracks another branch, this variable tells git to set\n+\tup pull to rebase with preserve merges (see \"branch.<name>.rebase\").\n+\tWhen `never`, rebase is never automatically set to true.\n+\tWhen `local`, rebase is set to true for tracked branches of\n+\tother local branches.\n+\tWhen `remote`, rebase is set to true for tracked branches of\n+\tremote branches.\n+\tWhen `always`, rebase will be set to true for all tracking\n+\tbranches.\n+\tSee \"branch.autosetupmerge\" for details on how to set up a\n+\tbranch to track another branch.\n+\tThis option defaults to never.\n+\n branch.<name>.remote::\n \tWhen in branch <name>, it tells 'git-fetch' which remote to fetch.\n \tIf this option is not given, 'git-fetch' defaults to remote \"origin\".\n@@ -471,6 +486,11 @@ branch.<name>.rebase::\n \tit unless you understand the implications (see linkgit:git-rebase[1]\n \tfor details).\n \n+branch.<name>.preservemerges::\n+\tWhen true, and branch.<name>.rebase is true, preserve merges when\n+\trebasing the branch <name> on top of the fetched branch when\n+\t\"git pull\" is run.\n+\n browser.<tool>.cmd::\n \tSpecify the command to invoke the specified browser. The\n \tspecified command is evaluated in shell with the URLs passed\ndiff --git a/branch.c b/branch.c\nindex b1e59f2..a5e62c8 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -47,6 +47,21 @@ static int should_setup_rebase(const struct tracking *tracking)\n \treturn 0;\n }\n \n+static int should_setup_preservemerges(const struct tracking *tracking)\n+{\n+\tswitch (autopreservemerges) {\n+\tcase AUTOPRESERVEMERGES_NEVER:\n+\t\treturn 0;\n+\tcase AUTOPRESERVEMERGES_LOCAL:\n+\t\treturn tracking->remote == NULL;\n+\tcase AUTOPRESERVEMERGES_REMOTE:\n+\t\treturn tracking->remote != NULL;\n+\tcase AUTOPRESERVEMERGES_ALWAYS:\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n /*\n  * This is called when new_ref is branched off of orig_ref, and tries\n  * to infer the settings for branch.<new_ref>.{remote,merge} from the\n@@ -91,6 +106,11 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,\n \t\tgit_config_set(key, \"true\");\n \t\tprintf(\"This branch will rebase on pull.\\n\");\n \t}\n+\tif (should_setup_preservemerges(&tracking)) {\n+\t\tsprintf(key, \"branch.%s.preservemerges\", new_ref);\n+\t\tgit_config_set(key, \"true\");\n+\t\tprintf(\"This branch will preserve merges on pull.\\n\");\n+\t}\n \tfree(tracking.src);\n \n \treturn 0;\ndiff --git a/cache.h b/cache.h\nindex de8c2b6..97be98c 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -467,8 +467,17 @@ enum rebase_setup_type {\n \tAUTOREBASE_ALWAYS,\n };\n \n+enum preservemerges_setup_type {\n+\tAUTOPRESERVEMERGES_NEVER = 0,\n+\tAUTOPRESERVEMERGES_LOCAL,\n+\tAUTOPRESERVEMERGES_REMOTE,\n+\tAUTOPRESERVEMERGES_ALWAYS,\n+};\n+\n+\n extern enum branch_track git_branch_track;\n extern enum rebase_setup_type autorebase;\n+extern enum preservemerges_setup_type autopreservemerges;\n \n #define GIT_REPO_VERSION 0\n extern int repository_format_version;\ndiff --git a/config.c b/config.c\nindex 53f04a0..6302f5a 100644\n--- a/config.c\n+++ b/config.c\n@@ -536,6 +536,21 @@ static int git_default_branch_config(const char *var, const char *value)\n \t\t\treturn error(\"Malformed value for %s\", var);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"branch.autosetuppreservemerges\")) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\telse if (!strcmp(value, \"never\"))\n+\t\t\tautopreservemerges = AUTOPRESERVEMERGES_NEVER;\n+\t\telse if (!strcmp(value, \"local\"))\n+\t\t\tautopreservemerges = AUTOPRESERVEMERGES_LOCAL;\n+\t\telse if (!strcmp(value, \"remote\"))\n+\t\t\tautopreservemerges = AUTOPRESERVEMERGES_REMOTE;\n+\t\telse if (!strcmp(value, \"always\"))\n+\t\t\tautopreservemerges = AUTOPRESERVEMERGES_ALWAYS;\n+\t\telse\n+\t\t\treturn error(\"Malformed value for %s\", var);\n+\t\treturn 0;\n+\t}\n \n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 0c6d11f..72e735c 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -42,6 +42,7 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;\n unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;\n enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n+enum preservemerges_setup_type autopreservemerges = AUTOPRESERVEMERGES_NEVER;\n \n /* This is set by setup_git_dir_gently() and/or git_default_config() */\n char *git_work_tree_cfg;\ndiff --git a/git-pull.sh b/git-pull.sh\nindex 270a50d..03b7da0 100755\n--- a/git-pull.sh\n+++ b/git-pull.sh\n@@ -20,6 +20,7 @@ strategy_args= no_stat= no_commit= squash= no_ff= log_arg=\n curr_branch=$(git symbolic-ref -q HEAD)\n curr_branch_short=$(echo \"$curr_branch\" | sed \"s|refs/heads/||\")\n rebase=$(git config --bool branch.$curr_branch_short.rebase)\n+preservemerges=$(git config --bool branch.$curr_branch_short.preservemerges)\n while :\n do\n \tcase \"$1\" in\n@@ -59,7 +60,7 @@ do\n \t\trebase=true\n \t\t;;\n \t--preserve-merges) # no short option for this\n-\t\tpreserve_merges=\"--preserve-merges\"\n+\t\tpreservemerges=true\n \t\trebase=true\n \t\t;;\n \t--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)\n@@ -181,9 +182,14 @@ then\n \texit\n fi\n \n+if test true = \"$preservemerges\"\n+then\n+\tpreservemerges_flag=\"--preserve-merges\"\n+fi\n+\n merge_name=$(git fmt-merge-msg $log_arg <\"$GIT_DIR/FETCH_HEAD\") || exit\n test true = \"$rebase\" &&\n-\texec git-rebase $preserve_merges $strategy_args --onto $merge_head \\\n+\texec git-rebase $preservemerges_flag $strategy_args --onto $merge_head \\\n \t${oldremoteref:-$merge_head}\n exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \\\n \t\"$merge_name\" HEAD $merge_head\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 2147eac..f10b8ac 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -228,103 +228,121 @@ test_expect_success 'autosetuprebase local on a tracked local branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase local &&\n+\tgit config branch.autosetuppreservemerges local &&\n \t(git show-ref -q refs/remotes/local/o || git fetch local) &&\n \tgit branch mybase &&\n \tgit branch --track myr1 mybase &&\n \ttest \"$(git config branch.myr1.remote)\" = . &&\n \ttest \"$(git config branch.myr1.merge)\" = refs/heads/mybase &&\n-\ttest \"$(git config branch.myr1.rebase)\" = true\n+\ttest \"$(git config branch.myr1.rebase)\" = true &&\n+\ttest \"$(git config branch.myr1.preservemerges)\" = true\n '\n \n test_expect_success 'autosetuprebase always on a tracked local branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase always &&\n+\tgit config branch.autosetuppreservemerges always &&\n \t(git show-ref -q refs/remotes/local/o || git fetch local) &&\n \tgit branch mybase2 &&\n \tgit branch --track myr2 mybase &&\n \ttest \"$(git config branch.myr2.remote)\" = . &&\n \ttest \"$(git config branch.myr2.merge)\" = refs/heads/mybase &&\n-\ttest \"$(git config branch.myr2.rebase)\" = true\n+\ttest \"$(git config branch.myr2.rebase)\" = true &&\n+\ttest \"$(git config branch.myr2.preservemerges)\" = true\n '\n \n test_expect_success 'autosetuprebase remote on a tracked local branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase remote &&\n+\tgit config branch.autosetuppreservemerges remote &&\n \t(git show-ref -q refs/remotes/local/o || git fetch local) &&\n \tgit branch mybase3 &&\n \tgit branch --track myr3 mybase2 &&\n \ttest \"$(git config branch.myr3.remote)\" = . &&\n \ttest \"$(git config branch.myr3.merge)\" = refs/heads/mybase2 &&\n-\t! test \"$(git config branch.myr3.rebase)\" = true\n+\t! test \"$(git config branch.myr3.rebase)\" = true &&\n+\t! test \"$(git config branch.myr3.preservemerges)\" = true\n '\n \n test_expect_success 'autosetuprebase never on a tracked local branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase never &&\n+\tgit config branch.autosetuppreservemerges never &&\n \t(git show-ref -q refs/remotes/local/o || git fetch local) &&\n \tgit branch mybase4 &&\n \tgit branch --track myr4 mybase2 &&\n \ttest \"$(git config branch.myr4.remote)\" = . &&\n \ttest \"$(git config branch.myr4.merge)\" = refs/heads/mybase2 &&\n-\t! test \"$(git config branch.myr4.rebase)\" = true\n+\t! test \"$(git config branch.myr4.rebase)\" = true &&\n+\t! test \"$(git config branch.myr4.preservemerges)\" = true\n '\n \n test_expect_success 'autosetuprebase local on a tracked remote branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase local &&\n+\tgit config branch.autosetuppreservemerges local &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --track myr5 local/master &&\n \ttest \"$(git config branch.myr5.remote)\" = local &&\n \ttest \"$(git config branch.myr5.merge)\" = refs/heads/master &&\n-\t! test \"$(git config branch.myr5.rebase)\" = true\n+\t! test \"$(git config branch.myr5.rebase)\" = true &&\n+\t! test \"$(git config branch.myr5.preservemerges)\" = true\n '\n \n test_expect_success 'autosetuprebase never on a tracked remote branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase never &&\n+\tgit config branch.autosetuppreservemerges never &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --track myr6 local/master &&\n \ttest \"$(git config branch.myr6.remote)\" = local &&\n \ttest \"$(git config branch.myr6.merge)\" = refs/heads/master &&\n-\t! test \"$(git config branch.myr6.rebase)\" = true\n+\t! test \"$(git config branch.myr6.rebase)\" = true &&\n+\t! test \"$(git config branch.myr6.preservemerges)\" = true\n '\n \n test_expect_success 'autosetuprebase remote on a tracked remote branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase remote &&\n+\tgit config branch.autosetuppreservemerges remote &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --track myr7 local/master &&\n \ttest \"$(git config branch.myr7.remote)\" = local &&\n \ttest \"$(git config branch.myr7.merge)\" = refs/heads/master &&\n-\ttest \"$(git config branch.myr7.rebase)\" = true\n+\ttest \"$(git config branch.myr7.rebase)\" = true &&\n+\ttest \"$(git config branch.myr7.preservemerges)\" = true\n '\n \n test_expect_success 'autosetuprebase always on a tracked remote branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase remote &&\n+\tgit config branch.autosetuppreservemerges remote &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --track myr8 local/master &&\n \ttest \"$(git config branch.myr8.remote)\" = local &&\n \ttest \"$(git config branch.myr8.merge)\" = refs/heads/master &&\n-\ttest \"$(git config branch.myr8.rebase)\" = true\n+\ttest \"$(git config branch.myr8.rebase)\" = true &&\n+\ttest \"$(git config branch.myr8.preservemerges)\" = true\n '\n \n test_expect_success 'autosetuprebase unconfigured on a tracked remote branch' '\n \tgit config --unset branch.autosetuprebase &&\n+\tgit config --unset branch.autosetuppreservemerges &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --track myr9 local/master &&\n \ttest \"$(git config branch.myr9.remote)\" = local &&\n \ttest \"$(git config branch.myr9.merge)\" = refs/heads/master &&\n-\ttest \"z$(git config branch.myr9.rebase)\" = z\n+\ttest \"z$(git config branch.myr9.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr9.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase unconfigured on a tracked local branch' '\n@@ -335,7 +353,8 @@ test_expect_success 'autosetuprebase unconfigured on a tracked local branch' '\n \tgit branch --track myr10 mybase2 &&\n \ttest \"$(git config branch.myr10.remote)\" = . &&\n \ttest \"$(git config branch.myr10.merge)\" = refs/heads/mybase2 &&\n-\ttest \"z$(git config branch.myr10.rebase)\" = z\n+\ttest \"z$(git config branch.myr10.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr10.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase unconfigured on untracked local branch' '\n@@ -345,7 +364,8 @@ test_expect_success 'autosetuprebase unconfigured on untracked local branch' '\n \tgit branch --no-track myr11 mybase2 &&\n \ttest \"z$(git config branch.myr11.remote)\" = z &&\n \ttest \"z$(git config branch.myr11.merge)\" = z &&\n-\ttest \"z$(git config branch.myr11.rebase)\" = z\n+\ttest \"z$(git config branch.myr11.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr11.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase unconfigured on untracked remote branch' '\n@@ -355,95 +375,112 @@ test_expect_success 'autosetuprebase unconfigured on untracked remote branch' '\n \tgit branch --no-track myr12 local/master &&\n \ttest \"z$(git config branch.myr12.remote)\" = z &&\n \ttest \"z$(git config branch.myr12.merge)\" = z &&\n-\ttest \"z$(git config branch.myr12.rebase)\" = z\n+\ttest \"z$(git config branch.myr12.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr12.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase never on an untracked local branch' '\n \tgit config branch.autosetuprebase never &&\n+\tgit config branch.autosetuppreservemerges never &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --no-track myr13 mybase2 &&\n \ttest \"z$(git config branch.myr13.remote)\" = z &&\n \ttest \"z$(git config branch.myr13.merge)\" = z &&\n-\ttest \"z$(git config branch.myr13.rebase)\" = z\n+\ttest \"z$(git config branch.myr13.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr13.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase local on an untracked local branch' '\n \tgit config branch.autosetuprebase local &&\n+\tgit config branch.autosetuppreservemerges local &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --no-track myr14 mybase2 &&\n \ttest \"z$(git config branch.myr14.remote)\" = z &&\n \ttest \"z$(git config branch.myr14.merge)\" = z &&\n-\ttest \"z$(git config branch.myr14.rebase)\" = z\n+\ttest \"z$(git config branch.myr14.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr14.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase remote on an untracked local branch' '\n \tgit config branch.autosetuprebase remote &&\n+\tgit config branch.autosetuppreservemerges remote &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --no-track myr15 mybase2 &&\n \ttest \"z$(git config branch.myr15.remote)\" = z &&\n \ttest \"z$(git config branch.myr15.merge)\" = z &&\n-\ttest \"z$(git config branch.myr15.rebase)\" = z\n+\ttest \"z$(git config branch.myr15.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr15.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase always on an untracked local branch' '\n \tgit config branch.autosetuprebase always &&\n+\tgit config branch.autosetuppreservemerges always &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --no-track myr16 mybase2 &&\n \ttest \"z$(git config branch.myr16.remote)\" = z &&\n \ttest \"z$(git config branch.myr16.merge)\" = z &&\n-\ttest \"z$(git config branch.myr16.rebase)\" = z\n+\ttest \"z$(git config branch.myr16.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr16.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase never on an untracked remote branch' '\n \tgit config branch.autosetuprebase never &&\n+\tgit config branch.autosetuppreservemerges never &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --no-track myr17 local/master &&\n \ttest \"z$(git config branch.myr17.remote)\" = z &&\n \ttest \"z$(git config branch.myr17.merge)\" = z &&\n-\ttest \"z$(git config branch.myr17.rebase)\" = z\n+\ttest \"z$(git config branch.myr17.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr17.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase local on an untracked remote branch' '\n \tgit config branch.autosetuprebase local &&\n+\tgit config branch.autosetuppreservemerges local &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --no-track myr18 local/master &&\n \ttest \"z$(git config branch.myr18.remote)\" = z &&\n \ttest \"z$(git config branch.myr18.merge)\" = z &&\n-\ttest \"z$(git config branch.myr18.rebase)\" = z\n+\ttest \"z$(git config branch.myr18.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr18.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase remote on an untracked remote branch' '\n \tgit config branch.autosetuprebase remote &&\n+\tgit config branch.autosetuppreservemerges remote &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --no-track myr19 local/master &&\n \ttest \"z$(git config branch.myr19.remote)\" = z &&\n \ttest \"z$(git config branch.myr19.merge)\" = z &&\n-\ttest \"z$(git config branch.myr19.rebase)\" = z\n+\ttest \"z$(git config branch.myr19.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr19.preservemerges)\" = z\n '\n \n test_expect_success 'autosetuprebase always on an untracked remote branch' '\n \tgit config branch.autosetuprebase always &&\n+\tgit config branch.autosetuppreservemerges always &&\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n \tgit branch --no-track myr20 local/master &&\n \ttest \"z$(git config branch.myr20.remote)\" = z &&\n \ttest \"z$(git config branch.myr20.merge)\" = z &&\n-\ttest \"z$(git config branch.myr20.rebase)\" = z\n+\ttest \"z$(git config branch.myr20.rebase)\" = z &&\n+\ttest \"z$(git config branch.myr20.preservemerges)\" = z\n '\n \n test_expect_success 'detect misconfigured autosetuprebase (bad value)' '\n@@ -451,6 +488,11 @@ test_expect_success 'detect misconfigured autosetuprebase (bad value)' '\n \ttest_must_fail git branch\n '\n \n+test_expect_success 'detect misconfigured autosetuppreservemerges (bad value)' '\n+\tgit config branch.autosetuppreservemerges garbage &&\n+\ttest_must_fail git branch\n+'\n+\n test_expect_success 'detect misconfigured autosetuprebase (no value)' '\n \tgit config --unset branch.autosetuprebase &&\n \techo \"[branch] autosetuprebase\" >> .git/config &&\n@@ -458,4 +500,11 @@ test_expect_success 'detect misconfigured autosetuprebase (no value)' '\n \tgit config --unset branch.autosetuprebase\n '\n \n+test_expect_success 'detect misconfigured autosetuppreservemerges (no value)' '\n+\tgit config --unset branch.autosetuppreservemerges &&\n+\techo \"[branch] autosetuppreservemerges\" >> .git/config &&\n+\ttest_must_fail git branch &&\n+\tgit config --unset branch.autosetuppreservemerges\n+'\n+\n test_done\ndiff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh\nindex 9a376ef..5f4ce56 100644\n--- a/t/t3409-rebase-preserve-merges.sh\n+++ b/t/t3409-rebase-preserve-merges.sh\n@@ -25,44 +25,59 @@ export GIT_AUTHOR_EMAIL\n test_expect_success 'setup for merge-preserving rebase' \\\n \t'echo First > A &&\n \tgit add A &&\n-\tgit-commit -m \"Add A1\" &&\n+\tgit-commit -m A1 &&\n \tgit checkout -b topic &&\n \techo Second > B &&\n \tgit add B &&\n-\tgit-commit -m \"Add B1\" &&\n+\tgit-commit -m B1 &&\n+\tgit tag B1 &&\n+\techo Third >> B &&\n+\tgit commit -a -m B2\n \tgit checkout -f master &&\n-\techo Third >> A &&\n-\tgit-commit -a -m \"Modify A2\" &&\n+\techo Fourth >> A &&\n+\tgit-commit -a -m A2 &&\n+\tgit clone . clone\n+'\n \n-\tgit clone ./. clone1 &&\n-\tcd clone1 &&\n+test_expect_success 'git pull --rebase -p on moved topic' '\n+\tcd clone &&\n \tgit checkout -b topic origin/topic &&\n+\tgit reset --hard B1 &&\n \tgit merge origin/master &&\n+\tgit pull --rebase --preserve-merges &&\n+\ttest $(git rev-list --all --pretty=oneline | grep A2 | wc -l) = 1 &&\n+\tgit checkout origin/topic &&\n+\tgit branch -D topic &&\n \tcd ..\n+'\n \n-\tgit clone ./. clone2\n-\tcd clone2 &&\n+test_expect_success 'rebase -p merge on moved topic' '\n+\tcd clone &&\n \tgit checkout -b topic origin/topic &&\n+\tgit reset --hard B1 &&\n \tgit merge origin/master &&\n-\tcd .. &&\n-\n-\tgit checkout topic &&\n-\techo Fourth >> B &&\n-\tgit commit -a -m \"Modify B2\"\n-'\n-\n-test_expect_success 'git pull --rebase -p on moved topic' '\n-\tcd clone1 &&\n-\tgit pull --rebase --preserve-merges &&\n-\ttest $(git rev-list --all --pretty=oneline | grep \"Modify A\" | wc -l) = 1\n+\tgit rebase -p origin/topic &&\n+\ttest 1 = $(git rev-list --all --pretty=oneline | grep A2 | wc -l) &&\n+\ttest 1 = $(git rev-list --all --pretty=oneline | grep \"Merge commit\" | wc -l) &&\n+\tgit checkout origin/topic &&\n+\tgit branch -D topic &&\n+\tcd ..\n '\n \n-test_expect_success 'rebase -p merge on moved topic' '\n-\tcd ../clone2 &&\n-\tgit fetch &&\n-\tgit rebase -p origin/topic &&\n-\ttest 1 = $(git rev-list --all --pretty=oneline | grep \"Modify A\" | wc -l) &&\n-\ttest 1 = $(git rev-list --all --pretty=oneline | grep \"Merge commit\" | wc -l)\n+test_expect_success 'git pull on moved topic' '\n+\tcd clone &&\n+\tgit config branch.autosetuppreservemerges always &&\n+\tgit checkout -b topic origin/topic &&\n+\ttest true = $(git config branch.topic.preservemerges) &&\n+\tgit reset --hard B1 &&\n+\tgit merge origin/master &&\n+\tgit pull &&\n+\ttest 1 = $(git rev-list --all --pretty=oneline | grep A2 | wc -l) &&\n+\ttest 1 = $(git rev-list --all --pretty=oneline | grep \"Merge commit\" | wc -l) &&\n+\tgit checkout origin/topic &&\n+\tgit branch -D topic &&\n+\tcd ..\n '\n \n test_done\n+\n-- \n1.6.0.2\n"},{"id":"93076","messageId":"20081015030706.f876bfff.stephen@exigencecorp.com","threadId":"15639","inReplyTo":"20080924001027.GA19264@neumann","subject":"Re: [PATCH 1/3] Prepare for non-interactive merge-preserving rebase","fromName":"Stephen Haberman","fromEmail":"stephen@exigencecorp.com","sentAt":"2008-10-15T08:07:06Z","receivedAt":"2008-10-15T08:07:06Z","isPatch":true,"sender":{"key":"stephen@exigencecorp.com","avatar":"https://gravatar.com/avatar/23b93ad70a06ce53505f17ddba65176edbcfb6588e7a4c1a2dca04aaf0a6aff1?d=mp&s=160"},"body":"\n> The following DAG is created by the commands below:\n> \n>   -A---B      master\n>     \\\n>      C---M    topic\n>       \\ /\n>        D\n> \n>   git init\n>   echo 1 >foo\n>   git add foo\n>   git commit -m 'first on master'       # A\n>   echo 2 >>foo\n>   git commit -m 'second on master' foo  # B\n>   git checkout -b topic HEAD^\n>   echo 1 >bar\n>   git add bar\n>   git commit -m 'first on topic'        # C\n>   git checkout -b subtopic\n>   echo 1 >baz\n>   git add baz\n>   git commit -m 'first on subtopic'     # D\n>   git checkout topic\n>   git merge --no-ff subtopic            # M\n> \n> If I now execute 'git rebase -p master topic', I get the following:\n> \n>   -A---B            master\n>     \\   \\\n>      \\   C'---M'    topic\n>       \\      /\n>        C----D\n\nFollowing up on this old thread, I can't get M' to have the old parent\nD. I always see D change to D' and then topic is fast fowarded to D'\ninstead of an M' showing up. (I've tried 1.6.0.2, my rebase-i-p changes,\nand sp/maint.)\n\n> But I would rather like to have the following:\n> \n>   -A---B            master\n>         \\\n>          C'---M'    topic\n>           \\  /\n>            D'\n> \n> Would such a behaviour possible at all?\n\nYes, I think it just takes the following patch:\n\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -251,7 +251,7 @@ pick_one_preserving_merges () {\n                                GIT_AUTHOR_EMAIL=\"$GIT_AUTHOR_EMAIL\" \\\n                                GIT_AUTHOR_DATE=\"$GIT_AUTHOR_DATE\" \\\n                                output git merge $STRATEGY -m \"$msg\" \\\n-                                       $new_parents\n+                                       --no-ff $new_parents\n\nApplying this to either sp/maint or my rebase-i-p changes gets your\ndesired output.\n\nWith the only caveat being that the subtopic branch stays pointing at\nthe old D--since you are rebasing topic, it does not change where\nsubtopic points when rewriting D -> D'.\n\nMusing, I could see moving subtopic being possible, definitely with git\nsequencer, but also with a --other-branches-follow-rewrites flag of some\nsort that, after rewriting hash1->hash2, just finds any local branches\npointing at hash1 and updates their refs to be hash2. Not that I'm\nreally suggesting it, but I don't think it would be that hard.\n\nAnyway, subtopic still pointing at D aside, I think your desired output\nmakes sense, given you've explicitly told rebase to preserve merges. If\nyou wanted a non-ff M in the first place, I think passing along a\n--no-ff to keep M' around is reasonable. And would otherwise be harmless.\n\nI can write a test/patch for this unless you beat me to it or other\nthink it is unreasonable.\n\n- Stephen\n"}]}