Skip to content

Commit 1dd49d6

Browse files
committed
New files not part of dfetch diff generated patch
Fixes #886
1 parent fdb33f4 commit 1dd49d6

5 files changed

Lines changed: 102 additions & 12 deletions

File tree

dfetch/project/svn.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
in_directory,
1818
safe_rm,
1919
)
20-
from dfetch.vcs.patch import filter_patch
20+
from dfetch.vcs.patch import (
21+
combine_patches,
22+
create_svn_patch_for_new_file,
23+
filter_patch,
24+
)
2125

2226
logger = get_logger(__name__)
2327

@@ -372,15 +376,52 @@ def get_diff(
372376
self, old_revision: str, new_revision: Optional[str], ignore: Sequence[str]
373377
) -> str:
374378
"""Get the diff between two revisions."""
375-
cmd = ["svn", "diff", "--non-interactive", ".", "-r", old_revision]
379+
cmd = [
380+
"svn",
381+
"diff",
382+
"--non-interactive",
383+
"--ignore-properties",
384+
".",
385+
"-r",
386+
old_revision,
387+
]
376388
if new_revision:
377389
cmd[-1] += f":{new_revision}"
378390

379391
with in_directory(self.local_path):
380392
patch_text = run_on_cmdline(logger, cmd).stdout
381393

382-
return filter_patch(patch_text, ignore)
394+
filtered = filter_patch(patch_text, ignore)
395+
396+
if new_revision:
397+
return filtered
398+
399+
patches: list[bytes] = [filtered.encode("utf-8")] if filtered else []
400+
with in_directory(self.local_path):
401+
for file_path in self._untracked_not_ignored("."):
402+
patch = create_svn_patch_for_new_file(file_path)
403+
if patch:
404+
patches.append(patch.encode("utf-8"))
405+
406+
return combine_patches(patches)
383407

384408
def get_default_branch(self) -> str:
385409
"""Get the default branch of this repository."""
386410
return self.DEFAULT_BRANCH
411+
412+
@staticmethod
413+
def _untracked_not_ignored(path: str) -> list[str]:
414+
result = (
415+
run_on_cmdline(
416+
logger,
417+
["svn", "status", path],
418+
)
419+
.stdout.decode()
420+
.splitlines()
421+
)
422+
423+
files = []
424+
for line in result:
425+
if line.startswith("?"):
426+
files.append(line[1:].strip())
427+
return files

dfetch/vcs/patch.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Various patch utilities for VCS systems."""
22

3+
import difflib
34
from collections.abc import Sequence
5+
from pathlib import Path
46

57
import patch_ng
68

@@ -35,3 +37,34 @@ def dump_patch(patch_set: patch_ng.PatchSet) -> str:
3537
for line in h.text:
3638
patch_lines.append(line.rstrip(b"\n").decode("utf-8"))
3739
return "\n".join(patch_lines)
40+
41+
42+
def create_svn_patch_for_new_file(file_path: str) -> str:
43+
"""Create a svn patch for a new file."""
44+
diff = unified_diff_new_file(Path(file_path))
45+
return "" if not diff else "\n".join([f"Index: {file_path}", "=" * 67] + diff)
46+
47+
48+
def unified_diff_new_file(path: Path) -> list[str]:
49+
"""Create a unified diff for a new file."""
50+
with path.open("r", encoding="utf-8", errors="replace") as new_file:
51+
lines = new_file.readlines()
52+
53+
return list(
54+
difflib.unified_diff(
55+
[], lines, fromfile="/dev/null", tofile=str(path), lineterm=""
56+
)
57+
)
58+
59+
60+
def combine_patches(patches: Sequence[bytes]) -> str:
61+
"""Combine multiple patches into a single patch."""
62+
if not patches:
63+
return ""
64+
65+
final_patchset = patch_ng.PatchSet()
66+
for patch in patches:
67+
for patch_obj in patch_ng.fromstring(patch) or []:
68+
final_patchset.items += [patch_obj]
69+
70+
return dump_patch(final_patchset)

features/diff-in-git.feature

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,32 @@ Feature: Diff in git
3232
+An important sentence for the README!
3333
"""
3434

