Category Archives: Subversion Tutorial

วิธีการใช้ Subversion (SVN) – ตัวอย่างโปรเจค SimpleCalculator

จะเขียนอย่างย่อมากๆแต่เต็มไปด้วย Screen Shot เหมาะสำหรับคนใช้ TortoiseSVN เป็นมาบ้างแล้วเท่านั้นนะขอรับ (.  .’)

ขั้นที่ 1 เราจะสร้างเครื่องคิดเลขโง่ๆกันครับ สามารถทำการ

  • ยกกำลังสอง (Square)
  • ถอดราก (Sqrt) และ
  • กลับเครื่องหมาย (Inverse) ได้

หน้าตาออกมาประมาณนี้ และเราก็จะทำการ Implement ฟังก์ชันอันหนึ่งไปก่อน ส่วนอีกสองอันที่เหลือจะแจกให้ Dev คนอื่นทำ

Design UI

 

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

Create Sqr Branch

ขั้นที่ 4 Dev ทั้งสองทำงานบน Branch ของตัวเอง โดย check out SimpleCalculator/brances/square (หรือ squareroot) แล้วเพิ่มโค้ดส่วนที่เป็นการหา Square และ Square Root แล้ว commit กลับเข้าไป

ถ้ามาดูหน้าตาของ Revision Graph จะได้ประมาณนี้

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 ตามรูป

Merge with Square

พวกปุ่มทั้งหลาย ตั้งแต่ 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 แก้ไขนะครับ 🙂

Resolve Conflict

หลังจาก 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 จะไม่มีปัญหานี้เช่นกัน โอ้วว)

Final Revision Graph

จบแล้วครับ 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 ซึ่งก็ไม่ได้น่าจะเป็นเรื่องที่แก้ยาก ดูกันต่อไป 🙂

วิธีการใช้ Subversion (SVN) – Subversion กับ Visual Studio

โพสต์นี้ไม่ค่อยมีอะไรหรอกครับ แค่จะบอกว่า ถ้าจะใช้กับ Visual Studio มันมีบางโฟลเดอร์ที่ไม่ควรใส่เข้าไปในตัวซอฟแวร์ Version Control ด้วย อันได้แก่

    1. ไฟล์ .csproject.user และ ไฟล์ .suo ไฟล์พวกนี้มันเก็บ configuration ของแต่ละ user ครับ ดังนั้นไม่ต้องใส่เข้าไปด้วย เพราะมันจะเปลี่ยนทุกครั้งที่บันทึก project
    2. โฟลเดอร์ bin อันนี้เป็นผลจากการ build ไม่ต้องใส่เข้าไปเช่นกัน เพราะเดี๋ยวก็ต้อง build ใหม่อยู่ดี
    3. โฟลเดอร์ obj ไม่ต้องใส่เช่นกันครับ

After First Commit

บางคนอาจจะชอบเอาไฟล์ Input บางอย่างไปโปะไว้ใน bin/Debug หรือ bin/Release แบบนี้จะทำให้ไฟล์พวกนี้ไม่เข้าไปใน VC ด้วย ถ้าเป็นกรณีนี้ลองใช้วิธีเอาไฟล์ดังกล่าวใส่เข้าไปใน project แล้วตั้ง property ชื่อ “Copy to Output Directory” ให้เป็น Copy if newer แทน จะเป็น practice ที่ดีกว่า

จบแล้ว ง่ายจัง 🙂

วิธีการใช้ Subversion (SVN) – Branch และ Merge ใน Subversion

มาต่อจากตอนที่แล้วด้วยเรื่องของ Branch และ Merge ครับ เรื่องนี้เป็นหัวใจของระบบ Version Control เลยนะเนี่ย

การจัด Repository

มาพูดถึงการจัด Repository ก่อนครับ โดยปกติแล้วเค้าจะจัดกันแบบนี้

RepoRoot
— ProjectA
— — trunk
— —
branches
— — — branchA
— — — branchB
— — tags
— —  — 1.0.0
— —  — 1.1.5
— ProjectB

