mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-23 20:54:50 +03:00
Add parsing for binary files
This commit is contained in:
parent
857cf7816c
commit
3c9eb56a37
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2494,6 +2494,7 @@ name = "gitbutler-repo"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"bstr",
|
||||
"git2",
|
||||
"gitbutler-branch",
|
||||
|
@ -1,7 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { invoke } from '$lib/backend/ipc';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
import HunkViewer from '$lib/hunk/HunkViewer.svelte';
|
||||
import InfoMessage from '$lib/shared/InfoMessage.svelte';
|
||||
import LargeDiffMessage from '$lib/shared/LargeDiffMessage.svelte';
|
||||
import { getContext } from '$lib/utils/context';
|
||||
import { computeAddedRemovedByHunk } from '$lib/utils/metrics';
|
||||
import { getLocalCommits, getLocalAndRemoteCommits } from '$lib/vbranches/contexts';
|
||||
import { getLockText } from '$lib/vbranches/tooltip';
|
||||
@ -32,6 +35,7 @@
|
||||
}: Props = $props();
|
||||
|
||||
let alwaysShow = $state(false);
|
||||
const project = getContext(Project);
|
||||
const localCommits = isFileLocked ? getLocalCommits() : undefined;
|
||||
const remoteCommits = isFileLocked ? getLocalAndRemoteCommits() : undefined;
|
||||
|
||||
@ -49,11 +53,56 @@
|
||||
}
|
||||
const maxLineNumber = $derived(sections.at(-1)?.maxLineNumber);
|
||||
const minWidth = $derived(getGutterMinWidth(maxLineNumber));
|
||||
|
||||
const KB = 1024;
|
||||
const MB = 1024 ** 2;
|
||||
const GB = 1024 ** 3;
|
||||
|
||||
function formatFileSize(bytes: number): string {
|
||||
if (bytes < KB) return bytes + ' B';
|
||||
else if (bytes < MB) return (bytes / KB).toFixed(1) + ' KB';
|
||||
else if (bytes < GB) return (bytes / MB).toFixed(1) + ' MB';
|
||||
else return (bytes / GB).toFixed(1) + ' GB';
|
||||
}
|
||||
|
||||
let blobSize: number = $state(0);
|
||||
let blobName: string | undefined = $state(undefined);
|
||||
let blobContent: string | undefined = $state(undefined);
|
||||
let imageMimeType: string | undefined = $state(undefined);
|
||||
let errorMessage: string | undefined = $state(undefined);
|
||||
|
||||
async function fetchBlobInfo() {
|
||||
try {
|
||||
const blobInfo: Record<string, string> = await invoke('get_blob_info', {
|
||||
relativePath: filePath,
|
||||
projectId: project.id
|
||||
});
|
||||
|
||||
blobContent = blobInfo.content;
|
||||
blobName = blobInfo.name;
|
||||
imageMimeType = blobInfo.mimeType;
|
||||
blobSize = parseInt(blobInfo.size || '0', 10);
|
||||
errorMessage = undefined;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
errorMessage = "untracked file";
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
fetchBlobInfo();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div class="hunks">
|
||||
{#if isBinary}
|
||||
Binary content not shown
|
||||
{#if errorMessage}
|
||||
<p>{errorMessage}</p>
|
||||
{:else if isBinary}
|
||||
{#if imageMimeType && blobContent}
|
||||
<img src="data:{imageMimeType};base64,{blobContent}" alt={blobName} />
|
||||
{/if}
|
||||
<p>Size: {formatFileSize(blobSize)}</p>
|
||||
{:else if isLarge}
|
||||
Diff too large to be shown
|
||||
{:else if sections.length > 50 && !alwaysShow}
|
||||
@ -125,4 +174,13 @@
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
p {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
@ -40,6 +40,7 @@ gitbutler-stack.workspace = true
|
||||
uuid.workspace = true
|
||||
itertools = "0.13"
|
||||
toml.workspace = true
|
||||
base64 = "0.22.1"
|
||||
|
||||
[[test]]
|
||||
name = "repo"
|
||||
|
@ -1,17 +1,18 @@
|
||||
use crate::{Config, RepositoryExt};
|
||||
use anyhow::Result;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use gitbutler_command_context::CommandContext;
|
||||
use gitbutler_project::Project;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{Config, RepositoryExt};
|
||||
|
||||
pub trait RepoCommands {
|
||||
fn add_remote(&self, name: &str, url: &str) -> Result<()>;
|
||||
fn remotes(&self) -> Result<Vec<String>>;
|
||||
fn get_local_config(&self, key: &str) -> Result<Option<String>>;
|
||||
fn set_local_config(&self, key: &str, value: &str) -> Result<()>;
|
||||
fn check_signing_settings(&self) -> Result<bool>;
|
||||
fn read_file_from_workspace(&self, relative_path: &Path) -> Result<String>;
|
||||
fn read_file_from_workspace(&self, relative_path: &Path) -> Result<HashMap<String, String>>;
|
||||
}
|
||||
|
||||
impl RepoCommands for Project {
|
||||
@ -47,7 +48,7 @@ impl RepoCommands for Project {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_file_from_workspace(&self, relative_path: &Path) -> Result<String> {
|
||||
fn read_file_from_workspace(&self, relative_path: &Path) -> Result<HashMap<String, String>> {
|
||||
let ctx = CommandContext::open(self)?;
|
||||
if self
|
||||
.path
|
||||
@ -60,12 +61,47 @@ impl RepoCommands for Project {
|
||||
let entry = tree.get_path(relative_path)?;
|
||||
let blob = ctx.repository().find_blob(entry.id())?;
|
||||
|
||||
let mut file_info = HashMap::new();
|
||||
|
||||
if !blob.is_binary() {
|
||||
let content = std::str::from_utf8(blob.content())?;
|
||||
Ok(content.to_string())
|
||||
let content = std::str::from_utf8(blob.content())?.to_string();
|
||||
file_info.insert("content".to_string(), content);
|
||||
} else {
|
||||
anyhow::bail!("File is binary");
|
||||
let file_name = relative_path
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let extension = relative_path
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_lowercase();
|
||||
|
||||
file_info.insert("name".to_string(), file_name);
|
||||
file_info.insert("size".to_string(), blob.size().to_string());
|
||||
|
||||
let image_types = [
|
||||
("jpg", "image/jpeg"),
|
||||
("jpeg", "image/jpeg"),
|
||||
("png", "image/png"),
|
||||
("gif", "image/gif"),
|
||||
("svg", "image/svg+xml"),
|
||||
("webp", "image/webp"),
|
||||
("bmp", "image/bmp"),
|
||||
];
|
||||
|
||||
if let Some(&(_, mime_type)) =
|
||||
image_types.iter().find(|&&(ext, _)| ext == extension)
|
||||
{
|
||||
let binary_content = blob.content();
|
||||
let encoded_content = general_purpose::STANDARD.encode(binary_content);
|
||||
file_info.insert("content".to_string(), encoded_content);
|
||||
file_info.insert("mimeType".to_string(), mime_type.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(file_info)
|
||||
} else {
|
||||
anyhow::bail!("Invalid workspace file");
|
||||
}
|
||||
|
@ -152,6 +152,7 @@ fn main() {
|
||||
repo::commands::git_clone_repository,
|
||||
repo::commands::get_uncommited_files,
|
||||
repo::commands::get_pr_template_contents,
|
||||
repo::commands::get_blob_info,
|
||||
virtual_branches::commands::list_virtual_branches,
|
||||
virtual_branches::commands::create_virtual_branch,
|
||||
virtual_branches::commands::delete_local_branch,
|
||||
|
@ -1,16 +1,16 @@
|
||||
pub mod commands {
|
||||
use crate::error::{Error, UnmarkedError};
|
||||
use anyhow::Result;
|
||||
use gitbutler_branch_actions::RemoteBranchFile;
|
||||
use gitbutler_project as projects;
|
||||
use gitbutler_project::ProjectId;
|
||||
use gitbutler_repo::RepoCommands;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use tauri::State;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::error::{Error, UnmarkedError};
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(projects), err(Debug))]
|
||||
pub fn git_get_local_config(
|
||||
@ -77,6 +77,22 @@ pub mod commands {
|
||||
) -> Result<String, Error> {
|
||||
let project = projects.get(project_id)?;
|
||||
|
||||
Ok(project
|
||||
.read_file_from_workspace(relative_path)?
|
||||
.get("content")
|
||||
.unwrap()
|
||||
.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(projects))]
|
||||
pub fn get_blob_info(
|
||||
projects: State<'_, projects::Controller>,
|
||||
project_id: ProjectId,
|
||||
relative_path: &Path,
|
||||
) -> Result<HashMap<String, String>, Error> {
|
||||
let project = projects.get(project_id)?;
|
||||
|
||||
Ok(project.read_file_from_workspace(relative_path)?)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user