jj v0.43.0 เพิ่มคำสั่ง jj run — รันคำสั่งเดียวให้ทำงานทั่วทั้ง stack ของ commit
jj v0.43.0 เพิ่มคำสั่ง jj run สำหรับรันคำสั่งเดียว แล้วให้ jj ไปแก้งานทุกจุดที่ทำค้างไว้พร้อมกันทีเดียว เป็นท่าที่ git ทำได้แต่ยุ่งกว่า และเผยให้เห็นว่าโมเดลของ jj ต่างจาก git ตรงไหน

jj v0.43.0 เพิ่งปล่อยออกมาพร้อมคำสั่งใหม่ชื่อ jj run ที่ให้รันคำสั่งเดียว แล้วไล่แก้โค้ดทุกจุดของงานที่ทำค้างไว้พร้อมกันในทีเดียว ส่วน jj หรือชื่อเต็มว่า Jujutsu เป็นเครื่องมือจัดการเวอร์ชันโค้ดที่ git-compatible ใช้แทน git บน repo เดิมได้ ไม่ต้องย้ายของ
เวลาเขียนโค้ดจริง มักมีงานค้างซ้อนกันเป็นชั้น ๆ หลาย commit เวลาอยากแก้อะไรให้เหมือนกันทุกชั้น เช่น จัด format โค้ดใหม่ หรือเปลี่ยนชื่อฟังก์ชันทั้งชุด ปกติต้องไล่ทำทีละจุด jj run ทำให้ทั้งหมดนี้เหลือแค่คำสั่งบรรทัดเดียว ท่าเดียวกันนี้ git ทำได้อยู่แล้ว แต่ยุ่งกว่าและสะดุดง่ายกว่า
ปวดหัวตอนงานซ้อนกันหลายชั้น
เวลาเขียนโค้ดจริง งานมักไม่ได้จบแค่ commit เดียว หลายครั้งจะมี commit ค้างซ้อนกันเป็นสายยาว ที่เรียกกันว่า stack คือตัวล่างเป็นฐาน แล้วตัวบนต่อยอดขึ้นไปเรื่อย ๆ ทั้งที่งานยังไม่เสร็จดี
ปัญหาโผล่มาตอนที่อยากแก้อะไรบางอย่างให้เหมือนกันทั้งสาย เช่น รัน cargo fix เก็บ warning, จัด format โค้ดใหม่ทั้งไฟล์, หรือเปลี่ยนชื่อฟังก์ชันที่ใช้ร่วมกันในทุก commit สิ่งที่อยากได้คือสั่งรันคำสั่งนี้ให้ครบทุก commit ในสาย ไม่ใช่แค่ตัวบนสุดตัวเดียว
ใน git ท่านี้ทำได้ด้วย git rebase --exec โดยสั่งให้รันคำสั่งหนึ่งในทุก commit ระหว่าง rebase แต่มันสะดุดง่ายกว่าที่คิด เพราะ git มี working directory อยู่ชุดเดียว พอคำสั่งไปแก้ไฟล์แล้วผลไปชนกับการแก้ของ commit ถัดไป git จะหยุดกลางทาง แล้วให้ไล่แก้ conflict เองทีละ step กว่าจะครบทั้งสาย
สั่งทีเดียว ทั่วทั้งสาย

jj v0.43.0 แก้จุดนี้ด้วยคำสั่งใหม่ jj run หลักการคือรับคำสั่งหนึ่งไปรันกับชุดของ change หลายตัวพร้อมกัน โดย jj เรียก commit ว่า change แต่ประเด็นสำคัญคือแต่ละ change จะได้ working copy ของตัวเองแยกกัน ไม่ต้องแย่งไดเรกทอรีเดียวกันแบบ git
คำสั่งที่รันสามารถแก้ไฟล์ได้ตามปกติ เมื่อแก้เสร็จ jj จะเก็บผลของแต่ละ change ไว้ แล้วไล่อัปเดต change ที่อยู่ถัดขึ้นไปให้เอง ทั้งส่วนที่แก้และ conflict ที่ตามมา โดยไม่ต้องมานั่งไล่ rebase ทีละชั้น
หน้า release ทางการของ jj ยกตัวอย่างการใช้ไว้ตรงๆ สองแบบ:
jj run -- cargo check --all-features
jj run -- cargo fix
อ่านแล้วเดาออกได้ทันทีว่าสองคำสั่งนี้ทำอะไร บรรทัดแรกสั่งให้เช็คโค้ดแบบเปิด feature ครบในทุก commit ของสาย ส่วนบรรทัดที่สองสั่งให้ cargo fix เก็บ warning ให้ทุก commit ในทีเดียว
เทียบท่าเดิมของ git กับ jj run ให้เห็นภาพ:
| จุดที่ต่าง | git rebase --exec | jj run |
|---|---|---|
| working copy | มีชุดเดียว ใช้ร่วมกันทุก commit | แต่ละ change มีของตัวเอง |
| พอเจอ conflict | หยุดกลางทาง ให้แก้ทีละ step | เก็บไว้ใน change แล้วไปต่อได้ |
| การกระจายผลการแก้ | ต้องไล่จัดการเอง | jj ส่งต่อให้อัตโนมัติ |
ทำไม jj ถึงทำได้ลื่นกว่า

ความลื่นของ jj run ไม่ได้มาจากตัวคำสั่ง แต่มาจากโมเดลข้างใต้ที่ออกแบบต่างจาก git มาตั้งแต่แรก
อย่างแรก ทุก change ใน jj มี snapshot ของตัวเอง jj จึงมองสถานะโค้ดในแต่ละจุดเป็นภาพนิ่งที่แยกกันชัดเจน การเอาคำสั่งไปรันในแต่ละ change แล้วได้ working copy ส่วนตัวจึงเป็นเรื่องปกติของมัน ไม่ใช่ฟีเจอร์ที่เอามาแปะเพิ่มทีหลัง
อย่างที่สอง พอแก้ change ตัวล่าง jj จะจัดเรียง change ที่ต่อยอดอยู่ข้างบนให้ใหม่เองอัตโนมัติ นี่คือเหตุผลที่ผลของ jj run ไหลขึ้นไปทั้งสายได้ โดยไม่ต้องสั่ง rebase เพิ่ม
อย่างที่สาม jj มอง conflict เป็นสถานะปกติที่เก็บไว้ใน change ได้เลยโดยไม่บล็อกงาน ต่างจาก git ที่พอชน conflict แล้วต้องหยุดทุกอย่างจนกว่าจะแก้เสร็จ เมื่อ conflict ไม่ใช่สิ่งที่ทำให้งานค้าง การรันคำสั่งทั่วทั้ง stack จึงเดินหน้าจนจบได้ในทีเดียว แล้วค่อยกลับมาไล่แก้ conflict ทีหลัง
ใน jj การชน conflict ไม่ใช่สัญญาณให้หยุด แต่เป็นสถานะที่เก็บไว้ในงานได้
v0.43.0 เปลี่ยนอะไรอีกบ้าง
นอกจาก jj run แล้ว v0.43.0 ยังมีของเล็กๆ ที่คนใช้ jj อยู่แล้วน่าจะได้ใช้:
- jj show --reversed ดูรายละเอียด commit โดยไล่ลำดับกลับด้าน เหมาะเวลาอยากอ่านจากเก่าไปใหม่
- forks() ฟังก์ชัน revset ตัวใหม่ ที่ดึง commit ซึ่งมีลูกมากกว่าหนึ่งตัว คือจุดที่ประวัติแตกสายออกไป ใช้ตอนอยากรู้ว่างานแยกสายตรงไหน
- อ่าน config จาก /etc/jj ได้ ตั้งค่ากลางระดับเครื่องได้ เหมาะกับเครื่องที่หลายคนใช้ร่วมกัน หรืออยากวางค่ามาตรฐานให้ทั้งทีม
ควรอัปเดตไหม
เหตุผลที่ชัดที่สุดของการอัปเดตเป็น v0.43.0 คือมันแก้บั๊กเรื่อง Git object เสียหายบน CPU Intel Raptor Lake และเครื่องที่ใช้สถาปัตยกรรม aarch64 ในเวอร์ชันก่อนหน้า jj อาจรายงานว่า commit สำเร็จ ทั้งที่จริง object เสีย แล้วค่อยไปโผล่ทีหลังตอน git fsck ฟ้องว่า object พังหรือ blob หาย ข่าวดีคือ v0.43.0 แก้ปัญหานี้แล้ว เป็นบั๊กของเวอร์ชันเก่า ไม่ใช่อาการที่เพิ่งเกิดตอนนี้ ใครใช้ jj บนเครื่องกลุ่มนี้อยู่ก็ถือเป็นเหตุผลที่ควรอัปเดต
แต่ก็ต้องพูดกันตามตรง jj run เพิ่งมาในเวอร์ชันนี้เอง และตัว jj เองก็ยังอยู่ในช่วงก่อน 1.0 ฟีเจอร์นี้จึงยังน่าจับตา ไม่ใช่ของที่ผ่านการใช้งานหนักจนพิสูจน์แล้วว่านิ่ง ถ้าเป็นงานจริงที่สำคัญ ลองในโปรเจกต์ทดสอบก่อนดีกว่า อย่าเพิ่งเอา jj run ไปใช้กับ repo หลักเลย
อยากลองเองก็ไม่ยาก เริ่มจากอัปเดต jj เป็น v0.43.0 แล้วเปิด repo ที่มี commit ซ้อนกันเป็นสาย จากนั้นลองสั่ง jj run -- cargo fix ดูสักครั้ง แล้วสังเกตว่าผลของมันไหลขึ้นไปทั้ง stack แบบไหน
สิ่งที่ jj run บอกจริง ๆ จึงไม่ใช่ว่า jj มีคำสั่งเจ๋งเพิ่มมาอีกหนึ่งตัว แต่คือเมื่อโมเดลข้างใต้วางเรื่อง snapshot กับ conflict ไว้ถูกตั้งแต่แรก งานที่เคยต้องค่อย ๆ ทำทีละ step ก็กลายเป็นคำสั่งบรรทัดเดียวที่ทำงานจบได้ทั้งสาย
ที่มา: หน้า release Release v0.43.0 · jj-vcs/jj ของ jj (Jujutsu)



