我写了一篇关于在导入前清理 TFS 的所有方法的详细文章:
但请记住,有一条备用路径需要在 Azure 中安装临时 IaaS 服务器,并在其上安装 SQL Server 和 TFS/Azure DevOps Server。这条路径可能比尝试清理你拥有的东西更简单、更快捷。
摘录如下:
要为迁移准备 TFS 项目集合,您可能需要先删除(陈旧的)旧数据以减小数据库大小。
大多数操作已在此处记录。可以帮助检测您的空间分配位置的查询也可以在最近的支持票中找到。
删除旧工作区
删除工作区和搁置集可以大大减少迁移和升级时间。使用 tf 命令行或利用 TFS SideKicks 之类的工具来识别和删除这些。
构建结果
不仅是构建结果,而且经常被忽略的实际构建记录可能会占用大量数据。使用 tfsbuild destroy (XAML) 永久删除构建记录。过去,我遇到过在他们的数据库中有 180 万个“隐藏”构建的客户,并且删除它们会刮掉相当多的数据。这些记录保存在仓库周围。
如果您遇到 tfsbuild 执行速度非常慢,you may need this patched version it has a few fixes that prevent it from trying to download all the build logs 为您系统中的每个构建,只是为了获取构建 ID。
旧团队项目
当然,销毁旧的团队项目可以回馈大量数据。您不需要发送到 azure 的任何内容都会有所帮助。您还可以考虑拆分集合并留下旧项目。如果您再次需要该数据,您可以选择分离该集合并将其存储在某个地方。
冗余文件
删除的分支是一种非常常见的隐藏大小猪。在 TFVC 中删除东西时,它们实际上并没有被删除,它们只是被隐藏了。查找已删除的文件,尤其是旧的开发或功能分支可以为您提供大量数据。使用tf destroy 摆脱它们。
您可能还想查找已签入的 nuget 包文件夹,这些文件夹也会很快占用大量空间。
代码镜头索引
Team Foundation Server 2013 引入了 TFVC 控制文件的服务器端索引,以允许 Visual Studio 直接在 UI 中访问有关谁更改了哪些文件的数据。这个服务器端索引可以根据您的代码库的大小和流失率快速增长。
您可以通过tfsconfig codeindex 命令控制索引。您可以指定要索引/indexHistoryPeriod:#months 多久之前,完全删除索引/destroyCodeIndex 或排除特定有问题的文件/ignoreList:add $/path。
Code Lens 在产品内部也被称为 Code Sense 和 Code Index。
More can be found here.
如果删除索引超时,you read this post on StackOverflow with additional guidance。请注意,不支持在 TFS 集合 DB 上手动运行 SQL。
测试附件
哦,是的,尤其是当您使用测试附件时,这些附件可能会变得疯狂,这取决于您的 TFS 版本,要么使用内置的测试附件清理功能,要么使用 TFS 电动工具中的测试附件清理器。
XAML 构建
构建定义本身不会占用大量数据库空间,但构建结果可能会。但这些内容已在上一节中介绍过。
过去,我不得不修补 tfbuid.exe 来处理(非常)大量的构建记录,因为它倾向于在继续删除操作之前尝试在本地获取所有构建数据。您可能需要依靠 TFS 客户端对象模型来获得类似的结果。
Git 存储库
您的 git 存储库中的数据可能由于强制推送或删除的分支而不再可访问。也有可能 Git 中的某些数据可以更有效地打包。要清理存储库,您必须在本地克隆它们,清理它们,从 TFS 中删除远程存储库并将清理后的副本推送到新存储库(您可以使用与旧存储库相同的名称)。这样做会破坏现有构建定义的引用,您将不得不修复它们。当您使用它时,您还可以运行 BFG repo Cleaner 并转换存储库以启用 Git-LFS 支持,从而更优雅地处理存储库中的大型二进制文件。
git clone --mirror https://tfs/project/repo
# optionally run BFG repo cleaner at this point
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git repack -adf
# Delete and recreate the remote repository with the same name
git push origin --all
git push origin --tags
工作项目(附件)
工作项可以收集大量数据,尤其是当人们开始将大型附件附加到它们时。您可以使用witadmin destroywi 删除附件过大的工作项。要保留工作项,但删除其附件,您可以从当前工作项中删除附件,然后克隆它。克隆后,销毁旧工作项以清理附件。
您不再需要的旧工作项目(例如 6 年前的 sprint 项目)也可以删除。 My colleague René has a nice tool that allows you to bulk-destroy by first creating the appropriate work item query.
确保运行清理作业
TFS 通常不会立即从数据库中删除数据,在许多情况下,它只是将内容标记为已删除以供以后处理。要强制立即进行清理,请在您的项目集合数据库上运行以下存储过程:
EXEC prc_CleanupDeletedFileContent 1
# You may have to run the following command multiple times, the last
# parameter is the batch size, if there are more items to prune than the
# passed in number, you will have to run it multiple times
EXEC prc_DeleteUnusedFiles 1, 0, 100000
最后的 100000 是要处理的标记项目的数量。如果您删除了很多内容,您可能需要运行最后一个过程几次,然后才会删除所有内容。
其他有用的查询
要确定每个部分中存储了多少数据,您可以运行一些有用的查询。实际查询取决于您的 TFS 版本,但由于您正在准备迁移,我怀疑您目前使用的是 TFS 2017 或 2018。
查找最大的表:
SELECT TOP 10
o.name,
SUM(reserved_page_count) * 8.0 / 1024 SizeInMB,
SUM(
CASE
WHEN p.index_id <= 1 THEN p.row_count
ELSE 0
END) Row_Count
FROM sys.dm_db_partition_stats p
JOIN sys.objects o
ON p.object_id = o.object_id
GROUP BY o.name
ORDER BY SUM(reserved_page_count) DESC
找到最大的内容贡献者:
SELECT Owner =
CASE
WHEN OwnerId = 0 THEN 'Generic'
WHEN OwnerId = 1 THEN 'VersionControl'
WHEN OwnerId = 2 THEN 'WorkItemTracking'
WHEN OwnerId = 3 THEN 'TeamBuild'
WHEN OwnerId = 4 THEN 'TeamTest'
WHEN OwnerId = 5 THEN 'Servicing'
WHEN OwnerId = 6 THEN 'UnitTest'
WHEN OwnerId = 7 THEN 'WebAccess'
WHEN OwnerId = 8 THEN 'ProcessTemplate'
WHEN OwnerId = 9 THEN 'StrongBox'
WHEN OwnerId = 10 THEN 'FileContainer'
WHEN OwnerId = 11 THEN 'CodeSense'
WHEN OwnerId = 12 THEN 'Profile'
WHEN OwnerId = 13 THEN 'Aad'
WHEN OwnerId = 14 THEN 'Gallery'
WHEN OwnerId = 15 THEN 'BlobStore'
WHEN OwnerId = 255 THEN 'PendingDeletion'
END,
SUM(CompressedLength) / 1024.0 / 1024.0 AS BlobSizeInMB
FROM tbl_FileReference AS r
JOIN tbl_FileMetadata AS m
ON r.ResourceId = m.ResourceId
AND r.PartitionId = m.PartitionId
WHERE r.PartitionId = 1
GROUP BY OwnerId
ORDER BY 2 DESC
如果文件容器是问题:
SELECT
CASE
WHEN Container = 'vstfs:///Buil' THEN 'Build'
WHEN Container = 'vstfs:///Git/' THEN 'Git'
WHEN Container = 'vstfs:///Dist' THEN 'DistributedTask'
ELSE Container
END AS FileContainerOwner,
SUM(fm.CompressedLength) / 1024.0 / 1024.0 AS TotalSizeInMB
FROM
(SELECT DISTINCT LEFT(c.ArtifactUri, 13) AS Container,
fr.ResourceId,
ci.PartitionId
FROM tbl_Container c
INNER JOIN tbl_ContainerItem ci
ON c.ContainerId = ci.ContainerId
AND c.PartitionId = ci.PartitionId
INNER JOIN tbl_FileReference fr
ON ci.fileId = fr.fileId
AND ci.DataspaceId = fr.DataspaceId
AND ci.PartitionId = fr.PartitionId) c
INNER JOIN tbl_FileMetadata fm
ON fm.ResourceId = c.ResourceId
AND fm.PartitionId = c.PartitionId
GROUP BY c.Container
ORDER BY TotalSizeInMB DESC