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