Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions pubgate/absorb.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ def _apply_absorb_changes(
theirs_tmp = Path(tmpdir) / "theirs"
base_tmp.write_text(published_base, encoding="utf-8", newline="")
theirs_tmp.write_text(theirs_content, encoding="utf-8", newline="")
# Read ours from git objects instead of working copy
# to avoid CRLF mismatch on Windows (see _merge_file).
ours_content = _read_text_at_ref(git, "HEAD", change.path)
if ours_content is not None:
local_path.write_text(ours_content, encoding="utf-8", newline="")
clean = git.merge_file(local_path, base_tmp, theirs_tmp)
git.stage(change.path)
if clean:
Expand Down Expand Up @@ -281,6 +286,14 @@ def _merge_file(
base_tmp.write_text(base_content, encoding="utf-8", newline="")
theirs_tmp.write_text(theirs_content, encoding="utf-8", newline="")

# Read ours from git objects instead of the working copy.
# On Windows, checkout may denormalize LF→CRLF, causing
# git merge-file to see spurious differences vs the LF
# base/theirs content read from git objects.
ours_content = _read_text_at_ref(git, "HEAD", path)
if ours_content is not None:
ours_path.write_text(ours_content, encoding="utf-8", newline="")

clean = git.merge_file(ours_path, base_tmp, theirs_tmp)
git.stage(path)
if clean:
Expand Down
8 changes: 4 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def commit_files(self, files: dict[str, str | bytes], msg: str) -> str:
if isinstance(content, bytes):
full.write_bytes(content)
else:
full.write_text(content, encoding="utf-8")
full.write_text(content, encoding="utf-8", newline="")
_git(self.path, "add", "-A")
_git(self.path, "commit", "-m", msg)
return _git(self.path, "rev-parse", "HEAD").strip()
Expand Down Expand Up @@ -269,15 +269,15 @@ def topo(tmp_path: Path) -> Topology:
# Seed public repo with initial commit
_git(tmp_path, "clone", str(public_bare), str(public_work))
_git(public_work, "checkout", "-b", "main")
(public_work / "public-file.txt").write_text("public content\n", encoding="utf-8")
(public_work / "public-file.txt").write_text("public content\n", encoding="utf-8", newline="")
_git(public_work, "add", ".")
_git(public_work, "commit", "-m", "initial public commit")
_git(public_work, "push", "-u", "origin", "main")

# Clone internal and set up developer work directory
_git(tmp_path, "clone", str(internal_bare), str(work_dir_path))
_git(work_dir_path, "checkout", "-b", "main")
(work_dir_path / "file1.txt").write_text("internal content\n", encoding="utf-8")
(work_dir_path / "file1.txt").write_text("internal content\n", encoding="utf-8", newline="")
_git(work_dir_path, "add", ".")
_git(work_dir_path, "commit", "-m", "initial internal commit")
_git(work_dir_path, "push", "-u", "origin", "main")
Expand Down Expand Up @@ -313,7 +313,7 @@ def topo_empty_public(tmp_path: Path) -> Topology:
# Clone internal and set up developer work directory
_git(tmp_path, "clone", str(internal_bare), str(work_dir_path))
_git(work_dir_path, "checkout", "-b", "main")
(work_dir_path / "file1.txt").write_text("internal content\n", encoding="utf-8")
(work_dir_path / "file1.txt").write_text("internal content\n", encoding="utf-8", newline="")
_git(work_dir_path, "add", ".")
_git(work_dir_path, "commit", "-m", "initial internal commit")
_git(work_dir_path, "push", "-u", "origin", "main")
Expand Down
43 changes: 0 additions & 43 deletions tests/test_absorb.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,46 +635,3 @@ def test_warning_when_stage_state_unreadable(self, topo: Topology, caplog):
topo.pubgate.absorb()

assert "Could not read stage state" in caplog.text


class TestAbsorbCRLF:
def test_new_file_preserves_crlf(self, topo: Topology):
from conftest import _git

topo.bootstrap_absorb()

# Force a CRLF file into public repo (bypass autocrlf normalization)
ext_path = topo.external_contributor.path
(ext_path / "crlf.txt").write_bytes(b"line1\r\nline2\r\n")
_git(ext_path, "-c", "core.autocrlf=false", "add", "crlf.txt")
_git(ext_path, "commit", "-m", "add crlf file")
topo.external_contributor.push("origin", "main")

topo.pubgate.absorb()

raw = topo.work_dir.git.read_file_at_ref_bytes(topo.cfg.internal_absorb_branch, "crlf.txt")
assert raw == b"line1\r\nline2\r\n"

def test_modified_file_merge_preserves_crlf(self, topo: Topology):
from conftest import _git

# Set up a file with CRLF on public
ext_path = topo.external_contributor.path
(ext_path / "shared.txt").write_bytes(b"line1\r\nline2\r\n")
_git(ext_path, "-c", "core.autocrlf=false", "add", "shared.txt")
_git(ext_path, "commit", "-m", "add shared with crlf")
topo.external_contributor.push("origin", "main")

topo.bootstrap_absorb()

# Modify the file on public (keep CRLF)
(ext_path / "shared.txt").write_bytes(b"line1\r\nline2\r\nline3\r\n")
_git(ext_path, "-c", "core.autocrlf=false", "add", "shared.txt")
_git(ext_path, "commit", "-m", "modify shared with crlf")
topo.external_contributor.push("origin", "main")

topo.pubgate.absorb()

raw = topo.work_dir.git.read_file_at_ref_bytes(topo.cfg.internal_absorb_branch, "shared.txt")
assert raw is not None
assert b"\r\n" in raw
24 changes: 0 additions & 24 deletions tests/test_publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,27 +503,3 @@ def test_external_changes_prevent_base_advancement(self, topo: Topology, caplog)
topo.pubgate.publish()

assert "Keeping publish base" in caplog.text


class TestPublishCRLF:
def test_publish_preserves_crlf(self, topo: Topology):
from conftest import _git

# Force CRLF file into internal main (bypass autocrlf normalization)
work_path = topo.work_dir.path
(work_path / "crlf.txt").write_bytes(b"hello\r\nworld\r\n")
_git(work_path, "-c", "core.autocrlf=false", "add", "crlf.txt")
_git(work_path, "commit", "-m", "add crlf file")
topo.work_dir.push("origin", "main")

topo.bootstrap_absorb()
topo.pubgate.stage()
topo.merge_internal_pr(topo.cfg.internal_stage_branch, topo.cfg.internal_approved_branch)
topo.work_dir.run("checkout", "main")

topo.pubgate.publish()
topo.work_dir.run("fetch", "public-remote")

pr_ref = f"public-remote/{topo.cfg.public_publish_branch}"
raw = topo.work_dir.git.read_file_at_ref_bytes(pr_ref, "crlf.txt")
assert raw == b"hello\r\nworld\r\n"
Loading