diff --git a/modules/git/parse.go b/modules/git/parse.go index 045caf0bedb..844eb5f1969 100644 --- a/modules/git/parse.go +++ b/modules/git/parse.go @@ -56,13 +56,17 @@ func parseLsTreeLine(line []byte) (*LsTreeEntry, error) { return nil, fmt.Errorf("invalid ls-tree output (invalid object id): %q, err: %w", line, err) } - if len(entryName) > 0 && entryName[0] == '"' { - entry.Name, err = strconv.Unquote(string(entryName)) - if err != nil { - return nil, fmt.Errorf("invalid ls-tree output (invalid name): %q, err: %w", line, err) - } - } else { - entry.Name = string(entryName) + entry.Name, err = UnquoteDiffFilePath(string(entryName)) + if err != nil { + return nil, fmt.Errorf("invalid ls-tree output (invalid name): %q, err: %w", line, err) } return entry, nil } + +func UnquoteDiffFilePath(path string) (string, error) { + // ref: see the document of git config's "core.quotepath" + if !strings.HasPrefix(path, `"`) { + return path, nil + } + return strconv.Unquote(path) +} diff --git a/services/gitdiff/git_diff_tree.go b/services/gitdiff/git_diff_tree.go index 7708be20682..46d9fe99354 100644 --- a/services/gitdiff/git_diff_tree.go +++ b/services/gitdiff/git_diff_tree.go @@ -123,24 +123,19 @@ func validateGitDiffTreeArguments(ctx context.Context, gitRepo *git.Repository, } func parseGitDiffTree(gitOutput io.Reader) ([]*DiffTreeRecord, error) { - /* - The output of `git diff-tree --raw -r --find-renames` is of the form: + /* The output of `git diff-tree --raw -r --find-renames` is of the form: + : \t - : \t + or for renames: + : \t\t - or for renames: - - : \t\t - - See: for more details - */ + See: for more details */ results := make([]*DiffTreeRecord, 0) lines := bufio.NewScanner(gitOutput) for lines.Scan() { line := lines.Text() - - if len(line) == 0 { + if line == "" { continue } @@ -193,6 +188,13 @@ func parseGitDiffTreeLine(line string) (*DiffTreeRecord, error) { headPath = filePaths[0] } + if basePath, err = git.UnquoteDiffFilePath(basePath); err != nil { + return nil, fmt.Errorf("unparsable output for diff-tree --raw: `%s`, invalid base path: %w", line, err) + } + if headPath, err = git.UnquoteDiffFilePath(headPath); err != nil { + return nil, fmt.Errorf("unparsable output for diff-tree --raw: `%s`, invalid head path: %w", line, err) + } + return &DiffTreeRecord{ Status: status, Score: score, diff --git a/services/gitdiff/git_diff_tree_test.go b/services/gitdiff/git_diff_tree_test.go index 918ae56e624..84147f643a8 100644 --- a/services/gitdiff/git_diff_tree_test.go +++ b/services/gitdiff/git_diff_tree_test.go @@ -324,6 +324,22 @@ func TestParseGitDiffTree(t *testing.T) { }, }, }, + { + // make sure our code can unquote paths no matter core.quotePath is true or false: quoted chars are like "\t" or "\{oct}" + Name: "file modified with a C-quoted path", + GitOutput: ":100644 100644 b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0 04fea06420ca60892f73becee3614f6d023a4b7f M\t" + `"\346\227\245\346\234\254\350\252\236\ttest.txt"`, + Expected: []*DiffTreeRecord{ + { + Status: "modified", + HeadPath: "日本語\ttest.txt", + BasePath: "日本語\ttest.txt", + HeadMode: git.EntryModeBlob, + BaseMode: git.EntryModeBlob, + HeadBlobID: "04fea06420ca60892f73becee3614f6d023a4b7f", + BaseBlobID: "b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0", + }, + }, + }, { Name: "no changes", GitOutput: ``,