mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-17 23:14:03 +01:00
Handle remote branches
This commit is contained in:
2
src-tauri/gen/schemas/acl-manifests.json
generated
2
src-tauri/gen/schemas/acl-manifests.json
generated
File diff suppressed because one or more lines are too long
20
src-tauri/gen/schemas/desktop-schema.json
generated
20
src-tauri/gen/schemas/desktop-schema.json
generated
@@ -5432,6 +5432,11 @@
|
||||
"type": "string",
|
||||
"const": "yaak-git:allow-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Enables the checkout_remote command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "yaak-git:allow-checkout-remote"
|
||||
},
|
||||
{
|
||||
"description": "Enables the commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -5442,6 +5447,11 @@
|
||||
"type": "string",
|
||||
"const": "yaak-git:allow-delete-branch"
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_all command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "yaak-git:allow-fetch-all"
|
||||
},
|
||||
{
|
||||
"description": "Enables the initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -5492,6 +5502,11 @@
|
||||
"type": "string",
|
||||
"const": "yaak-git:deny-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Denies the checkout_remote command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "yaak-git:deny-checkout-remote"
|
||||
},
|
||||
{
|
||||
"description": "Denies the commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -5502,6 +5517,11 @@
|
||||
"type": "string",
|
||||
"const": "yaak-git:deny-delete-branch"
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_all command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "yaak-git:deny-fetch-all"
|
||||
},
|
||||
{
|
||||
"description": "Denies the initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
|
||||
20
src-tauri/gen/schemas/macOS-schema.json
generated
20
src-tauri/gen/schemas/macOS-schema.json
generated
@@ -5432,6 +5432,11 @@
|
||||
"type": "string",
|
||||
"const": "yaak-git:allow-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Enables the checkout_remote command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "yaak-git:allow-checkout-remote"
|
||||
},
|
||||
{
|
||||
"description": "Enables the commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -5442,6 +5447,11 @@
|
||||
"type": "string",
|
||||
"const": "yaak-git:allow-delete-branch"
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_all command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "yaak-git:allow-fetch-all"
|
||||
},
|
||||
{
|
||||
"description": "Enables the initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -5492,6 +5502,11 @@
|
||||
"type": "string",
|
||||
"const": "yaak-git:deny-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Denies the checkout_remote command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "yaak-git:deny-checkout-remote"
|
||||
},
|
||||
{
|
||||
"description": "Denies the commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -5502,6 +5517,11 @@
|
||||
"type": "string",
|
||||
"const": "yaak-git:deny-delete-branch"
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_all command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "yaak-git:deny-fetch-all"
|
||||
},
|
||||
{
|
||||
"description": "Denies the initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
|
||||
@@ -9,7 +9,7 @@ export type GitStatus = "added" | "conflict" | "current" | "modified" | "removed
|
||||
|
||||
export type GitStatusEntry = { relaPath: string, status: GitStatus, staged: boolean, prev: SyncModel | null, next: SyncModel | null, };
|
||||
|
||||
export type GitStatusSummary = { path: string, headRef: string | null, headRefShorthand: string | null, entries: Array<GitStatusEntry>, origins: Array<string>, branches: Array<string>, };
|
||||
export type GitStatusSummary = { path: string, headRef: string | null, headRefShorthand: string | null, entries: Array<GitStatusEntry>, origins: Array<string>, localBranches: Array<string>, remoteBranches: Array<string>, };
|
||||
|
||||
export type PullResult = { receivedBytes: number, receivedObjects: number, };
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ const COMMANDS: &[&str] = &[
|
||||
"checkout",
|
||||
"commit",
|
||||
"delete_branch",
|
||||
"fetch_all",
|
||||
"initialize",
|
||||
"log",
|
||||
"merge_branch",
|
||||
|
||||
@@ -41,7 +41,7 @@ export function useGit(dir: string) {
|
||||
mutationFn: (args) => invoke('plugin:yaak-git|delete_branch', { dir, ...args }),
|
||||
onSuccess,
|
||||
}),
|
||||
checkout: useMutation<void, string, { branch: string; force: boolean }>({
|
||||
checkout: useMutation<string, string, { branch: string; force: boolean }>({
|
||||
mutationKey: ['git', 'checkout', dir],
|
||||
mutationFn: (args) => invoke('plugin:yaak-git|checkout', { dir, ...args }),
|
||||
onSuccess,
|
||||
@@ -51,6 +51,11 @@ export function useGit(dir: string) {
|
||||
mutationFn: (args) => invoke('plugin:yaak-git|commit', { dir, ...args }),
|
||||
onSuccess,
|
||||
}),
|
||||
fetchAll: useMutation<string, string, void>({
|
||||
mutationKey: ['git', 'checkout', dir],
|
||||
mutationFn: () => invoke('plugin:yaak-git|fetch_all', { dir }),
|
||||
onSuccess,
|
||||
}),
|
||||
push: useMutation<PushResult, string, void>({
|
||||
mutationKey: ['git', 'push', dir],
|
||||
mutationFn: () => invoke('plugin:yaak-git|push', { dir }),
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-checkout-remote"
|
||||
description = "Enables the checkout_remote command without any pre-configured scope."
|
||||
commands.allow = ["checkout_remote"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-checkout-remote"
|
||||
description = "Denies the checkout_remote command without any pre-configured scope."
|
||||
commands.deny = ["checkout_remote"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-fetch-all"
|
||||
description = "Enables the fetch_all command without any pre-configured scope."
|
||||
commands.allow = ["fetch_all"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-fetch-all"
|
||||
description = "Denies the fetch_all command without any pre-configured scope."
|
||||
commands.deny = ["fetch_all"]
|
||||
@@ -7,6 +7,7 @@ Default permissions for the plugin
|
||||
- `allow-checkout`
|
||||
- `allow-commit`
|
||||
- `allow-delete-branch`
|
||||
- `allow-fetch-all`
|
||||
- `allow-initialize`
|
||||
- `allow-log`
|
||||
- `allow-merge-branch`
|
||||
@@ -105,6 +106,32 @@ Denies the checkout command without any pre-configured scope.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-git:allow-checkout-remote`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the checkout_remote command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-git:deny-checkout-remote`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the checkout_remote command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-git:allow-commit`
|
||||
|
||||
</td>
|
||||
@@ -157,6 +184,32 @@ Denies the delete_branch command without any pre-configured scope.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-git:allow-fetch-all`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the fetch_all command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-git:deny-fetch-all`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the fetch_all command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-git:allow-initialize`
|
||||
|
||||
</td>
|
||||
|
||||
@@ -6,6 +6,7 @@ permissions = [
|
||||
"allow-checkout",
|
||||
"allow-commit",
|
||||
"allow-delete-branch",
|
||||
"allow-fetch-all",
|
||||
"allow-initialize",
|
||||
"allow-log",
|
||||
"allow-merge-branch",
|
||||
|
||||
@@ -324,6 +324,16 @@
|
||||
"type": "string",
|
||||
"const": "deny-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Enables the checkout_remote command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-checkout-remote"
|
||||
},
|
||||
{
|
||||
"description": "Denies the checkout_remote command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-checkout-remote"
|
||||
},
|
||||
{
|
||||
"description": "Enables the commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -344,6 +354,16 @@
|
||||
"type": "string",
|
||||
"const": "deny-delete-branch"
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_all command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-fetch-all"
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_all command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-fetch-all"
|
||||
},
|
||||
{
|
||||
"description": "Enables the initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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)
|
||||
|
||||
37
src-tauri/yaak-git/src/fetch.rs
Normal file
37
src-tauri/yaak-git/src/fetch.rs
Normal 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(())
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)?)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user