fetch: allow fetch of annotated tags
If an ancestry check of an annotated tag was triggered then the fetch from that peer would fail. This is due to `graph_ahead_behind` only working for commit-ish objects. To allow for annotated tags, and other object that peel to commits, the check now peels the two objects to commits before getting their `Oid`s. The call to `graph_ahead_behind` can take these two `Oid`s instead. A test for annotated tags is included as part of the `examples/git` suite in the CLI tests. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
9950182ee0
commit
87d1cb50a0
|
|
@ -0,0 +1,70 @@
|
|||
Alice creates an annotated tag and pushed to her `rad` remote:
|
||||
|
||||
``` ~alice
|
||||
$ touch LICENSE
|
||||
$ git add LICENSE
|
||||
$ git commit -am "Add LICENSE"
|
||||
[master 62d19fd] Add LICENSE
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 LICENSE
|
||||
$ git tag v1.0 -a -m "Release v1.0"
|
||||
```
|
||||
|
||||
``` ~alice (stderr)
|
||||
$ git push rad v1.0 --tags
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
* [new tag] v1.0 -> v1.0
|
||||
```
|
||||
|
||||
Bob fetches the tag from Alice, by adding her as a remote:
|
||||
|
||||
``` ~bob
|
||||
$ cd heartwood
|
||||
$ rad remote add z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --name alice
|
||||
✓ Follow policy updated for z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Remote alice added
|
||||
✓ Remote-tracking branch alice/master created for z6MknSL…StBU8Vi
|
||||
```
|
||||
|
||||
Bob is able to fetch Alice's tag into his working copy by using the
|
||||
`--tags` flag:
|
||||
|
||||
``` ~bob (stderr)
|
||||
$ git fetch alice --tags
|
||||
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
* [new tag] v1.0 -> v1.0
|
||||
```
|
||||
|
||||
Alice forcefully creates a new version of the tag (let's say she made
|
||||
a mistake):
|
||||
|
||||
``` ~alice
|
||||
$ git commit --allow-empty -m "Release: v1.0"
|
||||
[master 8260c04] Release: v1.0
|
||||
$ git tag v1.0 -f -a -m "Release v1.0"
|
||||
Updated tag 'v1.0' (was be18ed6)
|
||||
```
|
||||
|
||||
``` ~alice (stderr)
|
||||
$ git push rad v1.0 -f
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
+ be18ed6...9dbdebc v1.0 -> v1.0 (forced update)
|
||||
```
|
||||
|
||||
We ensure that Bob is still able to fetch from Alice and get the new
|
||||
update of the tag:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync -f
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
||||
``` ~bob (stderr)
|
||||
$ git fetch alice --tags -f
|
||||
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
t [tag update] v1.0 -> v1.0
|
||||
```
|
||||
|
|
@ -2317,6 +2317,51 @@ fn git_push_and_fetch() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_tag() {
|
||||
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]);
|
||||
|
||||
test(
|
||||
"examples/rad-clone.md",
|
||||
&working.join("bob"),
|
||||
Some(&bob.home),
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
formula(&environment.tmp(), "examples/git/git-tag.md")
|
||||
.unwrap()
|
||||
.home(
|
||||
"alice",
|
||||
working.join("alice"),
|
||||
[("RAD_HOME", alice.home.path().display())],
|
||||
)
|
||||
.home(
|
||||
"bob",
|
||||
working.join("bob"),
|
||||
[("RAD_HOME", bob.home.path().display())],
|
||||
)
|
||||
.run()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rad_workflow() {
|
||||
let mut environment = Environment::new();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
pub mod error;
|
||||
|
||||
use either::Either;
|
||||
use radicle::git::{Namespaced, Oid, Qualified};
|
||||
use radicle::git::{self, Namespaced, Oid, Qualified};
|
||||
use radicle::storage::git::Repository;
|
||||
|
||||
use super::refs::{Applied, Policy, RefUpdate, Update};
|
||||
|
|
@ -38,10 +38,29 @@ pub fn contains(repo: &Repository, oid: Oid) -> Result<bool, error::Contains> {
|
|||
.map_err(error::Contains)
|
||||
}
|
||||
|
||||
pub fn ancestry(repo: &Repository, old: Oid, new: Oid) -> Result<Ancestry, error::Ancestry> {
|
||||
if !contains(repo, old)? || !contains(repo, new)? {
|
||||
return Err(error::Ancestry::Missing { a: old, b: new });
|
||||
/// Find the object identified by `oid` and peel it to its associated
|
||||
/// commit `Oid`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// - The object was not found
|
||||
/// - The object does not peel to a commit
|
||||
/// - Attempting to find the object fails
|
||||
fn find_and_peel(repo: &Repository, oid: Oid) -> Result<Oid, error::Ancestry> {
|
||||
match repo.backend.find_object(*oid, None) {
|
||||
Ok(object) => Ok(object
|
||||
.peel(git::raw::ObjectType::Commit)
|
||||
.map_err(|err| error::Ancestry::Peel { oid, err })?
|
||||
.id()
|
||||
.into()),
|
||||
Err(e) if git::is_not_found_err(&e) => Err(error::Ancestry::Missing { oid }),
|
||||
Err(err) => Err(error::Ancestry::Object { oid, err }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ancestry(repo: &Repository, old: Oid, new: Oid) -> Result<Ancestry, error::Ancestry> {
|
||||
let old = find_and_peel(repo, old)?;
|
||||
let new = find_and_peel(repo, new)?;
|
||||
|
||||
if old == new {
|
||||
return Ok(Ancestry::Equal);
|
||||
|
|
@ -49,7 +68,7 @@ pub fn ancestry(repo: &Repository, old: Oid, new: Oid) -> Result<Ancestry, error
|
|||
|
||||
let (ahead, behind) = repo
|
||||
.backend
|
||||
.graph_ahead_behind(new.into(), old.into())
|
||||
.graph_ahead_behind(*new, *old)
|
||||
.map_err(|err| error::Ancestry::Check { old, new, err })?;
|
||||
|
||||
if ahead > 0 && behind == 0 {
|
||||
|
|
|
|||
|
|
@ -7,17 +7,27 @@ pub struct Contains(#[source] pub raw::Error);
|
|||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Ancestry {
|
||||
#[error("missing one of {a} or {b} while checking ancestry")]
|
||||
Missing { a: Oid, b: Oid },
|
||||
#[error(transparent)]
|
||||
Contains(#[from] Contains),
|
||||
#[error("failed to check ancestry for {old} and {new}")]
|
||||
#[error("missing {oid} while checking ancestry")]
|
||||
Missing { oid: Oid },
|
||||
#[error("failed to check ancestry for {old} and {new}: {err}")]
|
||||
Check {
|
||||
old: Oid,
|
||||
new: Oid,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
},
|
||||
#[error("failed to peel object to commit {oid}: {err}")]
|
||||
Peel {
|
||||
oid: Oid,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
},
|
||||
#[error("failed to find object {oid}: {err}")]
|
||||
Object {
|
||||
oid: Oid,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
Loading…
Reference in New Issue