[{"data":1,"prerenderedAt":1244},["ShallowReactive",2],{"tag-git":3},[4],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"layout":11,"date":12,"subtitle":13,"image":14,"optimized_image":14,"category":15,"tags":16,"author":20,"paginate":7,"body":21,"_type":1238,"_id":1239,"_source":1240,"_file":1241,"_stem":1242,"_extension":1243},"/posts/git-worktrees-parallel-development","posts",false,"","Git Worktrees: Parallel Work Without Clobbering Your Workspace","A hands-on guide to Git worktrees: create isolated checkouts, run parallel development and AI agents, manage dependencies and ports, integrate safely, and clean up.","post","2025-07-15T11:00:00.000Z","A practical workflow for separate tasks, builds, and AI coding agents in one repository","/assets/img/uploads/git-worktrees-parallel-development.jpg","code",[15,17,18,19],"git","ai","best practices","jaimedearcos",{"type":22,"children":23,"toc":1228},"root",[24,32,45,50,57,105,170,191,196,202,221,337,388,409,414,465,485,491,496,516,649,654,667,673,678,717,722,727,732,738,764,842,868,873,936,969,995,1001,1006,1048,1099,1134,1140,1145,1175,1180,1186,1222],{"type":25,"tag":26,"props":27,"children":28},"element","p",{},[29],{"type":30,"value":31},"text","You are halfway through a search refactor when an urgent bug arrives. Or an AI coding agent is editing the API while you want another agent to work on tests. One checkout forces you to stop, stash, switch branches, and later reconstruct the context you had. Two agents in that checkout can overwrite each other's files or mistake unfinished work for their own.",{"type":25,"tag":26,"props":33,"children":34},{},[35,37,43],{"type":30,"value":36},"Git worktrees give each task a separate working directory and branch while keeping them attached to the same repository. This is useful for human parallelism, and especially useful now that several AI agents can produce useful code at the same time. ",{"type":25,"tag":38,"props":39,"children":40},"strong",{},[41],{"type":30,"value":42},"Parallel coding only helps when each task has an explicit boundary and the results can be integrated safely.",{"type":30,"value":44}," Worktrees provide the filesystem boundary; they do not make design decisions or resolve conflicting changes for you.",{"type":25,"tag":26,"props":46,"children":47},{},[48],{"type":30,"value":49},"Here is a workflow you can try with a repository you already have.",{"type":25,"tag":51,"props":52,"children":54},"h2",{"id":53},"what-a-worktree-actually-separates",[55],{"type":30,"value":56},"What a worktree actually separates",{"type":25,"tag":26,"props":58,"children":59},{},[60,62,68,70,76,78,84,86,92,94,103],{"type":30,"value":61},"A linked worktree has its own checked-out files, ",{"type":25,"tag":15,"props":63,"children":65},{"className":64},[],[66],{"type":30,"value":67},"HEAD",{"type":30,"value":69},", index, and uncommitted changes. The worktrees share the repository's objects and branch references. You can have ",{"type":25,"tag":15,"props":71,"children":73},{"className":72},[],[74],{"type":30,"value":75},"main",{"type":30,"value":77}," open in one directory, ",{"type":25,"tag":15,"props":79,"children":81},{"className":80},[],[82],{"type":30,"value":83},"feature/search-index",{"type":30,"value":85}," in another, and ",{"type":25,"tag":15,"props":87,"children":89},{"className":88},[],[90],{"type":30,"value":91},"feature/api-tests",{"type":30,"value":93}," in a third without cloning the repository three times. Git documents this model in ",{"type":25,"tag":95,"props":96,"children":100},"a",{"href":97,"rel":98},"https://git-scm.com/docs/git-worktree",[99],"nofollow",[101],{"type":30,"value":102},"git-worktree",{"type":30,"value":104},".",{"type":25,"tag":106,"props":107,"children":110},"pre",{"className":108,"code":109,"language":30,"meta":8,"style":8},"language-text shiki shiki-themes github-dark github-light","workspace/\n├── project/                         main; clean integration checkout\n└── .worktrees/\n    └── project/\n        ├── search-index/            feature/search-index\n        └── api-tests/               feature/api-tests\n",[111],{"type":25,"tag":15,"props":112,"children":113},{"__ignoreMap":8},[114,125,134,143,152,161],{"type":25,"tag":115,"props":116,"children":119},"span",{"class":117,"line":118},"line",1,[120],{"type":25,"tag":115,"props":121,"children":122},{},[123],{"type":30,"value":124},"workspace/\n",{"type":25,"tag":115,"props":126,"children":128},{"class":117,"line":127},2,[129],{"type":25,"tag":115,"props":130,"children":131},{},[132],{"type":30,"value":133},"├── project/                         main; clean integration checkout\n",{"type":25,"tag":115,"props":135,"children":137},{"class":117,"line":136},3,[138],{"type":25,"tag":115,"props":139,"children":140},{},[141],{"type":30,"value":142},"└── .worktrees/\n",{"type":25,"tag":115,"props":144,"children":146},{"class":117,"line":145},4,[147],{"type":25,"tag":115,"props":148,"children":149},{},[150],{"type":30,"value":151},"    └── project/\n",{"type":25,"tag":115,"props":153,"children":155},{"class":117,"line":154},5,[156],{"type":25,"tag":115,"props":157,"children":158},{},[159],{"type":30,"value":160},"        ├── search-index/            feature/search-index\n",{"type":25,"tag":115,"props":162,"children":164},{"class":117,"line":163},6,[165],{"type":25,"tag":115,"props":166,"children":167},{},[168],{"type":30,"value":169},"        └── api-tests/               feature/api-tests\n",{"type":25,"tag":26,"props":171,"children":172},{},[173,175,181,183,189],{"type":30,"value":174},"This layout keeps task checkouts outside the main checkout, so a file search or build in ",{"type":25,"tag":15,"props":176,"children":178},{"className":177},[],[179],{"type":30,"value":180},"project/",{"type":30,"value":182}," does not accidentally traverse the other tasks. Choose a location your tools can discover and that your team can inspect. ",{"type":25,"tag":15,"props":184,"children":186},{"className":185},[],[187],{"type":30,"value":188},"git worktree list",{"type":30,"value":190}," shows every worktree Git knows about, regardless of where you put it.",{"type":25,"tag":26,"props":192,"children":193},{},[194],{"type":30,"value":195},"Git normally prevents the same branch from being checked out in two worktrees at once. That safeguard matters: each task needs its own branch. Starting two tasks from the same commit is fine; letting them edit the same branch or directory is the collision we are trying to avoid.",{"type":25,"tag":51,"props":197,"children":199},{"id":198},"create-two-task-checkouts",[200],{"type":30,"value":201},"Create two task checkouts",{"type":25,"tag":26,"props":203,"children":204},{},[205,207,212,214,219],{"type":30,"value":206},"Run these commands from the existing ",{"type":25,"tag":15,"props":208,"children":210},{"className":209},[],[211],{"type":30,"value":180},{"type":30,"value":213}," checkout while ",{"type":25,"tag":15,"props":215,"children":217},{"className":216},[],[218],{"type":30,"value":75},{"type":30,"value":220}," is clean:",{"type":25,"tag":106,"props":222,"children":226},{"className":223,"code":224,"language":225,"meta":8,"style":8},"language-bash shiki shiki-themes github-dark github-light","mkdir -p ../.worktrees/project\ngit worktree add -b feature/search-index ../.worktrees/project/search-index main\ngit worktree add -b feature/api-tests ../.worktrees/project/api-tests main\ngit worktree list\n","bash",[227],{"type":25,"tag":15,"props":228,"children":229},{"__ignoreMap":8},[230,251,288,321],{"type":25,"tag":115,"props":231,"children":232},{"class":117,"line":118},[233,239,245],{"type":25,"tag":115,"props":234,"children":236},{"style":235},"--shiki-default:#B392F0;--shiki-light:#6F42C1",[237],{"type":30,"value":238},"mkdir",{"type":25,"tag":115,"props":240,"children":242},{"style":241},"--shiki-default:#79B8FF;--shiki-light:#005CC5",[243],{"type":30,"value":244}," -p",{"type":25,"tag":115,"props":246,"children":248},{"style":247},"--shiki-default:#9ECBFF;--shiki-light:#032F62",[249],{"type":30,"value":250}," ../.worktrees/project\n",{"type":25,"tag":115,"props":252,"children":253},{"class":117,"line":127},[254,258,263,268,273,278,283],{"type":25,"tag":115,"props":255,"children":256},{"style":235},[257],{"type":30,"value":17},{"type":25,"tag":115,"props":259,"children":260},{"style":247},[261],{"type":30,"value":262}," worktree",{"type":25,"tag":115,"props":264,"children":265},{"style":247},[266],{"type":30,"value":267}," add",{"type":25,"tag":115,"props":269,"children":270},{"style":241},[271],{"type":30,"value":272}," -b",{"type":25,"tag":115,"props":274,"children":275},{"style":247},[276],{"type":30,"value":277}," feature/search-index",{"type":25,"tag":115,"props":279,"children":280},{"style":247},[281],{"type":30,"value":282}," ../.worktrees/project/search-index",{"type":25,"tag":115,"props":284,"children":285},{"style":247},[286],{"type":30,"value":287}," main\n",{"type":25,"tag":115,"props":289,"children":290},{"class":117,"line":136},[291,295,299,303,307,312,317],{"type":25,"tag":115,"props":292,"children":293},{"style":235},[294],{"type":30,"value":17},{"type":25,"tag":115,"props":296,"children":297},{"style":247},[298],{"type":30,"value":262},{"type":25,"tag":115,"props":300,"children":301},{"style":247},[302],{"type":30,"value":267},{"type":25,"tag":115,"props":304,"children":305},{"style":241},[306],{"type":30,"value":272},{"type":25,"tag":115,"props":308,"children":309},{"style":247},[310],{"type":30,"value":311}," feature/api-tests",{"type":25,"tag":115,"props":313,"children":314},{"style":247},[315],{"type":30,"value":316}," ../.worktrees/project/api-tests",{"type":25,"tag":115,"props":318,"children":319},{"style":247},[320],{"type":30,"value":287},{"type":25,"tag":115,"props":322,"children":323},{"class":117,"line":145},[324,328,332],{"type":25,"tag":115,"props":325,"children":326},{"style":235},[327],{"type":30,"value":17},{"type":25,"tag":115,"props":329,"children":330},{"style":247},[331],{"type":30,"value":262},{"type":25,"tag":115,"props":333,"children":334},{"style":247},[335],{"type":30,"value":336}," list\n",{"type":25,"tag":26,"props":338,"children":339},{},[340,342,348,350,355,357,362,364,371,373,379,381,387],{"type":30,"value":341},"The ",{"type":25,"tag":15,"props":343,"children":345},{"className":344},[],[346],{"type":30,"value":347},"-b",{"type":30,"value":349}," flag creates each branch at ",{"type":25,"tag":15,"props":351,"children":353},{"className":352},[],[354],{"type":30,"value":75},{"type":30,"value":356}," and checks it out in the new directory. The final ",{"type":25,"tag":15,"props":358,"children":360},{"className":359},[],[361],{"type":30,"value":75},{"type":30,"value":363}," is the starting commit, stated explicitly so the result does not depend on whichever branch happens to be active in your shell. The ",{"type":25,"tag":95,"props":365,"children":368},{"href":366,"rel":367},"https://git-scm.com/docs/git-worktree#_commands",[99],[369],{"type":30,"value":370},"Git manual",{"type":30,"value":372}," describes both ",{"type":25,"tag":15,"props":374,"children":376},{"className":375},[],[377],{"type":30,"value":378},"add",{"type":30,"value":380}," and ",{"type":25,"tag":15,"props":382,"children":384},{"className":383},[],[385],{"type":30,"value":386},"list",{"type":30,"value":104},{"type":25,"tag":26,"props":389,"children":390},{},[391,393,399,401,407],{"type":30,"value":392},"Give each task a short, testable objective. For example: “Add an index for the order search query and measure the plan before and after” in ",{"type":25,"tag":15,"props":394,"children":396},{"className":395},[],[397],{"type":30,"value":398},"search-index",{"type":30,"value":400},"; “Add API contract tests for the current order response” in ",{"type":25,"tag":15,"props":402,"children":404},{"className":403},[],[405],{"type":30,"value":406},"api-tests",{"type":30,"value":408},". If both tasks need to rewrite the same handler, separate worktrees will prevent filesystem collisions but the integration may still be difficult. Split the work by stable interfaces, or sequence the dependent edits.",{"type":25,"tag":26,"props":410,"children":411},{},[412],{"type":30,"value":413},"Move into one directory to work on it:",{"type":25,"tag":106,"props":415,"children":417},{"className":223,"code":416,"language":225,"meta":8,"style":8},"cd ../.worktrees/project/search-index\ngit status --short --branch\n# Edit, run checks, and commit on feature/search-index.\n",[418],{"type":25,"tag":15,"props":419,"children":420},{"__ignoreMap":8},[421,434,456],{"type":25,"tag":115,"props":422,"children":423},{"class":117,"line":118},[424,429],{"type":25,"tag":115,"props":425,"children":426},{"style":241},[427],{"type":30,"value":428},"cd",{"type":25,"tag":115,"props":430,"children":431},{"style":247},[432],{"type":30,"value":433}," ../.worktrees/project/search-index\n",{"type":25,"tag":115,"props":435,"children":436},{"class":117,"line":127},[437,441,446,451],{"type":25,"tag":115,"props":438,"children":439},{"style":235},[440],{"type":30,"value":17},{"type":25,"tag":115,"props":442,"children":443},{"style":247},[444],{"type":30,"value":445}," status",{"type":25,"tag":115,"props":447,"children":448},{"style":241},[449],{"type":30,"value":450}," --short",{"type":25,"tag":115,"props":452,"children":453},{"style":241},[454],{"type":30,"value":455}," --branch\n",{"type":25,"tag":115,"props":457,"children":458},{"class":117,"line":136},[459],{"type":25,"tag":115,"props":460,"children":462},{"style":461},"--shiki-default:#6A737D;--shiki-light:#6A737D",[463],{"type":30,"value":464},"# Edit, run checks, and commit on feature/search-index.\n",{"type":25,"tag":26,"props":466,"children":467},{},[468,470,476,478,483],{"type":30,"value":469},"From that directory, ",{"type":25,"tag":15,"props":471,"children":473},{"className":472},[],[474],{"type":30,"value":475},"git status",{"type":30,"value":477}," describes only that task's checkout. Changes there will not appear in the ",{"type":25,"tag":15,"props":479,"children":481},{"className":480},[],[482],{"type":30,"value":406},{"type":30,"value":484}," working directory. New commits and branches do become visible through the shared repository, which is why integration still needs coordination.",{"type":25,"tag":51,"props":486,"children":488},{"id":487},"give-each-checkout-its-own-runtime",[489],{"type":30,"value":490},"Give each checkout its own runtime",{"type":25,"tag":26,"props":492,"children":493},{},[494],{"type":30,"value":495},"Worktrees separate tracked files and untracked files on disk. They do not automatically isolate everything your application uses. A local database, a Docker project name, an output bucket, and TCP port 3000 can still be shared accidentally.",{"type":25,"tag":26,"props":497,"children":498},{},[499,501,507,509,514],{"type":30,"value":500},"For a Node project, install dependencies in each worktree. Do not symlink one ",{"type":25,"tag":15,"props":502,"children":504},{"className":503},[],[505],{"type":30,"value":506},"node_modules",{"type":30,"value":508}," directory into several worktrees: package tooling and build caches can resolve paths relative to the checkout. Use the project's lockfile and version manager, then start each server on a distinct port. Start each terminal in ",{"type":25,"tag":15,"props":510,"children":512},{"className":511},[],[513],{"type":30,"value":180},{"type":30,"value":515}," for this example:",{"type":25,"tag":106,"props":517,"children":519},{"className":223,"code":518,"language":225,"meta":8,"style":8},"cd ../.worktrees/project/search-index\nnpm ci\nnpm run dev -- --port 3101\n\n# In a second terminal:\ncd ../.worktrees/project/api-tests\nnpm ci\nnpm run dev -- --port 3102\n",[520],{"type":25,"tag":15,"props":521,"children":522},{"__ignoreMap":8},[523,534,547,579,588,596,608,620],{"type":25,"tag":115,"props":524,"children":525},{"class":117,"line":118},[526,530],{"type":25,"tag":115,"props":527,"children":528},{"style":241},[529],{"type":30,"value":428},{"type":25,"tag":115,"props":531,"children":532},{"style":247},[533],{"type":30,"value":433},{"type":25,"tag":115,"props":535,"children":536},{"class":117,"line":127},[537,542],{"type":25,"tag":115,"props":538,"children":539},{"style":235},[540],{"type":30,"value":541},"npm",{"type":25,"tag":115,"props":543,"children":544},{"style":247},[545],{"type":30,"value":546}," ci\n",{"type":25,"tag":115,"props":548,"children":549},{"class":117,"line":136},[550,554,559,564,569,574],{"type":25,"tag":115,"props":551,"children":552},{"style":235},[553],{"type":30,"value":541},{"type":25,"tag":115,"props":555,"children":556},{"style":247},[557],{"type":30,"value":558}," run",{"type":25,"tag":115,"props":560,"children":561},{"style":247},[562],{"type":30,"value":563}," dev",{"type":25,"tag":115,"props":565,"children":566},{"style":241},[567],{"type":30,"value":568}," --",{"type":25,"tag":115,"props":570,"children":571},{"style":241},[572],{"type":30,"value":573}," --port",{"type":25,"tag":115,"props":575,"children":576},{"style":241},[577],{"type":30,"value":578}," 3101\n",{"type":25,"tag":115,"props":580,"children":581},{"class":117,"line":145},[582],{"type":25,"tag":115,"props":583,"children":585},{"emptyLinePlaceholder":584},true,[586],{"type":30,"value":587},"\n",{"type":25,"tag":115,"props":589,"children":590},{"class":117,"line":154},[591],{"type":25,"tag":115,"props":592,"children":593},{"style":461},[594],{"type":30,"value":595},"# In a second terminal:\n",{"type":25,"tag":115,"props":597,"children":598},{"class":117,"line":163},[599,603],{"type":25,"tag":115,"props":600,"children":601},{"style":241},[602],{"type":30,"value":428},{"type":25,"tag":115,"props":604,"children":605},{"style":247},[606],{"type":30,"value":607}," ../.worktrees/project/api-tests\n",{"type":25,"tag":115,"props":609,"children":611},{"class":117,"line":610},7,[612,616],{"type":25,"tag":115,"props":613,"children":614},{"style":235},[615],{"type":30,"value":541},{"type":25,"tag":115,"props":617,"children":618},{"style":247},[619],{"type":30,"value":546},{"type":25,"tag":115,"props":621,"children":623},{"class":117,"line":622},8,[624,628,632,636,640,644],{"type":25,"tag":115,"props":625,"children":626},{"style":235},[627],{"type":30,"value":541},{"type":25,"tag":115,"props":629,"children":630},{"style":247},[631],{"type":30,"value":558},{"type":25,"tag":115,"props":633,"children":634},{"style":247},[635],{"type":30,"value":563},{"type":25,"tag":115,"props":637,"children":638},{"style":241},[639],{"type":30,"value":568},{"type":25,"tag":115,"props":641,"children":642},{"style":241},[643],{"type":30,"value":573},{"type":25,"tag":115,"props":645,"children":646},{"style":241},[647],{"type":30,"value":648}," 3102\n",{"type":25,"tag":26,"props":650,"children":651},{},[652],{"type":30,"value":653},"The commands are illustrative; use your repository's build and start commands. Keep task-specific environment variables and local data separate where those resources can interfere. Never copy production secrets into a worktree merely to make a local preview start.",{"type":25,"tag":26,"props":655,"children":656},{},[657,659,665],{"type":30,"value":658},"Ignored files are also local to a worktree. A ",{"type":25,"tag":15,"props":660,"children":662},{"className":661},[],[663],{"type":30,"value":664},".env.local",{"type":30,"value":666}," file or generated build in one directory does not appear in another. This is convenient, but it means a fresh worktree may need its own safe local configuration before it can run.",{"type":25,"tag":51,"props":668,"children":670},{"id":669},"one-agent-one-worktree-one-objective",[671],{"type":30,"value":672},"One agent, one worktree, one objective",{"type":25,"tag":26,"props":674,"children":675},{},[676],{"type":30,"value":677},"An AI agent should be given the absolute path of its worktree and a bounded task. That makes its file operations, terminal commands, and build artifacts easy to attribute. A useful brief looks like this:",{"type":25,"tag":106,"props":679,"children":681},{"className":108,"code":680,"language":30,"meta":8,"style":8},"Work only in /workspace/.worktrees/project/search-index.\nGoal: improve the order search query without changing the API response.\nRun the repository's relevant tests and report the query plan change.\nCommit your work on feature/search-index; do not integrate or deploy it.\n",[682],{"type":25,"tag":15,"props":683,"children":684},{"__ignoreMap":8},[685,693,701,709],{"type":25,"tag":115,"props":686,"children":687},{"class":117,"line":118},[688],{"type":25,"tag":115,"props":689,"children":690},{},[691],{"type":30,"value":692},"Work only in /workspace/.worktrees/project/search-index.\n",{"type":25,"tag":115,"props":694,"children":695},{"class":117,"line":127},[696],{"type":25,"tag":115,"props":697,"children":698},{},[699],{"type":30,"value":700},"Goal: improve the order search query without changing the API response.\n",{"type":25,"tag":115,"props":702,"children":703},{"class":117,"line":136},[704],{"type":25,"tag":115,"props":705,"children":706},{},[707],{"type":30,"value":708},"Run the repository's relevant tests and report the query plan change.\n",{"type":25,"tag":115,"props":710,"children":711},{"class":117,"line":145},[712],{"type":25,"tag":115,"props":713,"children":714},{},[715],{"type":30,"value":716},"Commit your work on feature/search-index; do not integrate or deploy it.\n",{"type":25,"tag":26,"props":718,"children":719},{},[720],{"type":30,"value":721},"Give the second agent a different directory and objective. Do not send two agents into the same worktree, even if their prompts mention different files. A formatter, generated schema, package update, or broad search-and-replace can cross that informal boundary.",{"type":25,"tag":26,"props":723,"children":724},{},[725],{"type":30,"value":726},"Before starting another agent, ask whether the tasks are truly independent. One agent defining a new API while another writes tests against the old API is likely to create rework. A better split may be “agree on the contract first, then implement producer and consumer in separate branches.” Parallel execution is a scheduling tool, not a substitute for shared design.",{"type":25,"tag":26,"props":728,"children":729},{},[730],{"type":30,"value":731},"It also helps to keep one owner for integration. Agents can propose commits and report their checks, but the integrator should review the combined behavior, resolve conflicts, and run the full verification after combining changes. A passing build in each branch does not prove the combination passes.",{"type":25,"tag":51,"props":733,"children":735},{"id":734},"integrate-one-result-at-a-time",[736],{"type":30,"value":737},"Integrate one result at a time",{"type":25,"tag":26,"props":739,"children":740},{},[741,743,748,750,755,757,762],{"type":30,"value":742},"Assume ",{"type":25,"tag":15,"props":744,"children":746},{"className":745},[],[747],{"type":30,"value":83},{"type":30,"value":749}," is ready. Starting in ",{"type":25,"tag":15,"props":751,"children":753},{"className":752},[],[754],{"type":30,"value":180},{"type":30,"value":756},", check its diff and verification results in its worktree, then update it onto the latest ",{"type":25,"tag":15,"props":758,"children":760},{"className":759},[],[761],{"type":30,"value":75},{"type":30,"value":763},":",{"type":25,"tag":106,"props":765,"children":767},{"className":223,"code":766,"language":225,"meta":8,"style":8},"cd ../.worktrees/project/search-index\ngit status --short --branch\ngit diff main...HEAD\ngit rebase main\n# Run the project's required checks again after the rebase.\n",[768],{"type":25,"tag":15,"props":769,"children":770},{"__ignoreMap":8},[771,782,801,818,834],{"type":25,"tag":115,"props":772,"children":773},{"class":117,"line":118},[774,778],{"type":25,"tag":115,"props":775,"children":776},{"style":241},[777],{"type":30,"value":428},{"type":25,"tag":115,"props":779,"children":780},{"style":247},[781],{"type":30,"value":433},{"type":25,"tag":115,"props":783,"children":784},{"class":117,"line":127},[785,789,793,797],{"type":25,"tag":115,"props":786,"children":787},{"style":235},[788],{"type":30,"value":17},{"type":25,"tag":115,"props":790,"children":791},{"style":247},[792],{"type":30,"value":445},{"type":25,"tag":115,"props":794,"children":795},{"style":241},[796],{"type":30,"value":450},{"type":25,"tag":115,"props":798,"children":799},{"style":241},[800],{"type":30,"value":455},{"type":25,"tag":115,"props":802,"children":803},{"class":117,"line":136},[804,808,813],{"type":25,"tag":115,"props":805,"children":806},{"style":235},[807],{"type":30,"value":17},{"type":25,"tag":115,"props":809,"children":810},{"style":247},[811],{"type":30,"value":812}," diff",{"type":25,"tag":115,"props":814,"children":815},{"style":247},[816],{"type":30,"value":817}," main...HEAD\n",{"type":25,"tag":115,"props":819,"children":820},{"class":117,"line":145},[821,825,830],{"type":25,"tag":115,"props":822,"children":823},{"style":235},[824],{"type":30,"value":17},{"type":25,"tag":115,"props":826,"children":827},{"style":247},[828],{"type":30,"value":829}," rebase",{"type":25,"tag":115,"props":831,"children":832},{"style":247},[833],{"type":30,"value":287},{"type":25,"tag":115,"props":835,"children":836},{"class":117,"line":154},[837],{"type":25,"tag":115,"props":838,"children":839},{"style":461},[840],{"type":30,"value":841},"# Run the project's required checks again after the rebase.\n",{"type":25,"tag":26,"props":843,"children":844},{},[845,851,853,859,860,866],{"type":25,"tag":15,"props":846,"children":848},{"className":847},[],[849],{"type":30,"value":850},"git diff main...HEAD",{"type":30,"value":852}," shows committed changes on the task branch relative to the common ancestor. Review uncommitted changes separately with ",{"type":25,"tag":15,"props":854,"children":856},{"className":855},[],[857],{"type":30,"value":858},"git diff",{"type":30,"value":380},{"type":25,"tag":15,"props":861,"children":863},{"className":862},[],[864],{"type":30,"value":865},"git diff --cached",{"type":30,"value":867},"; do not assume the three-dot diff includes them. Rebase only a branch whose history your workflow permits you to rewrite. If it is shared with other people, agree on an update strategy before rewriting it.",{"type":25,"tag":26,"props":869,"children":870},{},[871],{"type":30,"value":872},"When the branch passes, integrate according to your repository's policy. For a team using squash commits, the main checkout can do this:",{"type":25,"tag":106,"props":874,"children":876},{"className":223,"code":875,"language":225,"meta":8,"style":8},"cd ../../../project\ngit merge --squash feature/search-index\ngit commit -m \"perf: speed up order search\"\n",[877],{"type":25,"tag":15,"props":878,"children":879},{"__ignoreMap":8},[880,892,914],{"type":25,"tag":115,"props":881,"children":882},{"class":117,"line":118},[883,887],{"type":25,"tag":115,"props":884,"children":885},{"style":241},[886],{"type":30,"value":428},{"type":25,"tag":115,"props":888,"children":889},{"style":247},[890],{"type":30,"value":891}," ../../../project\n",{"type":25,"tag":115,"props":893,"children":894},{"class":117,"line":127},[895,899,904,909],{"type":25,"tag":115,"props":896,"children":897},{"style":235},[898],{"type":30,"value":17},{"type":25,"tag":115,"props":900,"children":901},{"style":247},[902],{"type":30,"value":903}," merge",{"type":25,"tag":115,"props":905,"children":906},{"style":241},[907],{"type":30,"value":908}," --squash",{"type":25,"tag":115,"props":910,"children":911},{"style":247},[912],{"type":30,"value":913}," feature/search-index\n",{"type":25,"tag":115,"props":915,"children":916},{"class":117,"line":136},[917,921,926,931],{"type":25,"tag":115,"props":918,"children":919},{"style":235},[920],{"type":30,"value":17},{"type":25,"tag":115,"props":922,"children":923},{"style":247},[924],{"type":30,"value":925}," commit",{"type":25,"tag":115,"props":927,"children":928},{"style":241},[929],{"type":30,"value":930}," -m",{"type":25,"tag":115,"props":932,"children":933},{"style":247},[934],{"type":30,"value":935}," \"perf: speed up order search\"\n",{"type":25,"tag":26,"props":937,"children":938},{},[939,941,946,948,954,956,961,963,968],{"type":30,"value":940},"The relative ",{"type":25,"tag":15,"props":942,"children":944},{"className":943},[],[945],{"type":30,"value":428},{"type":30,"value":947}," above assumes the exact layout shown earlier and starts in ",{"type":25,"tag":15,"props":949,"children":951},{"className":950},[],[952],{"type":30,"value":953},"search-index/",{"type":30,"value":955},"; adjust it if you chose another directory. Squash creates one integration commit on ",{"type":25,"tag":15,"props":957,"children":959},{"className":958},[],[960],{"type":30,"value":75},{"type":30,"value":962},". A normal merge or pull request can be equally appropriate if that is your team's policy. The point is to integrate one reviewed result, then update the next branch against the new ",{"type":25,"tag":15,"props":964,"children":966},{"className":965},[],[967],{"type":30,"value":75},{"type":30,"value":104},{"type":25,"tag":26,"props":970,"children":971},{},[972,974,979,981,986,988,993],{"type":30,"value":973},"Now ",{"type":25,"tag":15,"props":975,"children":977},{"className":976},[],[978],{"type":30,"value":91},{"type":30,"value":980}," still starts from the old ",{"type":25,"tag":15,"props":982,"children":984},{"className":983},[],[985],{"type":30,"value":75},{"type":30,"value":987},". Rebase it, resolve any overlap, and rerun its checks before integrating it. Finally run the combined repository checks on ",{"type":25,"tag":15,"props":989,"children":991},{"className":990},[],[992],{"type":30,"value":75},{"type":30,"value":994},". Conflicts are useful information: they show that the tasks were coupled at a code boundary you may want to improve next time.",{"type":25,"tag":51,"props":996,"children":998},{"id":997},"clean-up-without-losing-work",[999],{"type":30,"value":1000},"Clean up without losing work",{"type":25,"tag":26,"props":1002,"children":1003},{},[1004],{"type":30,"value":1005},"Once a task has been integrated and you have checked that nothing remains uncommitted, retire its checkout:",{"type":25,"tag":106,"props":1007,"children":1009},{"className":223,"code":1008,"language":225,"meta":8,"style":8},"git worktree list\ngit worktree remove ../.worktrees/project/search-index\n",[1010],{"type":25,"tag":15,"props":1011,"children":1012},{"__ignoreMap":8},[1013,1028],{"type":25,"tag":115,"props":1014,"children":1015},{"class":117,"line":118},[1016,1020,1024],{"type":25,"tag":115,"props":1017,"children":1018},{"style":235},[1019],{"type":30,"value":17},{"type":25,"tag":115,"props":1021,"children":1022},{"style":247},[1023],{"type":30,"value":262},{"type":25,"tag":115,"props":1025,"children":1026},{"style":247},[1027],{"type":30,"value":336},{"type":25,"tag":115,"props":1029,"children":1030},{"class":117,"line":127},[1031,1035,1039,1044],{"type":25,"tag":115,"props":1032,"children":1033},{"style":235},[1034],{"type":30,"value":17},{"type":25,"tag":115,"props":1036,"children":1037},{"style":247},[1038],{"type":30,"value":262},{"type":25,"tag":115,"props":1040,"children":1041},{"style":247},[1042],{"type":30,"value":1043}," remove",{"type":25,"tag":115,"props":1045,"children":1046},{"style":247},[1047],{"type":30,"value":433},{"type":25,"tag":26,"props":1049,"children":1050},{},[1051,1053,1058,1060,1066,1068,1074,1076,1081,1083,1089,1091,1097],{"type":30,"value":1052},"Run those commands from the main ",{"type":25,"tag":15,"props":1054,"children":1056},{"className":1055},[],[1057],{"type":30,"value":180},{"type":30,"value":1059}," checkout. ",{"type":25,"tag":15,"props":1061,"children":1063},{"className":1062},[],[1064],{"type":30,"value":1065},"git worktree remove",{"type":30,"value":1067}," refuses to remove a dirty worktree by default. Do not use ",{"type":25,"tag":15,"props":1069,"children":1071},{"className":1070},[],[1072],{"type":30,"value":1073},"--force",{"type":30,"value":1075}," as a routine cleanup step. If you used a squash merge, Git may not consider the original branch “merged” because its commit is not literally in ",{"type":25,"tag":15,"props":1077,"children":1079},{"className":1078},[],[1080],{"type":30,"value":75},{"type":30,"value":1082},"; after verifying the squash contains the intended changes, delete that local branch with ",{"type":25,"tag":15,"props":1084,"children":1086},{"className":1085},[],[1087],{"type":30,"value":1088},"git branch -D feature/search-index",{"type":30,"value":1090},". With a normal merge, ",{"type":25,"tag":15,"props":1092,"children":1094},{"className":1093},[],[1095],{"type":30,"value":1096},"git branch -d",{"type":30,"value":1098}," can check ancestry for you.",{"type":25,"tag":26,"props":1100,"children":1101},{},[1102,1104,1109,1111,1116,1118,1124,1126,1132],{"type":30,"value":1103},"Avoid deleting a worktree directory by hand. Git retains administrative records for linked worktrees; use ",{"type":25,"tag":15,"props":1105,"children":1107},{"className":1106},[],[1108],{"type":30,"value":1065},{"type":30,"value":1110},". If someone has already deleted a directory, inspect ",{"type":25,"tag":15,"props":1112,"children":1114},{"className":1113},[],[1115],{"type":30,"value":188},{"type":30,"value":1117}," and use ",{"type":25,"tag":15,"props":1119,"children":1121},{"className":1120},[],[1122],{"type":30,"value":1123},"git worktree prune",{"type":30,"value":1125}," only for stale entries. The ",{"type":25,"tag":95,"props":1127,"children":1129},{"href":366,"rel":1128},[99],[1130],{"type":30,"value":1131},"worktree cleanup documentation",{"type":30,"value":1133}," explains these commands.",{"type":25,"tag":51,"props":1135,"children":1137},{"id":1136},"a-small-operating-checklist",[1138],{"type":30,"value":1139},"A small operating checklist",{"type":25,"tag":26,"props":1141,"children":1142},{},[1143],{"type":30,"value":1144},"The practical habit is simple:",{"type":25,"tag":1146,"props":1147,"children":1148},"ol",{},[1149,1155,1160,1165,1170],{"type":25,"tag":1150,"props":1151,"children":1152},"li",{},[1153],{"type":30,"value":1154},"Start each task from a known commit on its own branch and worktree.",{"type":25,"tag":1150,"props":1156,"children":1157},{},[1158],{"type":30,"value":1159},"Assign one person or agent to that worktree and keep the task narrow.",{"type":25,"tag":1150,"props":1161,"children":1162},{},[1163],{"type":30,"value":1164},"Isolate dependencies, ports, caches, and local data that could collide.",{"type":25,"tag":1150,"props":1166,"children":1167},{},[1168],{"type":30,"value":1169},"Review the diff and checks in each branch; integrate sequentially.",{"type":25,"tag":1150,"props":1171,"children":1172},{},[1173],{"type":30,"value":1174},"Verify the combined result and remove finished worktrees.",{"type":25,"tag":26,"props":1176,"children":1177},{},[1178],{"type":30,"value":1179},"Worktrees make it cheap to keep several contexts open at once. AI makes producing changes in those contexts faster. The engineering value comes from keeping each change understandable, testable, and safe to combine.",{"type":25,"tag":51,"props":1181,"children":1183},{"id":1182},"further-reading",[1184],{"type":30,"value":1185},"Further reading",{"type":25,"tag":1187,"props":1188,"children":1189},"ul",{},[1190,1200,1211],{"type":25,"tag":1150,"props":1191,"children":1192},{},[1193,1199],{"type":25,"tag":95,"props":1194,"children":1196},{"href":97,"rel":1195},[99],[1197],{"type":30,"value":1198},"Git: git-worktree documentation",{"type":30,"value":104},{"type":25,"tag":1150,"props":1201,"children":1202},{},[1203,1210],{"type":25,"tag":95,"props":1204,"children":1207},{"href":1205,"rel":1206},"https://git-scm.com/docs/git-rebase",[99],[1208],{"type":30,"value":1209},"Git: git-rebase documentation",{"type":30,"value":104},{"type":25,"tag":1150,"props":1212,"children":1213},{},[1214,1221],{"type":25,"tag":95,"props":1215,"children":1218},{"href":1216,"rel":1217},"https://git-scm.com/docs/git-merge",[99],[1219],{"type":30,"value":1220},"Git: git-merge documentation",{"type":30,"value":104},{"type":25,"tag":1223,"props":1224,"children":1225},"style",{},[1226],{"type":30,"value":1227},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":8,"searchDepth":127,"depth":127,"links":1229},[1230,1231,1232,1233,1234,1235,1236,1237],{"id":53,"depth":127,"text":56},{"id":198,"depth":127,"text":201},{"id":487,"depth":127,"text":490},{"id":669,"depth":127,"text":672},{"id":734,"depth":127,"text":737},{"id":997,"depth":127,"text":1000},{"id":1136,"depth":127,"text":1139},{"id":1182,"depth":127,"text":1185},"markdown","content:posts:git-worktrees-parallel-development.md","content","posts/git-worktrees-parallel-development.md","posts/git-worktrees-parallel-development","md",1791120244234]