OXYGENHUB

remote remove: remove all of the remote 34fad4a92451

It blanked remote.NAME.url and nothing else: the token, CA and store
settings stayed in .oxy/config, and the remote's tracking bookmarks in
the view, read later as if current. Repo::remove_remote unsets every
remote.NAME.* key of the repository's config, drops the tracking
bookmarks in an operation of its own, and deletes a store remote's
mirror under .oxy/remotes/NAME; an unknown name is refused.

closes #86
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-10 21:56 UTC
Signature
✔ signed by Andy McCall
Hash
34fad4a924514f17dd1f2f9f3d0471933027dfa03c33dfe5818ac5438906e7a1
Depends on
8fd78eb52ad2 94084087a876 982864068a59 cb8b8082fa73 d6e0d5b8b339 e2a5edbf1395 f8c695764c95 fce44ae9ad29
Depended on by
d3b270909c78 efad40f9ac8e c86270b7c670 4294602ee66f ebb9210bad0c 31d3e932e97a 5d79b5c2de88 7fc365369758 aa4939e07a09 9b723f69b032 fe9586d93fb1 b689779e9e74 530a0fc650a2 9d580f2132f3
In bookmarks
bare-hashes carried-not-removed carry-identical doctor-trust identical-sides keyless-tags main merge-remote-bookmark only-help-103 paths-for-resolutions-and-amend push-notes show-stat subrepo-pin-state switch-says
In tags
v0.10.0 v0.11.0 v0.4.4 v0.4.5 v0.4.6 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
Edits
5 atoms, 0 tree operations, 4 files

Browse the tree at this change

