diff --git a/docs/guide/create.md b/docs/guide/create.md
index 200be46aaa..73287a15ef 100644
--- a/docs/guide/create.md
+++ b/docs/guide/create.md
@@ -45,6 +45,8 @@ Run `vp create --list` to see the built-in templates and the common shorthand te
- `--directory
` writes the generated project into a specific target directory
- `--agent ` creates agent instructions files during scaffolding
- `--editor ` writes editor config files
+- `--git` initialize a git repository
+- `--no-git` skips git repository initialization
- `--hooks` enables pre-commit hook setup
- `--no-hooks` skips hook setup
- `--no-interactive` runs without prompts
diff --git a/packages/cli/snap-tests-global/command-create-help/snap.txt b/packages/cli/snap-tests-global/command-create-help/snap.txt
index ab69160242..5ff8f11dbb 100644
--- a/packages/cli/snap-tests-global/command-create-help/snap.txt
+++ b/packages/cli/snap-tests-global/command-create-help/snap.txt
@@ -17,6 +17,8 @@ Options:
--directory DIR Target directory for the generated project.
--agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.
--editor NAME Write editor config files for the specified editor.
+ --git Initialize a git repository with an initial commit
+ --no-git Skip git repository initialization
--hooks Set up pre-commit hooks (default in non-interactive mode)
--no-hooks Skip pre-commit hooks setup
--package-manager NAME Use specified package manager (pnpm, npm, yarn, bun)
@@ -78,6 +80,8 @@ Options:
--directory DIR Target directory for the generated project.
--agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.
--editor NAME Write editor config files for the specified editor.
+ --git Initialize a git repository with an initial commit
+ --no-git Skip git repository initialization
--hooks Set up pre-commit hooks (default in non-interactive mode)
--no-hooks Skip pre-commit hooks setup
--package-manager NAME Use specified package manager (pnpm, npm, yarn, bun)
@@ -139,6 +143,8 @@ Options:
--directory DIR Target directory for the generated project.
--agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.
--editor NAME Write editor config files for the specified editor.
+ --git Initialize a git repository with an initial commit
+ --no-git Skip git repository initialization
--hooks Set up pre-commit hooks (default in non-interactive mode)
--no-hooks Skip pre-commit hooks setup
--package-manager NAME Use specified package manager (pnpm, npm, yarn, bun)
diff --git a/packages/cli/snap-tests-global/new-check/snap.txt b/packages/cli/snap-tests-global/new-check/snap.txt
index 4602d3ef40..8266887b4b 100644
--- a/packages/cli/snap-tests-global/new-check/snap.txt
+++ b/packages/cli/snap-tests-global/new-check/snap.txt
@@ -17,6 +17,8 @@ Options:
--directory DIR Target directory for the generated project.
--agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.
--editor NAME Write editor config files for the specified editor.
+ --git Initialize a git repository with an initial commit
+ --no-git Skip git repository initialization
--hooks Set up pre-commit hooks (default in non-interactive mode)
--no-hooks Skip pre-commit hooks setup
--package-manager NAME Use specified package manager (pnpm, npm, yarn, bun)
diff --git a/packages/cli/snap-tests-global/new-vite-monorepo-bun/snap.txt b/packages/cli/snap-tests-global/new-vite-monorepo-bun/snap.txt
index 7ec03af6f9..0cd639acdc 100644
--- a/packages/cli/snap-tests-global/new-vite-monorepo-bun/snap.txt
+++ b/packages/cli/snap-tests-global/new-vite-monorepo-bun/snap.txt
@@ -1,4 +1,4 @@
-> vp create vite:monorepo --no-interactive --package-manager bun # create monorepo with bun
+> vp create vite:monorepo --no-interactive --package-manager bun --git # create monorepo with bun
> ls vite-plus-monorepo | LC_ALL=C sort # check files created
AGENTS.md
README.md
@@ -48,7 +48,7 @@ No pnpm-workspace.yaml
> test ! -f vite-plus-monorepo/.yarnrc.yml && echo 'No .yarnrc.yml' || echo 'ERROR: .yarnrc.yml exists' # verify no yarn config
No .yarnrc.yml
-> test -d vite-plus-monorepo/.git && echo 'Git initialized' || echo 'No git' # check git init
+> test -d vite-plus-monorepo/.git && echo 'Git initialized' # check git init
Git initialized
> ls vite-plus-monorepo/apps # check apps directory
diff --git a/packages/cli/snap-tests-global/new-vite-monorepo-bun/steps.json b/packages/cli/snap-tests-global/new-vite-monorepo-bun/steps.json
index 2a8f9e1280..699e751ba0 100644
--- a/packages/cli/snap-tests-global/new-vite-monorepo-bun/steps.json
+++ b/packages/cli/snap-tests-global/new-vite-monorepo-bun/steps.json
@@ -2,14 +2,14 @@
"ignoredPlatforms": ["win32"],
"commands": [
{
- "command": "vp create vite:monorepo --no-interactive --package-manager bun # create monorepo with bun",
+ "command": "vp create vite:monorepo --no-interactive --package-manager bun --git # create monorepo with bun",
"ignoreOutput": true
},
"ls vite-plus-monorepo | LC_ALL=C sort # check files created",
"cat vite-plus-monorepo/package.json # check package.json with catalog",
"test ! -f vite-plus-monorepo/pnpm-workspace.yaml && echo 'No pnpm-workspace.yaml' || echo 'ERROR: pnpm-workspace.yaml exists' # verify no pnpm config",
"test ! -f vite-plus-monorepo/.yarnrc.yml && echo 'No .yarnrc.yml' || echo 'ERROR: .yarnrc.yml exists' # verify no yarn config",
- "test -d vite-plus-monorepo/.git && echo 'Git initialized' || echo 'No git' # check git init",
+ "test -d vite-plus-monorepo/.git && echo 'Git initialized' # check git init",
"ls vite-plus-monorepo/apps # check apps directory",
"cat vite-plus-monorepo/apps/website/package.json # check website uses catalog:"
]
diff --git a/packages/cli/snap-tests-global/new-vite-monorepo/snap.txt b/packages/cli/snap-tests-global/new-vite-monorepo/snap.txt
index e0fb6cd576..b0c66b0574 100644
--- a/packages/cli/snap-tests-global/new-vite-monorepo/snap.txt
+++ b/packages/cli/snap-tests-global/new-vite-monorepo/snap.txt
@@ -1,4 +1,4 @@
-> vp create vite:monorepo --no-interactive # create monorepo with default values
+> vp create vite:monorepo --no-interactive --git # create monorepo with default values
> ls vite-plus-monorepo | LC_ALL=C sort # check files created
AGENTS.md
README.md
@@ -74,7 +74,7 @@ peerDependencyRules:
> test ! -f vite-plus-monorepo/.yarnrc.yml && echo 'No .yarnrc.yml' || echo 'ERROR: .yarnrc.yml exists' # verify no yarn config for pnpm
No .yarnrc.yml
-> test -d vite-plus-monorepo/.git && echo 'Git initialized' || echo 'No git' # check git init
+> test -d vite-plus-monorepo/.git && echo 'Git initialized' # check git init
Git initialized
> ls vite-plus-monorepo/apps # check apps directory created
diff --git a/packages/cli/snap-tests-global/new-vite-monorepo/steps.json b/packages/cli/snap-tests-global/new-vite-monorepo/steps.json
index 4212e84c7d..d6fbf85850 100644
--- a/packages/cli/snap-tests-global/new-vite-monorepo/steps.json
+++ b/packages/cli/snap-tests-global/new-vite-monorepo/steps.json
@@ -2,7 +2,7 @@
"ignoredPlatforms": ["win32"],
"commands": [
{
- "command": "vp create vite:monorepo --no-interactive # create monorepo with default values",
+ "command": "vp create vite:monorepo --no-interactive --git # create monorepo with default values",
"ignoreOutput": true
},
"ls vite-plus-monorepo | LC_ALL=C sort # check files created",
@@ -11,7 +11,7 @@
"cat vite-plus-monorepo/pnpm-workspace.yaml # check workspace config",
"test -f vite-plus-monorepo/.gitignore && echo '.gitignore exists' || echo 'ERROR: .gitignore missing' # verify gitignore renamed from _gitignore",
"test ! -f vite-plus-monorepo/.yarnrc.yml && echo 'No .yarnrc.yml' || echo 'ERROR: .yarnrc.yml exists' # verify no yarn config for pnpm",
- "test -d vite-plus-monorepo/.git && echo 'Git initialized' || echo 'No git' # check git init",
+ "test -d vite-plus-monorepo/.git && echo 'Git initialized' # check git init",
"ls vite-plus-monorepo/apps # check apps directory created",
"ls vite-plus-monorepo/apps/website/package.json # check website package.json",
{
diff --git a/packages/cli/snap-tests/create-org-bundled-monorepo/snap.txt b/packages/cli/snap-tests/create-org-bundled-monorepo/snap.txt
index 7c4209e2bd..2659dbb1b4 100644
--- a/packages/cli/snap-tests/create-org-bundled-monorepo/snap.txt
+++ b/packages/cli/snap-tests/create-org-bundled-monorepo/snap.txt
@@ -1,4 +1,4 @@
-> node $SNAP_CASES_DIR/.shared/mock-npm-registry.mjs -- vp create @your-org:workspace --no-interactive --directory my-mono # bundled monorepo: extract tarball, scaffold, inject create.defaultTemplate
+> node $SNAP_CASES_DIR/.shared/mock-npm-registry.mjs -- vp create @your-org:workspace --no-interactive --directory my-mono --git # bundled monorepo: extract tarball, scaffold, inject create.defaultTemplate
◇ Scaffolded my-mono
• Node pnpm
→ Next: cd my-mono && vp run
@@ -35,7 +35,7 @@ peerDependencyRules:
vite: "*"
vitest: "*"
-> test -d my-mono/.git && echo 'Git initialized' || echo 'No git' # git-init prompt covers bundled monorepo path
+> test -d my-mono/.git && echo 'Git initialized' # git-init prompt covers bundled monorepo path
Git initialized
> cat my-mono/.gitignore # node_modules excluded even though tarball shipped no .gitignore
diff --git a/packages/cli/snap-tests/create-org-bundled-monorepo/steps.json b/packages/cli/snap-tests/create-org-bundled-monorepo/steps.json
index c299dc0d00..03d4ad4cc0 100644
--- a/packages/cli/snap-tests/create-org-bundled-monorepo/steps.json
+++ b/packages/cli/snap-tests/create-org-bundled-monorepo/steps.json
@@ -1,9 +1,9 @@
{
"commands": [
- "node $SNAP_CASES_DIR/.shared/mock-npm-registry.mjs -- vp create @your-org:workspace --no-interactive --directory my-mono # bundled monorepo: extract tarball, scaffold, inject create.defaultTemplate",
+ "node $SNAP_CASES_DIR/.shared/mock-npm-registry.mjs -- vp create @your-org:workspace --no-interactive --directory my-mono --git # bundled monorepo: extract tarball, scaffold, inject create.defaultTemplate",
"cat my-mono/vite.config.ts # create.defaultTemplate auto-set to @your-org",
"cat my-mono/pnpm-workspace.yaml # workspace markers preserved",
- "test -d my-mono/.git && echo 'Git initialized' || echo 'No git' # git-init prompt covers bundled monorepo path",
+ "test -d my-mono/.git && echo 'Git initialized' # git-init prompt covers bundled monorepo path",
"cat my-mono/.gitignore # node_modules excluded even though tarball shipped no .gitignore"
]
}
diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts
index 2897ec7fe0..d9cb4d274b 100644
--- a/packages/cli/src/create/bin.ts
+++ b/packages/cli/src/create/bin.ts
@@ -29,6 +29,7 @@ import {
writeAgentInstructions,
} from '../utils/agent.ts';
import { detectExistingEditors, selectEditors, writeEditorConfigs } from '../utils/editor.ts';
+import { createInitialCommit, initGitRepository } from '../utils/git.ts';
import { renderCliDoc } from '../utils/help.ts';
import { displayRelative } from '../utils/path.ts';
import {
@@ -36,6 +37,7 @@ import {
defaultInteractive,
downloadPackageManager,
promptGitHooks,
+ promptGitInit,
runViteFmt,
runViteInstall,
selectPackageManager,
@@ -106,6 +108,8 @@ const helpMessage = renderCliDoc({
label: '--editor NAME',
description: 'Write editor config files for the specified editor.',
},
+ { label: '--git', description: 'Initialize a git repository with an initial commit' },
+ { label: '--no-git', description: 'Skip git repository initialization' },
{
label: '--hooks',
description: 'Set up pre-commit hooks (default in non-interactive mode)',
@@ -235,11 +239,12 @@ function parseArgs() {
verbose?: boolean;
agent?: string | string[] | false;
editor?: string;
+ git?: boolean;
hooks?: boolean;
'package-manager'?: string;
}>(viteArgs, {
alias: { h: 'help' },
- boolean: ['help', 'list', 'all', 'interactive', 'hooks', 'verbose'],
+ boolean: ['help', 'list', 'all', 'interactive', 'hooks', 'verbose', 'git'],
string: ['directory', 'agent', 'editor', 'package-manager'],
default: { interactive: defaultInteractive() },
});
@@ -256,6 +261,7 @@ function parseArgs() {
verbose: parsed.verbose || false,
agent: parsed.agent,
editor: parsed.editor,
+ git: parsed.git,
hooks: parsed.hooks,
packageManager: parsed['package-manager'],
} as Options,
@@ -747,6 +753,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
onCancel: () => cancelAndExit(),
}));
+ const shouldSetupGit = await promptGitInit(options);
if (!isMonorepo) {
shouldSetupHooks = await promptGitHooks(options);
}
@@ -827,7 +834,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
// #region Handle monorepo template
if (templateInfo.command === BuiltinTemplate.monorepo || isBundledMonorepo) {
// Ask up-front so the prompt isn't buried under scaffold output.
- let shouldInitGit = true;
+ let shouldInitGit = shouldSetupGit;
if (options.interactive && !compactOutput) {
pauseCreateProgress();
const selected = await prompts.confirm({
@@ -841,7 +848,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
} else {
shouldInitGit = selected;
}
- } else if (!compactOutput) {
+ } else if (shouldInitGit && !compactOutput) {
prompts.log.info('Initializing git repository (default: yes)');
}
@@ -902,6 +909,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
workspaceInfo.rootDir = fullPath;
updateCreateProgress('Integrating monorepo');
rewriteMonorepo(workspaceInfo, undefined, compactOutput);
+ if (shouldSetupGit) {
+ updateCreateProgress('Initializing git repository');
+ await initGitRepository(fullPath);
+ }
if (bundled?.monorepo) {
// Wire `create.defaultTemplate: ''` into the new workspace's
// vite.config.ts so a bare `vp create` from inside it opens the
@@ -922,6 +933,13 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
});
updateCreateProgress('Formatting code');
await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput });
+ if (shouldSetupGit) {
+ updateCreateProgress('Creating initial commit');
+ const committed = await createInitialCommit(fullPath);
+ if (!committed) {
+ prompts.log.warn('Initial commit failed. Check your git user.name/user.email config');
+ }
+ }
clearCreateProgress();
showCreateSummary({
description: describeScaffold(selectedTemplateName, selectedTemplateArgs),
@@ -1137,6 +1155,11 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
await runViteFmt(workspaceInfo.rootDir, options.interactive, [projectDir], {
silent: compactOutput,
});
+ if (shouldSetupGit) {
+ updateCreateProgress('Creating initial commit');
+ await initGitRepository(workspaceInfo.rootDir);
+ await createInitialCommit(workspaceInfo.rootDir);
+ }
} else {
if (shouldMigrateLintFmtTools) {
await installAndMigrate(fullPath);
@@ -1148,6 +1171,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
addFrameworkShim(fullPath, framework);
}
}
+ if (shouldSetupGit) {
+ updateCreateProgress('Initializing git repository');
+ await initGitRepository(fullPath);
+ }
if (shouldSetupHooks) {
installGitHooks(fullPath, compactOutput);
}
@@ -1159,6 +1186,13 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
});
updateCreateProgress('Formatting code');
await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput });
+ if (shouldSetupGit) {
+ updateCreateProgress('Creating initial commit');
+ const committed = await createInitialCommit(fullPath);
+ if (!committed) {
+ prompts.log.warn('Initial commit failed. Check your git user.name/user.email config');
+ }
+ }
}
clearCreateProgress();
diff --git a/packages/cli/src/utils/git.ts b/packages/cli/src/utils/git.ts
new file mode 100644
index 0000000000..d58de4c090
--- /dev/null
+++ b/packages/cli/src/utils/git.ts
@@ -0,0 +1,27 @@
+import { runCommandSilently } from './command.ts';
+
+export async function initGitRepository(cwd: string): Promise {
+ const result = await runCommandSilently({
+ command: 'git',
+ args: ['init'],
+ cwd,
+ envs: process.env,
+ });
+ return result.exitCode === 0;
+}
+
+export async function createInitialCommit(cwd: string): Promise {
+ await runCommandSilently({
+ command: 'git',
+ args: ['add', '-A'],
+ cwd,
+ envs: process.env,
+ });
+ const result = await runCommandSilently({
+ command: 'git',
+ args: ['commit', '-m', 'Initial commit from Vite+'],
+ cwd,
+ envs: process.env,
+ });
+ return result.exitCode === 0;
+}
diff --git a/packages/cli/src/utils/prompts.ts b/packages/cli/src/utils/prompts.ts
index 7f882b8ff8..9d14853d5a 100644
--- a/packages/cli/src/utils/prompts.ts
+++ b/packages/cli/src/utils/prompts.ts
@@ -215,6 +215,30 @@ export async function promptGitHooks(options: {
return true; // non-interactive default
}
+export async function promptGitInit(options: {
+ git?: boolean;
+ interactive: boolean;
+}): Promise {
+ if (options.git === false) {
+ return false;
+ }
+ if (options.git === true) {
+ return true;
+ }
+ if (options.interactive) {
+ const selected = await prompts.confirm({
+ message: 'Initialize a git repository with an initial commit?',
+ initialValue: false,
+ });
+ if (prompts.isCancel(selected)) {
+ cancelAndExit();
+ return false;
+ }
+ return selected;
+ }
+ return false; // non-interactive default
+}
+
export function defaultInteractive() {
// If CI environment, use non-interactive mode by default
return !process.env.CI && process.stdin.isTTY;