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

标签 c# .net visual-studio-2008 version-control tfs

我有一堆源代码管理文件夹,我想删除所有不再需要的项目。这些项目已被删除(代码已被移动或重写),并且由于我们大多数人默认使用“显示已删除项目”选项,其中一些文件夹现在显示更多已删除项目和文件夹以及合法项目。我想确保所有这些冗余代码永远消失——因为它绝对不会被需要。这些新项目是从尚未有人使用的旧项目的分支构建的。

不过,有相当多的文件分布在多个文件夹中,因此我宁愿避免逐一执行。我也在命令行,而不是使用 API。

我知道最终我将需要 tf destroy 命令。

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

谁能想出一个快速的好方法?

我想到了两种解决方案:

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

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

显然,选项 2 是一种作弊,但它会起作用 - 我只是理想地喜欢一个可重用的脚本,将来可以用于 TFS 中的任何文件夹(其他几个团队有其他长期存在的项目,可以完全清除!)。

提前致谢。

最佳答案

好吧,我写了一个控制台应用程序 (.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);
    }
  }
}

您必须传递要删除的 TFS 文件夹,例如“$/folder”。如果您只是通过它,那么所有匹配的已删除项目将被一个接一个地检测和销毁。

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

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

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

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

如果指定了 preview 选项,那么 tf 命令将使用/preview 开关写出(参见 MSDN 上的 TFS Destroy) 那么实际上并没有执行删除操作。

最后,您可以指定 norun,这会导致创建 tfc 文件,但不会实际运行。

关于c# - TFS 2008 源代码管理 - 销毁所有已删除项目的快速方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4920677/

相关文章:

c# - ASP.NET MVC 4 - 401 访问被拒绝

c# - 为什么我不能将事件标记为 NotNull?

c# - 将变量从用户表单传递到类

visual-studio-2008 - 将 ffmpeg 连接到 Visual Studio 2008

visual-studio-2008 - VS 2008 中的程序切片

c# - 如果文件存在于 asp.net mvc 中,则返回链接

c# - 从 WCF 服务返回类型化数据集导致未找到 'xs:string'

c# - linq2db:无法为表生成 POCO

c# - Windows 工具栏可停靠应用程序

visual-studio - Visual Studio : Is there a keyboard shortcut for search in the current line?