【问题标题】:Team Foundation Server - Moving Source with HistoryTeam Foundation Server - 使用历史移动源
【发布时间】:2011-01-14 00:12:12
【问题描述】:

我想知道将带有历史记录的源代码从一个团队项目移动到另一个团队项目的最佳方法是什么。我不关心工作项、报告或 SharePoint 网站,因为我们要从中恢复的系统没有使用这些功能。想要转移到不同的团队项目的原因还在于原始实现(从第三方维护的备份中恢复)使用了我们不希望使用的第三方流程模板向前走。我们希望在迁移完成后开始使用工作项跟踪和报告。

TFS Integration Platform 似乎是一种可能的情况。根据文档,它可用于更改流程模板。但是,我很好奇 tf.exe 移动语法是否可行?比如:

tf.exe 移动 $/ProjectA $/ProjectB

据我了解,此命令的操作很像重命名操作,而使用源代码管理资源管理器中的“移动”上下文菜单项移动更像是删除和添加操作。此外,假设 $/ProjectA 是一个项目的根源代码控制文件夹,而 $/ProjectB 是另一个项目的根源代码控制文件夹,tf.exe 移动路径实际上是否会将文件夹下的代码与相应的团队项目相关联?如果可能的话,关键是能够保存历史记录。

任何建议或提示将不胜感激!

编辑 - 是否可以分支到另一个项目来处理这种情况 - 就像 Microsoft 在 Branching Guidance 文档中讨论的那样?我认为这可能是答案,因为历史可能会与分支一起保存。但是,我目前无法访问 Team Foundation Server 2008 实例来对其进行测试。