ถ้าใช้ TortoiseSVN อยู่ คำว่า “จัด” ที่ผมใช้นีหมายถึงให้เปิด Repo Browser ขึ้นมาแล้วสร้าง Structure ตามตัวหนาด้านบนนะครับ

ส่วนที่สำคัญคือ trunk, branches, และ tags ซึ่งจริงๆแล้วชื่อพวกนี้ไม่ได้เป็นข้อบังคับหรือว่าอะไรเลย ไม่ได้มีความหมายพิเศษต่อการทำงานของ SVN ด้วย แต่ว่าเป็นอะไรที่คนส่วนใหญ่เค้าทำกัน แต่ SVN ก็จะมองเป็นโฟลเดอร์ธรรมดาๆ ดังนั้นเราจึงควรทำอะไรตามคนส่วนใหญ่ จะได้สื่อสารกันได้ง่ายๆ 🙂

คำว่า trunk และ branches มาจากส่วนประกอบของต้นไม้ คือ ลำต้น และ กิ่ง โดยลำต้น หรือ trunk ก็จะหมายถึงสายการพัฒนากลางหรือสายพัฒนาหลัก และเวอร์ชันล่าสุดของโปรเจกก็มักจะอยู่ที่นี่ ถ้าอยากได้ซอร์สโค้ดของโปรเจคก็ควรจะ check out จากที่นี่ครับ และ trunk ก็จะถูกคาดหวังว่าจะสามารถนำไป build และ compile เพื่อใช้งานได้

ส่วน branch เป็นกิ่งก้านสาขาที่แตกออกมา (ในกรณีปกติก็คงแตกออกมาจาก trunk นั่นแหละ) อาจจะแตกออกมาเพื่อให้คนๆหนึ่งไปรับผิดชอบ feature หนึ่งๆ เพราะเกรงว่าการแก้ไขโดยคนนั้นๆทำไปทำมาจะทำให้ trunk เจ๊ง (คอมไพล์ไม่ผ่าน) ก็เลยให้ไปทำใน branch ซะ แน่ใจว่าเสร็จแล้วค่อยเอามารวม (merge)

และ tag อันนี้แปลง่ายๆคือมันเป็น snapshot (ง่ายตรงไหนวะ …) คือเหมือนเป็นการบันทึก state ณ เวลาหนึ่งๆไว้นั่นเอง มักถูกใช้ในการเก็บเวอร์ชันต่างๆไว้ เป็นต้นว่า 1.0.0 ตอน release เวอร์ชัน 1.0.0 เป็นต้น ถ้าทำไปเรื่อยๆจนถึงเวอร์ชัน 2.1 แล้ว อยากดูโค้ดตอนที่ release 1.0.0 ว่าเป็นยังไงก็เปิด tags/1.0.0 ได้

ทั้งการทำ branch และ tag นั้น เป็นเพียงการ copy trunk ไปใส่ไว้ใน branches และ tags เท่านั้น ซึ่งการ copy ผ่าน SVN มันก็จะเป็นแค่การคัดลอก reference มา ดังนั้นจึงทำได้เร็วมาก และหายห่วงเรื่องไฟล์ซ้ำซ้อนได้เลย

เรื่องที่อาจจะงงอีกเรื่องคือ แล้วตอน check out ตัว ProjectA ออกมา มันจะมาทั้งยวงคือ trunk, tags, branches เลยรึเปล่า คำตอบคือ ปกติตอน check out ก็ต้องเลือกเอาว่าจะ check out ตัวไหนมาอ่ะครับ อาจจะเป็น ProjectA/trunk หรือ ProjectA/branches/gantbranch อะไรแบบนี้ก็ได้ จะได้ไม่มากันทั้งครอบครัว

Tree

(ผมวาดเอง เหมือนจะอาร์ต แต่ไม่อาร์ตแฮะ – -‘)

รูปแบบการทำงานร่วมกันบน Subversion

