Skip to content

Commit 9248b9d

Browse files
committed
WIP
1 parent 185f1cf commit 9248b9d

5 files changed

Lines changed: 114 additions & 46 deletions

File tree

dfetch/commands/format_patch.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import dfetch.manifest.project
3333
import dfetch.project
3434
from dfetch.log import get_logger
35-
from dfetch.project.git import GitSubProject
3635
from dfetch.project.superproject import SuperProject
3736
from dfetch.util.util import catch_runtime_exceptions, in_directory
3837
from dfetch.vcs.patch import PatchAuthor, PatchInfo, add_prefix_to_patch
@@ -117,7 +116,6 @@ def __call__(self, args: argparse.Namespace) -> None:
117116
path_prefix=re.split(r"\*", subproject.source, 1)[0].rstrip(
118117
"/"
119118
),
120-
is_git=isinstance(subproject, GitSubProject),
121119
)
122120

123121
output_patch_file = output_dir_path / pathlib.Path(patch).name

dfetch/project/superproject.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ def get_username(self) -> str:
111111
elif SvnRepo(self.root_directory).is_svn():
112112
username = SvnRepo(self.root_directory).get_username()
113113

114-
username = username or getpass.getuser()
114+
if not username:
115+
try:
116+
username = getpass.getuser()
117+
except Exception:
118+
username = ""
115119
if not username:
116120
try:
117121
username = os.getlogin()

dfetch/vcs/patch.py

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import hashlib
66
import stat
77
from collections.abc import Sequence
8-
from dataclasses import dataclass
8+
from dataclasses import dataclass, field
99
from pathlib import Path
1010

1111
import patch_ng
@@ -211,7 +211,9 @@ class PatchInfo:
211211
total_patches: int = 1
212212
current_patch_idx: int = 1
213213
revision: str = ""
214-
date: datetime.datetime = datetime.datetime.now(tz=datetime.timezone.utc)
214+
date: datetime.datetime = field(
215+
default_factory=lambda: datetime.datetime.now(tz=datetime.timezone.utc)
216+
)
215217
description: str = ""
216218

217219
def to_git_header(self) -> str:
@@ -235,50 +237,42 @@ def to_svn_header(self) -> str:
235237
return ""
236238

237239

238-
def add_prefix_to_patch(file_path: str, path_prefix: str, is_git: bool = True) -> str:
239-
"""Rewrite a patch to prefix file paths."""
240+
def add_prefix_to_patch(file_path: str, path_prefix: str) -> str:
241+
"""Add a prefix to all file paths in the given patch file."""
240242
patch = patch_ng.fromfile(file_path)
241-
242-
if not patch:
243+
if not patch or not patch.items:
243244
return ""
244245

245-
out: list[bytes] = []
246+
# make sure prefix is bytes
247+
prefix = path_prefix.strip("/").encode() # b"myprefix"
248+
if prefix:
249+
prefix += b"/"
246250

247251
for file in patch.items:
248-
# normalize prefix (no leading/trailing slash surprises)
249-
prefix = path_prefix.strip("/").encode()
250-
prefix = prefix + b"/" if prefix else b""
251-
252-
src = file.source
253-
tgt = file.target
254-
255-
# strip a/ b/ if present
256-
if src.startswith(b"a/"):
257-
src = src[2:]
258-
if tgt.startswith(b"b/"):
259-
tgt = tgt[2:]
260-
261-
new_src = b"a/" + prefix + src
262-
new_tgt = b"b/" + prefix + tgt
263-
264-
# diff header
265-
out.append(b"")
266-
if is_git:
267-
out.append(b"diff --git " + new_src + b" " + new_tgt)
268-
else:
269-
out.append(b"Index: " + new_src)
270-
out.append(b"=" * 67)
271-
out.append(b"--- " + new_src)
272-
out.append(b"+++ " + new_tgt)
273252

274-
for hunk in file.hunks:
275-
out.append(
276-
f"@@ -{hunk.startsrc},{hunk.linessrc} "
277-
f"+{hunk.starttgt},{hunk.linestgt} @@".encode()
278-
)
279-
for line in hunk.text:
280-
out.append(line.rstrip(b"\r\n"))
253+
def rewrite_path(path: bytes) -> bytes:
254+
if path == b"/dev/null":
255+
return b"/dev/null"
256+
return prefix + path
257+
258+
file.source = rewrite_path(file.source)
259+
file.target = rewrite_path(file.target)
281260

282-
out.append(b"") # blank line between files
261+
return dump(patch)
283262

284-
return b"\n".join(out).decode("utf-8")
263+
264+
def dump(patch_set: patch_ng.PatchSet) -> str:
265+
"""Dump a patch set to a string."""
266+
output_lines: list[str] = []
267+
for p in patch_set.items:
268+
for headline in p.header:
269+
output_lines.append(headline.rstrip(b"\r\n").decode("utf-8"))
270+
output_lines.append("--- " + p.source.decode("utf-8"))
271+
output_lines.append("+++ " + p.target.decode("utf-8"))
272+
for h in p.hunks:
273+
output_lines.append(
274+
f"@@ -{h.startsrc},{h.linessrc} +{h.starttgt},{h.linestgt} @@"
275+
)
276+
for line in h.text:
277+
output_lines.append(line.rstrip(b"\r\n").decode("utf-8"))
278+
return "\n".join(output_lines)

