Skip to content

Commit 705f380

Browse files
committed
Modify Diff View FileTree to show all files
== Changes * removes Show Status button on diff * uses `git diff-tree` to generate the file tree for the diff update locale to remove diff.show_diff_stats
1 parent 7535af2 commit 705f380

File tree

14 files changed

+290
-182
lines changed

14 files changed

+290
-182
lines changed

options/locale/locale_en-US.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2595,7 +2595,6 @@ diff.commit = commit
25952595
diff.git-notes = Notes
25962596
diff.data_not_available = Diff Content Not Available
25972597
diff.options_button = Diff Options
2598-
diff.show_diff_stats = Show Stats
25992598
diff.download_patch = Download Patch File
26002599
diff.download_diff = Download Diff File
26012600
diff.show_split_view = Split View

routers/web/repo/commit.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,18 +344,30 @@ func Diff(ctx *context.Context) {
344344
ctx.Data["Reponame"] = repoName
345345

346346
var parentCommit *git.Commit
347+
var parentCommitID string
347348
if commit.ParentCount() > 0 {
348349
parentCommit, err = gitRepo.GetCommit(parents[0])
349350
if err != nil {
350351
ctx.NotFound(err)
351352
return
352353
}
354+
parentCommitID = parentCommit.ID.String()
353355
}
354356
setCompareContext(ctx, parentCommit, commit, userName, repoName)
355357
ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
356358
ctx.Data["Commit"] = commit
357359
ctx.Data["Diff"] = diff
358360

361+
if !fileOnly {
362+
diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, parentCommitID, commitID)
363+
if err != nil {
364+
ctx.ServerError("GetDiffTree", err)
365+
return
366+
}
367+
368+
ctx.Data["DiffTree"] = transformDiffTreeForUI(diffTree, nil)
369+
}
370+
359371
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
360372
if err != nil {
361373
log.Error("GetLatestCommitStatus: %v", err)

routers/web/repo/compare.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,16 @@ func PrepareCompareDiff(
633633
ctx.Data["Diff"] = diff
634634
ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
635635

636+
if !fileOnly {
637+
diffTree, err := gitdiff.GetDiffTree(ctx, ci.HeadGitRepo, false, beforeCommitID, headCommitID)
638+
if err != nil {
639+
ctx.ServerError("GetDiffTree", err)
640+
return false
641+
}
642+
643+
ctx.Data["DiffTree"] = transformDiffTreeForUI(diffTree, nil)
644+
}
645+
636646
headCommit, err := ci.HeadGitRepo.GetCommit(headCommitID)
637647
if err != nil {
638648
ctx.ServerError("GetCommit", err)

routers/web/repo/pull.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
761761

762762
var methodWithError string
763763
var diff *gitdiff.Diff
764+
shouldGetUserSpecificDiff := false
764765

765766
// if we're not logged in or only a single commit (or commit range) is shown we
766767
// have to load only the diff and not get the viewed information
@@ -772,6 +773,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
772773
} else {
773774
diff, err = gitdiff.SyncAndGetUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diffOptions, files...)
774775
methodWithError = "SyncAndGetUserSpecificDiff"
776+
shouldGetUserSpecificDiff = true
775777
}
776778
if err != nil {
777779
ctx.ServerError(methodWithError, err)
@@ -816,6 +818,27 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
816818
}
817819
}
818820

821+
if !fileOnly {
822+
// note: use mergeBase is set to false because we already have the merge base from the pull request info
823+
diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, pull.MergeBase, headCommitID)
824+
if err != nil {
825+
ctx.ServerError("GetDiffTree", err)
826+
return
827+
}
828+
829+
filesViewedState := make(map[string]pull_model.ViewedState)
830+
if shouldGetUserSpecificDiff {
831+
// This sort of sucks because we already fetch this when getting the diff
832+
review, err := pull_model.GetNewestReviewState(ctx, ctx.Doer.ID, issue.ID)
833+
if !(err != nil || review == nil || review.UpdatedFiles == nil) {
834+
// If there wasn't an error and we have a review with updated files, use that
835+
filesViewedState = review.UpdatedFiles
836+
}
837+
}
838+
839+
ctx.Data["DiffFiles"] = transformDiffTreeForUI(diffTree, filesViewedState)
840+
}
841+
819842
ctx.Data["Diff"] = diff
820843
ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
821844

routers/web/repo/treelist.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ package repo
66
import (
77
"net/http"
88

9+
pull_model "code.gitea.io/gitea/models/pull"
910
"code.gitea.io/gitea/modules/base"
1011
"code.gitea.io/gitea/modules/git"
1112
"code.gitea.io/gitea/services/context"
13+
"code.gitea.io/gitea/services/gitdiff"
1214

1315
"github.com/go-enry/go-enry/v2"
1416
)
@@ -52,3 +54,40 @@ func isExcludedEntry(entry *git.TreeEntry) bool {
5254

5355
return false
5456
}
57+
58+
type FileDiffFile struct {
59+
Name string
60+
NameHash string
61+
IsSubmodule bool
62+
IsBinary bool
63+
IsViewed bool
64+
Status string
65+
}
66+
67+
// transformDiffTreeForUI transforms a DiffTree into a slice of FileDiffFile for UI rendering
68+
// it also takes a map of file names to their viewed state, which is used to mark files as viewed
69+
func transformDiffTreeForUI(diffTree *gitdiff.DiffTree, filesViewedState map[string]pull_model.ViewedState) []FileDiffFile {
70+
files := make([]FileDiffFile, 0, len(diffTree.Files))
71+
72+
for _, file := range diffTree.Files {
73+
nameHash := git.HashFilePathForWebUI(file.HeadPath)
74+
isSubmodule := file.HeadMode == git.EntryModeCommit
75+
isBinary := file.HeadMode == git.EntryModeExec
76+
77+
isViewed := false
78+
if fileViewedState, ok := filesViewedState[file.Path()]; ok {
79+
isViewed = (fileViewedState == pull_model.Viewed)
80+
}
81+
82+
files = append(files, FileDiffFile{
83+
Name: file.HeadPath,
84+
NameHash: nameHash,
85+
IsSubmodule: isSubmodule,
86+
IsBinary: isBinary,
87+
IsViewed: isViewed,
88+
Status: file.Status,
89+
})
90+
}
91+
92+
return files
93+
}

services/gitdiff/git_diff_tree.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ type DiffTreeRecord struct {
3434
BaseBlobID string
3535
}
3636

37+
func (d *DiffTreeRecord) Path() string {
38+
if d.HeadPath != "" {
39+
return d.HeadPath
40+
}
41+
return d.BasePath
42+
}
43+
3744
// GetDiffTree returns the list of path of the files that have changed between the two commits.
3845
// If useMergeBase is true, the diff will be calculated using the merge base of the two commits.
3946
// This is the same behavior as using a three-dot diff in git diff.

