git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] rebase: be cleverer with rebased upstream branches

From
Martin von Zweigbergk <martin.von.zweigbergk@gmail.com>
Date
Feb 14, 2011, 13:51 UTC
Message-ID
<1297691481-3308-1-git-send-email-martin.von.zweigbergk@gmail.com>

Since c85c792 (pull --rebase: be cleverer with rebased upstream branches, 2008-01-26), 'git pull --rebase' has used the reflog to try to rebase from the old upstream onto the new upstream.

However, if, instead of 'git pull --rebase', the user were to do 'git fetch' followed by 'git rebase', the reflog would not be walked. This patch teaches "git rebase" the same reflog-walking tricks that 'git pull --rebase' already knows.

This may be useful for rebasing one branch against another local branch that has been rebased. Currently, you would have to do that using 'git rebase --onto' or by configuring it on the branch.

It might seem like most of the related code in git-pull.sh can be removed once git-rebase.sh supports reflog walking. Unfortunately, not much of it can be removed, though. The reason is that git-pull.sh simulates one step of "reflog walking" by keeping track of the position of the remote-tracking branch before and after the fetch operation. This does not rely on reflogs. There are at least two cases where the reflog is not used: a) when it is disabled, b) when the remote branch was specified on the command line (as in 'git pull --rebase origin master'). In both of these cases, git-pull.sh remembers the position of the reference before the fetch and uses that as a kind of '$upstream@{1}'.

Signed-off-by: Martin von Zweigbergk <martin.von.zweigbergk@gmail.com>
---
    This applies on top of mz/rebase.
    I have been using this in combination with the patch for defaulting
    rebase to @{upstream} for a few months now. I find it very convenient
    e.g. when Junio has published some changes and I want to rebase my
    branches. Then I just go to each of my branches and run 'git rebase'
    without any arguments, and they get rebased correctly, whether they
    are based on origin/master, origin/pu (they shouldn't be, but let's
    say they are anyway), or on a local branch that is in turn based on
    e.g. origin/master.
    HOWEVER, this causes a very noticable delay in some cases. With this
    patch, 'git rebase' walks the reflog of the upstream ref until it
    finds a commit that the branch-to-rebase contains. If the upstream ref
    has moved a lot since the branch was last rebased, there may be quite
    a few commits to test before the old upstream commit is found.
    The same thing can already occur with 'git pull --rebase' for exactly
    the same reasons. For example, assume that your upstream remote branch
    changes quite frequently and that you often fetch from the remote so
    that your origin/master gets a long reflog. If you then checkout some
    branch you had not been working on for a while, and run 'git pull',
    you get into the same situation. The delay is probably less likely to
    be noticed in the case of 'git pull --rebase', however, since most
    users will probably assume it is a problem with the network or the
    server.
    Of course, 'git pull --rebase' can also be used with a local branch
    configured as upstream. In this case, the behavior today is just like
    what this patch introduces for 'git rebase'.
    What do you think? I think it's a useful feature, but how do we handle
    the delay problem? Maybe simply by making it configurable?
    Should such a configuration variable apply to 'git pull --rebase' as
    well? It would seem inconsistent otherwise, but maybe that's ok since
    'git pull --rebase' is usually used with remote-tracking branches,
    which probably change less frequently. Btw, is this a correct
    assumption? It is definitely true for my own work on git, but I
    actually think it's the other way around for my work at $dayjob. Am I
    missing some part to the puzzle that explains why I had not noticed
    the delay until I started using this patch?
 Documentation/git-rebase.txt |    7 ++++++-
 git-rebase.sh                |   13 +++++++++++++
 t/t3400-rebase.sh            |   26 ++++++++++++++++++++++++++
 3 files changed, 45 insertions(+), 1 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 095a67f..d4dbe28 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -24,7 +24,12 @@ it remains on the current branch.
 All changes made by commits in the current branch but that are not
 in <upstream> are saved to a temporary area.  This is the same set
 of commits that would be shown by `git log <upstream>..HEAD` (or
-`git log HEAD`, if --root is specified).
+`git log HEAD`, if --root is specified). If, however, there is a ref
+for the upstream branch, and this branch was rebased since the
+current branch was last rebased, the rebase uses that information to
+avoid rebasing changes done on the upstream branch. If you do not want
+'git rebase' to use this intelligence, refer to the upstream without
+using a reference (e.g. 'master~0').
 
 The current branch is reset to <upstream>, or <newbase> if the
 --onto option was supplied.  This has the exact same effect as
diff --git a/git-rebase.sh b/git-rebase.sh
index 5abfeac..1bc0c29 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -466,6 +466,19 @@ esac
 
 require_clean_work_tree "rebase" "Please commit or stash them."
 
+test -n "$upstream_name" && for reflog in \
+	$(git rev-list -g $upstream_name 2>/dev/null)
+do
+	if test $reflog = $(git merge-base $reflog $orig_head)
+	then
+		if test $reflog != $(git merge-base $onto $reflog)
+		then
+			upstream=$reflog
+		fi
+		break
+	fi
+done
+
 # Now we are rebasing commits $upstream..$orig_head (or with --root,
 # everything leading up to $orig_head) on top of $onto
 
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 349eebd..b64df31 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -209,4 +209,30 @@ test_expect_success 'rebase -m can copy notes' '
 	test "a note" = "$(git notes show HEAD)"
 '
 
+test_expect_success 'rebase against rebased upstream uses reflog' '
+	git checkout my-topic-branch &&
+	echo "Conflicting modification" > B &&
+	git add B &&
+	git commit -m "Modify B" &&
+	git reset --hard nonlinear &&
+	git checkout -b old-topic my-topic-branch@{1} &&
+	echo def > D &&
+	git add D &&
+	git commit -m "Add D" &&
+	git rebase my-topic-branch &&
+	test $(git rev-parse HEAD^) = $(git rev-parse my-topic-branch)
+'
+
+test_expect_success 'rebase against forwarded upstream does not reapply patches' '
+	git checkout my-topic-branch &&
+	echo abc > B &&
+	git add B &&
+	git commit -m "Conficting B" &&
+	git reset HEAD~2 &&
+	git reset HEAD@{1} &&
+	git checkout old-topic &&
+	git rebase my-topic-branch &&
+	test $(git rev-parse HEAD^) = $(git rev-parse my-topic-branch)
+'
+
 test_done
-- 
1.7.4.rc2.33.g8a14f
Next: Santi Béjar
Message 1 of 19 in “rebase: be cleverer with rebased upstream branches”
  1. rebase: be cleverer with rebased upstream branchesMartin von Zweigbergk, Feb 14, 2011
  2. Santi BéjarFeb 15, 2011
  3. Martin von ZweigbergkFeb 16, 2011
  4. Santi BéjarFeb 16, 2011
  5. Junio C HamanoFeb 15, 2011
  6. Martin von ZweigbergkFeb 16, 2011
  7. Santi BéjarFeb 16, 2011
  8. Santi BéjarFeb 16, 2011
  9. Junio C HamanoFeb 16, 2011
  10. Santi BéjarFeb 16, 2011
  11. Martin von ZweigbergkFeb 16, 2011
  12. Santi BéjarFeb 17, 2011
  13. Martin von ZweigbergkMar 12, 2011
  14. Santi BéjarMar 12, 2011
  15. Martin von ZweigbergkMar 13, 2011
  16. Martin von ZweigbergkMar 13, 2011
  17. Junio C HamanoMar 13, 2011
  18. Santi BéjarMar 13, 2011
  19. Santi BéjarMar 13, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.