จะเขียนอย่างย่อมากๆแต่เต็มไปด้วย Screen Shot เหมาะสำหรับคนใช้ TortoiseSVN เป็นมาบ้างแล้วเท่านั้นนะขอรับ (. .’)
ขั้นที่ 1 เราจะสร้างเครื่องคิดเลขโง่ๆกันครับ สามารถทำการ
- ยกกำลังสอง (Square)
- ถอดราก (Sqrt) และ
- กลับเครื่องหมาย (Inverse) ได้
หน้าตาออกมาประมาณนี้ และเราก็จะทำการ Implement ฟังก์ชันอันหนึ่งไปก่อน ส่วนอีกสองอันที่เหลือจะแจกให้ Dev คนอื่นทำ
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace SimpleCalculator
{
public partial class FormMain : Form
{
public FormMain()
{
InitializeComponent();
}
private void uxSignInverse_Click(object sender, EventArgs e)
{
try
{
int val = Convert.ToInt32(uxInput.Text);
uxInput.Text = (-1*val).ToString();
}
catch (Exception ex)
{
MessageBox.Show("Something Wrong!");
}
}
}
}
ขั้นที่ 2 เอาเข้า Repository ครับ ผมใช้วิธีสร้างโฟลเดอร์ว่างๆใน Repo Browser แล้ว check out ออกมาก่อน แล้วเอา project ใส่เข้าไป เลือก commit เฉพาะไฟล์ที่จำเป็นตามที่เขียนไว้ในเรื่องก่อน (การใช้งาน SVN กับ Visual Studio)
ขั้นที่ 3 แตก Branch ออกไปสองอัน เป็น Feature Branch โดยจะให้ Dev อีกสองคนมาเขียนส่วน Square Root และ Square
ขั้นที่ 4 Dev ทั้งสองทำงานบน Branch ของตัวเอง โดย check out SimpleCalculator/brances/square (หรือ squareroot) แล้วเพิ่มโค้ดส่วนที่เป็นการหา Square และ Square Root แล้ว commit กลับเข้าไป
ถ้ามาดูหน้าตาของ Revision Graph จะได้ประมาณนี้
ขั้นที่ 5 ถึงเวลา Merge อันนี้แอบยากและวุ่นวายนิดนึง เริ่มจากการ Merge โค้ดของ Square Branch กลับเข้าไปใน trunk ก่อน ดูให้แน่ใจว่าตอนนี้เราทำงานอยู่ที่โฟลเดอร์ trunk (Working Directory เป็น trunk)
อันนี้เป็นการ Merge ในกรณีที่ trunk ไม่มีการเปลี่ยนแปลงใดๆตั้งแต่แยก Branch ออกมา ถ้ามีการเปลี่ยน (ซึ่งก็มักจะเปลี่ยน) จะต้องทำการ Sync ตัว Branch ของเราเข้ากับ Trunk ก่อน Merge เสมอ ซึ่งเดี๋ยวจะต้องทำตอน Merge Square Root เข้ามา
การ Merge จะเอาการเปลี่ยนแปลง (Delta) ทั้งแต่ trunk จนไปถึง branch เข้ามาบวกให้กับ trunk ปัจจุบัน อาจจะดูงงๆนิดหน่อยเหมือนที่เขียนไว้ในโพสต์ก่อน (Branch และ Merge ใน SVN) ให้ FROM เป็น trunk และ TO เป็น branches/square และสั่งให้ Merge เข้าไปที่ trunk ตามรูป
พวกปุ่มทั้งหลาย ตั้งแต่ Dry run, Diff, Unified diff ลองเล่นๆดูได้ มีประโยชน์มากมาย
หลังจาก Merge เสร็จแล้ว ก็ตรวจดูให้เรียบร้อยว่ามันทำงานได้ ในที่นี้ก็คือ เราจะใช้ได้ทั้งสองฟังก์ชันทั้ง Inverse และ Square นั่นเอง ^ ^
มั่นใจว่าใช้ได้ก็ commit
ขั้นที่ 6 อันนี้ยาก เป็นการ Merge Square Root เข้าไปใน trunk เนื่องจากตอนนี้ trunk มีการเปลี่ยนไปแล้วตั้งแต่เวลาที่แตก branch ออกมา (จากการ commit ในขั้นตอนก่อน) ดังนั้นต้องปรับ base ของเราให้ทันสมัยก่อน ก่อนทำดูให้แน่ใจก่อนว่าตอนนี้ Working Directory อยู่ที่ branch
การปรับให้ทันสมัย (หรือที่ผมชอบเขียนว่า sync branch เข้ากับ trunk) ทำโดยการหาความเปลี่ยนแปลง (Delta) ของ trunk ตั้งแต่ตอนเริ่มแตกออกมา จนมาถึง trunk ปัจจุบัน (HEAD revision) มาบวกกับ branch ของเรา ดังนั้นจะใช้ FROM เป็น trunk ใช้ revision ตอนแตก branch หรือตอน sync ครั้งล่าสุด และ TO เป็น trunk ปัจจุบัน (HEAD) และสั่งให้ Merge ไปที่ตัว branch
- FROM trunk REVISION 10 (เช่นพบว่าแตกออกมาตอน rev 10)
- TO trunk REVISION HEAD
- MERGE TO branches/squareroot
ปัญหาสำคัญในขั้นตอนนี้อยู่ที่ การหา revision number ของ trunk ตอนแตก branch หรือตอน sync ครั้งล่าสุด ออกมาจะทำยังไง ถ้าเป็นการแตก branch ครั้งแรก เราสามารถดูจาก log message ได้ว่ามีการ copy จาก trunk เกิดขึ้นที่ revision ไหน แต่หลังจากการ sync ไปแล้วครั้งนึงแล้ว เราจะเห็นการ sync เป็นแค่การ commit ธรรมดา ดังนั้นมันจึงสำคัญมากที่จะต้องระบุเลข revision ของ trunk ที่เรา sync ล่าสุดใน log message ของการ commit ด้วย ตอน merge ครั้งหน้าจะได้ดูได้ง่ายๆ อันนี้เป็นข้อเสียของ SVN (ซึ่งเหมือนว่าจะไม่มีใน Git - -‘a) -- แต่ถ้าคิดว่าจะ merge ครั้งเดียวแล้วลบ branch นี้ทิ้งไปเลยก็ได้เช่นกัน
ในกรณีนี้อาจมีการ Conflict เกิดขึ้นได้ด้วย โดยเราจะต้องใช้วิจารณญาณแก้ไข conflict เอง ในตัวอย่างจะเกิดที่ FormMain.cs ไอคอนมันจะไม่ใช่สีแดง (Modified) หรือสีเขียว แต่จะเป็น สีเหลืองพร้อมเครื่องหมายตกใจ (อัศเจรีย์ – !) ให้คลิกขวาที่ไฟล์แล้วเลือก TortoiseSVN -> Edit Conflicts จะมีหน้าต่างสีแดงฉานขึ้นมาให้ใช้ ก็ให้ใช้ common sense แก้ไขนะครับ :)
หลังจาก Resolved (แปลว่าแก้ conflict ได้แล้ว) ได้แล้ว ก็ทำการทดสอบ (ควรจะใช้ได้ทั้ง 3 functions แล้ว) ก็ทำการ commit
หลังจากนั้นกลับไปทำงานที่ trunk (Working Directory เป็น trunk) แล้ว Merge ด้วยวิธีเดิมได้แล้ว (เพราะ sync กันแล้ว) ซึ่งก็ไม่น่าจะมีปัญหาอะไรใดๆ แต่พึงสังวรณ์ไว้ว่าการ merge ในขั้นตอนนี้ถึงจะมีปัญหา Subversion ก็จะบอกไม่ได้อยู่ดี .. เป็นข้อเสียอย่างหนึ่ง
การแก้แบบหนึ่งนั้นทำได้โดยใช้ 3-way merge tool ซึ่งจะเป็นการ merge แบบใช้ 3 ไฟล์ ได้ผลลัพธ์ออกมา 1 ไฟล์ โดยมี พ่อ, ลูก 1 และ ลูก 2 (น่าจะเข้าใจได้นะ)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace SimpleCalculator
{
public partial class FormMain : Form
{
public FormMain()
{
InitializeComponent();
}
private void uxSignInverse_Click(object sender, EventArgs e)
{
try
{
int val = Convert.ToInt32(uxInput.Text);
uxInput.Text = (-1*val).ToString();
}
catch (Exception ex)
{
MessageBox.Show("Something Wrong!");
}
}
private void uxSquareRoot_Click(object sender, EventArgs e)
{
try
{
int val = Convert.ToInt32(uxInput.Text);
// Find integer root
uxInput.Text = ((int)Math.Round(Math.Sqrt(val))).ToString();
}
catch (Exception ex)
{
MessageBox.Show("Something Wrong!");
}
}
private void uxSquare_Click(object sender, EventArgs e)
{
try
{
int val = Convert.ToInt32(uxInput.Text);
uxInput.Text = (val * val).ToString();
}
catch (Exception ex)
{
MessageBox.Show("Something Wrong!");
}
}
}
}
และอันนี้เป็น Revision Graph สุดท้าย จะเห็นได้อย่างนึงว่า มันบอกไม่ได้ว่ามีการ merge เข้าไปตรงไหน เพราะการ merge ก็เป็นแค่การ commit ธรรมดา (แต่เหมือนว่า Git จะไม่มีปัญหานี้เช่นกัน โอ้วว)
จบแล้วครับ series เผาเวลาว่างของผม :) ครั้งหน้าอาจจะเขียน Git (เมื่อถึงเวลาที่ Windows ใช้งาน Git ง่ายกว่านี้ และผมอยากใช้มันจริงๆ – เท่าที่รู้มันยังต้องรันผ่าน cygwin อยู่เลย)
แถมอีกนิด ทำไมต้อง Git ?
- ไม่ใช่ระบบรวมศูนย์แบบ SVN ดังนั้นไม่ต้องยุ่งกับ Network มาก เร็วปรื้ดดดด ลองเทียบความเร็วตอนใช้ SVN แล้วมี Repo เป็น Local กับตอนมีที่ Google Code ดู
- พอมันไม่มีการรวมศูนย์ มันก็จะกลายเป็นว่าต้องมีคนคอยไล่ Merge จากชาวบ้านชาวเมืองที่แตกหน่อไปแก้โค้ดกัน ดังนั้นไม่ต้องควบคุม Permission ว่าใครจะมายุ่งกับ repo กลางได้บ้าง แต่กลายเป็นว่าจะมีผู้ถูกแต่งตั้งเป็น “ผู้กุม version ล่าสุด” เป็นผู้ไล่ merge จากคนที่น่าสนใจแทน
- มีการ keep track ให้ว่าตอนแตก branch มันเป็น revision เท่าไหร่ ดังนั้นการ merge ทำได้ง่ายกว่า (ยังไม่ลองกับตัว และคงยังไม่ได้ลองเร็วๆนี้ -- ก็เค้าว่ากันว่าแบบนั้นนี่ >,<’)
- ขนาดรวมของ Repository เล็กกว่ามาก ได้ยินว่า repo ของ mozilla ใช้ SVN แล้วจะใหญ่ 14 GB แต่ถ้าใช้ Git จะใหญ่ 420 MB (ขอโทษที่ไม่ได้ cite ครับ แอบขี้เกียด)
ฟังดูแล้ว promising ไม่ใช่น้อย แต่ก็นั่นแหละ ติดปัญหาเรื่อง platform กับ GUI ซึ่งก็ไม่ได้น่าจะเป็นเรื่องที่แก้ยาก ดูกันต่อไป :)