Skip to content

Commit 311a8bb

Browse files
committed
fix: add '--' separator to Git commands to disambiguate refs from paths
When a branch name matches a filename in the working directory, Git commands like `git checkout` and `git reset` become ambiguous about whether arguments are refs or paths. This causes git-branchless commands (like `git branchless amend`) to fail when operating on branches that have the same name as files in the current directory. This commit adds the standard Git '--' separator to all checkout and reset operations in the check_out module to explicitly separate refs from paths, preventing such ambiguity. Changes: - check_out.rs: Added '--' separator to 5 git command invocations: * git reset <target> (line 150) * git checkout <target> (line 159) * git reset --hard HEAD (line 261) * git checkout <oid> (line 274) * git reset <oid> (line 286) - test_amend.rs: * Updated all 32 test snapshots to include '--' in command output * Added new test test_amend_with_branch_name_matching_file() to verify the fix works when branch names match filenames The '--' separator is a standard Git convention that tells Git to treat everything before it as refs/options and everything after it as paths. This ensures unambiguous command parsing even when branch names and filenames collide. Example scenario this fixes: $ git checkout -b foo $ touch foo $ git branchless amend # Previously failed, now works
1 parent 113cc19 commit 311a8bb

File tree

15 files changed

+228
-171
lines changed

15 files changed

+228
-171
lines changed

git-branchless-lib/src/core/check_out.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ pub fn check_out_commit(
147147

148148
if *reset {
149149
if let Some(target) = &target {
150-
try_exit_code!(git_run_info.run(effects, Some(event_tx_id), &["reset", target])?);
150+
try_exit_code!(git_run_info.run(
151+
effects,
152+
Some(event_tx_id),
153+
&["reset", target, "--"]
154+
)?);
151155
}
152156
} else {
153157
let checkout_args = {
@@ -156,6 +160,7 @@ pub fn check_out_commit(
156160
args.push(OsStr::new(target.as_str()));
157161
}
158162
args.extend(additional_args.iter().map(OsStr::new));
163+
args.push(OsStr::new("--"));
159164
args
160165
};
161166
match git_run_info.run(effects, Some(event_tx_id), checkout_args.as_slice())? {
@@ -258,7 +263,11 @@ pub fn restore_snapshot(
258263
// Discard any working copy changes. The caller is responsible for having
259264
// snapshotted them if necessary.
260265
try_exit_code!(git_run_info
261-
.run(effects, Some(event_tx_id), &["reset", "--hard", "HEAD"])
266+
.run(
267+
effects,
268+
Some(event_tx_id),
269+
&["reset", "--hard", "HEAD", "--"]
270+
)
262271
.wrap_err("Discarding working copy changes")?);
263272

264273
// Check out the unstaged changes. Note that we don't call `git reset --hard
@@ -271,7 +280,11 @@ pub fn restore_snapshot(
271280
.run(
272281
effects,
273282
Some(event_tx_id),
274-
&["checkout", &snapshot.commit_unstaged.get_oid().to_string()],
283+
&[
284+
"checkout",
285+
&snapshot.commit_unstaged.get_oid().to_string(),
286+
"--"
287+
],
275288
)
276289
.wrap_err("Checking out unstaged changes (fail if conflict)")?);
277290

@@ -283,7 +296,7 @@ pub fn restore_snapshot(
283296
.run(
284297
effects,
285298
Some(event_tx_id),
286-
&["reset", &head_commit.get_oid().to_string()],
299+
&["reset", &head_commit.get_oid().to_string(), "--"],
287300
)
288301
.wrap_err("Update HEAD for unstaged changes")?);
289302
}

git-branchless-record/tests/test_record.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ fn test_record_stash() -> eyre::Result<()> {
299299
1 file changed, 1 insertion(+)
300300
create mode 100644 test1.txt
301301
branchless: running command: <git-executable> branch -f master f777ecc9b0db5ed372b2615695191a8a17f79f24
302-
branchless: running command: <git-executable> checkout master
302+
branchless: running command: <git-executable> checkout master --
303303
"###);
304304
}
305305

@@ -322,7 +322,7 @@ fn test_record_stash() -> eyre::Result<()> {
322322
[master 9b6164c] foo
323323
1 file changed, 1 insertion(+), 1 deletion(-)
324324
branchless: running command: <git-executable> branch -f master 62fc20d2a290daea0d52bdc2ed2ad4be6491010e
325-
branchless: running command: <git-executable> checkout master
325+
branchless: running command: <git-executable> checkout master --
326326
"###);
327327
}
328328

@@ -371,7 +371,7 @@ fn test_record_stash_detached_head() -> eyre::Result<()> {
371371
insta::assert_snapshot!(stdout, @r###"
372372
[detached HEAD 9b6164c] foo
373373
1 file changed, 1 insertion(+), 1 deletion(-)
374-
branchless: running command: <git-executable> checkout 62fc20d2a290daea0d52bdc2ed2ad4be6491010e
374+
branchless: running command: <git-executable> checkout 62fc20d2a290daea0d52bdc2ed2ad4be6491010e --
375375
"###);
376376
}
377377

@@ -408,7 +408,7 @@ fn test_record_stash_default_message() -> eyre::Result<()> {
408408
[master fd2ffa4] stash: test1.txt (+1/-1)
409409
1 file changed, 1 insertion(+), 1 deletion(-)
410410
branchless: running command: <git-executable> branch -f master 62fc20d2a290daea0d52bdc2ed2ad4be6491010e
411-
branchless: running command: <git-executable> checkout master
411+
branchless: running command: <git-executable> checkout master --
412412
"###);
413413
}
414414

@@ -435,7 +435,7 @@ fn test_record_create_branch() -> eyre::Result<()> {
435435
{
436436
let (stdout, _stderr) = git.branchless("record", &["-c", "foo", "-m", "Update"])?;
437437
insta::assert_snapshot!(stdout, @r###"
438-
branchless: running command: <git-executable> checkout master -b foo
438+
branchless: running command: <git-executable> checkout master -b foo --
439439
M test1.txt
440440
[foo 836023f] Update
441441
1 file changed, 1 insertion(+), 1 deletion(-)

git-branchless-submit/tests/test_github_forge.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ fn test_github_forge_mock_client_closes_pull_requests() -> eyre::Result<()> {
330330
Attempting rebase in-memory...
331331
[1/2] Skipped commit (was already applied upstream): 62fc20d create test1.txt
332332
[2/2] Committed as: fa46633 create test2.txt
333-
branchless: running command: <git-executable> checkout mock-github-username/create-test2-txt
333+
branchless: running command: <git-executable> checkout mock-github-username/create-test2-txt --
334334
Your branch and 'origin/mock-github-username/create-test2-txt' have diverged,
335335
and have 2 and 2 different commits each, respectively.
336336
In-memory rebase succeeded.

git-branchless-submit/tests/test_phabricator_forge.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fn test_submit_phabricator_strategy_working_copy() -> eyre::Result<()> {
6666
[1/2] Committed as: 55af3db create test1.txt
6767
[2/2] Committed as: ccb7fd5 create test2.txt
6868
branchless: processing 2 rewritten commits
69-
branchless: running command: <git-executable> checkout ccb7fd5d90c1888bea906a41c197e9215d6b9bb3
69+
branchless: running command: <git-executable> checkout ccb7fd5d90c1888bea906a41c197e9215d6b9bb3 --
7070
In-memory rebase succeeded.
7171
Setting D0002 as stack root (no dependencies)
7272
Stacking D0003 on top of D0002
@@ -144,7 +144,7 @@ fn test_submit_phabricator_strategy_worktree() -> eyre::Result<()> {
144144
[1/2] Committed as: 55af3db create test1.txt
145145
[2/2] Committed as: ccb7fd5 create test2.txt
146146
branchless: processing 2 rewritten commits
147-
branchless: running command: <git-executable> checkout ccb7fd5d90c1888bea906a41c197e9215d6b9bb3
147+
branchless: running command: <git-executable> checkout ccb7fd5d90c1888bea906a41c197e9215d6b9bb3 --
148148
In-memory rebase succeeded.
149149
Setting D0002 as stack root (no dependencies)
150150
Stacking D0003 on top of D0002
@@ -195,7 +195,7 @@ fn test_submit_phabricator_update() -> eyre::Result<()> {
195195
[1/2] Committed as: 55af3db create test1.txt
196196
[2/2] Committed as: ccb7fd5 create test2.txt
197197
branchless: processing 2 rewritten commits
198-
branchless: running command: <git-executable> checkout ccb7fd5d90c1888bea906a41c197e9215d6b9bb3
198+
branchless: running command: <git-executable> checkout ccb7fd5d90c1888bea906a41c197e9215d6b9bb3 --
199199
In-memory rebase succeeded.
200200
Setting D0002 as stack root (no dependencies)
201201
Stacking D0003 on top of D0002

git-branchless-test/tests/test_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,7 @@ done
867867
[2/3] Committed as: 2ee3aea create test2.txt
868868
[3/3] Committed as: 6f48e0a create test3.txt
869869
branchless: processing 3 rewritten commits
870-
branchless: running command: <git-executable> checkout 6f48e0a628753731739619f27107c57f5d0cc1e0
870+
branchless: running command: <git-executable> checkout 6f48e0a628753731739619f27107c57f5d0cc1e0 --
871871
In-memory rebase succeeded.
872872
Fixed 3 commits with bash test.sh:
873873
62fc20d -> 300cb54 create test1.txt
@@ -1130,7 +1130,7 @@ done
11301130
[1/2] Committed as: 300cb54 create test1.txt
11311131
[2/2] Committed as: f15b423 descendant commit
11321132
branchless: processing 2 rewritten commits
1133-
branchless: running command: <git-executable> checkout f15b423404bbebfe4b09e305e074b525d008f44a
1133+
branchless: running command: <git-executable> checkout f15b423404bbebfe4b09e305e074b525d008f44a --
11341134
In-memory rebase succeeded.
11351135
Fixed 2 commits with bash test.sh:
11361136
62fc20d -> 300cb54 create test1.txt

0 commit comments

Comments
 (0)