เท่าที่อ่านมามี 3 แบบ

    1. ไม่ต้องแตก branch ทำบน trunk อย่างเดียว อันนี้ก็อาจจะมีปัญหาในกรณีที่แก้ไขโดยคนใดคนหนึ่งแล้วมันเจ๊ง ทำให้ trunk ไม่เสถียรเท่าไหร่
    2. แตก branch เป็นกรณีพิเศษ แต่งานส่วนใหญ่ยังทำกันบน trunk ในกรณีที่ต้องแก้ไขอะไรยิ่งใหญ่มากจนอาจทำให้ trunk ไม่เสถียร (คอมไพล์ไม่ผ่าน รันเทสต์แล้วเน่า)  เสถียรมากกว่าแบบแรก
    3. แตก branch ให้หมด ส่วน trunk เหมือนเป็นที่ share ไฟล์ระหว่าง developer เท่านั้น ใครทำอะไรใหม่มาก็เอามา merge เข้า trunk แล้วก็มีการ sync branch ของตัวเองกับ trunk เป็นระยะๆ โดยการ merge จาก trunk เข้ามาที่ branch แล้วก็ต้องทำการทดสอบด้วย (อาจจะคอมไพล์ และรันเทสต์)  เป็นแบบที่เสถียรสุด

ผมคิดว่าบลอกนี้คงนำเสนอแบบที่ 3 เป็นหลัก เพราะครอบคลุมที่สุด แบบที่ 1 ก็น่าจะทำกันเป็นทั้งแต่ update และ commit เป็นแล้ว 🙂

การ Branch หรือการแตกกิ่ง

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

Release Branch จะเก็บไว้เรื่อยๆเพื่อทำการ maintenance ครับ งานประเภทแก้ไขบัก เพิ่ม feature น้อยๆ ทำให้มีเวอร์ชันพวก 1.1 1.1.5 1.2 ออกมาในขณะที่เวอร์ชัน 2.0 ก็ออกมาแล้ว ส่วน Feature Branch หลัง merge แล้วก็อาจจะลบทิ้งไปได้เลย

การ Merge หรือการผสาน

ปกติการ merge มันหมายถึงการรวมไฟล์สองไฟล์เข้าด้วยกัน ซึ่งก็มักจะเป็นไฟล์ Text และคำที่มักจะมาคู่กันเสมอๆคือคำว่า diff ซึ่งแปลว่าส่วนต่างระหว่างไฟล์สองไฟล์ ซึ่งการ merge ในกรณีที่มีการแก้ไขเพิ่มเติมหรือลบออกจากไฟล์ต้นฉบับโดยคนคนเดียว มันก็ไม่ได้เป็นปัญหาอะไรมากมาย แต่กรณีที่มักจะเป็นปัญหาคือการแก้ไขโดยคนมากกว่า 1 คน ก็ต้องใช้ความสามารถมนุษย์เข้ามาช่วยในการ merge

แต่การ merge ที่ใช้กับ branch มันจะกระทำกับไฟล์หลายๆไฟล์พร้อมๆกัน และก็อาจเกิดกรณีที่เรียกว่า Conflict ขึ้นได้ด้วย (อ่านได้ในแนวคิดพื้นฐาน) ซึ่งการ merge การแก้ไขของ branch กลับเข้าไปที่ trunk มักทำตามขั้นตอนต่อไปนี้ครับ

    1. ทำการ sync branch ของเรากับ trunk ก่อน เป็นการนำการแก้ไขตั้งแต่เราแตก branch ออกมา จนถึง trunk ปัจจุบันเข้ามารวมกับ branch ของเรา สมมติว่าเราแตก branch มาตอน revision 11 แต่ตอนนี้ HEAD revision เป็น revision 23 เอาก็ต้องเอาผลต่างระหว่าง trunk ที่ rev HEAD กับ trunk ที่ rev 11 มารวมกับ branch ของเราก่อน แล้วทำการ merge เข้าไปที่ branch ทำเสร็จเรียบร้อยก็ทดสอบให้ดี แล้ว commit 1 ครั้ง แล้วอย่าลืมใส่ log message เข้าไปด้วยว่า sync กับ trunk ที่ revision เท่าไหร่ (ในกรณีนี้เป็น 23) ทำเสร็จแล้วจะเกิดการแก้ไขขึ้นที่ branch ของเราเท่านั้น โดย trunk ยังคงเหมือนเดิม
    2. ทำการ sync trunk เข้ากับ branch ของเรา ทำได้ไม่ยาก โดยการหาผลต่างระหว่าง branch ของเราที่ rev HEAD กับ trunk ที่ rev HEAD แล้วทำการ merge เข้าไปที่ trunk ทดสอบให้เรียบร้อย แล้วทำการ commit อย่าลืม log message ไว้ให้ดีด้วย ว่า merge กับ branch อะไรยังไง ทำเสร็จแล้วเกิดการแก้ไขที่ trunk โดย branch เหมือนเดิม
    3. ลบ branch ทิ้ง เพราะถ้าเป็นพวก Feature Branch ก็คงไม่ต้องใช้ทำอะไรอีกแล้ว