templates/repo/diff/box.tmpl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
</div>
5959
{{end}}
6060
<script id="diff-data-script" type="module">
61-
const diffDataFiles = [{{range $i, $file := .Diff.Files}}{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Type:{{$file.Type}},IsBin:{{$file.IsBin}},IsSubmodule:{{$file.IsSubmodule}},Addition:{{$file.Addition}},Deletion:{{$file.Deletion}},IsViewed:{{$file.IsViewed}}},{{end}}];
61+
const diffDataFiles = [{{range $i, $file := .DiffFiles}}
62+
{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Status:{{$file.Status}},IsSubmodule:{{$file.IsSubmodule}},IsViewed:{{$file.IsViewed}}},{{end}}];
6263
const diffData = {
6364
isIncomplete: {{.Diff.IsIncomplete}},
6465
tooManyFilesMessage: "{{ctx.Locale.Tr "repo.diff.too_many_files"}}",

templates/repo/diff/options_dropdown.tmpl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<div class="ui dropdown tiny basic button" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.options_button"}}">
22
{{svg "octicon-kebab-horizontal"}}
33
<div class="menu">
4-
<a class="item" id="show-file-list-btn">{{ctx.Locale.Tr "repo.diff.show_diff_stats"}}</a>
54
{{if .Issue.Index}}
65
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.patch" download="{{.Issue.Index}}.patch">{{ctx.Locale.Tr "repo.diff.download_patch"}}</a>
76
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.diff" download="{{.Issue.Index}}.diff">{{ctx.Locale.Tr "repo.diff.download_diff"}}</a>

web_src/js/components/DiffFileList.vue

Lines changed: 0 additions & 60 deletions
This file was deleted.

web_src/js/components/DiffFileTree.vue

Lines changed: 6 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,18 @@
11
<script lang="ts" setup>
2-
import DiffFileTreeItem, {type Item} from './DiffFileTreeItem.vue';
3-
import {loadMoreFiles} from '../features/repo-diff.ts';
2+
import DiffFileTreeItem from './DiffFileTreeItem.vue';
43
import {toggleElem} from '../utils/dom.ts';
54
import {diffTreeStore} from '../modules/stores.ts';
65
import {setFileFolding} from '../features/file-fold.ts';
7-
import {computed, onMounted, onUnmounted} from 'vue';
6+
import {computed, nextTick, onMounted, onUnmounted} from 'vue';
7+
import {pathListToTree, mergeChildIfOnlyOneDir} from './file_tree.ts';
88
99
const LOCAL_STORAGE_KEY = 'diff_file_tree_visible';
1010
1111
const store = diffTreeStore();
1212
1313
const fileTree = computed(() => {
14-
const result: Array<Item> = [];
15-
for (const file of store.files) {
16-
// Split file into directories
17-
const splits = file.Name.split('/');
18-
let index = 0;
19-
let parent = null;
20-
let isFile = false;
21-
for (const split of splits) {
22-
index += 1;
23-
// reached the end
24-
if (index === splits.length) {
25-
isFile = true;
26-
}
27-
let newParent: Item = {
28-
name: split,
29-
children: [],
30-
isFile,
31-
};
32-
33-
if (isFile === true) {
34-
newParent.file = file;
35-
}
36-
37-
if (parent) {
38-
// check if the folder already exists
39-
const existingFolder = parent.children.find(
40-
(x) => x.name === split,
41-
);
42-
if (existingFolder) {
43-
newParent = existingFolder;
44-
} else {
45-
parent.children.push(newParent);
46-
}
47-
} else {
48-
const existingFolder = result.find((x) => x.name === split);
49-
if (existingFolder) {
50-
newParent = existingFolder;
51-
} else {
52-
result.push(newParent);
53-
}
54-
}
55-
parent = newParent;
56-
}
57-
}
58-
const mergeChildIfOnlyOneDir = (entries: Array<Record<string, any>>) => {
59-
for (const entry of entries) {
60-
if (entry.children) {
61-
mergeChildIfOnlyOneDir(entry.children);
62-
}
63-
if (entry.children.length === 1 && entry.children[0].isFile === false) {
64-
// Merge it to the parent
65-
entry.name = `${entry.name}/${entry.children[0].name}`;
66-
entry.children = entry.children[0].children;
67-
}
68-
}
69-
};
70-
// Merge folders with just a folder as children in order to
71-
// reduce the depth of our tree.
72-
mergeChildIfOnlyOneDir(result);
14+
const result = pathListToTree(store.files);
15+
mergeChildIfOnlyOneDir(result); // mutation
7316
return result;
7417
});
7518
@@ -78,8 +21,8 @@ onMounted(() => {
7821
store.fileTreeIsVisible = localStorage.getItem(LOCAL_STORAGE_KEY) !== 'false';
7922
document.querySelector('.diff-toggle-file-tree-button').addEventListener('click', toggleVisibility);
8023
81-
hashChangeListener();
8224
window.addEventListener('hashchange', hashChangeListener);
25+
nextTick(hashChangeListener);
8326
});
8427
8528
onUnmounted(() => {
@@ -121,19 +64,12 @@ function updateState(visible: boolean) {
12164
toggleElem(toShow, !visible);
12265
toggleElem(toHide, visible);
12366
}
124-
125-
function loadMoreData() {
126-
loadMoreFiles(store.linkLoadMore);
127-
}
12867
</script>
12968

13069
<template>
13170
<div v-if="store.fileTreeIsVisible" class="diff-file-tree-items">
13271
<!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
13372
<DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
134-
<div v-if="store.isIncomplete" class="tw-pt-1">
135-
<a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
136-
</div>
13773
</div>
13874
</template>
13975

0 commit comments

Comments
 (0)