วันอังคารที่ 3 กรกฎาคม พ.ศ. 2555

Serialization และ Deserialization

 เป็นหัวข้อน่ารู้อีกอย่างนึงครับ สำหรับเรื่องของ Serialization และ Deserialization
ตามนิยาม (ในวิกิพีเดีย) เค้าบอกไว้ว่า
Serilizationคือกระบวนการแปลงวัตถุให้กลายเป็นสายข้อมูลในรูปแบบบิตทำให้สามารถเก็บรักษาวัตถุดังกล่าวไว้บนสื่อเก็บข้อมูล (เช่น HDD)หรือจะเอาไปส่งผ่าน Network ก็ได้
Deserilization คือกระบวนการย้อนกลับของ Serialization คือการแปลงจากสายข้อมูลในรูปแบบบิตให้กลับมาเป็นวัตถุของเรานั่นเอง

จากรูป วัตถุ "ปลาดิบ" จะถูกแปลงเป็นข้อมูลในรูปแบบ Binaryเพื่อเก็บไว้บนดิสค์ครับและหากต้องการนำมาใช้ใหม่ก็แค่โหลดข้อมูลขึ้นมาทำการ Deserialzation
จริงๆแล้วขั้นตอนการเก็บรักษา "สถานะ" ของวัตถุตามที่เห็นในรูปมันก็สามารถทำมือได้เช่นกัน โดยอาจจะเขียนข้อมูลต่างๆใส่ Text Fileแล้วเขียนลอจิกที่ทำหน้าที่โหลดข้อมูลดังกล่าวขึ้นมาเองแต่ตามความเข้าใจของผมวิธีการนี้ก็เป็นวิธีการหนึ่งในการเก็บรักษาสภาพของวัตถุที่สะดวกดีครับเขียนเพิ่มนิดหน่อยก็ทำ Serialization ได้เลย
วิธีการทำใน C# มีขั้นตอนดังต่อไปนี้ ยกตัวอย่างจากคลาสที่เขียนในวันนี้
  1. เพิ่ม Attribute Serializable เข้าไปที่หัว Class ก่อน
  2. ให้ Class implements ISerializable ต้องเขียน method GetObjectData เพิ่ม
  3. ทำ Deserialization Constructor ที่มีรูปแบบตามที่กำหนด
  4. เพิ่มลอจิกส่วนที่เป็นการทำ Serialize และ Deserialize
ดูโค้ดดีกว่า
ส่วนของ ToBinaryFile และ FromBinaryFile เป็นส่วนของข้อ 4 นะครับ
เวลาจะทำ Serialize ก็เรียก ToBinaryFile("filename.osl") และเวลาจะ Deserialize ก็เรียก Word.FromBinaryFile("filename.osl")

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace WordParser
{
    [Serializable()]
    class Word : ISerializable 
    {
        public string Value { get; set; }
        public string Description { get; set; }
        public int Popularity { get; set; }

        public Word()
        {

        }
        
        public Word(SerializationInfo info, StreamingContext context)
        {
            Value = (string)info.GetValue("Value", typeof(string));
            Popularity = (int)info.GetValue("Popularity", typeof(int));
            Description = (string)info.GetValue("Description", typeof(string));
        }

        #region ISerializable Members

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Value", Value);
            info.AddValue("Popularity", Popularity);
            info.AddValue("Description", Description);
        }

        #endregion

        public static Word FromBinaryFile(string file)
        {
            using (Stream stream = File.Open(file, FileMode.Open))
            {
                BinaryFormatter bformatter = new BinaryFormatter();
                Word ret = (Word)bformatter.Deserialize(stream);
                return ret;
            }

            
        }

        public void ToBinaryFile(string file)
        {
            using (Stream stream = File.Open(file, FileMode.Create))
            {
                BinaryFormatter bformatter = new BinaryFormatter();
                bformatter.Serialize(stream, this);
            }

        }

    }
}  


Credit: DevStock

เปรียบเทียบ VB.NET VS. C#

"... เปรียบเทียบ C#.net กับ vb.net
การเลือกระหว่างภาษา C#.Net และ VB.Net มักเป็นข้อที่หลายคนคิดในช่วงวางแผนก่อนที่จะเริ่ม ลงทุนลงแรงเพื่อศึกษาและนำมาใช้ในการพัฒนาระบบขึ้นมา

