Git Advanced Techniques สำหรับ Team Development

ในโลกของการพัฒนาซอฟต์แวร์ที่เปลี่ยนแปลงอย่างรวดเร็วในปัจจุบัน การทำงานร่วมกันเป็นหัวใจสำคัญของความสำเร็จ และ Git ก็เป็นเครื่องมือที่ขาดไม่ได้ในการจัดการ Source Code แต่การใช้ Git แค่พื้นฐานอาจไม่เพียงพอสำหรับทีมพัฒนาที่ต้องการประสิทธิภาพสูงสุด ความเข้าใจและประยุกต์ใช้เทคนิค Git ขั้นสูงจะช่วยให้ทีมของคุณทำงานได้อย่างราบรื่นขึ้น ลดความผิดพลาด จัดการประวัติการเปลี่ยนแปลงได้อย่างมีระเบียบ และแก้ไขปัญหาได้อย่างรวดเร็ว ในบทความนี้ SiamLancard.com จะพาทุกท่านดำดิ่งสู่โลกของ Git Advanced Techniques ที่ออกแบบมาเพื่อยกระดับการทำงานร่วมกันของทีมพัฒนาโดยเฉพาะครับ

สารบัญ

ทำไมต้อง Git Advanced Techniques สำหรับ Team Development?

สำหรับทีมพัฒนาซอฟต์แวร์ การใช้ Git เพียงแค่คำสั่งพื้นฐานอย่าง git add, git commit, git push, และ git pull อาจเพียงพอสำหรับการเริ่มต้น แต่เมื่อโปรเจกต์ซับซ้อนขึ้น ทีมมีขนาดใหญ่ขึ้น หรือต้องการ maintainable history ที่ดีขึ้น คำสั่งพื้นฐานเหล่านี้ก็เริ่มมีข้อจำกัดครับ

การเรียนรู้และนำ Git Advanced Techniques มาใช้ จะช่วยให้ทีม:

  • เพิ่มประสิทธิภาพการทำงานร่วมกัน: ลดปัญหา Conflict และทำให้การ Review Code ง่ายขึ้น
  • จัดการประวัติโค้ดให้สะอาดและชัดเจน: ทำให้การ Debug หรือการย้อนกลับไปดู Commit เก่า ๆ เป็นเรื่องง่าย
  • แก้ไขข้อผิดพลาดได้อย่างรวดเร็ว: มีเครื่องมือในการกู้คืนโค้ดหรือหาสาเหตุของ Bug ได้อย่างมีประสิทธิภาพ
  • ปรับแต่ง Workflow ให้เข้ากับทีม: สามารถสร้างกฎเกณฑ์หรือ Automation เพื่อลดภาระงานซ้ำซ้อน
  • ลดความซับซ้อนของโปรเจกต์: โดยเฉพาะโปรเจกต์ที่มีหลาย Component หรือหลายเวอร์ชัน

มาดูกันว่าเทคนิคเหล่านี้มีอะไรบ้างและจะช่วยทีมของคุณได้อย่างไรครับ

กลยุทธ์การแตก Branch ที่มีประสิทธิภาพ

กลยุทธ์การแตก Branch เป็นสิ่งสำคัญที่ช่วยจัดระเบียบการพัฒนา ทำให้การทำงานพร้อมกันหลาย Feature หรือการจัดการ Release เป็นไปได้อย่างราบรื่น มีหลายกลยุทธ์ที่เป็นที่นิยม แต่ละแบบก็มีจุดเด่นและข้อจำกัดที่แตกต่างกันไปครับ

Git Flow

Git Flow เป็นกลยุทธ์ที่ซับซ้อนแต่มีประสิทธิภาพสูง เหมาะสำหรับโปรเจกต์ที่มีรอบการ Release ที่ชัดเจน และต้องการการจัดการเวอร์ชันที่เข้มงวด มักใช้กับโปรเจกต์ที่มี LTS (Long Term Support) หรือมีการ Release เป็นเวอร์ชันหลัก ๆ ครับ

โดยหลักการแล้ว Git Flow จะมี Branch หลัก 2 แบบที่คงอยู่ตลอดไป:

  • main (หรือ master): Branch ที่ใช้เก็บโค้ดที่พร้อมสำหรับ Production เสมอ
  • develop: Branch ที่ใช้รวม Feature ทั้งหมดที่จะไปสู่ Release ถัดไป

และ Branch ชั่วคราวอื่น ๆ สำหรับการพัฒนา:

  • feature branches: แตกมาจาก develop สำหรับพัฒนา Feature ใหม่ ๆ เมื่อเสร็จแล้วจะ Merge กลับเข้า develop
  • release branches: แตกมาจาก develop เพื่อเตรียม Release โดยเฉพาะ ใช้สำหรับ Bug Fixes เล็ก ๆ น้อย ๆ และการทดสอบก่อน Release เมื่อเสร็จแล้วจะ Merge เข้า main และ develop
  • hotfix branches: แตกมาจาก main เพื่อแก้ไข Bug ด่วนใน Production เมื่อเสร็จแล้วจะ Merge เข้า main และ develop

ข้อดี:

  • โครงสร้างชัดเจน เหมาะกับการจัดการ Release ที่ซับซ้อน
  • เหมาะสำหรับโปรเจกต์ที่มีหลายเวอร์ชันพร้อมกัน

ข้อเสีย:

  • ซับซ้อนกว่ากลยุทธ์อื่น ๆ อาจใช้เวลานานในการเรียนรู้สำหรับทีมใหม่
  • มี Branch เยอะ อาจทำให้สับสนได้

ตัวอย่าง Workflow (อย่างย่อ):


# เริ่มต้นโปรเจกต์
git flow init -d

# เริ่มพัฒนา Feature ใหม่
git flow feature start my-new-feature
# ... ทำงาน ...
git add .
git commit -m "Implement feature X"
# ...
git flow feature finish my-new-feature

# เริ่ม Release ใหม่
git flow release start v1.0.0
# ... ทดสอบ, แก้ Bug ...
git add .
git commit -m "Fix bugs for v1.0.0"
# ...
git flow release finish v1.0.0

# แก้ Hotfix ด่วน
git flow hotfix start critical-bug
# ... แก้ไข Bug ...
git add .
git commit -m "Fix critical bug in production"
# ...
git flow hotfix finish critical-bug

GitHub Flow

