Skip to content

Commit 8f54de6

Browse files
committed
feat(src-tauri): 更新项目路线图和仓库管理功能
更新了项目路线图文件,并对src-tauri目录下的命令、领域模型和库文件进行了修改。同时,添加了新的组件RemoteManagementDialog.tsx,并更新了RepoView.tsx组件。此外,还更新了repoStore.ts和types/index.ts文件。
1 parent a2361bc commit 8f54de6

10 files changed

Lines changed: 428 additions & 31 deletions

File tree

ROADMAP.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@
4949
- [X] 支持 HTTPS/SSH
5050
- [X] 克隆进度显示
5151

52-
- [ ] **标签管理**
53-
- [ ] 创建标签(轻量/带注释)
54-
- [ ] 删除标签
55-
- [ ] 推送/删除远程标签
56-
- [ ] 标签列表视图
52+
- [X] **标签管理**
53+
- [X] 创建标签(轻量/带注释)
54+
- [X] 删除标签
55+
- [X] 推送/删除远程标签
56+
- [X] 标签列表视图
5757

5858
- [ ] **远程仓库管理**
5959
- [ ] 查看远程列表

src-tauri/src/commands/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pub mod commit;
33
pub mod stash;
44
pub mod clone;
55

6-
pub use repo::{scan_repositories, get_repo_status, get_branch_info, get_commit_history, get_local_branches, switch_branch, publish_branch, push_branch, get_git_username, delete_branch, rename_branch, create_branch, get_file_diff, merge_branch, fetch_remote, pull_branch, get_tags, create_tag, delete_tag, push_tag, delete_remote_tag};
6+
pub use repo::{scan_repositories, get_repo_status, get_branch_info, get_commit_history, get_local_branches, switch_branch, publish_branch, push_branch, get_git_username, delete_branch, rename_branch, create_branch, get_file_diff, merge_branch, fetch_remote, pull_branch, get_tags, create_tag, delete_tag, push_tag, delete_remote_tag, get_remotes, add_remote, remove_remote, rename_remote, set_remote_url};
77
pub use commit::{stage_files, unstage_files, stage_all, unstage_all, commit, revoke_latest_commit, batch_commit, generate_commit_message, review_code};
88
pub use stash::{get_stash_list, stash_save, stash_apply, stash_pop, stash_drop};
99
pub use clone::clone_repository;

src-tauri/src/commands/repo.rs