Tool ดีๆแบบพวก TortoiseSVN จะทำให้เรื่องพวกนี้ทำได้ไม่ยากมากครับ อยู่ในเมนูส่วน Branch/Tag และ Merge ถ้ามีปัญหาก็ Revert กลับไปได้เสมอ รวมถึงการทำ Dry-run ก็สามารถดูผลลัพธ์ก่อนที่จะทำการปฎิบัติการจริงๆได้ และยังมี Diff ช่วยในการดูความแตกต่างของแต่ละไฟล์ที่จะทำการ merge กันได้อีกด้วย

ความวุ่นวายจะเกิดตอนจะรวม Feature Branch หลายๆอันเข้าไปใน trunk มากกว่า ต้องทำการ sync กับ trunk ก่อนทุกครั้ง คงเหนื่อยเอาเรื่อง

สิ่งที่จะมีปัญหาคือการระบุ FROM และ TO ในหน้าต่าง Merge มันอาจจะดูงงๆ เช่น ถ้าเราต้องการจะเอา branch ของเราเข้าไปรวมใน trunk (sync trunk เข้ากับ branch) มันก็น่าจะเป็น FROM branch TO trunk หรืออะไรทำนองนั้น แล้วทำไมมันถึงต้องใช้ FROM trunk TO branch เล่า ? ฟังดูงงยิ่งนัก

วิธีเข้าใจก็คือ ให้มองการใช้ FROM กับ TO เป็นการหาผลต่างครับ เราต้องการหาผลต่างระหว่าง trunk ไปหา branch เพื่อที่จะเอาไปรวมกับ trunk โดยที่ FROM จะใส่อันที่เก่ากว่า และ TO ใส่อันที่ใหม่กว่า พูดแบบนี้เข้าใจมากกว่าครับ และจะช่วยให้เข้าใจในกรณีของการ sync branch เข้ากับ trunk ด้วย ว่าทำไมทั้ง FROM ทั้ง TO ถึงต้องใส่ trunk ทั้งคู่เลย

Merge with Square

(ตัวอย่างหน้าต่าง merge ใน TSVN)

อ่านต่อ

ส่วนใหญ่ก็อ่านมาจาก Manual ของ SVN ลองไปอ่านดู

ตอนต่อไป

จะเอา project SimpleCalculator ที่ลองแตก feature branch ไปสองอัน และลองเล่น merge มาให้ดูครับ (screen shot พร้อมแล้ว) อันนี้จริงๆก็ไม่ได้ว่างมาก แต่อยากมีประสบการณ์ด้านนี้ซักหน่อย เพราะงานที่ทำอยู่คงไม่ได้ใช้ Dev Tool เจ๋งๆพวกนี้ไปซักพักเลยแฮะ ^ ^’

วิธีการใช้ Subversion (SVN) – แนวคิดพื้นฐาน

ปัญหาอย่างหนึ่งที่ทำให้ผมสนใจเรื่อง Software Engineering คือจะทำยังไงให้คนหลายๆคนทำงานด้วยกันได้ครับ ประเด็นหนึ่งที่น่าสนใจเกี่ยวกับปัญหานี้ก็เป็นเรื่องการจัดการ "สถานะ" ของระบบนี่แหละ (ราชบัณฑิตแปล Configuration ว่า "โครงแบบ" อ่านไม่รู้เรื่องเลยแฮะ) หรือถ้าพูดภาษาชาวบ้านมันก็คือ Software Configuration Management (SCM) นั่นเอง