GitHub Flow เป็นกลยุทธ์ที่เรียบง่ายกว่า Git Flow มาก เหมาะสำหรับทีมที่เน้นการ Deploy บ่อยครั้ง และใช้ Continuous Integration/Deployment (CI/CD) เป็นหลัก โดยมีหลักการเพียง 6 ข้อ:

  1. Branch main ต้อง Deploy ได้เสมอ
  2. สร้าง Branch ใหม่จาก main เมื่อจะทำงานใหม่ (Feature, Bug Fix)
  3. ตั้งชื่อ Branch ให้สื่อความหมาย
  4. Commit บ่อย ๆ และ Push ไปยัง Branch ใน Remote บ่อย ๆ
  5. เมื่อ Feature เสร็จ ให้เปิด Pull Request (หรือ Merge Request)
  6. เมื่อ Review และ Test ผ่าน ให้ Merge เข้า main และ Deploy ทันที

ข้อดี:

  • เรียบง่าย เข้าใจง่าย เหมาะสำหรับทีมขนาดเล็กถึงกลาง
  • เน้นการ Deploy อย่างต่อเนื่อง (Continuous Deployment)

ข้อเสีย:

  • อาจไม่เหมาะกับโปรเจกต์ที่มี Release Cycle ยาวนาน หรือต้องการ Stage การทดสอบที่ซับซ้อน
  • การจัดการเวอร์ชันอาจไม่เข้มงวดเท่า Git Flow

GitLab Flow

GitLab Flow เป็นการผสมผสานระหว่างความเรียบง่ายของ GitHub Flow และการจัดการ Release ของ Git Flow โดยเพิ่ม “Environment Branches” เข้ามา เช่น production, staging, pre-production เป็นต้น

โดยทั่วไปแล้ว จะมี main (หรือ master) เป็น Branch หลัก และอาจมี Branch อื่น ๆ เช่น pre-production และ production ที่ใช้สำหรับ Environment ต่าง ๆ เมื่อ Feature เสร็จแล้วจะ Merge เข้า main จากนั้น main ก็จะถูก Merge ไปยัง pre-production และ production ตามลำดับการ Deploy

ข้อดี:

  • มีความยืดหยุ่นสูง สามารถปรับให้เข้ากับ Workflow ได้หลากหลาย
  • รองรับ Environment ที่หลากหลายและมี Stage การทดสอบที่ชัดเจน

ข้อเสีย:

  • อาจซับซ้อนกว่า GitHub Flow เล็กน้อย
  • ต้องมีการจัดการ Branch สำหรับ Environment อย่างรอบคอบ

ตารางเปรียบเทียบกลยุทธ์การแตก Branch

เพื่อให้เห็นภาพความแตกต่างได้ชัดเจนขึ้น นี่คือตารางเปรียบเทียบกลยุทธ์การแตก Branch ทั้งสามแบบครับ:

คุณสมบัติ Git Flow GitHub Flow GitLab Flow
ความซับซ้อน สูง ต่ำ ปานกลาง
จำนวน Branch หลัก 2 (main, develop) 1 (main) 1+ (main + Environment branches)
รูปแบบการ Release กำหนดรอบ Release ชัดเจน, มี Release branches Continuous Deployment, Release ทุกครั้งที่ Merge เข้า main ยืดหยุ่น, สามารถมี Environment-specific branches สำหรับ Staging/Production
การจัดการ Hotfix มี hotfix branches เฉพาะ ทำใน Feature branch แล้ว Merge เข้า main ทำใน Feature branch หรือ Environment branch
เหมาะสำหรับ โปรเจกต์ใหญ่, มีหลายเวอร์ชัน, รอบ Release ยาวนาน โปรเจกต์ที่เน้น Deploy บ่อย, CI/CD, ทีมขนาดเล็ก-กลาง โปรเจกต์ที่ต้องการ Staging Environment, ยืดหยุ่นแต่มีโครงสร้าง
การเรียนรู้ สูง ต่ำ ปานกลาง

การเลือกกลยุทธ์ที่เหมาะสมขึ้นอยู่กับขนาดทีม ความซับซ้อนของโปรเจกต์ และวัฒนธรรมการทำงานของทีมครับ ลองพิจารณาและปรับใช้ให้เข้ากับบริบทของคุณดูครับ

การจัดการประวัติ Commit อย่างชาญฉลาดด้วย git rebase -i

git rebase -i หรือ Interactive Rebase เป็นหนึ่งในเครื่องมือที่ทรงพลังที่สุดของ Git ที่ช่วยให้คุณสามารถปรับแต่งประวัติ Commit ก่อนที่จะ Push ขึ้น Remote ได้ ทำให้ประวัติการเปลี่ยนแปลงสะอาด อ่านง่าย และเป็นระเบียบ ซึ่งเป็นสิ่งสำคัญมากสำหรับการ Review Code และการ Debug ครับ

โดยปกติแล้ว เราจะใช้ git rebase -i HEAD~N โดย N คือจำนวน Commit ล่าสุดที่คุณต้องการแก้ไข


git rebase -i HEAD~3

คำสั่งนี้จะเปิด Editor พร้อมรายการ Commit 3 รายการล่าสุด และคำสั่งที่คุณสามารถใช้ได้ เช่น:

  • pick: ใช้ Commit นี้ตามปกติ (ค่าเริ่มต้น)
  • reword: ใช้ Commit นี้ แต่แก้ไขข้อความ Commit message
  • edit: ใช้ Commit นี้ แต่หยุดเพื่อแก้ไขไฟล์หรือ Commit เพิ่มเติม
  • squash: รวม Commit นี้เข้ากับ Commit ก่อนหน้า
  • fixup: คล้ายกับ squash แต่ไม่เก็บ Commit message ของ Commit ปัจจุบัน
  • drop: ลบ Commit นี้ทิ้ง

การรวม Commit (Squashing Commits)

บ่อยครั้งที่เราอาจมี Commit เล็ก ๆ น้อย ๆ จำนวนมากระหว่างการพัฒนา Feature เดียวกัน เช่น “WIP: added half of feature”, “Fix typo”, “Added missing import”, “WIP: finished feature”. Commit เหล่านี้อาจทำให้ประวัติไม่สะอาดตา การรวม Commit เหล่านี้ให้เป็น Commit เดียวที่สื่อความหมายจะช่วยได้มากครับ


pick aaaaaaa Add feature A part 1
squash bbbbbbb Fix typo in feature A
squash ccccccc Add feature A part 2
pick ddddddd Add feature B

เมื่อรัน Rebase เสร็จ Git จะเปิด Editor ให้คุณเขียน Commit message ใหม่สำหรับ Commit ที่ถูกรวมกัน

ประโยชน์: ทำให้ประวัติสะอาดตา, Commit แต่ละอันมีความหมายชัดเจน, ง่ายต่อการ Review Code

การจัดลำดับ Commit ใหม่

บางครั้งคุณอาจ Commit ไปแล้ว แต่ภายหลังพบว่าลำดับของ Commit ไม่เหมาะสม หรือมี Commit บางอันที่ควรจะมาก่อนหรือหลังอีกอัน คุณสามารถจัดลำดับใหม่ได้


pick aaaaaaa Commit X
pick bbbbbbb Commit Y
pick ccccccc Commit Z

เปลี่ยนเป็น:


pick bbbbbbb Commit Y
pick aaaaaaa Commit X
pick ccccccc Commit Z

ข้อควรระวัง: การจัดลำดับใหม่ควรทำด้วยความระมัดระวัง เพราะอาจทำให้เกิด Conflict ได้หาก Commit มีการแก้ไขไฟล์เดียวกันในลักษณะที่ขัดแย้งกัน

การแก้ไข Commit ที่ผ่านมา

หากคุณต้องการแก้ไขไฟล์ใน Commit เก่า หรือเพิ่มไฟล์เข้าไปใน Commit เก่า คุณสามารถใช้ edit ได้


pick aaaaaaa First commit
edit bbbbbbb Second commit
pick ccccccc Third commit

เมื่อ Git ไปถึง Commit bbbbbbb มันจะหยุด Rebase และให้คุณแก้ไขไฟล์ จากนั้นคุณสามารถใช้:


git add .
git commit --amend # แก้ไข commit bbbbbbb
git rebase --continue # ดำเนินการ Rebase ต่อ

ประโยชน์: แก้ไขข้อผิดพลาดใน Commit เก่า, เพิ่มไฟล์ที่ลืมไป

เมื่อไหร่ควรใช้ Rebase เมื่อไหร่ควรใช้ Merge?

นี่คือประเด็นถกเถียงยอดนิยมในวงการ Git ครับ

  • git merge:
    • ข้อดี: รักษาประวัติการ Commit ดั้งเดิมทั้งหมด ไม่มีการเปลี่ยนแปลง SHA-1 ของ Commit ที่ผ่านมา
    • ข้อเสีย: สร้าง Merge Commit เพิ่มขึ้น ทำให้ประวัติ Commit ดูซับซ้อนเป็นกราฟหยัก ๆ
    • เหมาะสำหรับ: การรวม Branch ที่ถูก Push ขึ้น Remote แล้ว และมีคนอื่นทำงานร่วมด้วย เพื่อไม่ให้เกิดประวัติที่แตกต่างกัน (Diverged History)
  • git rebase:
    • ข้อดี: ทำให้ประวัติ Commit เป็นเส้นตรง (Linear History) ดูสะอาดตา อ่านง่าย
    • ข้อเสีย: เปลี่ยนแปลง SHA-1 ของ Commit ที่ถูก Rebase ซึ่งอาจสร้างปัญหาได้หาก Commit นั้นถูก Push ขึ้น Remote และมีคนอื่นดึงไปแล้ว
    • เหมาะสำหรับ:
      • ทำความสะอาดประวัติ Commit บน Local Branch ของคุณ ก่อน ที่จะ Push ขึ้น Remote (เช่น Squash Commit, แก้ไข Commit message)
      • อัปเดต Feature Branch ของคุณให้เป็นเวอร์ชันล่าสุดของ main ก่อนที่จะ Merge เพื่อหลีกเลี่ยง Merge Commit ที่ไม่จำเป็น

กฎทอง: ห้าม Rebase Commit ที่ถูก Push ขึ้น Public Branch แล้ว! (Don’t rebase commits that have been pushed to a public branch!) หากคุณ Rebase Branch ที่มีคนอื่นทำงานร่วมด้วย จะทำให้ประวัติของแต่ละคนไม่ตรงกัน และอาจต้องใช้ git push --force ซึ่งเป็นอันตรายและควรหลีกเลี่ยงใน Public Branch ครับ

หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับความแตกต่างระหว่าง Rebase และ Merge อย่างละเอียด อ่านเพิ่มเติมได้ที่นี่

การเลือกใช้ Commit หรือไฟล์บางส่วน (Cherry-pick & Patch)

บางครั้งคุณอาจต้องการนำการเปลี่ยนแปลงจาก Commit ใด Commit หนึ่ง มาใช้กับ Branch ปัจจุบัน โดยไม่ต้องการ Merge ทั้ง Branch หรือบางครั้งก็ต้องการเพียงแค่การเปลี่ยนแปลงในไฟล์เดียวจาก Commit นั้น ๆ ครับ

git cherry-pick

git cherry-pick เป็นคำสั่งที่ใช้ในการนำ Commit เดียวจาก Branch หนึ่งมาประยุกต์ใช้ (apply) บน Branch ปัจจุบันของคุณ ซึ่งมีประโยชน์มากในสถานการณ์ต่อไปนี้:

  • Hotfix: คุณแก้ไข Bug ใน Branch Feature แต่ต้องการนำ Bug Fix นั้นไปใช้กับ Branch main ทันทีโดยไม่ต้องรอให้ Feature นั้นเสร็จสมบูรณ์
  • นำ Feature บางส่วนมาใช้: คุณกำลังพัฒนา Feature A และพบว่าส่วนหนึ่งของ Feature A มีประโยชน์กับ Feature B ที่กำลังพัฒนาอยู่บนอีก Branch หนึ่ง

วิธีใช้งาน:


# สมมติว่าคุณอยู่ใน branch 'main' และต้องการนำ commit 'abcdef1' จาก branch 'feature/bugfix' มาใช้
git cherry-pick abcdef1

ถ้ามี Conflict เกิดขึ้น คุณต้องแก้ไข Conflict นั้นด้วยตนเอง แล้วใช้ git cherry-pick --continue หรือ git cherry-pick --abort เพื่อยกเลิก

ข้อควรระวัง: การ Cherry-pick จะสร้าง Commit ใหม่ที่มีเนื้อหาเหมือน Commit ต้นฉบับ แต่มี SHA-1 ที่แตกต่างกัน ซึ่งอาจทำให้ประวัติการ Commit ดูซ้ำซ้อนได้ หาก Commit เดิมถูก Merge เข้ามาใน Branch นี้ภายหลัง

git apply และ git format-patch

หากคุณต้องการเพียงแค่ “การเปลี่ยนแปลง” (diff) จากไฟล์บางไฟล์ ไม่ใช่ทั้ง Commit คุณสามารถใช้ git format-patch เพื่อสร้างไฟล์ Patch และ git apply เพื่อนำ Patch นั้นมาใช้ได้

  • git format-patch: ใช้สร้างไฟล์ Patch จาก Commit หนึ่งหรือหลาย Commit
  • 
    # สร้าง patch จาก commit ล่าสุด 1 commit
    git format-patch -1 HEAD
    
    # สร้าง patch จาก commit 'abcdef1'
    git format-patch -1 abcdef1
    
    # สร้าง patch จาก commit ทั้งหมดตั้งแต่ branch 'main' จนถึง HEAD
    git format-patch main..HEAD
    

    คำสั่งนี้จะสร้างไฟล์ .patch ขึ้นมา (เช่น 0001-My-commit-message.patch)

  • git apply: ใช้ประยุกต์ไฟล์ Patch เข้ากับ Working Directory ของคุณ
  • 
    # ในอีก repository หรือ branch หนึ่ง
    git apply 0001-My-commit-message.patch
    

    คำสั่งนี้จะนำการเปลี่ยนแปลงในไฟล์ Patch มาใช้กับไฟล์ของคุณ แต่จะยังไม่ Commit การเปลี่ยนแปลงนั้น คุณต้อง git add และ git commit ด้วยตนเอง

ประโยชน์: เหมาะสำหรับสถานการณ์ที่ต้องการแลกเปลี่ยนการเปลี่ยนแปลงโค้ดแบบละเอียด หรือส่ง Bug Fix ให้กับโปรเจกต์โอเพนซอร์สโดยไม่ต้องการให้มีประวัติ Commit ทั้งหมด

การจัดการงานที่ยังไม่เสร็จ (Work In Progress) ด้วย git stash

บางครั้งขณะที่คุณกำลังทำงานอยู่บน Feature หนึ่ง ๆ ก็มีเหตุการณ์ฉุกเฉินเข้ามา เช่น ต้องแก้ไข Bug ด่วน หรือต้องสลับไปทำงานบน Branch อื่นชั่วคราว แต่คุณยังไม่พร้อมที่จะ Commit งานที่ทำค้างไว้ git stash คือคำสั่งที่ช่วยแก้ปัญหานี้ครับ

git stash จะบันทึกสถานะของ Working Directory และ Staging Area ของคุณไว้ชั่วคราว แล้วคืนค่า Working Directory กลับไปสู่สถานะของ HEAD Commit ล่าสุด ทำให้คุณสามารถสลับไปทำงานอื่นได้โดยไม่ต้อง Commit งานที่ยังไม่เสร็จ

การใช้งานพื้นฐาน

  • Stash งานที่ทำค้างไว้:
    
    git stash save "Work on Feature X" # หรือแค่ git stash
    

    คำสั่งนี้จะบันทึกการเปลี่ยนแปลงทั้งหมด (ทั้งที่ Stage และ Unstaged) แล้วเคลียร์ Working Directory ของคุณ

  • ดูรายการ Stash:
    
    git stash list
    # ผลลัพธ์:
    # stash@{0}: On feature/my-feature: Work on Feature X
    # stash@{1}: On main: Temporary bugfix
    
  • ดูรายละเอียดของ Stash:
    
    git stash show stash@{0}
    

    หรือถ้าต้องการดูแบบเป็น Diff:

    
    git stash show -p stash@{0}
    

การจัดการ Stash หลายรายการ

คุณสามารถมี Stash ได้หลายรายการ และ Git จะเก็บไว้ในรูปแบบ Stack (LIFO – Last In, First Out) โดย stash@{0} คือ Stash ล่าสุด

stash apply vs. stash pop

  • git stash apply:
    
    git stash apply stash@{0} # นำ stash ล่าสุดมาใช้ โดยยังคง stash นั้นไว้ในรายการ
    

    มีประโยชน์หากคุณต้องการนำ Stash เดียวกันไปใช้ในหลาย Branch หรือต้องการเก็บ Stash นั้นไว้เป็น Reference

  • git stash pop:
    
    git stash pop # นำ stash ล่าสุดมาใช้ และลบ stash นั้นออกจากรายการ
    

    เป็นคำสั่งที่ใช้บ่อยที่สุดเมื่อคุณต้องการกลับไปทำงานต่อจากจุดที่ Stash ไว้

  • ลบ Stash:
    
    git stash drop stash@{1} # ลบ stash เฉพาะ
    git stash clear # ลบ stash ทั้งหมด
    

ข้อควรระวัง: หากคุณ Stash ไว้ แล้วเปลี่ยน Branch แล้วนำ Stash นั้นมาใช้ อาจเกิด Conflict ได้ หากไฟล์ที่คุณแก้ไขใน Stash มีการเปลี่ยนแปลงใน Branch ใหม่นั้นด้วย

การกู้คืนและแก้ไขข้อผิดพลาด (Reflog, Reset, Revert)

ทุกคนเคยทำผิดพลาดครับ! การลบไฟล์ผิดพลาด, Commit โค้ดที่ยังไม่สมบูรณ์, หรือลบ Branch ที่สำคัญไปแล้ว Git มีเครื่องมืออันทรงพลังที่จะช่วยให้คุณกู้คืนหรือแก้ไขข้อผิดพลาดเหล่านี้ได้ครับ

git reflog: ประวัติการเปลี่ยนแปลง HEAD

git reflog (Reference Log) คือเครื่องมือช่วยชีวิตยามฉุกเฉิน มันจะบันทึกทุก ๆ การเคลื่อนไหวของ HEAD ของคุณ ซึ่งรวมถึง Commit, Merge, Rebase, Reset, Cherry-pick หรือแม้แต่การ Checkout Branch ต่าง ๆ


git reflog
# ผลลัพธ์:
# abcdef1 HEAD@{0}: commit: Add new feature
# 1234567 HEAD@{1}: checkout: moving from main to feature/X
# 890abcd HEAD@{2}: commit (initial): Initial commit

แต่ละรายการใน Reflog จะมี Index เช่น HEAD@{0} (สถานะปัจจุบัน), HEAD@{1} (สถานะก่อนหน้า 1 ขั้นตอน) และมี SHA-1 ของ Commit ที่ HEAD ชี้ไป

วิธีใช้กู้คืน: หากคุณลบ Branch ไปแล้ว หรือ Reset กลับไปไกลเกินไป คุณสามารถใช้ git reset --hard <SHA-1> หรือ git checkout <SHA-1> โดยใช้ SHA-1 จาก Reflog เพื่อย้อนกลับไปยังสถานะที่ต้องการได้ครับ


# สมมติว่าต้องการกลับไปที่สถานะก่อน checkout ไปยัง feature/X
git reset --hard 1234567

สำคัญ: Reflog เป็น Local เท่านั้น มันจะไม่ถูก Push ขึ้น Remote และมีอายุจำกัด (โดยปกติ 90 วัน) หลังจากนั้นจะถูกล้างทิ้งไป

git reset: การย้อนกลับ Commit (Soft, Mixed, Hard)

git reset ใช้เพื่อย้าย HEAD และ Branch Pointer ไปยัง Commit ที่ต้องการ มี 3 โหมดหลัก:

  • --soft: ย้าย HEAD และ Branch Pointer ไปยัง Commit ที่ระบุ แต่เก็บการเปลี่ยนแปลงทั้งหมดของ Commit ที่ถูกย้อนกลับไว้ใน Staging Area
  • 
    git reset --soft HEAD~1 # ย้อนกลับไป 1 Commit แต่เก็บการเปลี่ยนแปลงไว้ใน Staging
    

    เหมาะสำหรับ: ต้องการแก้ไข Commit message หรือเพิ่ม/ลบไฟล์ใน Commit ที่เพิ่งทำไป

  • --mixed (ค่าเริ่มต้น): ย้าย HEAD และ Branch Pointer ไปยัง Commit ที่ระบุ และย้ายการเปลี่ยนแปลงทั้งหมดของ Commit ที่ถูกย้อนกลับไปที่ Working Directory (Unstaged)
  • 
    git reset HEAD~1 # ย้อนกลับไป 1 Commit และย้ายการเปลี่ยนแปลงไปที่ Working Directory
    

    เหมาะสำหรับ: ต้องการรื้อ Commit นั้นทิ้งแล้วเริ่มใหม่ หรือต้องการรวม Commit หลายอันเข้าด้วยกัน

  • --hard: ย้าย HEAD และ Branch Pointer ไปยัง Commit ที่ระบุ และทิ้งการเปลี่ยนแปลงทั้งหมดของ Commit ที่ถูกย้อนกลับทิ้งไปจาก Working Directory และ Staging Area อย่างถาวร
  • 
    git reset --hard HEAD~1 # ย้อนกลับไป 1 Commit และทิ้งการเปลี่ยนแปลงทั้งหมด
    

    อันตราย: ใช้ด้วยความระมัดระวัง เพราะข้อมูลที่ถูกทิ้งไปอาจหายไปอย่างถาวร (เว้นแต่จะกู้คืนได้ด้วย git reflog)

คำเตือน: การใช้ git reset โดยเฉพาะ --hard กับ Commit ที่ถูก Push ขึ้น Remote แล้ว อาจทำให้เกิดความสับสนและปัญหาในทีมได้ ควรหลีกเลี่ยงหรือใช้ git revert แทน

git revert: การสร้าง Commit ใหม่เพื่อย้อนกลับ

git revert เป็นวิธีที่ปลอดภัยกว่าในการ “ย้อนกลับ” การเปลี่ยนแปลง เพราะมันไม่ได้ลบ Commit เก่าทิ้ง แต่จะสร้าง Commit ใหม่ที่มีเนื้อหาตรงกันข้ามกับ Commit ที่ต้องการย้อนกลับ


git revert abcdef1 # สร้าง commit ใหม่เพื่อย้อนกลับ commit abcdef1

ประโยชน์:

  • ปลอดภัย: ไม่มีการเปลี่ยนแปลงประวัติ Git ดั้งเดิม ทำให้ไม่กระทบ Branch ที่มีคนอื่นทำงานร่วมด้วย
  • Auditable: ประวัติยังคงอยู่ว่าเคยมีการเปลี่ยนแปลงอะไรไป และถูกย้อนกลับอย่างไร

เหมาะสำหรับ: การย้อนกลับ Commit ที่ถูก Push ขึ้น Public Branch แล้ว

การทำงานอัตโนมัติด้วย Git Hooks

Git Hooks คือ Script ที่ Git จะรันโดยอัตโนมัติเมื่อเกิดเหตุการณ์บางอย่างขึ้น (เช่น ก่อน Commit, หลัง Commit, ก่อน Push, หลัง Merge) คุณสามารถใช้ Hooks เพื่อสร้าง Automation ใน Workflow ของทีมได้ เช่น ตรวจสอบ Code Style, รัน Unit Test, หรือแจ้งเตือนเมื่อมีการ Push โค้ด

Hooks จะอยู่ในไดเรกทอรี .git/hooks/ ภายใน Repository ของคุณ โดยจะมีไฟล์ตัวอย่าง (เช่น pre-commit.sample) อยู่ คุณสามารถเปลี่ยนชื่อไฟล์เหล่านี้ให้เป็นชื่อที่ไม่มีนามสกุล .sample (เช่น pre-commit) เพื่อเปิดใช้งาน Hooks นั้น ๆ ครับ

Client-Side Hooks

รันบนเครื่องของนักพัฒนา (Local Repository) มีประโยชน์ในการบังคับใช้กฎเกณฑ์ก่อนที่จะส่ง Commit หรือ Push ขึ้น Remote

  • pre-commit: รันก่อนที่จะสร้าง Commit ใช้ตรวจสอบ Code Style, Syntax, รัน Lint หรือ Unit Test
  • prepare-commit-msg: รันก่อนที่จะเปิด Editor เพื่อเขียน Commit message ใช้เพื่อสร้าง Commit message เริ่มต้น
  • commit-msg: รันหลังเขียน Commit message ใช้เพื่อตรวจสอบรูปแบบของ Commit message (เช่น ต้องขึ้นต้นด้วย Ticket ID)
  • post-commit: รันหลัง Commit เสร็จ ใช้สำหรับแจ้งเตือนหรืออัปเดตสถานะบางอย่าง
  • pre-push: รันก่อน Push ไปยัง Remote ใช้เพื่อรัน Test อีกครั้ง หรือตรวจสอบว่ามีการ Rebase ที่ไม่ได้รับอนุญาตหรือไม่

