Team Foundation Server - 使用历史记录移动源

时间:2010-02-07 01:56:48

标签: version-control tfs

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

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

tf.exe移动$ / ProjectA $ / ProjectB

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

非常感谢任何建议或提示!

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

4 个答案:

答案 0 :(得分:38)

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

他们都保留了历史。至少在2005/2008中,无论名称和/或父路径发生变化的频率或频率如何,都会在VersionedItem表中保留相同的物理项。实际上没有办法在没有大量手工工作的情况下进行“假”重命名(删除+添加)。

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

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

回到手头的问题......

这并不像说 tf重命名$ / 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 其中changeset 5678在移动后。或者你可以省略版本。

完整性的最终替代方案&调试目的:

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

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

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

答案 1 :(得分:17)

理查德上面的答案写得很好并且很好地解释了情况。不过,我确实有一些更实际的问题需要补充。

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

tf history $/ProjectB/somefile.cs

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

第一个问题是,使用TFS2010在VS2010中为我显示的GUI最初只显示移动后的历史记录。列表中最近的项是重命名操作。它可以通过微妙的小下拉箭头进行扩展。下面是以前位置的历史记录。如果您不知道如何寻找,那么您的历史就会消失。

第二个问题是如果你以后删除ProjectA(因为你已经完成了向ProjectB的迁移,比如说),那么历史真的已经消失了。扩展$ / ProjectB / somefile.cs历史记录中的下拉列表不会产生较旧的历史记录。

答案 2 :(得分: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 Repo与TFS服务器取消关联。

  • 配置新的Git Repo以连接 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

答案 3 :(得分:0)

关于上面的原始命令: -

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 }

另请注意,它需要使用反引号`来逃避"