ข้อเสนอแนะอย่างง่ายที่สุดของไมโครซอฟต์ ก็คือว่าเรามีความชำนาญ มีความคุ้นเคย ชอบพอกับภาษาใดมาก่อน ก็ให้เลือกภาษาที่เป็นแนวเดียวกันนั้น เนื่องจากทุกภาษาใน .Net จะถูกแปลเป็นภาษา Microsoft Intermediate Language (MSIL) โดยเฉพาะสองภาษา ที่จะเปรียบเทียบกันนี้ ถ้าเขียนโค้ด VB.Net อย่างถูกต้องดี รหัส MSIL ที่ได้ ก็แทบจะเหมือนกันเลย ดังนั้น performance ของงานที่ได้ ก็จะไม่แตกต่างกัน นี่เป็นการยกระดับงาน Visual Basic เป็นอย่างมาก แล้วการที่ VB.Net ใช้ Base Class Library ของ .Net framework ร่วมกันกับ C# ก็ทำให้ VB.Net programmer ถือเป็น first-class citizen ไปแล้ว

การเขียนโค้ด VB.Net อย่างถูกต้องดี เช่นมีการใช้ Option Strict Onหรือมีการใช้ DirectCast แทนที่จะใช้ CType เป็นต้น.

รายละเอียดการเปรียบเทียบสองภาษา ในข้อความนี้ รวบรวมประมวลมาจากแหล่งข้อมูลต่างๆ หลายแห่ง ซึ่งอาจจะต้องมีการพูดถึงศัพท์หรือเทคนิคลึกๆ บ้างเพื่อให้ได้ความสมบูรณ์นะครับ

ดูข้อดีของ Visual Basic .Net
- รองรับ Optional argument ซึ่งสำคัญมากที่คุณต้องการใช้งานร่วมกับ ActiveX component หรือการเขียนโค้ดชนกับพวก Office
- ทำตัวไม่ซีเรียสได้ คือยอมรับการทำ late-binding ได้ ถ้าไม่กำหนด Option Strict On การเขียนโค้ดพวกนี้ใช้กับพวก ActiveX อีกนั่นเอง (ผมแนะนำให้หลีกเลี่ยงการเขียนโค้ดแบบ late-binding ใน .Net)
- รองรับการทำ named indexer (การสร้าง property ที่มี argument)
- มีคำสั่ง VB แบบเดิมๆ เช่น Left, Mid, UCase, ... ให้ใช้ง่ายๆ สำหรับผู้ใช้ VB6 มาก่อน (การเรียกใช้ฟังก์ชันแบบเดิมๆ นี้จะมีผลต่อประสิทธิภาพของโปรแกรม)
- มีประโยค With..End With ให้ใช้
- ความเรียบง่าย เช่นการสร้างประโยค Event
- สามารถกำหนดชื่อเมธอดของการ implements interface ที่ต่างจากที่กำหนดไว้ใน interface ได้ (ผมว่าไม่ค่อยได้ประโยชน์เลย ทำให้ยุ่งยากในการค้นหาเสียมากกว่า)
- มีประโยค Catch...When... ทำให้สามารถทำการ filter exception ด้วยเงื่อนไขได้ นอกเหนือจากการ filter ด้วยชนิดของ exception เท่านั้น
- Visual Studio .Net จะทำการ compile โค้ดในลักษณะ background ซึ่งช่วยเป็นข้อดีในโปรเจ็กต์ขนาดเล็ก แต่ถ้าโปรเจ็กต์ขนาดใหญ่มหึมา จะกลับเป็นข้อเสียอย่างมาก (มีฝรั่งหลายคนบ่นว่าต้องถึงกับต้องยอมเปลี่ยนจาก VB.Net มาเป็น C# เลย ในโปรเจ็กต์ที่มีไฟล์มีคลาสเป็นพันๆ)

ข้อดีของ C# .Net
- รองรับ XML documentation คล้ายๆ javadoc คือเอาคอมเม้นต์ในโค้ดมาแปลงเป็นเอกสาร technical manual ได้เลย แต่ใน VB.Net เวอร์ชั่น 2005 (Whidbey) ก็จะรองรับในคุณสมบัตินี้ด้วย
- สามารถทำ operator overloading ได้ (VB.Net 2005 ก็จะทำได้เช่นกัน)
- รองรับ unsigned datatype (VB.Net 2005 ก็จะทำได้เช่นกัน)
- มีประโยค using เพื่อใช้จัดการกับ resource ที่เป็นแบบ unmanaged
- รองรับ unsafe code

สังเกตได้ว่า อะไรที่ใน C# มี ซึ่งเป็นสิ่งที่ซับซ้อน ใน VB.Net เวอร์ชันถัดไป ก็จะมีด้วย แต่มักจะถูกนำมาแสดงในรูปแบบที่ง่ายต่อความเข้าใจ แต่อะไรที่ VB.Net มี มักจะไม่ถูกนำไปเพิ่มให้กับ C# เช่นรูปแบบการสร้าง event ที่เรียบง่าย อย่างประโยค Handles หรือคีย์เวิร์ด My ที่จะมีใน VB.Net Whidbey (คิดว่าใน C# อาจจะไม่มี) หรือ Optional argument (ใน C# แก้ปัญหานี้ด้วยการทำ overloading แต่ก็จะไม่สามารถใช้งานร่วมกับ ActiveX component แบบเดิมได้อยู่ดี)

ถ้าขนาดของโปรเจ็กต์ที่คุณคิดว่าจะต้องทำในอนาคตใหญ่มากๆ ก็ควรจะเลือก C# ไปเสียแต่แรกเลย เว้นแต่จะมีวิธีแก้ปัญหา IDE ที่ช้ามากๆ เมื่อมีไฟล์จำนวนมากของ VB.Net ได้

เขาว่าภาษา VB.Net เป็น 4GL (ภาษา Generation ที่สี่) แต่ภาษา C# ถูกวางเป็น 3GL กว่าๆ คือสูงกว่า 3GL เช่น C++ แต่ไม่ถึง 4GL

ถ้าเอาความอย่างที่ Microsoft วาง position ไว้
ภาษา VB .Net เป็น task-oriented
ภาษา C# .Net เป็น code-focused

ถ้าเอามาสร้าง application ทางธุรกิจ ภาษา VB.Net จะเป็นตัวเลือกที่ยอดเยี่ยมอย่างปฏิเสธไม่ได้ เนื่องจากแทนที่โปรแกรมเมอร์จะไม่ต้องคำนึงถึงเรื่องลึกๆ ที่เรียนกันในวิชาของพวก computer science กันบ่อยๆจึงเรียกว่ามี productivity ดี เพราะเอาเวลามาสร้างงานเลย

แต่นั่นก็เป็นอีกเหตุผลหนึ่งว่า ถ้าใช้ C# ศัพท์แสงในเนื้อภาษาจะเป็นศัพท์แบบที่ใช้ในวงการ computer science เช่น abstract, static เป็นต้น ซึ่งจะจำเป็นถ้าต้องมีการใช้เครื่องมืออย่างพวก Rational Rose หรือ Modeling tools อื่นๆ


สิ่งที่รวบรวมมารวมกับความคิดเห็นเพิ่มเติมบางอย่างนี้เข้าไปมีทั้งด้าน รูปแบบภาษา การใช้งานจริง และแฟกเตอร์อื่นๆ อาจจะมีข้อผิดพลาดบ้าง ถ้ามีก็ช่วยบอกเพื่อปรับปรุงเพิ่มเติมต่อไปนะครับ...


Credit: DevStock

State ใน Asp.Net

  1. State Management นั้น แบ่งออกเป็น 2 กลุ่มใหญ่ๆครับ คือ Client-side ได้แก่
    - Cookies
    - Hidden Field
    - View State
    - Query Strings
    - Control state

    และ Server-side ได้แก่
    - Aplication object
    - Session object
    - Database
    - Transfer method

Cookies
นักพัฒนาเว็บอาจจะรู้จักเจ้าขนมหวานชิ้นนี้มานานแล้วซึ่งเป็นวิธีการส่งค่าแบบ Client-sideโดยจะเอาค่าที่ต้องการไปเก็บอยู่ที่เครื่องของ client ที่รัน browser เลยและค่าของ Cookies จะถูกส่งมาที่ server ทุกครั้ง เมื่อมีการเรียกapplication ใน domain แม้การใช้งาน Cookies จะมีมานานแล้วแต่ก็ยังเป็นที่นิยมอยู่ในปัจจุบัน

ข้อดี คือ ใช้ง่ายครับ และเนื่องจากมันเป็นการเก็บข้อมูลแบบ Client-side ดังนั้น Server จึงไม่ต้องรับภาระตรงนี้ให้เหนื่อยเปล่า

แต่ข้อเสีย คือ เนื่องจากข้อมูลที่เก็บมันอยู่ที่เครื่อง Clientดังนั้นผู้ใช้ฝั่ง Client จึงสามารถแก้ไขดัดแปลงข้อมูลใน Cookiesได้ตลอดเวลา ข้อมูลที่ควรเก็บใน Cookiesจึงไม่ควรเป็นข้อมูลที่สำคัญมากด้วยเหตุผลทางด้านความปลอดภัยครับ

จุดอ่อนอีกอย่างหนึ่งคือ Cookies ไม่สามารถเก็บข้อมูลขนาดใหญ่ได้ โดยที่browser ของแต่ละเจ้าก็มีการจำกัดจำนวน Cookiesในแต่ละโดเมนไม่เท่ากันด้วย เช่น IE6.0 เก็บได้สงสุด 20 Cookiesต่อหนึ่งโดเมน (ค่านี้เปลี่ยนแปลงได้ขึ้นอยู่กับแต่ละเวอร์ชัน)และประการที่สำคัญที่สุดคือเราจะไม่สามารถใช้ Cookies ได้หาก browser ฝั่งclient มีการบล็อคการใช้งาน Cookies เหตุเพราะในอดีต Cookiesถือเป็นอาวุธสำคัญอีกอย่างของบรรดา hacker ทั้งหลายดังนั้นผู้ใช้บางคนจึงไม่อยากเปิดใช้งาน Cookies บน browser สักเท่าไหร่

อย่างไรก็ดีเราสามารถสั่งให้ Cookies ส่งค่าผ่าน SSL (HTTPS) เพียงอย่างเดียวเท่านั้นได้ หากต้องการความปลอดภัยในการใช้งานที่มากขึ้น
ข้อแนะนำอีกประการหนึ่งคือ เราควรเข้ารหัสข้อมูลที่เก็บใน Cookies ด้วยเพื่อป้องกันการเปลี่ยนแปลงข้อมูลโดยตรงจากผู้ใช้

วิธีใช้แสนง่าย
Code:
//รับค่าจาก 
	Cookiesstring value = Request.Cookies["MyValue"].Value;
//กำหนดค่าใน 
	CookiesResponse.Cookies["MyValue"].Value = "keep value";
//ตรวจสอบวันหมดอายุ
	DateTime expire = Request.Cookies["MyValue"].Expires;
//กำหนดวันหมดอายุอีก 30 วันข้างหน้า
	Response.Cookies["MyValue"].Expires = DateTime.Now.AddDays(30);
//กำหนดให้หมดอายุทันที
	Response.Cookies["MyValue"].Expires = DateTime.Now.AddDays(-1);  


Hidden Field เป็น Html Control ตัวหนึ่งนะครับสามารถเซตค่าได้แต่จะไม่ถูก render ที่ browser เท่านั้นเอง
คิดง่ายๆ ก็เหมือน TextBox ที่มองไม่เห็นนั่นแหละครับ ถึงแม้ว่าจะไม่ถูกrender แต่ก็สามารถ view source ดูได้นะครับดังนั้นด้านความปลอดภัยเจ้าตัวนี้ไม่มีเลยนอกจากจะเข้ารหัสให้มันเท่านั้นเองครับ
Hidden Field จะไม่สามารถทำการส่งค่าข้ามเพจได้ ใช้ได้เฉพาะการ postback กลับมาที่หน้าเดิมเท่านั้น

Code:
//ประกาศตัวแปร 
	Hidden FieldSystem.Web.UI.HtmlControls.HtmlInputHidden Hidden1;
//กำหนดค่า
	Hidden1.Value = "MyValue";
//รับค่า
	string str = Hidden1.Value;



View State เป็น property ที่มีอยู่ในทุก Controlของเพจรวมถึงตัวเพจเองด้วย View State เป็นหัวใจสำคัญของการควบคุม StateValue ใน ASP.NET ซึ่งโดยปกติแล้ว Control ทุกตัวจะทำการเก็บค่า ViewState โดยอัตโนมัติ ทำให้เราสามารถดึงเอาข้อมูลจาก Controlต่างๆหลังจากผู้ใช้ postback มาที่ server โดยไม่ต้องทำอะไรทำให้บรรยากาศในการพัฒนาเว็บคล้ายๆกับการเขียน Windows Application
นอกจากจะเก็บข้อมูลแต่ละ Control แล้ว View Stateของหน้าเพจยังอนุญาตให้เราเก็บอะไรต่อมิอะไรได้ด้วยแม้แต่ objectซึ่งดีกว่า Hidden Field ที่เก็บได้แต่ String โดยค่าต่างๆที่เก็บเข้าไปจะถูกเข้ารหัสและบีบข้อมูลทำให้มีความปลอดภัยเพิ่มขึ้นถึงแม้ข้อมูล ViewState นี้จะถูกส่งไปที่ Client Browser ก็ตาม
เนื่องจาก View Stateเป็น property ของหน้าเพจใดๆ ดังนั้นเราจะไม่สามารถทำการส่งค่า View Stateข้ามเพจได้ ใช้ได้เฉพาะการ postback กลับมาที่หน้าเดิมเท่านั้น

Code:
//บันทึกข้อมูล
	ViewState["MyValue"] = "MyValue";ViewState["MyTable"] = new DataTable();
//ดึงข้อมูลกลับมา
DataTable tbl = (DataTable)ViewState["MyTable"];



ViewState เก็บค่าลงใน Page ซึ่งจะคล้ายๆกับ HiddenField แต่ ViewStateสามารถเก็บ Object ได้ (แสดงว่าเก็บได้ทุกอย่าง) โดยการ Serialize ข้อมูล
ข้อเสียของ ViewState คือไม่สามารถส่งค่าข้ามฟอร์มได้ เพราะมันจะเก็บลงเฉพาะ Page ตัวเองเท่านั้น
และต้องพึงระวังว่าการโหลดเพจของคุณจะช้าลงเพราะขนาดไฟล์ที่โหลดจะใหญ่ขึ้นครับ

การเก็บค่าไว้ใน ViewState ก็ง่ายนิดเดียว
ViewState["MyInt"] = 10;
ViewState["MyBool"] = true;
ViewState["MyString"] = "Hello World";
ViewState["MyTable"] = new DataTable();

แต่ว่า...ถ้าจะนำออกมาใช้ ก็อย่าลืม Casting ด้วยนะครับ
int a = (int)ViewState["MyInt"];
bool b = (bool)ViewState["MyBool"];
string c = ViewState["MyString"].ToString(); 
//ใช้แบบนี้ง่ายกว่า Casting ครับ
DataTable table = (DataTable)ViewState["MyTable"];

ถ้าอยากรู้ว่าขนาดของ ViewState เพิ่มขึ้นขนาดไหนก็ไปเปิด Trace ดูได้ครับ




เพิ่มเติมครับ
บางครั้งหากเราเขียน Web Custom Control
มันก็จะมีการใช้คู่กันแบบนี้ครับ
 public class MyTextBox : TextBox
 {
        public string MyName
        {
            get{ return ViewState[ "MyName" ].ToString(); }
            set{  ViewState[ "MyName" ] = value; }
        }
/*** ไม่ได้ใช้แบบนี้ เนื่องจาก พอเกิดการ PostBack ค่ามันจะหายครับ****/
        private string _name;
        public string MyName
        {
            get{ return name; }
            set{ name = value; }
        }
}
เวลาเรียกใช้ก็จะเรียกผ่าน Property ชื่อ MyName ครับ
แต่เก็บค่าในแบบ ViewState

Control State คล้ายๆ View state แต่จะต้องเขียน code มากกว่า ซับซ้อนกว่าแต่ข้อดีคือ ไม่สามารถที่จะ disable ได้ ในขณะ View state สามารถถูกdisable ได้ ทำให้ control บางอย่างที่ใช้ View stateเป็นกลไกหลักในการเก็บข้อมูลไม่สามารถใช้งานได้ถูกต้อง

Query Strings เป็นวิธีที่ simple และ classic ที่สุด คือส่งค่าไปกับ urlซึ่งก็มีข้อดีคือความง่าย แต่ต้องแลกมาด้วย security นั่นแหละรวมถึงความยาวด้วย เพราะ browser จำกัดความยาวของ url ไว้

Application State เป็นตัวแปรที่มี scope ในระดับ application แต่ว่าจะมีlifetime จำกัด (volatile state) นั่นคือหาก application มีการ restartใหม่ ตัวแปรนี้จะหายไป และถูกสร้างขึ้นใหม่ด้วย

Session State ใช้งานง่าย ปลอดภัยสูง เก็บข้อมูลได้ทุกชนิด แต่ก็ทำให้server รับภาระมากเหมือนกัน หากเขียนจัดการไม่ดี web จะถูกโจมตีได้ง่ายมาก

Profile Properties ใช้งานคล้ายๆ Session แต่ว่าเก็บแบบถาวรกว่าคือไม่สูญหายไปไหน แม้ application จะถูก restart ก็ตาม แต่การจะใช้profile ได้ ต้องมีการเซ็ตอัพระบบ user อีกมากมาย และก็ทำงานได้ช้ากว่าsession

Database Support ก็เป็นการใช้ DB ในการเก็บข้อมูลนั่นแหละ

Credit: DevStock

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

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


Credit: DevStock

วิธีการใช้ 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 SquareRoot เข้ามา
การ 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 rootuxInput.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 ซึ่งก็ไม่ได้น่าจะเป็นเรื่องที่แก้ยาก ดูกันต่อไป :)

