Version Control and Git From Zero to Hero : Part 5 (Fixing the Mistakes)

ภาพจาก https://solarsystem.nasa.gov/resources/925/solar-system-and-beyond-poster-set/

บทความโดย ผศ.ดร.ณัฐโชติ พรหมฤทธิ์
ภาควิชาคอมพิวเตอร์
คณะวิทยาศาสตร์
มหาวิทยาลัยศิลปากร

ใน Part 1-4 เราได้เรียนรู้แนวคิดและความเป็นมาของ Version Control รวมทั้งการใช้งาน Git เบื้องต้น ได้แก่

  • การสร้าง Project บน Gitlab Server
  • การดู Version ของ Git
  • การ Config Username และ Email
  • การเชื่อมโยง Local Project กับ Remote Project (Remote Repository)
  • การดูสถานะของไฟล์ใน Working Directory
  • การ Check-In
  • การดู History
  • การ Sync History กับ Remote Project
  • การ Clone Project
  • การเปรียบเทียบความเปลี่ยนแปลงของ Source Code และ
  • การใช้งาน Git ร่วมกับ Jupyter Notebook

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

Initializing Git

ให้สร้าง Project ใหม่ชื่อว่า gitpro บน https://gitlab.cpsudevops.com หรือ https://gitlab.com

ที่นี่จะใช้เพื่อเก็บ Remote Project สำหรับ Sync กับ Local Project ซึ่งในตอนแรกมันจะเป็นเพียง Project ว่างเปล่าที่ยังไม่มี Source Code ใดๆ

แต่เมื่อเขียน Code เราจะทำงานที่  Local Project ซึ่งต้องมีการ Link กับ Remote Project สำหรับการเตรียมใช้ Git ในฝั่ง Local Host เราจะสร้าง Folder gitpro และใช้คำสั่ง git init เพื่อสร้าง Local Project โดย Git จะสร้าง Folder .git  ภายใน Folder gitpro สำหรับเก็บ Commits ต่างๆ ที่มีโครงสร้างดังภาพ

git init

เราจะเชื่อมโยง Local Project กับ Remote Project เข้าด้วยกัน

git remote add origin http://gitlab.cpsudevops.com/nuttachot/gitpro.git

เพื่อจะใช้งาน Git ร่วมกับ Jupyter Notebook เราต้องปรับแต่งให้มันบันทึก Source Code แบบ Plain Text (ดูได้จาก Part 4) และควร Config Git ไม่ให้ไป Track ไฟล์ *.ipynb โดยการเพิ่มข้อความ *.ipynb ในไฟล์ .gitignore แล้ว Checkin ด้วยคำสั่ง git add และ git commit

git add .
git commit -m 'add gitignore'

Fixing the Mistakes

git add . จะส่งไฟล์ทุกไฟล์ไปยัง Staging Area เพื่อเตรียมการ Commit แต่ถ้าเราเพิ่มไฟล์บางไฟล์ลงใน Staging Area โดยไม่ตั้งใจก็สามารถเอามันออกมาโดยใช้คำสั่ง git reset

Python Code ในไฟล์ hello.Rmd
git reset hello.Rmd

ขั้นต่อไปให้แก้ไข Code ในไฟล์ hello.Rmd ตามภาพด้านล่าง กด Save แล้ว Check-In

git add .
git commit -m 'add hello.Rmd'

เราสามารถดู Commit ทั้งหมดได้โดยใช้คำสั่ง git log --oneline แต่ถ้าต้องการเห็นว่ามีความเปลี่ยนแปลงที่ไฟล์ใดและมีการเปลี่ยนแปลงอย่างไรบ้าง ก็ให้เพิ่ม parameter --stat ครับ

git log --oneline
git log --oneline --stat

บางครั้งเราเขียน Commit Message ผิด เราสามารแก้ไขมันโดยการเพิ่ม parameter --amend ตามหลังคำสั่ง git commit ซึ่งจะทำให้ Hash ID ใน Commit เดิมเปลี่ยนไปด้วย

git commit --amend -m 'edit add function'

ในกรณีที่เราสร้างไฟล์ใหม่แต่ต้องการเพิ่มไฟล์นั้นใน Commit เดิมก็สามารถใช้พารามิเตอร์ --amend ได้เช่นกัน จากภาพด้านล่างจะเห็นว่าไฟล์ sub.Rmd ถูกเพิ่มลงใน Commit เดิม ("edit add function") แต่ Hash ID เปลี่ยนไปเช่นกัน

git add .
git commit --amend --no-edit

ทีนี้ก็ให้ Sync History กับ Remote Project ได้แล้ว โดยใช้คำสั่ง git push -u ซึ่งในครั้งต่อไปเราจะสามารถใช้เพียงคำสั่ง git push สั้นๆ

git push -u origin master

จากภาพจะเห็น origin/master อยู่ที่ตำแหน่งเดียวกันกับ HEAD บน Local Host  แล้ว ตอนนี้คนอื่นๆ ในทีมก็สามารถ Clone Project ไปใช้ต่อได้ (สรุปคำสั่ง Git)

