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

时间:2011-02-07 11:21:14

标签: 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中的任何文件夹(其他几个团队还有其他长期存在的项目可以完全清除!)。

提前致谢。

2 个答案:

答案 0 :(得分:10)

好的,所以我写了一个控制台应用程序(.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选项,则使用/ preview开关写出tf命令(请参阅MSDN上的TFS Destroy) 然后删除实际上没有执行。

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

答案 1 :(得分:2)

我知道这是一个老问题,但我认为这可能会有所帮助。

我们有一个旧的集合,在VSO下有20多个团队项目,真的需要清理我们的团队项目。这段代码非常适合我们。

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;

static void Main(string[] args)
{
    TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri("COLLECTION_URL")); //Example: https://xxxx.visualstudio.com
    var versionControl = tfs.GetService<VersionControlServer>();

    ItemSpec spec = new ItemSpec("$/", RecursionType.Full);

    var folderItemSet = versionControl.GetItems(spec, VersionSpec.Latest, DeletedState.Deleted, ItemType.Folder, true);
    DestoryItemSet(versionControl, folderItemSet);

    //Delete remaining files
    var fileItemSet = versionControl.GetItems(spec, VersionSpec.Latest, DeletedState.Deleted, ItemType.File, true);
    DestoryItemSet(versionControl, fileItemSet);
}

private static void DestoryItemSet(VersionControlServer versionControl, ItemSet itemSet)
{
    foreach (var deletedItem in itemSet.Items)
    {
        try
        {
            versionControl.Destroy(new ItemSpec(deletedItem.ServerItem, RecursionType.Full, deletedItem.DeletionId), VersionSpec.Latest, null, Microsoft.TeamFoundation.VersionControl.Common.DestroyFlags.None);
            Console.WriteLine("{0} destroyed successfully.", deletedItem.ServerItem);
        }
        catch (ItemNotFoundException) //For get rid of exception for deleting the nested objects
        {

        }
        catch (Exception)
        {
            throw;
        }
    }
}

我使用了Microsoft.TeamFoundationServer.ExtendedClient NuGet包。