TFS 2008源代码管理 - 快速销毁所有已删除项目的方法

And*_*tan 8 .net c# version-control tfs visual-studio-2008

我有一堆源控制文件夹,我想要摆脱所有不再需要的项目.这些项目已被删除(代码已被移动或重写),因为我们大多数人默认使用"显示已删除的项目"选项,其中一些文件夹现在会显示更多已删除的项目和文件夹以及合法项目.我想确保所有这些冗余代码永远消失 - 因为它绝对不会被要求.这些是从旧版本的分支构建的新项目,目前还没有人使用.

但是,有很多文件分布在多个文件夹中,因此我宁愿避免单独执行每个文件.我也在命令行,而不是使用API​​.

我知道最终我需要这个tf destroy命令.

我也知道tf dir [wildcard] /recursive /deleted将返回路径中的所有已删除项目(不幸的是与所有合法项目一起).

谁能想到一个快速做到这一点的好方法?

我想到了两个解决方案:

1)获取dir命令的输出并找到之后的所有项目:Xnnnnnnn- 这些是已删除的项目; 然后简单地吐出一堆tf destroy调用,或构造一个响应文件(虽然不确定这个位).这听起来像Powershell的一个潜在用途,但实际上还没有做任何事情......

2)准备好所有项目,然后简单地从TFS中销毁它们,然后重新添加它们,这样只有所需的东西才能在TFS中.但是,这确实消除了可能有用的分支关系,因为有一段时间我将不得不维护这些库中的一些版本(升级前和升级后).不理想,但我无能为力.

显然,选项2是一个骗子,但它会起作用 - 我理想情况下就像一个可重用的脚本,可以在未来用于TFS中的任何文件夹(其他几个团队还有其他长期项目可以用于完全清除!).

提前致谢.

And*_*tan 11

好的,所以我写了一个控制台应用程序(.Net 4):

它没有说出来我对此提出任何保证 - 它将在TFS中破坏物品!!!!

更新(2012年5月8日)如果在具有质量和质量(我意味着数千或数万)已删除项目的文件夹上运行此操作,则可能在TFS命令行超时之前无法完成.此命令占用的大部分时间是生成.tfc脚本.如果您运行它并发现这种情况,请尝试首先定位一些子文件夹

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace TFDestroyDeleted
{
  class Program
  {
    static void Main(string[] args)
    {
      if (args.Length < 1 || args.Length > 3)
        Usage();

      bool prepareOnly = false;
      bool previewOnly = false;

      if (args.Any(s => StringComparer.InvariantCultureIgnoreCase
          .Compare(s, "preview") == 0)) previewOnly = true;

      if (args.Any(s => StringComparer.InvariantCultureIgnoreCase
          .Compare(s, "norun") == 0)) prepareOnly = true;

      string tfOutput = null;

      Process p = new Process();

      p.StartInfo = new ProcessStartInfo("tf")
      {
        Arguments = string.Format
          ("dir /recursive /deleted \"{0}\"", args[0]),
        UseShellExecute = false,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        RedirectStandardInput = true
      };

      p.Start();
      tfOutput = p.StandardOutput.ReadToEnd();
      p.WaitForExit();

      string basePath = null;
      string nextDelete = null;
      List<string> toDelete = new List<string>();

      using (var ms = 
        new MemoryStream(Encoding.Default.GetBytes(tfOutput)))
      {
        using (StreamReader sr = new StreamReader(ms))
        {
          while (!sr.EndOfStream)
          {
            nextDelete = null;
            string line = sr.ReadLine();
            if (string.IsNullOrWhiteSpace(line))
              basePath = null;
            else
            {
              if (basePath == null)
              {
                if (line.EndsWith(":"))
                  basePath = line.Substring(0, line.Length - 1);
                else
                  continue;
              }
              else
              {
                nextDelete = Regex.Match(line, @"^.*?;X[0-9]+").Value;
                if (!string.IsNullOrWhiteSpace(nextDelete))
                {
                  toDelete.Add(
                    string.Format
                    ( 
                      "{0}/{1}", basePath, 
                      nextDelete.StartsWith("$") ? nextDelete.Substring(1) 
                      : nextDelete
                    ));
                }
              }
            }
          }
        }
      }

      using (var fs = File.OpenWrite("destroy.tfc"))
      {
        fs.SetLength(0);
        using (var sw = new StreamWriter(fs))
        {
          //do the longest items first, naturally deleting items before their
          //parent folders
          foreach (var s in toDelete.OrderByDescending(s => s.Length))
          {
            if (!previewOnly)
              sw.WriteLine("destroy \"{0}\" /i", s);
            else
              sw.WriteLine("destroy \"{0}\" /i /preview", s);
          }
          sw.Flush();
        }
      }

      if (!prepareOnly)
      {
        p.StartInfo = new ProcessStartInfo("tf")
          {
            Arguments = string.Format("@{0}", "destroy.tfc"),
            UseShellExecute = false
          };
        p.Start();
        p.WaitForExit();
      }

      p.Close();

    }

    static void Usage()
    {
      Console.WriteLine(@"Usage:
TFDestroyDeleted [TFFolder] (preview) (norun)
Where [TFFolder] is the TFS root folder to be purged - it should be quoted if there are spaces.  E.g: ""$/folder/subfolder"".
norun - Specify this if you only want a command file prepared for tf.
preview - Specify this if you want each destroy to be only a preview (i.e. when run, it won't actually do the destroy) ");
      Environment.Exit(0);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您必须传递要删除的TFS文件夹,例如'$/folder'.如果您只是通过它,那么将逐个检测并销毁所有匹配的已删除项目.

出于某种原因 - 如果您不小心传递了一个实际上不存在的文件夹,那么该操作将永远存在.当然,CTRL + C会阻止它.

该应用程序使用/deleted开关在文件夹上执行递归目录.

然后它会遍历输出中的每一行,查找删除提示,即带有的项目;Xnnnnnnn.如果找到,它会将该项的完整tfs路径添加到列表中.

完成后,列表按长度按降序排序,内容写入tf.exe命令行的tfc响应文件.

如果preview指定了该选项,则tf使用/ preview开关写出命令(请参阅MSDN上的TFS Destroy)然后实际上不执行删除操作.

最后,您可以指定norun导致创建tfc文件的原因,但不能实际运行.