Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for existing fork before adding new one #5567

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/desktop/src/lib/components/PullRequestPreview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
}

const remotes = await remotesService.remotes(project.id);
if (remotes.includes(remoteName)) {
if (remotes.find((r) => r.name === remoteName)) {
toasts.error('Remote already exists');
return;
}
Expand Down
26 changes: 20 additions & 6 deletions apps/desktop/src/lib/remotes/service.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
import { invoke } from '$lib/backend/ipc';
import { showError } from '$lib/notifications/toasts';

export interface GitRemote {
name?: string;
url?: string;
}

export class RemotesService {
async remotes(projectId: string) {
return await invoke<string[]>('list_remotes', { projectId });
return await invoke<GitRemote[]>('list_remotes', { projectId });
}

async addRemote(projectId: string, name: string, url: string) {
try {
await invoke('add_remote', { projectId, name, url });
} catch (e) {
showError('Failed to add remote', e);
const remotes = await this.remotes(projectId);

const sameNameRemote = remotes.find((remote) => remote.name === name);
if (sameNameRemote) {
throw new Error(`Remote with name ${sameNameRemote.name} already exists.`);
}

const sameUrlRemote = remotes.find((remote) => remote.url === url);
if (sameUrlRemote) {
// This should not happen, and indicates we are incorrectly showing an "apply from fork"
// button in the user interface.
throw new Error(`Remote ${sameUrlRemote.name} with url ${sameUrlRemote.url} already exists.`);
}

return await invoke<string>('add_remote', { projectId, name, url });
}
}
37 changes: 32 additions & 5 deletions crates/gitbutler-repo/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::{Config, RepositoryExt};
use crate::{remote::GitRemote, Config, RepositoryExt};
use anyhow::{bail, Result};
use base64::engine::Engine as _;
use git2::Oid;
use gitbutler_command_context::CommandContext;
use gitbutler_project::Project;
use infer::MatcherType;
use itertools::Itertools;
use serde::Serialize;
use std::path::Path;
use tracing::warn;
Expand Down Expand Up @@ -97,7 +98,7 @@ impl FileInfo {

pub trait RepoCommands {
fn add_remote(&self, name: &str, url: &str) -> Result<()>;
fn remotes(&self) -> Result<Vec<String>>;
fn remotes(&self) -> Result<Vec<GitRemote>>;
fn get_local_config(&self, key: &str) -> Result<Option<String>>;
fn set_local_config(&self, key: &str, value: &str) -> Result<()>;
fn check_signing_settings(&self) -> Result<bool>;
Expand Down Expand Up @@ -144,14 +145,40 @@ impl RepoCommands for Project {
}
}

fn remotes(&self) -> Result<Vec<String>> {
fn remotes(&self) -> anyhow::Result<Vec<GitRemote>> {
let ctx = CommandContext::open(self)?;
ctx.repository().remotes_as_string()
let repo = ctx.repository();
let remotes = repo
.remotes_as_string()?
.iter()
.map(|name| repo.find_remote(name))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|remote| remote.into())
.collect_vec();
Ok(remotes)
}

fn add_remote(&self, name: &str, url: &str) -> Result<()> {
let ctx = CommandContext::open(self)?;
ctx.repository().remote(name, url)?;
let repo = ctx.repository();

// Bail if remote with given name already exists.
if repo.find_remote(name).is_ok() {
bail!("Remote name '{}' already exists", name);
}

// Bail if remote with given url already exists.
if repo
.remotes_as_string()?
.iter()
.map(|name| repo.find_remote(name))
.any(|result| result.is_ok_and(|remote| remote.url() == Some(url)))
{
bail!("Remote with url '{}' already exists", url);
}

repo.remote(name, url)?;
Ok(())
}

Expand Down
2 changes: 2 additions & 0 deletions crates/gitbutler-repo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ pub mod rebase;

mod commands;
pub use commands::{FileInfo, RepoCommands};
pub use remote::GitRemote;

mod repository_ext;
pub use repository_ext::{GixRepositoryExt, LogUntil, RepositoryExt};

pub mod credentials;

mod config;
mod remote;

pub use config::Config;

Expand Down
18 changes: 18 additions & 0 deletions crates/gitbutler-repo/src/remote.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use serde::Serialize;

/// Struct for exposing remote information to the front end.
#[derive(Default, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GitRemote {
pub name: Option<String>,
pub url: Option<String>,
}

impl From<git2::Remote<'_>> for GitRemote {
fn from(value: git2::Remote) -> Self {
GitRemote {
name: value.name().map(|name| name.to_owned()),
url: value.url().map(|url| url.to_owned()),
}
}
}
11 changes: 5 additions & 6 deletions crates/gitbutler-tauri/src/remotes.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use crate::error::Error;
use gitbutler_project as projects;
use gitbutler_project::ProjectId;
use gitbutler_repo::RepoCommands;
use gitbutler_repo::{GitRemote, RepoCommands};
use tauri::State;
use tracing::instrument;

use crate::error::Error;

#[tauri::command(async)]
#[instrument(skip(projects), err(Debug))]
pub fn list_remotes(
projects: State<'_, projects::Controller>,
project_id: ProjectId,
) -> Result<Vec<String>, Error> {
) -> Result<Vec<GitRemote>, Error> {
let project = projects.get(project_id)?;
project.remotes().map_err(Into::into)
Ok(project.remotes()?)
}

#[tauri::command(async)]
Expand All @@ -25,5 +24,5 @@ pub fn add_remote(
url: &str,
) -> Result<(), Error> {
let project = projects.get(project_id)?;
project.add_remote(name, url).map_err(Into::into)
Ok(project.add_remote(name, url)?)
}
Loading