Skip to content

Commit 219a19a

Browse files
committed
Enables creating branches without remote tracking
Introduces a `noTracking` option to the `checkout` operation, allowing `git checkout -b --no-track` to be used. The `GitRepositoryService.switch` method now supports this option, which is leveraged by the `branch create --switch` command. When creating a local branch from a remote branch with a different name, the command will now use `--no-track` to prevent automatic setup of a remote tracking branch. (#5360, #5397)
1 parent 4e6b765 commit 219a19a

6 files changed

Lines changed: 44 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
3030

3131
### Fixed
3232

33+
- Fixes _Create & Switch to Branch_ from a remote ref incorrectly setting upstream tracking when the new branch name differs from the remote branch name — e.g. creating `feature/foo` from `origin/main` no longer makes it track `origin/main` ([#5360](https://github.com/gitkraken/vscode-gitlens/issues/5360))
3334
- Fixes _Keep Staged_ not keeping staged changes when stashing selected files — choosing _Keep Staged_ while stashing specific tracked files no longer drops the `--keep-index` flag, so staged changes are correctly kept intact ([#5281](https://github.com/gitkraken/vscode-gitlens/issues/5281))
3435
- Fixes pushing a branch that needs a force-push (e.g. after an amend or rebase) silently reporting success without updating the remote — a non-fast-forward (_tip of your current branch is behind_) rejection is now surfaced as an error instead of being swallowed as non-fatal ([#5364](https://github.com/gitkraken/vscode-gitlens/issues/5364))
3536
- Fixes _Fetch_, _Pull_, _Switch_, _Reset_, and _Restore_ operations silently reporting success when the underlying Git command failed with a message Git treats as a warning (e.g. an unreachable remote, or an invalid ref/revision) — these failures are now surfaced as errors instead of being swallowed as non-fatal

packages/git-cli/src/providers/__tests__/operations.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,33 @@ suite('OperationsGitSubProvider Test Suite', () => {
144144
);
145145
});
146146

147+
test('checkout with createBranch passes -b and ref', async () => {
148+
await operations.checkout(repoPath, 'origin/main', { createBranch: 'feature/foo' });
149+
150+
const call = (gitStub.run as sinon.SinonStub).getCalls().find(c => c.args.includes('checkout'));
151+
assert.ok(call, 'expected git.run to be invoked with checkout');
152+
const gitArgs = call.args.filter((a): a is string => typeof a === 'string');
153+
assert.deepStrictEqual(gitArgs, ['checkout', '-b', 'feature/foo', 'origin/main', '--']);
154+
});
155+
156+
test('checkout with createBranch and noTracking passes --no-track', async () => {
157+
await operations.checkout(repoPath, 'origin/main', { createBranch: 'feature/foo', noTracking: true });
158+
159+
const call = (gitStub.run as sinon.SinonStub).getCalls().find(c => c.args.includes('checkout'));
160+
assert.ok(call, 'expected git.run to be invoked with checkout');
161+
const gitArgs = call.args.filter((a): a is string => typeof a === 'string');
162+
assert.deepStrictEqual(gitArgs, ['checkout', '-b', 'feature/foo', '--no-track', 'origin/main', '--']);
163+
});
164+
165+
test('checkout with createBranch and noTracking=false omits --no-track', async () => {
166+
await operations.checkout(repoPath, 'origin/main', { createBranch: 'feature/foo', noTracking: false });
167+
168+
const call = (gitStub.run as sinon.SinonStub).getCalls().find(c => c.args.includes('checkout'));
169+
assert.ok(call, 'expected git.run to be invoked with checkout');
170+
const gitArgs = call.args.filter((a): a is string => typeof a === 'string');
171+
assert.deepStrictEqual(gitArgs, ['checkout', '-b', 'feature/foo', 'origin/main', '--']);
172+
});
173+
147174
test('checkout surfaces an invalid-ref failure as CheckoutError', async () => {
148175
// `unknownRevision` is a GitWarning, so without `errors: 'throw'` the default handler swallows
149176
// it and the checkout resolves as if it succeeded.

packages/git-cli/src/providers/operations.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,18 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
5252
async checkout(
5353
repoPath: string,
5454
ref: string,
55-
options?: { createBranch?: string },
55+
options?: { createBranch?: string; noTracking?: boolean },
5656
runOptions?: GitOperationRunOptions,
5757
): Promise<void> {
5858
const scope = getScopedLogger();
5959

6060
const params = ['checkout'];
6161
if (options?.createBranch) {
62-
params.push('-b', options.createBranch, ref, '--');
62+
params.push('-b', options.createBranch);
63+
if (options.noTracking) {
64+
params.push('--no-track');
65+
}
66+
params.push(ref, '--');
6367
} else {
6468
params.push(ref, '--');
6569
}

packages/git/src/providers/operations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface GitOperationsSubProvider {
2828
checkout(
2929
repoPath: string,
3030
ref: string,
31-
options?: { createBranch?: string | undefined },
31+
options?: { createBranch?: string | undefined; noTracking?: boolean },
3232
runOptions?: GitOperationRunOptions,
3333
): Promise<void>;
3434
cherryPick(

src/commands/git/branch/create.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,10 @@ export class BranchCreateGitCommand extends QuickCommand<State> {
263263
steps.markStepsComplete();
264264

265265
if (state.flags.includes('--switch')) {
266-
await state.repo.git.switch(state.reference.ref, { createBranch: state.name });
266+
await state.repo.git.switch(state.reference.ref, {
267+
createBranch: state.name,
268+
...(isRemoteBranch && state.name !== remoteBranchName ? { noTracking: true } : undefined),
269+
});
267270
} else {
268271
try {
269272
await state.repo.git.branches.createBranch?.(

src/git/gitRepositoryService.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,10 @@ export class GitRepositoryService {
513513

514514
@gate()
515515
@debug()
516-
async switch(ref: string, options?: { createBranch?: string | undefined; progress?: boolean }): Promise<void> {
516+
async switch(
517+
ref: string,
518+
options?: { createBranch?: string | undefined; noTracking?: boolean; progress?: boolean },
519+
): Promise<void> {
517520
const { progress, ...opts } = { progress: true, ...options };
518521
if (!progress) return this.switchCore(ref, opts);
519522

@@ -528,7 +531,7 @@ export class GitRepositoryService {
528531
);
529532
}
530533

531-
private async switchCore(ref: string, options?: { createBranch?: string }) {
534+
private async switchCore(ref: string, options?: { createBranch?: string; noTracking?: boolean }) {
532535
try {
533536
await this.ops?.checkout(ref, options);
534537
} catch (ex) {

0 commit comments

Comments
 (0)