feat(git): refactor Git commands to use async/await pattern
This update modifies several Git command functions to utilize the async/await pattern, enhancing performance and responsiveness during operations. The changes allow for non-blocking execution of commands, improving the overall user experience. - Converted multiple Git command functions to async - Added error handling for async operations - Updated related tests to support async execution
This commit is contained in:
@@ -96,7 +96,10 @@
|
|||||||
"Bash(identify src-tauri/icons/GitCat.ico)",
|
"Bash(identify src-tauri/icons/GitCat.ico)",
|
||||||
"Bash(python3 -c \"import PIL; print\\(PIL.__version__\\)\")",
|
"Bash(python3 -c \"import PIL; print\\(PIL.__version__\\)\")",
|
||||||
"Bash(python3 *)",
|
"Bash(python3 *)",
|
||||||
"Bash(xxd -l 16 src-tauri/icons/GitCat.ico)"
|
"Bash(xxd -l 16 src-tauri/icons/GitCat.ico)",
|
||||||
|
"Bash(pkg-config --exists openssl)",
|
||||||
|
"Bash(sudo apt-get install -y libssl-dev pkg-config)",
|
||||||
|
"Bash(dpkg -L libssl3t64)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+225
-180
@@ -789,63 +789,75 @@ fn validate_tag_ref_name(name: &str) -> Result<String, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn stage_files(path: String, files: Vec<String>) -> Result<GitStatus, String> {
|
pub async fn stage_files(path: String, files: Vec<String>) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
validate_files(&files)?;
|
let repo = resolve_repo(&path)?;
|
||||||
|
validate_files(&files)?;
|
||||||
|
|
||||||
if !files.is_empty() {
|
if !files.is_empty() {
|
||||||
// A file we've detected as an unstaged rename (see `detect_worktree_renames`) has
|
// A file we've detected as an unstaged rename (see `detect_worktree_renames`) has
|
||||||
// to be staged with both its old and new path so `git add` records it as a rename
|
// to be staged with both its old and new path so `git add` records it as a rename
|
||||||
// instead of leaving the old path's deletion unstaged.
|
// instead of leaving the old path's deletion unstaged.
|
||||||
let current_status = status_for_repo(&repo)?;
|
let current_status = status_for_repo(&repo)?;
|
||||||
let mut add_paths: Vec<String> = Vec::new();
|
let mut add_paths: Vec<String> = Vec::new();
|
||||||
for file in &files {
|
for file in &files {
|
||||||
match find_status(¤t_status.files, file) {
|
match find_status(¤t_status.files, file) {
|
||||||
Some(entry) => {
|
Some(entry) => {
|
||||||
if let Some(old_path) = &entry.old_path {
|
if let Some(old_path) = &entry.old_path {
|
||||||
add_paths.push(old_path.clone());
|
add_paths.push(old_path.clone());
|
||||||
|
}
|
||||||
|
add_paths.push(entry.path.clone());
|
||||||
}
|
}
|
||||||
add_paths.push(entry.path.clone());
|
None => add_paths.push(file.clone()),
|
||||||
}
|
}
|
||||||
None => add_paths.push(file.clone()),
|
|
||||||
}
|
}
|
||||||
|
run_git_with_paths(&repo, &["add"], &add_paths)?;
|
||||||
}
|
}
|
||||||
run_git_with_paths(&repo, &["add"], &add_paths)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_for_repo(&repo)
|
status_for_repo(&repo)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("Could not stage files: {err}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn unstage_files(path: String, files: Vec<String>) -> Result<GitStatus, String> {
|
pub async fn unstage_files(path: String, files: Vec<String>) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
validate_files(&files)?;
|
let repo = resolve_repo(&path)?;
|
||||||
|
validate_files(&files)?;
|
||||||
|
|
||||||
|
if !files.is_empty() {
|
||||||
|
let current_status = status_for_repo(&repo)?;
|
||||||
|
unstage_selected_files(&repo, ¤t_status.files, &files)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_for_repo(&repo)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("Could not unstage files: {err}"))?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn restore_files(path: String, files: Vec<String>, staged: bool) -> Result<GitStatus, String> {
|
||||||
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
|
let repo = resolve_repo(&path)?;
|
||||||
|
validate_files(&files)?;
|
||||||
|
|
||||||
|
if files.is_empty() {
|
||||||
|
return status_for_repo(&repo);
|
||||||
|
}
|
||||||
|
|
||||||
if !files.is_empty() {
|
|
||||||
let current_status = status_for_repo(&repo)?;
|
let current_status = status_for_repo(&repo)?;
|
||||||
unstage_selected_files(&repo, ¤t_status.files, &files)?;
|
if staged {
|
||||||
}
|
restore_staged_files(&repo, ¤t_status.files, &files)?;
|
||||||
|
} else {
|
||||||
|
restore_worktree_files(&repo, ¤t_status.files, &files)?;
|
||||||
|
}
|
||||||
|
|
||||||
status_for_repo(&repo)
|
status_for_repo(&repo)
|
||||||
}
|
})
|
||||||
|
.await
|
||||||
#[tauri::command]
|
.map_err(|err| format!("Could not restore files: {err}"))?
|
||||||
pub fn restore_files(path: String, files: Vec<String>, staged: bool) -> Result<GitStatus, String> {
|
|
||||||
let repo = resolve_repo(&path)?;
|
|
||||||
validate_files(&files)?;
|
|
||||||
|
|
||||||
if files.is_empty() {
|
|
||||||
return status_for_repo(&repo);
|
|
||||||
}
|
|
||||||
|
|
||||||
let current_status = status_for_repo(&repo)?;
|
|
||||||
if staged {
|
|
||||||
restore_staged_files(&repo, ¤t_status.files, &files)?;
|
|
||||||
} else {
|
|
||||||
restore_worktree_files(&repo, ¤t_status.files, &files)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_for_repo(&repo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -1055,47 +1067,55 @@ pub fn apply_file_patch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn commit(path: String, message: String) -> Result<GitStatus, String> {
|
pub async fn commit(path: String, message: String) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
if message.trim().is_empty() {
|
let repo = resolve_repo(&path)?;
|
||||||
return Err("Commit message must not be empty.".to_string());
|
if message.trim().is_empty() {
|
||||||
}
|
return Err("Commit message must not be empty.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
let current_status = status_for_repo(&repo)?;
|
let current_status = status_for_repo(&repo)?;
|
||||||
if has_unresolved_conflicts(¤t_status) {
|
if has_unresolved_conflicts(¤t_status) {
|
||||||
return Err("Merge conflicts must be resolved before you can commit.".to_string());
|
return Err("Merge conflicts must be resolved before you can commit.".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
run_git(&repo, ["commit", "-m", message.as_str()])?;
|
run_git(&repo, ["commit", "-m", message.as_str()])?;
|
||||||
status_for_repo(&repo)
|
status_for_repo(&repo)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("Could not commit: {err}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn amend_commit(path: String, message: Option<String>) -> Result<GitStatus, String> {
|
pub async fn amend_commit(path: String, message: Option<String>) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
if verify_commit(&repo, "HEAD").is_err() {
|
let repo = resolve_repo(&path)?;
|
||||||
return Err("There is no commit to amend.".to_string());
|
if verify_commit(&repo, "HEAD").is_err() {
|
||||||
}
|
return Err("There is no commit to amend.".to_string());
|
||||||
|
|
||||||
let current_status = status_for_repo(&repo)?;
|
|
||||||
if has_unresolved_conflicts(¤t_status) {
|
|
||||||
return Err("Merge conflicts must be resolved before you can commit.".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let message = message
|
|
||||||
.map(|message| message.trim().to_string())
|
|
||||||
.filter(|message| !message.is_empty());
|
|
||||||
|
|
||||||
match message {
|
|
||||||
Some(message) => {
|
|
||||||
run_git(&repo, ["commit", "--amend", "-m", message.as_str()])?;
|
|
||||||
}
|
}
|
||||||
None => {
|
|
||||||
run_git(&repo, ["commit", "--amend", "--no-edit"])?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status_for_repo(&repo)
|
let current_status = status_for_repo(&repo)?;
|
||||||
|
if has_unresolved_conflicts(¤t_status) {
|
||||||
|
return Err("Merge conflicts must be resolved before you can commit.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = message
|
||||||
|
.map(|message| message.trim().to_string())
|
||||||
|
.filter(|message| !message.is_empty());
|
||||||
|
|
||||||
|
match message {
|
||||||
|
Some(message) => {
|
||||||
|
run_git(&repo, ["commit", "--amend", "-m", message.as_str()])?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
run_git(&repo, ["commit", "--amend", "--no-edit"])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_for_repo(&repo)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("Could not amend commit: {err}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -1132,39 +1152,43 @@ pub fn undo_last_commit(path: String) -> Result<GitStatus, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn pull(
|
pub async fn pull(
|
||||||
path: String,
|
path: String,
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
) -> Result<GitStatus, String> {
|
) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
let pull_args = ["pull", "--no-rebase", "--ff", "--no-edit"];
|
let repo = resolve_repo(&path)?;
|
||||||
let output = match (username.as_deref(), password.as_deref()) {
|
let pull_args = ["pull", "--no-rebase", "--ff", "--no-edit"];
|
||||||
(Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => {
|
let output = match (username.as_deref(), password.as_deref()) {
|
||||||
run_git_authenticated_output(&repo, pull_args, u, p)?
|
(Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => {
|
||||||
|
run_git_authenticated_output(&repo, pull_args, u, p)?
|
||||||
|
}
|
||||||
|
_ => git_command()
|
||||||
|
.arg("-C")
|
||||||
|
.arg(&repo)
|
||||||
|
.args(pull_args)
|
||||||
|
.output()
|
||||||
|
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
return status_for_repo(&repo);
|
||||||
}
|
}
|
||||||
_ => git_command()
|
|
||||||
.arg("-C")
|
|
||||||
.arg(&repo)
|
|
||||||
.args(pull_args)
|
|
||||||
.output()
|
|
||||||
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
if output.status.success() {
|
let status = status_for_repo(&repo)?;
|
||||||
return status_for_repo(&repo);
|
if has_unresolved_conflicts(&status) {
|
||||||
}
|
return Ok(status);
|
||||||
|
}
|
||||||
|
|
||||||
let status = status_for_repo(&repo)?;
|
let details = command_output_details(&output);
|
||||||
if has_unresolved_conflicts(&status) {
|
if is_auth_error(&details) {
|
||||||
return Ok(status);
|
return Err(format!("AUTH_FAILED:{details}"));
|
||||||
}
|
}
|
||||||
|
Err(format!("Git command failed: {details}"))
|
||||||
let details = command_output_details(&output);
|
})
|
||||||
if is_auth_error(&details) {
|
.await
|
||||||
return Err(format!("AUTH_FAILED:{details}"));
|
.map_err(|err| format!("Could not pull: {err}"))?
|
||||||
}
|
|
||||||
Err(format!("Git command failed: {details}"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -1203,22 +1227,26 @@ pub async fn fetch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn push(
|
pub async fn push(
|
||||||
path: String,
|
path: String,
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
) -> Result<GitStatus, String> {
|
) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
let push_args = push_args_for_repo(&repo)?;
|
let repo = resolve_repo(&path)?;
|
||||||
match (username.as_deref(), password.as_deref()) {
|
let push_args = push_args_for_repo(&repo)?;
|
||||||
(Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => {
|
match (username.as_deref(), password.as_deref()) {
|
||||||
run_git_authenticated(&repo, push_args, u, p)?;
|
(Some(u), Some(p)) if !u.is_empty() || !p.is_empty() => {
|
||||||
|
run_git_authenticated(&repo, push_args, u, p)?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
run_git(&repo, push_args)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
status_for_repo(&repo)
|
||||||
run_git(&repo, push_args)?;
|
})
|
||||||
}
|
.await
|
||||||
}
|
.map_err(|err| format!("Could not push: {err}"))?
|
||||||
status_for_repo(&repo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Credential storage (OS keychain) ────────────────────────────────────────
|
// ── Credential storage (OS keychain) ────────────────────────────────────────
|
||||||
@@ -1388,61 +1416,69 @@ pub fn cred_delete(key: String) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn merge_branch(path: String, branch: String) -> Result<GitStatus, String> {
|
pub async fn merge_branch(path: String, branch: String) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
let branch = branch.trim();
|
let repo = resolve_repo(&path)?;
|
||||||
if branch.is_empty() {
|
let branch = branch.trim();
|
||||||
return Err("Branch name must not be empty.".to_string());
|
if branch.is_empty() {
|
||||||
}
|
return Err("Branch name must not be empty.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
let output = git_command()
|
let output = git_command()
|
||||||
.arg("-C")
|
.arg("-C")
|
||||||
.arg(&repo)
|
.arg(&repo)
|
||||||
.args(["merge", "--no-edit", branch])
|
.args(["merge", "--no-edit", branch])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?;
|
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?;
|
||||||
|
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
return status_for_repo(&repo);
|
return status_for_repo(&repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A merge that stops on conflicts leaves unmerged paths in the work tree.
|
// A merge that stops on conflicts leaves unmerged paths in the work tree.
|
||||||
// Surface those through the status so the UI can offer conflict resolution
|
// Surface those through the status so the UI can offer conflict resolution
|
||||||
// instead of treating the conflict as a hard error.
|
// instead of treating the conflict as a hard error.
|
||||||
let status = status_for_repo(&repo)?;
|
let status = status_for_repo(&repo)?;
|
||||||
if has_unresolved_conflicts(&status) {
|
if has_unresolved_conflicts(&status) {
|
||||||
return Ok(status);
|
return Ok(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
let details = if !stderr.trim().is_empty() {
|
let details = if !stderr.trim().is_empty() {
|
||||||
stderr.trim()
|
stderr.trim()
|
||||||
} else if !stdout.trim().is_empty() {
|
} else if !stdout.trim().is_empty() {
|
||||||
stdout.trim()
|
stdout.trim()
|
||||||
} else {
|
} else {
|
||||||
"unknown error"
|
"unknown error"
|
||||||
};
|
};
|
||||||
|
|
||||||
Err(format!("Merge failed: {details}"))
|
Err(format!("Merge failed: {details}"))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("Could not merge: {err}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn rebase_branch(path: String, branch: String) -> Result<GitStatus, String> {
|
pub async fn rebase_branch(path: String, branch: String) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
let branch = branch.trim();
|
let repo = resolve_repo(&path)?;
|
||||||
if branch.is_empty() {
|
let branch = branch.trim();
|
||||||
return Err("Branch name must not be empty.".to_string());
|
if branch.is_empty() {
|
||||||
}
|
return Err("Branch name must not be empty.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
let output = git_command()
|
let output = git_command()
|
||||||
.arg("-C")
|
.arg("-C")
|
||||||
.arg(&repo)
|
.arg(&repo)
|
||||||
.args(["rebase", branch])
|
.args(["rebase", branch])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?;
|
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?;
|
||||||
|
|
||||||
rebase_status_or_error(&repo, output, "Rebase failed", true)
|
rebase_status_or_error(&repo, output, "Rebase failed", true)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("Could not rebase: {err}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -1494,19 +1530,23 @@ fn rebase_status_or_error(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn cherry_pick_commit(path: String, commit: String) -> Result<GitStatus, String> {
|
pub async fn cherry_pick_commit(path: String, commit: String) -> Result<GitStatus, String> {
|
||||||
let repo = resolve_repo(&path)?;
|
tauri::async_runtime::spawn_blocking(move || -> Result<GitStatus, String> {
|
||||||
let commit_hash = verify_commit(&repo, &commit)?;
|
let repo = resolve_repo(&path)?;
|
||||||
|
let commit_hash = verify_commit(&repo, &commit)?;
|
||||||
|
|
||||||
let output = git_command()
|
let output = git_command()
|
||||||
.arg("-C")
|
.arg("-C")
|
||||||
.arg(&repo)
|
.arg(&repo)
|
||||||
.args(["cherry-pick", commit_hash.as_str()])
|
.args(["cherry-pick", commit_hash.as_str()])
|
||||||
.env("GIT_EDITOR", "true")
|
.env("GIT_EDITOR", "true")
|
||||||
.output()
|
.output()
|
||||||
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?;
|
.map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?;
|
||||||
|
|
||||||
cherry_pick_status_or_error(&repo, output, "Cherry-pick failed")
|
cherry_pick_status_or_error(&repo, output, "Cherry-pick failed")
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("Could not cherry-pick: {err}"))?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -4841,12 +4881,12 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
windows,
|
windows,
|
||||||
ignore = "Git for Windows can fail local pull tests with a sh signal pipe error"
|
ignore = "Git for Windows can fail local pull tests with a sh signal pipe error"
|
||||||
)]
|
)]
|
||||||
fn pull_merges_diverging_branch_instead_of_requiring_fast_forward() {
|
async fn pull_merges_diverging_branch_instead_of_requiring_fast_forward() {
|
||||||
let repo = init_temp_repo("pull_diverged");
|
let repo = init_temp_repo("pull_diverged");
|
||||||
commit_initial_file(&repo.path);
|
commit_initial_file(&repo.path);
|
||||||
let base_branch = git_output_test(&repo.path, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
let base_branch = git_output_test(&repo.path, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
||||||
@@ -4876,7 +4916,9 @@ mod tests {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
let status = pull(repo.path.to_string_lossy().to_string(), None, None).unwrap();
|
let status = pull(repo.path.to_string_lossy().to_string(), None, None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert!(status.clean, "{:?}", status.files);
|
assert!(status.clean, "{:?}", status.files);
|
||||||
assert!(repo.path.join("remote.txt").exists());
|
assert!(repo.path.join("remote.txt").exists());
|
||||||
@@ -4925,12 +4967,12 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
windows,
|
windows,
|
||||||
ignore = "Git for Windows can fail local push tests with a sh signal pipe error"
|
ignore = "Git for Windows can fail local push tests with a sh signal pipe error"
|
||||||
)]
|
)]
|
||||||
fn push_sets_upstream_for_branch_without_tracking_remote() {
|
async fn push_sets_upstream_for_branch_without_tracking_remote() {
|
||||||
let repo = init_temp_repo("push_sets_upstream_integration");
|
let repo = init_temp_repo("push_sets_upstream_integration");
|
||||||
let remote = init_bare_temp_repo("push_sets_upstream_integration_remote");
|
let remote = init_bare_temp_repo("push_sets_upstream_integration_remote");
|
||||||
commit_initial_file(&repo.path);
|
commit_initial_file(&repo.path);
|
||||||
@@ -4947,7 +4989,9 @@ mod tests {
|
|||||||
["remote", "add", "origin", remote.path.to_str().unwrap()],
|
["remote", "add", "origin", remote.path.to_str().unwrap()],
|
||||||
);
|
);
|
||||||
|
|
||||||
let status = push(repo.path.to_string_lossy().to_string(), None, None).unwrap();
|
let status = push(repo.path.to_string_lossy().to_string(), None, None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
status.upstream.as_deref(),
|
status.upstream.as_deref(),
|
||||||
@@ -5015,8 +5059,8 @@ mod tests {
|
|||||||
assert_eq!(contents.replace("\r\n", "\n"), "resolved\n");
|
assert_eq!(contents.replace("\r\n", "\n"), "resolved\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn commit_rejects_unresolved_merge_conflicts() {
|
async fn commit_rejects_unresolved_merge_conflicts() {
|
||||||
let repo = init_temp_repo("commit_rejects_conflicts");
|
let repo = init_temp_repo("commit_rejects_conflicts");
|
||||||
fs::write(repo.path.join("file.txt"), "base\n").expect("base file should be written");
|
fs::write(repo.path.join("file.txt"), "base\n").expect("base file should be written");
|
||||||
run_git_test(&repo.path, ["add", "file.txt"]);
|
run_git_test(&repo.path, ["add", "file.txt"]);
|
||||||
@@ -5042,6 +5086,7 @@ mod tests {
|
|||||||
repo.path.to_string_lossy().to_string(),
|
repo.path.to_string_lossy().to_string(),
|
||||||
"should not commit".to_string(),
|
"should not commit".to_string(),
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
||||||
assert!(err.contains("Merge conflicts"));
|
assert!(err.contains("Merge conflicts"));
|
||||||
|
|||||||
Reference in New Issue
Block a user