Credit: DevStock

วิธีการใช้ 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.  
    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.  
    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) มันก็น่าจะเป็น FROMbranch TO trunk หรืออะไรทำนองนั้น แล้วทำไมมันถึงต้องใช้ FROM trunk TO branch เล่า ? ฟังดูงงยิ่งนัก
วิธีเข้าใจก็คือ ให้มองการใช้ FROM กับ TO เป็นการหาผลต่างครับเราต้องการหาผลต่างระหว่าง trunk ไปหา branch เพื่อที่จะเอาไปรวมกับ trunkโดยที่ FROM จะใส่อันที่เก่ากว่า และ TO ใส่อันที่ใหม่กว่าพูดแบบนี้เข้าใจมากกว่าครับ และจะช่วยให้เข้าใจในกรณีของการ sync branchเข้ากับ trunk ด้วย ว่าทำไมทั้ง FROM ทั้ง TO ถึงต้องใส่ trunk ทั้งคู่เลย
Merge with Square
(ตัวอย่างหน้าต่าง merge ใน TSVN)
Credit: DevStock

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

ปัญหาอย่างหนึ่งที่ทำให้ผมสนใจเรื่อง Software Engineeringคือจะทำยังไงให้คนหลายๆคนทำงานด้วยกันได้ครับประเด็นหนึ่งที่น่าสนใจเกี่ยวกับปัญหานี้ก็เป็นเรื่องการจัดการ "สถานะ"ของระบบนี่แหละ (ราชบัณฑิตแปล Configuration ว่า "โครงแบบ"อ่านไม่รู้เรื่องเลยแฮะ) หรือถ้าพูดภาษาชาวบ้านมันก็คือ SoftwareConfiguration 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 โดยสมาชิกคนหนึ่งคนอื่นก็จะเห็นการแก้ไขนั้นๆด้วย ถ้าพูดให้ง่ายๆก็คือมันเป็น SharedFolder เหมือนที่เราเห็นใน Network นั่นเอง
อย่างไรก็ตามตัวRepo นี่มันมีความสามารถกว่านั้นมาก เพราะมันจะเก็บ "สถานะ"เก่าๆย้อนหลังไว้ได้ด้วย ดังนั้นหากมีสมาชิกเผลอลบไฟล์บางไฟล์ออกไปจากRepo ก็ยังสามารถกู้กลับมาได้โดยง่ายโดยการย้อนไป "สถานะ" ก่อนหน้า ...เจ๋งใช่มั้ยล่ะ
สถานะแต่ละสถานะก็จะมีหมายเลขกำกับด้วยซึ่งเราจะเรียกว่า Revision Number ซึ่งทำการแก้ไขเข้าไปหลายๆครั้งRevision Number ก็จะยิ่งเพิ่มมากขึ้น และเราสามารถดึงข้อมูลจาก Revisionใดๆใน Repo ก็ได้ Revision ล่าสุดใน Repo จะมีชื่อเรียกพิเศษเรียกว่า HeadRevision ด้วยครับ

คำสั่งพื้นฐาน: 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.  
    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นี้เป็นงานที่คอมพิวเตอร์ทำเองไม่ได้ครับ เดี๋ยวทำมาแล้วไม่ถูกใจ แย่เลย 

Credit: DevStock