重拾散亂的VSS程式碼版本
2 | 8,931 |
每次系統上線時,完整保留建置當時使用的原始碼版本是版本控制的基本守則,跟燙傷時要沖脫泡蓋送一樣屬於生活須知。落實了這一步,才不會在後續開發產生分支後(例如: 修改部分程式編譯成另一套版本與原版本並存運行),落入找不齊該版本原始碼,程式從此無法修改的悲慘下場。
針對這類需求,版控軟體多已設想周到,提供了如分支(Branch)、標籤(Label)等功能協助管理版本(連清朝就出現的版控軟體VSS都有)。
不過,人難免年少荒唐,即便有VSS版控,手邊卻還是有個專案因為時程趕外加偷懶茍且又無知,草草Build完上線就繼續改寫加入新功能,等後來因程式規格差異決定要拆出另一個特別版本與原版本並行時,才發現VSS中部分程式已參雜了上線後才修改簽入的部分(而且還改很大),再也找不齊當初編譯所用的版本,搞出一個再也無法修改重新編譯的程式怪獸...
最近,抱著贖罪的心情,決定開發一個小工具程式,可在VSS中搜羅當初編譯之前所簽入的最後版本號碼,如此即可整理出編譯當時"最新的程式碼",找齊當初編譯所用的原始碼。(前題是平日有確實遵守"編譯前必須先Check In"準則,這相當於"上完廁所要洗手"的生活須知等級,若不幸遇上衛生習慣如此不堪的開發者,那... 節哀吧!)
開始研究才發現,VSS老歸老,在自動化整合方面可不含糊,微軟甚至也準備好Microsoft.VisualStudio.SourceSafe.Interop,只要在.NET專案中加入參考,就可運用程式存取VSS資料庫,取得檔案、版本資訊,甚至進行簽入、簽出等各項版控操作。
於是我寫了以下的工具:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.VisualStudio.SourceSafe.Interop;
using System.IO;
namespace DumpVssProjectStruc
{
public class VssReader
{
IVSSDatabase vssdb = new VSSDatabase();
DateTime? notAfter = null;
public VssReader(string ssdir, string ssuser, string sspwd)
{
vssdb.Open(ssdir, ssuser, sspwd);
}
//取得專案檔案結構XML,傳入saveFolder參數可將最後版本檔案存入指定目錄
//傳入notAfter則可選取某個時間之前簽入的檔案
public XDocument ReadProject(string path,
string saveFolder = null, DateTime? notAfter = null)
{
this.notAfter = notAfter;
XDocument xd = XDocument.Parse("<P />");
Explore(path, xd.Root, saveFolder);
return xd;
}
//以遞迴方式取得專案結構的所有檔案,若傳入savePath則會一併取得檔案寫入
public void Explore(string path, XElement container, string savePath = null)
{
VSSItem proj = vssdb.get_VSSItem(path, false);
//在XML建立目錄元素
XElement dir = new XElement("D",
new XAttribute("P", proj.Spec), new XAttribute("N", proj.Name));
if (!string.IsNullOrEmpty(savePath))
{
//若有指定儲存位置,計算路徑並確保資料夾已建立
savePath = Path.Combine(savePath, proj.Name);
if (!Directory.Exists(savePath))
Directory.CreateDirectory(savePath);
}
foreach (VSSItem item in proj.get_Items(false))
{
//若為VSSITEM_PROJECT,等同目錄,要再展開其下的子項目
if (item.Type == (int)VSSItemType.VSSITEM_PROJECT)
Explore(item.Spec, dir, savePath);
else
{
//在XML建立檔案項目元素
XElement file = new XElement("F",
new XAttribute("P", item.Spec),
new XAttribute("N", item.Name));
bool saved = false;
//取得該項目的所有版本資訊
foreach (VSSVersion v in item.Versions)
{
//若有指定簽入時間限制,忽略晚於限制時間的版本
if (notAfter != null & v.Date.CompareTo(notAfter) > 0)
continue;
//在檔案項目元素下新增版本元素
file.Add(new XElement("V",
new XAttribute("N", //若為標籤,則在前方加上l當作版號
string.IsNullOrEmpty(v.Label) ?
v.VersionNumber.ToString() : "l" + v.Label),
new XAttribute("U", v.Username),
new XAttribute("T",
v.Date.ToString("yyyy/MM/dd HH:mm:ss"))
));
//存入第一個吻合的版本(忽略標籤版本)
if (!string.IsNullOrEmpty(savePath) &&
string.IsNullOrEmpty(v.Label) && !saved)
{
string filePath = Path.Combine(savePath, item.Name);
v.VSSItem.Get(ref filePath, 0);
saved = true;
}
//若有簽入時間限制,則只包含吻合的第一筆
if (notAfter != null) break;
}
dir.Add(file);
}
}
container.Add(dir);
}
}
}
先執行VssReader vr = new VssReader("\\ vss_server\vssShare", ssUser, ssPwd); 可開啟指定的VSS資料庫。
接著Console.Write(vr.ReadProject("$/TBM/AuditLogon").ToString());即可取得如下包含版本歷程的VSS專案結構XML檔: (這個產出結果也可用來產生報表或進行其他分析,挺好用的)
<P>
<D P="$/TBM/AuditLogon" N="AuditLogon">
<F P="$/TBM/AuditLogon/AssemblyInfo.cs" N="AssemblyInfo.cs">
<V N="lSealed" U="jeffrey" T="2011/11/23 17:33:26" />
<V N="1" U="jeffrey" T="2008/07/03 12:14:28" />
</F>
<F P="$/TBM/AuditLogon/AuditLogon.csproj" N="AuditLogon.csproj">
<V N="lSealed" U="jeffrey" T="2011/11/23 17:33:26" />
<V N="1" U="jeffrey" T="2008/07/03 12:14:28" />
</F>
<F P="$/TBM/AuditLogon/Form1.cs" N="Form1.cs">
<V N="3" U="jeffrey" T="2011/11/25 16:02:15" />
<V N="lSealed" U="jeffrey" T="2011/11/23 17:33:26" />
<V N="2" U="jeffrey" T="2008/07/15 14:25:32" />
<V N="1" U="jeffrey" T="2008/07/03 12:14:28" />
</F>
<F P="$/TBM/AuditLogon/Form1.resx" N="Form1.resx">
<V N="lSealed" U="jeffrey" T="2011/11/23 17:33:26" />
<V N="1" U="jeffrey" T="2008/07/03 12:14:28" />
</F>
</D>
</P>
如果要取得一份2010/04/01 16:00:00前簽入的原始檔案集合並儲存於D:\\SourceRecovery,執行
vr.ReadProject("$/TBM/AuditLogon", @"D:\SourceRecovery", new DateTime(2010, 4, 1, 16, 0, 0));
改不動的系統終於重見天日,這下可以重新做人囉~~
Comments
# by Alex Lee
"跟燙傷時要沖脫泡蓋送一樣樣屬於生活須知。" "樣"字重複~
# by Jeffrey
to Alex, 已修正,感謝!