Shopify themes live inside a zip file that gets uploaded to a store. For years, the standard workflow was to edit files locally, drag them into the admin, and hope nothing broke. That works fine when you're the only person touching the theme—but the moment a second developer joins, or you need to maintain a staging version alongside a live one, the "upload and pray" approach falls apart fast. Git—the version-control system used by virtually every professional software team—solves these problems by tracking every change, enabling you to work in parallel branches, and giving you a reliable way to roll back if something goes wrong.
This guide walks you through a practical Git workflow designed specifically for Shopify theme development. You don't need to be a Git expert—just comfortable with the command line and familiar with the basics of commits and branches.
Why Git matters for Shopify themes
A Shopify theme is a collection of Liquid templates, CSS/JavaScript assets, JSON configuration files, and locale strings. All of these are plain text, which means Git can track them perfectly. Without version control you have no history: if you break the navigation section at 2 PM and don't notice until 4 PM, you're left manually reverse-engineering what changed. With Git, you run git diff HEAD~1 and see exactly which lines changed, then git revert to undo it in seconds.
Version control also becomes essential for collaboration. Two developers editing sections/header.liquid at the same time will overwrite each other's work if they're both pushing directly to the live theme. Git's branching model lets each developer work independently on their own branch and only merge when their changes are tested and reviewed.
Finally, Git is the prerequisite for Shopify's built-in GitHub integration, which can automatically sync a branch in your repository to a theme in the store. Once that's set up, deploying is as simple as merging a pull request.
Recommended branch structure
A three-tier branch model works well for most Shopify theme projects:
- main — always represents the code that is live in your store. Only merge into main when code is ready for customers to see. Protect this branch in GitHub to prevent accidental direct pushes.
- staging — a stable integration branch. Features are merged here first. Shopify's GitHub integration syncs this branch to a "Staging" unpublished theme so the client or QA reviewer can preview changes without touching the live theme.
- feature/<name> — short-lived branches for individual changes. Examples:
feature/homepage-hero-redesign,fix/mobile-nav-overlap. Branch from staging, not main, so your feature is always built on top of the latest integrated work.
In prose, the flow looks like this: main is the tip of the live iceberg. staging floats just ahead of main and accumulates tested features. Feature branches branch off staging, get developed and reviewed, merge back into staging, and eventually staging is promoted into main for a release.
Connecting GitHub to Shopify
Shopify has a native GitHub integration that lives inside Online Store → Themes. Click "Add theme" → "Connect from GitHub", authorize the GitHub app, select your repository, and pick the branch that should sync to that theme. Whenever you push commits to that branch, Shopify automatically pulls the latest files into the theme—no manual upload required.
You can connect multiple themes to different branches. The recommended setup: connect main to your published live theme and staging to a separate unpublished "Staging" theme. When staging is approved, merging into main triggers an automatic deploy to the live theme.
The GitHub integration is great for deploys, but for active local development you still want Shopify CLI. Running shopify theme dev starts a local development server that hot-reloads your theme changes against a real store—without pushing to the published theme. Use shopify theme push when you want to manually sync files to a specific unpublished theme. The rule of thumb: CLI for development speed, GitHub integration for automated deploys.
Daily workflow
Here is what a typical development day looks like with this workflow:
- Pull the latest staging. Run
git checkout staging && git pull origin stagingto make sure you're starting from the latest shared baseline. - Create a feature branch.
git checkout -b feature/product-page-redesign - Start the dev server. Run
shopify theme dev --store your-store.myshopify.com(or the alias you've set in your Shopify CLI config). The CLI creates a temporary dev theme—it does NOT modify the live or staging theme. - Edit and commit often. Small, frequent commits (e.g., "Add sticky header to sections/header.liquid") make the history readable and diffs reviewable.
- Open a pull request to staging. Push your branch to GitHub and open a PR targeting
staging. A teammate or you-from-tomorrow reviews the diff. - Preview on staging theme. Once the PR is merged into staging, the GitHub integration pushes changes to the unpublished Staging theme automatically. Share the preview link with the client or QA.
- Merge staging into main to release. Open a second PR from staging to main. Merge it, and the GitHub integration deploys immediately to the live published theme.
Handling collisions with merchants editing in the theme editor
Here is the scenario that causes the most headaches: your client opens the Shopify Theme Editor (the visual drag-and-drop customizer) and moves a section around or changes a font color. Those changes go directly into config/settings_data.json inside the live theme in Shopify's servers—not in your Git repository. When you next deploy from Git, your old settings_data.json overwrites their edits. This is a very real pain point.
The cleanest solution is to add config/settings_data.json to your .shopifyignore so CLI pushes never touch it. If you need to version settings_data.json (for example, to snapshot the initial theme configuration before client goes live), do a one-time shopify theme pull to grab the live file, commit it, then add it to .shopifyignore for subsequent pushes.
A second, deeper mitigation is a clear client communication policy: agree on a "code freeze" window before deploys so merchants don't make theme-editor changes while a deployment is in flight. This won't always be possible, but documenting the risk prevents the surprise.
When to break this workflow
This three-branch setup is a professional standard, but it can be overkill in some situations. If you're a solo developer building a small informational store with no ongoing development after launch, a single main branch plus feature branches is perfectly fine. You can skip the staging branch until you have a second person on the project or a client who needs to preview before approval.
Similarly, if the store is running a theme you purchased from the Theme Store and you're only making minor CSS tweaks, you might use a single feature branch per change and merge directly to main after a quick visual check. The key insight is: the workflow should match the complexity of the project. Start simple, add the staging layer when you actually feel the pain of not having it.
What you should never skip, regardless of project size: a .gitignore that excludes node_modules/ and any .env files, and meaningful commit messages that explain why you made a change, not just what file you edited.
What to put in your .gitignore for Shopify themes
A Shopify theme repository typically contains the following directories: layout/ (base Liquid templates like theme.liquid), templates/ (page-type templates like product.json and collection.json), sections/ (reusable page sections with schema), snippets/ (small reusable Liquid partials), assets/ (CSS, JavaScript, image files), config/ (settings_schema.json and settings_data.json), and locales/ (translation strings). All of these belong in Git—except for a few files you may want to exclude.
# Shopify theme .gitignore
# Node dependencies (if you use a build tool like webpack or esbuild)
node_modules/
# Environment / secrets
.env
.env.local
# OS files
.DS_Store
Thumbs.db
# Editor directories
.vscode/
.idea/
# Optional: exclude settings_data.json if merchants
# actively customize the store via the Theme Editor
# config/settings_data.jsonThe decision about config/settings_data.json deserves extra thought. This file stores all of the Theme Editor customizations: which sections are on which pages, their order, color scheme selections, font choices, and content. If you track it in Git, you can snapshot the editor state and restore it if something goes wrong. If you exclude it, merchant edits made in the Theme Editor are never overwritten by a deploy. Neither approach is universally correct—pick the one that matches how actively your client uses the Theme Editor.
Frequently asked questions
- Do I need to commit config/settings_data.json?
It depends. Committing settings_data.json gives you a snapshot of the theme editor state, which is useful at the start of a project. But if merchants are actively customizing the store via the theme editor, keeping it in Git leads to painful merge conflicts and the risk of overwriting their changes on deploy. The safest long-term approach is to add it to .shopifyignore after the initial snapshot.
- Can I use Shopify CLI with this workflow?
Yes—they complement each other. Use shopify theme dev for local development (it uses a temporary dev theme and never touches your published or staging themes). Use shopify theme push when you need to manually sync a specific branch to a specific theme. The GitHub integration handles automated deploys on merge.
- What if I don't use GitHub—can I use GitLab or Bitbucket?
Shopify's native integration only supports GitHub. For GitLab or Bitbucket, you'll need to set up a CI/CD pipeline that runs shopify theme push on merge. Tools like GitLab CI and Bitbucket Pipelines both support this pattern with a short YAML workflow file and a Shopify CLI step.