Subversion (หรือเรียกย่อว่า SVN) เป็น Tool อันหนึ่งในกลุ่ม Version Control ครับ ซึ่งก็มีหลายตัวตั้งแต่ CVS, Team Foundation Server, Git, Bazaar แต่ SVN ดูเหมือนจะเป็นที่นิยมที่สุด ส่วนหนึ่งเป็นเพราะผู้สร้าง SVN เป็นคนเดียวกับผู้สร้าง CVS ที่เคยโด่งดังและใช้กันแพร่หลายมากมาก่อนครับ แต่ด้วย Architecture ของ CVS มันทำให้ไม่สามารถเพิ่ม Functionality เจ๋งๆบางอย่างเข้าไปได้ เลยตั้งโปรเจคใหม่คือ SVN ซะเลย

SubversionLogo

วิธีการเข้าใจ Subversion ในระดับพื้นฐานนั้นไม่ยากครับ

เรื่องของ Repository

Repository

ก่อนอื่นขอให้เข้าใจ Concept เรื่องของ Repository ก่อน ตัว Repo นี่จะเป็นเหมือนศูนย์กลางในการเก็บข้อมูลซึ่งทุกคนจะใช้ร่วมกันครับ ดังนั้น หากมีการแก้ไขข้อมูลใน Repo โดยสมาชิกคนหนึ่ง คนอื่นก็จะเห็นการแก้ไขนั้นๆด้วย ถ้าพูดให้ง่ายๆก็คือมันเป็น Shared Folder เหมือนที่เราเห็นใน Network นั่นเอง

อย่างไรก็ตามตัว Repo นี่มันมีความสามารถกว่านั้นมาก เพราะมันจะเก็บ "สถานะ" เก่าๆย้อนหลังไว้ได้ด้วย ดังนั้นหากมีสมาชิกเผลอลบไฟล์บางไฟล์ออกไปจาก Repo ก็ยังสามารถกู้กลับมาได้โดยง่ายโดยการย้อนไป "สถานะ" ก่อนหน้า … เจ๋งใช่มั้ยล่ะ

สถานะแต่ละสถานะก็จะมีหมายเลขกำกับด้วย ซึ่งเราจะเรียกว่า Revision Number ซึ่งทำการแก้ไขเข้าไปหลายๆครั้ง Revision Number ก็จะยิ่งเพิ่มมากขึ้น และเราสามารถดึงข้อมูลจาก Revision ใดๆใน Repo ก็ได้ Revision ล่าสุดใน Repo จะมีชื่อเรียกพิเศษเรียกว่า Head Revision ด้วยครับ

คำสั่งพื้นฐาน: Checkout

ขั้นแรกก่อนสิ่งอื่นใด เราต้องทำการดึงข้อมูลที่เราต้องการออกมาจาก Repository ก่อน ข้อมูลนี่จริงๆแล้วมันก็มักจะเป็น Source Code และ Project File ทั้งหลายนั่นแหละครับ กระบวนการนี้เรียกว่าการ Checkout ซึ่งเราต้องระบุตำแหน่ง Repository และ Path ใน Repository ที่เราต้องการ Checkout ออกมา หลังจาก Checkout ออกมาแล้ว ก็จะได้เป็น Copy อยู่บนเครื่องของเรา อยากแก้ไขทำอะไรก็ได้

อาจจะสงสัยเรื่อง Path ใน Repo ว่าทำไมต้องระบุด้วย ? สาเหตุก็เพราะจริงๆแล้ว Repo อันเดียวอาจเก็บโปรเจคไว้หลายๆโปรเจค และเราอาจจะต้องการจัดการแค่โปรเจคเดียว หรือเราอาจจะต้องการทำงานบน "กิ่ง" (Branch) อันเดียวก็ได้ (ไว้ค่อยพูดต่อไปว่าคืออะไร) ตัว Repo ของเราเองก็สามารถจัดเป็นโครงสร้างต้นไม้ได้เหมือน Directory ทั่วๆไป การใช้ Path จึงเป็นประโยชน์มากครับ และก่อให้เกิด "รูปแบบการจัดวาง" (Layout) มาตรฐานของ Repository ขึ้นมา เช่นโฟลเดอร์ trunk, branches, tags และอื่นๆ ไว้ว่ากันทีหลัง

