ทุก worktree ใหม่ของโปรเจกต์ Laravel ต้องตั้งชุดเดิมซ้ำทุกครั้ง
ในโพสต์เรื่อง git worktree ผมไล่ไว้ว่าโฟลเดอร์ worktree ใหม่มีแค่ไฟล์ที่อยู่ใน git ไฟล์ที่ .gitignore ไว้ไม่ตามมาเลย จะรันโปรเจกต์ Laravel ได้ต้องตั้งเองอีกหลายอย่าง
vendorต้องคัดลอกมาจริงหรือcomposer installใหม่ ห้าม symlink.envและไฟล์.env.*อื่นที่ git ไม่ได้เก็บ- โฟลเดอร์ใน
storage/ที่ git ไม่ได้เก็บ ถ้าไม่มีstorage/framework/viewsทุกหน้าที่ render Blade จะ error public/buildสำหรับ Vite manifest- ฐานข้อมูลแยก ถ้า branch นั้นแก้ migration
APP_URLให้ตรงกับโดเมนของโฟลเดอร์ใหม่node_modulesถ้าจะ build หน้าเว็บ
ทำครั้งเดียวก็ไม่เท่าไร แต่ช่วงหลังผมเปิด Claude Code หลาย session ให้ทำงานพร้อมกัน แต่ละ session ได้ worktree ของตัวเอง ทุกตัวต้องตั้งชุดเดิมซ้ำ และพลาดได้ทุกรอบ อย่างที่เคยเจอคือ agent symlink vendor มาจากโฟลเดอร์หลักเพราะเร็วกว่า test ผ่านหมดจนดูเหมือนใช้ได้ ทั้งที่กำลังรันโค้ดของโฟลเดอร์หลักอยู่ ผมเลยรวมทุกขั้นไว้ในคำสั่งเดียวชื่อ lwt

บรรทัด → คือคำสั่งที่ lwt รันจริง ส่วนบรรทัด ✓ บอกว่าแต่ละขั้นได้อะไร
ทั้งชุดใช้เวลาราว 15 วินาทีกับโปรเจกต์ของเว็บนี้ ซึ่งรวมคัดลอกฐานข้อมูลและ build หน้าเว็บแล้ว
ติดตั้งครั้งเดียว ใช้ได้ทุกโปรเจกต์
composer global require phattarachai/worktree-prompt
แล้วเพิ่ม alias ไว้ใน ~/.zshrc หรือใส่ ~/.composer/vendor/bin ไว้ใน PATH
alias lwt="$HOME/.composer/vendor/bin/lwt"
ต้องมี PHP 8.3 ขึ้นไป และถ้าจะคัดลอกฐานข้อมูล ต้องมี pg_dump กับ psql หรือ mysqldump กับ mysql ในเครื่องด้วย หน้าจอถามตอบสร้างด้วย Laravel Prompts แบบเดียวกับ git-prompt
lwt ไม่ได้เป็นคำสั่ง artisan เพราะ worktree ที่เพิ่งสร้างยังไม่มี vendor จึงรัน php artisan ไม่ได้จนกว่าจะตั้งเสร็จ และแบบนี้ก็ไม่ต้อง composer require ลงทุกโปรเจกต์ด้วย
lwt add ถามแค่ที่ยังไม่ได้บอก
สั่ง lwt add เฉย ๆ จะถามทีละข้อ ตอบไปแล้ว Enter ได้เลยเพราะค่าที่เติมไว้ให้ส่วนใหญ่คือค่าที่เราจะเลือกอยู่แล้ว

หลังจาก Database จะถาม APP_URL และ node_modules ต่อ
- Branch พิมพ์ชื่อ branch ที่มีอยู่แล้ว ทั้งในเครื่องหรือบน remote ก็ได้ หรือพิมพ์ชื่อใหม่ lwt สั่ง
git fetchให้ก่อนเสมอ ถ้าเป็น branch ที่มีแค่บน remote จะได้ branch ในเครื่องที่ track ตัวบน remote ให้ - Branch from ถามเฉพาะ branch ใหม่ ค่าเริ่มต้นคือ branch หลักของ remote (
origin/HEADซึ่งมักเป็นorigin/main) และใส่--no-trackให้ ไม่อย่างนั้นgit pushครั้งแรกจะไม่ยอม เพราะ upstream เป็นorigin/mainคนละชื่อกับ branch ของเรา - Folder ค่าเริ่มต้นคือโฟลเดอร์ข้าง ๆ โฟลเดอร์หลัก ชื่อ
<โปรเจกต์>-<ท้ายชื่อ branch>ถ้าเครื่องเราตั้งโฟลเดอร์~/wwwให้ทุกโฟลเดอร์ย่อยเปิดเป็น<ชื่อโฟลเดอร์>.testได้ (parked folder) worktree ใหม่ก็เปิดในเบราว์เซอร์ได้ทันที - Database ถ้า branch ที่เลือกมี migration ที่ branch หลักยังไม่มี lwt จะเลือกแบบคัดลอกไว้ให้แทน
- APP_URL ค่าเริ่มต้นคือ
http://<ชื่อโฟลเดอร์>.testพอเปลี่ยนSESSION_DOMAIN,SANCTUM_STATEFUL_DOMAINSและค่า*_URLที่ใช้โดเมนเดิมก็เปลี่ยนตามให้ ปล่อยว่างถ้าจะใช้ค่าเดิม - node_modules ค่าเริ่มต้นคือข้าม เพราะงานฝั่ง backend และการรัน test ไม่ต้องใช้
ทุกข้อใส่เป็น option ได้ อย่าง lwt add fix/login-redirect --db=copy --npm ข้อที่ใส่มาแล้วจะไม่ถามซ้ำ
vendor เป็นของจริงเสมอ และตรวจว่า App\ โหลดจากโฟลเดอร์ใหม่
ในโพสต์ก่อนผมลองให้ดูแล้วว่า symlink vendor ทำให้ class ใน app/ โหลดจากโฟลเดอร์หลัก เพราะ autoload ของ Composer หาโฟลเดอร์โปรเจกต์จาก __DIR__ ซึ่ง PHP แปลงผ่าน symlink เป็น path จริงเสมอ lwt จึงทำ vendor ให้ worktree เองทุกครั้ง
- ถ้า
composer.lockเหมือนกับโฟลเดอร์หลัก lwt คัดลอกvendorมาด้วยcp -cซึ่งบน macOS (APFS) เป็นการ clone แบบ copy-on-write ไฟล์ยังใช้พื้นที่ดิสก์ชุดเดิมจนกว่าจะมีฝั่งไหนแก้ vendor 226 MB ของเว็บนี้ใช้เวลาราว 2 วินาที ส่วนcp -Rปกติใช้ราว 6 วินาที บน Linux ใช้cp --reflink=autoซึ่ง clone ได้บน btrfs หรือ XFS และคัดลอกปกติบนดิสก์อื่น - ถ้า
composer.lockต่างกัน คือ branch นี้เปลี่ยน dependency lwt จะcomposer installใหม่ในโฟลเดอร์นั้น - ถ้าเจอ
vendorที่เป็น symlink อยู่แล้ว lwt จะลบ symlink นั้นออกแล้วทำใหม่ พร้อมบอกเหตุผล
ตั้งเสร็จแล้ว lwt จะถาม Composer ว่า namespace App\ โหลดจากโฟลเดอร์ไหน ถ้าไม่ใช่โฟลเดอร์ worktree จะแจ้งและจบด้วย exit code ที่ไม่ใช่ 0 แล้วค่อยสั่ง php artisan --version ดูว่าแอปบูตได้
ไฟล์อื่นที่ git ไม่ได้เก็บมาให้ lwt ก็ตั้งให้ครบ
.envคัดลอกจากโฟลเดอร์หลัก หรือใส่--env=symlinkถ้าอยากให้ใช้ไฟล์เดียวกัน ส่วนไฟล์.env.*อื่นที่ gitignore ไว้อย่าง.env.localก็คัดลอกให้ด้วย แต่ไฟล์ที่ git เก็บอยู่อย่าง.env.testingจะไม่แตะเลย เคยมี agentln -sfnทับ.env.testingที่ commit ไว้ ไฟล์กลายเป็น symlink แล้วโผล่ในgit statusเกือบติดไปใน commitstorage/และbootstrap/cacheสร้างโฟลเดอร์ที่ขาดให้ ถ้าไม่มีstorage/framework/viewsทุกหน้าที่ render Blade จะ error ว่าPlease provide a valid cache pathซึ่งตอนรัน feature test จะเห็นเป็น HTTP 500 เต็มจอ จนดูเหมือน branch ทำแอปพังpublic/buildและbootstrap/ssrคัดลอกจากโฟลเดอร์หลัก หน้าที่อ่าน Vite manifest และ test ที่ render view จึงทำงานได้ ถ้า branch เพิ่ม class ของ Tailwind ใหม่ ต้อง build ใหม่ในโฟลเดอร์นั้นpublic/storageลิงก์ไปที่storage/app/publicของ worktree เอง ไฟล์ที่ผู้ใช้อัปโหลดไว้ยังอยู่ที่โฟลเดอร์หลักnode_modulesเมื่อใส่--npmถ้า lockfile เหมือนกันจะคัดลอกแบบ clone ถ้าต่างกันจะnpm ciซึ่งไม่เขียนpackage-lock.jsonใหม่ ถ้ารันnpm installใน worktree เอง npm จะเปลี่ยน"name"ใน lockfile เป็นชื่อโฟลเดอร์ ใช้ pnpm, yarn หรือ bun ก็ได้ lwt ดูจาก lockfile ที่มี ถ้าโปรเจกต์เก็บ.cache/puppeteerไว้ในโฟลเดอร์ ก็คัดลอกให้ด้วย ใส่--buildแทนถ้าจะnpm run buildต่อเลย
ถ้าโปรเจกต์มีไฟล์อื่นที่ gitignore ไว้แต่ทุก worktree ต้องมี เช่น certificate สำหรับ dev ให้เขียนชื่อไว้ในไฟล์ .worktreeinclude ที่ root บรรทัดละหนึ่ง path ซึ่งเป็นไฟล์เดียวกับที่ Claude Code ใช้ตอนสร้าง worktree lwt คัดลอกเฉพาะ path ที่ gitignore ไว้ และไม่ทับไฟล์ที่มีอยู่แล้ว
.cache/certs
storage/app/fixtures
composer script บางตัวเขียนไฟล์ใน public/ ใหม่ระหว่าง composer install อย่างที่ Filament publish asset ของตัวเอง lwt จึงดู git status ก่อนและหลังตั้ง ไฟล์ที่ git เก็บอยู่แล้วโดนเปลี่ยนไประหว่างนั้นจะคืนกลับด้วย git restore ส่วนไฟล์ใหม่ที่ไม่ได้อยู่ใน .gitignore จะขึ้นรายชื่อให้ดูก่อน commit worktree ที่ได้จึง clean เหมือนตอนเพิ่งสร้าง
branch ที่มี migration ได้ฐานข้อมูลของตัวเอง
ทุก worktree ต่อฐานข้อมูลตัวเดียวกันกับโฟลเดอร์หลัก ถ้า branch หนึ่ง migrate เพิ่ม column หรือลบตาราง โฟลเดอร์อื่นก็โดนไปด้วย สำหรับ branch แบบนี้ให้ใส่ --db
--db=copyสร้างฐานข้อมูลชื่อ<ฐานข้อมูลเดิม>_<ท้ายชื่อ branch>คัดลอกข้อมูลเดิมเข้าไปด้วยpg_dump | psqlบน PostgreSQL หรือmysqldump | mysqlบน MySQL แล้วสั่งphp artisan migrateบนฐานข้อมูลใหม่นั้น migration ของ branch จึงไม่แตะฐานข้อมูลของโฟลเดอร์หลัก--db=emptyสร้างฐานข้อมูลเปล่าแล้ว migrate ใส่--seedถ้าอยากได้ข้อมูลตั้งต้น- SQLite ได้ไฟล์ของตัวเองในโฟลเดอร์ worktree
- ตอนสั่ง migrate lwt ใส่
DB_DATABASEของฐานข้อมูลใหม่ไปกับคำสั่งด้วย เผื่อมีค่าใน environment ของ shell ที่ทับ.envอยู่
lwt สร้างฐานข้อมูลเฉพาะบนเครื่องเรา ถ้า DB_HOST เป็นเซิร์ฟเวอร์อื่น lwt จะไม่สร้างให้และใช้ฐานข้อมูลเดิมต่อ เพราะ .env ที่ตั้งผิดเพียงบรรทัดเดียวอาจพาไปสร้างฐานข้อมูลบนเซิร์ฟเวอร์ที่คนอื่นใช้อยู่
บน MySQL โปรเจกต์ส่วนใหญ่ใช้ user ของแอปที่มีสิทธิ์แค่ฐานข้อมูลของตัวเอง สร้างฐานข้อมูลใหม่ไม่ได้ จะเห็น error 1044 Access denied ให้ใส่ --db-admin=root lwt จะสร้างฐานข้อมูลด้วย root แล้วให้สิทธิ์ user ของแอปเฉพาะฐานข้อมูลนั้น (บน PostgreSQL จะตั้ง user ของแอปเป็นเจ้าของ) รหัสผ่านของ admin อ่านจาก LWT_DB_ADMIN_PASSWORD ถ้ามี
lwt add feat/export-pdf --db=copy --db-admin=root
test ยังใช้ฐานข้อมูล testing ชุดเดียวกันทุกโฟลเดอร์
lwt แยกเฉพาะฐานข้อมูลของแอป ไม่แยกฐานข้อมูลของ test โปรเจกต์ของผมส่วนใหญ่ตั้งไว้แบบนี้ คือ test รันด้วย Pest บนฐานข้อมูล testing ที่โหลดจาก snapshot ด้วย php artisan snapshot:load testing --env=testing --force ของ spatie/laravel-db-snapshots และทุก test อยู่ใน DatabaseTransactions จึงย้อนกลับเองทุกครั้ง หลายโฟลเดอร์รัน test พร้อมกันบนฐานข้อมูลนี้ได้ บางโปรเจกต์ยังมี test ที่เช็กว่ารันอยู่บนฐานข้อมูลชื่อนี้จริง กันไม่ให้ test ไปเขียนฐานข้อมูลอื่น ถ้าแยกชื่อตาม worktree test ตัวนี้ก็จะไม่ผ่าน
ตอนตั้ง lwt จะเปิดดูฐานข้อมูล testing ที่ .env.testing กับ phpunit.xml ชี้ไว้ แล้วบอกจำนวนตารางในบรรทัด tests ตอนท้าย ถ้ายังไม่มีหรือยังว่าง lwt จะบอกคำสั่งที่ต้องรัน
! The tests' database shop_testing doesn't exist — load it once, while no other checkout is testing: php artisan snapshot:load testing --env=testing --force
พอใช้ร่วมกันก็มีคำสั่งที่ต้องระวัง
- อย่าสั่ง
migrate:freshหรือphp artisan test --parallelระหว่างที่โฟลเดอร์อื่นรัน test อยู่ ทั้งสองอย่างล้างหรือสร้างฐานข้อมูลใหม่ทับ snapshot:loadก็โหลดทับทั้งฐานข้อมูลเหมือนกัน รันตอนที่ไม่มีใครรัน test อยู่- branch ที่เพิ่ม column ใหม่ และมี test ที่อ่านหรือเขียน column นั้น ให้ migrate เฉพาะไฟล์นั้นเข้าไป
php artisan migrate --path=database/migrations/<ไฟล์> --env=testingcolumn ที่เพิ่มเข้ามาไม่กระทบโฟลเดอร์อื่นที่ยังไม่ใช้
lwt setup ตั้ง worktree ที่สร้างจากที่อื่น
worktree ไม่ได้มาจาก lwt add เสมอไป บางทีสร้างด้วย git worktree add เอง จากแท็บ Worktrees ของ git-prompt หรือ Claude Code สร้างให้ใน .claude/worktrees/ เข้าไปในโฟลเดอร์นั้นแล้วสั่ง
lwt setup
ได้ทุกขั้นเหมือน lwt add ยกเว้นการสร้าง branch สั่งซ้ำได้ ของที่ตั้งไว้แล้วจะเก็บไว้เหมือนเดิม
ถ้าใช้ AI agent ให้ใส่ -n ซึ่งไม่ถามอะไรเลย ใช้ค่าเริ่มต้นกับ option ที่ใส่มาเท่านั้น และ exit code จะไม่ใช่ 0 ถ้ามีขั้นไหนที่ขอแล้วไม่สำเร็จ เช่น สร้างฐานข้อมูลไม่ได้ หรือ migrate ไม่ผ่าน agent จะได้รู้ว่าต้องหยุดก่อน ไม่ไป migrate ต่อบนฐานข้อมูลที่ใช้ร่วมกัน
lwt setup -n # worktree ที่ agent สร้าง
lwt add feat/export-pdf -n --db=copy # branch ที่มี migration
lwt remove feat/export-pdf -n --drop-db --delete-branch
ผมเขียนวิธีใช้แบบนี้ไว้ใน skill ของ Claude Code ทุก session ที่ทำงานใน worktree จึงสั่ง lwt setup -n ก่อน ไม่ต้องตั้งเองทีละขั้น
lwt remove ลบให้ครบทั้งโฟลเดอร์ ฐานข้อมูล และ branch
git worktree remove ลบแค่โฟลเดอร์ branch ยังอยู่ ฐานข้อมูลที่สร้างไว้ก็ยังอยู่ และถ้าเป็น worktree ใน .claude/worktrees/ ก็เหลือโฟลเดอร์ว่างค้างไว้ lwt remove เก็บให้ครบในคำสั่งเดียว

ฐานข้อมูลกับ branch ถามก่อนลบทุกครั้ง ภาพนี้ตอบ Enter ทั้งสองข้อ
- ถ้าในโฟลเดอร์ยังมีไฟล์ที่แก้ค้างหรือไฟล์ใหม่ที่ยังไม่ commit lwt จะไม่ลบเลย ไฟล์ที่ gitignore ไว้อย่าง
vendor,node_modulesและ.envไม่นับ - ลบเฉพาะฐานข้อมูลที่ lwt สร้างเอง lwt จดชื่อไว้ในโฟลเดอร์
.git/worktrees/<ชื่อ>/ของ worktree นั้น ซึ่งอยู่นอกไฟล์งาน ไม่มีทางติดไปใน commit และ git ลบให้เองพร้อม worktree และไม่ลบฐานข้อมูลที่ชื่อตรงกับของโฟลเดอร์หลักแน่นอน - branch ที่ merge เข้า branch หลักแล้ว ค่าที่เลือกไว้คือลบ ถ้ายังมี commit ที่ branch หลักไม่มี จะบอกจำนวน commit แล้วถามยืนยันอีกรอบ
- ถ้า parent folder อย่าง
.claude/worktrees/ว่างหลังลบ ก็ลบโฟลเดอร์นั้นด้วย แต่ไม่แตะโฟลเดอร์ที่โฟลเดอร์หลักอยู่ - ถ้าเผลอลบโฟลเดอร์ทิ้งเองไปแล้ว
lwt removeจะgit worktree pruneให้ แล้วจัดการ branch ต่อ
คำสั่งทั้งหมด
| คำสั่ง | ทำอะไร |
|---|---|
lwt add [branch] |
fetch สร้างหรือเลือก branch เลือกโฟลเดอร์ git worktree add แล้วตั้งให้ |
lwt setup [path] |
ตั้ง worktree ที่มีอยู่แล้ว ค่าเริ่มต้นคือโฟลเดอร์ที่อยู่ |
lwt remove [path|branch] |
ลบ worktree ที่ clean ฐานข้อมูลที่ lwt สร้าง และ branch ถ้าเลือก |
lwt list |
ทุก worktree ตั้งแล้วหรือยัง และฐานข้อมูลที่ lwt สร้างให้ |
| Option | |
|---|---|
--db=shared|empty|copy |
ฐานข้อมูลเดิม (ค่าเริ่มต้น) ฐานข้อมูลใหม่ที่ migrate แล้ว หรือฐานข้อมูลที่คัดลอกจากเดิม |
--db-admin=<user> |
สร้างฐานข้อมูลด้วย root หรือ postgres เมื่อ user ของแอปไม่มีสิทธิ์ |
--url=<url> |
APP_URL ของ worktree |
--npm, --build |
node_modules และ npm run build |
--vendor=install |
บังคับ composer install แม้ lockfile เหมือนกัน |
--base=<ref>, --path=<โฟลเดอร์> |
ของ add แตก branch จากไหน และวางโฟลเดอร์ไว้ที่ไหน |
--delete-branch, --drop-db |
ของ remove ตอนใช้กับ -n |
-n |
ไม่ถามอะไรเลย สำหรับ script และ AI agent |
GitHub: phattarachai/worktree-prompt






