优化代码冲突解决
This commit is contained in:
@@ -38,6 +38,7 @@ pub struct GitConflictInfo {
|
||||
pub has_conflicts: bool,
|
||||
pub conflicted_files: Vec<String>,
|
||||
pub operation: String,
|
||||
pub upstream_commit: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -553,6 +554,9 @@ pub async fn git_pull(path: String) -> Result<GitPullResult, AppCommandError> {
|
||||
conflict: None,
|
||||
});
|
||||
}
|
||||
let upstream_commit = String::from_utf8_lossy(&upstream_check.stdout)
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
// Step 3: check if we can fast-forward
|
||||
let merge_base = crate::process::tokio_command("git")
|
||||
@@ -609,6 +613,7 @@ pub async fn git_pull(path: String) -> Result<GitPullResult, AppCommandError> {
|
||||
has_conflicts: true,
|
||||
conflicted_files,
|
||||
operation: "pull".to_string(),
|
||||
upstream_commit: Some(upstream_commit),
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -649,10 +654,15 @@ pub async fn git_pull(path: String) -> Result<GitPullResult, AppCommandError> {
|
||||
|
||||
/// Start a merge with the upstream branch (used by merge workspace after pull conflict detection).
|
||||
/// This recreates the conflict state so that :1:, :2:, :3: stage entries are available.
|
||||
/// If `upstream_commit` is provided, merge against that specific commit instead of `@{u}`.
|
||||
#[tauri::command]
|
||||
pub async fn git_start_pull_merge(path: String) -> Result<(), AppCommandError> {
|
||||
pub async fn git_start_pull_merge(
|
||||
path: String,
|
||||
upstream_commit: Option<String>,
|
||||
) -> Result<(), AppCommandError> {
|
||||
let target = upstream_commit.as_deref().unwrap_or("@{u}");
|
||||
let output = crate::process::tokio_command("git")
|
||||
.args(["merge", "--no-commit", "@{u}"])
|
||||
.args(["merge", "--no-commit", target])
|
||||
.current_dir(&path)
|
||||
.output()
|
||||
.await
|
||||
@@ -671,6 +681,17 @@ pub async fn git_start_pull_merge(path: String) -> Result<(), AppCommandError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn git_has_merge_head(path: String) -> Result<bool, AppCommandError> {
|
||||
let output = crate::process::tokio_command("git")
|
||||
.args(["rev-parse", "--verify", "MERGE_HEAD"])
|
||||
.current_dir(&path)
|
||||
.output()
|
||||
.await
|
||||
.map_err(AppCommandError::io)?;
|
||||
Ok(output.status.success())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn git_fetch(path: String) -> Result<String, AppCommandError> {
|
||||
let output = crate::process::tokio_command("git")
|
||||
@@ -1413,6 +1434,7 @@ pub async fn git_merge(
|
||||
has_conflicts: true,
|
||||
conflicted_files,
|
||||
operation: "merge".to_string(),
|
||||
upstream_commit: None,
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -1445,6 +1467,7 @@ pub async fn git_rebase(
|
||||
has_conflicts: true,
|
||||
conflicted_files,
|
||||
operation: "rebase".to_string(),
|
||||
upstream_commit: None,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder};
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewUrl, WebviewWindowBuilder};
|
||||
|
||||
use crate::app_error::AppCommandError;
|
||||
use crate::db::AppDatabase;
|
||||
@@ -424,6 +424,7 @@ pub async fn open_merge_window(
|
||||
state: tauri::State<'_, MergeWindowState>,
|
||||
folder_id: i32,
|
||||
operation: String,
|
||||
upstream_commit: Option<String>,
|
||||
) -> Result<(), AppCommandError> {
|
||||
let owner_label = window.label().to_string();
|
||||
let label = format!("merge-{folder_id}");
|
||||
@@ -450,9 +451,11 @@ pub async fn open_merge_window(
|
||||
.with_detail(format!("folder_id={folder_id}"))
|
||||
})?;
|
||||
|
||||
let url = WebviewUrl::App(
|
||||
format!("merge?folderId={folder_id}&operation={operation}").into(),
|
||||
);
|
||||
let mut url_str = format!("merge?folderId={folder_id}&operation={operation}");
|
||||
if let Some(ref commit) = upstream_commit {
|
||||
url_str.push_str(&format!("&upstreamCommit={commit}"));
|
||||
}
|
||||
let url = WebviewUrl::App(url_str.into());
|
||||
let builder = WebviewWindowBuilder::new(&app, &label, url)
|
||||
.title(format!("解决冲突 - {}", folder.name))
|
||||
.inner_size(1400.0, 900.0)
|
||||
@@ -493,6 +496,51 @@ pub fn restore_window_after_merge(
|
||||
}
|
||||
}
|
||||
|
||||
/// Clean up dangling merge state when a merge window is closed without
|
||||
/// completing or aborting. Checks if MERGE_HEAD exists, aborts the merge,
|
||||
/// and notifies the parent window.
|
||||
pub async fn cleanup_dangling_merge(app: &AppHandle, merge_window_label: &str) {
|
||||
let folder_id: i32 = match merge_window_label
|
||||
.strip_prefix("merge-")
|
||||
.and_then(|s| s.parse().ok())
|
||||
{
|
||||
Some(id) => id,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let db = match app.try_state::<AppDatabase>() {
|
||||
Some(db) => db,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let folder =
|
||||
match crate::db::service::folder_service::get_folder_by_id(&db.conn, folder_id).await {
|
||||
Ok(Some(f)) => f,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Check if MERGE_HEAD exists
|
||||
let check = crate::process::tokio_command("git")
|
||||
.args(["rev-parse", "--verify", "MERGE_HEAD"])
|
||||
.current_dir(&folder.path)
|
||||
.output()
|
||||
.await;
|
||||
let has_merge_head = check.map(|o| o.status.success()).unwrap_or(false);
|
||||
|
||||
if has_merge_head {
|
||||
let _ = crate::process::tokio_command("git")
|
||||
.args(["merge", "--abort"])
|
||||
.current_dir(&folder.path)
|
||||
.output()
|
||||
.await;
|
||||
|
||||
let _ = app.emit(
|
||||
"folder://merge-aborted",
|
||||
serde_json::json!({ "folder_id": folder_id }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_welcome_window(app: &AppHandle) -> Result<(), AppCommandError> {
|
||||
if let Some(existing) = app.get_webview_window("welcome") {
|
||||
ensure_windows_undecorated(&existing);
|
||||
|
||||
@@ -124,6 +124,13 @@ pub fn run() {
|
||||
if let Some(state) = app.try_state::<windows::MergeWindowState>() {
|
||||
windows::restore_window_after_merge(app, &state, &label);
|
||||
}
|
||||
// Clean up dangling merge state (MERGE_HEAD) if window closed
|
||||
// without completing or aborting the merge
|
||||
let app_clone = window.app_handle().clone();
|
||||
let label_clone = label.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
windows::cleanup_dangling_merge(&app_clone, &label_clone).await;
|
||||
});
|
||||
}
|
||||
|
||||
if let tauri::WindowEvent::CloseRequested { .. } = event {
|
||||
@@ -195,6 +202,7 @@ pub fn run() {
|
||||
folders::git_init,
|
||||
folders::git_pull,
|
||||
folders::git_start_pull_merge,
|
||||
folders::git_has_merge_head,
|
||||
folders::git_fetch,
|
||||
folders::git_push,
|
||||
folders::git_new_branch,
|
||||
|
||||
Reference in New Issue
Block a user