Skip to content

Commit 55b825c

Browse files
committed
WIP: Move diff into superproject
1 parent dee6af7 commit 55b825c

7 files changed

Lines changed: 122 additions & 113 deletions

File tree

dfetch/commands/diff.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
import dfetch.commands.command
6161
from dfetch.log import get_logger
6262
from dfetch.project.metadata import Metadata
63-
from dfetch.project.superproject import SuperProject
63+
from dfetch.project.superproject import NoVcsSuperProject, SuperProject
6464
from dfetch.util.util import catch_runtime_exceptions, in_directory
6565

6666
logger = get_logger(__name__)
@@ -99,6 +99,11 @@ def __call__(self, args: argparse.Namespace) -> None:
9999
superproject = SuperProject.create()
100100
old_rev, new_rev = self._parse_revs(args.revs)
101101

102+
if isinstance(superproject, NoVcsSuperProject):
103+
raise RuntimeError(
104+
"Can only create patch if your project is an SVN or Git repo",
105+
)
106+
102107
with in_directory(superproject.root_directory):
103108
exceptions: list[str] = []
104109
projects = superproject.manifest.selected_projects(args.projects)
@@ -114,10 +119,9 @@ def __call__(self, args: argparse.Namespace) -> None:
114119
)
115120
subproject = superproject.get_sub_project(project)
116121

117-
if subproject is None:
118-
raise RuntimeError(
119-
"Can only create patch if your project is an SVN or Git repo",
120-
)
122+
if not subproject:
123+
raise RuntimeError("No subproject!")
124+
121125
old_rev = old_rev or superproject.get_file_revision(
122126
subproject.metadata_path
123127
)
@@ -127,7 +131,12 @@ def __call__(self, args: argparse.Namespace) -> None:
127131
f" the last revision to {Metadata.FILENAME} in {subproject.local_path}."
128132
" Please either commit this, or specify a revision to start from with --revs"
129133
)
130-
patch = subproject.diff(old_rev, new_rev)
134+
patch = superproject.diff(
135+
project.destination,
136+
old_rev,
137+
new_rev,
138+
ignore=(Metadata.FILENAME,),
139+
)
131140

132141
msg = self._rev_msg(old_rev, new_rev)
133142
if patch:

dfetch/commands/update_patch.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
import dfetch.manifest.project
4141
import dfetch.project
4242
from dfetch.log import get_logger
43-
from dfetch.project.superproject import GitSuperProject, SuperProject, SvnSuperProject
43+
from dfetch.project.superproject import GitSuperProject, NoVcsSuperProject, SuperProject
4444
from dfetch.util.util import catch_runtime_exceptions, in_directory
4545

4646
logger = get_logger(__name__)
@@ -74,7 +74,7 @@ def __call__(self, args: argparse.Namespace) -> None:
7474

7575
exceptions: list[str] = []
7676

77-
if not isinstance(superproject, (GitSuperProject, SvnSuperProject)):
77+
if isinstance(superproject, NoVcsSuperProject):
7878
raise RuntimeError(
7979
"The project containing the manifest is not under version control,"
8080
" updating patches is not supported"
@@ -123,10 +123,11 @@ def __call__(self, args: argparse.Namespace) -> None:
123123
)
124124

