diff --git a/modules/git/parse.go b/modules/git/parse.go index d4ff0ecb23e6a..94020e690dd05 100644 --- a/modules/git/parse.go +++ b/modules/git/parse.go @@ -46,8 +46,8 @@ func parseLsTreeLine(line []byte) (*LsTreeEntry, error) { entry.Size = optional.Some(size) } - entry.EntryMode, err = ParseEntryMode(string(entryMode)) - if err != nil || entry.EntryMode == EntryModeNoEntry { + entry.EntryMode = ParseEntryMode(string(entryMode)) + if entry.EntryMode == EntryModeNoEntry { return nil, fmt.Errorf("invalid ls-tree output (invalid mode): %q, err: %w", line, err) } diff --git a/modules/git/tree_entry_mode.go b/modules/git/tree_entry_mode.go index f36c07bc2a002..2ceba113740cd 100644 --- a/modules/git/tree_entry_mode.go +++ b/modules/git/tree_entry_mode.go @@ -4,7 +4,6 @@ package git import ( - "fmt" "strconv" ) @@ -55,21 +54,38 @@ func (e EntryMode) IsExecutable() bool { return e == EntryModeExec } -func ParseEntryMode(mode string) (EntryMode, error) { +func ParseEntryMode(mode string) EntryMode { switch mode { case "000000": - return EntryModeNoEntry, nil + return EntryModeNoEntry case "100644": - return EntryModeBlob, nil + return EntryModeBlob case "100755": - return EntryModeExec, nil + return EntryModeExec case "120000": - return EntryModeSymlink, nil + return EntryModeSymlink case "160000": - return EntryModeCommit, nil - case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons - return EntryModeTree, nil + return EntryModeCommit + case "040000": + return EntryModeTree default: - return 0, fmt.Errorf("unparsable entry mode: %s", mode) + // git uses 040000 for tree object, but some users may get 040755 from non-standard git implementations + m, _ := strconv.ParseInt(mode, 8, 32) + modeInt := EntryMode(m) + switch modeInt & 0o770000 { + case 0o040000: + return EntryModeTree + case 0o160000: + return EntryModeCommit + case 0o120000: + return EntryModeSymlink + case 0o100000: + if modeInt&0o777 == 0o755 { + return EntryModeExec + } + return EntryModeBlob + default: + return EntryModeNoEntry + } } } diff --git a/modules/git/tree_entry_test.go b/modules/git/tree_entry_test.go index b28abfb545152..8e3fb5ff9933d 100644 --- a/modules/git/tree_entry_test.go +++ b/modules/git/tree_entry_test.go @@ -27,3 +27,30 @@ func TestEntriesCustomSort(t *testing.T) { entries.CustomSort(strings.Compare) assert.Equal(t, expected, entries) } + +func TestParseEntryMode(t *testing.T) { + tests := []struct { + modeStr string + expectMod EntryMode + }{ + {"000000", EntryModeNoEntry}, + {"000755", EntryModeNoEntry}, + + {"100644", EntryModeBlob}, + {"100755", EntryModeExec}, + + {"120000", EntryModeSymlink}, + {"120755", EntryModeSymlink}, + {"160000", EntryModeCommit}, + {"160755", EntryModeCommit}, + + {"040000", EntryModeTree}, + {"040755", EntryModeTree}, + + {"777777", EntryModeNoEntry}, // invalid mode + } + for _, test := range tests { + mod := ParseEntryMode(test.modeStr) + assert.Equal(t, test.expectMod, mod, "modeStr: %s", test.modeStr) + } +} diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 480aafe87951c..1dbb0975a3d25 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -2542,8 +2542,8 @@ "repo.diff.too_many_files": "Some files were not shown because too many files have changed in this diff", "repo.diff.show_more": "Show More", "repo.diff.load": "Load Diff", - "repo.diff.generated": "generated", - "repo.diff.vendored": "vendored", + "repo.diff.generated": "Generated", + "repo.diff.vendored": "Vendored", "repo.diff.comment.add_line_comment": "Add line comment", "repo.diff.comment.placeholder": "Leave a comment", "repo.diff.comment.add_single_comment": "Add single comment", @@ -3724,8 +3724,8 @@ "projects.exit_fullscreen": "Exit Fullscreen", "git.filemode.changed_filemode": "%[1]s → %[2]s", "git.filemode.directory": "Directory", - "git.filemode.normal_file": "Normal file", - "git.filemode.executable_file": "Executable file", - "git.filemode.symbolic_link": "Symbolic link", + "git.filemode.normal_file": "Regular", + "git.filemode.executable_file": "Executable", + "git.filemode.symbolic_link": "Symlink", "git.filemode.submodule": "Submodule" } diff --git a/services/gitdiff/git_diff_tree.go b/services/gitdiff/git_diff_tree.go index 2a3c7c944504f..b4f26210be785 100644 --- a/services/gitdiff/git_diff_tree.go +++ b/services/gitdiff/git_diff_tree.go @@ -166,16 +166,6 @@ func parseGitDiffTreeLine(line string) (*DiffTreeRecord, error) { return nil, fmt.Errorf("unparsable output for diff-tree --raw: `%s`, expected 5 space delimited values got %d)", line, len(fields)) } - baseMode, err := git.ParseEntryMode(fields[0]) - if err != nil { - return nil, err - } - - headMode, err := git.ParseEntryMode(fields[1]) - if err != nil { - return nil, err - } - baseBlobID := fields[2] headBlobID := fields[3] @@ -201,8 +191,8 @@ func parseGitDiffTreeLine(line string) (*DiffTreeRecord, error) { return &DiffTreeRecord{ Status: status, Score: score, - BaseMode: baseMode, - HeadMode: headMode, + BaseMode: git.ParseEntryMode(fields[0]), + HeadMode: git.ParseEntryMode(fields[1]), BaseBlobID: baseBlobID, HeadBlobID: headBlobID, BasePath: basePath, diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index be5c1dbece1cf..f00c90d737808 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -399,20 +399,20 @@ type DiffFile struct { isAmbiguous bool // basic fields (parsed from diff result) - Name string - NameHash string - OldName string - Addition int - Deletion int - Type DiffFileType - Mode string - OldMode string - IsCreated bool - IsDeleted bool - IsBin bool - IsLFSFile bool - IsRenamed bool - IsSubmodule bool + Name string + NameHash string + OldName string + Addition int + Deletion int + Type DiffFileType + EntryMode string + OldEntryMode string + IsCreated bool + IsDeleted bool + IsBin bool + IsLFSFile bool + IsRenamed bool + IsSubmodule bool // basic fields but for render purpose only Sections []*DiffSection IsIncomplete bool @@ -501,21 +501,36 @@ func (diffFile *DiffFile) ShouldBeHidden() bool { return diffFile.IsGenerated || diffFile.IsViewed } -func (diffFile *DiffFile) ModeTranslationKey(mode string) string { - switch mode { - case "040000": - return "git.filemode.directory" - case "100644": - return "git.filemode.normal_file" - case "100755": - return "git.filemode.executable_file" - case "120000": - return "git.filemode.symbolic_link" - case "160000": - return "git.filemode.submodule" - default: - return mode +func (diffFile *DiffFile) TranslateDiffEntryMode(locale translation.Locale) string { + entryModeTr := func(mode string) string { + entryMode := git.ParseEntryMode(mode) + switch { + case entryMode.IsDir(): + return locale.TrString("git.filemode.directory") + case entryMode.IsRegular(): + return locale.TrString("git.filemode.normal_file") + case entryMode.IsExecutable(): + return locale.TrString("git.filemode.executable_file") + case entryMode.IsLink(): + return locale.TrString("git.filemode.symbolic_link") + case entryMode.IsSubModule(): + return locale.TrString("git.filemode.submodule") + default: + return mode + } + } + + if diffFile.EntryMode != "" && diffFile.OldEntryMode != "" { + oldMode := entryModeTr(diffFile.OldEntryMode) + newMode := entryModeTr(diffFile.EntryMode) + return locale.TrString("git.filemode.changed_filemode", oldMode, newMode) + } + if diffFile.EntryMode != "" { + if entryMode := git.ParseEntryMode(diffFile.EntryMode); !entryMode.IsRegular() { + return entryModeTr(diffFile.EntryMode) + } } + return "" } type limitByteWriter struct { @@ -695,10 +710,10 @@ parsingLoop: strings.HasPrefix(line, "new mode "): if strings.HasPrefix(line, "old mode ") { - curFile.OldMode = prepareValue(line, "old mode ") + curFile.OldEntryMode = prepareValue(line, "old mode ") } if strings.HasPrefix(line, "new mode ") { - curFile.Mode = prepareValue(line, "new mode ") + curFile.EntryMode = prepareValue(line, "new mode ") } if strings.HasSuffix(line, " 160000\n") { curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{} @@ -733,7 +748,7 @@ parsingLoop: curFile.Type = DiffFileAdd curFile.IsCreated = true if strings.HasPrefix(line, "new file mode ") { - curFile.Mode = prepareValue(line, "new file mode ") + curFile.EntryMode = prepareValue(line, "new file mode ") } if strings.HasSuffix(line, " 160000\n") { curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{} diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index ff9bd2e792ede..2a3330d890b73 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -82,43 +82,42 @@ {{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}} {{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.Repository.IsArchived) $.IsShowingAllCommits}}