Skip to content

Commit f16a052

Browse files
codexByron
andcommitted
repo: migrate add_remote to gix config
Co-authored-by: Sebastian Thiel <sebastian.thiel@icloud.com>
1 parent ad74d5c commit f16a052

3 files changed

Lines changed: 101 additions & 9 deletions

File tree

crates/gitbutler-repo/src/commands.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{path::Path, sync::Mutex};
33
use anyhow::{Result, bail};
44
use base64::engine::Engine as _;
55
use but_core::commit::sign_buffer;
6+
use but_core::git_config::edit_repo_config;
67
use but_ctx::Context;
78
use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
89
use ignore::WalkBuilder;
@@ -11,7 +12,7 @@ use itertools::Itertools;
1112
use serde::Serialize;
1213
use tracing::warn;
1314

14-
use crate::{RepositoryExt, remote::GitRemote};
15+
use crate::remote::GitRemote;
1516

1617
#[derive(Default, Debug, Serialize)]
1718
#[serde(rename_all = "camelCase")]
@@ -202,25 +203,40 @@ impl RepoCommands for Context {
202203
}
203204

204205
fn add_remote(&self, name: &str, url: &str) -> Result<()> {
205-
#[expect(deprecated, reason = "legacy remote configuration adapter")]
206-
let repo = self.git2_repo.get()?;
206+
let repo = self.open_isolated_repo()?;
207207

208208
// Bail if remote with given name already exists.
209209
if repo.find_remote(name).is_ok() {
210210
bail!("Remote name '{name}' already exists");
211211
}
212212

213213
// Bail if remote with given url already exists.
214-
if repo
215-
.remotes_as_string()?
214+
if let Some(remote_name) = repo
215+
.remote_names()
216216
.iter()
217-
.map(|name| repo.find_remote(name))
218-
.any(|result| result.is_ok_and(|remote| remote.url() == Some(url)))
217+
.filter_map(|name| repo.find_remote(name.as_ref()).ok())
218+
.find_map(|remote| {
219+
remote
220+
.url(gix::remote::Direction::Fetch)
221+
.and_then(|remote_url| {
222+
if remote_url.to_bstring() == url {
223+
remote
224+
.name()
225+
.and_then(|n| n.as_symbol().map(ToOwned::to_owned))
226+
} else {
227+
None
228+
}
229+
})
230+
})
219231
{
220-
bail!("Remote with url '{url}' already exists");
232+
bail!("Remote with url '{url}' already exists at '{remote_name}'");
221233
}
222234

223-
repo.remote(name, url)?;
235+
edit_repo_config(&repo, gix::config::Source::Local, |config| {
236+
let mut section = config.section_mut_or_create_new("remote", Some(name.into()))?;
237+
section.push("url".try_into()?, Some(url.into()));
238+
Ok(())
239+
})?;
224240
Ok(())
225241
}
226242

crates/gitbutler-repo/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ mod managed_hooks_tests;
55
mod merge_base_octopussy;
66
mod read_file_from_workspace_security;
77
mod rebase;
8+
mod remotes;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use but_ctx::{Context, RepoOpenMode};
2+
use but_settings::AppSettings;
3+
use but_testsupport::legacy::test_repository;
4+
use gitbutler_project as projects;
5+
use gitbutler_repo::RepoCommands;
6+
use tempfile::TempDir;
7+
8+
struct TestCtx {
9+
ctx: Context,
10+
_tmp: TempDir,
11+
}
12+
13+
fn ctx() -> TestCtx {
14+
let (repo, tmp) = test_repository();
15+
let project = projects::Project::new_for_gitbutler_repo(
16+
repo.workdir().unwrap().to_path_buf(),
17+
projects::AuthKey::SystemExecutable,
18+
);
19+
TestCtx {
20+
ctx: Context::new_from_legacy_project_and_settings_with_repo_open_mode(
21+
&project,
22+
AppSettings::default(),
23+
RepoOpenMode::Isolated,
24+
)
25+
.expect("can create context"),
26+
_tmp: tmp,
27+
}
28+
}
29+
30+
#[test]
31+
fn add_remote_writes_fetch_url_to_local_config() -> anyhow::Result<()> {
32+
let test = ctx();
33+
let ctx = &test.ctx;
34+
35+
ctx.add_remote("origin", "https://example.com/repo.git")?;
36+
37+
let repo = ctx.open_isolated_repo()?;
38+
let remote = repo.find_remote("origin")?;
39+
assert_eq!(
40+
remote
41+
.url(gix::remote::Direction::Fetch)
42+
.map(|url| url.to_bstring().to_string()),
43+
Some("https://example.com/repo.git".to_owned())
44+
);
45+
Ok(())
46+
}
47+
48+
#[test]
49+
fn add_remote_rejects_duplicate_name() {
50+
let test = ctx();
51+
let ctx = &test.ctx;
52+
ctx.add_remote("origin", "https://example.com/repo.git")
53+
.expect("first add succeeds");
54+
55+
let err = ctx
56+
.add_remote("origin", "https://example.com/other.git")
57+
.expect_err("duplicate name should fail");
58+
assert_eq!(err.to_string(), "Remote name 'origin' already exists");
59+
}
60+
61+
#[test]
62+
fn add_remote_rejects_duplicate_url() {
63+
let test = ctx();
64+
let ctx = &test.ctx;
65+
ctx.add_remote("origin", "https://example.com/repo.git")
66+
.expect("first add succeeds");
67+
68+
let err = ctx
69+
.add_remote("upstream", "https://example.com/repo.git")
70+
.expect_err("duplicate url should fail");
71+
assert_eq!(
72+
err.to_string(),
73+
"Remote with url 'https://example.com/repo.git' already exists at 'origin'"
74+
);
75+
}

0 commit comments

Comments
 (0)