Skip to content

Commit a2bee91

Browse files
committed
WIP
1 parent 6cf1a72 commit a2bee91

File tree

7 files changed

+141
-6
lines changed

7 files changed

+141
-6
lines changed

dfetch.code-workspace

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"module": "dfetch.__main__",
6464
"justMyCode": false,
6565
"args": [
66-
"update"
66+
"updatepatch", "sphinxcontrib.asciinema"
6767
]
6868
}
6969
]

dfetch/__main__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import dfetch.commands.init
1616
import dfetch.commands.report
1717
import dfetch.commands.update
18+
import dfetch.commands.update_patch
1819
import dfetch.commands.validate
1920
import dfetch.log
2021
import dfetch.util.cmdline
@@ -45,6 +46,7 @@ def create_parser() -> argparse.ArgumentParser:
4546
dfetch.commands.init.Init.create_menu(subparsers)
4647
dfetch.commands.report.Report.create_menu(subparsers)
4748
dfetch.commands.update.Update.create_menu(subparsers)
49+
dfetch.commands.update_patch.UpdatePatch.create_menu(subparsers)
4850
dfetch.commands.validate.Validate.create_menu(subparsers)
4951

5052
return parser

dfetch/commands/update_patch.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Update is the main functionality of dfetch.
2+
3+
You can add Projects to your :ref:`Manifest` and update will fetch the version specified.
4+
It tries to determine what kind of vcs it is: git, svn or something else.
5+
6+
"""
7+
8+
import argparse
9+
import os
10+
from pathlib import Path
11+
12+
import dfetch.commands.command
13+
import dfetch.manifest.project
14+
import dfetch.project
15+
from dfetch.commands.common import check_child_manifests
16+
from dfetch.log import get_logger
17+
from dfetch.project.superproject import SuperProject
18+
from dfetch.util.util import catch_runtime_exceptions, in_directory
19+
20+
logger = get_logger(__name__)
21+
22+
23+
class UpdatePatch(dfetch.commands.command.Command):
24+
"""Update a patch to reflect the last changes.
25+
26+
Some stuff
27+
"""
28+
29+
@staticmethod
30+
def create_menu(subparsers: dfetch.commands.command.SubparserActionType) -> None:
31+
"""Add the menu for the update-patch action."""
32+
parser = dfetch.commands.command.Command.parser(subparsers, UpdatePatch)
33+
parser.add_argument(
34+
"projects",
35+
metavar="<project>",
36+
type=str,
37+
nargs="*",
38+
help="Specific project(s) to update",
39+
)
40+
41+
def __call__(self, args: argparse.Namespace) -> None:
42+
"""Perform the update patch."""
43+
superproject = SuperProject()
44+
45+
exceptions: list[str] = []
46+
destinations: list[str] = [
47+
os.path.realpath(project.destination)
48+
for project in superproject.manifest.projects
49+
]
50+
with in_directory(superproject.root_directory):
51+
for project in superproject.manifest.selected_projects(args.projects):
52+
with catch_runtime_exceptions(exceptions) as exceptions:
53+
dfetch.project.make(project).update_patch(
54+
files_to_ignore=superproject.ignored_files(project.destination),
55+
)
56+
57+
if exceptions:
58+
raise RuntimeError("\n".join(exceptions))

dfetch/project/git.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,15 @@ def current_revision(self) -> str:
5252
return str(self._local_repo.get_current_hash())
5353

5454
def _diff_impl(
55-
self, old_revision: str, new_revision: Optional[str], ignore: Sequence[str]
55+
self,
56+
old_revision: str,
57+
new_revision: Optional[str],
58+
ignore: Sequence[str],
59+
reverse: bool = False,
5660
) -> str:
5761
"""Get the diff of two revisions."""
5862
diff_since_revision = str(
59-
self._local_repo.create_diff(old_revision, new_revision, ignore)
63+
self._local_repo.create_diff(old_revision, new_revision, ignore, reverse)
6064
)
6165

6266
if new_revision:

dfetch/project/subproject.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import fnmatch
44
import os
55
import pathlib
6+
import shutil
67
from abc import ABC, abstractmethod
78
from collections.abc import Sequence
89
from typing import Optional
@@ -93,7 +94,10 @@ def update_is_required(self, force: bool = False) -> Optional[Version]:
9394
return wanted
9495

9596
def update(
96-
self, force: bool = False, files_to_ignore: Optional[Sequence[str]] = None
97+
self,
98+
force: bool = False,
99+
files_to_ignore: Optional[Sequence[str]] = None,
100+
no_patch: bool = True,
97101
) -> None:
98102
"""Update this subproject if required.
99103
@@ -128,7 +132,7 @@ def update(
128132
actually_fetched = self._fetch_impl(to_fetch)
129133
self._log_project(f"Fetched {actually_fetched}")
130134

131-
applied_patches = self._apply_patches()
135+
applied_patches = [] if not no_patch else self._apply_patches()
132136

133137
self.__metadata.fetched(
134138
actually_fetched,
@@ -139,6 +143,56 @@ def update(
139143
logger.debug(f"Writing repo metadata to: {self.__metadata.path}")
140144
self.__metadata.dump()
141145

146+
def update_patch(self, files_to_ignore: Optional[Sequence[str]] = None) -> None:
147+
"""Update the patch."""
148+
149+
# Check if the project has a patch, maybe suggest creating one?
150+
if not self.__project.patch:
151+
self._log_project(
152+
f'skipped - there is no patch file, use "dfetch diff {self.__project.name}" instead'
153+
)
154+
return
155+
156+
# Check if the project was ever fetched
157+
on_disk_version = self.on_disk_version()
158+
if not on_disk_version:
159+
self._log_project(
160+
f'skipped - the project was never fetched before, use "dfetch update {self.__project.name}" '
161+
)
162+
return
163+
164+
# Make sure no uncommitted changes (don't care about ignored files)
165+
if self._are_there_local_changes(files_to_ignore):
166+
self._log_project(
167+
"skipped - local changes after last update (use --force to overwrite)"
168+
)
169+
return
170+
171+
# Select patch to overwrite & make backup
172+
patch_to_update = self.__project.patch[-1]
173+
shutil.move(patch_to_update, patch_to_update + ".backup")
174+
175+
# force update to fetched version from metadata without applying patch
176+
self.update(force=True, files_to_ignore=files_to_ignore, no_patch=True)
177+
178+
# generate reverse patch
179+
patch_text = self._diff_impl(
180+
old_revision=self.metadata_revision(),
181+
new_revision=None,
182+
ignore=files_to_ignore,
183+
reverse=True,
184+
)
185+
186+
if patch_text:
187+
patch_path = pathlib.Path(patch_to_update)
188+
self._log_project(f"Updating patch {patch_to_update}")
189+
patch_path.write_text(patch_text, encoding="UTF-8")
190+
else:
191+
self._log_project(f"No diffs found, kept patch {patch_to_update} unchanged")
192+
193+
# force update again to fetched version from metadata but with applying patch
194+
self.update(force=True, files_to_ignore=files_to_ignore, no_patch=False)
195+
142196
def _apply_patches(self) -> list[str]:
143197
"""Apply the patches."""
144198
cwd = pathlib.Path(".").resolve()
@@ -386,6 +440,7 @@ def _diff_impl(
386440
old_revision: str, # noqa
387441
new_revision: Optional[str], # noqa
388442
ignore: Sequence[str],
443+
reverse: bool = False,
389444
) -> str:
390445
"""Get the diff of two revisions, should be implemented by the child class."""
391446

dfetch/project/svn.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,20 @@ def current_revision(self) -> str:
190190
return SvnRepo.get_last_changed_revision(self.local_path)
191191

192192
def _diff_impl(
193-
self, old_revision: str, new_revision: Optional[str], ignore: Sequence[str]
193+
self,
194+
old_revision: str,
195+
new_revision: Optional[str],
196+
ignore: Sequence[str],
197+
reverse=False,
194198
) -> str:
195199
"""Get the diff between two revisions."""
200+
201+
if not new_revision:
202+
new_revision = "HEAD"
203+
204+
if reverse:
205+
new_revision, old_revision = old_revision, new_revision
206+
196207
filtered = self._repo.create_diff(old_revision, new_revision, ignore)
197208

198209
if new_revision:

dfetch/vcs/git.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ def create_diff(
383383
old_hash: Optional[str],
384384
new_hash: Optional[str],
385385
ignore: Optional[Sequence[str]] = None,
386+
reverse: bool = False,
386387
) -> str:
387388
"""Generate a relative diff patch."""
388389
with in_directory(self._path):
@@ -394,6 +395,10 @@ def create_diff(
394395
"--no-ext-diff", # Don't allow external diff tools
395396
"--no-color",
396397
]
398+
399+
if reverse:
400+
cmd.extend(["-R", "--src-prefix=b/", "--dst-prefix=a/"])
401+
397402
if old_hash:
398403
cmd.append(old_hash)
399404
if new_hash:

0 commit comments

Comments
 (0)