คำสั่งพื้นฐาน: Commit

หลังจากที่ Checkout ออกมาแล้ว หากเราทำการแก้ไขไฟล์บางไฟล์ และต้องการส่งผลการแก้ไขของเรากลับไปที่ Repo ก็สามารถทำได้โดยการ Commit ครับ สิ่งที่เราแก้ไขก็จะถูกส่งไปที่ Repository ทันที หากมีสมาชิกคนอื่นมา Checkout หลังจากนี้ ก็ย่อมได้รุ่นที่เราแก้ไขไปแล้วด้วยเช่นกัน

คำสั่งพื้นฐาน: Update

ถ้ามีคนแก้ไขไฟล์อื่นแล้ว Commit กลับเข้าไปใน Repo การแก้ไขนั้นย่อมยังไม่ส่งผลใดๆกลับมาที่ Copy ที่อยู่บนเครื่องทำงานของเราใช่มั้ยครับ ดังนั้นเราต้องใช้คำสั่ง Update เพื่อให้ผลการแก้ไขนั้นลงมาบนเครื่องเราด้วย

คำสั่งพื้นฐาน: Add, Delete

ในความเป็นจริงเราไม่ได้ทำการแก้ไขไฟล์อย่างเดียว เรามีการ เพิ่ม ลบ ไฟล์เข้าไปอยู่เสมอ เช่นตอนสร้าง Class ใหม่ เป็นต้น การเพิ่มไฟล์เข้าไปทำได้โดยใช้คำสั่ง Add และในทางกลับกัน การลบไฟล์ออกใช้คำสั่ง Delete ครับ

ปัญหาที่เกิดขึ้น

SharingProblem

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

    1. นาย ก Update
    2. นาย ข Update
    3. นาย ก แก้ Gant.cs
    4. นาย ข แก้ Gant.cs
    5. นาย ก Commit
    6. นาย ข Commit

การทำงานในขั้นตอนแบบด้านบนจะไม่เกิดขึ้นเพราะ Subversion จะไม่ยอมให้ นาย ข Commit ในขั้นตอนสุดท้ายครับ โดยจะเกิดสิ่งที่เรียกว่า out-of-date error แทน

เหตุการณ์นี้เรียกว่า Conflict ครับ และวิธีการแก้โดยปกติก็คือ ต้องให้สมาชิกที่เป็นมนุษย์ทำการ Review เอง โดยการทำการเปรียบเทียบความแตกต่างระหว่างไฟล์ แล้วทำการสร้าง Version ใหม่ที่เป็นการรวม (Merge) ระหว่างสองไฟล์ขึ้น ก่อนที่จะทำการ Commit กลับเข้าไปครับ โดยจะมี Tool มากมายคอยช่วยเหลือเช่น Diff ใช้ดูความแตกต่างระหว่างไฟล์สองไฟล์แบบ "ฉลาด" ไม่ใช่การเทียบบรรทัดต่อบรรทัดธรรมดาครับ และใช้ได้ดีกับไฟล์ Source Code หรือ Text File เท่านั้น งาน Merge นี้เป็นงานที่คอมพิวเตอร์ทำเองไม่ได้ครับ เดี๋ยวทำมาแล้วไม่ถูกใจ แย่เลย 

สรุป

ผมพยายามเขียนโพสต์นี้ให้มัน Generic มากๆเพราะอยากเน้น Concept แต่ตอนต่อๆไปจะลองเขียนให้มันเฉพาะมากขึ้น เช่น TortoiseSVN กับ Visual Studio แต่ ไม่รู้จะเข้ามาเขียนเมื่อไหร่น่ะ o__o