125125
# generate reverse patch
126-
patch_text = subproject.diff(
126+
patch_text = superproject.diff(
127+
subproject.local_path,
127128
old_revision="",
128129
new_revision="",
129-
# ignore=files_to_ignore,
130+
ignore=[],
130131
reverse=True,
131132
)
132133

dfetch/project/gitsubproject.py

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@
22

33
import os
44
import pathlib
5-
from collections.abc import Sequence
65
from functools import lru_cache
7-
from typing import Optional
86

97
from dfetch.log import get_logger
108
from dfetch.manifest.project import ProjectEntry
119
from dfetch.manifest.version import Version
1210
from dfetch.project.subproject import SubProject
1311
from dfetch.util.util import safe_rmtree
1412
from dfetch.vcs.git import GitLocalRepo, GitRemote, get_git_version
15-
from dfetch.vcs.patch import PatchInfo, reverse_patch
13+
from dfetch.vcs.patch import PatchInfo
1614

1715
logger = get_logger(__name__)
1816

@@ -44,39 +42,6 @@ def _list_of_tags(self) -> list[str]:
4442
"""Get list of all available tags."""
4543
return [str(tag) for tag in self._remote_repo.list_of_tags()]
4644

47-
def _diff_impl(
48-
self,
49-
old_revision: str,
50-
new_revision: Optional[str],
51-
ignore: Sequence[str],
52-
reverse: bool = False,
53-
) -> str:
54-
"""Get the diff of two revisions."""
55-
diff_since_revision = str(
56-
self._local_repo.create_diff(old_revision, new_revision, ignore, reverse)
57-
)
58-
59-
if new_revision:
60-
return diff_since_revision
61-
62-
combined_diff = []
63-
64-
if diff_since_revision:
65-
combined_diff += [diff_since_revision]
66-
67-
untracked_files_patch = str(self._local_repo.untracked_files_patch(ignore))
68-
if untracked_files_patch:
69-
if reverse:
70-
reversed_patch = reverse_patch(untracked_files_patch.encode("utf-8"))
71-
if not reversed_patch:
72-
raise RuntimeError(
73-
"Failed to reverse untracked files patch; patch parsing returned empty."
74-
)
75-
untracked_files_patch = reversed_patch
76-
combined_diff += [untracked_files_patch]
77-
78-
return "\n".join(combined_diff)
79-
8045
@staticmethod
8146
def revision_is_enough() -> bool:
8247
"""See if this VCS can uniquely distinguish branch with revision only."""

dfetch/project/subproject.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -385,16 +385,6 @@ def _are_there_local_changes(self, files_to_ignore: Sequence[str]) -> bool:
385385
def _fetch_impl(self, version: Version) -> Version:
386386
"""Fetch the given version of the subproject, should be implemented by the child class."""
387387

388-
@abstractmethod
389-
def _diff_impl(
390-
self,
391-
old_revision: str, # noqa
392-
new_revision: Optional[str], # noqa
393-
ignore: Sequence[str],
394-
reverse: bool = False,
395-
) -> str:
396-
"""Get the diff of two revisions, should be implemented by the child class."""
397-
398388
@abstractmethod
399389
def get_default_branch(self) -> str:
400390
"""Get the default branch of this repository."""
@@ -407,12 +397,6 @@ def is_license_file(filename: str) -> bool:
407397
for pattern in SubProject.LICENSE_GLOBS
408398
)
409399

410-
def diff(self, old_revision: str, new_revision: str, reverse: bool = False) -> str:
411-
"""Generate a relative diff for a subproject."""
412-
return self._diff_impl(
413-
old_revision, new_revision, ignore=(Metadata.FILENAME,), reverse=reverse
414-
)
415-
416400
@abstractmethod
417401
def create_formatted_patch_header(self, patch_info: PatchInfo) -> str:
418402
"""Create a formatted patch header for the given patch info."""

dfetch/project/superproject.py

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,16 @@
2121
from dfetch.project.gitsubproject import GitSubProject
2222
from dfetch.project.subproject import SubProject
2323
from dfetch.project.svnsubproject import SvnSubProject
24-
from dfetch.util.util import resolve_absolute_path
24+
from dfetch.util.util import (
25+
in_directory,
26+
resolve_absolute_path,
27+
)
2528
from dfetch.vcs.git import GitLocalRepo
29+
from dfetch.vcs.patch import (
30+
combine_patches,
31+
create_svn_patch_for_new_file,
32+
reverse_patch,
33+
)
2634
from dfetch.vcs.svn import SvnRepo
2735

2836
logger = get_logger(__name__)
@@ -102,6 +110,17 @@ def get_file_revision(self, path: str | pathlib.Path) -> str:
102110
def import_projects() -> Sequence[ProjectEntry]:
103111
"""Import projects from underlying superproject."""
104112

113+
@abstractmethod
114+
def diff(
115+
self,
116+
path: str | pathlib.Path,
117+
old_revision: str,
118+
new_revision: str | None,
119+
ignore: Sequence[str],
120+
reverse: bool = False,
121+
) -> str:
122+
"""Get the diff of two revisions."""
123+
105124

106125
class GitSuperProject(SuperProject):
107126
"""A git specific superproject."""
@@ -192,6 +211,41 @@ def import_projects() -> Sequence[ProjectEntry]:
192211

193212
return projects
194213

214+
def diff(
215+
self,
216+
path: str | pathlib.Path,
217+
old_revision: str,
218+
new_revision: str | None,
219+
ignore: Sequence[str],
220+
reverse: bool = False,
221+
) -> str:
222+
"""Get the diff of two revisions."""
223+
local_repo = GitLocalRepo(path)
224+
diff_since_revision = str(
225+
local_repo.create_diff(old_revision, new_revision, ignore, reverse)
226+
)
227+
228+
if new_revision:
229+
return diff_since_revision
230+
231+
combined_diff = []
232+
233+
if diff_since_revision:
234+
combined_diff += [diff_since_revision]
235+
236+
untracked_files_patch = str(local_repo.untracked_files_patch(ignore))
237+
if untracked_files_patch:
238+
if reverse:
239+
reversed_patch = reverse_patch(untracked_files_patch.encode("utf-8"))
240+
if not reversed_patch:
241+
raise RuntimeError(
242+
"Failed to reverse untracked files patch; patch parsing returned empty."
243+
)
244+
untracked_files_patch = reversed_patch
245+
combined_diff += [untracked_files_patch]
246+
247+
return "\n".join(combined_diff)
248+
195249

