Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8e4a4c8
Add review-patch command for interactive patch inspection
claude Jun 18, 2026
3376475
Fix pylint R0801, isort order, and code-review findings in review-patch
claude Jun 18, 2026
fc9791e
Add review-patch to CI run, demo script, and docs
claude Jun 18, 2026
8ee7bc0
Skip redundant restore fetch when all patches already applied
claude Jun 18, 2026
5a9fe93
Eliminate fetches after the first in review-patch
claude Jun 18, 2026
9dfd4e7
Refactor review-patch to reduce cyclomatic complexity below 8
claude Jun 19, 2026
cc1153c
Update review-patch feature files to match reduced-fetch behaviour
claude Jun 19, 2026
61457aa
Address review comments: count validation, pathspec hardening, VCS di…
claude Jun 19, 2026
0f44da9
Update review-patch asciicast and add rendered GIF
claude Jun 19, 2026
b1278e0
Fix pyright errors in test_review_patch.py
claude Jun 19, 2026
ba8ca8a
Fix bandit B101 by using GitSuperProject | None type narrowing
claude Jun 19, 2026
171dac0
Restore dfetch_data.yaml metadata after review-patch exits
claude Jun 19, 2026
f16a5f3
Replace metadata patch assertion with git status no-changes check
claude Jun 19, 2026
4a2534b
Silence patch_ng logs during interactive TUI patch steps
claude Jun 19, 2026
e8dd5d3
Clear TUI screen on patch failure before propagating the error
claude Jun 19, 2026
3032305
Fix restore after patch failure by using git restore --source=HEAD
claude Jun 19, 2026
cef0bae
Fix pre-commit
ben-edna Jun 19, 2026
d894c1a
Improve docs
spoorcc Jun 23, 2026
bda087a
review comments
spoorcc Jun 24, 2026
31fa66f
Rename to replay-patches
spoorcc Jun 24, 2026
38f7000
Add combined mode
spoorcc Jun 24, 2026
1a0a8c3
review comments
spoorcc Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
- run: dfetch update
- run: dfetch update
- run: dfetch update-patch
- run: dfetch replay-patches

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Verify the repo is clean after the smoke run.

dfetch replay-patches exiting 0 is not enough here; the feature's main guarantee is that it restores the original state. Add a follow-up git status --porcelain or git diff --exit-code check after this step so CI fails if the command leaves staged or working-tree changes behind.

Also applies to: 202-202

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/run.yml at line 59, The smoke workflow currently only
checks that dfetch replay-patches exits successfully, but it does not verify
that the repository is restored to a clean state afterward. Update the run step
in the workflow job to add a follow-up cleanliness check such as git status
--porcelain or git diff --exit-code immediately after dfetch replay-patches so
CI fails if any staged or working-tree changes remain.

- run: dfetch format-patch
- run: dfetch report -t sbom
- run: dfetch remove test-repo
Expand Down Expand Up @@ -198,6 +199,7 @@ jobs:
- run: dfetch update
- run: dfetch update
- run: dfetch update-patch
- run: dfetch replay-patches
- run: dfetch format-patch
- run: dfetch report -t sbom
- run: dfetch remove test-repo
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Release 0.15.0 (unreleased)
==============================

* Add ``replay-patches`` command to step through patch contributions interactively (#1290)

Release 0.14.3 (released 2026-06-25)
====================================

Expand All @@ -11,7 +16,6 @@ Release 0.14.2 (released 2026-06-21)

Release 0.14.1 (released 2026-06-19)
====================================

* Implement C-043: add ``pip-audit`` OSV gate to the release workflow
* Add CRA Compliance: OSCAL 1.2.2 Component Definition
* Fix ``dfetch import`` mangling the namespace of a generic VCS URL whose path contains ``.git`` (#1268)
Expand Down
2 changes: 2 additions & 0 deletions dfetch/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import dfetch.commands.import_
import dfetch.commands.init
import dfetch.commands.remove
import dfetch.commands.replay_patches
import dfetch.commands.report
import dfetch.commands.update
import dfetch.commands.update_patch
Expand Down Expand Up @@ -56,6 +57,7 @@ def create_parser() -> argparse.ArgumentParser:
dfetch.commands.remove.Remove.create_menu(subparsers)
dfetch.commands.report.Report.create_menu(subparsers)
dfetch.commands.update.Update.create_menu(subparsers)
dfetch.commands.replay_patches.ReplayPatches.create_menu(subparsers)
dfetch.commands.update_patch.UpdatePatch.create_menu(subparsers)
dfetch.commands.validate.Validate.create_menu(subparsers)

Expand Down
26 changes: 26 additions & 0 deletions dfetch/commands/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
import sys
from abc import ABC, abstractmethod
from argparse import ArgumentParser # pylint: disable=unused-import
from collections.abc import Callable
from typing import TYPE_CHECKING, TypeVar

from dfetch.log import get_logger
from dfetch.manifest.project import ProjectEntry
from dfetch.project.superproject import SuperProject
from dfetch.util.util import in_directory

if TYPE_CHECKING and sys.version_info >= (3, 10):
from typing import TypeAlias

Expand All @@ -18,6 +24,8 @@
argparse._SubParsersAction # pyright: ignore[reportPrivateUsage] #pylint: disable=protected-access
)

_command_logger = get_logger(__name__)


def pascal_to_kebab(name: str) -> str:
"""Insert a dash before each uppercase letter (except the first) and lowercase everything."""
Expand Down Expand Up @@ -60,6 +68,24 @@ def __call__(self, args: argparse.Namespace) -> None:
NotImplementedError: This is an abstract method that should be implemented by a subclass.
"""

@staticmethod
def _iter_projects(
superproject: SuperProject,
project_names: list[str],
process: Callable[[ProjectEntry], None],
) -> None:
"""Iterate over selected projects, log per-project errors, re-raise if any failed."""
had_errors = False
with in_directory(superproject.root_directory):
for project in superproject.manifest.selected_projects(project_names):
try:
process(project)
except RuntimeError as exc:
_command_logger.print_error_line(project.name, str(exc))
had_errors = True
if had_errors:
raise RuntimeError()

@staticmethod
def parser(
subparsers: SubparserActionType,
Expand Down
Loading
Loading