Server-Side Hooks

รันบน Server ที่เก็บ Remote Repository มีประโยชน์ในการบังคับใช้กฎเกณฑ์สำหรับโค้ดทั้งหมดที่ถูก Push เข้ามา

  • pre-receive: รันเมื่อมี Push เข้ามา ก่อนที่จะอัปเดต Branch บน Server ใช้เพื่อตรวจสอบสิทธิ์, ตรวจสอบ Code Quality หรือป้องกันการ Push โค้ดที่ไม่ถูกต้อง
  • post-receive: รันหลังจาก Push สำเร็จ ใช้เพื่อแจ้งเตือน (เช่น ส่งอีเมล), อัปเดต Ticket System หรือ Trigger CI/CD Pipeline

ตัวอย่างการใช้งาน Hook: pre-commit สำหรับตรวจสอบ Code Style

สมมติว่าคุณต้องการให้มั่นใจว่าโค้ดที่ Commit เข้ามาต้องผ่าน Linter เสมอ

1. สร้างไฟล์ .git/hooks/pre-commit (หรือเปลี่ยนชื่อจาก pre-commit.sample)

2. เพิ่ม Script เข้าไป (ตัวอย่างนี้ใช้ Python Black Linter)


#!/bin/sh

# Stash any unstaged changes that are not part of the current commit
git stash save --keep-index --include-untracked "pre-commit hook stash" > /dev/null

# Format changed Python files with Black
# Use git diff --name-only --cached to get staged files
PYTHON_FILES=$(git diff --name-only --cached --diff-filter=ACM | grep '\.py$')

if [ -n "$PYTHON_FILES" ]; then
    echo "Running Black Linter on staged Python files..."
    echo "$PYTHON_FILES" | xargs black --check
    if [ $? -ne 0 ]; then
        echo "Error: Black Linter found issues. Please format your code before committing."
        git stash pop > /dev/null # Restore stashed changes
        exit 1
    fi
    echo "Black Linter passed."
fi

# Restore stashed changes if any
git stash pop > /dev/null

exit 0

3. ทำให้ Script สามารถรันได้


chmod +x .git/hooks/pre-commit

ตอนนี้ทุกครั้งที่คุณ git commit, Linter จะทำงานโดยอัตโนมัติก่อน หากมีข้อผิดพลาด Commit จะถูกยกเลิก

ข้อควรจำ: Git Hooks เป็น Local และไม่ถูก Track โดย Git ดังนั้นหากต้องการให้ Hooks ถูกแชร์ในทีม ต้องใช้เครื่องมืออื่น ๆ ช่วย เช่น Husky สำหรับ JavaScript/Node.js หรือจัดการด้วย Script ของทีมเองครับ

การแก้ไขปัญหาและ Debugging ขั้นสูงด้วย git bisect

เมื่อมี Bug เกิดขึ้นใน Production แต่คุณไม่รู้ว่า Bug นั้นถูก introduce เข้ามาใน Commit ไหน git bisect คือเครื่องมือที่จะช่วยคุณหาสาเหตุของ Bug ได้อย่างรวดเร็ว โดยใช้หลักการ Binary Search

ขั้นตอนการใช้งาน git bisect:

  1. เริ่ม Bisect:
    
    git bisect start
    
  2. ระบุ Commit ที่ “Bad” (มี Bug):
    
    git bisect bad # Commit ปัจจุบันมี Bug
    # หรือ
    git bisect bad <commit-hash-of-bad-version> # ระบุ commit ที่คุณรู้ว่ามี Bug
    
  3. ระบุ Commit ที่ “Good” (ไม่มี Bug):
    
    git bisect good <commit-hash-of-good-version> # ระบุ commit ที่คุณรู้ว่ายังไม่มี Bug
    

    Git จะ Checkout ไปยัง Commit ตรงกลางระหว่าง “Good” และ “Bad” โดยอัตโนมัติ

  4. ทดสอบและระบุสถานะ:

    หลังจาก Git Checkout ไปยัง Commit ใหม่ ให้คุณทำการทดสอบว่า Bug นั้นปรากฏขึ้นใน Commit นี้หรือไม่

    • หากมี Bug: git bisect bad
    • หากไม่มี Bug: git bisect good

    Git จะ Checkout ไปยัง Commit กลางถัดไป ทำซ้ำขั้นตอนนี้ไปเรื่อย ๆ

  5. สิ้นสุด Bisect:

    ในที่สุด Git จะระบุ Commit แรกที่ Bug ปรากฏขึ้นมา

    
    <commit-hash> is the first bad commit
    
  6. ออกจากโหมด Bisect:
    
    git bisect reset
    

    คำสั่งนี้จะคืนค่า HEAD กลับไปยัง Branch เดิมที่คุณอยู่ก่อนเริ่ม bisect

ตัวอย่าง: คุณรู้ว่า Bug อยู่ใน main แต่ไม่รู้ว่าเริ่มจาก Commit ไหน รู้แค่ว่า v1.0 ยังไม่มี Bug และ HEAD ปัจจุบันมี Bug


git bisect start
git bisect bad HEAD
git bisect good v1.0

# Git จะ checkout ไปยัง commit กลาง
# คุณทำการทดสอบ...

# หากพบ Bug ใน commit กลาง
git bisect bad

# หากไม่พบ Bug ใน commit กลาง
git bisect good

# ...ทำซ้ำจนกว่า Git จะพบ commit ต้นเหตุ

ประโยชน์: ลดเวลาในการ Debug อย่างมหาศาล โดยเฉพาะในโปรเจกต์ที่มีประวัติ Commit ยาวนาน

นอกจากนี้ยังมี git bisect run <command> ที่สามารถใช้ Script เพื่อรัน Test อัตโนมัติในแต่ละ Commit ซึ่งจะช่วยให้กระบวนการ Bisect รวดเร็วยิ่งขึ้นไปอีกครับ

การปรับแต่ง Git ด้วย .gitattributes

ไฟล์ .gitattributes ช่วยให้คุณสามารถกำหนดพฤติกรรมเฉพาะของ Git สำหรับไฟล์หรือประเภทไฟล์บางอย่างใน Repository ของคุณได้ ซึ่งมีประโยชน์มากสำหรับการทำงานร่วมกันในทีมครับ