35+
@wip
3536
Scenario: New files are part of the patch
3637
Given files as '*.tmp' are ignored in git
3738
And "SomeProject/NEWFILE.md" in MyProject is created and committed with
3839
"""
39-
A completely new untracked file.
40+
A completely new tracked file.
4041
"""
42+
And "SomeProject/NEW_UNCOMMITTED_FILE.md" in MyProject is created
4143
And "SomeProject/IGNORE_ME.tmp" in MyProject is created
4244
When I run "dfetch diff SomeProject"
4345
Then the patch file 'MyProject/SomeProject.patch' is generated
4446
"""
4547
diff --git a/NEWFILE.md b/NEWFILE.md
4648
new file mode 100644
47-
index 0000000..9a3c71c
49+
index 0000000..a2d8605
4850
--- /dev/null
4951
+++ b/NEWFILE.md
5052
@@ -0,0 +1 @@
51-
+A completely new untracked file.
53+
+A completely new tracked file.
54+
diff --git a/dev/null b/NEW_UNCOMMITTED_FILE.md
55+
new file mode 100644
56+
index 0000000..9a3c71c
57+
--- /dev/null
58+
+++ b/NEW_UNCOMMITTED_FILE.md
59+
@@ -0,0 +1 @@
60+
+Some content
5261
"""
5362

5463
Scenario: No change is present

features/diff-in-svn.feature

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Feature: Diff in svn
2121
"""
2222
An important sentence for the README!
2323
"""
24-
When I run "dfetch -v diff SomeProject" in MySvnProject
24+
When I run "dfetch diff SomeProject" in MySvnProject
2525
Then the patch file 'MySvnProject/SomeProject.patch' is generated
2626
"""
2727
Index: README.md
@@ -34,11 +34,12 @@ Feature: Diff in svn
3434
"""
3535

3636
Scenario: New files are part of the patch
37-
Given files as '*.tmp' are ignored in 'SomeProject' in svn
37+
Given files as '*.tmp' are ignored in 'MySvnProject/SomeProject' in svn
3838
And "SomeProject/NEWFILE.md" in MySvnProject is changed, added and committed with
3939
"""
40-
A completely new untracked file.
40+
A completely new tracked file.
4141
"""
42+
And "SomeProject/NEW_UNCOMMITTED_FILE.md" in MySvnProject is created
4243
And "SomeProject/IGNORE_ME.tmp" in MySvnProject is created
4344
When I run "dfetch diff SomeProject" in MySvnProject
4445
Then the patch file 'MySvnProject/SomeProject.patch' is generated
@@ -48,7 +49,13 @@ Feature: Diff in svn
4849
--- NEWFILE.md
4950
+++ NEWFILE.md
5051
@@ -0,0 +1,1 @@
51-
+A completely new untracked file.
52+
+A completely new tracked file.
53+
Index: NEW_UNCOMMITTED_FILE.md
54+
===================================================================
55+
--- /dev/null
56+
+++ NEW_UNCOMMITTED_FILE.md
57+
@@ -0,0 +1,1 @@
58+
+Some content
5259
"""
5360

5461
Scenario: No change is present
@@ -78,7 +85,7 @@ Feature: Diff in svn
7885

7986
Scenario: Metadata is not part of diff
8087
Given the metadata file ".dfetch_data.yaml" of "MySvnProject/SomeProject" is changed
81-
When I run "dfetch diff SomeProject"
88+
When I run "dfetch diff SomeProject" in MySvnProject
8289
Then the output shows
8390
"""
8491
Dfetch (0.10.0)

features/steps/svn_steps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def step_impl(context):
123123
def step_impl(context, directory, path):
124124
with in_directory(directory):
125125
extend_file(path, context.text)
126-
add_and_commit("A change")
126+
add_and_commit(f"Added {path} to {directory}")
127127

128128

129129
@given("files as '{pattern}' are ignored in '{directory}' in svn")

0 commit comments

Comments
 (0)