{"id":2127,"date":"2026-02-16T09:47:29","date_gmt":"2026-02-15T20:47:29","guid":{"rendered":"https:\/\/www.ronella.xyz\/?p=2127"},"modified":"2026-02-16T09:47:29","modified_gmt":"2026-02-15T20:47:29","slug":"a-practical-workflow-for-git-worktrees-in-everyday-development","status":"publish","type":"post","link":"https:\/\/www.ronella.xyz\/?p=2127","title":{"rendered":"A Practical Workflow for Git Worktrees in Everyday Development"},"content":{"rendered":"<p>Git worktrees provide a clean, efficient way to work on multiple branches at once without juggling stashes, temporary commits, or extra clones. This article walks through a concrete workflow for adopting <code>git worktree<\/code> in everyday development.<\/p>\n<hr \/>\n<h2>Why git worktree?<\/h2>\n<p>In a typical Git workflow, switching branches while you have uncommitted work forces you to choose between stashing, WIP commits, or creating another clone of the repository. Git worktrees give you another option: multiple checked\u2011out branches from the same repository, each in its own directory, sharing a single <code>.git<\/code> data store.<\/p>\n<p>This is especially useful when:<\/p>\n<ul>\n<li>You frequently interrupt a feature for hotfixes, reviews, or experiments.<\/li>\n<li>Your project has heavy dependencies (node modules, large virtualenvs, Gradle caches), making multiple clones expensive.<\/li>\n<li>You want to run tests or builds for several branches in parallel on the same machine.<\/li>\n<\/ul>\n<hr \/>\n<h2>One\u2011time setup and conventions<\/h2>\n<p>Start from an existing clone of your repository, with <code>main<\/code> (or <code>develop<\/code>) checked out:<\/p>\n<pre><code class=\"language-bash\">git worktree list<\/code><\/pre>\n<p>Initially, you should see only the main worktree (the directory you\u2019re in), showing its path, commit, and branch.<\/p>\n<p>Choose a consistent convention for worktree locations, such as:<\/p>\n<ul>\n<li>A <code>.worktrees\/<\/code> directory inside the main repo (e.g. <code>.worktrees\/feature-new-ui<\/code>).<\/li>\n<li>Or sibling directories (e.g. <code>..\/project-feature-new-ui<\/code>).<\/li>\n<\/ul>\n<p>The important part is that each worktree has a unique, meaningful path and that you avoid nesting one Git worktree inside another, which can confuse Git\u2019s metadata.<\/p>\n<hr \/>\n<h2>Creating a worktree for an existing branch<\/h2>\n<p>Suppose your remote has a branch <code>feature\/new-ui<\/code> that you want to work on without leaving <code>main<\/code> in your primary directory.<\/p>\n<p>From the main repo directory:<\/p>\n<pre><code class=\"language-bash\">git fetch origin\ngit worktree add .worktrees\/new-ui feature\/new-ui\ncd .worktrees\/new-ui<\/code><\/pre>\n<p>Key points:<\/p>\n<ul>\n<li><code>git worktree add &lt;path&gt; &lt;branch&gt;<\/code> creates a new directory at <code>&lt;path&gt;<\/code> and checks out <code>&lt;branch&gt;<\/code> there.<\/li>\n<li>The new directory behaves like a normal working copy: you edit files, run tests, commit, and push as usual.<\/li>\n<\/ul>\n<p>Your typical flow inside that worktree looks like:<\/p>\n<pre><code class=\"language-bash\"># already in .worktrees\/new-ui\ngit status\n# edit files, run tests\ngit commit -am &quot;Implement new UI&quot;\ngit push -u origin feature\/new-ui<\/code><\/pre>\n<p>When you\u2019re done for now, you can simply <code>cd<\/code> back to the main directory, which still has your original branch and working state untouched.<\/p>\n<hr \/>\n<h2>Starting a new feature in its own worktree<\/h2>\n<p>Very often, the branch doesn\u2019t exist yet; you want to create it and work in a dedicated directory from the start.<\/p>\n<p>From the main repo directory:<\/p>\n<pre><code class=\"language-bash\">git fetch origin\ngit worktree add -b feature\/new-api .worktrees\/new-api origin\/main\ncd .worktrees\/new-api<\/code><\/pre>\n<p>Here:<\/p>\n<ul>\n<li><code>-b feature\/new-api<\/code> tells Git to create <code>feature\/new-api<\/code> as a new branch.<\/li>\n<li><code>origin\/main<\/code> is the base commit; you can use <code>main<\/code>, <code>develop<\/code>, or any other starting point appropriate to your branching model.<\/li>\n<\/ul>\n<p>Now you can develop the feature completely within <code>.worktrees\/new-api<\/code>, while the main directory remains on <code>main<\/code> for reviews, builds, or other work.<\/p>\n<hr \/>\n<h2>Managing multiple active worktrees<\/h2>\n<p>Over time, you might accumulate several active worktrees: a couple of feature branches, a long\u2011running refactor, and maybe a release branch.<\/p>\n<p>To see what\u2019s active:<\/p>\n<pre><code class=\"language-bash\">git worktree list<\/code><\/pre>\n<p>The output lists each worktree\u2019s path, current commit, and checked\u2011out branch, with the main worktree first. For example:<\/p>\n<pre><code class=\"language-bash\">\/home\/user\/project                     a1b2c3d [main]\n\/home\/user\/project\/.worktrees\/new-ui   d4e5f6 [feature\/new-ui]\n\/home\/user\/project\/.worktrees\/new-api  987654 [feature\/new-api]<\/code><\/pre>\n<p>With this view you can:<\/p>\n<ul>\n<li>Jump between directories instead of switching branches in a single directory.<\/li>\n<li>Keep long\u2011running work (like big refactors) open and test them periodically without disturbing your day\u2011to\u2011day branch.<\/li>\n<li>Run multiple test suites or build processes in parallel on different branches.<\/li>\n<\/ul>\n<p>Each worktree is a self\u2011contained environment for that branch; there is no \u201cone worktree, many branches\u201d mode\u2014every worktree corresponds to a single branch or detached HEAD at a time.<\/p>\n<hr \/>\n<h2>Handling urgent hotfixes and reviews<\/h2>\n<p>A classic use case: you\u2019re mid\u2011feature when a production incident appears.<\/p>\n<p>Instead of stashing or committing half\u2011baked work:<\/p>\n<ol>\n<li>\n<p>Leave your feature worktree as is.<\/p>\n<\/li>\n<li>\n<p>From the main repo directory, create a hotfix worktree:<\/p>\n<pre><code class=\"language-bash\">git fetch origin\ngit worktree add .worktrees\/hotfix-critical hotfix\/critical-bug\ncd .worktrees\/hotfix-critical<\/code><\/pre>\n<\/li>\n<li>\n<p>Apply the fix, commit, and push:<\/p>\n<pre><code class=\"language-bash\"># implement fix\ngit commit -am \"Fix critical bug in production\"\ngit push origin hotfix\/critical-bug<\/code><\/pre>\n<\/li>\n<li>\n<p>Once the hotfix is merged back into <code>main<\/code> and any release branches, you can remove this worktree (see next section).<\/p>\n<\/li>\n<\/ol>\n<p>You can use the same pattern for:<\/p>\n<ul>\n<li>Checking out a PR branch to test it locally.<\/li>\n<li>Pairing on a branch without touching your current environment.<\/li>\n<li>Running experiments on a throwaway branch in a dedicated directory.<\/li>\n<\/ul>\n<hr \/>\n<h2>Cleaning up: remove and prune<\/h2>\n<p>Worktrees are cheap, but they will accumulate if you never remove them.<\/p>\n<p>Once a branch is merged and you no longer need its dedicated directory:<\/p>\n<pre><code class=\"language-bash\"># from the main repo directory (or any worktree in the same repo)\ngit worktree remove .worktrees\/new-ui<\/code><\/pre>\n<p>Important details:<\/p>\n<ul>\n<li><code>git worktree remove &lt;path&gt;<\/code> removes the worktree directory and its administrative entry; it does not necessarily delete the Git branch itself.<\/li>\n<li>The worktree must be clean (no untracked or modified tracked files) unless you add <code>--force<\/code>, which will discard uncommitted changes.<\/li>\n<\/ul>\n<p>Over time you may manually delete directories or encounter stale entries (e.g. after a crash). To clean up those leftovers:<\/p>\n<pre><code class=\"language-bash\">git worktree prune --verbose<\/code><\/pre>\n<p>This command prunes worktree records whose directories no longer exist, using expiration rules that can be configured via Git settings like <code>gc.worktreePruneExpire<\/code>. You can also use <code>--expire &lt;time&gt;<\/code> with <code>prune<\/code> if you want to only remove older, unused entries.<\/p>\n<p>A light maintenance habit is:<\/p>\n<ul>\n<li>Remove the worktree for a feature once its PR is merged and the branch is closed.<\/li>\n<li>Run <code>git worktree prune<\/code> occasionally to clean up stale metadata.<\/li>\n<\/ul>\n<hr \/>\n<h2>Practical guidelines and best practices<\/h2>\n<p>To make <code>git worktree<\/code> a reliable part of your team\u2019s workflow, adopt a few simple rules:<\/p>\n<ul>\n<li><strong>Organize worktrees predictably<\/strong>: Use stable directory patterns (<code>.worktrees\/&lt;branch-name&gt;<\/code> or similar) and use names that reflect the branch, like <code>.worktrees\/feature-auth-api<\/code>.<\/li>\n<li><strong>Avoid nesting<\/strong>: Never create a worktree inside another worktree\u2019s directory; this can confuse Git\u2019s detection of repositories and worktrees.<\/li>\n<li><strong>Keep your base branches fresh<\/strong>: Regularly fetch and update <code>main<\/code>\/<code>develop<\/code> and rebase or merge them into your feature worktrees to minimize integration surprises.<\/li>\n<li><strong>Clean up after merges<\/strong>: Remove worktrees you no longer need, then prune occasionally to ensure <code>git worktree list<\/code> remains readable and accurate.<\/li>\n<li><strong>Check Git version<\/strong>: Some newer options and behaviors (like more detailed <code>list<\/code> output and improved prune behavior) depend on having a reasonably up\u2011to\u2011date Git installation.<\/li>\n<\/ul>\n<p>By following this workflow\u2014create a worktree per active branch, keep them organized, and clean them up when done\u2014you get parallel branch development with far less friction than stashes, temporary commits, or multiple clones, while still relying on standard Git primitives.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Git worktrees provide a clean, efficient way to work on multiple branches at once without juggling stashes, temporary commits, or extra clones. This article walks through a concrete workflow for adopting git worktree in everyday development. Why git worktree? In a typical Git workflow, switching branches while you have uncommitted work forces you to choose [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[20],"tags":[],"_links":{"self":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2127"}],"collection":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2127"}],"version-history":[{"count":1,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2127\/revisions"}],"predecessor-version":[{"id":2128,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2127\/revisions\/2128"}],"wp:attachment":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}