匯出 Microsoft TFS 的異動紀錄

TFS ChangeLog

在使用版本控制系統時,若是想要查詢異動紀錄,唯一的辦法就是開啟 Visual Studio 進入原始檔控制總管,然後點選專案再選取檢視紀錄。雖然這樣已經很方便了,但若是要進行註解的搜尋就會有些麻煩了。

基於此原因,就想到將變更紀錄匯出,就可以快速進行註解的搜尋了。

使用參考:

Microsoft TeamFoundationServer ExtendedClient

Dapper

 

首先透過

TfsTeamProjectCollection teamProjects = new TfsTeamProjectCollection(new Uri(<TFS Server 網址>));

建立 TFS Client 端連線,接著取回版本控制服務

VersionControlServer vcsTFS = teamProjects.GetService<VersionControlServer>();

在版控服務中可以取回所有專案的名稱

var allProjectName = vcsTFS.GetAllTeamProjects(false).Select(x => x.Name).ToList();

取回指定專案的異動集資料

var allHistories = vcsTFS.QueryHistory("$/" + projectName, VersionSpec.Latest, 0, RecursionType.Full, null, null, null, int.MaxValue, true, false);

透過迴圈可以取得每一個異動集的資訊

foreach (Changeset history in allHistories)
{
    var changesetID = history.ChangesetId;  // 異動集的編號
    var Comment = history.Comment;  // 簽入的說明
    var Committer = history.Committer;  // 簽入的人員
    var CommitterDisplayName = history.CommitterDisplayName;  // 簽入人員名稱
    var CreationDate = history.CreationDate;  // 簽入日期
    var changes = history.Changes;  // 簽入的檔案資訊
}

其中 history.Changes 紀錄了所有簽入的檔案資訊

foreach (Change change in history.Changes)
{
    var ChangeType = change.ChangeType;  // 異動類別
    var CheckinDate = change.Item.CheckinDate;  // 簽入日期
    var ItemType = change.Item.ItemType;  // 項目類別
    var ServerItem = change.Item.ServerItem;  // 檔案路徑
}

若是要下載該項目,可以使用 change.Item.DownloadFile() 將檔案取回。
例:

byte[] bFile = new byte[change.Item.ContentLength];
change.Item.DownloadFile().Read(bFile, 0, (int)change.Item.ContentLength);

ChangeType 列舉:
1  = 無
2  = 新增
4  = 修改
8  = 編碼
16  = 更名
32  = 刪除
64  = 回復刪除
128  = 分支
256  = 合併
512  = 鎖定
1024 = RollBack
2048 = 來源更名
8192 = 屬性更改


ItemType 列舉:
0 = 其他
1 = 資料夾
2 = 檔案

程式碼:

private void Query_ProjectList()
{
		tpc = new TfsTeamProjectCollection(new Uri(txtURL.Text));
    vcs = tpc.GetService<VersionControlServer>();
    lstProjectData = vcs.GetAllTeamProjects(false).Select(x => new ProjectVM() { ProjectName = x.Name }).ToList();

    foreach (var tmp in lstProjectData)
    {
        int iItem = 0;
        List<Changeset> allHistory = vcs.QueryHistory("$/" + tmp.ProjectName, VersionSpec.Latest, 0, RecursionType.Full, null, null, null, int.MaxValue, true, false).Cast<Changeset>().ToList();
        tmp.ChangeCount = allHistory.Count();
        allHistory.ForEach(x => { iItem += x.Changes.Count(); });
        tmp.ChangeItemCount = iItem;
    }
}

private void Export_Project(string projectName)
{
    TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri(txtURL.Text));
    VersionControlServer vcs = tpc.GetService<VersionControlServer>();
    var allHistories = vcs.QueryHistory("$/" + projectName, VersionSpec.Latest, 0, RecursionType.Full, null, null, null, int.MaxValue, true, false);

    foreach (Changeset history in allHistories)
    {
        ChangeSetVM tmpChangeSetVM = new ChangeSetVM()
        {
            ProjectName = projectName,
            ChangeSetID = history.ChangesetId,
            Comment = history.Comment,
            Committer = history.Committer,
            CommitterDisplayName = history.CommitterDisplayName,
            CreationDate = history.CreationDate
        };

        if (IsChangeSetExist(projectName, history.ChangesetId))
        		continue;
        else
        		InsertChangeSet(tmpChangeSetVM);

        foreach (Change change in history.Changes)
        {
            var ChangeType = change.ChangeType;
            var CheckinDate = change.Item.CheckinDate;
            var ItemType = change.Item.ItemType;
            var ServerItem = change.Item.ServerItem;
        }

        foreach (Change change in history.Changes)
        {
            if (IsChangeSetItemExist(projectName, history.ChangesetId, change.Item.ItemId)) continue;

            ChangeSetItemVM tmpItem = new ChangeSetItemVM()
            {
                ProjectName = projectName,
                ChangeSetID = history.ChangesetId,
                ChangeType = (int)change.ChangeType,
                ItemId = change.Item.ItemId,
                ItemType = (int)change.Item.ItemType,
                CheckinDate = change.Item.CheckinDate,
                ServerItem = change.Item.ServerItem
            };

            byte[] bFile = new byte[change.Item.ContentLength];
            if (change.ChangeType == ChangeType.Delete || change.Item.ContentLength == 0 || tmpItem.ServerItem.Contains("/packages/"))
                bFile = new byte[0];
            else
                change.Item.DownloadFile().Read(bFile, 0, (int)change.Item.ContentLength);

            tmpItem.FileData = bFile;
            InsertChangeSetItem(tmpItem);

        }
    }
}

private bool IsChangeSetExist(string ProjectName, int ChangeSetID)
{
    try
    {
        string strSQL = "SELECT COUNT(1) FROM ChangeSet WHERE ProjectName=@ProjectName AND ChangeSetID=@ChangeSetID";
        var paras = new { ProjectName, ChangeSetID };
        int iCheck = 0;
        using (IDbConnection _conDB = new System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.AppSettings["connectionString"].ToString()))
        {
            iCheck = _conDB.ExecuteScalar<int>(strSQL, paras);
        }

        return iCheck > 0 ? true : false;
    }
    catch
    {
        throw;
    }
}

private void InsertChangeSet(ChangeSetVM item)
{
    try
    {
        string strSQL = "INSERT INTO ChangeSet (ProjectName, ChangeSetID,  Comment, Committer, CommitterDisplayName, CreationDate) VALUES (@ProjectName, @ChangeSetID, @Comment, @Committer, @CommitterDisplayName, @CreationDate)";
        var paras = new { item.ProjectName, item.ChangeSetID, item.Comment, item.Committer, item.CommitterDisplayName, item.CreationDate };
        using (IDbConnection _conDB = new System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.AppSettings["connectionString"].ToString()))
        {
            _conDB.Execute(strSQL, paras);
        }
    }
    catch
    {
        throw;
    }
}


private bool IsChangeSetItemExist(string ProjectName, int ChangeSetID, int ItemID)
{
    try
    {
        string strSQL = "SELECT COUNT(1) FROM ChangeSetItem WHERE ProjectName=@ProjectName and ChangeSetID=@ChangeSetID AND ItemID=@ItemID";
        var paras = new { ProjectName, ChangeSetID, ItemID };
        int iCheck = 0;
        using (IDbConnection _conDB = new System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.AppSettings["connectionString"].ToString()))
        {
            iCheck = _conDB.ExecuteScalar<int>(strSQL, paras);
        }

        return iCheck > 0 ? true : false;
    }
    catch
    {
        throw;
    }
}

private void InsertChangeSetItem(ChangeSetItemVM item)
{
    try
    {
        string strSQL = "INSERT INTO ChangeSetItem (ProjectName, ChangeSetID, ChangeType, ItemId, ItemType, CheckinDate, ServerItem, FileData) VALUES (@ProjectName, @ChangeSetID, @ChangeType, @ItemId, @ItemType, @CheckinDate, @ServerItem, @FileData)";
        var paras = new { item.ProjectName, item.ChangeSetID, item.ChangeType, item.ItemId, item.ItemType, item.CheckinDate, item.ServerItem, item.FileData };
        using (IDbConnection _conDB = new System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.AppSettings["connectionString"].ToString()))
        {
            _conDB.Execute(strSQL, paras);
        }
    }
    catch
    {
        throw;
    }
}


private class ProjectVM
{
    public string ProjectName { get; set; }
    public int ChangeCount { get; set; }
    public int ChangeItemCount { get; set; }
}
private class ChangeSetVM
{
    public string ProjectName { get; set; }
    public int ChangeSetID { get; set; }
    public string Comment { get; set; }
    public string Committer { get; set; }
    public string CommitterDisplayName { get; set; }
    public DateTime CreationDate { get; set; }
}
private class ChangeSetItemVM
{
    public string ProjectName { get; set; }
    public int ChangeSetID { get; set; }
    public int ChangeType { get; set; }
    public int ItemId { get; set; }
    public int ItemType { get; set; }
    public DateTime CheckinDate { get; set; }
    public string ServerItem { get; set; }
    public byte[] FileData { get; set; }
}

 

 


程式是運氣與直覺堆砌而成的奇蹟。
若不具備這兩者,不可能以這樣的工時實現這樣的規格。
修改規格是對奇蹟吐槽的褻瀆行為。
而追加修改則是相信奇蹟還會重現的魯莽行動。