Categories
การพัฒนาซอฟท์แวร์

รูปแบบ Commit Message ของ Git ที่น่าสนใจ

ที่มา: dev.to

เพื่อให้การทำงานเป็นทีมมีประสิทธิภาพมากขึ้น เราควรใส่ใจกับ Commit Message ของ Git

เพราะ Commit Message ช่วยในการสื่อสารกับเพื่อนร่วมทีม หรือแม้แต่เป็นการสื่อสารกับตัวเราเองในอนาคต เพื่อให้รู้ว่ามีการเปลี่ยนแปลงอะไรบ้าง จะเกิดผลกระทบอะไรบ้าง

บล็อกนี้นำเสนอรูปแบบหนึ่งสำหรับ Commit Messageของ Git ที่น่าสนใจ

รูปแบบของ Commit Message

!type(?scope): !subject

❗คือ บังคับว่าต้องมี
❓คือ มีหรือไม่มี ก็ได้


Type

  • ❗ต้องมีเสมอ
จากรูปแบบ Commit Message :
!type(?scope): !subject

Type บอกว่าการเปลี่ยนแปลงนี้ทำเกี่ยวกับอะไร โดยมี Types ให้เลือก ดังนี้

  • test
    • สร้างหรือเปลี่ยนแปลง Test Code
  • feat
    • เพิ่ม Feature ใหม่ สำหรับ Project
  • refactor
    • ทำ Code Refactoring โดยไม่กระทบกับ Logic หรือ Rules
  • style
    • เมื่อมีการเปลี่ยน Code Style หรือมีการ Formatting Code
  • fix
    • แก้ Error หรือ Bugs ในระบบ
  • docs
    • เปลี่ยนแปลง Document ของ Project
  • chore
    • มีการเปลี่ยนแปลงที่ไม่เกี่ยวกับ Code หรือ Test
    • เป็นการเปลี่ยนแปลงด้าน Development Dependency หรือ Tools เช่น
      • eslint
      • prettier
      • .gitignore
  • build
    • มีการเปลี่ยนแปลงที่ส่งผลต่อ Build Process หรือ External Dependency
    • เปลี่ยนแปลง Dependency ของ Project
  • perf
    • การเปลี่ยนแปลงที่เกี่ยวข้องกับการเพิ่ม Performance ของระบบ
  • ci
    • เปลี่ยนแปลง CI/CD Configuration File เช่น
      • Circle
      • Travis
      • Jenkins
  • revert
    • มีการย้อยกลับไปที่ Commit ก่อนหน้านี้
📝 Notes (เกี่ยวกับ Type)
  • ใน Commit message ต้องมี Type เสมอ
  • ต้องมีแค่ 1 Type ต่อ 1 Commit
  • ถ้าไม่รู้จะใช้ Type ใด เป็นไปได้ว่า Commit ใหญ่เกินไป
    • ควรแยก Commit ให้เล็กพอที่จะเลือก Type ได้
    • และแต่ละ Commit ต้องเป็นการเปลี่ยนแปลงเพียงเรื่องเดียว

Subject

  • ❗ต้องมีเสมอ
จากรูปแบบ Commit Message :
!type(?scope): !subject

Subject ต้องใช้ประโยคเชิงคำสั่งที่ขึ้นต้นด้วย Verb.1 (โดยไม่มี to) แทนที่จะเป็น Past Tense

เพื่อบอกทีมว่า commit นี้จะทำอะไร ถ้าเรา apply มัน:

If applied, this commit will do something according to this message

เปรียบเทียบ 2 commit message นี้ดู

ตัวอย่าง: การใช้ Subject ใน Commit Message ของ Git
ตัวอย่าง: การใช้ Subject ใน Commit Message ของ Git

จะเห็นว่า

If applied, this commit will change the markup ✅, ดู make sense กว่า

“If applied, this commit will changed the markup” 🟥

นอกจากนี้การเขียน Subject ควรเขียนสั้น ๆ กระชับ โดยทั่วไปไม่ควรเกิน 50 ตัวอักษร ถ้าต้องการเขียนยาวกว่านี้ให้ไปเขียนใน Body ของการ Commit


Scope

  • ❓ มีหรือไม่มี ก็ได้
  • บอก Context หรือบริบทของ Commit นี้

จากรูปแบบ Commit Message :

!type(?scope): !subject

จากรูปแบบที่เขียนมาก่อนหน้านี้ เราจัดการ Commit Message ให้ชัดเจนด้วยการใช้

  • Type บอกประเภทของการเปลี่ยนใน Commit นี้
  • Subject บอกอย่างชัดเจนว่าจะเกิดอะไรขึ้น เมื่อ Apply Commit นี้

แม้ว่า Scope ไม่จำเป็นต้องมีก็ได้ แต่มันช่วยบอก Context ของ Commit นี้เพิ่มเติมได้

และยังช่วยลดภาระของ Subject ลงได้ ทำให้ Subject สั้นและกระชับกว่าเดิม

หมายเหตุ :

  1. Scope ต้องใส่ใน “วงเล็บ” เช่น (Scope) เสมอ
  2. Scope จะถูกแบ่งแยกด้วย “/” (Slash) ได้ เช่น
    • (module/AuthService)
    • (package/PackageName)

ตัวอย่าง

ตัวอย่าง: การใช้ Scope ใน Commit Message ของ Git
ตัวอย่าง: การใช้ Scope ใน Commit Message ของ Git

แนวปฏิบัติเพิ่มเติมเพื่อการ Commit ที่ดี

  • Commit บ่อย ๆ และ เล็ก ๆ
    • ช่วยให้การ review code ง่ายขึ้น
    • ช่วยให้ย้อนกลับไปยังจุดที่ต้องการได้ง่าย
    • ช่วยให้การ merge ง่ายขึ้น เพราะเมื่อเกิด merge conflict จะมี code ไม่มากที่ต้องพิจารณา
    • การเปลี่ยนแปลง code ทีละน้อย ทำให้คุณภาพโค้ดดีขึ้น พัฒนาขึ้นอย่างต่อเนื่อง
  • การเขียน Body ใน Commit
    • เว้น 1 บรรทัดจากหัวข้อ
    • ควรเขียนให้สั้น และกระชับเช่นกัน
    • ควร Wrap ความยาวต่อ 1 บรรทัด ไม่เกิน 72 ตัวอักษร
    • ควรเขียนอธิบายว่าทำอะไร และทำไม (What + Why)
    • ขึ้นต้นประโยคด้วยตัวพิมพ์ใหญ่ และปิดท้ายประโยคด้วยจุด
  • Commit Message เป็นเพียงเครื่องมือหนึ่งในการสื่อสาร ทีมพัฒนาซอฟต์แวร์ยังจำเป็นต้องคุยกัน สื่อสารกันบ่อย ๆ
Categories
การพัฒนาซอฟท์แวร์

การเลือกใช้ Integer ใน Golang

การเลือกใช้ Integer ใน Golang

Golang เป็นภาษาที่มีประเภทของ Integer มากกว่าภาษาอื่นบางภาษา จากตัวเลือกที่ค่อนข้างเยอะ บางครั้งเราอาจสงสัยว่าเมื่อใดที่เราควรใช้ Integer ประเภทใด บทความนี้แนะนำการเลือกใช้ Integer ใน Golang ครับ

ก่อนที่เราจะไปดูหลักเกณฑ์ในการเลือกใช้ เรามาดูกันก่อนว่า Golang มี Integer กี่แบบ

ประเภทขนาด ช่วงของข้อมูลที่เก็บได้
int88 bits-128 ถึง 127
int1616 bits-32768 ถึง 32767
int3232 bits–2147483648 ถึง 2147483647
int6464 bits–9223372036854775808 ถึง 9223372036854775807
uint88 bits0 ถึง 255
uint1616 bits0 ถึง 65536
uint3232 bits0 ถึง 4294967295
uint6464 bits0 ถึง 18446744073709551615
ตาราง แสดงประเภทและช่วงข้อมูลที่เก็บได้สำหรับ Integer แต่ละชนิดใน Golang

นอกจากนี้ Golang ยังมี int และ uint ซึ่งขนาดของมันสามารถเป็นได้ทั้ง 32 bits หรือ 64 bits ขึ้นกับ platform เช่น ถ้าบนเครื่อง 32-bits compiler ก็จะใช้ค่า int และ uint เป็น 32 bits

