Handle remote branches

This commit is contained in:
Gregory Schier
2025-02-07 13:21:30 -08:00
parent 2da898d2d4
commit a42bee098b
20 changed files with 272 additions and 32 deletions

View File

@@ -22,9 +22,13 @@ pub(crate) fn branch_set_upstream_after_push(repo: &Repository, branch_name: &st
Ok(())
}
pub(crate) fn git_checkout_branch(dir: &Path, branch: &str, force: bool) -> Result<()> {
pub(crate) fn git_checkout_branch(dir: &Path, branch_name: &str, force: bool) -> Result<String> {
if branch_name.starts_with("origin/") {
return git_checkout_remote_branch(dir, branch_name, force);
}
let repo = open_repo(dir)?;
let branch = get_branch_by_name(&repo, branch)?;
let branch = get_branch_by_name(&repo, branch_name)?;
let branch_ref = branch.into_reference();
let branch_tree = branch_ref.peel_to_tree()?;
@@ -36,7 +40,22 @@ pub(crate) fn git_checkout_branch(dir: &Path, branch: &str, force: bool) -> Resu
repo.checkout_tree(branch_tree.as_object(), Some(&mut options))?;
repo.set_head(branch_ref.name().unwrap())?;
Ok(())
Ok(branch_name.to_string())
}
pub(crate) fn git_checkout_remote_branch(dir: &Path, branch_name: &str, force: bool) -> Result<String> {
let branch_name = branch_name.trim_start_matches("origin/");
let repo = open_repo(dir)?;
let refname = format!("refs/remotes/origin/{}", branch_name);
let remote_ref = repo.find_reference(&refname)?;
let commit = remote_ref.peel_to_commit()?;
let mut new_branch = repo.branch(branch_name, &commit, false)?;
let upstream_name = format!("origin/{}", branch_name);
new_branch.set_upstream(Some(&upstream_name))?;
return git_checkout_branch(dir, branch_name, force)
}
pub(crate) fn git_create_branch(dir: &Path, name: &str) -> Result<()> {

View File

@@ -1,5 +1,6 @@
use crate::branch::{git_checkout_branch, git_create_branch, git_delete_branch, git_merge_branch};
use crate::error::Result;
use crate::fetch::git_fetch_all;
use crate::git::{
git_add, git_commit, git_init, git_log, git_status, git_unstage, GitCommit, GitStatusSummary,
};
@@ -10,7 +11,7 @@ use tauri::command;
// NOTE: All of these commands are async to prevent blocking work from locking up the UI
#[command]
pub async fn checkout(dir: &Path, branch: &str, force: bool) -> Result<()> {
pub async fn checkout(dir: &Path, branch: &str, force: bool) -> Result<String> {
git_checkout_branch(dir, branch, force)
}
@@ -49,6 +50,11 @@ pub async fn commit(dir: &Path, message: &str) -> Result<()> {
git_commit(dir, message)
}
#[command]
pub async fn fetch_all(dir: &Path) -> Result<()> {
git_fetch_all(dir)
}
#[command]
pub async fn push(dir: &Path) -> Result<PushResult> {
git_push(dir)

View File

@@ -0,0 +1,37 @@
use crate::callbacks::default_callbacks;
use crate::error::Result;
use crate::repository::open_repo;
use git2::{FetchOptions, ProxyOptions, Repository};
use std::path::Path;
pub(crate) fn git_fetch_all(dir: &Path) -> Result<()> {
let repo = open_repo(dir)?;
let remotes = repo.remotes()?.iter().flatten().map(String::from).collect::<Vec<_>>();
for (_idx, remote) in remotes.into_iter().enumerate() {
fetch_from_remote(&repo, &remote)?;
}
Ok(())
}
fn fetch_from_remote(repo: &Repository, remote: &str) -> Result<()> {
let mut remote = repo.find_remote(remote)?;
let mut options = FetchOptions::new();
let callbacks = default_callbacks();
options.prune(git2::FetchPrune::On);
let mut proxy = ProxyOptions::new();
proxy.auto();
options.proxy_options(proxy);
options.download_tags(git2::AutotagOption::All);
options.remote_callbacks(callbacks);
remote.fetch(&[] as &[&str], Some(&mut options), None)?;
// fetch tags (also removing remotely deleted ones)
remote.fetch(&["refs/tags/*:refs/tags/*"], Some(&mut options), None)?;
Ok(())
}

View File

@@ -1,6 +1,6 @@
use crate::error::Result;
use crate::repository::open_repo;
use crate::util::list_branch_names;
use crate::util::{local_branch_names, remote_branch_names};
use chrono::{DateTime, Utc};
use git2::IndexAddOption;
use log::{info, warn};
@@ -19,7 +19,8 @@ pub struct GitStatusSummary {
pub head_ref_shorthand: Option<String>,
pub entries: Vec<GitStatusEntry>,
pub origins: Vec<String>,
pub branches: Vec<String>,
pub local_branches: Vec<String>,
pub remote_branches: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TS)]
@@ -288,7 +289,8 @@ pub fn git_status(dir: &Path) -> Result<GitStatusSummary> {
}
let origins = repo.remotes()?.into_iter().filter_map(|o| Some(o?.to_string())).collect();
let branches = list_branch_names(&repo)?;
let local_branches = local_branch_names(&repo)?;
let remote_branches = remote_branch_names(&repo)?;
Ok(GitStatusSummary {
entries,
@@ -296,7 +298,8 @@ pub fn git_status(dir: &Path) -> Result<GitStatusSummary> {
path: dir.to_string_lossy().to_string(),
head_ref,
head_ref_shorthand,
branches,
local_branches,
remote_branches,
})
}

View File

@@ -1,4 +1,4 @@
use crate::commands::{add, branch, checkout, commit, delete_branch, initialize, log, merge_branch, pull, push, status, unstage};
use crate::commands::{add, branch, checkout, commit, delete_branch, fetch_all, initialize, log, merge_branch, pull, push, status, unstage};
use tauri::{
generate_handler,
plugin::{Builder, TauriPlugin},
@@ -9,6 +9,7 @@ mod branch;
mod callbacks;
mod commands;
mod error;
mod fetch;
mod git;
mod merge;
mod pull;
@@ -24,6 +25,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
checkout,
commit,
delete_branch,
fetch_all,
initialize,
log,
merge_branch,

View File

@@ -34,7 +34,7 @@ pub(crate) fn get_current_branch(repo: &Repository) -> Result<Option<Branch>> {
Ok(None)
}
pub(crate) fn list_branch_names(repo: &Repository) -> Result<Vec<String>> {
pub(crate) fn local_branch_names(repo: &Repository) -> Result<Vec<String>> {
let mut branches = Vec::new();
for branch in repo.branches(Some(BranchType::Local))? {
let branch = branch?.0;
@@ -45,6 +45,17 @@ pub(crate) fn list_branch_names(repo: &Repository) -> Result<Vec<String>> {
Ok(branches)
}
pub(crate) fn remote_branch_names(repo: &Repository) -> Result<Vec<String>> {
let mut branches = Vec::new();
for branch in repo.branches(Some(BranchType::Remote))? {
let branch = branch?.0;
let name = branch.name_bytes()?;
let name = bytes_to_string(name)?;
branches.push(name);
}
Ok(branches)
}
pub(crate) fn get_branch_by_name<'s>(repo: &'s Repository, name: &str) -> Result<Branch<'s>> {
Ok(repo.find_branch(name, BranchType::Local)?)
}