From 7b040741ca107977ec57902ff4598e36dd45b45f Mon Sep 17 00:00:00 2001 From: jingfelix Date: Tue, 8 Oct 2024 20:30:46 +0800 Subject: [PATCH] fix: apply with fuzzy lines Signed-off-by: jingfelix --- README.md | 6 ++++++ pdm.lock | 8 ++++---- pyproject.toml | 2 +- requirements.txt | 2 +- src/Patche/commands/apply.py | 17 ++++++++++++----- src/Patche/utils/resolve.py | 24 +++++++++++++++++++++--- 6 files changed, 45 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 115b7c0..f0ca838 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ Modern patch, written in Python. +## Usage + +```shell +patche apply +``` + ## Config `patche` loads the configuration from a file named `.patche.env` in `$HOME`. diff --git a/pdm.lock b/pdm.lock index a9ac3bf..62d1a91 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:35883457653897e26304508d572de32079d9747255fe14c2e96e20edeea08815" +content_hash = "sha256:6555696240de046378cde17536e2701e6f633824f840d48863c2781c5abaff22" [[metadata.targets]] requires_python = ">=3.10" @@ -341,11 +341,11 @@ files = [ [[package]] name = "whatthepatch-pydantic" -version = "1.0.6a1" +version = "1.0.6a2" requires_python = ">=3.7" summary = "A patch parsing and application library." groups = ["default"] files = [ - {file = "whatthepatch_pydantic-1.0.6a1-py3-none-any.whl", hash = "sha256:729215b49682ca52b4f85b8ddfe2d61927b162235f98561796e5fef512c0b796"}, - {file = "whatthepatch_pydantic-1.0.6a1.tar.gz", hash = "sha256:29326a7e8edd54f419b84d9166abf13a94d0537501ed1927369d2f997be89c38"}, + {file = "whatthepatch_pydantic-1.0.6a2-py3-none-any.whl", hash = "sha256:d767a6e149d36ea07785eb56363a4b033c19b944a24dc8bcd3c0af6beb0744b9"}, + {file = "whatthepatch_pydantic-1.0.6a2.tar.gz", hash = "sha256:a929f39579fe45bbeb7a4c599e1bd2ab93b9e947f2cfa02ddd82928469785880"}, ] diff --git a/pyproject.toml b/pyproject.toml index 24a2e3a..fef76ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dependencies = [ "pydantic>=2.5.3", "pydantic-settings>=2.2.1", "cscopy>=0.0.1", - "whatthepatch-pydantic==1.0.6a1", + "whatthepatch-pydantic==1.0.6a2", "structlog>=24.2.0", ] requires-python = ">=3.10" diff --git a/requirements.txt b/requirements.txt index 57fcc6b..8268d1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,4 +20,4 @@ typer==0.12.3 typer[all]==0.12.3 typing-extensions==4.12.2 viztracer==0.16.3 -whatthepatch-pydantic==1.0.6a1 +whatthepatch-pydantic==1.0.6a2 diff --git a/src/Patche/commands/apply.py b/src/Patche/commands/apply.py index 05c2e62..1ec6c8d 100644 --- a/src/Patche/commands/apply.py +++ b/src/Patche/commands/apply.py @@ -22,11 +22,13 @@ def apply( if not os.path.exists(patch_path): logger.error(f"Warning: {patch_path} not found!") - return + raise typer.Exit(code=1) if reverse: logger.info("Reversing patch...") + has_failed = False + with open(patch_path, mode="r", encoding="utf-8") as (f): diffes = parse_patch(f.read()).diff @@ -47,12 +49,17 @@ def apply( # 检查失败数 for failed_hunk in apply_result.failed_hunk_list: + has_failed = True logger.error(f"Failed hunk: {failed_hunk.index}") else: logger.error(f"{old_filename} not found!") + raise typer.Exit(code=1) # 写入文件 - with open(new_filename, mode="w+", encoding="utf-8") as f: - for line in new_line_list: - if line.status: - f.write(line.content + "\n") + if not has_failed: + with open(new_filename, mode="w+", encoding="utf-8") as f: + for line in new_line_list: + if line.status: + f.write(line.content + "\n") + + raise typer.Exit(code=1 if has_failed else 0) diff --git a/src/Patche/utils/resolve.py b/src/Patche/utils/resolve.py index 3306c56..73ced31 100644 --- a/src/Patche/utils/resolve.py +++ b/src/Patche/utils/resolve.py @@ -1,6 +1,6 @@ from Patche.app import logger from Patche.config import settings -from Patche.model import ApplyResult, Change, Hunk, Line +from Patche.model import ApplyResult, Hunk, Line from Patche.utils.common import find_list_positions @@ -25,14 +25,15 @@ def apply_change( # 然后对每个hunk进行处理,添加偏移 failed_hunk_list: list[Hunk] = [] + last_pos = None for hunk in hunk_list: current_hunk_fuzz = 0 while current_hunk_fuzz <= fuzz: - hunk.context = hunk.context[1:] - hunk.post = hunk.post[: fuzz - current_hunk_fuzz] + # hunk.context = hunk.context[1:] + # hunk.post = hunk.post[: fuzz - current_hunk_fuzz] logger.debug( f"current_fuzz: {current_hunk_fuzz} len(hunk.context): {len(hunk.context)} len(hunk.post): {len(hunk.post)}" @@ -49,6 +50,10 @@ def apply_change( current_hunk_fuzz += 1 + if current_hunk_fuzz <= fuzz: + hunk.context = hunk.context[1:] + hunk.post = hunk.post[: 3 - current_hunk_fuzz] + # 初始位置是 context 的第一个 # 注意,前几个有可能是空 pos_origin = None @@ -88,6 +93,19 @@ def apply_change( # 选择 offset 最小的 pos pos_new = pos_origin + min_offset - 1 + # 处理 pos_new 小于 last_pos 的情况 + logger.debug(f"pos_origin: {pos_origin}, last_pos: {last_pos}") + if last_pos is None: + last_pos = pos_new + elif pos_new < last_pos: + # 特别主要 pos_new 小于 last_pos 的情况 + logger.warning(f"Apply failed with hunk {hunk.index}") + logger.error(f"pos: {pos_new} is greater than last_pos: {last_pos}") + failed_hunk_list.append(hunk) + continue + else: + last_pos = pos_new + old_lines = [ change.line for change in hunk.context + hunk.middle + hunk.post