แต่เมื่อเรากลับมาแก้ไข Code สักไฟล์แล้วพัง และอยากจะ Reset กลับไปยัง Version เดิมใน Commit ล่าสุด เราจะใช้คำสั่ง git checkout เพื่อนำไฟล์จากใน Commit ล่าสุดกลับมาแทนที่ไฟล์ที่พังใน Working Directory

ลบคำสั่ง sub(3,2) ในไฟล์ sub.Rmd

เปรียบเทียบความเปลี่ยนแปลงของไฟล์ sub.Rmd ใน Working Directory ก็จะเห็นว่ามีการลบคำสั่ง sub(3,2) ครับ

แค่ใช้คำสั่ง git checkout เราก็จะหายจากอาการหัวร้อนแล้ว ^^

git checkout sub.Rmd
Refresh browser เพื่อแสดง Code ที่ checkout กลับมา

ในกรณีที่มีความผิดพลาดจากการ Check-In โดยไม่ตั้งใจ เช่นเราสร้างไฟล์ขึ้นมา 2 ไฟล์ โดยมีการ Commit ถึง 2 ครั้ง ทั้งที่ทั้ง 2 ไฟล์ เป็นงานเดียวกัน เราจะใช้ท่าที่อันตรายขึ้นในการแก้ปัญหาครับ

ให้เขียน Code สร้างฟังก์ชัน sump ตามภาพ กด Save แล้ว Check-In

เพิ่มไฟล์ sum.Rmd
git add .
git submmit -m 'add sum function'
แสดง History เมื่อมีการเพิ่มไฟล์ sum.Rmd

และเพิ่มอีกไฟล์ชื่อ report.Rmd สำหรับเรียกใช้ฟังก์ชัน sump จากไฟล์ sum.Rmd ครับ ซึ่งการเรียกใช้ฟังก์ชันข้ามไฟล์เราจะต้องเพิ่ม Code ตอนต้นของโปรแกรมเพื่อ Import ฟังก์ชันจากต่างไฟล์ ในกรณีที่เรารันไฟล์ sum.py เราจะเขียน Code ดังนี้ from sum import sump แต่สำหรับการ Import ฟังก์ชันที่รันบน Jupyter Notebook เราจะต้องติดตั้ง Library ipynb ก่อน เพื่อให้มันรู้จักกัน

pip install ipynb

เขียน Code เรียกใช้ฟังก์ชัน sump ตามภาพด้านบน กด Save แล้ว Check-In ครับ

git add .
git commit -m 'add report.Rmd'

เพื่อจะแก้ปัญหานี้เราจะดีดนิ้วสลาย Commit 2 Commit ได้แก่ Commit ID 7fb82f5 และ 9732d45 ซึ่งอยู่หลังจาก Commit ID fdd69ae โดยใช้คำสั่ง git reset เพื่อเรียกคืนไฟล์ที่เคย Commit กลับมาแล้วรวบรวม ไฟล์ทั้ง 2 ไฟล์กลับไป Check-In ใหม่ใน  Commit เดียวกัน  

คำสั่ง git reset มี parermeter อยู่ 3 แบบ คือ --soft, --mixed และ --hard ซึ่งแต่ละแบบมีความรุนแรงในการแก้ปัญหาต่างกัน

git reset --soft fdd69ae
จะลบ Commit ทุก Commit หลัง Commit ID fdd69ae แล้วนำไฟล์ที่เคยอยู่ใน Commit เหล่านั้นกลับมายัง Staging Area
git reset --mixed fdd69ae หรือ git reset fdd69ae
จะลบ Commit ทุก Commit หลัง Commit ID fdd69ae แล้วนำไฟล์ที่เคยอยู่ใน Commit เหล่านั้นกลับมายัง Working Directory
git reset --hard fdd69ae
จะลบ Commit ทุก Commit หลัง Commit ID fdd69ae และจะทำลายไฟล์ที่เคยอยู่ใน Commit เหล่านั้นซะ

ดังนั้นเราจะเลือก git reset --mixed fdd69ae หรือ git reset fdd69ae เพื่อสลาย Commit  และดึงไฟล์ทั้ง 2 ไฟล์กลับมายัง Working Directory แล้วแก้ไข Code อีกเล็กน้อย ก่อน Commit ใหม่อีกครั้ง

เมื่อได้ไฟล์ทั้ง 2 ไฟล์กลับมาเราจะแก้ไข Code ใหม่ตามภาพด้านบน กด Save แล้ว Check-In ครับ

หน้าตาของ History ของเราก็จะคล้ายกับภาพด้านบนซึ่งมีทั้งหมด 3 Commit ตอนนี้ก็ถึงเวลา Sync History กับ Remote Project แล้ว

git push

ครั้งนี้เราได้เรียนรู้เกี่ยวกับการแก้ปัญหาในกรณีต่างๆ หวังว่าผู้อ่านจะสามารถใช้งาน Git ได้อย่างยืดหยุ่นขึ้น แต่ก็ยังมีอีกหลายท่าของ Git ที่เรายังไม่ได้ใช้งาน ท่านผู้อ่านสามารถติดตามได้ใน Part ต่อไปเร็วๆ นี้นะครับ

ขอขอบคุณ Nipa.Cloud ที่ให้การสนับสนุน Environment ในการเรียนการสอน
รายวิชา Dev-Ops and Cloud Engineering 101