Skip to content

Commit a792b6e

Browse files
committed
Create re-usable file service for reading git blobs
1 parent 850b53c commit a792b6e

File tree

9 files changed

+103
-66
lines changed

9 files changed

+103
-66
lines changed

apps/desktop/src/lib/file/FileDiff.svelte

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
<script lang="ts">
2-
import { invoke } from '$lib/backend/ipc';
32
import { Project } from '$lib/backend/projects';
3+
import { FileService } from '$lib/files/fileService';
44
import HunkViewer from '$lib/hunk/HunkViewer.svelte';
55
import InfoMessage from '$lib/shared/InfoMessage.svelte';
66
import LargeDiffMessage from '$lib/shared/LargeDiffMessage.svelte';
77
import { computeAddedRemovedByHunk } from '$lib/utils/metrics';
88
import { getLocalCommits, getLocalAndRemoteCommits } from '$lib/vbranches/contexts';
99
import { getLockText } from '$lib/vbranches/tooltip';
1010
import { getContext } from '@gitbutler/shared/context';
11+
import type { FileInfo } from '$lib/files/file';
1112
import type { HunkSection, ContentSection } from '$lib/utils/fileSections';
1213
13-
interface FileInfo {
14-
content: string;
15-
name?: string;
16-
mimeType?: string;
17-
size?: number;
18-
}
19-
2014
interface Props {
2115
filePath: string;
2216
isBinary: boolean;
@@ -43,6 +37,7 @@
4337
4438
let alwaysShow = $state(false);
4539
const project = getContext(Project);
40+
const fileService = getContext(FileService);
4641
const localCommits = isFileLocked ? getLocalCommits() : undefined;
4742
const remoteCommits = isFileLocked ? getLocalAndRemoteCommits() : undefined;
4843
@@ -80,18 +75,15 @@
8075
});
8176
8277
async function fetchBlobInfo() {
78+
if (!isBinary) {
79+
return;
80+
}
8381
try {
84-
const fetchedFileInfo: FileInfo = await invoke('get_blob_info', {
85-
relativePath: filePath,
86-
projectId: project.id,
87-
commitId
88-
});
89-
fileInfo = fetchedFileInfo;
90-
91-
// If file.size > 5mb; don't render it
92-
if (fileInfo.size && fileInfo.size > 5 * 1024 * 1024) {
93-
isLarge = true;
94-
}
82+
const file = commitId
83+
? await fileService.readFromCommit(filePath, project.id, commitId)
84+
: await fileService.readFromWorkspace(filePath, project.id);
85+
fileInfo = file.data;
86+
isLarge = file.isLarge;
9587
} catch (error) {
9688
console.error(error);
9789
}

apps/desktop/src/lib/files/file.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type FileInfo = {
2+
content: string;
3+
name?: string;
4+
mimeType?: string;
5+
size?: number;
6+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Tauri } from '$lib/backend/tauri';
2+
import type { FileInfo } from './file';
3+
4+
export class FileService {
5+
constructor(private tauri: Tauri) {}
6+
7+
async readFromWorkspace(filePath: string, projectId: string) {
8+
const data: FileInfo = await this.tauri.invoke('get_workspace_file', {
9+
relativePath: filePath,
10+
projectId: projectId
11+
});
12+
return {
13+
data,
14+
isLarge: isLarge(data.size)
15+
};
16+
}
17+
18+
async readFromCommit(filePath: string, projectId: string, commitId: string | undefined) {
19+
const data: FileInfo = await this.tauri.invoke('get_commit_file', {
20+
relativePath: filePath,
21+
projectId: projectId,
22+
commitId
23+
});
24+
return {
25+
data,
26+
isLarge: isLarge(data.size)
27+
};
28+
}
29+
}
30+
31+
function isLarge(size: number | undefined) {
32+
return size && size > 5 * 1024 * 1024 ? true : false;
33+
}

apps/desktop/src/routes/+layout.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { PromptService } from '$lib/backend/prompt';
1010
import { Tauri } from '$lib/backend/tauri';
1111
import { UpdaterService } from '$lib/backend/updater';
1212
import { loadAppSettings } from '$lib/config/appSettings';
13+
import { FileService } from '$lib/files/fileService';
1314
import { RemotesService } from '$lib/remotes/service';
1415
import { RustSecretService } from '$lib/secrets/secretsService';
1516
import { TokenMemoryService } from '$lib/stores/tokenMemoryService';
@@ -45,7 +46,8 @@ export const load: LayoutLoad = async () => {
4546
const tokenMemoryService = new TokenMemoryService();
4647
const httpClient = new HttpClient(window.fetch, PUBLIC_API_BASE_URL, tokenMemoryService.token);
4748
const authService = new AuthService();
48-
const updaterService = new UpdaterService(new Tauri(), posthog);
49+
const tauri = new Tauri();
50+
const updaterService = new UpdaterService(tauri, posthog);
4951
const promptService = new PromptService();
5052

5153
const userService = new UserService(httpClient, tokenMemoryService, posthog);
@@ -59,6 +61,7 @@ export const load: LayoutLoad = async () => {
5961
const aiPromptService = new AIPromptService();
6062
const lineManagerFactory = new LineManagerFactory();
6163
const stackingLineManagerFactory = new StackingLineManagerFactory();
64+
const fileService = new FileService(tauri);
6265

6366
return {
6467
commandService,
@@ -77,6 +80,8 @@ export const load: LayoutLoad = async () => {
7780
lineManagerFactory,
7881
stackingLineManagerFactory,
7982
secretsService,
80-
posthog
83+
posthog,
84+
tauri,
85+
fileService
8186
};
8287
};

apps/desktop/src/routes/[projectId]/+layout.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import { showHistoryView } from '$lib/config/config';
1919
import { cloudFunctionality } from '$lib/config/uiFeatureFlags';
2020
import { StackingReorderDropzoneManagerFactory } from '$lib/dragging/stackingReorderDropzoneManager';
21+
import { FileService } from '$lib/files/fileService';
2122
import { DefaultForgeFactory } from '$lib/forge/forgeFactory';
2223
import { octokitFromAccessToken } from '$lib/forge/github/octokit';
2324
import { createForgeStore } from '$lib/forge/interface/forge';
@@ -93,6 +94,7 @@
9394
setContext(SyncedSnapshotService, data.syncedSnapshotService);
9495
setContext(CloudBranchesService, data.cloudBranchesService);
9596
setContext(CloudBranchCreationService, data.cloudBranchCreationService);
97+
setContext(FileService, data.fileService);
9698
});
9799
98100
const routesService = getRoutesService();

crates/gitbutler-repo/src/commands.rs

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,11 @@ pub trait RepoCommands {
116116
///
117117
/// If nothing could be found at `probably_relative_path`, the returned structure indicates this,
118118
/// but it's no error.
119-
fn read_file_from_workspace(
119+
fn read_file_from_workspace(&self, probably_relative_path: &Path) -> Result<FileInfo>;
120+
121+
fn read_file_from_commit(
120122
&self,
121-
treeish: Option<Oid>,
123+
treeish: Oid,
122124
probably_relative_path: &Path,
123125
) -> Result<FileInfo>;
124126
}
@@ -182,24 +184,10 @@ impl RepoCommands for Project {
182184
Ok(())
183185
}
184186

185-
fn read_file_from_workspace(
186-
&self,
187-
treeish: Option<Oid>,
188-
probably_relative_path: &Path,
189-
) -> Result<FileInfo> {
187+
fn read_file_from_workspace(&self, probably_relative_path: &Path) -> Result<FileInfo> {
190188
let ctx = CommandContext::open(self)?;
191189
let repo = ctx.repo();
192190

193-
if let Some(treeish) = treeish {
194-
if !probably_relative_path.is_relative() {
195-
bail!(
196-
"Refusing to read '{}' from tree as it's not relative to the worktree",
197-
probably_relative_path.display(),
198-
);
199-
}
200-
return read_file_from_tree(repo, Some(treeish), probably_relative_path);
201-
}
202-
203191
let (path_in_worktree, relative_path) = if probably_relative_path.is_relative() {
204192
(
205193
gix::path::realpath(self.path.join(probably_relative_path))?,
@@ -238,30 +226,33 @@ impl RepoCommands for Project {
238226
let blob = repo.find_blob(entry.id)?;
239227
FileInfo::from_content(&relative_path, blob.content())
240228
}
241-
None => read_file_from_tree(repo, None, &relative_path)?,
229+
None => self.read_file_from_commit(
230+
repo.head()?.peel_to_commit()?.id(),
231+
&relative_path,
232+
)?,
242233
}
243234
}
244235
Err(err) => return Err(err.into()),
245236
})
246237
}
247-
}
248238

