add upstream
This commit is contained in:
@@ -110,7 +110,9 @@ impl CommitAiEngine {
|
||||
};
|
||||
|
||||
if diff.trim().is_empty() {
|
||||
return Err("Keine gestagten Änderungen für eine Commit-Message vorhanden.".to_string());
|
||||
return Err(
|
||||
"Keine gestagten Änderungen für eine Commit-Message vorhanden.".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let (system, user) = build_messages(diff, notes);
|
||||
|
||||
+150
-28
@@ -5,8 +5,8 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Output, Stdio},
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc, Mutex,
|
||||
atomic::{AtomicU64, Ordering},
|
||||
},
|
||||
thread,
|
||||
time::Duration,
|
||||
@@ -606,12 +606,13 @@ pub fn push(
|
||||
password: Option<String>,
|
||||
) -> Result<GitStatus, String> {
|
||||
let repo = resolve_repo(&path)?;
|
||||
let push_args = push_args_for_repo(&repo)?;
|
||||
match (username.as_deref(), password.as_deref()) {
|
||||
(Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => {
|
||||
run_git_authenticated(&repo, ["push"], u, p)?;
|
||||
run_git_authenticated(&repo, push_args, u, p)?;
|
||||
}
|
||||
_ => {
|
||||
run_git(&repo, ["push"])?;
|
||||
run_git(&repo, push_args)?;
|
||||
}
|
||||
}
|
||||
status_for_repo(&repo)
|
||||
@@ -670,19 +671,11 @@ fn remote_url_for(repo: &Path, remote: &str) -> Option<String> {
|
||||
return None;
|
||||
}
|
||||
let url = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
||||
if url.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(url)
|
||||
}
|
||||
if url.is_empty() { None } else { Some(url) }
|
||||
}
|
||||
|
||||
fn upstream_remote_name(repo: &Path) -> Option<String> {
|
||||
let branch = run_git(repo, ["rev-parse", "--abbrev-ref", "HEAD"]).ok()?;
|
||||
let branch = String::from_utf8_lossy(&branch).trim().to_string();
|
||||
if branch.is_empty() || branch == "HEAD" {
|
||||
return None;
|
||||
}
|
||||
let branch = current_branch_name(repo).ok()?;
|
||||
let out = git_command()
|
||||
.arg("-C")
|
||||
.arg(repo)
|
||||
@@ -693,11 +686,7 @@ fn upstream_remote_name(repo: &Path) -> Option<String> {
|
||||
return None;
|
||||
}
|
||||
let name = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
||||
if name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(name)
|
||||
}
|
||||
if name.is_empty() { None } else { Some(name) }
|
||||
}
|
||||
|
||||
fn first_remote_name(repo: &Path) -> Option<String> {
|
||||
@@ -709,6 +698,50 @@ fn first_remote_name(repo: &Path) -> Option<String> {
|
||||
.map(str::to_string)
|
||||
}
|
||||
|
||||
fn current_branch_name(repo: &Path) -> Result<String, String> {
|
||||
let branch = run_git(repo, ["rev-parse", "--abbrev-ref", "HEAD"])?;
|
||||
let branch = String::from_utf8_lossy(&branch).trim().to_string();
|
||||
if branch.is_empty() || branch == "HEAD" {
|
||||
return Err("Aktueller Branch konnte nicht ermittelt werden.".to_string());
|
||||
}
|
||||
Ok(branch)
|
||||
}
|
||||
|
||||
fn branch_has_upstream(repo: &Path) -> bool {
|
||||
git_command()
|
||||
.arg("-C")
|
||||
.arg(repo)
|
||||
.args(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"])
|
||||
.output()
|
||||
.map(|output| output.status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn initial_push_remote_name(repo: &Path) -> Result<String, String> {
|
||||
if remote_url_for(repo, "origin").is_some() {
|
||||
return Ok("origin".to_string());
|
||||
}
|
||||
|
||||
first_remote_name(repo).ok_or_else(|| {
|
||||
"Dieser Branch hat keinen Upstream und es ist kein Remote konfiguriert.".to_string()
|
||||
})
|
||||
}
|
||||
|
||||
fn push_args_for_repo(repo: &Path) -> Result<Vec<OsString>, String> {
|
||||
if branch_has_upstream(repo) {
|
||||
return Ok(vec![OsString::from("push")]);
|
||||
}
|
||||
|
||||
let branch = current_branch_name(repo)?;
|
||||
let remote = initial_push_remote_name(repo)?;
|
||||
Ok(vec![
|
||||
OsString::from("push"),
|
||||
OsString::from("--set-upstream"),
|
||||
OsString::from(remote),
|
||||
OsString::from(branch),
|
||||
])
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn cred_load(key: String) -> Result<Option<StoredCredential>, String> {
|
||||
let entry = cred_entry(&key)?;
|
||||
@@ -1604,7 +1637,6 @@ fn status_for_repo(repo: &Path) -> Result<GitStatus, String> {
|
||||
let (branch, mut files) = parse_status_output(&output)?;
|
||||
detect_worktree_renames(repo, &mut files);
|
||||
|
||||
|
||||
Ok(GitStatus {
|
||||
repo_path: repo.to_string_lossy().to_string(),
|
||||
current_branch: branch.current_branch,
|
||||
@@ -3170,6 +3202,22 @@ mod tests {
|
||||
}
|
||||
|
||||
fn init_temp_repo(name: &str) -> TempRepo {
|
||||
let repo = temp_dir(name);
|
||||
|
||||
run_git_test(&repo.path, ["init", "-q"]);
|
||||
run_git_test(&repo.path, ["config", "user.email", "test@example.com"]);
|
||||
run_git_test(&repo.path, ["config", "user.name", "Tester"]);
|
||||
|
||||
repo
|
||||
}
|
||||
|
||||
fn init_bare_temp_repo(name: &str) -> TempRepo {
|
||||
let repo = temp_dir(name);
|
||||
run_git_test(&repo.path, ["init", "--bare", "-q"]);
|
||||
repo
|
||||
}
|
||||
|
||||
fn temp_dir(name: &str) -> TempRepo {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("system time should be after Unix epoch")
|
||||
@@ -3181,9 +3229,6 @@ mod tests {
|
||||
));
|
||||
|
||||
fs::create_dir_all(&path).expect("temp repo directory should be created");
|
||||
run_git_test(&path, ["init", "-q"]);
|
||||
run_git_test(&path, ["config", "user.email", "test@example.com"]);
|
||||
run_git_test(&path, ["config", "user.name", "Tester"]);
|
||||
|
||||
TempRepo { path }
|
||||
}
|
||||
@@ -3504,14 +3549,18 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(comparison
|
||||
assert!(
|
||||
comparison
|
||||
.files
|
||||
.iter()
|
||||
.any(|file| file.path == "old.txt" && file.status == FileStatusKind::Modified));
|
||||
assert!(comparison
|
||||
.any(|file| file.path == "old.txt" && file.status == FileStatusKind::Modified)
|
||||
);
|
||||
assert!(
|
||||
comparison
|
||||
.files
|
||||
.iter()
|
||||
.any(|file| file.path == "added.txt" && file.status == FileStatusKind::Added));
|
||||
.any(|file| file.path == "added.txt" && file.status == FileStatusKind::Added)
|
||||
);
|
||||
assert!(comparison.patch.contains("second line"));
|
||||
}
|
||||
|
||||
@@ -3568,10 +3617,12 @@ mod tests {
|
||||
|
||||
assert_eq!(comparison.to_short, "working tree");
|
||||
assert!(comparison.to_hash.is_empty());
|
||||
assert!(comparison
|
||||
assert!(
|
||||
comparison
|
||||
.files
|
||||
.iter()
|
||||
.any(|file| file.path == "old.txt" && file.status == FileStatusKind::Modified));
|
||||
.any(|file| file.path == "old.txt" && file.status == FileStatusKind::Modified)
|
||||
);
|
||||
assert!(comparison.patch.contains("working tree change"));
|
||||
}
|
||||
|
||||
@@ -3705,6 +3756,77 @@ mod tests {
|
||||
assert_eq!(parent_count, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_args_use_set_upstream_for_branch_without_tracking_remote() {
|
||||
let repo = init_temp_repo("push_sets_upstream");
|
||||
let remote = init_bare_temp_repo("push_sets_upstream_remote");
|
||||
commit_initial_file(&repo.path);
|
||||
run_git_test(
|
||||
&repo.path,
|
||||
["checkout", "-q", "-b", "features/ai_commits_local"],
|
||||
);
|
||||
fs::write(repo.path.join("feature.txt"), "feature\n")
|
||||
.expect("feature file should be written");
|
||||
run_git_test(&repo.path, ["add", "feature.txt"]);
|
||||
run_git_test(&repo.path, ["commit", "-q", "-m", "feature work"]);
|
||||
run_git_test(
|
||||
&repo.path,
|
||||
["remote", "add", "origin", remote.path.to_str().unwrap()],
|
||||
);
|
||||
|
||||
let args = push_args_for_repo(&repo.path).unwrap();
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|arg| arg.to_string_lossy().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
args,
|
||||
vec![
|
||||
"push",
|
||||
"--set-upstream",
|
||||
"origin",
|
||||
"features/ai_commits_local"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
windows,
|
||||
ignore = "Git for Windows can fail local push tests with a sh signal pipe error"
|
||||
)]
|
||||
fn push_sets_upstream_for_branch_without_tracking_remote() {
|
||||
let repo = init_temp_repo("push_sets_upstream_integration");
|
||||
let remote = init_bare_temp_repo("push_sets_upstream_integration_remote");
|
||||
commit_initial_file(&repo.path);
|
||||
run_git_test(
|
||||
&repo.path,
|
||||
["checkout", "-q", "-b", "features/ai_commits_local"],
|
||||
);
|
||||
fs::write(repo.path.join("feature.txt"), "feature\n")
|
||||
.expect("feature file should be written");
|
||||
run_git_test(&repo.path, ["add", "feature.txt"]);
|
||||
run_git_test(&repo.path, ["commit", "-q", "-m", "feature work"]);
|
||||
run_git_test(
|
||||
&repo.path,
|
||||
["remote", "add", "origin", remote.path.to_str().unwrap()],
|
||||
);
|
||||
|
||||
let status = push(repo.path.to_string_lossy().to_string(), None, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
status.upstream.as_deref(),
|
||||
Some("origin/features/ai_commits_local")
|
||||
);
|
||||
let local_head = git_output_test(&repo.path, ["rev-parse", "HEAD"]);
|
||||
let remote_head = git_output_test(
|
||||
&remote.path,
|
||||
["rev-parse", "refs/heads/features/ai_commits_local"],
|
||||
);
|
||||
assert_eq!(remote_head, local_head);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_and_resolve_conflict_round_trip() {
|
||||
let repo = init_temp_repo("resolve_conflict");
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
mod git;
|
||||
|
||||
use git::{
|
||||
apply_file_patch, cancel_code_search, cancel_file_history, checkout_branch, commit,
|
||||
commit_ai_generate, commit_ai_status, compare_commits, compare_file_to_head,
|
||||
compare_file_to_parent, create_branch, cred_delete, cred_load, cred_save, delete_branch,
|
||||
diff_file_against_working_tree, get_file_patch, get_remote_url, get_status, list_branches,
|
||||
list_commits, list_file_history, list_repository_files, merge_branch, open_repo_in_explorer,
|
||||
open_repository, open_repository_bundle, open_repository_file, pull, push, read_conflict,
|
||||
rename_branch, resolve_conflict, resolve_conflict_side, restore_file_from_commit,
|
||||
restore_files, restore_to_commit, search_code_introductions, stage_files, unstage_files,
|
||||
SearchCancellationState,
|
||||
SearchCancellationState, apply_file_patch, cancel_code_search, cancel_file_history,
|
||||
checkout_branch, commit, commit_ai_generate, commit_ai_status, compare_commits,
|
||||
compare_file_to_head, compare_file_to_parent, create_branch, cred_delete, cred_load, cred_save,
|
||||
delete_branch, diff_file_against_working_tree, get_file_patch, get_remote_url, get_status,
|
||||
list_branches, list_commits, list_file_history, list_repository_files, merge_branch,
|
||||
open_repo_in_explorer, open_repository, open_repository_bundle, open_repository_file, pull,
|
||||
push, read_conflict, rename_branch, resolve_conflict, resolve_conflict_side,
|
||||
restore_file_from_commit, restore_files, restore_to_commit, search_code_introductions,
|
||||
stage_files, unstage_files,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
|
||||
Reference in New Issue
Block a user