Compare commits
24 Commits
v0.7.2
...
474e0b60f9
| Author | SHA1 | Date | |
|---|---|---|---|
| 474e0b60f9 | |||
| 10af4d7448 | |||
| 94bfe971b3 | |||
| b77c442f56 | |||
| a3f9c9fda1 | |||
| 2a0a591194 | |||
| 23526ae62b | |||
| addff12c17 | |||
| c56765ce26 | |||
| d18c49982e | |||
| 58db521b5b | |||
| c21fb5813b | |||
| 33a5a1a262 | |||
| df8e69bce2 | |||
| 58fdcfba9f | |||
| 27ef86c1b4 | |||
| 9fc34e6989 | |||
| 4b79b6dd1d | |||
| d0cbc2f985 | |||
| d53e28668b | |||
| 0b8896d11d | |||
| 8c0c3ad169 | |||
| aebed5639d | |||
| 4514de9ff5 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: hakoerber
|
||||
@@ -21,7 +21,8 @@ If you want, add yourself to the `CONTRIBUTORS` file in your pull request.
|
||||
|
||||
For Rust, just use `cargo fmt`. For Python, use
|
||||
[black](https://github.com/psf/black). I'd rather not spend any effort in
|
||||
configuring the formatters (not possible for black anyway).
|
||||
configuring the formatters (not possible for black anyway). For shell scripts,
|
||||
use [`shfmt`](https://github.com/mvdan/sh).
|
||||
|
||||
## Tooling
|
||||
|
||||
@@ -41,6 +42,9 @@ When contributing, consider whether it makes sense to add tests which could
|
||||
prevent regressions in the future. When fixing bugs, it makes sense to add
|
||||
tests that expose the wrong behaviour beforehand.
|
||||
|
||||
To also ensure proper formatting and that the linter is happy, use `just check`.
|
||||
If that succeeds, your code is most likely fine to push!
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation lives in `docs` and uses
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
nonnominandus
|
||||
Maximilian Volk
|
||||
|
||||
66
Cargo.lock
generated
66
Cargo.lock
generated
@@ -80,16 +80,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
@@ -97,9 +97,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.1.18"
|
||||
version = "3.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
|
||||
checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
@@ -110,9 +110,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
@@ -155,12 +155,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
||||
checksum = "8ff1f980957787286a554052d03c7aee98d99cc32e09f6d45f0a814133c87978"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -321,18 +321,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git-repo-manager"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"comfy-table",
|
||||
@@ -388,9 +388,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb"
|
||||
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -410,9 +410,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.1"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
@@ -584,7 +584,7 @@ checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
@@ -1002,9 +1002,9 @@ checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.24.1"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9550962e7cf70d9980392878dfaf1dcc3ece024f4cf3bf3c46b978d0bad61d6c"
|
||||
checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -1015,9 +1015,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.95"
|
||||
version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1105,9 +1105,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.34"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
|
||||
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
@@ -1129,11 +1129,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.26"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
|
||||
checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1154,9 +1154,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -1212,12 +1212,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "git-repo-manager"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
edition = "2021"
|
||||
|
||||
authors = [
|
||||
@@ -28,7 +28,7 @@ rust-version = "1.57"
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
[profile.e2e-tests]
|
||||
inherits = "release"
|
||||
inherits = "dev"
|
||||
|
||||
[lib]
|
||||
name = "grm"
|
||||
@@ -54,7 +54,7 @@ version = "=0.14.4"
|
||||
version = "=2.1.0"
|
||||
|
||||
[dependencies.clap]
|
||||
version = "=3.1.18"
|
||||
version = "=3.2.5"
|
||||
features = ["derive", "cargo"]
|
||||
|
||||
[dependencies.console]
|
||||
|
||||
24
Justfile
24
Justfile
@@ -1,46 +1,54 @@
|
||||
set positional-arguments
|
||||
|
||||
target := "x86_64-unknown-linux-musl"
|
||||
static_target := "x86_64-unknown-linux-musl"
|
||||
|
||||
check: fmt-check lint test
|
||||
cargo check
|
||||
cargo fmt --check
|
||||
cargo clippy --no-deps -- -Dwarnings
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
git clean -f -d -X
|
||||
|
||||
fmt:
|
||||
cargo fmt
|
||||
git ls-files | grep '\.py$' | xargs black
|
||||
git ls-files | grep '\.sh$' | xargs -L 1 shfmt --indent 4 --write
|
||||
|
||||
fmt-check:
|
||||
cargo fmt --check
|
||||
git ls-files | grep '\.py$' | xargs black --check
|
||||
git ls-files | grep '\.sh$' | xargs -L 1 shfmt --indent 4 --diff
|
||||
|
||||
lint:
|
||||
cargo clippy --no-deps -- -Dwarnings
|
||||
git ls-files | grep '\.sh$' | xargs -L 1 shellcheck --norc
|
||||
|
||||
lint-fix:
|
||||
cargo clippy --no-deps --fix
|
||||
|
||||
release:
|
||||
cargo build --release --target {{target}}
|
||||
cargo build --release
|
||||
|
||||
release-static:
|
||||
cargo build --release --target {{static_target}} --features=static-build
|
||||
|
||||
test-binary:
|
||||
env \
|
||||
GITHUB_API_BASEURL=http://rest:5000/github \
|
||||
GITLAB_API_BASEURL=http://rest:5000/gitlab \
|
||||
cargo build --target {{target}} --profile e2e-tests --features=static-build
|
||||
cargo build --profile e2e-tests
|
||||
|
||||
install:
|
||||
cargo install --path .
|
||||
|
||||
install-static:
|
||||
cargo install --target {{target}} --features=static-build --path .
|
||||
cargo install --target {{static_target}} --features=static-build --path .
|
||||
|
||||
build:
|
||||
cargo build
|
||||
|
||||
build-static:
|
||||
cargo build --target {{target}} --features=static-build
|
||||
cargo build --target {{static_target}} --features=static-build
|
||||
|
||||
test: test-unit test-integration test-e2e
|
||||
|
||||
@@ -56,7 +64,7 @@ test-e2e +tests=".": test-binary
|
||||
&& docker-compose build \
|
||||
&& docker-compose run \
|
||||
--rm \
|
||||
-v $PWD/../target/{{target}}/e2e-tests/grm:/grm \
|
||||
-v $PWD/../target/e2e-tests/grm:/grm \
|
||||
pytest \
|
||||
"GRM_BINARY=/grm ALTERNATE_DOMAIN=alternate-rest python3 -m pytest -p no:cacheprovider --color=yes "$@"" \
|
||||
&& docker-compose rm --stop -f
|
||||
|
||||
@@ -248,6 +248,7 @@ def test_repos_find_remote_user_empty(
|
||||
@pytest.mark.parametrize("force_ssh", [True, False])
|
||||
@pytest.mark.parametrize("use_alternate_endpoint", [True, False])
|
||||
@pytest.mark.parametrize("use_config", [True, False])
|
||||
@pytest.mark.parametrize("override_remote_name", [True, False])
|
||||
def test_repos_find_remote_user(
|
||||
provider,
|
||||
configtype,
|
||||
@@ -258,6 +259,7 @@ def test_repos_find_remote_user(
|
||||
force_ssh,
|
||||
use_alternate_endpoint,
|
||||
use_config,
|
||||
override_remote_name,
|
||||
):
|
||||
if use_config:
|
||||
with tempfile.NamedTemporaryFile() as config:
|
||||
@@ -274,6 +276,8 @@ def test_repos_find_remote_user(
|
||||
cfg += f"worktree = {str(worktree).lower()}\n"
|
||||
if force_ssh:
|
||||
cfg += f"force_ssh = true\n"
|
||||
if override_remote_name:
|
||||
cfg += f'remote_name = "otherremote"\n'
|
||||
if use_owner:
|
||||
cfg += """
|
||||
[filters]
|
||||
@@ -310,6 +314,8 @@ def test_repos_find_remote_user(
|
||||
args += ["--user", "myuser1"]
|
||||
if force_ssh:
|
||||
args += ["--force-ssh"]
|
||||
if override_remote_name:
|
||||
args += ["--remote-name", "otherremote"]
|
||||
if not worktree_default:
|
||||
args += ["--worktree", str(worktree).lower()]
|
||||
if use_alternate_endpoint:
|
||||
@@ -350,7 +356,10 @@ def test_repos_find_remote_user(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
if override_remote_name:
|
||||
assert repo["remotes"][0]["name"] == "otherremote"
|
||||
else:
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh or i == 1:
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
@@ -535,14 +544,14 @@ def test_repos_find_remote_group(
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
if force_ssh or i == 1:
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
== f"ssh://git@example.com/mygroup1/myproject{i}.git"
|
||||
)
|
||||
assert repo["remotes"][0]["type"] == "ssh"
|
||||
else:
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
== f"https://example.com/mygroup1/myproject{i}.git"
|
||||
@@ -659,7 +668,7 @@ def test_repos_find_remote_user_and_group(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh or i == 1:
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
@@ -684,7 +693,7 @@ def test_repos_find_remote_user_and_group(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh or i == 1:
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
@@ -814,7 +823,7 @@ def test_repos_find_remote_owner(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh or i == 1:
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
@@ -837,7 +846,7 @@ def test_repos_find_remote_owner(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh or i == 1:
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
@@ -861,7 +870,7 @@ def test_repos_find_remote_owner(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh:
|
||||
assert (
|
||||
repo["remotes"][0]["url"] == f"ssh://git@example.com/myuser2/myproject3.git"
|
||||
@@ -890,7 +899,7 @@ def test_repos_find_remote_owner(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh or i == 1:
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
@@ -910,7 +919,7 @@ def test_repos_find_remote_owner(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh:
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
@@ -936,7 +945,7 @@ def test_repos_find_remote_owner(
|
||||
assert repo["worktree_setup"] is (not worktree_default and worktree)
|
||||
assert isinstance(repo["remotes"], list)
|
||||
assert len(repo["remotes"]) == 1
|
||||
assert repo["remotes"][0]["name"] == provider
|
||||
assert repo["remotes"][0]["name"] == "origin"
|
||||
if force_ssh:
|
||||
assert (
|
||||
repo["remotes"][0]["url"]
|
||||
|
||||
@@ -12,9 +12,18 @@ import os.path
|
||||
@pytest.mark.parametrize("has_config", [True, False])
|
||||
@pytest.mark.parametrize("has_default", [True, False])
|
||||
@pytest.mark.parametrize("has_prefix", [True, False])
|
||||
def test_worktree_add_simple(
|
||||
remote_branch_already_exists, has_config, has_default, has_prefix
|
||||
@pytest.mark.parametrize("worktree_with_slash", [True, False])
|
||||
def test_worktree_add(
|
||||
remote_branch_already_exists,
|
||||
has_config,
|
||||
has_default,
|
||||
has_prefix,
|
||||
worktree_with_slash,
|
||||
):
|
||||
if worktree_with_slash:
|
||||
worktree_name = "dir/test"
|
||||
else:
|
||||
worktree_name = "test"
|
||||
with TempGitRepositoryWorktree() as (base_dir, _commit):
|
||||
if has_config:
|
||||
with open(os.path.join(base_dir, "grm.toml"), "w") as f:
|
||||
@@ -42,54 +51,61 @@ def test_worktree_add_simple(
|
||||
touch change
|
||||
git add change
|
||||
git commit -m commit
|
||||
git push origin HEAD:test
|
||||
git push origin HEAD:{worktree_name}
|
||||
#git reset --hard 'HEAD@{1}'
|
||||
git branch -va
|
||||
)
|
||||
git --git-dir ./.git-main-working-tree worktree remove tmp
|
||||
"""
|
||||
)
|
||||
cmd = grm(["wt", "add", "test"], cwd=base_dir)
|
||||
cmd = grm(["wt", "add", worktree_name], cwd=base_dir)
|
||||
assert cmd.returncode == 0
|
||||
|
||||
files = os.listdir(base_dir)
|
||||
if has_config is True:
|
||||
assert len(files) == 3
|
||||
assert set(files) == {".git-main-working-tree", "grm.toml", "test"}
|
||||
if worktree_with_slash:
|
||||
assert set(files) == {".git-main-working-tree", "grm.toml", "dir"}
|
||||
assert set(os.listdir(os.path.join(base_dir, "dir"))) == {"test"}
|
||||
else:
|
||||
assert set(files) == {".git-main-working-tree", "grm.toml", "test"}
|
||||
else:
|
||||
assert len(files) == 2
|
||||
assert set(files) == {".git-main-working-tree", "test"}
|
||||
if worktree_with_slash:
|
||||
assert set(files) == {".git-main-working-tree", "dir"}
|
||||
assert set(os.listdir(os.path.join(base_dir, "dir"))) == {"test"}
|
||||
else:
|
||||
assert set(files) == {".git-main-working-tree", "test"}
|
||||
|
||||
repo = git.Repo(os.path.join(base_dir, "test"))
|
||||
repo = git.Repo(os.path.join(base_dir, worktree_name))
|
||||
assert not repo.bare
|
||||
assert not repo.is_dirty()
|
||||
if has_config and has_default:
|
||||
if has_prefix and not remote_branch_already_exists:
|
||||
assert (
|
||||
str(repo.active_branch.tracking_branch()) == "origin/myprefix/test"
|
||||
str(repo.active_branch.tracking_branch())
|
||||
== f"origin/myprefix/{worktree_name}"
|
||||
)
|
||||
else:
|
||||
assert str(repo.active_branch.tracking_branch()) == "origin/test"
|
||||
assert (
|
||||
str(repo.active_branch.tracking_branch())
|
||||
== f"origin/{worktree_name}"
|
||||
)
|
||||
else:
|
||||
assert repo.active_branch.tracking_branch() is None
|
||||
|
||||
|
||||
def test_worktree_add_into_subdirectory():
|
||||
def test_worktree_add_invalid_name():
|
||||
with TempGitRepositoryWorktree() as (base_dir, _commit):
|
||||
cmd = grm(["wt", "add", "dir/test"], cwd=base_dir)
|
||||
assert cmd.returncode == 0
|
||||
|
||||
files = os.listdir(base_dir)
|
||||
assert len(files) == 2
|
||||
assert set(files) == {".git-main-working-tree", "dir"}
|
||||
|
||||
files = os.listdir(os.path.join(base_dir, "dir"))
|
||||
assert set(files) == {"test"}
|
||||
|
||||
repo = git.Repo(os.path.join(base_dir, "dir", "test"))
|
||||
assert not repo.bare
|
||||
assert not repo.is_dirty()
|
||||
assert repo.active_branch.tracking_branch() is None
|
||||
for worktree_name in ["/absolute/path" "trailingslash/"]:
|
||||
args = ["wt", "add", worktree_name]
|
||||
cmd = grm(args, cwd=base_dir)
|
||||
assert cmd.returncode != 0
|
||||
print(cmd.stdout)
|
||||
print(cmd.stderr)
|
||||
assert not os.path.exists(worktree_name)
|
||||
assert not os.path.exists(os.path.join(base_dir, worktree_name))
|
||||
assert "invalid worktree name" in str(cmd.stderr.lower())
|
||||
|
||||
|
||||
def test_worktree_add_into_invalid_subdirectory():
|
||||
@@ -212,67 +228,6 @@ def test_worktree_add_with_explicit_no_tracking(
|
||||
assert repo.active_branch.tracking_branch() is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("remote_branch_already_exists", [True, False])
|
||||
@pytest.mark.parametrize("has_default", [True, False])
|
||||
@pytest.mark.parametrize("has_prefix", [True, False])
|
||||
def test_worktree_add_with_config(
|
||||
remote_branch_already_exists, has_default, has_prefix
|
||||
):
|
||||
with TempGitRepositoryWorktree() as (base_dir, _commit):
|
||||
with open(os.path.join(base_dir, "grm.toml"), "w") as f:
|
||||
f.write(
|
||||
f"""
|
||||
[track]
|
||||
default = {str(has_default).lower()}
|
||||
default_remote = "origin"
|
||||
"""
|
||||
)
|
||||
if has_prefix:
|
||||
f.write(
|
||||
"""
|
||||
default_remote_prefix = "myprefix"
|
||||
"""
|
||||
)
|
||||
if remote_branch_already_exists:
|
||||
shell(
|
||||
f"""
|
||||
cd {base_dir}
|
||||
git --git-dir ./.git-main-working-tree worktree add tmp
|
||||
(
|
||||
cd tmp
|
||||
touch change
|
||||
git add change
|
||||
git commit -m commit
|
||||
git push origin HEAD:test
|
||||
#git reset --hard 'HEAD@{1}'
|
||||
git branch -va
|
||||
)
|
||||
git --git-dir ./.git-main-working-tree worktree remove tmp
|
||||
"""
|
||||
)
|
||||
cmd = grm(["wt", "add", "test"], cwd=base_dir)
|
||||
print(cmd.stderr)
|
||||
assert cmd.returncode == 0
|
||||
|
||||
files = os.listdir(base_dir)
|
||||
assert len(files) == 3
|
||||
assert set(files) == {".git-main-working-tree", "grm.toml", "test"}
|
||||
|
||||
repo = git.Repo(os.path.join(base_dir, "test"))
|
||||
assert not repo.bare
|
||||
assert not repo.is_dirty()
|
||||
assert str(repo.active_branch) == "test"
|
||||
if has_default:
|
||||
if has_prefix and not remote_branch_already_exists:
|
||||
assert (
|
||||
str(repo.active_branch.tracking_branch()) == "origin/myprefix/test"
|
||||
)
|
||||
else:
|
||||
assert str(repo.active_branch.tracking_branch()) == "origin/test"
|
||||
else:
|
||||
assert repo.active_branch.tracking_branch() is None
|
||||
|
||||
|
||||
def test_worktree_delete():
|
||||
with TempGitRepositoryWorktree() as (base_dir, _commit):
|
||||
cmd = grm(["wt", "add", "test", "--track", "origin/test"], cwd=base_dir)
|
||||
|
||||
164
release.sh
Executable file
164
release.sh
Executable file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o nounset
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
usage() {
|
||||
printf '%s\n' "usage: $0 (master|minor|patch)" >&2
|
||||
}
|
||||
|
||||
if (($# != 1)); then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
current_version="$(grep '^version \?=' Cargo.toml | head -1 | cut -d '=' -f 2 | tr -d " '"'"')"
|
||||
|
||||
major="$(printf '%s' "${current_version}" | grep -oP '^\d+')"
|
||||
minor="$(printf '%s' "${current_version}" | grep -oP '\.\d+\.' | tr -d '.')"
|
||||
patch="$(printf '%s' "${current_version}" | grep -oP '\d+$' | tr -d '.')"
|
||||
|
||||
case "$1" in
|
||||
major)
|
||||
((major++)) || true
|
||||
minor=0
|
||||
patch=0
|
||||
;;
|
||||
minor)
|
||||
((minor++)) || true
|
||||
patch=0
|
||||
;;
|
||||
patch)
|
||||
((patch++)) || true
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
new_version="${major}.${minor}.${patch}"
|
||||
|
||||
if ! [[ "${new_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
printf '%s\n' 'Version has to a complete semver' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
current_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
if [[ "${current_branch}" != "develop" ]]; then
|
||||
printf '%s\n' 'You need to be on develop' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gitstatus="$(git status --porcelain)"
|
||||
if [[ -n "${gitstatus}" ]]; then
|
||||
printf '%s\n' 'There are uncommitted changes' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if git tag --list "v${new_version}" | grep -q .; then
|
||||
printf 'Tag %s already exists\n' "v${new_version}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for remote in $(git remote); do
|
||||
if git ls-remote --tags "${remote}" | grep -q "refs/tags/v${new_version}$"; then
|
||||
printf 'Tag %s already exists on %s' "v${new_version}" "${remote}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
git fetch --all
|
||||
|
||||
for remote in $(git remote); do
|
||||
for branch in master develop; do
|
||||
if ! git diff --quiet "${remote}/${branch}..${branch}"; then
|
||||
printf 'Remote branch %s/%s not up to date, synchronize first!\n' "${remote}" "${branch}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if ! git merge-base --is-ancestor master develop; then
|
||||
printf '%s\n' 'Develop is not a straight descendant of master, rebase!' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
changes="$(git log --oneline master..develop | wc -l)"
|
||||
if ((changes == 0)); then
|
||||
printf '%s\n' 'No changes between master and develop?' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
just update-dependencies
|
||||
|
||||
just check
|
||||
|
||||
sed -i "0,/^version/{s/^version.*$/version = \"${new_version}\"/}" Cargo.toml
|
||||
|
||||
cargo update --package git-repo-manager --precise "${new_version}"
|
||||
|
||||
diff="$(git diff --numstat)"
|
||||
if (($(printf '%s\n' "${diff}" | wc -l || true) != 2)); then
|
||||
printf '%s\n' 'Weird changes detected, bailing' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! printf '%s\n' "${diff}" | grep -Pq '^1\s+1\s+Cargo.lock$'; then
|
||||
printf '%s\n' 'Weird changes detected, bailing' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! printf '%s\n' "${diff}" | grep -Pq '^1\s+1\s+Cargo.toml$'; then
|
||||
printf '%s\n' 'Weird changes detected, bailing' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git add Cargo.lock Cargo.toml
|
||||
|
||||
git commit -m "Release v${new_version}"
|
||||
|
||||
git switch master 2>/dev/null || { [[ -d "../master" ]] && cd "../master"; } || {
|
||||
printf '%s\n' 'Could not change to master' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
current_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
if [[ "${current_branch}" != "master" ]]; then
|
||||
printf '%s\n' 'Looks like branch switching to master did not work' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git merge --no-ff --no-edit develop
|
||||
git tag "v${new_version}"
|
||||
|
||||
for remote in $(git remote); do
|
||||
while ! git push "${remote}" "v${new_version}" master; do
|
||||
:
|
||||
done
|
||||
done
|
||||
|
||||
git switch develop 2>/dev/null || { [[ -d "../develop" ]] && cd "../develop"; } || {
|
||||
printf '%s\n' 'Could not change to develop' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
current_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
if [[ "${current_branch}" != "develop" ]]; then
|
||||
printf '%s\n' 'Looks like branch switching to develop did not work' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git merge --ff-only master
|
||||
|
||||
for remote in $(git remote); do
|
||||
while ! git push "${remote}" develop; do
|
||||
:
|
||||
done
|
||||
done
|
||||
|
||||
cargo publish
|
||||
|
||||
printf 'Published %s successfully\n' "${new_version}"
|
||||
exit 0
|
||||
@@ -53,6 +53,8 @@ pub struct ConfigProvider {
|
||||
|
||||
pub worktree: Option<bool>,
|
||||
pub init_worktree: Option<bool>,
|
||||
|
||||
pub remote_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -192,6 +194,7 @@ impl Config {
|
||||
.get_repos(
|
||||
config.worktree.unwrap_or(false),
|
||||
config.force_ssh.unwrap_or(false),
|
||||
config.remote_name,
|
||||
)?
|
||||
}
|
||||
RemoteProvider::Gitlab => {
|
||||
@@ -205,6 +208,7 @@ impl Config {
|
||||
.get_repos(
|
||||
config.worktree.unwrap_or(false),
|
||||
config.force_ssh.unwrap_or(false),
|
||||
config.remote_name,
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,6 +103,9 @@ pub struct FindRemoteArgs {
|
||||
#[clap(arg_enum, short, long, help = "Remote provider to use")]
|
||||
pub provider: RemoteProvider,
|
||||
|
||||
#[clap(short, long, help = "Name of the remote to use")]
|
||||
pub remote_name: Option<String>,
|
||||
|
||||
#[clap(
|
||||
multiple_occurrences = true,
|
||||
name = "user",
|
||||
@@ -189,6 +192,9 @@ pub struct SyncRemoteArgs {
|
||||
#[clap(arg_enum, short, long, help = "Remote provider to use")]
|
||||
pub provider: RemoteProvider,
|
||||
|
||||
#[clap(short, long, help = "Name of the remote to use")]
|
||||
pub remote_name: Option<String>,
|
||||
|
||||
#[clap(
|
||||
multiple_occurrences = true,
|
||||
name = "user",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
@@ -64,7 +66,11 @@ fn main() {
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
.get_repos(worktree, args.force_ssh)
|
||||
.get_repos(
|
||||
worktree,
|
||||
args.force_ssh,
|
||||
args.remote_name,
|
||||
)
|
||||
}
|
||||
cmd::RemoteProvider::Gitlab => {
|
||||
match provider::Gitlab::new(filter, token, args.api_url) {
|
||||
@@ -74,7 +80,11 @@ fn main() {
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
.get_repos(worktree, args.force_ssh)
|
||||
.get_repos(
|
||||
worktree,
|
||||
args.force_ssh,
|
||||
args.remote_name,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -280,6 +290,7 @@ fn main() {
|
||||
.get_repos(
|
||||
config.worktree.unwrap_or(false),
|
||||
config.force_ssh.unwrap_or(false),
|
||||
config.remote_name,
|
||||
) {
|
||||
Ok(provider) => provider,
|
||||
Err(error) => {
|
||||
@@ -299,6 +310,7 @@ fn main() {
|
||||
.get_repos(
|
||||
config.worktree.unwrap_or(false),
|
||||
config.force_ssh.unwrap_or(false),
|
||||
config.remote_name,
|
||||
) {
|
||||
Ok(provider) => provider,
|
||||
Err(error) => {
|
||||
@@ -382,7 +394,11 @@ fn main() {
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
.get_repos(worktree, args.force_ssh)
|
||||
.get_repos(
|
||||
worktree,
|
||||
args.force_ssh,
|
||||
args.remote_name,
|
||||
)
|
||||
}
|
||||
cmd::RemoteProvider::Gitlab => {
|
||||
match provider::Gitlab::new(filter, token, args.api_url) {
|
||||
@@ -392,7 +408,11 @@ fn main() {
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
.get_repos(worktree, args.force_ssh)
|
||||
.get_repos(
|
||||
worktree,
|
||||
args.force_ssh,
|
||||
args.remote_name,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -484,25 +504,9 @@ fn main() {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut name: &str = &action_args.name;
|
||||
let subdirectory;
|
||||
let split = name.split_once('/');
|
||||
match split {
|
||||
None => subdirectory = None,
|
||||
Some(split) => {
|
||||
if split.0.is_empty() || split.1.is_empty() {
|
||||
print_error("Worktree name cannot start or end with a slash");
|
||||
process::exit(1);
|
||||
} else {
|
||||
(subdirectory, name) = (Some(Path::new(split.0)), split.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match worktree::add_worktree(
|
||||
&cwd,
|
||||
name,
|
||||
subdirectory,
|
||||
&action_args.name,
|
||||
track,
|
||||
action_args.no_track,
|
||||
) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![feature(io_error_more)]
|
||||
#![feature(const_option_ext)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ use super::JsonError;
|
||||
use super::Project;
|
||||
use super::Provider;
|
||||
|
||||
const PROVIDER_NAME: &str = "github";
|
||||
const ACCEPT_HEADER_JSON: &str = "application/vnd.github.v3+json";
|
||||
const GITHUB_API_BASEURL: &str =
|
||||
option_env!("GITHUB_API_BASEURL").unwrap_or("https://api.github.com");
|
||||
@@ -88,10 +87,6 @@ impl Provider for Github {
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
PROVIDER_NAME
|
||||
}
|
||||
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.filter
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use super::JsonError;
|
||||
use super::Project;
|
||||
use super::Provider;
|
||||
|
||||
const PROVIDER_NAME: &str = "gitlab";
|
||||
const ACCEPT_HEADER_JSON: &str = "application/json";
|
||||
const GITLAB_API_BASEURL: &str = option_env!("GITLAB_API_BASEURL").unwrap_or("https://gitlab.com");
|
||||
|
||||
@@ -105,10 +104,6 @@ impl Provider for Gitlab {
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
PROVIDER_NAME
|
||||
}
|
||||
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.filter
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ use super::repo;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
const DEFAULT_REMOTE_NAME: &str = "origin";
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, clap::ArgEnum, Clone)]
|
||||
pub enum RemoteProvider {
|
||||
#[serde(alias = "github", alias = "GitHub")]
|
||||
@@ -122,7 +124,6 @@ pub trait Provider {
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn name(&self) -> &str;
|
||||
fn filter(&self) -> &Filter;
|
||||
fn secret_token(&self) -> &auth::AuthToken;
|
||||
fn auth_header_key() -> &'static str;
|
||||
@@ -213,6 +214,7 @@ pub trait Provider {
|
||||
&self,
|
||||
worktree_setup: bool,
|
||||
force_ssh: bool,
|
||||
remote_name: Option<String>,
|
||||
) -> Result<HashMap<Option<String>, Vec<repo::Repo>>, String> {
|
||||
let mut repos = vec![];
|
||||
|
||||
@@ -292,10 +294,12 @@ pub trait Provider {
|
||||
|
||||
let mut ret: HashMap<Option<String>, Vec<repo::Repo>> = HashMap::new();
|
||||
|
||||
let remote_name = remote_name.unwrap_or_else(|| DEFAULT_REMOTE_NAME.to_string());
|
||||
|
||||
for repo in repos {
|
||||
let namespace = repo.namespace();
|
||||
|
||||
let mut repo = repo.into_repo_config(self.name(), worktree_setup, force_ssh);
|
||||
let mut repo = repo.into_repo_config(&remote_name, worktree_setup, force_ssh);
|
||||
|
||||
// Namespace is already part of the hashmap key. I'm not too happy
|
||||
// about the data exchange format here.
|
||||
|
||||
@@ -222,7 +222,7 @@ fn sync_repo(root_path: &Path, repo: &repo::Repo, init_worktree: bool) -> Result
|
||||
if newly_created && repo.worktree_setup && init_worktree {
|
||||
match repo_handle.default_branch() {
|
||||
Ok(branch) => {
|
||||
worktree::add_worktree(&repo_path, &branch.name()?, None, None, false)?;
|
||||
worktree::add_worktree(&repo_path, &branch.name()?, None, false)?;
|
||||
}
|
||||
Err(_error) => print_repo_error(
|
||||
&repo.name,
|
||||
|
||||
@@ -21,10 +21,17 @@ pub const GIT_MAIN_WORKTREE_DIRECTORY: &str = ".git-main-working-tree";
|
||||
pub fn add_worktree(
|
||||
directory: &Path,
|
||||
name: &str,
|
||||
subdirectory: Option<&Path>,
|
||||
track: Option<(&str, &str)>,
|
||||
no_track: bool,
|
||||
) -> Result<(), String> {
|
||||
// A branch name must never start or end with a slash. Everything else is ok.
|
||||
if name.starts_with('/') || name.ends_with('/') {
|
||||
return Err(format!(
|
||||
"Invalid worktree name: {}. It cannot start or end with a slash",
|
||||
name
|
||||
));
|
||||
}
|
||||
|
||||
let repo = repo::RepoHandle::open(directory, true).map_err(|error| match error.kind {
|
||||
repo::RepoErrorKind::NotFound => {
|
||||
String::from("Current directory does not contain a worktree setup")
|
||||
@@ -38,11 +45,6 @@ pub fn add_worktree(
|
||||
return Err(format!("Worktree {} already exists", &name));
|
||||
}
|
||||
|
||||
let path = match subdirectory {
|
||||
Some(dir) => directory.join(dir).join(name),
|
||||
None => directory.join(Path::new(name)),
|
||||
};
|
||||
|
||||
let mut remote_branch_exists = false;
|
||||
|
||||
let mut target_branch = match repo.find_local_branch(name) {
|
||||
@@ -193,10 +195,80 @@ pub fn add_worktree(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(subdirectory) = subdirectory {
|
||||
std::fs::create_dir_all(subdirectory).map_err(|error| error.to_string())?;
|
||||
// We have to create subdirectories first, otherwise adding the worktree
|
||||
// will fail
|
||||
if name.contains('/') {
|
||||
let path = Path::new(&name);
|
||||
if let Some(base) = path.parent() {
|
||||
// This is a workaround of a bug in libgit2 (?)
|
||||
//
|
||||
// When *not* doing this, we will receive an error from the `Repository::worktree()`
|
||||
// like this:
|
||||
//
|
||||
// > failed to make directory '/{repo}/.git-main-working-tree/worktrees/dir/test
|
||||
//
|
||||
// This is a discrepancy between the behaviour of libgit2 and the
|
||||
// git CLI when creating worktrees with slashes:
|
||||
//
|
||||
// The git CLI will create the worktree's configuration directory
|
||||
// inside {git_dir}/worktrees/{last_path_component}. Look at this:
|
||||
//
|
||||
// ```
|
||||
// $ git worktree add 1/2/3 -b 1/2/3
|
||||
// $ ls .git/worktrees
|
||||
// 3
|
||||
// ```
|
||||
//
|
||||
// Interesting: When adding a worktree with a different name but the
|
||||
// same final path component, git starts adding a counter suffix to
|
||||
// the worktree directories:
|
||||
//
|
||||
// ```
|
||||
// $ git worktree add 1/3/3 -b 1/3/3
|
||||
// $ git worktree add 1/4/3 -b 1/4/3
|
||||
// $ ls .git/worktrees
|
||||
// 3
|
||||
// 31
|
||||
// 32
|
||||
// ```
|
||||
//
|
||||
// I *guess* that the mapping back from the worktree directory under .git to the actual
|
||||
// worktree directory is done via the `gitdir` file inside `.git/worktrees/{worktree}.
|
||||
// This means that the actual directory would not matter. You can verify this by
|
||||
// just renaming it:
|
||||
//
|
||||
// ```
|
||||
// $ mv .git/worktrees/3 .git/worktrees/foobar
|
||||
// $ git worktree list
|
||||
// /tmp/ fcc8a2a7 [master]
|
||||
// /tmp/1/2/3 fcc8a2a7 [1/2/3]
|
||||
// /tmp/1/3/3 fcc8a2a7 [1/3/3]
|
||||
// /tmp/1/4/3 fcc8a2a7 [1/4/3]
|
||||
// ```
|
||||
//
|
||||
// => Still works
|
||||
//
|
||||
// Anyway, libgit2 does not do this: It tries to create the worktree
|
||||
// directory inside .git with the exact name of the worktree, including
|
||||
// any slashes. It should be this code:
|
||||
//
|
||||
// https://github.com/libgit2/libgit2/blob/f98dd5438f8d7bfd557b612fdf1605b1c3fb8eaf/src/libgit2/worktree.c#L346
|
||||
//
|
||||
// As a workaround, we can create the base directory manually for now.
|
||||
//
|
||||
// Tracking upstream issue: https://github.com/libgit2/libgit2/issues/6327
|
||||
std::fs::create_dir_all(
|
||||
directory
|
||||
.join(GIT_MAIN_WORKTREE_DIRECTORY)
|
||||
.join("worktrees")
|
||||
.join(base),
|
||||
)
|
||||
.map_err(|error| error.to_string())?;
|
||||
std::fs::create_dir_all(base).map_err(|error| error.to_string())?;
|
||||
}
|
||||
}
|
||||
repo.new_worktree(name, &path, &target_branch)?;
|
||||
|
||||
repo.new_worktree(name, &directory.join(&name), &target_branch)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user