|
@@ -525,6 +525,193 @@ class CommitSignTests(PorcelainGpgTestCase):
|
|
|
# GPG Signatures aren't deterministic, so we can't do a static assertion.
|
|
|
commit.verify()
|
|
|
|
|
|
+ def test_sign_uses_config_signingkey(self) -> None:
|
|
|
+ """Test that sign=True uses user.signingKey from config."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up user.signingKey in config
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create commit with sign=True (should use signingKey from config)
|
|
|
+ sha = porcelain.commit(
|
|
|
+ self.repo.path,
|
|
|
+ message="Signed with configured key",
|
|
|
+ author="Joe <joe@example.com>",
|
|
|
+ committer="Bob <bob@example.com>",
|
|
|
+ signoff=True, # This should read user.signingKey from config
|
|
|
+ )
|
|
|
+
|
|
|
+ self.assertIsInstance(sha, bytes)
|
|
|
+ self.assertEqual(len(sha), 40)
|
|
|
+
|
|
|
+ commit = self.repo.get_object(sha)
|
|
|
+ # Verify the commit is signed with the configured key
|
|
|
+ commit.verify()
|
|
|
+ commit.verify(keyids=[PorcelainGpgTestCase.DEFAULT_KEY_ID])
|
|
|
+
|
|
|
+ def test_commit_gpg_sign_config_enabled(self) -> None:
|
|
|
+ """Test that commit.gpgSign=true automatically signs commits."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up user.signingKey and commit.gpgSign in config
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.set(("commit",), "gpgSign", True)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create commit without explicit signoff parameter (should auto-sign due to config)
|
|
|
+ sha = porcelain.commit(
|
|
|
+ self.repo.path,
|
|
|
+ message="Auto-signed commit",
|
|
|
+ author="Joe <joe@example.com>",
|
|
|
+ committer="Bob <bob@example.com>",
|
|
|
+ # No signoff parameter - should use commit.gpgSign config
|
|
|
+ )
|
|
|
+
|
|
|
+ self.assertIsInstance(sha, bytes)
|
|
|
+ self.assertEqual(len(sha), 40)
|
|
|
+
|
|
|
+ commit = self.repo.get_object(sha)
|
|
|
+ # Verify the commit is signed due to config
|
|
|
+ commit.verify()
|
|
|
+ commit.verify(keyids=[PorcelainGpgTestCase.DEFAULT_KEY_ID])
|
|
|
+
|
|
|
+ def test_commit_gpg_sign_config_disabled(self) -> None:
|
|
|
+ """Test that commit.gpgSign=false does not sign commits."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up user.signingKey and commit.gpgSign=false in config
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.set(("commit",), "gpgSign", False)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create commit without explicit signoff parameter (should not sign)
|
|
|
+ sha = porcelain.commit(
|
|
|
+ self.repo.path,
|
|
|
+ message="Unsigned commit",
|
|
|
+ author="Joe <joe@example.com>",
|
|
|
+ committer="Bob <bob@example.com>",
|
|
|
+ # No signoff parameter - should use commit.gpgSign=false config
|
|
|
+ )
|
|
|
+
|
|
|
+ self.assertIsInstance(sha, bytes)
|
|
|
+ self.assertEqual(len(sha), 40)
|
|
|
+
|
|
|
+ commit = self.repo.get_object(sha)
|
|
|
+ # Verify the commit is not signed
|
|
|
+ self.assertIsNone(commit._gpgsig)
|
|
|
+
|
|
|
+ def test_commit_gpg_sign_config_no_signing_key(self) -> None:
|
|
|
+ """Test that commit.gpgSign=true works without user.signingKey (uses default)."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up commit.gpgSign but no user.signingKey
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("commit",), "gpgSign", True)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create commit without explicit signoff parameter (should auto-sign with default key)
|
|
|
+ sha = porcelain.commit(
|
|
|
+ self.repo.path,
|
|
|
+ message="Default signed commit",
|
|
|
+ author="Joe <joe@example.com>",
|
|
|
+ committer="Bob <bob@example.com>",
|
|
|
+ # No signoff parameter - should use commit.gpgSign config with default key
|
|
|
+ )
|
|
|
+
|
|
|
+ self.assertIsInstance(sha, bytes)
|
|
|
+ self.assertEqual(len(sha), 40)
|
|
|
+
|
|
|
+ commit = self.repo.get_object(sha)
|
|
|
+ # Verify the commit is signed with default key
|
|
|
+ commit.verify()
|
|
|
+
|
|
|
+ def test_explicit_signoff_overrides_config(self) -> None:
|
|
|
+ """Test that explicit signoff parameter overrides commit.gpgSign config."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up commit.gpgSign=false but explicitly pass signoff=True
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.set(("commit",), "gpgSign", False)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create commit with explicit signoff=True (should override config)
|
|
|
+ sha = porcelain.commit(
|
|
|
+ self.repo.path,
|
|
|
+ message="Explicitly signed commit",
|
|
|
+ author="Joe <joe@example.com>",
|
|
|
+ committer="Bob <bob@example.com>",
|
|
|
+ signoff=True, # This should override commit.gpgSign=false
|
|
|
+ )
|
|
|
+
|
|
|
+ self.assertIsInstance(sha, bytes)
|
|
|
+ self.assertEqual(len(sha), 40)
|
|
|
+
|
|
|
+ commit = self.repo.get_object(sha)
|
|
|
+ # Verify the commit is signed despite config=false
|
|
|
+ commit.verify()
|
|
|
+ commit.verify(keyids=[PorcelainGpgTestCase.DEFAULT_KEY_ID])
|
|
|
+
|
|
|
+ def test_explicit_false_disables_signing(self) -> None:
|
|
|
+ """Test that explicit signoff=False disables signing even with config=true."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up commit.gpgSign=true but explicitly pass signoff=False
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.set(("commit",), "gpgSign", True)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create commit with explicit signoff=False (should disable signing)
|
|
|
+ sha = porcelain.commit(
|
|
|
+ self.repo.path,
|
|
|
+ message="Explicitly unsigned commit",
|
|
|
+ author="Joe <joe@example.com>",
|
|
|
+ committer="Bob <bob@example.com>",
|
|
|
+ signoff=False, # This should override commit.gpgSign=true
|
|
|
+ )
|
|
|
+
|
|
|
+ self.assertIsInstance(sha, bytes)
|
|
|
+ self.assertEqual(len(sha), 40)
|
|
|
+
|
|
|
+ commit = self.repo.get_object(sha)
|
|
|
+ # Verify the commit is NOT signed despite config=true
|
|
|
+ self.assertIsNone(commit._gpgsig)
|
|
|
+
|
|
|
|
|
|
class TimezoneTests(PorcelainTestCase):
|
|
|
def put_envs(self, value) -> None:
|
|
@@ -2282,6 +2469,205 @@ class TagCreateSignTests(PorcelainGpgTestCase):
|
|
|
# GPG Signatures aren't deterministic, so we can't do a static assertion.
|
|
|
tag.verify()
|
|
|
|
|
|
+ def test_sign_uses_config_signingkey(self) -> None:
|
|
|
+ """Test that sign=True uses user.signingKey from config."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up user.signingKey in config
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create tag with sign=True (should use signingKey from config)
|
|
|
+ porcelain.tag_create(
|
|
|
+ self.repo.path,
|
|
|
+ b"signed-tag",
|
|
|
+ b"foo <foo@bar.com>",
|
|
|
+ b"Tag with configured key",
|
|
|
+ annotated=True,
|
|
|
+ sign=True, # This should read user.signingKey from config
|
|
|
+ )
|
|
|
+
|
|
|
+ tags = self.repo.refs.as_dict(b"refs/tags")
|
|
|
+ self.assertEqual(list(tags.keys()), [b"signed-tag"])
|
|
|
+ tag = self.repo[b"refs/tags/signed-tag"]
|
|
|
+ self.assertIsInstance(tag, Tag)
|
|
|
+
|
|
|
+ # Verify the tag is signed with the configured key
|
|
|
+ tag.verify()
|
|
|
+ tag.verify(keyids=[PorcelainGpgTestCase.DEFAULT_KEY_ID])
|
|
|
+
|
|
|
+ def test_tag_gpg_sign_config_enabled(self) -> None:
|
|
|
+ """Test that tag.gpgSign=true automatically signs tags."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up user.signingKey and tag.gpgSign in config
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.set(("tag",), "gpgSign", True)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create tag without explicit sign parameter (should auto-sign due to config)
|
|
|
+ porcelain.tag_create(
|
|
|
+ self.repo.path,
|
|
|
+ b"auto-signed-tag",
|
|
|
+ b"foo <foo@bar.com>",
|
|
|
+ b"Auto-signed tag",
|
|
|
+ annotated=True,
|
|
|
+ # No sign parameter - should use tag.gpgSign config
|
|
|
+ )
|
|
|
+
|
|
|
+ tags = self.repo.refs.as_dict(b"refs/tags")
|
|
|
+ self.assertEqual(list(tags.keys()), [b"auto-signed-tag"])
|
|
|
+ tag = self.repo[b"refs/tags/auto-signed-tag"]
|
|
|
+ self.assertIsInstance(tag, Tag)
|
|
|
+
|
|
|
+ # Verify the tag is signed due to config
|
|
|
+ tag.verify()
|
|
|
+ tag.verify(keyids=[PorcelainGpgTestCase.DEFAULT_KEY_ID])
|
|
|
+
|
|
|
+ def test_tag_gpg_sign_config_disabled(self) -> None:
|
|
|
+ """Test that tag.gpgSign=false does not sign tags."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up user.signingKey and tag.gpgSign=false in config
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.set(("tag",), "gpgSign", False)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create tag without explicit sign parameter (should not sign)
|
|
|
+ porcelain.tag_create(
|
|
|
+ self.repo.path,
|
|
|
+ b"unsigned-tag",
|
|
|
+ b"foo <foo@bar.com>",
|
|
|
+ b"Unsigned tag",
|
|
|
+ annotated=True,
|
|
|
+ # No sign parameter - should use tag.gpgSign=false config
|
|
|
+ )
|
|
|
+
|
|
|
+ tags = self.repo.refs.as_dict(b"refs/tags")
|
|
|
+ self.assertEqual(list(tags.keys()), [b"unsigned-tag"])
|
|
|
+ tag = self.repo[b"refs/tags/unsigned-tag"]
|
|
|
+ self.assertIsInstance(tag, Tag)
|
|
|
+
|
|
|
+ # Verify the tag is not signed
|
|
|
+ self.assertIsNone(tag._signature)
|
|
|
+
|
|
|
+ def test_tag_gpg_sign_config_no_signing_key(self) -> None:
|
|
|
+ """Test that tag.gpgSign=true works without user.signingKey (uses default)."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up tag.gpgSign but no user.signingKey
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("tag",), "gpgSign", True)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create tag without explicit sign parameter (should auto-sign with default key)
|
|
|
+ porcelain.tag_create(
|
|
|
+ self.repo.path,
|
|
|
+ b"default-signed-tag",
|
|
|
+ b"foo <foo@bar.com>",
|
|
|
+ b"Default signed tag",
|
|
|
+ annotated=True,
|
|
|
+ # No sign parameter - should use tag.gpgSign config with default key
|
|
|
+ )
|
|
|
+
|
|
|
+ tags = self.repo.refs.as_dict(b"refs/tags")
|
|
|
+ self.assertEqual(list(tags.keys()), [b"default-signed-tag"])
|
|
|
+ tag = self.repo[b"refs/tags/default-signed-tag"]
|
|
|
+ self.assertIsInstance(tag, Tag)
|
|
|
+
|
|
|
+ # Verify the tag is signed with default key
|
|
|
+ tag.verify()
|
|
|
+
|
|
|
+ def test_explicit_sign_overrides_config(self) -> None:
|
|
|
+ """Test that explicit sign parameter overrides tag.gpgSign config."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up tag.gpgSign=false but explicitly pass sign=True
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.set(("tag",), "gpgSign", False)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create tag with explicit sign=True (should override config)
|
|
|
+ porcelain.tag_create(
|
|
|
+ self.repo.path,
|
|
|
+ b"explicit-signed-tag",
|
|
|
+ b"foo <foo@bar.com>",
|
|
|
+ b"Explicitly signed tag",
|
|
|
+ annotated=True,
|
|
|
+ sign=True, # This should override tag.gpgSign=false
|
|
|
+ )
|
|
|
+
|
|
|
+ tags = self.repo.refs.as_dict(b"refs/tags")
|
|
|
+ self.assertEqual(list(tags.keys()), [b"explicit-signed-tag"])
|
|
|
+ tag = self.repo[b"refs/tags/explicit-signed-tag"]
|
|
|
+ self.assertIsInstance(tag, Tag)
|
|
|
+
|
|
|
+ # Verify the tag is signed despite config=false
|
|
|
+ tag.verify()
|
|
|
+ tag.verify(keyids=[PorcelainGpgTestCase.DEFAULT_KEY_ID])
|
|
|
+
|
|
|
+ def test_explicit_false_disables_tag_signing(self) -> None:
|
|
|
+ """Test that explicit sign=False disables signing even with config=true."""
|
|
|
+ c1, c2, c3 = build_commit_graph(
|
|
|
+ self.repo.object_store, [[1], [2, 1], [3, 1, 2]]
|
|
|
+ )
|
|
|
+ self.repo.refs[b"HEAD"] = c3.id
|
|
|
+
|
|
|
+ # Set up tag.gpgSign=true but explicitly pass sign=False
|
|
|
+ cfg = self.repo.get_config()
|
|
|
+ cfg.set(("user",), "signingKey", PorcelainGpgTestCase.DEFAULT_KEY_ID)
|
|
|
+ cfg.set(("tag",), "gpgSign", True)
|
|
|
+ cfg.write_to_path()
|
|
|
+
|
|
|
+ self.import_default_key()
|
|
|
+
|
|
|
+ # Create tag with explicit sign=False (should disable signing)
|
|
|
+ porcelain.tag_create(
|
|
|
+ self.repo.path,
|
|
|
+ b"explicit-unsigned-tag",
|
|
|
+ b"foo <foo@bar.com>",
|
|
|
+ b"Explicitly unsigned tag",
|
|
|
+ annotated=True,
|
|
|
+ sign=False, # This should override tag.gpgSign=true
|
|
|
+ )
|
|
|
+
|
|
|
+ tags = self.repo.refs.as_dict(b"refs/tags")
|
|
|
+ self.assertEqual(list(tags.keys()), [b"explicit-unsigned-tag"])
|
|
|
+ tag = self.repo[b"refs/tags/explicit-unsigned-tag"]
|
|
|
+ self.assertIsInstance(tag, Tag)
|
|
|
+
|
|
|
+ # Verify the tag is NOT signed despite config=true
|
|
|
+ self.assertIsNone(tag._signature)
|
|
|
+
|
|
|
|
|
|
class TagCreateTests(PorcelainTestCase):
|
|
|
def test_annotated(self) -> None:
|
|
@@ -4974,6 +5360,109 @@ class BranchCreateTests(PorcelainTestCase):
|
|
|
porcelain.branch_create(self.repo, b"foo")
|
|
|
self.assertEqual({b"master", b"foo"}, set(porcelain.branch_list(self.repo)))
|
|
|
|
|
|
+ def test_auto_setup_merge_true_from_remote_tracking(self) -> None:
|
|
|
+ """Test branch.autoSetupMerge=true sets up tracking from remote-tracking branch."""
|
|
|
+ [c1] = build_commit_graph(self.repo.object_store, [[1]])
|
|
|
+ self.repo[b"HEAD"] = c1.id
|
|
|
+ # Create a remote-tracking branch
|
|
|
+ self.repo.refs[b"refs/remotes/origin/feature"] = c1.id
|
|
|
+
|
|
|
+ # Set branch.autoSetupMerge to true (default)
|
|
|
+ config = self.repo.get_config()
|
|
|
+ config.set((b"branch",), b"autoSetupMerge", b"true")
|
|
|
+ config.write_to_path()
|
|
|
+
|
|
|
+ # Create branch from remote-tracking branch
|
|
|
+ porcelain.branch_create(self.repo, "myfeature", "origin/feature")
|
|
|
+
|
|
|
+ # Verify tracking was set up
|
|
|
+ config = self.repo.get_config()
|
|
|
+ self.assertEqual(config.get((b"branch", b"myfeature"), b"remote"), b"origin")
|
|
|
+ self.assertEqual(
|
|
|
+ config.get((b"branch", b"myfeature"), b"merge"), b"refs/heads/feature"
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_auto_setup_merge_false(self) -> None:
|
|
|
+ """Test branch.autoSetupMerge=false disables tracking setup."""
|
|
|
+ [c1] = build_commit_graph(self.repo.object_store, [[1]])
|
|
|
+ self.repo[b"HEAD"] = c1.id
|
|
|
+ # Create a remote-tracking branch
|
|
|
+ self.repo.refs[b"refs/remotes/origin/feature"] = c1.id
|
|
|
+
|
|
|
+ # Set branch.autoSetupMerge to false
|
|
|
+ config = self.repo.get_config()
|
|
|
+ config.set((b"branch",), b"autoSetupMerge", b"false")
|
|
|
+ config.write_to_path()
|
|
|
+
|
|
|
+ # Create branch from remote-tracking branch
|
|
|
+ porcelain.branch_create(self.repo, "myfeature", "origin/feature")
|
|
|
+
|
|
|
+ # Verify tracking was NOT set up
|
|
|
+ config = self.repo.get_config()
|
|
|
+ self.assertRaises(KeyError, config.get, (b"branch", b"myfeature"), b"remote")
|
|
|
+ self.assertRaises(KeyError, config.get, (b"branch", b"myfeature"), b"merge")
|
|
|
+
|
|
|
+ def test_auto_setup_merge_always(self) -> None:
|
|
|
+ """Test branch.autoSetupMerge=always sets up tracking even from local branches."""
|
|
|
+ [c1] = build_commit_graph(self.repo.object_store, [[1]])
|
|
|
+ self.repo[b"HEAD"] = c1.id
|
|
|
+ self.repo.refs[b"refs/heads/main"] = c1.id
|
|
|
+
|
|
|
+ # Set branch.autoSetupMerge to always
|
|
|
+ config = self.repo.get_config()
|
|
|
+ config.set((b"branch",), b"autoSetupMerge", b"always")
|
|
|
+ config.write_to_path()
|
|
|
+
|
|
|
+ # Create branch from local branch - normally wouldn't set up tracking
|
|
|
+ porcelain.branch_create(self.repo, "feature", "main")
|
|
|
+
|
|
|
+ # With always, tracking should NOT be set up from local branches
|
|
|
+ # (Git only sets up tracking from remote-tracking branches even with always)
|
|
|
+ config = self.repo.get_config()
|
|
|
+ self.assertRaises(KeyError, config.get, (b"branch", b"feature"), b"remote")
|
|
|
+ self.assertRaises(KeyError, config.get, (b"branch", b"feature"), b"merge")
|
|
|
+
|
|
|
+ def test_auto_setup_merge_always_from_remote(self) -> None:
|
|
|
+ """Test branch.autoSetupMerge=always still sets up tracking from remote branches."""
|
|
|
+ [c1] = build_commit_graph(self.repo.object_store, [[1]])
|
|
|
+ self.repo[b"HEAD"] = c1.id
|
|
|
+ # Create a remote-tracking branch
|
|
|
+ self.repo.refs[b"refs/remotes/origin/feature"] = c1.id
|
|
|
+
|
|
|
+ # Set branch.autoSetupMerge to always
|
|
|
+ config = self.repo.get_config()
|
|
|
+ config.set((b"branch",), b"autoSetupMerge", b"always")
|
|
|
+ config.write_to_path()
|
|
|
+
|
|
|
+ # Create branch from remote-tracking branch
|
|
|
+ porcelain.branch_create(self.repo, "myfeature", "origin/feature")
|
|
|
+
|
|
|
+ # Verify tracking was set up
|
|
|
+ config = self.repo.get_config()
|
|
|
+ self.assertEqual(config.get((b"branch", b"myfeature"), b"remote"), b"origin")
|
|
|
+ self.assertEqual(
|
|
|
+ config.get((b"branch", b"myfeature"), b"merge"), b"refs/heads/feature"
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_auto_setup_merge_default(self) -> None:
|
|
|
+ """Test default behavior (no config) is same as true."""
|
|
|
+ [c1] = build_commit_graph(self.repo.object_store, [[1]])
|
|
|
+ self.repo[b"HEAD"] = c1.id
|
|
|
+ # Create a remote-tracking branch
|
|
|
+ self.repo.refs[b"refs/remotes/origin/feature"] = c1.id
|
|
|
+
|
|
|
+ # Don't set any config - should default to true
|
|
|
+
|
|
|
+ # Create branch from remote-tracking branch
|
|
|
+ porcelain.branch_create(self.repo, "myfeature", "origin/feature")
|
|
|
+
|
|
|
+ # Verify tracking was set up
|
|
|
+ config = self.repo.get_config()
|
|
|
+ self.assertEqual(config.get((b"branch", b"myfeature"), b"remote"), b"origin")
|
|
|
+ self.assertEqual(
|
|
|
+ config.get((b"branch", b"myfeature"), b"merge"), b"refs/heads/feature"
|
|
|
+ )
|
|
|
+
|
|
|
|
|
|
class BranchDeleteTests(PorcelainTestCase):
|
|
|
def test_simple(self) -> None:
|