Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions patchoulene/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,26 @@ Currently, these problems are diagnosed:

The message contains content that would confuse `git mailsplit` or `git mailinfo`, which means that a patch generated from `git format-patch` on this commit would not be correctly handled by `git am`.

### `status`

```console
$ ./patl status <rev>
```

Check patches in `rev` for possibly outdated patches, based on information in the database.

An example output item is as follows:

```
Commit 677c9368d444 ("riscv: dts: spacemit: k3: add rfkill node for WLAN on Pico-ITX board"):
mail:20260716213314.3027969-5-aurelien@aurel32.net
has replacement:
- "riscv: dts: spacemit: k3: add rfkill node for WLAN"
mail:20260729172450.1660418-5-aurelien@aurel32.net
```

This shows that the commit `677c9368d444` (which is an ancestor of `rev`) is identified and, according to the database, it has a replacement.

### `walk`

```console
Expand Down
95 changes: 95 additions & 0 deletions patchoulene/src/patchoulene/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import difflib
import json
import os
import pathlib
import re
import shlex
Expand Down Expand Up @@ -213,13 +214,107 @@ def msg_merged(commit: GitCommit, primary: str) -> str:
db[primary]["merged"].append(rev2)


NO_COLOR = os.getenv("NO_COLOR", "") or not sys.stdout.isatty()


def color_sel(color_msg: str, msg: str) -> str:
return msg if NO_COLOR else color_msg


def dim(msg: str) -> str:
return color_sel(f"\x1b[90m{msg}\x1b[m", msg)


def do_status(db: dict, commits: list[GitCommit], rev: str):
def msg_unrecorded(commit: GitCommit) -> str:
clean = clean_subject(commit.message)

msgs = [
f'Warning: Commit {commit.commit[:12]} ("{clean}"):',
f"- This patch has not been recorded, please run './patl walk {rev}'",
"",
]

return "\n".join(msgs)

def msg_replacement_header(commit: GitCommit) -> str:
clean = clean_subject(commit.message)
guessed = guess_id(commit.message)
primary = guessed[0]

msgs = [
f'Commit {commit.commit[:12]} ("{clean}"):',
f" {dim(primary)}",
f" has replacement:",
]

return "\n".join(msgs)

def msg_replacement(
commit: GitCommit, pid: str, subject: str | None, merged: list[str]
) -> str:
clean = clean_subject(commit.message)
subject_suffix = ""
if subject == clean:
subject_suffix = " (identical subject)"
suffix = ""
if merged := info.get("merged", []):
suffix = f" (merged {', '.join(merged)})"
msgs = [f' - "{subject}"{subject_suffix}', f" {dim(pid)}{suffix}"]
return "\n".join(msgs)

for c in commits:
guessed = guess_id(c.message)
if not guessed:
continue
primary = guessed[0]

if primary not in db:
print(msg_unrecorded(c), file=sys.stderr)
continue

# Walk the replacment graph to find the set of patches that replaces c

replacements = set()
todo = {primary}

while todo:
pid = todo.pop()

if pid not in db or not db[pid].get("replacement", None):
replacements.add(pid)
continue

repl = db[pid]["replacement"]
if not isinstance(repl, list):
repl = [repl]

todo.update(repl)

if replacements != {primary}:
print(msg_replacement_header(c))
for pid in replacements:
info = db.get(pid, {})
subject: str = info.get("subject", None)
merged: list[str] = info.get("merged", [])

print(msg_replacement(c, pid, subject, merged))
print()


def main():
repo = GitRepo(".")
match sys.argv[1:]:
case ["check", ref]:
base = guess_base(repo, ref)
print(f"Base for {ref} is {base}", file=sys.stderr)
do_check_messages(repo, [f"^refs/tags/{base}", ref])
case ["status", rev]:
base = guess_base(repo, rev)
print(f"Base for {rev} is {base}", file=sys.stderr)
commits = repo.commit_list([f"^refs/tags/{base}", rev])
db = read_patch_db()
do_status(db, commits, rev)
case ["diff", rev1, rev2]:
base1 = guess_base(repo, rev1)
print(f"Base for {rev1} is {base1}", file=sys.stderr)
Expand Down