ตัวอย่างการใช้งาน:

  • การจัดการ Line Endings: ปัญหา Line Endings (CRLF vs LF) มักสร้างความปวดหัวให้กับทีมที่ทำงานข้ามแพลตฟอร์ม (Windows, macOS, Linux) คุณสามารถบังคับให้ Git จัดการ Line Endings ให้เป็นมาตรฐานเดียวกันได้
  • 
    # กำหนดให้ไฟล์ข้อความทั้งหมดใช้ LF
    * text=auto
    
    # สำหรับไฟล์เชลล์สคริปต์ ให้ใช้ LF เสมอและเป็น executable
    *.sh text eol=lf
    
  • Binary Files และ Diff: สำหรับไฟล์ไบนารี Git จะไม่สามารถแสดง Diff ได้โดยตรง แต่คุณสามารถบอก Git ว่าไฟล์เหล่านี้เป็นไบนารี เพื่อให้ Git จัดการได้อย่างเหมาะสม (เช่น ไม่พยายามแสดง Diff)
  • 
    *.jpg binary
    *.png binary
    
  • Large File Storage (LFS): สำหรับไฟล์ขนาดใหญ่ (เช่น assets, models) ที่ไม่ควรเก็บใน Git Repository โดยตรง เพราะจะทำให้ Repository มีขนาดใหญ่และช้า คุณสามารถใช้ Git LFS ร่วมกับ .gitattributes ได้
  • 
    # บอก Git LFS ให้ติดตามไฟล์ .psd
    *.psd filter=lfs diff=lfs merge=lfs -text
    
  • Merge Strategies: กำหนด Merge Strategy เฉพาะสำหรับไฟล์บางประเภท เช่น การรวมไฟล์ JSON โดยใช้เครื่องมือเฉพาะ

.gitattributes ควรถูก Commit เข้า Repository เพื่อให้กฎเหล่านี้ถูกแชร์และบังคับใช้กับทุกคนในทีมครับ

สร้างทางลัดคำสั่งด้วย Git Aliases

คำสั่ง Git บางคำสั่งค่อนข้างยาวและซับซ้อน การสร้าง Alias (นามแฝง) จะช่วยให้คุณประหยัดเวลาและพิมพ์คำสั่งได้รวดเร็วขึ้นครับ

คุณสามารถกำหนด Alias ได้ในไฟล์ .gitconfig ของคุณ (Global: ~/.gitconfig หรือ Local: .git/config) หรือใช้คำสั่ง git config

ตัวอย่าง:

  • Alias สำหรับ Commit บ่อย ๆ:
    
    git config --global alias.co checkout
    git config --global alias.ci commit
    git config --global alias.st status
    git config --global alias.br branch
    

    จากนี้ไปคุณสามารถใช้ git co, git ci, git st, git br ได้

  • Alias สำหรับ Log ที่สวยงาม:
    
    git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
    

    เมื่อพิมพ์ git lg คุณจะได้ Log ที่มีสีสันและกราฟแสดง Branching History ที่สวยงามครับ

  • Alias สำหรับ Add & Commit:
    
    git config --global alias.ac '!git add -A && git commit'
    

    คำสั่งนี้ใช้ ! เพื่อบอก Git ว่านี่คือ Shell Command ทำให้สามารถรันหลายคำสั่งพร้อมกันได้ เมื่อพิมพ์ git ac -m "My commit" จะทำการ git add -A และ git commit -m "My commit"

การใช้ Aliases ทำให้ Workflow ของคุณเร็วขึ้นและลดความผิดพลาดในการพิมพ์คำสั่งครับ

คำถามที่พบบ่อย (FAQ)

Q1: ทีมของผมควรใช้ Git Flow, GitHub Flow หรือ GitLab Flow ดีครับ?

A1: การเลือกขึ้นอยู่กับหลายปัจจัยครับ:

  • Git Flow: เหมาะสำหรับโปรเจกต์ที่มี Release Cycle ยาวนาน, ต้องการการจัดการเวอร์ชันที่เข้มงวด, และมี Hotfix ที่ต้องดูแลแยกต่างหาก (เช่น Library, OS, แอปพลิเคชันที่ Deploy ไม่บ่อย)
  • GitHub Flow: เหมาะสำหรับทีมที่เน้นการ Deploy อย่างต่อเนื่อง, โปรเจกต์ที่เรียบง่าย, และทีมขนาดเล็กถึงกลางที่ต้องการความคล่องตัวสูง
  • GitLab Flow: เป็นตัวเลือกที่ดีหากคุณต้องการความสมดุลระหว่างความเรียบง่ายและการจัดการ Environment ที่ซับซ้อนขึ้น มักใช้กับโปรเจกต์ที่มี Staging, Pre-production Environment ก่อนขึ้น Production

แนะนำให้ทีมปรึกษาหารือกันและเลือก Flow ที่ทุกคนเข้าใจและสามารถปฏิบัติตามได้อย่างสม่ำเสมอครับ

Q2: การใช้ git rebase กับ git merge ต่างกันอย่างไร และควรใช้เมื่อไหร่?

A2:

  • git merge: จะรวมประวัติของ Commit จาก Branch หนึ่งเข้ากับอีก Branch หนึ่ง โดยสร้าง Merge Commit ใหม่ขึ้นมา ประวัติการ Commit จะเป็นแบบ Non-linear (มีกิ่งก้านสาขา) เหมาะสำหรับการรวม Branch ที่ถูก Push ขึ้น Remote แล้ว และมีคนอื่นทำงานร่วมด้วย เพื่อรักษาประวัติเดิมไม่ให้เกิดความสับสน
  • git rebase: จะย้าย Commit จาก Branch หนึ่งไปต่อท้ายอีก Branch หนึ่ง ทำให้ประวัติการ Commit เป็นแบบ Linear (เส้นตรง) และสะอาดตา เหมาะสำหรับการทำความสะอาดประวัติ Commit บน Local Branch ของคุณ ก่อน Push ขึ้น Remote หรืออัปเดต Feature Branch ให้เป็นเวอร์ชันล่าสุดของ main เพื่อหลีกเลี่ยง Merge Commit ที่ไม่จำเป็น

กฎทอง: ห้าม Rebase Commit ที่ถูก Push ขึ้น Public Branch แล้ว! เพราะจะทำให้ประวัติของ Commit เปลี่ยนแปลงและสร้างปัญหาให้กับสมาชิกในทีมคนอื่น ๆ ครับ

Q3: ผมลบ Branch สำคัญทิ้งไปแล้ว จะกู้คืนได้อย่างไรครับ?

