3

我将工作文件夹设置为 RAM 驱动器。晚上有长时间停电,UPS 用完了,我的机器也坏了。值得庆幸的是,我在回家之前搁置了我的更改,并且在 Team Explorer 中可以看到搁置集。变更集包括项目文件和一些尚未添加到源代码管理的新文件。

我正在尝试恢复受影响的文件,但出现错误:

尝试查看搁​​置的文件会给出TF10187(或一般的、未编号的), The system cannot find the file specified即使我可以在Pending Changes列表中看到它们。

尝试完全取消搁置集会产生与incompatible changes我无法解决的相关错误。

TFS 将搁置集本地缓存在 RAM 磁盘上,该磁盘已重新初始化,因此丢失了缓存,但我希望我错了。

有人可以帮忙吗?

4

3 回答 3

6

昨天有人来找我问同样的问题,幸运的是他们有 TFS 项目数据库(tfs_)的备份,所以我们将它恢复到另一个数据库,我四处寻找并弄清楚了(所以,如果你有备份,那么是的,您可以恢复所有文件)。

首先是关于数据库中表的一些信息。

可以通过查询 tbl_Workspace 表并查找 Type=1 (Shelveset) 的所有记录来识别 Shelveset,当然您也可以使用 WorkspaceName 列按名称进行过滤。

其他感兴趣的表是:

tbl_PendingChanges(从 tbl_Workspace 引用 WorkspaceId)——哪些文件是 ShelveSet 的一部分

tbl_VersionedItem(通过 ItemId 列链接到 tbl_PendingChanges)- 文件的父路径和名称

tbl_Content(通过 FileId 链接到 PendingChanges)- 这是您的文件内容作为压缩 (gzip) 数据存储的位置

现在解决方案;以下查询可以显示您的文件:

SELECT c.[CreationDate], c.[Content], vi.[ChildItem], vi.ParentPath
FROM [dbo].[tbl_Content] c 
INNER JOIN [dbo].[tbl_PendingChange] pc ON pc.FileId = c.FileId
INNER JOIN [dbo].[tbl_Workspace] w ON w.WorkspaceId = pc.WorkspaceId
INNER JOIN [dbo].[tbl_VersionedItem] vi ON vi.ItemId = pc.ItemId
WHERE w.WorkspaceName = '<YOUR SHELVESET NAME>'

有了这些,我编写了一些代码来从 SQL 中获取数据,然后使用 GZipStream 类解压缩内容并将文件保存到磁盘。

一个小时左右就恢复了一周的工作。

这是使用 TFS 2010 完成的。

希望这可以帮助!

于 2013-05-09T10:03:51.750 回答
3

这是 TFS2015 的更新响应,其中有另一个架构更改。下面是一个用于将 txt 文件写入桌面的 C# 控制台应用程序。确保填写 connString 和shelfsetName 变量。

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.IO.Compression;

namespace RestoreTFSShelve
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string shelvesetName = "";
            string connString = "";

            SqlConnection cn = new SqlConnection(connString);
            SqlCommand cmd = new SqlCommand(@"
SELECT c.[CreationDate], c.[Content], v.FullPath
FROM [dbo].[tbl_Content] c
INNER JOIN [dbo].tbl_FileMetadata f ON f.ResourceId = c.ResourceId
INNER JOIN [dbo].tbl_FileReference b ON f.ResourceId = b.ResourceId
INNER JOIN [dbo].[tbl_PendingChange] pc ON pc.FileId = b.FileId
INNER JOIN [dbo].[tbl_Workspace] w ON w.WorkspaceId = pc.WorkspaceId
INNER JOIN [dbo].[tbl_Version] v ON v.ItemId = pc.ItemId AND v.VersionTo = 2147483647
WHERE w.WorkspaceName = '@ShelvesetName'", cn);

            cmd.Parameters.AddWithValue("@ShelvesetName", shelvesetName);

            DataTable dt = new DataTable();
            new SqlDataAdapter(cmd).Fill(dt);

            foreach (DataRow row in dt.Rows)
            {
                string[] arrFilePath = row[2].ToString().Split('\\');
                string fileName = arrFilePath[arrFilePath.Length - 2];
                byte[] unzippedContent = Decompress((byte[])row[1]);
                File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName), unzippedContent);
            }
        }

        private static byte[] Decompress(byte[] gzip)
        {
            using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
            {
                const int size = 4096;
                byte[] buffer = new byte[size];
                using (MemoryStream memory = new MemoryStream())
                {
                    int count = 0;
                    do
                    {
                        count = stream.Read(buffer, 0, size);
                        if (count > 0)
                        {
                            memory.Write(buffer, 0, count);
                        }
                    }
                    while (count > 0);
                    return memory.ToArray();
                }
            }
        }
    }
}
于 2017-04-03T21:56:06.630 回答
2

使用 TFS 2012 实例时,我也遇到过类似的情况。自从 TFS 2012 的架构发生更改以来,我的 SQL 查询有点不同。希望这对某人有所帮助。

SELECT c.[CreationDate], c.[Content], v.FullPath
FROM [dbo].[tbl_Content] c 
INNER JOIN [dbo].[tbl_File] f ON f.ResourceId = c.ResourceId
INNER JOIN [dbo].[tbl_PendingChange] pc ON pc.FileId = f.FileId--c.FileId
INNER JOIN [dbo].[tbl_Workspace] w ON w.WorkspaceId = pc.WorkspaceId
INNER JOIN [dbo].[tbl_Version] v ON v.ItemId = pc.ItemId AND v.VersionTo = 2147483647
WHERE w.WorkspaceName = @ShelvesetName

2147483647 似乎是 2^32 - 1,我认为它可能代表 TFS 2012 中的“最新”。然后我还编写了一个 C# 小部件来解压缩 Gzip 编码的流并将其以正确的文件名转储到磁盘。我没有保留等级制度。

string cnstring = string.Format("Server={0};Database={1};Trusted_Connection=True;", txtDbInstance.Text, txtDbName.Text);
SqlConnection cn = new SqlConnection(cnstring);
SqlCommand cmd = new SqlCommand(@"
SELECT c.[CreationDate], c.[Content], v.FullPath
FROM [dbo].[tbl_Content] c 
INNER JOIN [dbo].[tbl_File] f ON f.ResourceId = c.ResourceId
INNER JOIN [dbo].[tbl_PendingChange] pc ON pc.FileId = f.FileId--c.FileId
INNER JOIN [dbo].[tbl_Workspace] w ON w.WorkspaceId = pc.WorkspaceId
INNER JOIN [dbo].[tbl_Version] v ON v.ItemId = pc.ItemId AND v.VersionTo = 2147483647
WHERE w.WorkspaceName = @ShelvesetName", cn);

cmd.Parameters.AddWithValue("@ShelvesetName", txtShelvesetName.Text);

DataTable dt = new DataTable();
new SqlDataAdapter(cmd).Fill(dt);
listBox1.DisplayMember = "FullPath";
listBox1.ValueMember = "FullPath";
listBox1.DataSource = dt;

if(!Directory.Exists(txtOutputLocation.Text)) { Directory.CreateDirectory(txtOutputLocation.Text); }
foreach (DataRow row in dt.Rows)
{
    string[] arrFilePath = row[2].ToString().Split('\\');
    string fileName = arrFilePath[arrFilePath.Length - 2];
    byte[] unzippedContent = Decompress((byte[])row[1]);
    File.WriteAllBytes(Path.Combine(txtOutputLocation.Text, fileName), unzippedContent);
}
}

    static byte[] Decompress(byte[] gzip)
    {
using(GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
{
    const int size = 4096;
    byte[] buffer = new byte[size];
    using(MemoryStream memory = new MemoryStream())
    {
        int count = 0;
        do
        {
    count = stream.Read(buffer, 0, size);
    if(count > 0)
    {
        memory.Write(buffer, 0, count);
    }
        }
        while(count > 0);
        return memory.ToArray();
    }
}
}
于 2014-05-06T21:19:59.087 回答