mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 03:30:40 +00:00
fix(core): support older Git during repository initialization
Replace git init --initial-branch with git init followed by symbolic-ref HEAD refs/heads/main. This keeps new repositories on main without requiring Git 2.28 or newer. Also ensure checkpoint shadow repository setup uses its dedicated git config during the initial commit.
This commit is contained in:
parent
4bf5bf22de
commit
cd8d9dce6a
5 changed files with 77 additions and 7 deletions
14
packages/core/src/services/gitInit.ts
Normal file
14
packages/core/src/services/gitInit.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { SimpleGit } from 'simple-git';
|
||||
|
||||
export async function initRepositoryWithMainBranch(
|
||||
git: SimpleGit,
|
||||
): Promise<void> {
|
||||
await git.init(false);
|
||||
await git.raw(['symbolic-ref', 'HEAD', 'refs/heads/main']);
|
||||
}
|
||||
|
|
@ -162,12 +162,27 @@ describe('GitService', () => {
|
|||
expect(actualConfigContent).toBe(expectedConfigContent);
|
||||
});
|
||||
|
||||
it('should use the shadow git config during repository setup', async () => {
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
|
||||
expect(hoistedMockEnv).toHaveBeenCalledWith({
|
||||
HOME: repoDir,
|
||||
XDG_CONFIG_HOME: repoDir,
|
||||
});
|
||||
});
|
||||
|
||||
it('should initialize git repo in historyDir if not already initialized', async () => {
|
||||
hoistedMockCheckIsRepo.mockResolvedValue(false);
|
||||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
expect(hoistedMockSimpleGit).toHaveBeenCalledWith(repoDir);
|
||||
expect(hoistedMockInit).toHaveBeenCalled();
|
||||
expect(hoistedMockInit).toHaveBeenCalledWith(false);
|
||||
expect(hoistedMockRaw).toHaveBeenCalledWith([
|
||||
'symbolic-ref',
|
||||
'HEAD',
|
||||
'refs/heads/main',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should initialize git repo when root repo check throws', async () => {
|
||||
|
|
@ -184,6 +199,7 @@ describe('GitService', () => {
|
|||
const service = new GitService(projectRoot, storage);
|
||||
await service.setupShadowGitRepository();
|
||||
expect(hoistedMockInit).not.toHaveBeenCalled();
|
||||
expect(hoistedMockRaw).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should copy .gitignore from projectRoot if it exists', async () => {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import type { SimpleGit } from 'simple-git';
|
|||
import { simpleGit, CheckRepoActions } from 'simple-git';
|
||||
import type { Storage } from '../config/storage.js';
|
||||
import { isNodeError } from '../utils/errors.js';
|
||||
import { initRepositoryWithMainBranch } from './gitInit.js';
|
||||
|
||||
export class GitService {
|
||||
private projectRoot: string;
|
||||
|
|
@ -57,7 +58,11 @@ export class GitService {
|
|||
'[user]\n name = Qwen Code\n email = qwen-code@qwen.ai\n[commit]\n gpgsign = false\n';
|
||||
await fs.writeFile(gitConfigPath, gitConfigContent);
|
||||
|
||||
const repo = simpleGit(repoDir);
|
||||
const repo = simpleGit(repoDir).env({
|
||||
// Prevent git from using the user's global git config.
|
||||
HOME: repoDir,
|
||||
XDG_CONFIG_HOME: repoDir,
|
||||
});
|
||||
let isRepoDefined = false;
|
||||
try {
|
||||
isRepoDefined = await repo.checkIsRepo(CheckRepoActions.IS_REPO_ROOT);
|
||||
|
|
@ -68,10 +73,7 @@ export class GitService {
|
|||
}
|
||||
|
||||
if (!isRepoDefined) {
|
||||
await repo.init(false, {
|
||||
'--initial-branch': 'main',
|
||||
});
|
||||
|
||||
await initRepositoryWithMainBranch(repo);
|
||||
await repo.commit('Initial commit', { '--allow-empty': null });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,43 @@ describe('GitWorktreeService', () => {
|
|||
expect(hoistedMockCheckIsRepo).toHaveBeenNthCalledWith(2);
|
||||
});
|
||||
|
||||
it('initializeRepository should initialize a new repo on main', async () => {
|
||||
hoistedMockCheckIsRepo.mockResolvedValue(false);
|
||||
const service = new GitWorktreeService('/repo');
|
||||
|
||||
const result = await service.initializeRepository();
|
||||
|
||||
expect(result).toEqual({ initialized: true });
|
||||
expect(hoistedMockInit).toHaveBeenCalledWith(false);
|
||||
expect(hoistedMockRaw).toHaveBeenCalledWith([
|
||||
'symbolic-ref',
|
||||
'HEAD',
|
||||
'refs/heads/main',
|
||||
]);
|
||||
expect(hoistedMockAdd).toHaveBeenCalledWith('.');
|
||||
expect(hoistedMockCommit).toHaveBeenCalledWith('Initial commit', {
|
||||
'--allow-empty': null,
|
||||
});
|
||||
expect(hoistedMockInit.mock.invocationCallOrder[0]!).toBeLessThan(
|
||||
hoistedMockRaw.mock.invocationCallOrder[0]!,
|
||||
);
|
||||
expect(hoistedMockRaw.mock.invocationCallOrder[0]!).toBeLessThan(
|
||||
hoistedMockCommit.mock.invocationCallOrder[0]!,
|
||||
);
|
||||
});
|
||||
|
||||
it('initializeRepository should not update HEAD for an existing repo', async () => {
|
||||
hoistedMockCheckIsRepo.mockResolvedValue(true);
|
||||
const service = new GitWorktreeService('/repo');
|
||||
|
||||
const result = await service.initializeRepository();
|
||||
|
||||
expect(result).toEqual({ initialized: false });
|
||||
expect(hoistedMockInit).not.toHaveBeenCalled();
|
||||
expect(hoistedMockRaw).not.toHaveBeenCalled();
|
||||
expect(hoistedMockCommit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('createWorktree should create a sanitized branch and worktree path', async () => {
|
||||
const service = new GitWorktreeService('/repo');
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import type { SimpleGit } from 'simple-git';
|
|||
import { Storage } from '../config/storage.js';
|
||||
import { isCommandAvailable } from '../utils/shell-utils.js';
|
||||
import { isNodeError } from '../utils/errors.js';
|
||||
import { initRepositoryWithMainBranch } from './gitInit.js';
|
||||
|
||||
/**
|
||||
* Commit message used for the baseline snapshot in worktrees.
|
||||
|
|
@ -185,7 +186,7 @@ export class GitWorktreeService {
|
|||
}
|
||||
|
||||
try {
|
||||
await this.git.init(false, { '--initial-branch': 'main' });
|
||||
await initRepositoryWithMainBranch(this.git);
|
||||
|
||||
// Create initial commit so we can create worktrees
|
||||
await this.git.add('.');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue