Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 102758c25c | |||
| 6aa385b044 | |||
| e44b63edbb | |||
| 1e6c9407b6 | |||
| b967b6dca3 | |||
| 83973f8a1a | |||
| ff32759058 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -188,7 +188,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "git-repo-manager"
|
name = "git-repo-manager"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"comfy-table",
|
"comfy-table",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "git-repo-manager"
|
name = "git-repo-manager"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = [
|
authors = [
|
||||||
"Hannes Körber <hannes@hkoerber.de>",
|
"Hannes Körber <hannes@hkoerber.de>",
|
||||||
|
|||||||
110
README.md
110
README.md
@@ -3,110 +3,8 @@
|
|||||||
GRM helps you manage git repositories in a declarative way. Configure your
|
GRM helps you manage git repositories in a declarative way. Configure your
|
||||||
repositories in a [TOML](https://toml.io/) file, GRM does the rest.
|
repositories in a [TOML](https://toml.io/) file, GRM does the rest.
|
||||||
|
|
||||||
## Quickstart
|
**Take a look at the [official documentation](https://hakoerber.github.io/git-repo-manager/)
|
||||||
|
for installation & quickstart.**
|
||||||
See [the example configuration](example.config.toml) to get a feel for the way
|
|
||||||
you configure your repositories.
|
|
||||||
|
|
||||||
### Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cargo install --git https://github.com/hakoerber/git-repo-manager.git --branch master
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get the example configuration
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl --proto '=https' --tlsv1.2 -sSfO https://raw.githubusercontent.com/hakoerber/git-repo-manager/master/example.config.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run it!
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ grm sync --config example.config.toml
|
|
||||||
[⚙] Cloning into "/home/me/projects/git-repo-manager" from "https://code.hkoerber.de/hannes/git-repo-manager.git"
|
|
||||||
[✔] git-repo-manager: Repository successfully cloned
|
|
||||||
[⚙] git-repo-manager: Setting up new remote "github" to "https://github.com/hakoerber/git-repo-manager.git"
|
|
||||||
[✔] git-repo-manager: OK
|
|
||||||
[⚙] Cloning into "/home/me/projects/dotfiles" from "https://github.com/hakoerber/dotfiles.git"
|
|
||||||
[✔] dotfiles: Repository successfully cloned
|
|
||||||
[✔] dotfiles: OK
|
|
||||||
```
|
|
||||||
|
|
||||||
If you run it again, it will report no changes:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ grm sync --config example.config.toml
|
|
||||||
[✔] git-repo-manager: OK
|
|
||||||
[✔] dotfiles: OK
|
|
||||||
```
|
|
||||||
|
|
||||||
### Generate your own configuration
|
|
||||||
|
|
||||||
Now, if you already have a few repositories, it would be quite laborious to write
|
|
||||||
a configuration from scratch. Luckily, GRM has a way to generate a configuration
|
|
||||||
from an existing file tree:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ grm find ~/your/project/root > config.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
This will detect all repositories and remotes and write them to `config.toml`.
|
|
||||||
|
|
||||||
### Show the state of your projects
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ grm status --config example.config.toml
|
|
||||||
+------------------+------------+----------------------------------+--------+---------+
|
|
||||||
| Repo | Status | Branches | HEAD | Remotes |
|
|
||||||
+=====================================================================================+
|
|
||||||
| git-repo-manager | | branch: master <origin/master> ✔ | master | github |
|
|
||||||
| | | | | origin |
|
|
||||||
|------------------+------------+----------------------------------+--------+---------|
|
|
||||||
| dotfiles | No changes | branch: master <origin/master> ✔ | master | origin |
|
|
||||||
+------------------+------------+----------------------------------+--------+---------+
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use `status` without `--config` to check the current directory:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ cd ./dotfiles
|
|
||||||
$ grm status
|
|
||||||
+----------+------------+----------------------------------+--------+---------+
|
|
||||||
| Repo | Status | Branches | HEAD | Remotes |
|
|
||||||
+=============================================================================+
|
|
||||||
| dotfiles | No changes | branch: master <origin/master> ✔ | master | origin |
|
|
||||||
+----------+------------+----------------------------------+--------+---------+
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manage worktrees for projects
|
|
||||||
|
|
||||||
Optionally, GRM can also set up a repository to support multiple worktrees. See
|
|
||||||
[the git documentation](https://git-scm.com/docs/git-worktree) for details about
|
|
||||||
worktrees. Long story short: Worktrees allow you to have multiple independent
|
|
||||||
checkouts of the same repository in different directories, backed by a single
|
|
||||||
git repository.
|
|
||||||
|
|
||||||
To use this, specify `worktree_setup = true` for a repo in your configuration.
|
|
||||||
After the sync, you will see that the target directory is empty. Actually, the
|
|
||||||
repository was bare-cloned into a hidden directory: `.git-main-working-tree`.
|
|
||||||
Don't touch it! GRM provides a command to manage working trees.
|
|
||||||
|
|
||||||
Use `grm worktree add <name>` to create a new checkout of a new branch into
|
|
||||||
a subdirectory. An example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ grm worktree add mybranch
|
|
||||||
$ cd ./mybranch
|
|
||||||
$ git status
|
|
||||||
On branch mybranch
|
|
||||||
|
|
||||||
nothing to commit, working tree clean
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're done with your worktree, use `grm worktree delete <name>` to remove it.
|
|
||||||
GRM will refuse to delete worktrees that contain uncommitted or unpushed changes,
|
|
||||||
otherwise you might lose work.
|
|
||||||
|
|
||||||
# Why?
|
# Why?
|
||||||
|
|
||||||
@@ -146,10 +44,6 @@ repositories itself.
|
|||||||
* Support multiple file formats (YAML, JSON).
|
* Support multiple file formats (YAML, JSON).
|
||||||
* Add systemd timer unit to run regular syncs
|
* Add systemd timer unit to run regular syncs
|
||||||
|
|
||||||
# Dev Notes
|
|
||||||
|
|
||||||
It requires nightly features due to the usage of [`std::path::Path::is_symlink()`](https://doc.rust-lang.org/std/fs/struct.FileType.html#method.is_symlink). See the [tracking issue](https://github.com/rust-lang/rust/issues/85748).
|
|
||||||
|
|
||||||
# Crates
|
# Crates
|
||||||
|
|
||||||
* [`toml`](https://docs.rs/toml/) for the configuration file
|
* [`toml`](https://docs.rs/toml/) for the configuration file
|
||||||
|
|||||||
@@ -192,6 +192,26 @@ can also use the following:
|
|||||||
$ grm wt clean
|
$ grm wt clean
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that this will not delete the default branch of the repository. It can of
|
||||||
|
course still be delete with `grm wt delete` if neccessary.
|
||||||
|
|
||||||
|
### Converting an existing repository
|
||||||
|
|
||||||
|
It is possible to convert an existing directory to a worktree setup, using `grm
|
||||||
|
wt convert`. This command has to be run in the root of the repository you want
|
||||||
|
to convert:
|
||||||
|
|
||||||
|
```
|
||||||
|
grm wt convert
|
||||||
|
[✔] Conversion successful
|
||||||
|
```
|
||||||
|
|
||||||
|
This command will refuse to run if you have any changes in your repository.
|
||||||
|
Commit them and try again!
|
||||||
|
|
||||||
|
Afterwards, the directory is empty, as there are no worktrees checked out yet.
|
||||||
|
Now you can use the usual commands to set up worktrees.
|
||||||
|
|
||||||
### Manual access
|
### Manual access
|
||||||
|
|
||||||
GRM isn't doing any magic, it's just git under the hood. If you need to have access
|
GRM isn't doing any magic, it's just git under the hood. If you need to have access
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ pub enum WorktreeAction {
|
|||||||
Delete(WorktreeDeleteArgs),
|
Delete(WorktreeDeleteArgs),
|
||||||
#[clap(about = "Show state of existing worktrees")]
|
#[clap(about = "Show state of existing worktrees")]
|
||||||
Status(WorktreeStatusArgs),
|
Status(WorktreeStatusArgs),
|
||||||
|
#[clap(about = "Convert a normal repository to a worktree setup")]
|
||||||
|
Convert(WorktreeConvertArgs),
|
||||||
#[clap(about = "Clean all worktrees that do not contain uncommited/unpushed changes")]
|
#[clap(about = "Clean all worktrees that do not contain uncommited/unpushed changes")]
|
||||||
Clean(WorktreeCleanArgs),
|
Clean(WorktreeCleanArgs),
|
||||||
}
|
}
|
||||||
@@ -116,6 +118,9 @@ pub struct WorktreeDeleteArgs {
|
|||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub struct WorktreeStatusArgs {}
|
pub struct WorktreeStatusArgs {}
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct WorktreeConvertArgs {}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub struct WorktreeCleanArgs {}
|
pub struct WorktreeCleanArgs {}
|
||||||
|
|
||||||
|
|||||||
145
src/lib.rs
145
src/lib.rs
@@ -13,13 +13,16 @@ use output::*;
|
|||||||
use comfy_table::{Cell, Table};
|
use comfy_table::{Cell, Table};
|
||||||
|
|
||||||
use repo::{
|
use repo::{
|
||||||
clone_repo, detect_remote_type, get_repo_status, init_repo, open_repo, Remote,
|
clone_repo, detect_remote_type, get_repo_status, init_repo, open_repo, repo_make_bare,
|
||||||
RemoteTrackingStatus, Repo, RepoErrorKind,
|
repo_set_config_push, Remote, RemoteTrackingStatus, Repo, RepoErrorKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GIT_MAIN_WORKTREE_DIRECTORY: &str = ".git-main-working-tree";
|
const GIT_MAIN_WORKTREE_DIRECTORY: &str = ".git-main-working-tree";
|
||||||
const BRANCH_NAMESPACE_SEPARATOR: &str = "/";
|
const BRANCH_NAMESPACE_SEPARATOR: &str = "/";
|
||||||
|
|
||||||
|
const GIT_CONFIG_BARE_KEY: &str = "core.bare";
|
||||||
|
const GIT_CONFIG_PUSH_DEFAULT: &str = "push.default";
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -631,7 +634,7 @@ fn show_single_repo_status(path: &Path, is_worktree: bool) {
|
|||||||
|
|
||||||
if let Err(error) = repo_handle {
|
if let Err(error) = repo_handle {
|
||||||
if error.kind == RepoErrorKind::NotFound {
|
if error.kind == RepoErrorKind::NotFound {
|
||||||
print_error(&"Directory is not a git directory".to_string());
|
print_error("Directory is not a git directory");
|
||||||
} else {
|
} else {
|
||||||
print_error(&format!("Opening repository failed: {}", error));
|
print_error(&format!("Opening repository failed: {}", error));
|
||||||
}
|
}
|
||||||
@@ -868,28 +871,33 @@ pub fn run() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let repo = match open_repo(&dir, true) {
|
fn get_repo(dir: &Path) -> git2::Repository {
|
||||||
Ok(r) => r,
|
match open_repo(dir, true) {
|
||||||
Err(e) => {
|
Ok(r) => r,
|
||||||
match e.kind {
|
Err(e) => {
|
||||||
RepoErrorKind::NotFound => {
|
match e.kind {
|
||||||
print_error("Current directory does not contain a worktree setup")
|
RepoErrorKind::NotFound => {
|
||||||
|
print_error("Current directory does not contain a worktree setup")
|
||||||
|
}
|
||||||
|
_ => print_error(&format!("Error opening repo: {}", e)),
|
||||||
}
|
}
|
||||||
_ => print_error(&format!("Error opening repo: {}", e)),
|
process::exit(1);
|
||||||
}
|
}
|
||||||
process::exit(1);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let worktrees = repo
|
fn get_worktrees(repo: &git2::Repository) -> Vec<String> {
|
||||||
.worktrees()
|
repo.worktrees()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| e.unwrap().to_string())
|
.map(|e| e.unwrap().to_string())
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>()
|
||||||
|
}
|
||||||
|
|
||||||
match args.action {
|
match args.action {
|
||||||
cmd::WorktreeAction::Add(action_args) => {
|
cmd::WorktreeAction::Add(action_args) => {
|
||||||
|
let repo = get_repo(&dir);
|
||||||
|
let worktrees = get_worktrees(&repo);
|
||||||
if worktrees.contains(&action_args.name) {
|
if worktrees.contains(&action_args.name) {
|
||||||
print_error("Worktree already exists");
|
print_error("Worktree already exists");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
@@ -1014,6 +1022,7 @@ pub fn run() {
|
|||||||
|
|
||||||
cmd::WorktreeAction::Delete(action_args) => {
|
cmd::WorktreeAction::Delete(action_args) => {
|
||||||
let worktree_dir = dir.join(&action_args.name);
|
let worktree_dir = dir.join(&action_args.name);
|
||||||
|
let repo = get_repo(&dir);
|
||||||
|
|
||||||
match remove_worktree(
|
match remove_worktree(
|
||||||
&action_args.name,
|
&action_args.name,
|
||||||
@@ -1040,6 +1049,8 @@ pub fn run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd::WorktreeAction::Status(_args) => {
|
cmd::WorktreeAction::Status(_args) => {
|
||||||
|
let repo = get_repo(&dir);
|
||||||
|
let worktrees = get_worktrees(&repo);
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
add_worktree_table_header(&mut table);
|
add_worktree_table_header(&mut table);
|
||||||
for worktree in &worktrees {
|
for worktree in &worktrees {
|
||||||
@@ -1081,8 +1092,100 @@ pub fn run() {
|
|||||||
}
|
}
|
||||||
println!("{}", table);
|
println!("{}", table);
|
||||||
}
|
}
|
||||||
|
cmd::WorktreeAction::Convert(_args) => {
|
||||||
|
// Converting works like this:
|
||||||
|
// * Check whether there are uncommitted/unpushed changes
|
||||||
|
// * Move the contents of .git dir to the worktree directory
|
||||||
|
// * Remove all files
|
||||||
|
// * Set `core.bare` to `true`
|
||||||
|
|
||||||
|
let repo = open_repo(&dir, false).unwrap_or_else(|error| {
|
||||||
|
if error.kind == RepoErrorKind::NotFound {
|
||||||
|
print_error("Directory does not contain a git repository");
|
||||||
|
} else {
|
||||||
|
print_error(&format!("Opening repository failed: {}", error));
|
||||||
|
}
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
let status = get_repo_status(&repo, false);
|
||||||
|
if status.changes.unwrap().is_some() {
|
||||||
|
print_error("Changes found in repository, refusing to convert");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(error) = std::fs::rename(".git", GIT_MAIN_WORKTREE_DIRECTORY) {
|
||||||
|
print_error(&format!("Error moving .git directory: {}", error));
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in match std::fs::read_dir(&dir) {
|
||||||
|
Ok(iterator) => iterator,
|
||||||
|
Err(error) => {
|
||||||
|
print_error(&format!("Opening directory failed: {}", error));
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
match entry {
|
||||||
|
Ok(entry) => {
|
||||||
|
let path = entry.path();
|
||||||
|
// The path will ALWAYS have a file component
|
||||||
|
if path.file_name().unwrap() == GIT_MAIN_WORKTREE_DIRECTORY {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if path.is_file() || path.is_symlink() {
|
||||||
|
if let Err(error) = std::fs::remove_file(&path) {
|
||||||
|
print_error(&format!("Failed removing {}", error));
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
} else if let Err(error) = std::fs::remove_dir_all(&path) {
|
||||||
|
print_error(&format!("Failed removing {}", error));
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
print_error(&format!("Error getting directory entry: {}", error));
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let worktree_repo = open_repo(&dir, true).unwrap_or_else(|error| {
|
||||||
|
print_error(&format!(
|
||||||
|
"Opening newly converted repository failed: {}",
|
||||||
|
error
|
||||||
|
));
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
repo_make_bare(&worktree_repo, true).unwrap_or_else(|error| {
|
||||||
|
print_error(&format!("Error: {}", error));
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
repo_set_config_push(&worktree_repo, "upstream").unwrap_or_else(|error| {
|
||||||
|
print_error(&format!("Error: {}", error));
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
print_success("Conversion done");
|
||||||
|
}
|
||||||
cmd::WorktreeAction::Clean(_args) => {
|
cmd::WorktreeAction::Clean(_args) => {
|
||||||
for worktree in &worktrees {
|
let repo = get_repo(&dir);
|
||||||
|
let worktrees = get_worktrees(&repo);
|
||||||
|
|
||||||
|
let default_branch = match get_default_branch(&repo) {
|
||||||
|
Ok(branch) => branch,
|
||||||
|
Err(error) => {
|
||||||
|
print_error(&format!("Failed getting default branch: {}", error));
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let default_branch_name = default_branch.name().unwrap().unwrap();
|
||||||
|
|
||||||
|
for worktree in worktrees
|
||||||
|
.iter()
|
||||||
|
.filter(|worktree| *worktree != default_branch_name)
|
||||||
|
{
|
||||||
let repo_dir = &dir.join(&worktree);
|
let repo_dir = &dir.join(&worktree);
|
||||||
if repo_dir.exists() {
|
if repo_dir.exists() {
|
||||||
match remove_worktree(worktree, repo_dir, false, &repo) {
|
match remove_worktree(worktree, repo_dir, false, &repo) {
|
||||||
@@ -1108,6 +1211,7 @@ pub fn run() {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for entry in std::fs::read_dir(&dir).unwrap() {
|
for entry in std::fs::read_dir(&dir).unwrap() {
|
||||||
let dirname = path_as_string(
|
let dirname = path_as_string(
|
||||||
&entry
|
&entry
|
||||||
@@ -1120,6 +1224,9 @@ pub fn run() {
|
|||||||
if dirname == GIT_MAIN_WORKTREE_DIRECTORY {
|
if dirname == GIT_MAIN_WORKTREE_DIRECTORY {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if dirname == default_branch_name {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if !&worktrees.contains(&dirname) {
|
if !&worktrees.contains(&dirname) {
|
||||||
print_warning(&format!(
|
print_warning(&format!(
|
||||||
"Found {}, which is not a valid worktree directory!",
|
"Found {}, which is not a valid worktree directory!",
|
||||||
|
|||||||
62
src/repo.rs
62
src/repo.rs
@@ -199,16 +199,43 @@ pub fn open_repo(path: &Path, is_worktree: bool) -> Result<Repository, RepoError
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_repo(path: &Path, is_worktree: bool) -> Result<Repository, Box<dyn std::error::Error>> {
|
pub fn init_repo(path: &Path, is_worktree: bool) -> Result<Repository, Box<dyn std::error::Error>> {
|
||||||
match is_worktree {
|
let repo = match is_worktree {
|
||||||
false => match Repository::init(path) {
|
false => Repository::init(path)?,
|
||||||
Ok(r) => Ok(r),
|
true => Repository::init_bare(path.join(super::GIT_MAIN_WORKTREE_DIRECTORY))?,
|
||||||
Err(e) => Err(Box::new(e)),
|
};
|
||||||
},
|
|
||||||
true => match Repository::init_bare(path.join(super::GIT_MAIN_WORKTREE_DIRECTORY)) {
|
if is_worktree {
|
||||||
Ok(r) => Ok(r),
|
repo_set_config_push(&repo, "upstream")?;
|
||||||
Err(e) => Err(Box::new(e)),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_repo_config(repo: &git2::Repository) -> Result<git2::Config, String> {
|
||||||
|
repo.config()
|
||||||
|
.map_err(|error| format!("Failed getting repository configuration: {}", error))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repo_make_bare(repo: &git2::Repository, value: bool) -> Result<(), String> {
|
||||||
|
let mut config = get_repo_config(repo)?;
|
||||||
|
|
||||||
|
config
|
||||||
|
.set_bool(super::GIT_CONFIG_BARE_KEY, value)
|
||||||
|
.map_err(|error| format!("Could not set {}: {}", super::GIT_CONFIG_BARE_KEY, error))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repo_set_config_push(repo: &git2::Repository, value: &str) -> Result<(), String> {
|
||||||
|
let mut config = get_repo_config(repo)?;
|
||||||
|
|
||||||
|
config
|
||||||
|
.set_str(super::GIT_CONFIG_PUSH_DEFAULT, value)
|
||||||
|
.map_err(|error| {
|
||||||
|
format!(
|
||||||
|
"Could not set {}: {}",
|
||||||
|
super::GIT_CONFIG_PUSH_DEFAULT,
|
||||||
|
error
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_repo(
|
pub fn clone_repo(
|
||||||
@@ -230,10 +257,7 @@ pub fn clone_repo(
|
|||||||
RemoteType::Https => {
|
RemoteType::Https => {
|
||||||
let mut builder = git2::build::RepoBuilder::new();
|
let mut builder = git2::build::RepoBuilder::new();
|
||||||
builder.bare(is_worktree);
|
builder.bare(is_worktree);
|
||||||
match builder.clone(&remote.url, &clone_target) {
|
builder.clone(&remote.url, &clone_target)?;
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(Box::new(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RemoteType::Ssh => {
|
RemoteType::Ssh => {
|
||||||
let mut callbacks = RemoteCallbacks::new();
|
let mut callbacks = RemoteCallbacks::new();
|
||||||
@@ -248,12 +272,16 @@ pub fn clone_repo(
|
|||||||
builder.bare(is_worktree);
|
builder.bare(is_worktree);
|
||||||
builder.fetch_options(fo);
|
builder.fetch_options(fo);
|
||||||
|
|
||||||
match builder.clone(&remote.url, &clone_target) {
|
builder.clone(&remote.url, &clone_target)?;
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(Box::new(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is_worktree {
|
||||||
|
let repo = open_repo(&clone_target, false)?;
|
||||||
|
repo_set_config_push(&repo, "upstream")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_repo_status(repo: &git2::Repository, is_worktree: bool) -> RepoStatus {
|
pub fn get_repo_status(repo: &git2::Repository, is_worktree: bool) -> RepoStatus {
|
||||||
|
|||||||
Reference in New Issue
Block a user