ในทางตรงข้าม ถ้าบนเครื่อง 64-bits ส่วนใหญ่แล้ว compiler ก็จะใช้ค่า int และ uint เป็น 64 bits (แต่จาก FAQ ของ Golang เค้าบอกว่าไม่ได้เป็นอย่างนี้เสมอไป แต่ก็ไม่ได้ให้ข้อมูลอะไรเพิ่มเติม และผมเองก็หาข้อมูลเพิ่มเติมไม่ได้ด้วยครับ)

FAQ : size ของ int ใน golang

Zero value

ถึงแม้ Golang จะมีประเภทของ Integer หลายประเภท แต่ค่า zero value ของ Integer ทุกประเภท คือ 0

เลือกประเภทของ Integer

เมื่อเรารู้จัก Integer แต่ละประเภทใน Golang แล้ว การเลือกว่าจะใช้ Integer ประเภทใด มีหลักที่เรียบง่ายอยู่ 3 ข้อ คือ

1. ถ้ารู้ขนาดและเครื่องหมาย

ถ้าเราทำงานกับ Binary file หรือ Network protocol หรือข้อมูลที่เรารู้ขอบเขตของขนาดข้อมูลและเครื่องหมายว่าเป็นจำนวนเต็มบวกหรือลบอย่างแน่นอนล่วงหน้าแล้ว ให้เราเลือกใช้ Integer เจาะจงประเภทใดประเภทหนึ่งจากตารางข้างบนไปเลย

2. ถ้าต้องรองรับ Integer ประเภทใดก็ได้

ถ้าเราต้องเขียน Function ที่ควรทำงานกับจำนวนเต็มประเภทใดก็ได้ ให้เขียน function มาคู่หนึ่ง โดยที่ function หนึ่งมี parameter เป็น int64 และอีก function หนึ่งมี parameter เป็น uint64

💡 เราสามารถเห็น pattern นี้ได้ใน Go standard library เช่น ฟังก์ชัน FormatInt / FormatUint หรือ ParseInt / ParseUint ใน strconv package

เหตุผลที่ int64 และ uint64 เหมาะสมในสถานการณ์นี้ เพราะในตอนนี้ Golang ยังไม่มี Generics (เฉพาะตอนนี้) และ Golang ไม่มี Function Overloading อีกด้วย จากเหตุผลเหล่านี้ ส่งผลให้เราต้องเขียนหลาย Function และต้องตั้งชื่อให้ต่างกัน

การใช้ int64 และ uint64 ช่วยให้เราเขียน Function แค่ครั้งเดียว แล้วให้ผู้ที่เรียกใช้แปลงค่า Arguments ที่จะส่งให้ Function และแปลงค่าที่รับคืนจาก Function ได้เองไม่ยากนัก

💡 แต่ถ้าขนาดของ Integer มีความสำคัญ เราควรเขียน Function แยกให้กับ Integer แต่ละประเภท เหมือนกับ math/bits package

3. กรณีอื่น ๆ

กรณีอื่น ๆ นอกจากข้อ (1) และ (2) ให้ใช้ int

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

อ้างอิง

Categories
การพัฒนาซอฟท์แวร์

รีวิวหนังสือ Head First Go

หนังสือ Head First Go
หนังสือ Head First Go

หนังสือ Head First Go เล่มนี้ เป็นหนังสือ Golang ที่ผมคิดว่าอ่านง่าย อ่านสนุกมาก

สำหรับมือใหม่ที่เพิ่งหัดเขียน Golang หนังสือเล่มนี้เหมาะมากที่จะอ่านเป็นเล่มแรก ๆ เพราะมีรูปภาพประกอบการอธิบาย ให้เข้าใจได้ง่าย มีรูปอธิบายโค้ดแต่ละบรรทัด และโค้ดตัวอย่างก็ไม่ยาวจนเกินไป ผมคิดว่ารูปประกอบในหนังสือเล่มนี้ช่วยให้จำ syntax และเข้าใจหัวข้อต่าง ๆ ได้ง่ายขึ้นมาก

ตัวอย่างการอธิบายโค้ด Golang ในหนังสือ Head First Go
ตัวอย่างรูปประกอบการอธิบายเนื้อหา
ตัวอย่างการอธิบายโค้ด Golang ในหนังสือ Head First Go
ตัวอย่างรูปประกอบการอธิบายเนื้อหา
ตัวอย่างการอธิบายโค้ด Golang ในหนังสือ Head First Go
ตัวอย่างรูปประกอบการอธิบายเนื้อหา

ในฐานะคนที่เขียน Golang มานานแล้ว ผมก็ชอบหนังสือเล่มนี้นะครับ ผมชอบวิธีอธิบาย ชอบรูปประกอบ เพราะช่วยให้เราจำสิ่งที่เรารู้อยู่แล้วได้ง่ายขึ้น และยังช่วยให้เราได้เทคนิคใหม่ ๆ เพื่อนำไปใช้อธิบายคนอื่นต่อได้ด้วย

อีกส่วนที่ผมชอบมาก คือ หัวข้อ “there are no Dumb Questions” ที่มีแทรกอยู่เรื่อย ๆ ซึ่งจะตอบคำถามสำหรับมือใหม่ที่เพิ่งจะมาเรียนรู้ Golang แล้วสงสัยว่า

  • ทำไม Golang ไม่มีสิ่งนี้ ?
  • ในภาษาอื่นทำแบบนี้ แต่ทำไม Golang ทำอีกแบบนึง ?
ผมชอบเนื้อหาส่วน "there are no Dumb Questions" ในหนังสือ Head First Go เล่มนี้มาก
ผมชอบเนื้อหาส่วน “there are no Dumb Questions”

ผมว่าเนื้อหาส่วนนี้ช่วยให้คนที่เคยเขียนภาษาอื่นมาก่อน เข้าใจธรรมชาติของ Golang มากขึ้น

และช่วยให้คนที่เขียน Golang มาซักพักแล้ว เข้าใจมากขึ้นว่า ผู้ที่พัฒนา Golang ขึ้นมา มีเจตนาอะไรที่เลือกทำอย่างนั้น

ในส่วนของเนื้อหา หนังสือเล่มนี้ก็ครอบคลุมส่วนหลัก ๆ ของ Golang ได้ครบถ้วนเพียงพอสำหรับมือใหม่ที่จะเริ่มเขียน Golang แล้ว

แต่สำหรับมือเก๋าอาจไม่ได้ความรู้อะไรเพิ่มเติมมากนัก บางประเด็นก็ไม่ได้ลงลึกนัก เช่น เรื่อง Channel ที่เนื้อหาหลักไม่พูดถึงเรื่อง buffer เลย แต่ก็ไปเก็บตกให้เล็กน้อยในส่วนของเนื้อหาท้ายเล่ม

ผมอ่านหนังสือเล่มนี้ใน oreilly.com ผมคิดว่าการจัด format ดีมาก ไม่ต่างจากหนังสือที่เป็นเล่มเลย ส่วนถ้าเป็น kindle ก็ไม่ได้แย่มาก แต่สู้อ่านใน oreilly.com ไม่ได้ (ลองไปกดดาวน์โหลด kindle version มาลองอ่านดูก่อนก็ได้ครับ)

หนังสือเล่มนี้หนา 560 หน้า ผมใช้เวลาอ่านวันละ 2-3 ชั่วโมง + coding ตามเนื้อหาแทบทุกส่วน และยังทดลองเพิ่มเติมเองเพื่อให้เข้าใจมากขึ้น เรียกว่าอ่านละเอียดมาก อ่านเหมือนคนไม่เคยเขียน Golang มาก่อน ผมใช้เวลาอ่าน 10 วันก็จบครับ

……

มือใหม่ที่ต้องการศึกษา Golang ผมแนะนำหนังสือเล่มนี้เลยครับ

Categories
การพัฒนาซอฟท์แวร์

Hosting ฟรี สำหรับ Static Website

รวม Hosting ฟรี (หรือราคาถูก) สำหรับวาง Static Website ที่น่าใช้งาน

Netlify

Github Pages

Gitlab Pages

Render

Vercel

Firebase Hosting

Cloudflare Pages

Surge

Amazon S3

Statically

VULTR

DigitalOcean App Platform

000webhost

Freehostia

AwardSpace

Categories
UX

แอบฟังเสียงผู้ใช้งาน

ผมนั่งฟังแฟนของผมประชุมกับที่ทำงาน เกี่ยวกับซอฟต์แวร์ที่จ้างภายนอกพัฒนา