features/format-patch-in-git.feature

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Feature: Formatting a patch for git repositories
55
applied to the original repository. This way the upstream repository can
66
be kept up to date with local changes.
77

8+
@wip
89
Scenario: All patch files are formatted
910
Given the manifest 'dfetch.yaml'
1011
"""
@@ -22,6 +23,7 @@ Feature: Formatting a patch for git repositories
2223
patch:
2324
- 001-diff.patch
2425
- 002-diff.patch
26+
- 003-new-file.patch
2527
"""
2628
And the patch file '001-diff.patch'
2729
"""
@@ -45,14 +47,24 @@ Feature: Formatting a patch for git repositories
4547
-A test repo for testing patch.
4648
+A test repo for testing formatting patches.
4749
"""
50+
And the patch file '003-new-file.patch'
51+
"""
52+
diff --git a/NEWFILE.md b/NEWFILE.md
53+
new file mode 100644
54+
index 0000000..e69de29
55+
--- /dev/null
56+
+++ b/NEWFILE.md
57+
@@ -0,0 +1 @@
58+
+This is a new file.
59+
"""
4860
And all projects are updated
4961
When I run "dfetch format-patch ext/test-repo-tag --output-directory patches"
5062
Then the patch file 'patches/001-diff.patch' is generated
5163
"""
5264
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
5365
From: John Doe <john@dfetch.io>
5466
Date: Mon, 02 Feb 2026 20:57:35 +0000
55-
Subject: [PATCH 1/2] Patch for ext/test-repo-tag
67+
Subject: [PATCH 1/3] Patch for ext/test-repo-tag
5668
5769
Patch for ext/test-repo-tag
5870
@@ -70,7 +82,7 @@ Feature: Formatting a patch for git repositories
7082
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
7183
From: John Doe <john@dfetch.io>
7284
Date: Mon, 02 Feb 2026 21:02:42 +0000
73-
Subject: [PATCH 2/2] Patch for ext/test-repo-tag
85+
Subject: [PATCH 2/3] Patch for ext/test-repo-tag
7486
7587
Patch for ext/test-repo-tag
7688
@@ -82,4 +94,20 @@ Feature: Formatting a patch for git repositories
8294
-A test repo for testing patch.
8395
+A test repo for testing formatting patches.
8496
97+
"""
98+
And the patch file 'patches/003-new-file.patch' is generated
99+
"""
100+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
101+
From: John Doe <john@dfetch.io>
102+
Date: Mon, 02 Feb 2026 21:02:42 +0000
103+
Subject: [PATCH 3/3] Patch for ext/test-repo-tag
104+
105+
Patch for ext/test-repo-tag
106+
107+
diff --git a/NEWFILE.md b/NEWFILE.md
108+
--- a/NEWFILE.md
109+
+++ b/NEWFILE.md
110+
@@ -0,0 +1 @@
111+
+This is a new file.
112+
85113
"""

tests/test_patch.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from hypothesis import strategies as st
1414

1515
from dfetch.vcs.patch import (
16+
add_prefix_to_patch,
1617
apply_patch,
1718
create_git_patch_for_new_file,
1819
create_svn_patch_for_new_file,
@@ -337,3 +338,46 @@ def test_reverse_patch_small_random(original_lines, rng):
337338

338339
restored = target_file.read_text()
339340
assert restored == original, "Reverse patch did not restore original!"
341+
342+
343+
def test_patch_prefix_new_file(tmp_path):
344+
"""Test adding prefix to new file."""
345+
original_patch = "\n".join(
346+
[
347+
"diff --git a/test.txt b/test.txt",
348+
"new file mode 100644",
349+
"index 0000000..8029fa5",
350+
"--- /dev/null",
351+
"+++ test.txt",
352+
"@@ -0,0 +1,3 @@",
353+
"+Hello World",
354+
"+",
355+
"+Line above is empty",
356+
"",
357+
]
358+
)
359+
360+
original_patch_file = tmp_path / "original.patch"
361+
original_patch_file.write_text(original_patch)
362+
363+
expected_patch = "\n".join(
364+
[
365+
"diff --git a/src/test.txt b/src/test.txt",
366+
"new file mode 100644",
367+
"index 0000000..8029fa5",
368+
"--- /dev/null",
369+
"+++ src/test.txt",
370+
"@@ -0,0 +1,3 @@",
371+
"+Hello World",
372+
"+",
373+
"+Line above is empty",
374+
"",
375+
]
376+
)
377+
378+
prefixed_patch = add_prefix_to_patch(
379+
original_patch_file,
380+
path_prefix="src",
381+
)
382+
383+
assert prefixed_patch == expected_patch

0 commit comments

Comments
 (0)