modified CHANGELOG.md +4 −0
1 1
# Changelog
2 2
3 +
## Unreleased
4 +
5 +
- **`oxy remote remove` removes all of a remote (#86).** It blanked the URL alone: the token, CA and store settings stayed in the config and the remote's tracking bookmarks in the view, read later as if current. Now every `remote.NAME.*` key goes, with its tracking bookmarks and a store remote's mirror; an unknown name is refused.
6 +
3 7
## 0.4.3 (2026-10-10)
4 8
5 9
- **Claim your own commits in a Git import (#97).** `oxy git import --claim` and `oxy git fetch --claim` set `git.claim`: a commit whose Git author email is your identity's email is authored with your key (and sealed by you on push), so a forge credits it to your account; anyone else's commits keep the zero key, as before.
…
164 unchanged lines
modified crates/oxy-core/src/repo/sync.rs +34 −0
…
1638 unchanged lines
1639 1639
        Ok(results)
1640 1640
    }
1641 1641
1642 +
    /// Remove remote `name` (oxygen#86): every `remote.NAME.*` key of this
1643 +
    /// repository's config (its URL, token, CA and store settings), its
1644 +
    /// tracking bookmarks in the view, and a store remote's mirror under
1645 +
    /// `.oxy/remotes/NAME`. Nothing stays to be read as if it were current.
1646 +
    pub fn remove_remote(&mut self, name: &str) -> Result<()> {
1647 +
        if !self.config.remotes.contains_key(name) {
1648 +
            return fail(format!("no remote {name}: `oxy remote list` shows them"));
1649 +
        }
1650 +
        let prefix = format!("remote.{name}.");
1651 +
        let keys: Vec<String> = self
1652 +
            .config
1653 +
            .repo_layer()
1654 +
            .keys()
1655 +
            .filter(|k| k.starts_with(&prefix))
1656 +
            .cloned()
1657 +
            .collect();
1658 +
        for k in keys {
1659 +
            self.config.set(&k, "")?;
1660 +
        }
1661 +
        self.config.remotes.remove(name);
1662 +
        self.save_config()?;
1663 +
        let (op_ref, op) = self.head_op()?;
1664 +
        if op.view.remotes.contains_key(name) {
1665 +
            let mut view = op.view.clone();
1666 +
            view.remotes.remove(name);
1667 +
            self.record_op(&[op_ref], &format!("oxy remote remove {name}"), view)?;
1668 +
        }
1669 +
        let mirror = self.oxy.join("remotes").join(name);
1670 +
        if mirror.is_dir() {
1671 +
            fs::remove_dir_all(&mirror)?;
1672 +
        }
1673 +
        Ok(())
1674 +
    }
1675 +
1642 1676
    /// `changes`, in dependency order, cut into batches of about
1643 1677
    /// `push.batch-bytes` by what each change and its texts and blobs weigh.
1644 1678
    /// There is always one, perhaps empty.
…
452 unchanged lines
modified crates/oxy-core/src/repo/tests.rs +55 −0
…
8241 unchanged lines
8242 8242
    assert_eq!(by_message["mine"], key, "her own commit, any case of email");
8243 8243
    assert_eq!(by_message["someone else's"], ObjectRef::ZERO, "not hers");
8244 8244
}
8245 +
8246 +
/// oxygen#86: removing a remote removes all of it: every `remote.NAME.*`
8247 +
/// key (URL, token, CA), its tracking bookmarks, and nothing else.
8248 +
#[test]
8249 +
fn removing_a_remote_leaves_nothing_of_it() {
8250 +
    hermetic();
8251 +
    let dir = tempfile::tempdir().unwrap();
8252 +
    let origin = dir.path().join("origin");
8253 +
    Repo::init_bare_unbound(&origin).unwrap();
8254 +
    let main = dir.path().join("main");
8255 +
    let mut repo = Repo::init(&main).unwrap();
8256 +
    repo.config
8257 +
        .set("remote.origin.url", &origin.to_string_lossy())
8258 +
        .unwrap();
8259 +
    repo.config
8260 +
        .set("remote.origin.token", "secret-ish")
8261 +
        .unwrap();
8262 +
    repo.config
8263 +
        .set("remote.keep.url", &origin.to_string_lossy())
8264 +
        .unwrap();
8265 +
    repo.save_config().unwrap();
8266 +
    write(&main, "a.txt", b"a\n");
8267 +
    repo.commit("a", &[]).unwrap();
8268 +
    assert!(repo.push("origin", false).unwrap()[0].ok);
8269 +
    assert!(
8270 +
        repo.view().unwrap().remotes.contains_key("origin"),
8271 +
        "a tracking bookmark"
8272 +
    );
8273 +
8274 +
    repo.remove_remote("origin").unwrap();
8275 +
    let again = Repo::open(&main).unwrap();
8276 +
    assert!(
8277 +
        !again
8278 +
            .config
8279 +
            .repo_layer()
8280 +
            .keys()
8281 +
            .any(|k| k.starts_with("remote.origin.")),
8282 +
        "{:?}",
8283 +
        again.config.repo_layer()
8284 +
    );
8285 +
    assert!(!again.config.remotes.contains_key("origin"));
8286 +
    assert!(
8287 +
        !again.view().unwrap().remotes.contains_key("origin"),
8288 +
        "its tracking bookmarks go"
8289 +
    );
8290 +
    assert!(
8291 +
        again.config.remotes.contains_key("keep"),
8292 +
        "another remote stays"
8293 +
    );
8294 +
    let mut again = again;
8295 +
    assert!(
8296 +
        again.remove_remote("origin").is_err(),
8297 +
        "a second removal is refused"
8298 +
    );
8299 +
}
modified src/cli/sync.rs +1 −2
…
295 unchanged lines
296 296
297 297
pub fn remote_remove(name: &str) -> Result<i32> {
298 298
    let mut repo = open()?;
299 -
    repo.config.set(&format!("remote.{name}.url"), "")?;
300 -
    repo.save_config()?;
299 +
    repo.remove_remote(name)?;
301 300
    println!("{} remote {}", ui::done("Removed"), ui::bookmark(name));
302 301
    Ok(0)
303 302
}
…
836 unchanged lines