团队基础服务器 – 带历史logging的移动源

我想知道在将源代码和历史从一个团队项目移植到另一个团队项目时,最好的方法是什么。 我不关心工作项目,报告或SharePoint网站,因为我们要从中恢复的系统没有使用这些function。 想要迁移到另一个团队项目的原因也是由于原始实施(由第三方维护的备份恢复)正在使用我们不希望使用的第三方stream程模板向前走。 我们希望在迁移完成后开始使用工作项目跟踪和报告。

TFS集成平台似乎是一个可能的情况。 根据文档,可以使用它来更改stream程模板。 但是,我很好奇,如果tf.exe移动语法可能工作? 就像是:

tf.exe移动$ / ProjectA $ / ProjectB

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

任何意见或提示将不胜感激!

编辑 – 可以分支到另一个项目处理这种情况 – 很像微软在分支指导文档中讨论的? 我认为这可能是答案,因为历史可能会与分支保存。 但是,我目前无法访问Team Foundation Server 2008实例来testing它。

移动和重命名是别名。 在任何版本的TFS中,从命令行或用户界面完全没有区别。

他们两人保存历史。 至less在2005/2008年,无论名称和/或父path多么频繁地或多剧烈地变化,您都将相同的物理项目保留在VersionedItem表中。 实际上没有办法得到一个“假”重命名(删除+添加),没有大量的手工工作。

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

TFS 2010改变了这个故事:它是一个分支+在封面下删除,导致itemID改变。 即便如此,像“Get”和“History”这样的日常命令也是“伪造”的。 老客户约95%兼容。 好处是,当系统中有多个重命名并且基于path的项目查找开始变得模糊不清时,服务器将只接受您指定的名称并与之一起运行。 这提高了整个系统的性能,并消除了不熟悉的用户经常陷入的几个陷阱,代价是不够灵活,并且不能保证100%精度的历史logging(例如,在两个分支合并期间有名称冲突)。

回到手头的问题

这不像说tf重命名$ / projectA $ / projectB那么简单。 源代码pipe理树中的顶级文件夹为团队项目创build向导保留; 你不能针对它们运行标准的tf命令。 你需要的是一个脚本,如:

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

[当然,如果$ / ProjectA下没有太多的孩子,可以手工做]

至于我提到的疑难问题,我现在要详细说明一下,因为查看旧的历史对你来说似乎非常重要。 一旦你签入重命名, 历史$ / ProjectA / somefile.cs将无法正常工作。 默认情况下,tf命令假定版本=“最新”。 这些select中的任何一个都将是你想要的完整历史:

  • tf历史$ / ProjectA / somefile.cs; 1234其中changeset 1234在移动之前
  • tf历史$ / ProjectB / somefile.cs; 5678其中changeset 5678是在移动之后。 或者你可以忽略版本。

完整性和debugging目的的最后一种select:

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

(在TFS 2010中,“插槽模式”是默认行为;有一个-ItemMode选项可以请求查找跨历史logging,而不是基于path)。

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

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

在TFS2010中,默认行为使得移动文件看起来像是在移动之前丢失了所有的历史logging。 我的用户可能使用的命令(以及VS2010 GUI使用的命令)是:

 tf history $/ProjectB/somefile.cs 

我的用户打算在移动之前和之后获取somefile.cs的所有历史logging。 他们希望“当前存储在$ / ProjectB / somefile.cs中的代码的历史”,而不pipe文件名是否在任何时间点。 也许其他人看到它不同。

第一个问题是,使用TFS2010在VS2010中显示的graphics用户界面仅显示移动以来的历史logging。 列表中最近的项目是重命名操作。 它可以用一个微妙的下拉箭头进行扩展。 下面是从以前的位置的历史。 如果你不知道如何去寻找这个东西,那么你的历史已经消失了。

第二个问题是,如果你以后删除了ProjectA(因为你已经完成了ProjectB的迁移),那么历史真的没有了。 展开$ / ProjectB / somefile.cs历史logging中的下拉列表不会生成较早的历史logging。

另一种select(我认为更容易)是导入到Git,然后使用Git-TF命令行工具导出回到TFS。

  • 从二进制,巧克力或源代码安装Git-TF。

  • 克隆一个TFS文件夹:

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

  • 通过删除.Git/tf文件夹和.Git/git-tf文件,从TFS服务器分离Git Repo。

  • configuration新的Git回购连接到一个空的 TFS文件夹。

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

  • 不要忘记 – --deep

git tf pull

你应该在这里得到一个消息“git-tf:这是一个新configuration的仓库,没有任何东西可以从tfs中获取。

git commit -a -m "merge commit"

git tf checkin --deep

关于上面的原始命令:

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

如果完整path文件名中有任何空格,则源名称和目标名称都需要用引号括起来。 我发现很难这样做$ _。ServerItem作为围绕它与逃脱“返回整个子对象,而不仅仅是.serverItemstring。或者,如果我设法得到string,我得到不需要的回车,如


$ PROJ /文件夹/文件

最终我得到了下面的命令,但是我发现历史依然没有被转移,这是关键所在! 所以我相信这个命令是在源代码资源pipe理器中使用右键单击并select重命名(或移动)的直接等价物。

 $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 } 

另外请注意,它需要使用倒退“逃脱”