add Repo Managment / Kontext Menu in Braches

This commit is contained in:
Christoph Brandau
2026-07-02 08:35:29 +02:00
parent 97fc4fc1e0
commit eef4869bbb
7 changed files with 719 additions and 62 deletions
+116
View File
@@ -347,6 +347,42 @@ pub fn create_branch(
status_for_repo(&repo)
}
#[tauri::command]
pub fn rename_branch(
path: String,
old_branch: String,
new_branch: String,
) -> Result<GitStatus, String> {
let repo = resolve_repo(&path)?;
let old_branch = validate_existing_local_branch_name(&repo, &old_branch)?;
let new_branch = validate_new_branch_name(&repo, &new_branch)?;
run_git(
&repo,
[
"branch",
"-m",
"--",
old_branch.as_str(),
new_branch.as_str(),
],
)?;
status_for_repo(&repo)
}
#[tauri::command]
pub fn delete_branch(path: String, branch: String) -> Result<GitStatus, String> {
let repo = resolve_repo(&path)?;
let branch = validate_existing_local_branch_name(&repo, &branch)?;
let status = status_for_repo(&repo)?;
if status.current_branch.as_deref() == Some(branch.as_str()) {
return Err("Der aktuelle Branch kann nicht geloescht werden.".to_string());
}
run_git(&repo, ["branch", "-d", "--", branch.as_str()])?;
status_for_repo(&repo)
}
#[tauri::command]
pub fn stage_files(path: String, files: Vec<String>) -> Result<GitStatus, String> {
let repo = resolve_repo(&path)?;
@@ -2103,6 +2139,41 @@ fn validate_new_branch_name(repo: &Path, branch: &str) -> Result<String, String>
Ok(normalized)
}
fn validate_existing_local_branch_name(repo: &Path, branch: &str) -> Result<String, String> {
let branch = branch.trim();
if branch.is_empty() {
return Err("Branch-Name darf nicht leer sein.".to_string());
}
let normalized = validate_branch_ref_name(branch)?;
if !ref_exists(repo, &format!("refs/heads/{normalized}"))? {
return Err(format!(
"Lokaler Branch '{normalized}' wurde nicht gefunden."
));
}
Ok(normalized)
}
fn validate_branch_ref_name(branch: &str) -> Result<String, String> {
let output = git_command()
.args(["check-ref-format", "--branch", branch])
.output()
.map_err(|err| format!("Git konnte nicht gestartet werden. Ist Git installiert? {err}"))?;
if !output.status.success() {
let details = command_output_details(&output);
return Err(format!("Ungueltiger Branch-Name: {details}"));
}
let normalized = String::from_utf8_lossy(&output.stdout).trim().to_string();
Ok(if normalized.is_empty() {
branch.to_string()
} else {
normalized
})
}
fn ref_exists(repo: &Path, ref_name: &str) -> Result<bool, String> {
let output = git_command()
.arg("-C")
@@ -3516,6 +3587,51 @@ mod tests {
assert!(err.contains("existiert bereits"));
}
#[test]
fn rename_branch_renames_existing_local_branch() {
let repo = init_temp_repo("rename_branch");
commit_initial_file(&repo.path);
let current = git_output_test(&repo.path, ["rev-parse", "--abbrev-ref", "HEAD"]);
run_git_test(&repo.path, ["branch", "feature/old-panel"]);
let status = rename_branch(
repo.path.to_string_lossy().to_string(),
"feature/old-panel".to_string(),
"feature/new-panel".to_string(),
)
.unwrap();
assert_eq!(status.current_branch.as_deref(), Some(current.as_str()));
assert!(
!ref_exists(&repo.path, "refs/heads/feature/old-panel").unwrap(),
"old branch should be gone"
);
assert!(
ref_exists(&repo.path, "refs/heads/feature/new-panel").unwrap(),
"new branch should exist"
);
}
#[test]
fn delete_branch_removes_local_branch_but_rejects_current_branch() {
let repo = init_temp_repo("delete_branch");
commit_initial_file(&repo.path);
let current = git_output_test(&repo.path, ["rev-parse", "--abbrev-ref", "HEAD"]);
run_git_test(&repo.path, ["branch", "stale"]);
let status =
delete_branch(repo.path.to_string_lossy().to_string(), "stale".to_string()).unwrap();
assert_eq!(status.current_branch.as_deref(), Some(current.as_str()));
assert!(
!ref_exists(&repo.path, "refs/heads/stale").unwrap(),
"deleted branch should be gone"
);
let err = delete_branch(repo.path.to_string_lossy().to_string(), current).unwrap_err();
assert!(err.contains("aktuelle Branch"));
}
#[test]
fn apply_file_patch_stages_and_discards_selected_changes() {
let repo = init_temp_repo("apply_file_patch");