Add functionality for persistent branches
This commit is contained in:
@@ -71,6 +71,10 @@ Now, when you run a `grm sync`, you'll notice that the directory of the reposito
|
|||||||
is empty! Well, not totally, there is a hidden directory called `.git-main-working-tree`.
|
is empty! Well, not totally, there is a hidden directory called `.git-main-working-tree`.
|
||||||
This is where the repository actually "lives" (it's a bare checkout).
|
This is where the repository actually "lives" (it's a bare checkout).
|
||||||
|
|
||||||
|
Note that there are few specific things you can configure for a certain
|
||||||
|
workspace. This is all done in an optional `grm.toml` file right in the root
|
||||||
|
of the worktree. More on that later.
|
||||||
|
|
||||||
### Creating a new worktree
|
### Creating a new worktree
|
||||||
|
|
||||||
To actually work, you'll first have to create a new worktree checkout. All
|
To actually work, you'll first have to create a new worktree checkout. All
|
||||||
@@ -195,6 +199,36 @@ $ grm wt clean
|
|||||||
Note that this will not delete the default branch of the repository. It can of
|
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.
|
course still be delete with `grm wt delete` if neccessary.
|
||||||
|
|
||||||
|
### Persistent branches
|
||||||
|
|
||||||
|
You most likely have a few branches that are "special", that you don't want to
|
||||||
|
clean up and that are the usual target for feature branches to merge into. GRM
|
||||||
|
calls them "persistent branches" and treats them a bit differently:
|
||||||
|
|
||||||
|
* Their worktrees will never be deleted by `grm wt clean`
|
||||||
|
* If the branches in other worktrees are merged into them, they will be cleaned
|
||||||
|
up, even though they may not be in line with their upstream. Same goes for
|
||||||
|
`grm wt delete`, which will not require a `--force` flag. Note that of
|
||||||
|
course, actual changes in the worktree will still block an automatic cleanup!
|
||||||
|
* As soon as you enable persistent branches, non-persistent branches will only
|
||||||
|
ever cleaned up when merged into a persistent branch.
|
||||||
|
|
||||||
|
To elaborate: This is mostly relevant for a feature-branch workflow. Whenever a
|
||||||
|
feature branch is merged, it can usually be thrown away. As merging is usually
|
||||||
|
done on some remote code management platform (GitHub, GitLab, ...), this means
|
||||||
|
that you usually keep a branch around until it is merged into one of the "main"
|
||||||
|
branches (`master`, `main`, `develop`, ...)
|
||||||
|
|
||||||
|
Enable persistent branches by setting the following in the `grm.toml` in the
|
||||||
|
worktree root:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
persistent_branches = [
|
||||||
|
"master",
|
||||||
|
"develop",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
### Converting an existing repository
|
### Converting an existing repository
|
||||||
|
|
||||||
It is possible to convert an existing directory to a worktree setup, using `grm
|
It is possible to convert an existing directory to a worktree setup, using `grm
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ def test_repos_find_in_root():
|
|||||||
assert set(origin.keys()) == {"name", "type", "url"}
|
assert set(origin.keys()) == {"name", "type", "url"}
|
||||||
assert someremote["type"] == "file"
|
assert someremote["type"] == "file"
|
||||||
|
|
||||||
|
|
||||||
def test_repos_find_with_invalid_repo():
|
def test_repos_find_with_invalid_repo():
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
shell(
|
shell(
|
||||||
|
|||||||
143
e2e_tests/test_worktree_config_presistent_branch.py
Normal file
143
e2e_tests/test_worktree_config_presistent_branch.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
|
||||||
|
def test_worktree_never_clean_persistent_branches():
|
||||||
|
with TempGitRepositoryWorktree() as base_dir:
|
||||||
|
with open(os.path.join(base_dir, "grm.toml"), "w") as f:
|
||||||
|
f.write(
|
||||||
|
"""
|
||||||
|
persistent_branches = [
|
||||||
|
"mybranch",
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "add", "mybranch", "--track", "origin/master"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
before = checksum_directory(f"{base_dir}/mybranch")
|
||||||
|
|
||||||
|
cmd = grm(["wt", "clean"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
assert "mybranch" in os.listdir(base_dir)
|
||||||
|
repo = git.Repo(os.path.join(base_dir, "mybranch"))
|
||||||
|
assert str(repo.active_branch) == "mybranch"
|
||||||
|
|
||||||
|
after = checksum_directory(f"{base_dir}/mybranch")
|
||||||
|
assert before == after
|
||||||
|
|
||||||
|
|
||||||
|
def test_worktree_clean_branch_merged_into_persistent():
|
||||||
|
with TempGitRepositoryWorktree() as base_dir:
|
||||||
|
with open(os.path.join(base_dir, "grm.toml"), "w") as f:
|
||||||
|
f.write(
|
||||||
|
"""
|
||||||
|
persistent_branches = [
|
||||||
|
"master",
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "add", "test", "--track", "origin/test"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
shell(
|
||||||
|
f"""
|
||||||
|
cd {base_dir}/test
|
||||||
|
touch change1
|
||||||
|
git add change1
|
||||||
|
git commit -m "commit1"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "add", "master"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
shell(
|
||||||
|
f"""
|
||||||
|
cd {base_dir}/master
|
||||||
|
git merge --no-ff test
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "clean"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
assert "test" not in os.listdir(base_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def test_worktree_no_clean_unmerged_branch():
|
||||||
|
with TempGitRepositoryWorktree() as base_dir:
|
||||||
|
with open(os.path.join(base_dir, "grm.toml"), "w") as f:
|
||||||
|
f.write(
|
||||||
|
"""
|
||||||
|
persistent_branches = [
|
||||||
|
"master",
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "add", "test", "--track", "origin/test"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
shell(
|
||||||
|
f"""
|
||||||
|
cd {base_dir}/test
|
||||||
|
touch change1
|
||||||
|
git add change1
|
||||||
|
git commit -m "commit1"
|
||||||
|
git push origin test
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "add", "master"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
cmd = grm(["wt", "clean"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
assert "test" in os.listdir(base_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def test_worktree_delete_branch_merged_into_persistent():
|
||||||
|
with TempGitRepositoryWorktree() as base_dir:
|
||||||
|
with open(os.path.join(base_dir, "grm.toml"), "w") as f:
|
||||||
|
f.write(
|
||||||
|
"""
|
||||||
|
persistent_branches = [
|
||||||
|
"master",
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "add", "test", "--track", "origin/test"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
shell(
|
||||||
|
f"""
|
||||||
|
cd {base_dir}/test
|
||||||
|
touch change1
|
||||||
|
git add change1
|
||||||
|
git commit -m "commit1"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "add", "master"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
shell(
|
||||||
|
f"""
|
||||||
|
cd {base_dir}/master
|
||||||
|
git merge --no-ff test
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = grm(["wt", "delete", "test"], cwd=base_dir)
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
|
||||||
|
assert "test" not in os.listdir(base_dir)
|
||||||
@@ -3,6 +3,7 @@ use std::process;
|
|||||||
|
|
||||||
mod cmd;
|
mod cmd;
|
||||||
|
|
||||||
|
use grm::repo;
|
||||||
use grm::config;
|
use grm::config;
|
||||||
use grm::output::*;
|
use grm::output::*;
|
||||||
|
|
||||||
@@ -171,12 +172,21 @@ fn main() {
|
|||||||
}
|
}
|
||||||
cmd::WorktreeAction::Delete(action_args) => {
|
cmd::WorktreeAction::Delete(action_args) => {
|
||||||
let worktree_dir = cwd.join(&action_args.name);
|
let worktree_dir = cwd.join(&action_args.name);
|
||||||
|
|
||||||
|
let worktree_config = match repo::read_worktree_root_config(&cwd) {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(error) => {
|
||||||
|
print_error(&format!("Error getting worktree configuration: {}", error));
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let repo = grm::Repo::open(&cwd, true).unwrap_or_else(|error| {
|
let repo = grm::Repo::open(&cwd, true).unwrap_or_else(|error| {
|
||||||
print_error(&format!("Error opening repository: {}", error));
|
print_error(&format!("Error opening repository: {}", error));
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
match repo.remove_worktree(&action_args.name, &worktree_dir, action_args.force)
|
match repo.remove_worktree(&action_args.name, &worktree_dir, action_args.force, &worktree_config)
|
||||||
{
|
{
|
||||||
Ok(_) => print_success(&format!("Worktree {} deleted", &action_args.name)),
|
Ok(_) => print_success(&format!("Worktree {} deleted", &action_args.name)),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@@ -191,6 +201,9 @@ fn main() {
|
|||||||
changes
|
changes
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
grm::WorktreeRemoveFailureReason::NotMerged(message) => {
|
||||||
|
print_warning(&message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
119
src/repo.rs
119
src/repo.rs
@@ -5,6 +5,8 @@ use git2::{Cred, RemoteCallbacks, Repository};
|
|||||||
|
|
||||||
use crate::output::*;
|
use crate::output::*;
|
||||||
|
|
||||||
|
const WORKTREE_CONFIG_FILE_NAME: &str = "grm.toml";
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum RemoteType {
|
pub enum RemoteType {
|
||||||
@@ -16,6 +18,7 @@ pub enum RemoteType {
|
|||||||
pub enum WorktreeRemoveFailureReason {
|
pub enum WorktreeRemoveFailureReason {
|
||||||
Changes(String),
|
Changes(String),
|
||||||
Error(String),
|
Error(String),
|
||||||
|
NotMerged(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum GitPushDefaultSetting {
|
pub enum GitPushDefaultSetting {
|
||||||
@@ -39,6 +42,37 @@ impl RepoError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct WorktreeRootConfig {
|
||||||
|
pub persistent_branches: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_worktree_root_config(worktree_root: &Path) -> Result<Option<WorktreeRootConfig>, String> {
|
||||||
|
let path = worktree_root.join(WORKTREE_CONFIG_FILE_NAME);
|
||||||
|
let content = match std::fs::read_to_string(&path) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
match e.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => return Ok(None),
|
||||||
|
_ => return Err(format!("Error reading configuration file \"{}\": {}", path.display(), e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let config: WorktreeRootConfig = match toml::from_str(&content) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!(
|
||||||
|
"Error parsing configuration file \"{}\": {}",
|
||||||
|
path.display(), e
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(config))
|
||||||
|
}
|
||||||
|
|
||||||
impl std::error::Error for RepoError {}
|
impl std::error::Error for RepoError {}
|
||||||
|
|
||||||
impl std::fmt::Display for RepoError {
|
impl std::fmt::Display for RepoError {
|
||||||
@@ -237,10 +271,9 @@ impl Repo {
|
|||||||
// name exists
|
// name exists
|
||||||
let branch = self
|
let branch = self
|
||||||
.find_local_branch(
|
.find_local_branch(
|
||||||
&head
|
head
|
||||||
.shorthand()
|
.shorthand()
|
||||||
.expect("Branch name is not valid utf-8")
|
.expect("Branch name is not valid utf-8"),
|
||||||
.to_string(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Ok(branch)
|
Ok(branch)
|
||||||
@@ -642,6 +675,7 @@ impl Repo {
|
|||||||
name: &str,
|
name: &str,
|
||||||
worktree_dir: &Path,
|
worktree_dir: &Path,
|
||||||
force: bool,
|
force: bool,
|
||||||
|
worktree_config: &Option<WorktreeRootConfig>,
|
||||||
) -> Result<(), WorktreeRemoveFailureReason> {
|
) -> Result<(), WorktreeRemoveFailureReason> {
|
||||||
if !worktree_dir.exists() {
|
if !worktree_dir.exists() {
|
||||||
return Err(WorktreeRemoveFailureReason::Error(format!(
|
return Err(WorktreeRemoveFailureReason::Error(format!(
|
||||||
@@ -684,25 +718,55 @@ impl Repo {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
match branch.upstream() {
|
let mut is_merged_into_persistent_branch = false;
|
||||||
Ok(remote_branch) => {
|
let mut has_persistent_branches = false;
|
||||||
let (ahead, behind) = worktree_repo
|
if let Some(config) = worktree_config {
|
||||||
.graph_ahead_behind(&branch, &remote_branch)
|
if let Some(branches) = &config.persistent_branches {
|
||||||
.unwrap();
|
has_persistent_branches = true;
|
||||||
|
for persistent_branch in branches {
|
||||||
|
let persistent_branch = worktree_repo
|
||||||
|
.find_local_branch(persistent_branch)
|
||||||
|
.map_err(WorktreeRemoveFailureReason::Error)?;
|
||||||
|
|
||||||
if (ahead, behind) != (0, 0) {
|
let (ahead, _behind) = worktree_repo
|
||||||
|
.graph_ahead_behind(&branch, &persistent_branch)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if ahead == 0 {
|
||||||
|
is_merged_into_persistent_branch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_persistent_branches && !is_merged_into_persistent_branch {
|
||||||
|
return Err(WorktreeRemoveFailureReason::NotMerged(format!(
|
||||||
|
"Branch {} is not merged into any persistent branches",
|
||||||
|
name
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_persistent_branches {
|
||||||
|
match branch.upstream() {
|
||||||
|
Ok(remote_branch) => {
|
||||||
|
let (ahead, behind) = worktree_repo
|
||||||
|
.graph_ahead_behind(&branch, &remote_branch)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if (ahead, behind) != (0, 0) {
|
||||||
|
return Err(WorktreeRemoveFailureReason::Changes(format!(
|
||||||
|
"Branch {} is not in line with remote branch",
|
||||||
|
name
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
return Err(WorktreeRemoveFailureReason::Changes(format!(
|
return Err(WorktreeRemoveFailureReason::Changes(format!(
|
||||||
"Branch {} is not in line with remote branch",
|
"No remote tracking branch for branch {} found",
|
||||||
name
|
name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
|
||||||
return Err(WorktreeRemoveFailureReason::Changes(format!(
|
|
||||||
"No remote tracking branch for branch {} found",
|
|
||||||
name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -737,13 +801,22 @@ impl Repo {
|
|||||||
.name()
|
.name()
|
||||||
.map_err(|error| format!("Failed getting default branch name: {}", error))?;
|
.map_err(|error| format!("Failed getting default branch name: {}", error))?;
|
||||||
|
|
||||||
|
let config = read_worktree_root_config(directory)?;
|
||||||
|
|
||||||
for worktree in worktrees
|
for worktree in worktrees
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|worktree| *worktree != &default_branch_name)
|
.filter(|worktree| *worktree != &default_branch_name)
|
||||||
|
.filter(|worktree| match &config {
|
||||||
|
None => true,
|
||||||
|
Some(config) => match &config.persistent_branches {
|
||||||
|
None => true,
|
||||||
|
Some(branches) => !branches.contains(worktree),
|
||||||
|
},
|
||||||
|
})
|
||||||
{
|
{
|
||||||
let repo_dir = &directory.join(&worktree);
|
let repo_dir = &directory.join(&worktree);
|
||||||
if repo_dir.exists() {
|
if repo_dir.exists() {
|
||||||
match self.remove_worktree(worktree, repo_dir, false) {
|
match self.remove_worktree(worktree, repo_dir, false, &config) {
|
||||||
Ok(_) => print_success(&format!("Worktree {} deleted", &worktree)),
|
Ok(_) => print_success(&format!("Worktree {} deleted", &worktree)),
|
||||||
Err(error) => match error {
|
Err(error) => match error {
|
||||||
WorktreeRemoveFailureReason::Changes(changes) => {
|
WorktreeRemoveFailureReason::Changes(changes) => {
|
||||||
@@ -753,6 +826,10 @@ impl Repo {
|
|||||||
));
|
));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
WorktreeRemoveFailureReason::NotMerged(message) => {
|
||||||
|
warnings.push(message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
WorktreeRemoveFailureReason::Error(error) => {
|
WorktreeRemoveFailureReason::Error(error) => {
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
@@ -773,14 +850,13 @@ impl Repo {
|
|||||||
let mut unmanaged_worktrees = Vec::new();
|
let mut unmanaged_worktrees = Vec::new();
|
||||||
for entry in std::fs::read_dir(&directory).map_err(|error| error.to_string())? {
|
for entry in std::fs::read_dir(&directory).map_err(|error| error.to_string())? {
|
||||||
let dirname = crate::path_as_string(
|
let dirname = crate::path_as_string(
|
||||||
&entry
|
entry
|
||||||
.map_err(|error| error.to_string())?
|
.map_err(|error| error.to_string())?
|
||||||
.path()
|
.path()
|
||||||
.strip_prefix(&directory)
|
.strip_prefix(&directory)
|
||||||
// that unwrap() is safe as each entry is
|
// that unwrap() is safe as each entry is
|
||||||
// guaranteed to be a subentry of &directory
|
// guaranteed to be a subentry of &directory
|
||||||
.unwrap()
|
.unwrap(),
|
||||||
.to_path_buf(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let default_branch = self
|
let default_branch = self
|
||||||
@@ -794,6 +870,9 @@ impl Repo {
|
|||||||
if dirname == crate::GIT_MAIN_WORKTREE_DIRECTORY {
|
if dirname == crate::GIT_MAIN_WORKTREE_DIRECTORY {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if dirname == WORKTREE_CONFIG_FILE_NAME {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if dirname == default_branch_name {
|
if dirname == default_branch_name {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/table.rs
15
src/table.rs
@@ -46,7 +46,7 @@ fn add_repo_status(
|
|||||||
}
|
}
|
||||||
None => String::from("\u{2714}"),
|
None => String::from("\u{2714}"),
|
||||||
},
|
},
|
||||||
&repo_status
|
repo_status
|
||||||
.branches
|
.branches
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(branch_name, remote_branch)| {
|
.map(|(branch_name, remote_branch)| {
|
||||||
@@ -73,8 +73,7 @@ fn add_repo_status(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
.trim()
|
.trim(),
|
||||||
.to_string(),
|
|
||||||
&match is_worktree {
|
&match is_worktree {
|
||||||
true => String::from(""),
|
true => String::from(""),
|
||||||
false => match repo_status.head {
|
false => match repo_status.head {
|
||||||
@@ -82,13 +81,12 @@ fn add_repo_status(
|
|||||||
None => String::from("Empty"),
|
None => String::from("Empty"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&repo_status
|
repo_status
|
||||||
.remotes
|
.remotes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| format!("{}\n", r))
|
.map(|r| format!("{}\n", r))
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
.trim()
|
.trim(),
|
||||||
.to_string(),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -127,14 +125,13 @@ pub fn get_worktree_status_table(
|
|||||||
}
|
}
|
||||||
for entry in std::fs::read_dir(&directory).map_err(|error| error.to_string())? {
|
for entry in std::fs::read_dir(&directory).map_err(|error| error.to_string())? {
|
||||||
let dirname = crate::path_as_string(
|
let dirname = crate::path_as_string(
|
||||||
&entry
|
entry
|
||||||
.map_err(|error| error.to_string())?
|
.map_err(|error| error.to_string())?
|
||||||
.path()
|
.path()
|
||||||
.strip_prefix(&directory)
|
.strip_prefix(&directory)
|
||||||
// this unwrap is safe, as we can be sure that each subentry of
|
// this unwrap is safe, as we can be sure that each subentry of
|
||||||
// &directory also has the prefix &dir
|
// &directory also has the prefix &dir
|
||||||
.unwrap()
|
.unwrap(),
|
||||||
.to_path_buf(),
|
|
||||||
);
|
);
|
||||||
if dirname == crate::GIT_MAIN_WORKTREE_DIRECTORY {
|
if dirname == crate::GIT_MAIN_WORKTREE_DIRECTORY {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user