Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

### Fixed

- 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))
- 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))
- 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))
- 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
Expand Down
27 changes: 27 additions & 0 deletions packages/git-cli/src/providers/__tests__/operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,33 @@ suite('OperationsGitSubProvider Test Suite', () => {
);
});

test('checkout with createBranch passes -b and ref', async () => {
await operations.checkout(repoPath, 'origin/main', { createBranch: 'feature/foo' });

const call = (gitStub.run as sinon.SinonStub).getCalls().find(c => c.args.includes('checkout'));
assert.ok(call, 'expected git.run to be invoked with checkout');
const gitArgs = call.args.filter((a): a is string => typeof a === 'string');
assert.deepStrictEqual(gitArgs, ['checkout', '-b', 'feature/foo', 'origin/main', '--']);
});

test('checkout with createBranch and noTracking passes --no-track', async () => {
await operations.checkout(repoPath, 'origin/main', { createBranch: 'feature/foo', noTracking: true });

const call = (gitStub.run as sinon.SinonStub).getCalls().find(c => c.args.includes('checkout'));
assert.ok(call, 'expected git.run to be invoked with checkout');
const gitArgs = call.args.filter((a): a is string => typeof a === 'string');
assert.deepStrictEqual(gitArgs, ['checkout', '-b', 'feature/foo', '--no-track', 'origin/main', '--']);
});

test('checkout with createBranch and noTracking=false omits --no-track', async () => {
await operations.checkout(repoPath, 'origin/main', { createBranch: 'feature/foo', noTracking: false });

const call = (gitStub.run as sinon.SinonStub).getCalls().find(c => c.args.includes('checkout'));
assert.ok(call, 'expected git.run to be invoked with checkout');
const gitArgs = call.args.filter((a): a is string => typeof a === 'string');
assert.deepStrictEqual(gitArgs, ['checkout', '-b', 'feature/foo', 'origin/main', '--']);
});

test('checkout surfaces an invalid-ref failure as CheckoutError', async () => {
// `unknownRevision` is a GitWarning, so without `errors: 'throw'` the default handler swallows
// it and the checkout resolves as if it succeeded.
Expand Down
8 changes: 6 additions & 2 deletions packages/git-cli/src/providers/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,18 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
async checkout(
repoPath: string,
ref: string,
options?: { createBranch?: string },
options?: { createBranch?: string; noTracking?: boolean },
runOptions?: GitOperationRunOptions,
): Promise<void> {
const scope = getScopedLogger();

const params = ['checkout'];
if (options?.createBranch) {
params.push('-b', options.createBranch, ref, '--');
params.push('-b', options.createBranch);
if (options.noTracking) {
params.push('--no-track');
}
params.push(ref, '--');
} else {
params.push(ref, '--');
}
Expand Down
2 changes: 1 addition & 1 deletion packages/git/src/providers/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface GitOperationsSubProvider {
checkout(
repoPath: string,
ref: string,
options?: { createBranch?: string | undefined },
options?: { createBranch?: string | undefined; noTracking?: boolean },
runOptions?: GitOperationRunOptions,
): Promise<void>;
cherryPick(
Expand Down
5 changes: 4 additions & 1 deletion src/commands/git/branch/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,10 @@ export class BranchCreateGitCommand extends QuickCommand<State> {
steps.markStepsComplete();

if (state.flags.includes('--switch')) {
await state.repo.git.switch(state.reference.ref, { createBranch: state.name });
await state.repo.git.switch(state.reference.ref, {
createBranch: state.name,
...(isRemoteBranch && state.name !== remoteBranchName ? { noTracking: true } : undefined),
});
} else {
try {
await state.repo.git.branches.createBranch?.(
Expand Down
7 changes: 5 additions & 2 deletions src/git/gitRepositoryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,10 @@ export class GitRepositoryService {

@gate()
@debug()
async switch(ref: string, options?: { createBranch?: string | undefined; progress?: boolean }): Promise<void> {
async switch(
ref: string,
options?: { createBranch?: string | undefined; noTracking?: boolean; progress?: boolean },
): Promise<void> {
const { progress, ...opts } = { progress: true, ...options };
if (!progress) return this.switchCore(ref, opts);

Expand All @@ -528,7 +531,7 @@ export class GitRepositoryService {
);
}

private async switchCore(ref: string, options?: { createBranch?: string }) {
private async switchCore(ref: string, options?: { createBranch?: string; noTracking?: boolean }) {
try {
await this.ops?.checkout(ref, options);
} catch (ex) {
Expand Down
Loading