Lines changed: 127 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::domain::{RepositoryInfo, RepoStatus, StatusItem, BranchInfo, CommitInfo, LocalBranch, TagInfo};
1+
use crate::domain::{RepositoryInfo, RepoStatus, StatusItem, BranchInfo, CommitInfo, LocalBranch, TagInfo, RemoteInfo};
22
use crate::error::{AppError, Result};
33
use git2::{Repository, StatusOptions};
44
use ignore::WalkBuilder;
@@ -488,9 +488,11 @@ fn publish_branch_impl(
488488
// Get repository config for credential helper
489489
let config = repo.config()?;
490490

491-
// Clone the username/password for the callback
492-
let auth_username = username.clone();
493-
let auth_password = password.clone();
491+
// Clone credentials for both push and fetch callbacks
492+
let auth_username_push = username.clone();
493+
let auth_password_push = password.clone();
494+
let auth_username_fetch = username.clone();
495+
let auth_password_fetch = password.clone();
494496

495497
// Set up remote callbacks for authentication
496498
let mut callbacks = git2::RemoteCallbacks::new();
@@ -501,7 +503,7 @@ fn publish_branch_impl(
501503
let default_username = username_from_url.unwrap_or("git");
502504

503505
// If username and password are provided, use them
504-
if let (Some(user), Some(pass)) = (&auth_username, &auth_password) {
506+
if let (Some(user), Some(pass)) = (&auth_username_push, &auth_password_push) {
505507
return git2::Cred::userpass_plaintext(user, pass);
506508
}
507509

@@ -531,8 +533,35 @@ fn publish_branch_impl(
531533
let mut branch = repo.find_branch(branch_name, git2::BranchType::Local)?;
532534
branch.set_upstream(Some(&format!("{}/{}", remote, branch_name)))?;
533535

534-
// Manually update remote-tracking reference
535-
update_tracking_branch(repo, remote, branch_name)?;
536+
// Fetch to update remote-tracking branch instead of manually updating it
537+
// This ensures the remote-tracking branch reflects the actual remote state
538+
let config_for_fetch = repo.config()?;
539+
let mut fetch_callbacks = git2::RemoteCallbacks::new();
540+
fetch_callbacks.credentials(move |url, username_from_url, allowed_types| {
541+
let default_username = username_from_url.unwrap_or("git");
542+
if let (Some(user), Some(pass)) = (&auth_username_fetch, &auth_password_fetch) {
543+
return git2::Cred::userpass_plaintext(user, pass);
544+
}
545+
if allowed_types.contains(git2::CredentialType::SSH_KEY) {
546+
git2::Cred::ssh_key_from_agent(default_username)
547+
} else if allowed_types.contains(git2::CredentialType::USER_PASS_PLAINTEXT) {
548+
git2::Cred::credential_helper(&config_for_fetch, url, Some(default_username))
549+
} else if allowed_types.contains(git2::CredentialType::DEFAULT) {
550+
git2::Cred::credential_helper(&config_for_fetch, url, Some(default_username))
551+
} else {
552+
Err(git2::Error::from_str("no authentication method available"))
553+
}
554+
});
555+
556+
let mut fetch_options = git2::FetchOptions::new();
557+
fetch_options.remote_callbacks(fetch_callbacks);
558+
559+
// Fetch the specific branch to update the remote-tracking reference
560+
let fetch_refspec = format!("{}:{}", branch_name, branch_name);
561+
if let Err(e) = remote_obj.fetch(&[&fetch_refspec], Some(&mut fetch_options), None) {
562+
// If fetch fails (e.g., remote doesn't allow anonymous fetch), log but don't fail the push
563+
eprintln!("Warning: Failed to fetch after push: {}", e);
564+
}
536565

537566
Ok(())
538567
}
@@ -567,9 +596,11 @@ fn push_branch_impl(
567596
// Get repository config for credential helper
568597
let config = repo.config()?;
569598

570-
// Clone the username/password for the callback
571-
let auth_username = username.clone();
572-
let auth_password = password.clone();
599+
// Clone credentials for both push and fetch callbacks
600+
let auth_username_push = username.clone();
601+
let auth_password_push = password.clone();
602+
let auth_username_fetch = username.clone();
603+
let auth_password_fetch = password.clone();
573604

574605
// Set up remote callbacks for authentication
575606
let mut callbacks = git2::RemoteCallbacks::new();
@@ -580,7 +611,7 @@ fn push_branch_impl(
580611
let default_username = username_from_url.unwrap_or("git");
581612

582613
// If username and password are provided, use them
583-
if let (Some(user), Some(pass)) = (&auth_username, &auth_password) {
614+
if let (Some(user), Some(pass)) = (&auth_username_push, &auth_password_push) {
584615
return git2::Cred::userpass_plaintext(user, pass);
585616
}
586617

@@ -606,21 +637,36 @@ fn push_branch_impl(
606637
// Perform the push
607638
remote_obj.push(&[&refspec], Some(&mut push_options))?;
608639

609-
// Manually update remote-tracking reference
610-
update_tracking_branch(repo, remote, branch_name)?;
640+
// Fetch to update remote-tracking branch instead of manually updating it
641+
// This ensures the remote-tracking branch reflects the actual remote state
642+
let config_for_fetch = repo.config()?;
643+
let mut fetch_callbacks = git2::RemoteCallbacks::new();
644+
fetch_callbacks.credentials(move |url, username_from_url, allowed_types| {
645+
let default_username = username_from_url.unwrap_or("git");
646+
if let (Some(user), Some(pass)) = (&auth_username_fetch, &auth_password_fetch) {
647+
return git2::Cred::userpass_plaintext(user, pass);
648+
}
649+
if allowed_types.contains(git2::CredentialType::SSH_KEY) {
650+
git2::Cred::ssh_key_from_agent(default_username)
651+
} else if allowed_types.contains(git2::CredentialType::USER_PASS_PLAINTEXT) {
652+
git2::Cred::credential_helper(&config_for_fetch, url, Some(default_username))
653+
} else if allowed_types.contains(git2::CredentialType::DEFAULT) {
654+
git2::Cred::credential_helper(&config_for_fetch, url, Some(default_username))
655+
} else {
656+
Err(git2::Error::from_str("no authentication method available"))
657+
}
658+
});
611659

612-
Ok(())
613-
}
660+
let mut fetch_options = git2::FetchOptions::new();
661+
fetch_options.remote_callbacks(fetch_callbacks);
614662

615-
fn update_tracking_branch(repo: &Repository, remote: &str, branch_name: &str) -> Result<()> {
616-
// Get the OID of the local branch
617-
let local_branch = repo.find_branch(branch_name, git2::BranchType::Local)?;
618-
let oid = local_branch.get().peel_to_commit()?.id();
663+
// Fetch the specific branch to update the remote-tracking reference
664+
let fetch_refspec = format!("{}:{}", branch_name, branch_name);
665+
if let Err(e) = remote_obj.fetch(&[&fetch_refspec], Some(&mut fetch_options), None) {
666+
// If fetch fails (e.g., remote doesn't allow anonymous fetch), log but don't fail the push
667+
eprintln!("Warning: Failed to fetch after push: {}", e);
668+
}
619669

620-
// Update the remote-tracking branch reference
621-
let remote_ref_name = format!("refs/remotes/{}/{}", remote, branch_name);
622-
repo.reference(&remote_ref_name, oid, true, "Update remote-tracking branch after push")?;
623-
624670
Ok(())
625671
}
626672

@@ -1120,3 +1166,61 @@ fn delete_remote_tag_impl(
11201166

11211167
Ok(())
11221168
}
1169+
1170+
/// Get list of remotes for a repository
1171+
#[tauri::command]
1172+
pub async fn get_remotes(path: String) -> std::result::Result<Vec<RemoteInfo>, String> {
1173+
let repo = Repository::open(&path).map_err(|e| e.to_string())?;
1174+
get_remotes_impl(&repo).map_err(|e| e.to_string())
1175+
}
1176+
1177+
fn get_remotes_impl(repo: &Repository) -> Result<Vec<RemoteInfo>> {
1178+
let remotes = repo.remotes()?;
1179+
let mut remote_infos = Vec::new();
1180+
1181+
for remote_name in remotes.iter() {
1182+
if let Some(name) = remote_name {
1183+
if let Ok(remote) = repo.find_remote(name) {
1184+
remote_infos.push(RemoteInfo {
1185+
name: name.to_string(),
1186+
url: remote.url().map(|s| s.to_string()),
1187+
push_url: remote.pushurl().map(|s| s.to_string()),
1188+
});
1189+
}
1190+
}
1191+
}
1192+
1193+
Ok(remote_infos)
1194+
}
1195+
1196+
/// Add a new remote
1197+
#[tauri::command]
1198+
pub async fn add_remote(path: String, name: String, url: String) -> std::result::Result<(), String> {
1199+
let repo = Repository::open(&path).map_err(|e| e.to_string())?;
1200+
repo.remote(&name, &url).map_err(|e| e.to_string())?;
1201+
Ok(())
1202+
}
1203+
1204+
/// Remove a remote
1205+
#[tauri::command]
1206+
pub async fn remove_remote(path: String, name: String) -> std::result::Result<(), String> {
1207+
let repo = Repository::open(&path).map_err(|e| e.to_string())?;
1208+
repo.remote_delete(&name).map_err(|e| e.to_string())?;
1209+
Ok(())
1210+
}
1211+
1212+
/// Rename a remote
1213+
#[tauri::command]
1214+
pub async fn rename_remote(path: String, old_name: String, new_name: String) -> std::result::Result<(), String> {
1215+
let repo = Repository::open(&path).map_err(|e| e.to_string())?;
1216+
repo.remote_rename(&old_name, &new_name).map_err(|e| e.to_string())?;
1217+
Ok(())
1218+
}
1219+
1220+
/// Set remote URL
1221+
#[tauri::command]
1222+
pub async fn set_remote_url(path: String, name: String, url: String) -> std::result::Result<(), String> {
1223+
let repo = Repository::open(&path).map_err(|e| e.to_string())?;
1224+
repo.remote_set_url(&name, &url).map_err(|e| e.to_string())?;
1225+
Ok(())
1226+
}

src-tauri/src/domain/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pub mod repository;
22
pub mod status;
33

4-
pub use repository::{BranchInfo, BatchCommitResult, BatchFailure, CommitInfo, LocalBranch, RepositoryInfo, StashInfo, TagInfo};
4+
pub use repository::{BranchInfo, BatchCommitResult, BatchFailure, CommitInfo, LocalBranch, RepositoryInfo, StashInfo, TagInfo, RemoteInfo};
55
pub use status::{CommitSuggestion, CommitType, RepoStatus, StatusItem};

src-tauri/src/domain/repository.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,11 @@ pub struct TagInfo {
7676
pub tagger: Option<String>,
7777
pub date: Option<i64>,
7878
}
79+
80+
#[derive(Debug, Clone, Serialize, Deserialize)]
81+
#[serde(rename_all = "camelCase")]
82+
pub struct RemoteInfo {
83+
pub name: String,
84+
pub url: Option<String>,
85+
pub push_url: Option<String>,
86+
}

src-tauri/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ pub fn run() {
5252
delete_tag,
5353
push_tag,
5454
delete_remote_tag,
55+
// Remote commands
56+
get_remotes,
57+
add_remote,
58+
remove_remote,
59+
rename_remote,
60+
set_remote_url,
5561
])
5662
.run(tauri::generate_context!())
5763
.expect("error while running tauri application");

0 commit comments

Comments
 (0)