หลายประเด็นเกี่ยวกับ UX น่าสนใจและเป็นบทเรียนที่ดี

  1. ความไม่สะดวกเพียงแค่เล็กน้อย แต่ส่งผลกับผู้ใช้มากถ้าต้องใช้ซอฟต์แวร์นั้นเยอะๆทั้งวันถ้านักพัฒนาคิดมากขึ้นและลงแรงอีกนิดจะช่วยผู้ใช้งานได้เยอะเลย เราต้องลงแรงให้มากที่สุดในการออกแบบ UX เท่าที่ resource (คน+เวลา) มี
  2. feedback การใช้จริงแบบนี้มีผลมากกับนักพัฒนา ถ้านักพัฒนาได้ฟัง ซอฟต์แวร์เวอร์ชันต่อไปจะดีขึ้นอย่างมาก

ผมคิดว่าข้อ 2 นี่สำคัญมาก ทำอย่างไรเราจะได้ฟังความคิดเห็นแท้จริงจากผู้ใช้(พูดในสิ่งที่คิดทั้งหมด) และไม่ผ่านการกรองของคนอื่นที่ไม่ใช่ผู้ใช้งานจริง ๆ

Categories
การพัฒนาซอฟท์แวร์

เปิด UML class diagram ใน IntelliJ

กด Ctrl+Alt+U เพื่อเปิด UML class diagram

Categories
การพัฒนาซอฟท์แวร์

รวมสไลด์จากงาน I/O Extended Bangkok (9 ก.ค. 2017)

เมื่อวันเสาร์ที่ผ่านมา (9 ก.ค. 2017) มีการจัดงาน I/O Extended Bangkok ที่ตลาดหลักทรัพย์แห่งประเทศไทย มีหัวข้อที่น่าสนใจหลายหัวข้อเลยครับ ผมรวบรวมสไลด์เท่าที่หาได้ สำหรับเพื่อน ๆ ที่ไม่ได้ไปร่วมงานหรือเพื่อน ๆ ที่ไปร่วมงานแล้วอยากได้ Reference จากในสไลด์ของ speaker แต่ละท่านเพื่อนำไปศึกษาเพิ่มเติม

What’s new in Firebase and Cloud Platform


Speaker : Jirawat Karanwittayakarn (GDE Firebase)

…..

What’s new in Android O

GitHub -> https://github.com/akexorcist/Android-O-Feature

Speaker : Somkiat Wongkitwattana (GDE Android)

…..

Unlock the power of web Components with Polymer

Speaker : Tanakrit Saisillapee

…..

Introduction to Kotlin

Speaker : Travis Subanaphong

…..

Building apps for the Google Assistant

Github -> https://github.com/thangman22/googleiobkk17-action-on-google

Speaker : Warat Wongmaneekit (GDE Web Technologies)

…..

Speeding up your Android Gradle builds

Speaker : Jirawat Karanwittayakarn (GDE Firebase)

…..

Introduction to Architecture Components

GitHub -> https://github.com/akexorcist/Android-ArchComponents

Speaker : Somkiat Wongkitwattana (GDE Android)

…..

Modern web The next era of web experience

Speaker : Warat Wongmaneekit (GDE Web Technologies)

Categories
iOS

[iOS] การทำให้ UITableView ไม่แสดง Separator สำหรับ Cell ที่ว่าง

ผมคิดว่าคนที่พัฒนาแอพ iOS คงจะเคยเจอปัญหาว่า UITableView จะแสดงเส้นคั่น (separator) สำหรับ cell ที่ว่างกันมาบ้าง ตามรูปนี้ครับ

บล็อกนี้ผมจะเสนอวิธีง่ายๆ ที่จะแก้ปัญหานี้ครับ

Swift

override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.tableFooterView = UIView()
}

Objective-C

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // This will remove extra separators from tableview
    self.tableView.tableFooterView = [UIView new];
}

หรืออาจจะทำแบบนี้ก็ได้ครับ

self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];

อ้างอิง stackoverflow

Categories
Git

การใช้ไฟล์ .gitignore กับโปรเจคเก่า ๆ

บางครั้งเรามีโปรเจคเก่าที่ใช้ Git อยู่แล้ว แต่เรา ignore ไฟล์ไม่ครบ อาจมีไฟล์ที่เราไม่ต้องการ ถูก track เข้าไปด้วย เช่น ไฟล์จาก OS หรือ ไฟล์จาก IDE เป็นต้น

