Skip to content

Commit

Permalink
Modify Diff View FileTree to show all files
Browse files Browse the repository at this point in the history
== Changes

* removes Show Status button on diff
* uses `git diff-tree` to generate the file tree for the diff
  • Loading branch information
McRaeAlex committed Feb 6, 2025
1 parent a6613d9 commit 4799444
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 154 deletions.
8 changes: 8 additions & 0 deletions routers/web/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,14 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
}
}

// note: use mergeBase is set to false because we already have the merge base from the pull request info
diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, pull.MergeBase, headCommitID)
if err != nil {
ctx.ServerError("GetDiffTree", err)
return
}

ctx.Data["DiffFiles"] = transformDiffTreeForUI(diffTree, nil)
ctx.Data["Diff"] = diff
ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0

Expand Down
39 changes: 39 additions & 0 deletions routers/web/repo/treelist.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ package repo
import (
"net/http"

pull_model "code.gitea.io/gitea/models/pull"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/gitdiff"

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

return false
}

type FileDiffFile struct {
Name string
NameHash string
IsSubmodule bool
IsBinary bool
IsViewed bool
Status string
}

// transformDiffTreeForUI transforms a DiffTree into a slice of FileDiffFile for UI rendering
// it also takes a map of file names to their viewed state, which is used to mark files as viewed
func transformDiffTreeForUI(diffTree *gitdiff.DiffTree, filesViewedState map[string]pull_model.ViewedState) []FileDiffFile {
files := make([]FileDiffFile, 0, len(diffTree.Files))

for _, file := range diffTree.Files {
nameHash := git.HashFilePathForWebUI(file.HeadPath)
isSubmodule := file.HeadMode == git.EntryModeCommit
isBinary := file.HeadMode == git.EntryModeExec

isViewed := false
if fileViewedState, ok := filesViewedState[file.Path()]; ok {
isViewed = (fileViewedState == pull_model.Viewed)
}

files = append(files, FileDiffFile{
Name: file.HeadPath,
NameHash: nameHash,
IsSubmodule: isSubmodule,
IsBinary: isBinary,
IsViewed: isViewed,
Status: file.Status,
})
}

return files
}
7 changes: 7 additions & 0 deletions services/gitdiff/git_diff_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ type DiffTreeRecord struct {
BaseBlobID string
}

func (d *DiffTreeRecord) Path() string {
if d.HeadPath != "" {
return d.HeadPath
}
return d.BasePath
}