A3: ไม่ต้องกังวลครับ! คุณสามารถใช้ git reflog เพื่อดูกิจกรรมทั้งหมดที่ HEAD ของคุณเคยชี้ไป รวมถึงการลบ Branch ด้วย


git reflog

มองหา Commit SHA-1 ที่เป็นจุดสุดท้ายของ Branch ที่คุณลบไป (มักจะอยู่ในรูปแบบ HEAD@{N}: branch: moving from old-branch to new-branch หรือ HEAD@{N}: branch: deleted old-branch) เมื่อเจอ SHA-1 นั้นแล้ว คุณสามารถสร้าง Branch ใหม่จาก Commit นั้นได้:


git branch <ชื่อ-branch-ที่ต้องการกู้คืน> <SHA-1-จาก-reflog>

จากนั้นคุณก็สามารถ Checkout ไปยัง Branch ที่กู้คืนมาได้เลยครับ

Q4: เราจะบังคับใช้ Code Style หรือรัน Unit Test ก่อน Commit ได้อย่างไร?

A4: คุณสามารถใช้ Git Hooks โดยเฉพาะ pre-commit Hook ครับ

สร้าง Script ในไฟล์ .git/hooks/pre-commit (และทำให้เป็น executable ด้วย chmod +x) ภายใน Script นี้ คุณสามารถเพิ่มคำสั่งสำหรับ Linter หรือ Unit Test Framework ที่คุณใช้ได้ หาก Linter/Test ไม่ผ่าน Script จะส่งค่า Exit Code ที่ไม่ใช่ศูนย์ (non-zero) ออกไป ซึ่งจะทำให้ Commit ถูกยกเลิกครับ

ข้อควรระวัง: Git Hooks เป็น Local และไม่ถูกแชร์อัตโนมัติในทีม หากต้องการแชร์ Hooks ให้ทุกคนในทีมใช้ คุณอาจต้องใช้เครื่องมือช่วยจัดการ Hooks หรือมีกระบวนการ Deploy Hooks ให้กับสมาชิกในทีม

Q5: ควรใช้ git push --force เมื่อไหร่และเมื่อไหร่ควรหลีกเลี่ยง?

A5: git push --force เป็นคำสั่งที่อันตรายและควรใช้ด้วยความระมัดระวังสูงสุด เพราะมันจะเขียนทับประวัติ Commit บน Remote Repository ด้วยประวัติ Commit ของ Local Repository ของคุณ

  • ควรใช้เมื่อ:
    • คุณได้ทำการ Rebase หรือแก้ไขประวัติ Commit บน Local Branch ส่วนตัว ที่ยังไม่ได้ถูก Push ขึ้น Remote และคุณเป็นคนเดียวที่ทำงานบน Branch นั้น (หรือมั่นใจว่าไม่มีใครดึงไปแล้ว)
    • คุณต้องการแก้ไขประวัติ Commit ที่เพิ่ง Push ขึ้นไป ใน Branch ที่ไม่มีใครอื่นทำงานด้วย หรือ Branch ที่เป็น Temporary Branch เท่านั้น
  • ควรหลีกเลี่ยงเมื่อ:
    • คุณกำลัง Push ไปยัง Public Branch (เช่น main, develop) หรือ Branch ที่มีคนอื่นทำงานร่วมด้วย เพราะจะทำให้ประวัติของเพื่อนร่วมทีมไม่ตรงกัน และอาจทำให้พวกเขาต้องทำการ Reset หรือ Rebase ที่ซับซ้อนเพื่อแก้ไขปัญหา

หากคุณต้องการยกเลิก Commit บน Public Branch ควรใช้ git revert แทน ซึ่งจะสร้าง Commit ใหม่เพื่อย้อนกลับการเปลี่ยนแปลงแทนที่จะลบประวัติเดิมทิ้งครับ

สรุปและ Call to Action

Git Advanced Techniques ไม่ใช่เพียงแค่ชุดคำสั่งที่ซับซ้อน แต่เป็นปรัชญาและแนวปฏิบัติที่ช่วยให้ทีมพัฒนาสามารถทำงานร่วมกันได้อย่างมีประสิทธิภาพ มีระเบียบ และมีความยืดหยุ่นสูง การทำความเข้าใจและนำเทคนิคเหล่านี้ไปปรับใช้ ไม่ว่าจะเป็นกลยุทธ์การแตก Branch ที่เหมาะสม การจัดการประวัติ Commit ให้สะอาด การกู้คืนข้อผิดพลาด หรือการสร้าง Automation ด้วย Git Hooks จะช่วยยกระดับคุณภาพของโค้ดและ Workflow การทำงานของทีมคุณได้อย่างแน่นอนครับ

การลงทุนในการเรียนรู้ Git ขั้นสูงเป็นการลงทุนที่คุ้มค่าในระยะยาวสำหรับทีมพัฒนาทุกคน อย่ากลัวที่จะลองผิดลองถูก (แต่ควรทำใน Branch ส่วนตัวก่อนนะครับ!) เพราะนั่นคือหนทางสู่การเป็นผู้เชี่ยวชาญ และเมื่อใดที่คุณติดขัดหรือต้องการคำแนะนำเพิ่มเติม SiamLancard.com ยินดีเป็นส่วนหนึ่งในการสนับสนุนการเรียนรู้และพัฒนาทักษะด้านเทคโนโลยีของทุกคนครับ

หากคุณสนใจศึกษาเทคนิค Git เพิ่มเติม หรือต้องการปรึกษาเกี่ยวกับการนำ Git ไปปรับใช้ในองค์กรของคุณ โปรด ติดต่อเรา หรือ อ่านบทความอื่น ๆ ที่เกี่ยวข้องกับการพัฒนาซอฟต์แวร์และ DevOps บนเว็บไซต์ของเราได้เลยครับ

จัดส่งรวดเร็วส่งด่วนทั่วประเทศ
รับประกันสินค้าเคลมง่าย มีใบรับประกัน
ผ่อนชำระได้บัตรเครดิต 0% สูงสุด 10 เดือน
สะสมแต้ม รับส่วนลดส่วนลดและคะแนนสะสม

© 2026 SiamLancard — จำหน่ายการ์ดแลน อุปกรณ์ Server และเครื่องพิมพ์ใบเสร็จ

SiamLancard
Logo
Free Forex EA Download — XM Signal · EA Forex ฟรี
iCafeForex.com - สอนเทรด Forex | SiamCafe.net
Shopping cart