196250
class SvnSuperProject(SuperProject):
197251
"""A SVN specific superproject."""
@@ -263,6 +317,40 @@ def import_projects() -> Sequence[ProjectEntry]:
263317

264318
return projects
265319

320+
def diff(
321+
self,
322+
path: str | pathlib.Path,
323+
old_revision: str,
324+
new_revision: str | None,
325+
ignore: Sequence[str],
326+
reverse: bool = False,
327+
) -> str:
328+
"""Get the diff between two revisions."""
329+
repo = SvnRepo(path)
330+
if reverse:
331+
if new_revision:
332+
new_revision, old_revision = old_revision, new_revision
333+
334+
filtered = repo.create_diff(old_revision, new_revision, ignore)
335+
336+
if new_revision:
337+
return filtered
338+
339+
patches: list[bytes] = [filtered.encode("utf-8")] if filtered else []
340+
with in_directory(path):
341+
for file_path in repo.untracked_files(".", ignore):
342+
patch = create_svn_patch_for_new_file(file_path)
343+
if patch:
344+
patches.append(patch.encode("utf-8"))
345+
346+
patch_str = combine_patches(patches)
347+
348+
# SVN has no way of producing a reverse working copy patch, reverse ourselves
349+
if reverse and not new_revision:
350+
patch_str = reverse_patch(patch_str.encode("UTF-8"))
351+
352+
return patch_str
353+
266354

267355
class NoVcsSuperProject(SuperProject):
268356
"""A superproject without any version control."""
@@ -318,3 +406,14 @@ def import_projects() -> Sequence[ProjectEntry]:
318406
"Only git or SVN projects can be imported.",
319407
"Run this command within either a git or SVN repository",
320408
)
409+
410+
def diff(
411+
self,
412+
path: str | pathlib.Path,
413+
old_revision: str,
414+
new_revision: str | None,
415+
ignore: Sequence[str],
416+
reverse: bool = False,
417+
) -> str:
418+
"""Get the diff between two revisions."""
419+
return ""

dfetch/project/svnsubproject.py

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
import os
44
import pathlib
55
import urllib.parse
6-
from collections.abc import Sequence
7-
from typing import Optional
86

97
from dfetch.log import get_logger
108
from dfetch.manifest.project import ProjectEntry
@@ -13,15 +11,9 @@
1311
from dfetch.util.util import (
1412
find_matching_files,
1513
find_non_matching_files,
16-
in_directory,
1714
safe_rm,
1815
)
19-
from dfetch.vcs.patch import (
20-
PatchInfo,
21-
combine_patches,
22-
create_svn_patch_for_new_file,
23-
reverse_patch,
24-
)
16+
from dfetch.vcs.patch import PatchInfo
2517
from dfetch.vcs.svn import SvnRemote, SvnRepo, get_svn_version
2618

2719
logger = get_logger(__name__)
@@ -186,38 +178,6 @@ def _license_files(url_path: str) -> list[str]:
186178
def _get_revision(self, branch: str) -> str:
187179
return self._get_info(branch)["Revision"]
188180

189-
def _diff_impl(
190-
self,
191-
old_revision: str,
192-
new_revision: Optional[str],
193-
ignore: Sequence[str],
194-
reverse: bool = False,
195-
) -> str:
196-
"""Get the diff between two revisions."""
197-
if reverse:
198-
if new_revision:
199-
new_revision, old_revision = old_revision, new_revision
200-
201-
filtered = self._repo.create_diff(old_revision, new_revision, ignore)
202-
203-
if new_revision:
204-
return filtered
205-
206-
patches: list[bytes] = [filtered.encode("utf-8")] if filtered else []
207-
with in_directory(self.local_path):
208-
for file_path in self._repo.untracked_files(".", ignore):
209-
patch = create_svn_patch_for_new_file(file_path)
210-
if patch:
211-
patches.append(patch.encode("utf-8"))
212-
213-
patch_str = combine_patches(patches)
214-
215-
# SVN has no way of producing a reverse working copy patch, reverse ourselves
216-
if reverse and not new_revision:
217-
patch_str = reverse_patch(patch_str.encode("UTF-8"))
218-
219-
return patch_str
220-
221181
def get_default_branch(self) -> str:
222182
"""Get the default branch of this repository."""
223183
return self._repo.DEFAULT_BRANCH

tests/test_subproject.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,6 @@ def wanted_version(self):
4343
def _list_of_tags(self):
4444
return []
4545

46-
def _diff_impl(
47-
self,
48-
old_revision,
49-
new_revision,
50-
ignore,
51-
reverse=False,
52-
):
53-
return ""
54-
5546
def get_default_branch(self):
5647
return ""
5748

0 commit comments

Comments
 (0)