Tuesday, November 27, 2007

Git อีกทีน่า

วันนี้ลองมาดูประเด็นที่ทำให้คนที่เคยใช้ cvs หรือ svn เกิดอาการเหวอเมื่อใช้ git ได้

ขั้นตอนปกติในการใช้ git
  1. สร้าง working copy ด้วยคำสั่ง git clone url
  2. แก้ไข file ที่ต้องการ
  3. สั่ง git add yourfilename เพื่อ mark ว่าต้องการ commit file นี้ในอนาคต
  4. สั่ง git commit
ดูเผินๆแล้ว จะเห็นว่าไม่น่ามีอะไร
สำหรับ user ที่ใช้ svn มาก่อน ก็จะบอกว่า
ขั้นที่ 3 มันเป็นขั้นที่เกินมาเนี่ย ใน svn ไม่เห็นต้องสั่งอะไรแบบนั้นเลย

ถ้าไม่รู้ว่าจริงๆแล้ว git ทำอะไรในชั้นที่ 3 มันก็อาจจะทำให้เกิด error ขึ้นได้
ลองจำลองเหตุการณ์ใหม่
  1. user clone project
  2. user แก้ไข file x
  3. user สั่ง git add x เพื่อบอก git ว่าต้องการที่จะ commit file นี้
  4. user กลับไป edit file x อีกรอบ
  5. user สั่ง git commit
ผลก็คือ เฉพาะการแก้ไขในขั้นที่ 2 เท่านั้น ที่ถูก commit เข้าไป
การแก้ไขในขั้นที่ 4 ไม่รวมอยู่ในนั้นด้วย
ขั้นตอนที่ถูกก็คือ
  1. user clone project
  2. user แก้ไข file x
  3. user สั่ง git add x เพื่อบอก git ว่าต้องการที่จะ commit file นี้
  4. user กลับไป edit file x อีกรอบ
  5. user สั่ง git add x อีกรอบ
  6. user สั่ง git commit
จะเห็นว่า git มีความแตกต่างกับพวก svn หรือ cvs อยู่
ตัว git เองมองสิ่งที่ track คือ content ไม่ใช่ file ทั้ง file
การที่เราสั่ง git add เป็นการสั่ง add content ที่เปลี่ยนแปลง ณ ขณะนั้น
การเปลี่ยนแปลงที่ตามหลังมา ไม่นับว่าอยู่ในกลุ่มที่จะ commit

keyword "Content" ยังนำความแตกต่างมาสู่วิธีการ commit change ด้วย
เราสามารถจัดการ content ที่ต้องการให้้ git commit ได้ในระดับ fine-grained
สมมติว่ามีการเปลี่ยนแปลงใน file ที่เราแก้ไข 5 จุด
เราสามารถเลือกได้ว่า การ commit ครั้งนี้ เราต้องการที่จะ commit เฉพาะจุดที่ 2 กับ 3 เท่านั้นนะ

ในการที่จะจัดการกับ content ที่จะ commit
เราสามารถทำได้โดยการ switch เข้าสู่ mode interactive ด้วยคำสั่ง
git add -i
ลองดูตัวอย่างการใช้งาน
สมมติว่าผมมี file x อยู่ ใน file นี้ผมมีการแก้ไข 2 จุดก็คือ
มีการ insert บรรทัดแรกสุด กับ บรรทัดท้ายสุดลงไป

เริ่มด้วยคำสั่ง git add -i
git ก็จะแสดงให้เห็นว่า file x มีการเปลี่ยนแปลงเพิ่มบรรทัดอยู่ 2 จุดนะ

pphetra@pann:~/temp/s$ git add -i
staged unstaged path
1: unchanged +2/-0 x

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help

ลองสั่ง help ดู ก็จะได้คำอธิบายย่อๆดังนี้

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> 8
status - show paths with changes
update - add working tree state to the staged set of changes
revert - revert staged set of changes back to the HEAD version
patch - pick hunks and update selectively
diff - view diff between HEAD and index
add untracked - add contents of untracked files to the staged set of changes

ลองเลือก menu patch
มันก็จะแสดง file ขึ้นมา ให้เราก็ระบุหมายเลข file ที่ต้องการ

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> 5
staged unstaged path
1: unchanged +2/-0 x
Patch update> 1
diff --git a/x b/x
index abe1496..61916db 100644
--- a/x
+++ b/x
@@ -1,3 +1,5 @@
+hi bunny
hello
hi pok
hi pann
+hi pune
Stage this hunk [y/n/a/d/s/?]? ?
y - stage this hunk
n - do not stage this hunk
a - stage this and all the remaining hunks
d - do not stage this hunk nor any of the remaining hunks
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks

กรณีนี้เราต้องการ ที่จะ commit เฉพาะ row บนสุดเท่านั้น
เราก็สั่ง split
จากนั้น git จะแสดงการเปลี่ยนแปลงให้เราตัดสินใจทีละอัน

@@ -1,3 +1,5 @@
+hi bunny
hello
hi pok
hi pann
+hi pune
Stage this hunk [y/n/a/d/s/?]? s
Split into 2 hunks.
@@ -1,3 +1,4 @@
+hi bunny
hello
hi pok
hi pann
Stage this hunk [y/n/a/d/j/J/?]? y
@@ -1,3 +2,4 @@
hello
hi pok
hi pann
+hi pune
Stage this hunk [y/n/a/d/K/?]? d
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> 7
Bye.
จะเห็นได้ว่า การจัดการ patch ของ git เร้าใจกว่า svn เยอะ
แต่ก็อาจจะนำความเวียนหัวมาสู่ผู้ที่คุ้นเคยกับโลกของ svn อย่างเดียวได้

Related link from Roti

2 comments:

Sirn said...

ดูแล้วผมยังรู้สึกว่า git มันยุ่งยากอยู่ดีแฮะ ถูกใจกับ Mercurial+Mq มากกว่า

PPhetra said...

sirn เขียนแนะนำ Mq ให้ฟังหน่อยสิ
ว่าประทับใจอะไร ตรงไหน