Skip to content

fix(cli): preserve .git directory when scaffolding into a non-empty folder#3740

Open
itsjustriley wants to merge 2 commits intomainfrom
fix/cli-preserve-git-on-scaffold
Open

fix(cli): preserve .git directory when scaffolding into a non-empty folder#3740
itsjustriley wants to merge 2 commits intomainfrom
fix/cli-preserve-git-on-scaffold

Conversation

@itsjustriley
Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

Fixes dt#1201 · Closes #2989

Running npm create @shopify/hydrogen@latest in a directory that already contains a git repo wipes out the .git/ folder, taking the entire commit history with it. People hit this when they want to start with a blank repo + README and scaffold Hydrogen on top — a pretty common way to start a project.

The usual workaround is to mv .git ../tmp-git, scaffold, then move it back. That's fragile, not obvious, and nobody should have to think about it.

WHAT is this pull request doing?

When the CLI clears a non-empty target directory during scaffolding, it now skips .git/ (both directory and gitfile forms). Everything else is deleted as before.

Two small copy updates so users know what's happening:

  • Confirmation prompt: "The directory X is not empty. Continuing will delete its contents (your .git directory is kept). Continue?"
  • Declined-confirmation hint: "You may use --force or -f to delete its contents (.git is always preserved)."

A few deliberate scope choices:

  • No new flag. Preserving .git/ is the right default; an opt-in --preserve-git flag would just be surface area nobody sets correctly.
  • Only .git/ is carved out. .gitignore, .env, and .shopify/ are legitimate scaffold-owned files, so they're deleted as before.
  • Both @shopify/cli-hydrogen and @shopify/create-hydrogen bumped. Per CLAUDE.md, changes to the init command path require both packages to be bumped so new scaffolds pick up the fix.

HOW to test your changes?

Everything below runs in a throwaway $TMPDIR directory. Your real .git folders outside the sandbox are never touched.

Setup (once):

git fetch origin fix/cli-preserve-git-on-scaffold
git checkout fix/cli-preserve-git-on-scaffold
pnpm install
pnpm --filter @shopify/cli-hydrogen build
pnpm --filter @shopify/create-hydrogen build

export HYDROGEN_BIN="$PWD/packages/create-hydrogen/dist/create-app.js"
export SANDBOX=$(mktemp -d -t hydrogen-tophat.XXXXXX)

Scenario 1 — the bug (non-empty dir with existing .git):

cd "$SANDBOX" && mkdir repo-a && cd repo-a
git init -q
git config user.email you@example.com
git config user.name You
echo "pre-existing" > README.md
mkdir src && echo "// old" > src/app.js
git add -A && git commit -q -m "Pre-existing project"

# Answer YES at the prompt
node "$HYDROGEN_BIN" --path . --mock-shop --language ts \
  --styling tailwind --markets none --no-install-deps --no-shortcut

What to check:

  • Confirmation prompt reads "…your .git directory is kept".
  • .git/ still exists after the scaffold.
  • git log --oneline shows both your "Pre-existing project" commit and the new "Scaffold Storefront" commit on top.
  • src/app.js is gone (not part of the skeleton); README.md is now the skeleton's README.

Scenario 2 — decline the prompt:

cd "$SANDBOX" && mkdir repo-b && cd repo-b
git init -q
echo keepme > keepme.txt

# Answer NO at the prompt
node "$HYDROGEN_BIN" --path . --mock-shop --language ts \
  --styling tailwind --markets none --no-install-deps --no-shortcut

What to check:

  • The info message after declining includes ".git is always preserved".
  • keepme.txt and .git/ both survive.

Scenario 3 — --force path (no prompt, still preserves .git):

cd "$SANDBOX" && mkdir repo-c && cd repo-c
git init -q
git config user.email you@example.com
git config user.name You
echo existing > README.md
git add -A && git commit -q -m "Pre-existing (force)"
OLD=$(git rev-parse HEAD)

node "$HYDROGEN_BIN" --path . --force --mock-shop --language ts \
  --styling tailwind --markets none --no-install-deps --no-shortcut

git cat-file -e "$OLD" && echo "original commit still reachable ✓"

What to check:

  • No prompt appears.
  • .git/ exists.
  • Original commit hash is still reachable.

Cleanup:

rm -rf "$SANDBOX"
unset HYDROGEN_BIN SANDBOX

Empty directories and non-empty directories without .git/ are covered by automated tests in packages/cli/src/lib/onboarding/common.test.ts, so no need to tophat those manually.

Checklist

  • I've read the Contributing Guidelines
  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've added a changeset if this PR contains user-facing or functional changes. Test changes or internal-only config changes do not require a changeset.
  • I've added tests to cover my changes
  • I've added or updated the documentation

When scaffolding Hydrogen into a non-empty directory, the CLI now preserves
any existing .git directory or file (gitfile for submodule worktrees) instead
of deleting it along with other contents. This allows users to safely add a
Hydrogen storefront to an existing repository without losing version history.

The confirmation prompt has been updated to explicitly state that .git is
preserved while other contents are deleted.

Fixes dt#1201
Closes #2989
@itsjustriley itsjustriley force-pushed the fix/cli-preserve-git-on-scaffold branch from a1c4447 to 15e9e68 Compare April 21, 2026 13:53
@shopify
Copy link
Copy Markdown
Contributor

shopify Bot commented Apr 21, 2026

Oxygen deployed a preview of your fix/cli-preserve-git-on-scaffold branch. Details:

Storefront Status Preview link Deployment details Last update (UTC)
Skeleton (skeleton.hydrogen.shop) ✅ Successful (Logs) Preview deployment Inspect deployment April 22, 202610:12 AM

Learn more about Hydrogen's GitHub integration.

@itsjustriley itsjustriley marked this pull request as ready for review April 21, 2026 14:03
@itsjustriley itsjustriley requested a review from a team as a code owner April 21, 2026 14:03
Comment thread packages/cli/src/lib/onboarding/common.ts Outdated
Comment thread packages/cli/src/lib/onboarding/common.ts
Address review feedback from @andguy95:

1. **Conditional .git messaging**: The confirmation prompt only mentions
   ".git is kept" when .git actually exists in the directory. Previously,
   the message appeared even when scaffolding into directories without git.

2. **Abort handler gap**: The abort handler now uses clearDirectoryPreservingGit()
   instead of rmdir(force: true), ensuring .git is preserved even when
   scaffolding fails mid-process (network errors, Ctrl+C, etc.).

3. **.git-only directories**: Skip the confirmation prompt entirely when the
   directory contains only .git (nothing to delete). This avoids a misleading
   "delete contents" prompt when there are no contents to delete.

New helper functions:
- hasGitDirectory(): Check if .git exists (file or directory)
- hasOnlyGitDirectory(): Check if directory contains only .git

Tests added for all three behaviors.

Co-Authored-By: Claude Opus 4.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants