diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 8d606a371..75e74f3c2 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -426,6 +426,29 @@ fn git_match_paths( Ok(files) } +#[tauri::command] +fn git_commit( + handle: tauri::AppHandle, + project_id: &str, + message: &str, + files: Vec<&str>, + push: bool, +) -> Result { + let app_state = handle.state::(); + + let repo = repositories::Repository::open( + &app_state.projects_storage, + &app_state.users_storage, + project_id, + )?; + + let success = repo + .commit(message, files, push) + .with_context(|| "Failed to commit")?; + + Ok(success) +} + fn main() { let quit = tauri::CustomMenuItem::new("quit".to_string(), "Quit"); let hide = tauri::CustomMenuItem::new("toggle".to_string(), format!("Hide {}", app_title())); @@ -539,7 +562,8 @@ fn main() { search, git_status, git_file_paths, - git_match_paths + git_match_paths, + git_commit ]); let tauri_context = generate_context!(); diff --git a/src-tauri/src/repositories/repositories.rs b/src-tauri/src/repositories/repositories.rs index 215298e15..21328dd32 100644 --- a/src-tauri/src/repositories/repositories.rs +++ b/src-tauri/src/repositories/repositories.rs @@ -1,5 +1,6 @@ use crate::{deltas, fs, projects, sessions, users}; use anyhow::{Context, Result}; +use git2::Signature; use std::{collections::HashMap, path::Path}; use tauri::regex::Regex; use walkdir::WalkDir; @@ -157,8 +158,6 @@ impl Repository { // get file status from git pub fn status(&self) -> Result> { - println!("Git Status"); - let mut options = git2::StatusOptions::new(); options.include_untracked(true); options.include_ignored(false); @@ -197,6 +196,51 @@ impl Repository { return Ok(files); } + + // commit method + pub fn commit(&self, message: &str, files: Vec<&str>, push: bool) -> Result { + println!("Git Commit"); + let repo = &self.git_repository; + + let config = repo.config()?; + let name = config.get_string("user.name")?; + let email = config.get_string("user.email")?; + + // Get the repository's index + let mut index = repo.index()?; + + // Add the specified files to the index + for path_str in files { + let path = Path::new(path_str); + index.add_path(path)?; + } + + // Write the updated index to disk + index.write()?; + + // Get the default signature for the repository + let signature = Signature::now(&name, &email)?; + + // Create the commit with the updated index + let tree_id = index.write_tree()?; + let tree = repo.find_tree(tree_id)?; + let parent_commit = repo.head()?.peel_to_commit()?; + let commit = repo.commit( + Some("HEAD"), + &signature, + &signature, + message, + &tree, + &[&parent_commit], + )?; + println!("Created commit {}", commit); + + if push { + println!("Pushing to remote"); + } + + return Ok(true); + } } fn init( diff --git a/src/lib/components/CommandPalette.svelte b/src/lib/components/CommandPalette.svelte index ae108021e..99ef84859 100644 --- a/src/lib/components/CommandPalette.svelte +++ b/src/lib/components/CommandPalette.svelte @@ -5,20 +5,38 @@ import BookmarkIcon from './icons/BookmarkIcon.svelte'; import BranchIcon from './icons/BranchIcon.svelte'; import { invoke } from '@tauri-apps/api'; - import { redirect } from '@sveltejs/kit'; import { goto } from '$app/navigation'; + import { shortPath } from '$lib/paths'; let showCommand = false; + let showCommit = false; + let is_command_down = false; let is_k_down = false; + let is_c_down = false; export let projectId: string; let palette: HTMLElement; + let commitPalette: HTMLElement; + + let changedFiles = {}; + let commitMessage = ''; + let commitMessageInput: HTMLElement; + + const listFiles = (params: { projectId: string }) => + invoke>('git_status', params); const matchFiles = (params: { projectId: string; matchPattern: string }) => invoke>('git_match_paths', params); + const commit = (params: { + projectId: string; + message: string; + files: Array; + push: boolean; + }) => invoke('git_commit', params); + function onKeyDown(event: KeyboardEvent) { if (event.repeat) return; switch (event.key) { @@ -29,8 +47,12 @@ case 'k': is_k_down = true; break; + case 'c': + is_c_down = true; + break; case 'Escape': showCommand = false; + showCommit = false; break; case 'ArrowDown': if (showCommand) { @@ -57,6 +79,10 @@ document.getElementById('command')?.focus(); }, 100); } + if (is_command_down && is_c_down) { + showCommit = true; + executeCommand('commit'); + } } function onKeyUp(event: KeyboardEvent) { @@ -69,6 +95,10 @@ is_k_down = false; event.preventDefault(); break; + case 'c': + is_c_down = false; + event.preventDefault(); + break; } } @@ -77,6 +107,9 @@ if (showCommand && !palette.contains(target)) { showCommand = false; } + if (showCommit && !commitPalette.contains(target)) { + showCommit = false; + } } let activeClass = ['active', 'bg-zinc-700/50', 'text-white']; @@ -121,10 +154,15 @@ function selectItem() { showCommand = false; + showCommit = false; const menu = document.getElementById('commandMenu'); if (menu) { const active = menu.querySelector('li.active'); if (active) { + const command = active.getAttribute('data-command'); + if (command) { + executeCommand(command); + } console.log('active', active); } else { goto('/projects/' + projectId + '/search?search=' + search); @@ -132,6 +170,25 @@ } } + function executeCommand(command: string) { + switch (command) { + case 'commit': + listFiles({ projectId: projectId }).then((files) => { + console.log('files', files); + changedFiles = files; + }); + showCommit = true; + setTimeout(function () { + commitMessageInput.focus(); + }, 100); + break; + case 'bookmark': + break; + case 'branch': + break; + } + } + let search = ''; $: { @@ -139,9 +196,9 @@ } let baseCommands = [ - { text: 'Commit', key: 'C', icon: CommitIcon }, - { text: 'Bookmark', key: 'B', icon: BookmarkIcon }, - { text: 'Branch', key: 'H', icon: BranchIcon } + { text: 'Commit', key: 'C', icon: CommitIcon, command: 'commit' }, + { text: 'Bookmark', key: 'B', icon: BookmarkIcon, command: 'bookmark' }, + { text: 'Branch', key: 'H', icon: BranchIcon, command: 'branch' } ]; $: menuItems = baseCommands; @@ -171,12 +228,35 @@ menuItems = searchResults; } } + + function doCommit() { + console.log('do commit', commitMessage); + // get checked files + let changedFiles: Array = []; + let doc = document.getElementsByClassName('file-checkbox'); + Array.from(doc).forEach((c) => { + if (c.checked) { + changedFiles.push(c.dataset['file']); + } + }); + console.log('files', changedFiles, commitMessage); + commit({ + projectId: projectId, + message: commitMessage, + files: changedFiles, + push: false + }).then((result) => { + console.log('commit result', result); + commitMessage = ''; + showCommit = false; + }); + }
- {#if showCommand} + {#if showCommand || showCommit}