All of lore.kernel.org
 help / color / mirror / Atom feed
From: "D. Ben Knoble" <ben.knoble+github@gmail.com>
To: git@vger.kernel.org
Cc: "D. Ben Knoble" <ben.knoble+github@gmail.com>,
	"Alex Henrie" <alexhenrie24@gmail.com>,
	"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
	"Felipe Contreras" <felipe.contreras@gmail.com>,
	"Patrick Steinhardt" <ps@pks.im>,
	"Junio C Hamano" <gitster@pobox.com>,
	"Elijah Newren" <newren@gmail.com>
Subject: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only
Date: Tue,  4 Feb 2025 22:06:28 -0500	[thread overview]
Message-ID: <20250205030642.95252-1-ben.knoble+github@gmail.com> (raw)

When running "git pull" with the following configuration options, we
fail to merge divergent branches:

- pull.ff=only
- pull.rebase (unset)
- branch.<current_branch>.rebase=true

Yet it seems that the user intended to make rebase the default for the
current branch while using --ff-only for non-rebase pulls. Since this
case appears uncovered by existing tests, changing the behavior here
might be safe: it makes what was an error into a successful rebase.

Add a test for the behavior and make it pass: this requires knowing from
where the rebase was requested. Previous commits (e4dc25ed49 (pull:
since --ff-only overrides, handle it first, 2021-07-22), adc27d6a93
(pull: make --rebase and --no-rebase override pull.ff=only, 2021-07-22))
took care to differentiate that --rebase overrides pull.ff=only, but
don't distinguish which config setting requests the rebase. Split
config_get_rebase into 2 parts so that we know where the rebase comes
from, since we only want to allow branch-config to override pull.ff=only
(like --rebase does); pull.rebase should still be overridden by
pull.ff=only or --ff-only.

Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
---
Notes:
    - I also looked at ea1954af77 (pull: should be noop when already-up-to-date,
      2021-11-17) when trying to understand how some options override others,
      but it didnt' seem germane to the final version.
    - I think I've got the right test script, since it's the one that started
      failing before I added the "else" branch to the new code (which also
      confirms that it's necessary to preserve current behavior); the only new
      behavior should be the one mentioned by the new test.
    - A possible #leftoverbits: it would be good to document more clearly the
      interplay of --ff[-only], --rebase, pull.ff, pull.rebase, and
      branch.<name>.rebase, particularly when they override each other.
      Confusingly, branch.<name>.merge has nothing to do with whether pull will
      merge or rebase ;) lest you think I'd forgotten something that _looks_
      parallel to pull.rebase.

 builtin/pull.c               | 39 ++++++++++++++++++++++++++++--------
 t/t7601-merge-pull-config.sh |  8 ++++++++
 2 files changed, 39 insertions(+), 8 deletions(-)

diff --git a/builtin/pull.c b/builtin/pull.c
index 9c4a00620a..c30f233dcc 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -326,13 +326,13 @@ static const char *config_get_ff(void)
 }
 
 /**
- * Returns the default configured value for --rebase. It first looks for the
+ * Returns the default configured value for --rebase. It looks for the
  * value of "branch.$curr_branch.rebase", where $curr_branch is the current
  * branch, and if HEAD is detached or the configuration key does not exist,
- * looks for the value of "pull.rebase". If both configuration keys do not
- * exist, returns REBASE_FALSE.
+ * considers the result unspecified. Follow up by checking
+ * config_get_rebase_pull.
  */
-static enum rebase_type config_get_rebase(int *rebase_unspecified)
+static enum rebase_type config_get_rebase_branch(int *rebase_unspecified)
 {
 	struct branch *curr_branch = branch_get("HEAD");
 	const char *value;
@@ -349,11 +349,22 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified)
 		free(key);
 	}
 
+	*rebase_unspecified = 1;
+	return REBASE_INVALID;
+}
+
+/*
+ * Looks for the value of "pull.rebase". If it does not exist, returns
+ * REBASE_FALSE.
+ */
+static enum rebase_type config_get_rebase_pull(int *rebase_unspecified)
+{
+	const char *value;
+
 	if (!git_config_get_value("pull.rebase", &value))
 		return parse_config_rebase("pull.rebase", value, 1);
 
 	*rebase_unspecified = 1;
-
 	return REBASE_FALSE;
 }
 
@@ -1026,7 +1037,7 @@ int cmd_pull(int argc,
 		 * are relying on the next if-condition happening before
 		 * the config_get_rebase() call so that an explicit
 		 * "--rebase" can override a config setting of
-		 * pull.ff=only.
+		 * pull.ff=only. [continued…]
 		 */
 		if (opt_rebase >= 0 && opt_ff && !strcmp(opt_ff, "--ff-only")) {
 			free(opt_ff);
@@ -1034,8 +1045,20 @@ int cmd_pull(int argc,
 		}
 	}
 
-	if (opt_rebase < 0)
-		opt_rebase = config_get_rebase(&rebase_unspecified);
+	if (opt_rebase < 0) {
+		/*
+		 * […continued] But, if the config requests rebase *for this
+		 * branch*, override --ff-only, which otherwise takes precedence
+		 * over pull.rebase=true.
+		 */
+		opt_rebase = config_get_rebase_branch(&rebase_unspecified);
+		if (opt_rebase >= 0 && opt_ff && !strcmp(opt_ff, "--ff-only")) {
+			free(opt_ff);
+			opt_ff = xstrdup("--ff");
+		} else {
+		    opt_rebase = config_get_rebase_pull(&rebase_unspecified);
+		}
+	}
 
 	if (repo_read_index_unmerged(the_repository))
 		die_resolve_conflict("pull");
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 199a1d5db3..fd99f46aad 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -113,6 +113,14 @@
 	test_grep ! "You have divergent branches" err
 '
 
+test_expect_success 'pull.rebase not set and pull.ff=only and branch.<name>.rebase=true (not-fast-forward)' '
+	git reset --hard c2 &&
+	test_config pull.ff only &&
+	git switch -c bc2 &&
+	test_config branch.bc2.rebase true &&
+	git pull . c1
+'
+
 test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --rebase . c1 2>err &&
-- 
2.47.0


             reply	other threads:[~2025-02-05  3:11 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-05  3:06 D. Ben Knoble [this message]
2025-02-05 13:08 ` [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only Junio C Hamano
2025-02-05 16:36   ` D. Ben Knoble
2025-02-05 17:42     ` Junio C Hamano
2025-02-05 21:14       ` D. Ben Knoble
2025-02-07  2:35         ` Alex Henrie
2025-02-10 20:26           ` D. Ben Knoble
2025-02-11  6:55             ` Alex Henrie
2025-04-22 20:03               ` D. Ben Knoble
2025-04-22 19:58         ` D. Ben Knoble
2025-04-22 20:05           ` D. Ben Knoble
2025-04-22 20:07             ` D. Ben Knoble
2025-04-22 20:30               ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250205030642.95252-1-ben.knoble+github@gmail.com \
    --to=ben.knoble+github@gmail.com \
    --cc=alexhenrie24@gmail.com \
    --cc=avarab@gmail.com \
    --cc=felipe.contreras@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=newren@gmail.com \
    --cc=ps@pks.im \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.