【问题讨论】:

    标签: version-control tfs


    【解决方案1】:

    关于上面的原始命令:-

    Get-TfsChildItem $/ProjectA |
    select -Skip 1 |  # skip the root dir
    foreach {
        tf rename $_.serveritem $_.serveritem.replace("$/ProjectA", "$/ProjectB")
    }
    

    源名称和目标名称都需要用引号引起来,以防完整路径文件名中有空格。我发现很难在 $_.ServerItem 上执行此操作,因为用转义的 " 包围它会返回整个子对象,而不仅仅是 .serverItem 字符串。或者,如果我确实设法获取了字符串,我得到了不需要的回车,例如

    "
    $proj/文件夹/文件
    "

    最终我得到了使用以下命令的命令,但我发现历史仍然没有被传输,这就是重点!所以我相信这个命令直接相当于在源代码浏览器中单击鼠标右键并选择重命名(或移动)。

    $tfsServerString = "http://machine:8080/tfs/DefaultCollection"
    $tfs = Get-TfsServer $tfsServerString
    Get-TfsChildItem -server $tfs "$/Dest Project/MyTestProject" | select -Skip 1 | foreach { $sourceName = $_.serveritem; $targetName = $_.serveritem.replace("$/Dest Project/MyTestProject/", "$/Dest Project/Source/StoreControllers/dSprint/dSprint2/") ; ./tf rename `"$sourceName`" `"$targetName`" /login:myUser }
    

    另外注意,它需要使用反引号`来转义“

    【讨论】:

      【解决方案2】:

      另一个选择(我认为更简单)是导入到 Git,然后使用the Git-TF 命令行工具导出回 TFS。

      • 从二进制、Choclatey 或源代码安装 Git-TF。

      • 克隆一个 TFS 文件夹:

      git tf clone https://myAcc.visualstudio.com/mycollection $/TeamProjectA/Main --deep

      • 通过删除 .Git/tf 文件夹和 .Git/git-tf 文件来解除 Git 存储库与 TFS 服务器的关联。

      • 配置新的 Git 存储库以连接到 TFS 文件夹。

      git tf configure https://myAcc.visualstudio.com/mycollection $/TeamProjectB/Main --deep

      • 别忘了--deep

      git tf pull

      此时您应该会收到一条消息“git-tf:这是一个新配置的存储库。没有什么可以从 tfs 获取。”

      git commit -a -m "merge commit"

      git tf checkin --deep

      【讨论】:

      • 亲爱的 david004。我试过了,代码历史被保留了,但是所有的时间戳都设置为今天,所有的用户都变成了我。这是它的工作原理还是我错过了什么?
      • 是的,这很麻烦。我不知道如何避免这种情况,但我怀疑某种版本控制忍者可以为 TFS 编写一个脚本,提取和插入日期和作者信息。
      • TFS 中的时间戳是为审计完整性而设计的。如果您或其他人试图调整日期或时间,TFS 数据库可能会损坏或不被 TFS 服务信任(使其无法使用)。我希望 TFS 大师忍者可能能够破解任何审计完整性......但不要指望时间戳可以“通过正常方式”更改(根据 ISO 27001 审计/合规术语)。如果您证明不是这样,请在此处发表评论(使用脚本示例:-))。干杯!
      【解决方案3】:

      理查德上面的回答写得很好,很好地解释了情况。不过,我确实有几个更实用的问题要补充。

      在 TFS2010 中,默认行为看起来就像移动文件一样会导致您丢失移动前的所有历史记录。我的用户可能使用的命令(似乎是 VS2010 GUI 使用的命令)是:

      tf history $/ProjectB/somefile.cs
      

      我的用户打算获取 somefile.cs 的所有历史记录,包括移动之前和之后。他们想要“当前存储在 $/ProjectB/somefile.cs 中的代码的历史记录”,而不管文件名在任何时间点如何。也许其他人的看法不同。

      第一个问题是,使用 TFS2010 在 VS2010 中为我显示的 GUI 最初只显示移动后的历史记录。列表中最近的项目是重命名操作。它可以用一个微妙的小下拉箭头扩展。下面是上一个位置的历史记录。如果您不知道要查找此内容,那么您的历史可能看起来已经不复存在了。

      第二个问题是,如果您稍后删除 ProjectA(例如,因为您已经完成了到 ProjectB 的迁移),那么历史记录就真的消失了。展开 $/ProjectB/somefile.cs 历史记录中的下拉菜单不会产生较旧的历史记录。

      【讨论】:

      • 感谢您指出删除“ProjectA”的危险以及它如何影响视觉历史。
      • 感谢您通过变更集 ID 左侧的小箭头指出“最近”列表。我确实认为我已经失去了所有的历史:-(
      【解决方案4】:

      移动和重命名是别名。无论是命令行还是 UI,在任何版本的 TFS 中都绝对没有区别。

      它们都保留了历史。至少在 2005/2008 年,您在 VersionedItem 表中保留相同的物理项,无论名称和/或父路径更改的频率或剧烈程度如何。实际上,如果您不进行大量手动工作,就无法获得“假”重命名(删除 + 添加)。

      然而,虽然这种版本控制模型在理论上非常纯粹,但它也存在一些实际问题。因为不同的项目可以在不同的时间点占用相同的名称,所以 TFS 需要全名 + 版本来唯一标识你发送给它的任何输入。通常你不会注意到这个限制,但是一旦你在系统中重命名了项目,如果你说 tf [doSomething] $/newname -version:oldversion 那么它会混淆并抛出错误或对您可能不想要的项目进行操作。您必须小心传递有效的组合(新名称+新版本或旧名称+旧版本)以确保命令按照您想要的方式运行。

      TFS 2010 在某种程度上改变了故事:它是一个分支+删除幕后,导致 itemID 改变。即便如此,像 Get 和 History 这样的日常命令也被很好地“伪造”了。老客户大约 95% 兼容。优点是当您在系统中有多个重命名并且基于路径的项目查找开始变得不明确时,如上所述,服务器将简单地接受您指定的名称并使用它运行。这提高了整体系统性能并消除了不熟悉的用户经常陷入的几个陷阱,代价是不够灵活并且不能以 100% 的精度保存历史记录(例如,当两个分支合并期间出现名称冲突时)。

      回到手头的问题...

      不是说tf rename $/projectA $/projectB那么简单。源代码管理树中的顶级文件夹保留给团队项目创建向导;你不能对它们运行标准的 tf 命令。你需要的是这样的脚本:

      Get-TfsChildItem $/ProjectA |
          select -Skip 1 |  # skip the root dir
          foreach {
              tf rename $_.serveritem $_.serveritem.replace("$/ProjectA", "$/ProjectB")
          }
      

      [当然,如果$/ProjectA下的孩子不多的话,你也可以手工做]

      至于我提到的陷阱,我现在将详细说明一个,因为查找旧历史对您来说似乎非常重要。签入重命名后,tf history $/ProjectA/somefile.cs 将不起作用。默认情况下,tf 命令假定 version = "latest"。这些替代方案中的任何一个都将是您想要的完整历史记录:

      • tf history $/ProjectA/somefile.cs;1234 变更集 1234 在移动之前的位置
      • tf history $/ProjectB/somefile.cs;5678 其中变更集 5678 在移动之后。或者您可以省略版本。

      出于完整性和调试目的的最终替代方案:

      • tf 历史 $/ProjectA/somefile.cs -slotmode。您只会看到移动之前发生的变化;但是,您还会看到在您移动到 ​​B 下方的项目之前或之后可能存在于 $/ProjectA/somefile.cs“槽”中的任何其他项目的历史记录。

      (在 TFS 2010 中,“槽模式”是默认行为;有一个 -ItemMode 选项可请求像 2008 年那样跨历史跟踪您的查找,而不是基于路径。)

      编辑 - 不,分支不是一个很好的选择。虽然分支确实在系统中留下了足够的元数据来追踪与 ProjectB 之间的完整历史记录,但在 2008 年它的用户友好性并不高。计划花大量时间学习 tf merges 命令(没有等效的 UI )。 2010 显着提高了您在多个分支中可视化更改的能力,但它仍然不是您从重命名中获得的干净统一的体验。

      【讨论】:

      • 哇,理查德。绝对精彩的信息。明天我会在我的测试实验室试试。您提到了脚本,因为根是保留的。如果我从根目录以下级别的文件夹中进行重命名,我可以省略脚本,那么 - 对吗?无论哪种方式 - 很棒的答案。感谢您的宝贵时间!
      • 是的。基本思想是重命名 ProjectA\folder1 -> ProjectB\folder1、ProjectA\folder2 -> ProjectB\folder2 等。该脚本只是为了节省时间和精力,以防您有 50 个文件和文件夹。
      • 优秀。非常感谢您使这一点清晰易懂!
      • 嗨,理查德,感谢您的脚本。我真的想知道我怎么能运行这个?在我的 VS 控制台中,我似乎无法运行它...
      • 如果ProjectA在移动后被删除,那么ProjectB上的任何历史记录是否也会从ProjectB上的移动项目中消失?
      猜你喜欢
      • 2017-03-14
      • 1970-01-01
      • 2011-06-27
      • 1970-01-01
      • 2010-12-11
      • 1970-01-01
      • 2012-05-29
      • 2018-06-24
      • 2016-04-12
      相关资源
      最近更新 更多