Re: [PATCH v6 4/6] rebase: fast-forward --onto in more cases

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Denton

On 17/04/2019 19:01, Denton Liu wrote:
Before, when we had the following graph,

	A---B---C (master)
	     \
	      D (side)

running 'git rebase --onto master... master side' would result in D
being always rebased, no matter what. However, the desired behavior is
that rebase should notice that this is fast-forwardable and do that
instead.

Add detection to `can_fast_forward` so that this case can be detected
and a fast-forward will be performed. First of all, rewrite the function
to use gotos which simplifies the logic. Next, since the

	options.upstream &&
	!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)

conditions were removed in `cmd_rebase`, we reintroduce a substitute in
`can_fast_forward`. In particular, checking the merge bases of
`upstream` and `head` fixes a failing case in t3416.

The abbreviated graph for t3416 is as follows:

		    F---G topic
		   /
	  A---B---C---D---E master

and the failing command was

	git rebase --onto master...topic F topic

Before, Git would see that there was one merge base (C), and the merge
and onto were the same so it would incorrectly return 1, indicating that
we could fast-forward. This would cause the rebased graph to be 'ABCFG'
when we were expecting 'ABCG'.

With the additional logic, we detect that upstream and head's merge base
is F. Since onto isn't F, it means we're not rebasing the full set of
commits from master..topic. Since we're excluding some commits, a
fast-forward cannot be performed and so we correctly return 0.

Add '-f' to test cases that failed as a result of this change because
they were not expecting a fast-forward so that a rebase is forced.

While we're at it, remove a trailing whitespace from rebase.c.

Helped-by: Phillip Wood <phillip.wood@xxxxxxxxxxxxx>
Signed-off-by: Denton Liu <liu.denton@xxxxxxxxx>
---
  builtin/rebase.c               | 28 +++++++++++++++++++---------
  t/t3400-rebase.sh              |  2 +-
  t/t3404-rebase-interactive.sh  |  2 +-
  t/t3432-rebase-fast-forward.sh |  4 ++--
  4 files changed, 23 insertions(+), 13 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index de10b6f5ad..f5aca1eee0 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -895,8 +895,8 @@ static int is_linear_history(struct commit *from, struct commit *to)
  	return 1;
  }
-static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
-			    struct object_id *merge_base)
+static int can_fast_forward(struct commit *onto, struct commit *upstream,
+			    struct object_id *head_oid, struct object_id *merge_base)
  {
  	struct commit *head = lookup_commit(the_repository, head_oid);
  	struct commit_list *merge_bases = NULL;
@@ -915,6 +915,18 @@ static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
  	if (!oideq(merge_base, &onto->object.oid))
  		goto done;
+ if (!upstream)
+		goto done;
+
+	free_commit_list(merge_bases);
+	merge_bases = get_merge_bases(upstream, head);
+	if (!merge_bases || merge_bases->next) {
+		goto done;
+	}

Thanks for changing the ifs in this patch and the previous one, I find it much easier to follow now. Just one style nit (probably not worth a reroll) - we don't put braces around a single conditional statement like this unless another branch of the same if requires them (then all branches should have them).

Best Wishes

Phillip


+
+	if (!oideq(&onto->object.oid, &merge_bases->item->object.oid))
+		goto done;
+
  	res = 1;
done:
@@ -1688,13 +1700,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/*
  	 * Check if we are already based on onto with linear history,
-	 * but this should be done only when upstream and onto are the same
-	 * and if this is not an interactive rebase.
+	 * but this should be done if this is not an interactive rebase.
  	 */
-	if (can_fast_forward(options.onto, &options.orig_head, &merge_base) &&
-	    !is_interactive(&options) && !options.restrict_revision &&
-	    options.upstream &&
-	    !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) {
+	if (can_fast_forward(options.onto, options.upstream, &options.orig_head,
+		    &merge_base) &&
+	    !is_interactive(&options) && !options.restrict_revision) {
  		int flag;
if (!(options.flags & REBASE_FORCE)) {
@@ -1788,7 +1798,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
  	strbuf_addf(&msg, "%s: checkout %s",
  		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
  	if (reset_head(&options.onto->object.oid, "checkout", NULL,
-		       RESET_HEAD_DETACH | RESET_ORIG_HEAD |
+		       RESET_HEAD_DETACH | RESET_ORIG_HEAD |
  		       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
  		       NULL, msg.buf))
  		die(_("Could not detach HEAD"));
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 460d0523be..604d624ff8 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -295,7 +295,7 @@ test_expect_success 'rebase--am.sh and --show-current-patch' '
  		echo two >>init.t &&
  		git commit -a -m two &&
  		git tag two &&
-		test_must_fail git rebase --onto init HEAD^ &&
+		test_must_fail git rebase -f --onto init HEAD^ &&
  		GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
  		grep "show.*$(git rev-parse two)" stderr
  	)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index b60b11f9f2..f054186cc7 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1066,7 +1066,7 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int
  	git reset --hard &&
  	git checkout conflict-branch &&
  	set_fake_editor &&
-	test_must_fail git rebase --onto HEAD~2 HEAD~ &&
+	test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
  	test_must_fail git rebase --edit-todo &&
  	git rebase --abort
  '
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 4f04d67fd7..d0e5b1f3e6 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -64,9 +64,9 @@ test_expect_success 'add work to upstream' '
  changes='our and their changes'
  test_rebase_same_head success --onto B B
  test_rebase_same_head success --onto B... B
-test_rebase_same_head failure --onto master... master
+test_rebase_same_head success --onto master... master
  test_rebase_same_head failure --fork-point --onto B B
  test_rebase_same_head failure --fork-point --onto B... B
-test_rebase_same_head failure --fork-point --onto master... master
+test_rebase_same_head success --fork-point --onto master... master
test_done




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux