cli/clone: Add flag `--bare`
This commit is contained in:
parent
766e281d39
commit
86ea33b0ca
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -13,6 +13,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## New Features
|
## New Features
|
||||||
|
|
||||||
|
- `rad clone` now supports the flag `--bare` which works analoguously to
|
||||||
|
`git clone --bare`.
|
||||||
|
|
||||||
|
## Fixed Bugs
|
||||||
|
|
||||||
|
## 1.4.0
|
||||||
|
|
||||||
|
## Release Highlights
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
- `rad cob log` now supports the arguments `--from` and `--to` which can be used
|
- `rad cob log` now supports the arguments `--from` and `--to` which can be used
|
||||||
to range over particular operations on a COB.
|
to range over particular operations on a COB.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
To create a local bare copy of a repository on the radicle network, we use the
|
||||||
|
`clone` command, followed by the identifier or *RID* of the repository:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed --bare
|
||||||
|
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
|
||||||
|
Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from the network, found [..] potential seed(s).
|
||||||
|
✓ Target met: [..] seed(s)
|
||||||
|
✓ Creating checkout in ./heartwood..
|
||||||
|
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||||
|
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
|
✓ Repository successfully cloned under [..]/heartwood/
|
||||||
|
╭────────────────────────────────────╮
|
||||||
|
│ heartwood │
|
||||||
|
│ Radicle Heartwood Protocol & Stack │
|
||||||
|
│ 0 issues · 0 patches │
|
||||||
|
╰────────────────────────────────────╯
|
||||||
|
Run `cd ./heartwood` to go to the repository directory.
|
||||||
|
```
|
||||||
|
|
||||||
|
We can now have a look at the new directory that was created from the cloned
|
||||||
|
repository:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd heartwood
|
||||||
|
$ ls
|
||||||
|
FETCH_HEAD
|
||||||
|
HEAD
|
||||||
|
config
|
||||||
|
description
|
||||||
|
hooks
|
||||||
|
info
|
||||||
|
objects
|
||||||
|
refs
|
||||||
|
```
|
||||||
|
|
||||||
|
As expected, some `git` commands fail:
|
||||||
|
``` (stderr) (fail)
|
||||||
|
$ git status
|
||||||
|
fatal: this operation must be run in a work tree
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's check that the remote tracking branch was setup correctly:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git branch --remotes
|
||||||
|
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
|
||||||
|
rad/master
|
||||||
|
```
|
||||||
|
|
||||||
|
The first branch is ours, and the second points to the repository delegate.
|
||||||
|
We can also take a look at the remotes:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git remote -v
|
||||||
|
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (fetch)
|
||||||
|
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (push)
|
||||||
|
rad rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji (fetch)
|
||||||
|
rad rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (push)
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's check the last commit!
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git log -n 1
|
||||||
|
commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
|
||||||
|
Author: anonymous <anonymous@radicle.xyz>
|
||||||
|
Date: Mon Jan 1 14:39:16 2018 +0000
|
||||||
|
|
||||||
|
Second commit
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloned repositories show up in `rad ls`:
|
||||||
|
```
|
||||||
|
$ rad ls --seeded
|
||||||
|
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Name RID Visibility Head Description │
|
||||||
|
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ heartwood rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji public f2de534 Radicle Heartwood Protocol & Stack │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
@ -46,6 +46,7 @@ Usage
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
|
--bare Make a bare repository
|
||||||
--scope <scope> Follow scope: `followed` or `all` (default: all)
|
--scope <scope> Follow scope: `followed` or `all` (default: all)
|
||||||
-s, --seed <nid> Clone from this seed (may be specified multiple times)
|
-s, --seed <nid> Clone from this seed (may be specified multiple times)
|
||||||
--timeout <secs> Timeout for fetching repository (default: 9)
|
--timeout <secs> Timeout for fetching repository (default: 9)
|
||||||
|
|
@ -64,6 +65,7 @@ pub struct Options {
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
/// Sync settings.
|
/// Sync settings.
|
||||||
sync: SyncSettings,
|
sync: SyncSettings,
|
||||||
|
bare: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args for Options {
|
impl Args for Options {
|
||||||
|
|
@ -75,6 +77,7 @@ impl Args for Options {
|
||||||
let mut scope = Scope::All;
|
let mut scope = Scope::All;
|
||||||
let mut sync = SyncSettings::default();
|
let mut sync = SyncSettings::default();
|
||||||
let mut directory = None;
|
let mut directory = None;
|
||||||
|
let mut bare = false;
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
|
@ -99,6 +102,9 @@ impl Args for Options {
|
||||||
// We keep this flag here for consistency though it doesn't have any effect,
|
// We keep this flag here for consistency though it doesn't have any effect,
|
||||||
// since the command is fully non-interactive.
|
// since the command is fully non-interactive.
|
||||||
}
|
}
|
||||||
|
Long("bare") => {
|
||||||
|
bare = true;
|
||||||
|
}
|
||||||
Long("help") | Short('h') => {
|
Long("help") | Short('h') => {
|
||||||
return Err(Error::Help.into());
|
return Err(Error::Help.into());
|
||||||
}
|
}
|
||||||
|
|
@ -125,6 +131,7 @@ impl Args for Options {
|
||||||
directory,
|
directory,
|
||||||
scope,
|
scope,
|
||||||
sync,
|
sync,
|
||||||
|
bare,
|
||||||
},
|
},
|
||||||
vec![],
|
vec![],
|
||||||
))
|
))
|
||||||
|
|
@ -153,6 +160,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
options.sync.with_profile(&profile),
|
options.sync.with_profile(&profile),
|
||||||
&mut node,
|
&mut node,
|
||||||
&profile,
|
&profile,
|
||||||
|
options.bare,
|
||||||
)?
|
)?
|
||||||
.print_or_success()
|
.print_or_success()
|
||||||
.ok_or_else(|| anyhow::anyhow!("failed to clone {}", options.id))?;
|
.ok_or_else(|| anyhow::anyhow!("failed to clone {}", options.id))?;
|
||||||
|
|
@ -163,7 +171,11 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
.filter(|id| id != profile.id())
|
.filter(|id| id != profile.id())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let default_branch = proj.default_branch().clone();
|
let default_branch = proj.default_branch().clone();
|
||||||
let path = working.workdir().unwrap(); // SAFETY: The working copy is not bare.
|
let path = if !options.bare {
|
||||||
|
working.workdir().unwrap()
|
||||||
|
} else {
|
||||||
|
working.path()
|
||||||
|
};
|
||||||
|
|
||||||
// Configure repository and setup tracking for repository delegates.
|
// Configure repository and setup tracking for repository delegates.
|
||||||
radicle::git::configure_repository(&working)?;
|
radicle::git::configure_repository(&working)?;
|
||||||
|
|
@ -229,6 +241,7 @@ struct Checkout {
|
||||||
repository: storage::git::Repository,
|
repository: storage::git::Repository,
|
||||||
doc: Doc,
|
doc: Doc,
|
||||||
project: Project,
|
project: Project,
|
||||||
|
bare: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Checkout {
|
impl Checkout {
|
||||||
|
|
@ -236,6 +249,7 @@ impl Checkout {
|
||||||
repository: storage::git::Repository,
|
repository: storage::git::Repository,
|
||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
directory: Option<PathBuf>,
|
directory: Option<PathBuf>,
|
||||||
|
bare: bool,
|
||||||
) -> Result<Self, CheckoutFailure> {
|
) -> Result<Self, CheckoutFailure> {
|
||||||
let rid = repository.rid();
|
let rid = repository.rid();
|
||||||
let doc = repository
|
let doc = repository
|
||||||
|
|
@ -257,6 +271,7 @@ impl Checkout {
|
||||||
repository,
|
repository,
|
||||||
doc: doc.doc,
|
doc: doc.doc,
|
||||||
project: proj,
|
project: proj,
|
||||||
|
bare,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,7 +289,7 @@ impl Checkout {
|
||||||
"Creating checkout in ./{}..",
|
"Creating checkout in ./{}..",
|
||||||
term::format::tertiary(destination.display())
|
term::format::tertiary(destination.display())
|
||||||
));
|
));
|
||||||
match rad::checkout(self.id, &self.remote, self.path, storage, false) {
|
match rad::checkout(self.id, &self.remote, self.path, storage, self.bare) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
spinner.message(format!(
|
spinner.message(format!(
|
||||||
"Failed to checkout in ./{}",
|
"Failed to checkout in ./{}",
|
||||||
|
|
@ -303,6 +318,7 @@ fn clone(
|
||||||
settings: SyncSettings,
|
settings: SyncSettings,
|
||||||
node: &mut Node,
|
node: &mut Node,
|
||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
|
bare: bool,
|
||||||
) -> Result<CloneResult, CloneError> {
|
) -> Result<CloneResult, CloneError> {
|
||||||
// Seed repository.
|
// Seed repository.
|
||||||
if node.seed(id, scope)? {
|
if node.seed(id, scope)? {
|
||||||
|
|
@ -322,7 +338,7 @@ fn clone(
|
||||||
node::sync::FetcherResult::TargetReached(_) => {
|
node::sync::FetcherResult::TargetReached(_) => {
|
||||||
profile.storage.repository(id).map_or_else(
|
profile.storage.repository(id).map_or_else(
|
||||||
|err| Ok(CloneResult::RepositoryMissing { rid: id, err }),
|
|err| Ok(CloneResult::RepositoryMissing { rid: id, err }),
|
||||||
|repository| Ok(perform_checkout(repository, profile, directory)?),
|
|repository| Ok(perform_checkout(repository, profile, directory, bare)?),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
node::sync::FetcherResult::TargetError(failure) => {
|
node::sync::FetcherResult::TargetError(failure) => {
|
||||||
|
|
@ -330,7 +346,7 @@ fn clone(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(repository) => Ok(perform_checkout(repository, profile, directory)?),
|
Ok(repository) => Ok(perform_checkout(repository, profile, directory, bare)?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,8 +354,9 @@ fn perform_checkout(
|
||||||
repository: storage::git::Repository,
|
repository: storage::git::Repository,
|
||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
directory: Option<PathBuf>,
|
directory: Option<PathBuf>,
|
||||||
|
bare: bool,
|
||||||
) -> Result<CloneResult, rad::CheckoutError> {
|
) -> Result<CloneResult, rad::CheckoutError> {
|
||||||
Checkout::new(repository, profile, directory).map_or_else(
|
Checkout::new(repository, profile, directory, bare).map_or_else(
|
||||||
|failure| Ok(CloneResult::Failure(failure)),
|
|failure| Ok(CloneResult::Failure(failure)),
|
||||||
|checkout| checkout.run(&profile.storage),
|
|checkout| checkout.run(&profile.storage),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1155,6 +1155,26 @@ fn rad_clone() {
|
||||||
test("examples/rad-clone.md", working, Some(&bob.home), []).unwrap();
|
test("examples/rad-clone.md", working, Some(&bob.home), []).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rad_clone_bare() {
|
||||||
|
let mut environment = Environment::new();
|
||||||
|
let mut alice = environment.node("alice");
|
||||||
|
let bob = environment.node("bob");
|
||||||
|
let working = environment.tempdir().join("working");
|
||||||
|
|
||||||
|
// Setup a test project.
|
||||||
|
let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack");
|
||||||
|
|
||||||
|
let mut alice = alice.spawn();
|
||||||
|
let mut bob = bob.spawn();
|
||||||
|
// Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors.
|
||||||
|
alice.handle.seed(acme, Scope::Followed).unwrap();
|
||||||
|
|
||||||
|
bob.connect(&alice).converge([&alice]);
|
||||||
|
|
||||||
|
test("examples/rad-clone-bare.md", working, Some(&bob.home), []).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rad_clone_directory() {
|
fn rad_clone_directory() {
|
||||||
let mut environment = Environment::new();
|
let mut environment = Environment::new();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue