优化终端里的git操作

This commit is contained in:
xintaofei
2026-03-21 16:29:36 +08:00
parent 6e2ae6fb36
commit 51e9d15c8e
4 changed files with 137 additions and 168 deletions

View File

@@ -3,51 +3,36 @@ use std::collections::HashMap;
use tauri::Manager;
use tauri::State;
use crate::db::AppDatabase;
use crate::git_credential;
use crate::terminal::error::TerminalError;
use crate::terminal::manager::{SpawnOptions, TerminalManager};
use crate::terminal::types::TerminalInfo;
/// Temp files created for a terminal credential session.
struct TerminalCredFiles {
cred_file: std::path::PathBuf,
helper_script: std::path::PathBuf,
}
/// Build extra env vars and temp credential files for the terminal session.
/// Build extra env vars for the terminal session.
///
/// Uses `credential.helper` with a custom credential helper script that speaks
/// git's structured credential protocol (host/protocol on stdin, username/password
/// on stdout). This is added via `GIT_CONFIG_COUNT` which APPENDS to the user's
/// existing credential helpers (e.g. macOS Keychain) for multi-valued keys.
/// Our helper is tried first; if it has no match, git falls through to existing helpers.
async fn prepare_credential_env(
db: &AppDatabase,
/// Uses `credential.helper` with a script that calls the app binary with
/// `--credential-helper`. The binary opens the DB, looks up the matching
/// account, and outputs credentials. No credentials are written to disk.
fn prepare_credential_env(
app_data_dir: &std::path::Path,
terminal_id: &str,
) -> (Option<HashMap<String, String>>, Option<TerminalCredFiles>) {
let accounts = match git_credential::load_github_accounts(&db.conn).await {
Some(s) if !s.accounts.is_empty() => s.accounts,
_ => return (None, None),
) -> Option<HashMap<String, String>> {
// Get the path to the current running binary
let app_binary = match std::env::current_exe() {
Ok(p) => p,
Err(e) => {
eprintln!("[TERM] failed to get current exe path: {}", e);
return None;
}
};
let cred_file = app_data_dir.join(format!("git-creds-{}.tmp", terminal_id));
if let Err(e) = git_credential::write_credential_store_file(&accounts, &cred_file) {
eprintln!("[TERM] failed to write credential store file: {}", e);
return (None, None);
}
let helper_script = match git_credential::create_credential_helper_script(
app_data_dir,
&cred_file,
terminal_id,
&app_binary,
) {
Ok(p) => p,
Err(e) => {
eprintln!("[TERM] failed to create credential helper script: {}", e);
let _ = std::fs::remove_file(&cred_file);
return (None, None);
return None;
}
};
@@ -62,19 +47,14 @@ async fn prepare_credential_env(
"GIT_CONFIG_KEY_0".to_string(),
"credential.helper".to_string(),
);
// credential.helper values without '!' prefix get "git-credential-" prepended.
// The '!' prefix tells git to run it as a raw shell command.
// The '!' prefix tells git to run as a raw shell command (not git-credential-<name>).
// Paths with spaces (e.g. "Application Support") must be quoted.
env.insert(
"GIT_CONFIG_VALUE_0".to_string(),
format!("!\"{}\"", helper_path_str),
);
let files = TerminalCredFiles {
cred_file,
helper_script,
};
(Some(env), Some(files))
Some(env)
}
#[tauri::command]
@@ -82,11 +62,9 @@ pub async fn terminal_spawn(
working_dir: String,
initial_command: Option<String>,
manager: State<'_, TerminalManager>,
db: State<'_, AppDatabase>,
app_handle: tauri::AppHandle,
window: tauri::WebviewWindow,
) -> Result<String, TerminalError> {
// Generate terminal ID early so we can use it for the credential file name
let terminal_id = uuid::Uuid::new_v4().to_string();
let app_data_dir = app_handle
@@ -94,12 +72,7 @@ pub async fn terminal_spawn(
.app_data_dir()
.map_err(|e| TerminalError::SpawnFailed(e.to_string()))?;
let (extra_env, cred_files) =
prepare_credential_env(&db, &app_data_dir, &terminal_id).await;
let temp_files = cred_files
.map(|f| vec![f.cred_file, f.helper_script])
.unwrap_or_default();
let extra_env = prepare_credential_env(&app_data_dir);
manager.spawn_with_id(
SpawnOptions {
@@ -108,7 +81,7 @@ pub async fn terminal_spawn(
owner_window_label: window.label().to_string(),
initial_command,
extra_env,
temp_files,
temp_files: vec![],
},
app_handle,
)