249-
fn read_file_from_tree(
250-
repo: &git2::Repository,
251-
treeish: Option<Oid>,
252-
relative_path: &Path,
253-
) -> Result<FileInfo> {
254-
let tree = if let Some(id) = treeish {
255-
repo.find_object(id, None)?.peel_to_tree()?
256-
} else {
257-
repo.head()?.peel_to_tree()?
258-
};
259-
Ok(match tree.get_path(relative_path) {
260-
Ok(entry) => {
261-
let blob = repo.find_blob(entry.id())?;
262-
FileInfo::from_content(relative_path, blob.content())
239+
fn read_file_from_commit(&self, commit_id: Oid, relative_path: &Path) -> Result<FileInfo> {
240+
if !relative_path.is_relative() {
241+
bail!(
242+
"Refusing to read '{}' from tree as it's not relative to the worktree",
243+
relative_path.display(),
244+
);
263245
}
264-
Err(e) if e.code() == git2::ErrorCode::NotFound => FileInfo::deleted(),
265-
Err(e) => return Err(e.into()),
266-
})
246+
let ctx = CommandContext::open(self)?;
247+
let repo = ctx.repo();
248+
let tree = repo.find_commit(commit_id)?.tree()?;
249+
Ok(match tree.get_path(relative_path) {
250+
Ok(entry) => {
251+
let blob = repo.find_blob(entry.id())?;
252+
FileInfo::from_content(relative_path, blob.content())
253+
}
254+
Err(e) if e.code() == git2::ErrorCode::NotFound => FileInfo::deleted(),
255+
Err(e) => return Err(e.into()),
256+
})
257+
}
267258
}

crates/gitbutler-tauri/src/forge.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub mod commands {
4949
.into());
5050
}
5151
Ok(project
52-
.read_file_from_workspace(None, relative_path)?
52+
.read_file_from_workspace(relative_path)?
5353
.content
5454
.context("PR template was not valid UTF-8")?)
5555
}

crates/gitbutler-tauri/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ fn main() {
172172
repo::commands::check_signing_settings,
173173
repo::commands::git_clone_repository,
174174
repo::commands::get_uncommited_files,
175-
repo::commands::get_blob_info,
175+
repo::commands::get_commit_file,
176+
repo::commands::get_workspace_file,
176177
virtual_branches::commands::list_virtual_branches,
177178
virtual_branches::commands::create_virtual_branch,
178179
virtual_branches::commands::delete_local_branch,

crates/gitbutler-tauri/src/repo.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
pub mod commands {
22
use crate::error::{Error, UnmarkedError};
33
use anyhow::Result;
4-
use git2::Oid;
54
use gitbutler_branch_actions::RemoteBranchFile;
65
use gitbutler_project as projects;
76
use gitbutler_project::ProjectId;
@@ -72,17 +71,25 @@ pub mod commands {
7271

7372
#[tauri::command(async)]
7473
#[instrument(skip(projects))]
75-
pub fn get_blob_info(
74+
pub fn get_commit_file(
7675
projects: State<'_, projects::Controller>,
7776
project_id: ProjectId,
7877
relative_path: &Path,
79-
commit_id: Option<String>,
78+
commit_id: String,
8079
) -> Result<FileInfo, Error> {
8180
let project = projects.get(project_id)?;
82-
let commit_oid = commit_id
83-
.map(|id| Oid::from_str(&id).map_err(|e| anyhow::anyhow!(e)))
84-
.transpose()?;
81+
let commit_oid = git2::Oid::from_str(commit_id.as_ref()).map_err(anyhow::Error::from)?;
82+
Ok(project.read_file_from_commit(commit_oid, relative_path)?)
83+
}
8584

86-
Ok(project.read_file_from_workspace(commit_oid, relative_path)?)
85+
#[tauri::command(async)]
86+
#[instrument(skip(projects))]
87+
pub fn get_workspace_file(
88+
projects: State<'_, projects::Controller>,
89+
project_id: ProjectId,
90+
relative_path: &Path,
91+
) -> Result<FileInfo, Error> {
92+
let project = projects.get(project_id)?;
93+
Ok(project.read_file_from_workspace(relative_path)?)
8794
}
8895
}

0 commit comments

Comments
 (0)