Skip to content

Work diff canvas misnames deleted, binary and mode-only files whose paths contain spaces #280

Closed

Description

@NopeCharon

Expected and actual behavior / 实际现象与期望

parse_unified_diff (server/unified_diff.py) should name each file in the Work attempt diff canvas by its real path.

Git does not quote paths that only contain spaces, so the header for my notes.txt is diff --git a/my notes.txt b/my notes.txt. _paths_from_diff_header splits that line with shlex.split and takes the 3rd and 4th words, so the paths become a/my and notes.txt and the file is shown as notes.txt.

Ordinary text edits hide this, because the following --- a/... / +++ b/... lines overwrite the path. Entries that have no +++ b/... line keep the wrong name:

  • deleted files (+++ /dev/null);
  • binary files (Binary files a/... and b/... differ);
  • mode-only changes (old mode / new mode).

Because the wrong name does not match changed_files, the real path is also appended as a second, hunk-less "modified" entry, so the canvas lists the file twice and shows it with the wrong status.

Steps to reproduce / 复现步骤

from server.unified_diff import parse_unified_diff

parsed = parse_unified_diff(
    "diff --git a/old notes.txt b/old notes.txt\n"
    "deleted file mode 100644\n"
    "--- a/old notes.txt\t\n"
    "+++ /dev/null\n"
    "@@ -1 +0,0 @@\n"
    "-x\n"
    "diff --git a/cover art.png b/cover art.png\n"
    "Binary files a/cover art.png and b/cover art.png differ\n",
    changed_files=["old notes.txt", "cover art.png"],
)
print([(f["path"], f["status"]) for f in parsed["files"]])

Actual: [('notes.txt', 'deleted'), ('art.png', 'modified'), ('old notes.txt', 'modified'), ('cover art.png', 'modified')]

Expected: [('old notes.txt', 'deleted'), ('cover art.png', 'modified')]

The input is real git diff HEAD output after deleting old notes.txt and editing the binary cover art.png.

Relevant environment / 使用环境

Platform-independent; current main (fdd672e). Spaced (and non-ASCII spaced) paths are already a supported case in tests/test_local_git_provider_diff.py.

Sanitized evidence / 错误信息或截图(请先脱敏)

Without renames or copies, both names in an unquoted diff --git header are the same path behind their prefixes, which is how Git itself splits such a header. Renames and copies are followed by rename from / rename to lines, which the parser already reads.

Related issues or fixes / 相关问题或修复

#274 / #275 fixed a different parser defect in the same function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions