1 คะแนน โดย GN⁺ 2 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • คำสั่งทดลอง git history ที่ถูกเพิ่มเข้ามาตลอดช่วง Git 2.54 และ 2.55 ช่วยทำให้การแก้ไขคอมมิตเก่าและการจัดโครงสร้างบรานช์ที่แตกออกมาทำได้อัตโนมัติ โดยยังคงเวิร์กโฟลว์ Git แบบเดิมไว้
  • fixup จะรวมการเปลี่ยนแปลงที่สเตจไว้เข้าไปในคอมมิตเก่า แล้วรีเบสบรานช์โลคัลทั้งหมดที่แตกออกมาจากคอมมิตนั้นให้ตรงกับแฮชคอมมิตใหม่โดยอัตโนมัติ
  • reword ใช้เปลี่ยนข้อความคอมมิตเก่า และ split ใช้แยกการเปลี่ยนแปลงของคอมมิตออกเป็น 2 คอมมิตตามระดับ hunk ก่อนจะจัดเรียงคอมมิตและบรานช์ถัดไปใหม่
  • คำสั่งย่อยทั้งสามมีการประมวลผลแบบ atomic ที่ปฏิเสธงานที่อาจก่อให้เกิด conflict เพื่อป้องกันไม่ให้ working tree อยู่ในสภาพเสียครึ่งๆ กลางๆ แต่จะไม่ทำงานกับประวัติที่มี merge commit
  • แม้จะยังไม่มีการจัดการ conflict เป็นพลเมืองชั้นหนึ่ง, การ undo ด้วย operation log, หรือการโมเดล working copy เป็น commit แบบ jj แต่ก็สามารถใช้ความสามารถในการแก้ไขประวัติแบบ สไตล์ jj หลายอย่างได้จาก Git core distribution โดยไม่ต้องติดตั้งเพิ่ม

ทำไมการแก้ไขประวัติ Git แบบเดิมถึงไม่สะดวก

  • เมื่อจัดการหลายการเปลี่ยนแปลงแบบขนานกัน ต้องสลับไปมาระหว่างบรานช์และคอมมิตอยู่ตลอด และระหว่าง rebase -i นั้น working tree อาจตกอยู่ในสภาพไม่สมบูรณ์ได้
  • jj ซึ่งเป็นทางเลือกแทน Git มักถูกพูดถึงว่าเป็นคำตอบของปัญหานี้ แต่แม้จะลองใช้อยู่หลายวันทุก 3 เดือนตลอด 1 ปีครึ่ง สุดท้ายก็ยังกลับมาใช้เวิร์กโฟลว์ Git แบบเดิม
  • git history เป็น คำสั่งทดลอง ที่รวมอยู่ใน Git core distribution อยู่แล้ว จึงสามารถลองใช้ได้โดยไม่ต้องติดตั้งเครื่องมือเพิ่ม
    • Git 2.54 เพิ่ม reword และ split ในเดือนเมษายน
    • Git 2.55 เพิ่ม fixup ในเดือนมิถุนายน
  • คำสั่งย่อยมีทั้งหมด 3 แบบคือ fixup, reword, split

fixup: แก้คอมมิตเก่าและบรานช์ที่แตกออกมาพร้อมกัน

  • git history fixup ใช้แก้ส่วนที่ผิดในคอมมิตเก่า และรีเบสบรานช์ที่แตกออกมาจากคอมมิตนั้นโดยอัตโนมัติ
  • เพียงสเตจการเปลี่ยนแปลงด้วย git add แบบเดียวกับขั้นตอนแก้ไขทั่วไป แล้วรัน git history fixup <commit> การเปลี่ยนแปลงที่สเตจไว้จะถูกรวมเข้าไปในคอมมิตเป้าหมาย
  • คล้ายกับการรวม git commit --fixup และ autosquash rebase เข้าด้วยกัน แต่ต่างกันตรงที่มันจะ อัปเดตบรานช์อื่นๆ ด้วย หากบรานช์เหล่านั้นมีคอมมิตเป้าหมายรวมอยู่
  • ขณะที่ git rebase --update-refs ย้ายเฉพาะ reference ที่อยู่ในช่วง rebase ปัจจุบัน git history จะค้นหาและเขียนใหม่ให้กับ บรานช์โลคัลทั้งหมด ที่แตกออกมาจากคอมมิตเป้าหมาย
    • หากต้องการก็สามารถจำกัดขอบเขตการอัปเดตให้เหลือเฉพาะบรานช์ปัจจุบันได้
    • จะไม่ทำงานกับประวัติที่มี merge commit จึงอาจใช้ได้ยากกับบางรูปแบบการใช้งาน Git
  • เมื่อรวมการแก้ไขเข้า B จะเกิด B* ที่มีแฮชใหม่ และ C กับ D ที่อยู่ถัดขึ้นไปก็จะถูกสร้างใหม่เป็น C* และ D* ตามลำดับ
    • ปลายบรานช์ของ feat-1 และ feat-2 ก็จะเลื่อนไปตามคอมมิตใหม่ด้วย

การประมวลผลแบบ atomic และข้อจำกัดเรื่อง conflict

  • คำสั่งย่อยทั้งสามจะทำงานแบบ atomic ทั้งหมด จึงไม่ทิ้ง working tree ไว้ในสภาพพังครึ่งๆ กลางๆ
  • การรับประกันความเป็น atomic ทำโดยปฏิเสธงานที่อาจทำให้เกิด conflict ตั้งแต่ต้น
  • นั่นทำให้มีข้อจำกัดด้านความสามารถมากกว่า jj ที่ จัดการ conflict เป็น first-class object และคงสถานะ conflict ระหว่าง rebase ไว้เพื่อมาแก้ภายหลังได้
  • ข้อจำกัดของ git history ถูกกำหนดไว้โดยตั้งใจ เพื่อไม่ให้การเขียนประวัติใหม่กลายเป็นงานที่ต้องรักษาสถานะระหว่างทาง
    • หาก Git รองรับ first-class conflict ในอนาคต ก็อาจยกเลิกข้อจำกัดเหล่านี้ได้

reword: เปลี่ยนข้อความคอมมิตโดยไม่ยุ่งกับ working tree

  • git history reword ใช้เปลี่ยนข้อความของคอมมิตเก่า แล้วจัดโครงสร้างคอมมิตและบรานช์ด้านบนใหม่โดยอัตโนมัติ
  • เมื่อรัน git history reword <commit> โปรแกรมแก้ไขข้อความจะเปิดขึ้นพร้อมข้อความเดิมของคอมมิต และเมื่อแก้ไขแล้วบันทึก สแตกของคอมมิตถัดไปจะถูกสร้างใหม่ให้สอดคล้องกับแฮชใหม่
  • มีประโยชน์เมื่อระหว่างการพัฒนาซ้ำๆ โครงสร้างการออกแบบเปลี่ยนไป และต้องย้อนกลับไปแก้ข้อความคอมมิตก่อนหน้าให้สอดคล้องกับโครงสร้างปัจจุบัน
  • ทำงานแบบเดียวกับ fixup ยกเว้นว่ามันเปลี่ยนเฉพาะข้อความคอมมิต ไม่ได้เปลี่ยนเนื้อหาใน tree
  • reword จะไม่แตะต้อง index หรือ working tree แต่ เปลี่ยนเฉพาะ commit graph
    • จึงสามารถแก้คอมมิตในบรานช์ที่ไม่ได้ checkout อยู่ได้ โดยไม่รบกวนงานปัจจุบัน
    • เมื่อเปลี่ยนข้อความของ B แฮชก็จะเปลี่ยนตาม ทำให้ C ที่ตามมาถูกสร้างใหม่เป็น C* และ feat-1 ก็จะชี้ตามคอมมิตใหม่

split: แยก 1 คอมมิตออกเป็น 2 คอมมิต

  • git history split ใช้เลือกการเปลี่ยนแปลงของคอมมิตหนึ่งแบบโต้ตอบ แล้วแยกออกเป็น 2 คอมมิต
  • เมื่อรัน git history split <commit> จะมีพรอมป์ต์ให้เลือกการเปลี่ยนแปลงของคอมมิตเป้าหมายในระดับ hunk
    • hunk ที่เลือกเก็บไว้จะประกอบเป็นคอมมิตแรก
    • hunk ที่เหลือจะไปอยู่ในคอมมิตที่สอง
  • คล้าย git add -p แต่ไม่ต้องผ่านขั้นตอนซับซ้อนที่ต้องนำ git rebase มาผสมกัน
  • หากแยก B ที่มีการเปลี่ยนแปลง 2 อย่างซึ่งไม่เกี่ยวข้องกันออกจากกัน จะได้ B1 และ B2 และ C ที่ตามมาจะถูก สร้างใหม่เป็น C* บนคอมมิตทั้งสองนั้น
  • แม้จะเป็นคำสั่งที่มีขอบเขตการใช้งานเฉพาะที่สุดในสามคำสั่ง แต่ก็มีประโยชน์เมื่อจำเป็นต้องแยกคอมมิตจริงๆ

ความต่างด้านความสามารถเมื่อเทียบกับ jj

  • git history ไม่ได้เข้ามาแทนที่โมเดลการทำงานทั้งหมดที่ jj มีให้
  • ในทางกลับกัน git history รองรับการแก้คอมมิตเก่า การเปลี่ยนข้อความ การแยกคอมมิต และการอัปเดตบรานช์ที่แตกออกมา ภายใน Git แบบเดิม จึงช่วยนำ ข้อดีบางส่วนของ jj เข้ามาใช้ในเวิร์กโฟลว์ปัจจุบันได้
  • ข้อจำกัดต่างๆ ยังมีโอกาสถูกผ่อนคลายในอนาคต จึงยังมีพื้นที่ให้ Git รุ่นถัดๆ ไปพัฒนาความสามารถเพิ่มได้อีก

1 ความคิดเห็น

 
GN⁺ 2 시간 전
ความคิดเห็นบน Hacker News
  • เคยรู้สึกว่า Git ใช้งานไม่สะดวก แต่ดีขึ้นหลังจากอ่านสามบทแรกของหนังสือ Pro Git ที่เปิดให้อ่านฟรี หนังสือให้ โมเดลทางความคิด ที่ยอดเยี่ยมเกี่ยวกับการทำงานภายในของ Git และไม่ว่าจะดีหรือร้าย อินเทอร์เฟซผู้ใช้ของ Git ก็สะท้อนโครงสร้างภายในโดยตรง พอเข้าใจสิ่งนี้ ทุกอย่างก็ลงตัว

    • ไม่เห็นด้วยว่าอินเทอร์เฟซผู้ใช้สะท้อนโครงสร้างภายในโดยตรง เช่น git checkout ถูกออกแบบให้ไม่ทำให้การเปลี่ยนแปลงที่ยังไม่ได้เข้า Git หายไปเมื่อสลับบรานช์ แต่เมื่อกู้คืนไฟล์เป็นเวอร์ชันอื่นกลับทำหน้าที่ย้อนการเปลี่ยนแปลง นี่สะท้อนโครงสร้างภายในโดยตรงอย่างไร?
      แยกจากประเด็นนี้ ความไม่สม่ำเสมอของ CLI ก็ทำให้ Git ใช้งานยาก เพราะแม้จะรู้ว่าข้างในอยากทำอะไรและต้องใช้คำสั่งอะไร ก็ยังหาซินแท็กซ์บรรทัดคำสั่งที่ถูกต้องได้ยาก
    • Git ไม่ได้สับสนอย่างที่ขึ้นชื่อกัน แค่ใช้เวลาสักไม่กี่ชั่วโมงเพื่อทำความเข้าใจก็พอ น่าเสียดายที่บางคนไม่เรียนรู้มันทั้งที่เป็นเครื่องมือจำเป็นของวิศวกรซอฟต์แวร์ และถ้าเป็นมืออาชีพ แค่ git commit -am “Changes” ก็ไม่เพียงพอ
    • ผมเองก็เคยสับสนกับคำสั่ง Git แต่พอได้ดูวิดีโอ YouTube ที่ Linus อธิบาย โครงสร้างข้อมูลภายใน ของ Git ก็รู้ว่ามันเรียบง่ายมาก สามารถจับคู่ได้ง่ายว่าฟังก์ชันของเครื่องมือไปจัดการโครงสร้างข้อมูลอย่างไร และกลับเป็น abstraction ระดับสูงที่ Git พยายามมอบให้นั่นเองที่ทำให้สถานการณ์สับสนกว่าเดิม
    • ไม่ใช่แบบนั้น คำสั่ง Git ครึ่งหนึ่งเป็นระดับต่ำ อีกครึ่งหนึ่งเป็นระดับสูง ดังนั้นจึงไม่ได้ สอดคล้องกับโครงสร้างภายในแบบ 1:1
    • ประสบการณ์ผู้ใช้ของ Mercurial ดีที่สุดอย่างเห็นได้ชัด และเรียนรู้ได้โดยไม่ต้องอ่านเอกสารแยกต่างหาก แม้ตอนใช้ Git ก็ใช้เหมือน Mercurial พอเกิดปัญหาก็ลบ repository ในเครื่องทิ้ง แบบนี้ก็เอาตัวรอดมาตลอดอาชีพ
  • มี git rebase --abort และก่อน rebase จะสร้าง tag ไว้ก่อน แล้วกลับไปด้วย git reset --hard $set_tag ก็ได้ สถานะก่อนหน้าไม่ได้หายไป จึงไม่มีเหตุผลต้องกลัว interactive rebase

    • ผมสร้างบรานช์ชื่อ temp หรือ before-rebase ไว้เยอะมากเพื่อจุดประสงค์นั้น ใช้เหมือน tag แต่บรานช์ย้ายได้สะดวกกว่า ดังนั้นก่อนทำ rebase -i ใหญ่ ๆ มักจะรัน git branch before-rebase/some-feature
      จริง ๆ แล้วแทบไม่เคยต้องใช้ git reset before-rebase แต่บ่อยครั้งจะใช้ git log -p before-rebase เปรียบเทียบกับบรานช์หลัง rebase เพื่อตรวจสอบ การเปลี่ยนแปลงที่ไม่ตั้งใจระหว่างการแก้ conflict
    • ไม่จำเป็นต้องสร้าง tag เพื่อความปลอดภัยแยกต่างหากด้วยซ้ำ เพราะ reflog ทำหน้าที่นั้นให้อัตโนมัติ
    • ใช้ git reflog อยู่ไหม?
    • ไม่มีอะไรจะง่ายกว่า jj undo แล้ว
    • ใช่ หลังพิมพ์ git rebase แล้ว ประมาณครึ่งหนึ่งก็พิมพ์ --abort ทันที
  • แม้จะไม่ได้มองตัวเองว่าเป็น coder หรือ programmer แต่การเรียน Git เหมือนมอบ พลังพิเศษด้านการจัดระเบียบ ให้กับวิธีคิดของผม ใช้ได้กับสารพัดอย่าง เช่น เว็บไซต์ โปรเจกต์ออกแบบ อิเล็กทรอนิกส์ การแต่งเพลง คลังความรู้ส่วนตัว สคริปต์จัดการระยะไกล การจัดการ config โค้ดสั้น ๆ ฯลฯ แม้จะไม่สมบูรณ์แบบ แต่ก็แนะนำให้ทุกคนที่ทำงานด้วยกันเรียนรู้

    • ผมเองก็มีความสามารถในการจัดระเบียบดีขึ้นมาก แต่เรื่องนี้น่าจะเป็นผลของ ระบบควบคุมเวอร์ชัน โดยรวมมากกว่า Git เอง ระบบควบคุมเวอร์ชันช่วยจัดระเบียบและเก็บรักษาสิ่งที่สร้างขึ้น และดึงสำเนาออกมาได้มากเท่าที่ต้องการเมื่อไรก็ได้ ทำให้ทำงานได้โดยไม่ต้องกังวล
      ในหนังสือ 『Getting Things Done』 ของ David Allen มีเนื้อหาว่าจำเป็นต้องมี ระบบที่เชื่อถือได้ สำหรับรวบรวมและจัดระเบียบความคิด ผมใช้ OmniFocus สำหรับงาน โปรเจกต์ และรายการต่าง ๆ แต่สำหรับการจัดระเบียบและเก็บรักษาซอฟต์แวร์กับข้อมูลบางส่วนที่สร้างบนคอมพิวเตอร์ ผมเชื่อใจ Git
    • ใช้ Git กับการแต่งเพลงอย่างไร?
  • โดยทฤษฎีแล้วชอบคำสั่งเหล่านี้มาก และใช้คำสั่งเทียบเท่าของ jj อยู่เสมอ แต่ไม่พบวิธีให้ git history ลงนาม ใน commit ที่แก้ไขแล้วได้ แม้ดูทั้งคู่มือและซอร์สโค้ดของ Git แล้ว หากแก้ไข commit ที่มีลายเซ็น GPG เดิมด้วย git history reword HEAD~ ลายเซ็นทั้งหมดก็หายไป
    เพราะอยากรักษาลายเซ็น commit ไว้ สุดท้ายจึงกลับไปใช้ git rebase -i แบบเดิม

    • เรื่องนี้ดูเหมือนเป็นสิ่งที่ขาดไปอย่างใหญ่หลวง และเหมือนจะข้ามกระบวนการจัดการ commit มาตรฐานด้วย ในคู่มือก็ระบุว่า git history เป็น ฟีเจอร์ทดลอง จึงน่าจะควรรายงานเป็นบั๊ก
  • แม้ไม่มีคำสั่งใหม่ ก็แก้ไขประวัติ commit ของ Git ในเครื่องครั้งใหญ่ได้โดยไม่ลำบากนักมาตลอดกว่า 10 ปีแล้ว ผมมั่นใจว่าเหตุผลสำคัญที่ทำให้ Git และ rebase รู้สึกยากคือ ปัญหาส่วนติดต่อผู้ใช้ ที่ผู้ใช้ต้องไปทำความเข้าใจตัวเลือกบรรทัดคำสั่งและพฤติกรรมที่ถูกต้องเอง
    บน Windows กล่องโต้ตอบ rebase ของ TortoiseGit ทั้งทรงพลังและใช้งานง่าย แค่คลิกขวาที่ commit อ้างอิงใน log แล้วเลือก "Rebase onto this" ก็พอ และอย่างที่ https://tortoisegit.org/docs/tortoisegit/tgit-dug-rebase.htm... แสดงไว้ การเปลี่ยนลำดับ แยก รวม และแก้ไข commit ทำได้อย่างชัดเจน หากยกเลิกกลางคันก็ย้อนกลับสู่สถานะเดิม
    เพียงแต่เพราะเป็น shell extension จึงต้องคลิกเยอะเกินไป ถ้าเป็นแอปแยกที่มีแท็บน่าจะดีกว่า แต่ดูเหมือนนี่เป็นหลักการออกแบบที่สืบต่อมาจาก TortoiseSVN เคยลองใช้ Git Extensions อยู่พักหนึ่ง แต่หา features ที่ใช้บ่อยไม่เจอ หรือไม่ก็ชินกับ TortoiseGit ไปแล้วเลยไม่ได้ย้ายไปใช้ หน้าจอ log ดูได้ที่ https://tortoisegit.org/docs/tortoisegit/tgit-dug-showlog.ht...

    • สิ่งหนึ่งที่คิดถึงหลังย้ายจาก Windows มา Linux คือ shell แบบบูรณาการ ที่ทุกโปรแกรมสามารถเพิ่มเมนูคลิกขวาของไฟล์/โฟลเดอร์, overlay อย่างไอคอนสถานะของ TortoiseGit, และวัตถุพิเศษอย่าง My Computer ได้ ผมเข้าใจว่ามันถึงจุดสูงสุดใน XP และตั้งแต่ Vista เป็นต้นมา Microsoft เปลี่ยนไปในทิศทางที่ทำให้ขยายได้ยากขึ้น
      แต่จนถึงตอนนี้ก็ยังไม่เข้าใจว่าทำไมเมนูบางอย่างของ VC++6 ถึงสร้าง shell object ที่หน้าตาเหมือนโฟลเดอร์แทนที่จะเป็นรายการปกติ แล้วให้แสดงในกล่องโต้ตอบเปิดไฟล์ มันเป็น wizard สำหรับเพิ่มคอมโพเนนต์เข้าโปรเจกต์ และระหว่างที่กล่องโต้ตอบเปิดอยู่ ผมก็มักจะไปเจอโฟลเดอร์ปลอมนั้นใน My Computer
    • เวิร์กโฟลว์เดิมยุ่งยากกว่านิดหน่อย และคำสั่งใหม่ก็สะดวกกว่าจริง ๆ fixup ทำได้อยู่แล้วด้วย git commit --fixup หรือก่อนหน้านั้นด้วย git rebase -i ส่วน reword ก็ทำได้ด้วย git rebase -i
      น่าจะมี feedback สะสมมากพอว่าขั้นตอนแก้ไฟล์เพื่อกำหนด action ที่จะทำกับแต่ละ commit นั้นไม่สะดวก จึงมีการเพิ่ม คำสั่งระดับสูงกว่า ที่ห่อฟีเจอร์เดิมไว้
    • ในบรรดาส่วนติดต่อผู้ใช้ของ Git ผมยังไม่เคยเห็นเครื่องมือไหนที่เข้าใกล้ Magit ได้ มันให้ทั้งฟังก์ชันเชิงลึกและอินเทอร์เฟซแบบมีสถานะที่ถูกทำให้ง่ายขึ้น
      เครื่องมือบรรทัดคำสั่งเมื่อรันคำสั่งถัดไปแล้วจะเข้าถึง output ก่อนหน้าได้ยาก แม้จะอยาก cherry-pick commit ที่เห็นใน git log แต่ถ้า hash ไม่เหลืออยู่ใน terminal ก็พิมพ์จากความจำไม่ได้ แต่อินเทอร์เฟซแบบมีสถานะช่วยคงการโต้ตอบหลายอย่างไว้พร้อมกัน และรักษาข้อมูลก่อนหน้าไว้ให้
      Git มี ข้อมูลสถานะ ที่ต้องจำจำนวนมาก เช่น branch ปัจจุบัน, branch ก่อนหน้า, ไฟล์ที่ถูกแก้ไข, commit ที่ยังไม่ได้ push หากไม่เรียนรู้เครื่องมือรอบข้างอย่างการแสดง branch และ autocomplete ของ shell plugin, หลายแท็บ/การแบ่งหน้าต่าง, การคัดลอกข้ามแท็บ, การค้นหา autocomplete การใช้ Git อาจกลายเป็นประสบการณ์ที่น่าหงุดหงิดมาก
  • ผมมองว่าจุดอ่อนที่แท้จริงของ jj มีเพียง การเชื่อมกับ Git เท่านั้น มันทำให้ domain-specific language สำหรับชุดไฟล์, ชุด revision และ template เป็นระบบ แล้วใช้สอดคล้องกันทั้งในการตั้งค่าและ CLI พร้อมทั้งไม่เปิดเผยโครงสร้างภายในโดยตรง แต่ให้แนวคิดที่ทรงพลังผ่านอินเทอร์เฟซ
    แต่เพราะยืดหยุ่นเกินไป ความสามารถในการทำงานร่วมกับ Git จึงพึ่งพาขนบและวินัยอย่างมาก และแทบทุกสถานะของ jj สามารถแทนด้วย Git ได้ ทำให้ทำ remote repository พังได้ง่าย หากจะกู้คืนก็ต้องเข้าใจ Git อย่างลึกซึ้ง
    ถ้าเป็นโปรเจกต์พัฒนาเดี่ยวใหม่ ๆ jj ก็น่าลองใช้อยู่ มองภายใต้เงื่อนไขที่เอื้ออำนวยแล้วให้ความรู้สึกเหมือน Git ยุคแรกเมื่อเทียบกับ Subversion และถ้าคุ้นกับวิธีคิดของ jj เองก่อน ก็จะเตรียมรับมือกับการเชื่อม Git ที่สมบุกสมบันในภายหลังได้ดีขึ้นเล็กน้อย บริบทที่เกี่ยวข้องอยู่ที่ https://news.ycombinator.com/item?id=48903054

    • ตามหลักแล้ว jj มี backend ที่ไม่ใช่ Git ด้วย แต่ตอนนี้สิ่งเดียวที่ถูกระบุว่าเหมาะกับสภาพแวดล้อมใช้งานจริงคือ Git backend เท่านั้น jj ที่ไม่มีการเชื่อมกับ Git ยังใกล้กับแนวคิดมากกว่าความเป็นจริง
      ผมชอบ jj และ mental model ด้าน version control ของมัน จึงจะใช้ต่อไป แต่บางครั้งก็ต้องลงไปใช้ Git เพื่อให้งานจบ ทำให้รู้สึกเหมือนเป็น wrapper ที่สะดวกบน Git มากกว่าจะเป็นระบบจัดการเวอร์ชันใหม่ ถ้า Git เพิ่มคำสั่งที่รองรับ workflow ของ jj ขึ้นมา ก็ยากที่จะหาเหตุผลให้ติดตั้ง jj แยกต่างหาก
  • วิธีที่ git history fixup rebase branch ลูกหลานทั้งหมดโดยอัตโนมัติไม่เข้ากับ workflow ของผม ผมเก็บแต่ละเวอร์ชันของ feature branch ไว้เป็น myfeature.1, myfeature.2 เพื่อจะได้หาเวอร์ชันเก่าเมื่อพฤติกรรมเปลี่ยนไปในเวอร์ชันใหม่ ซึ่ง git reflog ทดแทนสิ่งนี้ไม่ได้
    ตัวอย่างเช่น ถ้าปัญหาเกิดขึ้นระหว่าง myfeature.58 กับ myfeature.59 และแต่ละ feature มี 15 commit ก็สามารถใช้ git range-diff myfeature.58~15..myfeature.58 myfeature.59~15..myfeature.59 เพื่อดูความต่างของสองเวอร์ชันแยกตาม commit ได้ แม้ base commit จะต่างกันก็ตาม ดังนั้นผมจึงไม่ต้องการ การ rebase อัตโนมัติของทุก branch ที่มี commit เฉพาะนั้นอยู่ และวิธีใช้ tag กับเวอร์ชันเก่าก็ไม่ค่อยเข้ากันนัก

  • Git รุ่นล่าสุดนำงาน git rebase --interactive ที่ใช้บ่อยสามอย่างมาทำเป็นคำสั่งแยกที่มีแรงเสียดทานต่ำ และดูเหมือนจะทำงานเฉพาะเมื่อไม่มี conflict

    • ผมสงสัยว่า git history มีประโยชน์จริงแค่ไหน ผมชอบใช้ interactive rebase เพื่อย้าย commit แบบมองเห็นภาพมากกว่า และไม่อยากเผลอระบุ orphan commit hash ที่หายไปจาก local branch ปัจจุบันให้เป็นเป้าหมายของ git history
  • ไม่เข้าใจว่าทำไมต้องทุ่มแรงขัดเกลาประวัติ Git ให้สมบูรณ์แบบขนาดนั้น ยังไงก็ไม่ได้กลับไปอ่านคอมมิตทีละอันอยู่แล้ว ดังนั้นก่อน merge ก็แค่ squash ทั้งหมด ก็พอ

    • ไม่เห็นด้วยอย่างแรง เวลาตรวจ PR อย่างน้อยก็อ่านประวัติคอมมิตเพื่อจับลำดับการเปลี่ยนแปลง และถ้ามีแต่ "oops" กับ "fix" เต็มไปหมดก็จะตีกลับทันที
      คอมมิตคือสิ่งที่เล่าเรื่องราวของงาน ดังนั้นควรให้ ประวัติคอมมิตที่อ่านง่าย เพื่อไม่ให้ผู้ตรวจต้องลำบากมากขึ้นในการทำความเข้าใจเจตนา
    • ผมอ่านคอมมิตทีละอันบ่อย ใน repository ที่ดูแลอย่างพิถีพิถัน มันมีประโยชน์มากในการไล่หาบั๊กหรือ regression และทำความเข้าใจ เจตนาของการเปลี่ยนแปลง ของผู้เขียน
    • เวลาสงสัยว่าทำไมโค้ดถึงออกมาเป็นแบบนี้ การดูบริบทของคอมมิตที่นำโค้ดนั้นเข้ามาช่วยได้มาก ถ้าเป็นคอมมิตก้อนใหญ่ที่ยัดการเปลี่ยนแปลงต่างชนิดกันไว้รวมกัน คุณค่าของมันก็ลดลงไปมาก
    • เป็นความคิดที่บอกชัดว่าไม่ได้ทำงานในทีมใหญ่ พอกลับมาจากลาพัก สิ่งแรกที่ทำคืออ่านประวัติ Git เพื่อดูว่าระหว่างนั้นมีอะไรเปลี่ยนไปบ้าง และเวลาที่ CI พังก็มีประโยชน์มาก จนแทบจะต้องดูประวัติในรูปแบบใดรูปแบบหนึ่งทุกวัน
      ถ้า codebase กับฟีเจอร์เล็กมาก จะ squash ทั้งหมดก็ได้ แต่ผมชอบวิธีที่เห็นลำดับการเปลี่ยนแปลงจริง และสามารถ cherry-pick หรือ revert คอมมิตได้แบบเลือกเฉพาะ
    • ประวัติที่จัดไว้ดี โดยให้หนึ่งคอมมิตต่อหนึ่งการเปลี่ยนแปลงเชิงตรรกะ มีประโยชน์อย่างยิ่งเวลาทำ bisect เพื่อหา regression
  • จากสถานะที่ checkout อยู่ที่ D ในตอนนี้ เมื่อเปลี่ยน B เป็น B' ผมเข้าใจว่า git rebase --update-refs จะปล่อย E ซึ่งอยู่นอกช่วง rebase ไว้ตามเดิม แล้วสร้างแค่ B' → C' → D' โดยยังคง B → E เดิมไว้
    ในทางกลับกัน git history fixup จะอัปเดตไปจนถึง E ให้เป็น E' ด้วยหรือไม่? ถ้าเป็นอย่างนั้นก็อยากรู้ว่าด้วย git rebase ที่ใช้จนชินมาหลายสิบปี มีวิธีทำ การอัปเดต branch ลูกหลานทั้งหมด ได้หรือเปล่า

    • แฮชของคอมมิตสะท้อนแฮชของคอมมิตพาเรนต์ด้วย ดังนั้นจะปล่อย E ไว้เหมือนเดิมไม่ได้ ผมยังไม่ได้ลองใช้ --update-refs เอง แต่ถ้าตามเอกสารแล้ว ผลลัพธ์น่าจะเป็นกราฟที่สามซึ่งมีถึง E' ดังนั้นพฤติกรรมที่ต้องการนั้นทำได้อยู่แล้วใน git rebase
    • ดู https://blog.hot-coffee.dev/en/blog/git_update_refs/ ได้