// GetDiffTree returns the list of path of the files that have changed between the two commits.
// If useMergeBase is true, the diff will be calculated using the merge base of the two commits.
// This is the same behavior as using a three-dot diff in git diff.
Expand Down
3 changes: 2 additions & 1 deletion templates/repo/diff/box.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
</div>
{{end}}
<script id="diff-data-script" type="module">
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}}];
const diffDataFiles = [{{range $i, $file := .DiffFiles}}
{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Status:{{$file.Status}},IsSubmodule:{{$file.IsSubmodule}},IsViewed:{{$file.IsViewed}}},{{end}}];
const diffData = {
isIncomplete: {{.Diff.IsIncomplete}},
tooManyFilesMessage: "{{ctx.Locale.Tr "repo.diff.too_many_files"}}",
Expand Down
1 change: 0 additions & 1 deletion templates/repo/diff/options_dropdown.tmpl
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<div class="ui dropdown tiny basic button" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.options_button"}}">
{{svg "octicon-kebab-horizontal"}}
<div class="menu">
<a class="item" id="show-file-list-btn">{{ctx.Locale.Tr "repo.diff.show_diff_stats"}}</a>
{{if .Issue.Index}}
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.patch" download="{{.Issue.Index}}.patch">{{ctx.Locale.Tr "repo.diff.download_patch"}}</a>
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.diff" download="{{.Issue.Index}}.diff">{{ctx.Locale.Tr "repo.diff.download_diff"}}</a>
Expand Down
60 changes: 0 additions & 60 deletions web_src/js/components/DiffFileList.vue

This file was deleted.

62 changes: 3 additions & 59 deletions web_src/js/components/DiffFileTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,71 +5,15 @@ import {toggleElem} from '../utils/dom.ts';
import {diffTreeStore} from '../modules/stores.ts';
import {setFileFolding} from '../features/file-fold.ts';
import {computed, onMounted, onUnmounted} from 'vue';
import { pathListToTree, mergeChildIfOnlyOneDir} from './file_tree.ts';
const LOCAL_STORAGE_KEY = 'diff_file_tree_visible';
const store = diffTreeStore();
const fileTree = computed(() => {
const result: Array<Item> = [];
for (const file of store.files) {
// Split file into directories
const splits = file.Name.split('/');
let index = 0;
let parent = null;
let isFile = false;
for (const split of splits) {
index += 1;
// reached the end
if (index === splits.length) {
isFile = true;
}
let newParent: Item = {
name: split,
children: [],
isFile,
};
if (isFile === true) {
newParent.file = file;
}
if (parent) {
// check if the folder already exists
const existingFolder = parent.children.find(
(x) => x.name === split,
);
if (existingFolder) {
newParent = existingFolder;
} else {
parent.children.push(newParent);
}
} else {
const existingFolder = result.find((x) => x.name === split);
if (existingFolder) {
newParent = existingFolder;
} else {
result.push(newParent);
}
}
parent = newParent;
}
}
const mergeChildIfOnlyOneDir = (entries: Array<Record<string, any>>) => {
for (const entry of entries) {
if (entry.children) {
mergeChildIfOnlyOneDir(entry.children);
}
if (entry.children.length === 1 && entry.children[0].isFile === false) {
// Merge it to the parent
entry.name = `${entry.name}/${entry.children[0].name}`;
entry.children = entry.children[0].children;
}
}
};
// Merge folders with just a folder as children in order to
// reduce the depth of our tree.
mergeChildIfOnlyOneDir(result);
let result = pathListToTree(store.files);
mergeChildIfOnlyOneDir(result); // mutation
return result;
});
Expand Down
33 changes: 10 additions & 23 deletions web_src/js/components/DiffFileTreeItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,8 @@
import {SvgIcon, type SvgName} from '../svg.ts';
import {diffTreeStore} from '../modules/stores.ts';
import {ref} from 'vue';
import { type Item, type File, type FileStatus } from './file_tree.ts';
type File = {
Name: string;
NameHash: string;
Type: number;
IsViewed: boolean;
IsSubmodule: boolean;
}
export type Item = {
name: string;
isFile: boolean;
file?: File;
children?: Item[];
};
defineProps<{
item: Item,
Expand All @@ -25,15 +12,15 @@ defineProps<{
const store = diffTreeStore();
const collapsed = ref(false);
function getIconForDiffType(pType: number) {
const diffTypes: Record<string, {name: SvgName, classes: Array<string>}> = {
'1': {name: 'octicon-diff-added', classes: ['text', 'green']},
'2': {name: 'octicon-diff-modified', classes: ['text', 'yellow']},
'3': {name: 'octicon-diff-removed', classes: ['text', 'red']},
'4': {name: 'octicon-diff-renamed', classes: ['text', 'teal']},
'5': {name: 'octicon-diff-renamed', classes: ['text', 'green']}, // there is no octicon for copied, so renamed should be ok
function getIconForDiffType(pType: FileStatus) {
const diffTypes: Record<FileStatus, {name: SvgName, classes: Array<string>}> = {
'added': {name: 'octicon-diff-added', classes: ['text', 'green']},
'modified': {name: 'octicon-diff-modified', classes: ['text', 'yellow']},
'deleted': {name: 'octicon-diff-removed', classes: ['text', 'red']},
'renamed': {name: 'octicon-diff-renamed', classes: ['text', 'teal']},
'typechange': {name: 'octicon-diff-modified', classes: ['text', 'green']}, // there is no octicon for copied, so renamed should be ok
};
return diffTypes[String(pType)];
return diffTypes[pType];
}
function fileIcon(file: File) {
Expand All @@ -54,7 +41,7 @@ function fileIcon(file: File) {
<!-- file -->
<SvgIcon :name="fileIcon(item.file)"/>
<span class="gt-ellipsis tw-flex-1">{{ item.name }}</span>
<SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/>
<SvgIcon :name="getIconForDiffType(item.file.Status).name" :class="getIconForDiffType(item.file.Status).classes"/>
</a>
<div v-else class="item-directory" :title="item.name" @click.stop="collapsed = !collapsed">
<!-- directory -->
Expand Down
76 changes: 76 additions & 0 deletions web_src/js/components/file_tree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
export type File = {
Name: string;
NameHash: string;
Status: FileStatus;
IsViewed: boolean;
IsSubmodule: boolean;
}
export type FileStatus = "added" | "modified" | "deleted" | "renamed" | "typechange";

export type Item = {
name: string;
path: string;
isFile: boolean;
file?: File;
children?: Item[];
};

export function pathListToTree(fileEntries: File[]): Item[] {
const pathToItem = new Map<string, Item>();

// init root node
const root: Item = { name: '', path: '', isFile: false, children: [] };
pathToItem.set('', root);

for (const fileEntry of fileEntries) {
const [parentPath, fileName] = splitPathLast(fileEntry.Name);

let parentItem = pathToItem.get(parentPath);
if (!parentItem) {
parentItem = constructParents(pathToItem, parentPath);
}

const fileItem: Item = { name: fileName, path: fileEntry.Name, isFile: true, file: fileEntry };

parentItem.children.push(fileItem);
}

return root.children;
}


function constructParents(pathToItem: Map<string, Item>, dirPath: string): Item {
const [dirParentPath, dirName] = splitPathLast(dirPath);

let parentItem = pathToItem.get(dirParentPath);
if (!parentItem) {
// if the parent node does not exist, create it
parentItem = constructParents(pathToItem, dirParentPath);
}

const dirItem: Item = { name: dirName, path: dirPath, isFile: false, children: [] };
parentItem.children.push(dirItem);
pathToItem.set(dirPath, dirItem);

return dirItem;
}

function splitPathLast(path: string): [string, string] {
const lastSlash = path.lastIndexOf('/');
return [path.substring(0, lastSlash), path.substring(lastSlash + 1)];
}

export function mergeChildIfOnlyOneDir(nodes: Item[]): void {
for (const node of nodes) {
if (node.children) {
mergeChildIfOnlyOneDir(node.children);
}

if (node.children?.length === 1 && node.children[0].children) {
const child = node.children[0];
node.name = `${node.name}/${child.name}`;
node.path = child.path;
node.children = child.children;
}
}
}
8 changes: 0 additions & 8 deletions web_src/js/features/repo-diff-filetree.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {createApp} from 'vue';
import DiffFileTree from '../components/DiffFileTree.vue';
import DiffFileList from '../components/DiffFileList.vue';

export function initDiffFileTree() {
const el = document.querySelector('#diff-file-tree');
Expand All @@ -10,10 +9,3 @@ export function initDiffFileTree() {
fileTreeView.mount(el);
}

export function initDiffFileList() {
const fileListElement = document.querySelector('#diff-file-list');
if (!fileListElement) return;

const fileListView = createApp(DiffFileList);
fileListView.mount(fileListElement);
}
Loading

0 comments on commit 4799444

Please sign in to comment.