TFS Meltdown - Как я могу восстановить отложенные изменения

Моя рабочая папка была настроена на ОЗУ. В течение ночи произошел длительный перерыв в подаче электроэнергии, ИБП закончился, и моя машина вышла из строя. К счастью, я отложил свои изменения до того, как отправился домой, и этот набор полок виден в Team Explorer. Набор изменений включает в себя файл проекта и некоторые новые файлы, которые еще не были добавлены в систему контроля версий.

Я пытаюсь восстановить поврежденные файлы, но получаю ошибки:

Попытка просмотра отложенных файлов дает TF10187 (или общий, ненумерованный) The system cannot find the file specified хотя я могу видеть их в Pending Changes список.

Попытка восстановить набор целиком приводит к ошибкам, связанным с incompatible changes который я не могу решить.

Я предполагаю, что TFS локально кеширует набор полок на диске RAM, который с тех пор заново инициализировался и, следовательно, потерял кеш, но я надеюсь, что я ошибаюсь.

Кто-нибудь может помочь?

3 ответа

Решение

Я попросил кого-то прийти ко мне и задать тот же вопрос вчера, к счастью, у него была резервная копия базы данных проекта TFS (tfs_), поэтому мы восстановили ее в другой базе данных, и я покопался в ней и выяснил (так что, если у вас есть резервная копия, тогда да, вы можете восстановить все файлы).

Прежде всего немного информации о таблицах в базе данных.

Shelveset может быть идентифицирован путем запроса таблицы tbl_Workspace и поиска всех записей с Type=1 (Shelveset), вы также можете фильтровать по имени с помощью столбца WorkspaceName.

Другие интересные таблицы:

tbl_PendingChanges (который ссылается на WorkspaceId из tbl_Workspace) - какие файлы являются частью 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.

Надеюсь это поможет!

Вот обновленный ответ для TFS2015, у которого было другое изменение схемы. Ниже представлено консольное приложение C# для записи текстовых файлов на рабочий стол. Обязательно заполните переменные connString и shelvesetName.

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();
                }
            }
        }
    }
}

У меня было что-то подобное со мной с экземпляром TFS 2012. Мой запрос SQL был немного другим, так как схема изменилась для TFS 2012. Надеюсь, это кому-нибудь поможет.

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();
    }
}
}
Другие вопросы по тегам