เราสามารถแก้ไขได้ง่าย ๆ ด้วยคำสั่งเพียงไม่กี่คำสั่ง แต่ก่อนอื่นผมขอแนะนำสำหรับคนที่ไม่รู้ว่าจะ Ignore ไฟล์อะไรดี สามารถเข้าไปดูได้ที่ https://github.com/github/gitignore ซึ่งรวบรวมไฟล์และโฟลเดอร์ที่เราควร ignore ไว้ครบถ้วน (ผมแนะนำให้ดูในโฟล์เดอร์ Global ด้วย เพราะมีคำแนะนำการ ignore ไฟล์ของ IDE และ OS เพิ่มเติม)

สมมุติว่าผมเขียน Java ด้วย Eclipse บน macOS ผมจะสามารถสร้างไฟล์ .gitignore ได้ประมาณนี้

[gistpen id=”1197″]

เมื่อสร้างไฟล์ .gitignore ก่อนที่จะทำขั้นตอนต่อไปให้ทำการ commit การเปลี่ยนแปลงทั้งหมดก่อน

แล้วใช้คำสั่งนี้เพื่อเคลียร์ index ของ git

$ git rm -r --cached .

แล้วใช้คำสั่ง

$ git add .

และ commit

$ git commit -m ".gitignore is now working"

เพียงเท่านี้เราก็สามารถ ignore ไฟล์และโฟลเดอร์ที่เราไม่ต้องการได้แล้วครับ

Categories
Git

การใช้งาน Git Submodules

หลังจากที่ผมได้รู้จัก Git Submodules มาซักพักหนึ่ง ผมก็พยายามใช้ในงานต่าง ๆ ซึ่งช่วยให้ผมจัดการ Source code สะดวกขึ้นมาก แต่เนื่องจากได้ใช้คำสั่งพวกนี้ในช่วงเริ่มโปรเจคซะเป็นส่วนใหญ่เลยทำให้มีอาการหลงลืมคำสั่งไปบ้าง ก็เลยมาบันทึกไว้หน่อยจะได้ไม่ต้องไปหาข้อมูลใหม่อีก

การเพิ่ม Submodules เข้ามาใน Git Repository

ใช้คำสั่งดังนี้

$ git submodule add https://github.com/golfz/module1 lib/module1
Initialized empty Git repository in ~/a_project/lib/module1/.git/
remote: Counting objects: 1006, done.

remote: Compressing objects: 100% (978/978), done.

remote: Total 1006 (delta 631), reused 0 (delta 0)

Receiving objects: 100% (1006/1006), 408.22 KiB, done.

Resolving deltas: 100% (631/631), done.

แต่ละส่วนของคำสั่งมีรายละเอียดดังนี้

git submodule add เป็นคำสั่ง git เพื่อเพิ่ม Submodules

https://github.com/golfz/module1 คือ git repository ภายนอกที่เราต้องการเพิ่มเข้ามาเป็น submodule ซึ่งคุณต้องมั่นใจว่าคุณมีสิทธิ clone repository นี้

lib/module1 คือ path ของ submodules ใน repository หลัก

ลองตรวจสอบ repository ดูด้วย git status
$ git status
# On branch master
# Changes to be committed:

# (use "git reset HEAD <file>..." to unstage)

#

# new file: .gitmodules

# new file: lib/module1

#

ถ้าเราลองตรวจสอบ .gitmodules ที่เพิ่มเข้ามาใหม่ จะพบข้อมูลดังนี้

$ cat .gitmodules
[submodule "lib/module1"]
path = lib/module1

url = https://github.com/golfz/module1

การใช้งาน Submodules

ถ้าเราเพิ่ม clone repository ลงมา เราจะพบว่าโฟลเดอร์ที่เป็น submodule จะว่างเปล่า ดังนั้นเราต้อง initial submodules ก่อน ดังนี้

$ git submodule init
Submodule 'lib/module1' (https://github.com/golfz/module1) registered for path 'lib/module1'

ขั้นตอนต่อไปเราจำเป็นต้อง pull files ลงมา

$ git submodule update
Initialized empty Git repository in ~/a_project/lib/module1/.git/
remote: Counting objects: 26, done.

remote: Compressing objects: 100% (22/22), done.

remote: Total 26 (delta 5), reused 0 (delta 0)

Receiving objects: 100% (26/26), 17.37 KiB, done.

Resolving deltas: 100% (5/5), done.

Submodule path 'lib/module1': checked out '1c407cb2315z0847facb57d79d680f88ca004332'

ตอนนี้ถ้าเราเข้าไปดูใน lib/module1 เราจะพบว่ามีไฟล์อยู่อย่างถูกต้องแล้ว

การลบ Submodules

ขั้นตอนในการลบ Submodules จะมีความซับซ้อนเล็กน้อย ดังนี้

1. เข้าไปลบข้อมูล submodules ในไฟล์ .gitmodules ให้ลบข้อมูลออกไปดังนี้
[submodule "lib/module1"]
path = lib/module1
url = https://github.com/golfz/module1

2. เข้าไปลบข้อมูล submodule entry ในไฟล์ .git/config ขั้นตอนนี้ไม่จำเป็นต้องทำก็ได้ แต่เพื่อป้องกันปัญหาในอนาคตหากเราใช้คำสั่ง “git submodule init” ผมแนะนำว่าทำก็ดีครับ โดยให้ลบข้อมูลออกไปดังนี้
[submodule "lib/module1"]
url = https://github.com/golfz/module1

3. ลบ path ของ Submodules ด้วยคำสั่ง
$ git rm --cached lib/module1
rm 'lib/module1'

การอัพเดต Submodules

นี่เป็นอย่างหนึ่งที่เราจะค่อนข้างสับสนเมื่อใช้งาน Submodules เพราะเมื่อเราเพิ่ม submodules เข้ามา เราจะได้รับ commit ล่าสุดของ repository ของ submodule นั้น

หากหลังจากนั้น Repository ของ submodule มีการอัพเดตแล้วเรา clone โปรเจคหลักไปที่อื่น แล้วใช้คำสั่ง

$ git submodule init

ต่อด้วย

$ git submodule update

เราจะพบว่าเราจะไม่ได้โค้ดล่าสุดของ submodule ซึ่งมักสร้างความสับสนอยู่บ่อยครั้ง

แต่หากคิดดูดี ๆ ก็จะพบว่ากลไกนี้มีความเหมาะสมแล้ว เนื่องจาก code ใน repository หลักของเราได้ถูกทดสอบกับ submodules เวอร์ชั่นใด เมื่อ clone repository หลักไปใช้ก็ควรได้ submodule เวอร์ชั่นเดิมที่เคยทดสอบแล้ว ไม่ใช่เวอร์ชั่นใหม่ล่าสุดที่เราไม่เคยทดสอบ

แต่หากเราต้องการ repository ของ submodule ใหม่ล่าสุด สามารถทำได้ดังนี้

1. เข้าไปที่โฟลเดอร์ของ submodule

$ cd lib/module1

2. checkout ไปที่ master

$ git checkout master
Previous HEAD position was b8ff8f6... re-ordering
Switched to branch 'master'

Your branch is behind 'origin/master' by 8 commits, and can be fast-forwarded.

3. ดึงอัพเดตล่าสุดลงมา

$ git pull
remote: Counting objects: 31, done.
remote: Compressing objects: 100% (24/24), done.

remote: Total 24 (delta 15), reused 0 (delta 0)

Unpacking objects: 100% (24/24), done.

From https://github.com/golfz/module1

b8ff8f6..5cab93f master -> origin/master

* [new tag] 1.2.28 -> 1.2.28

From https://github.com/golfz/module1

* [new tag] 1.2.26 -> 1.2.26

* [new tag] 1.2.27 -> 1.2.27

Updating c547e0d..5cab93f

Fast-forward

index.php | 109 ++++++++++++++-

css/admin.css | 26 ++++

js/admin.js | 17 +++

3 files changed, 51 insertions(+), 4 deletions(-)

create mode 100644 css/admin.css

create mode 100644 js/admin.js

4. กลับไปที่ repository หลัก แล้ว

$ git add lib/module1
$ git status
# On branch master
# Changes to be committed:

# (use "git reset HEAD ..." to unstage)

#

# modified: lib/module1

#