From e703bad294cc89358da319ac5f94cb89f22b897a Mon Sep 17 00:00:00 2001 From: cloudhead Date: Tue, 21 Nov 2023 18:44:35 +0100 Subject: [PATCH] remote-helper: Fix `FETCH_HEAD` setting on fetch The `git2` crate is not reliable at setting the `FETCH_HEAD` on fetch. Therefore we disable that feature, and let the git command-line do its thing. --- .../git/{git-pull.md => git-fetch.md} | 0 radicle-cli/examples/rad-patch-fetch-1.md | 31 +++++ radicle-cli/examples/rad-patch-fetch-2.md | 27 +++++ .../examples/rad-push-and-pull-patches.md | 81 +++++++++++++ radicle-cli/tests/commands.rs | 108 +++++++++++++++++- radicle-node/src/test/environment.rs | 16 ++- radicle-remote-helper/src/fetch.rs | 6 +- 7 files changed, 265 insertions(+), 4 deletions(-) rename radicle-cli/examples/git/{git-pull.md => git-fetch.md} (100%) create mode 100644 radicle-cli/examples/rad-patch-fetch-1.md create mode 100644 radicle-cli/examples/rad-patch-fetch-2.md create mode 100644 radicle-cli/examples/rad-push-and-pull-patches.md diff --git a/radicle-cli/examples/git/git-pull.md b/radicle-cli/examples/git/git-fetch.md similarity index 100% rename from radicle-cli/examples/git/git-pull.md rename to radicle-cli/examples/git/git-fetch.md diff --git a/radicle-cli/examples/rad-patch-fetch-1.md b/radicle-cli/examples/rad-patch-fetch-1.md new file mode 100644 index 00000000..f2eb0b71 --- /dev/null +++ b/radicle-cli/examples/rad-patch-fetch-1.md @@ -0,0 +1,31 @@ +This is a simple test to ensure the behavior of our remote helper is correct. + +``` ~alice +$ git rev-parse master +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 +$ git checkout -b alice/1 -q +$ git commit --allow-empty -m "Change #1" -q +$ git rev-parse HEAD +7461703ce0fda972df450d071d1d3702057a6352 +$ git push rad HEAD:alice/1 +``` + +``` ~bob +$ git status +On branch master +Your branch is up to date with 'rad/master'. + +nothing to commit, working tree clean +$ git fetch --all +Fetching rad +Fetching alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi +$ cat .git/FETCH_HEAD +7461703ce0fda972df450d071d1d3702057a6352 not-for-merge branch 'alice/1' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 not-for-merge branch 'master' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi +$ git merge FETCH_HEAD +Already up to date. +$ git rev-parse master +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 +$ git rev-parse HEAD +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 +``` diff --git a/radicle-cli/examples/rad-patch-fetch-2.md b/radicle-cli/examples/rad-patch-fetch-2.md new file mode 100644 index 00000000..45efc2ea --- /dev/null +++ b/radicle-cli/examples/rad-patch-fetch-2.md @@ -0,0 +1,27 @@ +Ensure that we're able to pull our own branches if they happened to be deleted +in our working copy. This also applies to a situation with multiple working +copies. + +``` +$ git checkout -b alice/1 -q +$ git commit --allow-empty -m "Changes #1" -q +$ git push rad -o patch.message="Changes" HEAD:refs/patches +``` + +``` +$ git checkout master -q +$ git branch -D alice/1 -q +$ git update-ref -d refs/remotes/rad/alice/1 +$ git update-ref -d refs/remotes/rad/patches/34dff44b9e86f0bdb1a65ee5b6ef90ce6e7af70d +$ git gc --prune=now +$ git branch -r + rad/master +``` + +``` +$ git pull +Already up to date. +$ git branch -r + rad/master + rad/patches/34dff44b9e86f0bdb1a65ee5b6ef90ce6e7af70d +``` diff --git a/radicle-cli/examples/rad-push-and-pull-patches.md b/radicle-cli/examples/rad-push-and-pull-patches.md new file mode 100644 index 00000000..42765151 --- /dev/null +++ b/radicle-cli/examples/rad-push-and-pull-patches.md @@ -0,0 +1,81 @@ +Testing pulling, fetching and the `FETCH_HEAD`. + +``` ~bob +$ git push rad +$ git checkout -b bob/1 -q +$ git commit --allow-empty -m "Changes #1" -q +$ git push -o patch.message="Changes" rad HEAD:refs/patches +$ rad sync -a +✓ Synced with 1 node(s) +``` + +``` ~alice +$ git checkout -b alice/1 -q +$ git rev-parse HEAD +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 +$ git checkout master -q +$ rad patch checkout 0fd67a0 +✓ Switched to branch patch/0fd67a0 +✓ Branch patch/0fd67a0 setup to track rad/patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21 +$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk +✓ Remote bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk added +✓ Remote-tracking branch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master created for z6Mkt67…v4N1tRk +$ git checkout master -q +$ git fetch --all -q +$ cat .git/FETCH_HEAD +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 branch 'master' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji +8d5f1bae4b69d8e3f6cbfc6f4bd675ed19990afc not-for-merge branch 'patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 not-for-merge branch 'master' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk +8d5f1bae4b69d8e3f6cbfc6f4bd675ed19990afc not-for-merge branch 'patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk +$ git rev-parse master +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 +``` + +``` ~alice (stderr) +$ git checkout patch/0fd67a0 -q +$ git commit --allow-empty -m "Changes #2" -q +$ git push +✓ Patch 0fd67a0 updated to c360232989049f6d95efe3512e68608317333a5e +✓ Synced with 1 node(s) +To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi + * [new branch] patch/0fd67a0 -> patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21 +``` + +``` ~bob +$ git commit --allow-empty -m "Changes #2" -q +$ git push +``` + +``` ~alice (stderr) +$ git checkout master -q +$ git pull +✓ Synced with 1 peer(s) +From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji + + c2aaf1c...8d5f1ba patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21 -> rad/patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21 (forced update) +$ git checkout - -q +$ git commit --allow-empty -m "Changes #3" -q +$ git push +✓ Patch 0fd67a0 updated to c4115970191cd0e67212b6d26ad9e3bd992dce35 +✓ Synced with 1 node(s) +To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi + c2aaf1c..d9f8caf patch/0fd67a0 -> patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21 +``` + +``` ~alice +$ cat .git/FETCH_HEAD +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 branch 'master' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji +8d5f1bae4b69d8e3f6cbfc6f4bd675ed19990afc not-for-merge branch 'patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji +``` + +``` ~bob (stderr) +$ git checkout master -q +$ git pull +✓ Synced with 1 peer(s) +From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji + + c2aaf1c...8d5f1ba patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21 -> rad/patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21 (forced update) +``` + +``` ~bob +$ git rev-parse master +f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 +``` diff --git a/radicle-cli/tests/commands.rs b/radicle-cli/tests/commands.rs index 59e9e940..813a42b5 100644 --- a/radicle-cli/tests/commands.rs +++ b/radicle-cli/tests/commands.rs @@ -1530,7 +1530,111 @@ fn git_push_diverge() { } #[test] -fn git_push_and_pull() { +fn rad_push_and_pull_patches() { + let mut environment = Environment::new(); + let alice = environment.node(Config::test(Alias::new("alice"))); + let bob = environment.node(Config::test(Alias::new("bob"))); + let working = environment.tmp().join("working"); + + fixtures::repository(working.join("alice")); + + test( + "examples/rad-init.md", + working.join("alice"), + Some(&alice.home), + [], + ) + .unwrap(); + + let alice = alice.spawn(); + let mut bob = bob.spawn(); + + bob.connect(&alice).converge([&alice]); + + logger::init(log::Level::Debug); + + test( + "examples/rad-clone.md", + working.join("bob"), + Some(&bob.home), + [], + ) + .unwrap(); + + formula(&environment.tmp(), "examples/rad-push-and-pull-patches.md") + .unwrap() + .home( + "alice", + working.join("alice"), + [("RAD_HOME", alice.home.path().display())], + ) + .home( + "bob", + working.join("bob").join("heartwood"), + [("RAD_HOME", bob.home.path().display())], + ) + .run() + .unwrap(); +} + +#[test] +fn rad_patch_fetch_1() { + let mut environment = Environment::new(); + let mut alice = environment.node(Config::test(Alias::new("alice"))); + let bob = environment.node(Config::test(Alias::new("bob"))); + let working = environment.tmp().join("working"); + let (repo, _) = fixtures::repository(working.join("alice")); + let rid = alice.project_from("heartwood", "Radicle Heartwood Protocol & Stack", &repo); + + let alice = alice.spawn(); + let mut bob = bob.spawn(); + + bob.connect(&alice).converge([&alice]); + bob.clone(rid, working.join("bob")).unwrap(); + + formula(&environment.tmp(), "examples/rad-patch-fetch-1.md") + .unwrap() + .home( + "alice", + working.join("alice"), + [("RAD_HOME", alice.home.path().display())], + ) + .home( + "bob", + working.join("bob").join("heartwood"), + [("RAD_HOME", bob.home.path().display())], + ) + .run() + .unwrap(); +} + +#[test] +fn rad_patch_fetch_2() { + let mut environment = Environment::new(); + let alice = environment.node(Config::test(Alias::new("alice"))); + let working = environment.tmp().join("working"); + + fixtures::repository(working.join("alice")); + + test( + "examples/rad-init.md", + working.join("alice"), + Some(&alice.home), + [], + ) + .unwrap(); + + test( + "examples/rad-patch-fetch-2.md", + working.join("alice"), + Some(&alice.home), + [], + ) + .unwrap(); +} + +#[test] +fn git_push_and_fetch() { let mut environment = Environment::new(); let alice = environment.node(Config::test(Alias::new("alice"))); let bob = environment.node(Config::test(Alias::new("bob"))); @@ -1566,7 +1670,7 @@ fn git_push_and_pull() { ) .unwrap(); test( - "examples/git/git-pull.md", + "examples/git/git-fetch.md", &working.join("bob"), Some(&bob.home), [], diff --git a/radicle-node/src/test/environment.rs b/radicle-node/src/test/environment.rs index b9c8b8a9..6ef2b883 100644 --- a/radicle-node/src/test/environment.rs +++ b/radicle-node/src/test/environment.rs @@ -271,6 +271,11 @@ impl NodeHandle { } } + /// Clone a repo into a directory. + pub fn clone>(&self, rid: Id, cwd: P) -> io::Result<()> { + self.rad("clone", &[rid.to_string().as_str()], cwd) + } + /// Run a `rad` CLI command. pub fn rad>(&self, cmd: &str, args: &[&str], cwd: P) -> io::Result<()> { let cwd = cwd.as_ref(); @@ -402,11 +407,12 @@ impl + Signer + Clone> Node { ) -> Id { transport::local::register(self.storage.clone()); + let branch = refname!("master"); let id = rad::init( repo, name, description, - refname!("master"), + branch.clone(), Visibility::default(), &self.signer, &self.storage, @@ -437,6 +443,14 @@ impl + Signer + Clone> Node { } git::push(repo, "rad", refs.iter().map(|(a, b)| (a, b))).unwrap(); + radicle::git::set_upstream( + repo, + &*radicle::rad::REMOTE_NAME, + branch.clone(), + radicle::git::refs::workdir::branch(&branch), + ) + .unwrap(); + self.storage .repository(id) .unwrap() diff --git a/radicle-remote-helper/src/fetch.rs b/radicle-remote-helper/src/fetch.rs index 92493961..c6c66600 100644 --- a/radicle-remote-helper/src/fetch.rs +++ b/radicle-remote-helper/src/fetch.rs @@ -63,9 +63,13 @@ pub fn run( }; } + let mut opts = git::raw::FetchOptions::new(); + // Setting this to false ensures that the FETCH_HEAD is set correctly. Go figure. + opts.update_fetchhead(false); + git::raw::Repository::open(working)? .remote_anonymous(&git::url::File::new(stored.path()).to_string())? - .fetch(&refspecs, None, None)?; + .fetch(&refspecs, Some(&mut opts), None)?; // Nb. An empty line means we're done. println!();