|
21 | 21 | from dfetch.project.gitsubproject import GitSubProject |
22 | 22 | from dfetch.project.subproject import SubProject |
23 | 23 | 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 | +) |
25 | 28 | 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 | +) |
26 | 34 | from dfetch.vcs.svn import SvnRepo |
27 | 35 |
|
28 | 36 | logger = get_logger(__name__) |
@@ -102,6 +110,17 @@ def get_file_revision(self, path: str | pathlib.Path) -> str: |
102 | 110 | def import_projects() -> Sequence[ProjectEntry]: |
103 | 111 | """Import projects from underlying superproject.""" |
104 | 112 |
|
| 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 | + |
105 | 124 |
|
106 | 125 | class GitSuperProject(SuperProject): |
107 | 126 | """A git specific superproject.""" |
@@ -192,6 +211,41 @@ def import_projects() -> Sequence[ProjectEntry]: |
192 | 211 |
|
193 | 212 | return projects |
194 | 213 |
|
| 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 | + |
195 | 249 |
|
196 | 250 | class SvnSuperProject(SuperProject): |
197 | 251 | """A SVN specific superproject.""" |
@@ -263,6 +317,40 @@ def import_projects() -> Sequence[ProjectEntry]: |
263 | 317 |
|
264 | 318 | return projects |
265 | 319 |
|
| 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 | + |
266 | 354 |
|
267 | 355 | class NoVcsSuperProject(SuperProject): |
268 | 356 | """A superproject without any version control.""" |
@@ -318,3 +406,14 @@ def import_projects() -> Sequence[ProjectEntry]: |
318 | 406 | "Only git or SVN projects can be imported.", |
319 | 407 | "Run this command within either a git or SVN repository", |
320 | 408 | ) |
| 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 "" |
0 commit comments