了解如何从 Subversion (SVN) 迁移到 Git(包括历史记录)
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
从另一个版本控制系统(如 Subversion (SVN))迁移到 Git 时,通常建议执行“提示迁移”,此操作仅迁移最新版本的存储库内容(不包括历史记录)。 但许多人希望进行更高级的迁移(包括历史记录)。 本文中提供的指南介绍了包含历史记录的迁移。
SVN 迁移到 Git 的复杂性可能会有所不同,具体取决于存储库的新旧、创建和合并的分支数,以及使用的是常规 SVN 还是有紧密关系的如 SVK。
在以下情况下,此迁移会更简单:
- 有一个新的存储库
- 有 trunk、分支和标记目录的标准设置
在以下情况下,此迁移会更复杂:
- 团队执行了大量分支和合并操作
- 存储库遵循非标准目录设置
- 目录设置随着时间推移发生了更改
可通过多种方式从 SVN 迁移到 Git。 本文中概述的方法基于使用 git-svn,此 Git 扩展可用于将 Subversion 存储库签出到本地 Git 存储库,然后将更改从本地 Git 存储库推送回 Subversion 存储库。 这些步骤详细概述了在 Windows 环境中从 SVN 迁移到 Git 的过程,而无需同步回原始 SVN 存储库。 结果将是一个空 Git 存储库,用于与团队的其余人员共享。
注意
在尝试将源代码从集中式版本控制系统迁移到 Git 之前,请确保熟悉集中式版本控制系统和分布式版本控制系统之间的差异,并计划团队的迁移。 准备好后,即可开始迁移。
从 SVN 迁移到 Git 的大致工作流如下所示:
- 准备迁移环境
- 将源 SVN 存储库转换为本地 Git 存储库
- (可选)在开发人员继续使用 SVN 时,将本地 Git 存储库与 SVN 存储库中的任何更改同步
- 将本地 Git 存储库推送到在 Azure Repos 上托管的远程 Git 存储库
- 锁定 SVN 存储库,将 SVN 存储库中的任何剩余更改同步到本地 Git 存储库,并将最终更改推送到 Azure Repos 中的远程 Git 存储库
- 开发人员改用 Git 作为主要源代码管理系统
准备迁移环境
在本地工作站配置迁移环境并安装以下软件:
- Git
- Subversion
- git-svn 实用工具(属于 Git)
还需要为组织创建 Git 存储库来托管转换后的 SVN 存储库,可以遵循 在项目中创建新的 Git 存储库
将源 SVN 存储库转换为本地 Git 存储库
此步骤的目标是将源 Subversion 存储库转换为本地的空 Git 存储库。 裸 Git 存储库没有可更改的本地工作签出文件,而是仅包含存储库的历史记录和存储库本身的元数据。 建议采用此格式来使用 Azure Repos 等服务上托管的远程存储库共享 Git 存储库。
提示
空 Git 存储库的结构不同,并且由于它没有工作目录,因此无法直接提交到存储库。
检索所有 Subversion 作者的列表
Subversion 仅使用每次提交的用户名,而 Git 存储真实姓名和电子邮件地址。 默认情况下,git-svn 工具将在作者和电子邮件字段中列出 SVN 用户名。 但是,可以为 SVN 用户创建映射文件及其相应的 Git 名称和电子邮件。
Subversion 用户
Git 用户
若要从本地 Subversion 签出根目录提取所有 SVN 用户的列表,请运行以下 PowerShell 命令:
对于编码结果 utf8NoBOM
,请运行以下命令:
svn.exe log --quiet | ? { $_ -notlike '-*' } | % { "{0} = {0} " -f ($_ -split ' | ')[1] } | Select-Object -Unique | Sort-Object | Out-File 'authors-transform.txt' -Encoding utf8NoBOM
对于编码结果 ASCII
,请运行以下命令:
svn.exe log --quiet | ? { $_ -notlike '-*' } | % { "{0} = {0} " -f ($_ -split ' | ')[1] } | Select-Object -Unique | Sort-Object | Out-File 'authors-transform.txt' -Encoding ASCII
此命令将检索所有日志消息,提取用户名,消除任何重复的用户名,对用户名进行排序,并将其置于 UTF-8 格式的 authors-transform.txt 文件中(或 ASCII 格式,具体取决于指定的编码)。 然后,可以编辑文件中的每一行,以创建 SVN 用户到格式正确的 Git 用户的映射。 例如,可将 jamal = jamal <jamal>
映射到 jamal = Jamal Hartnett <jamal@fabrikam-fiber.com>
。
使用 git-svn 克隆 Subversion 存储库
以下命令将使用在上一步中创建的 authors-transform.txt 文件执行标准 git-svn 转换。 它将 Git 存储库放置在本地计算机的 c:\mytempdir
文件夹中。
git svn clone ["SVN repo URL"] --prefix=svn/ --no-metadata --authors-file "authors-transform.txt" --stdlayout c:\mytempdir
注意
--prefix=svn/
是必需的,否则工具无法从导入的修订中判断 SVN 修订。 建议设置一个前缀(带有尾部斜杠),原因是因为 SVN 跟踪引用将位于 refs/remotes/$prefix/
,这与 Git 自己的远程跟踪分支布局 (refs/remotes/$remote/
) 兼容。
如果要跟踪共享通用存储库的多个项目,设置前缀也很有用。 默认情况下,前缀设置为 origin/
。
如果使用标准中继、分支、标记布局,则只需放置 --stdlayout
。 但是,如果使用的是其他布局,则可能需要传递 --trunk
、--branches
和 --tags
来找到具体是什么。 例如,如果存储库结构为 trunk/companydir
,并且对存储库使用的是分支而不是 trunk,则可能需要使用 --trunk=trunk/companydir --branches=branches
。
git svn clone ["SVN repo URL"] --prefix=svn/ --no-metadata --trunk=/trunk --branches=/branches --tags=/tags --authors-file "authors-transform.txt" c:\mytempdir
注意
此命令可能需要几分钟到几个小时,具体取决于 SVN 存储库的大小。 完成后,你将拥有存储库的 Git 签出。
转换版本控制特定的配置
如果 SVN 存储库使用 svn:ignore 属性,则可以使用以下命令转换为 .gitignore 文件:
cd c:\mytempdir
git svn show-ignore --id=origin/trunk > .gitignore
git add .gitignore
git commit -m 'Convert svn:ignore properties to .gitignore.'
提示
阅读有关 .gitignore 的更多信息:使用 Git 忽略文件更改
将存储库推送到空 Git 存储库
在此步骤中,你将创建一个裸存储库,使其默认分支与 SVN 的中继分支名称匹配。
创建空 Git 存储库
git init --bare c:\new-bare.git cd c:\new-bare.git git symbolic-ref HEAD refs/heads/svn/trunk
将本地 Git 存储库推送到新的空 Git 存储库
cd c:\mytempdir git remote add bare c:\new-bare.git git config remote.bare.push refs/remotes/*:refs/heads/* git push bare
将
trunk
分支重命名为main
。 主开发分支将命名为“trunk”,与 Subversion 中对应项的名称匹配。 你需要使用以下命令将其重命名为 Git 的标准main
分支:cd c:\new-bare.git git branch -m svn/trunk main
清理分支和标记 git-svn 会将所有 Subversions 标记转换为 Git 中非常简短的分支,格式为“tags/name”。 需要将所有这些分支转换为实际的 Git 标记或将其删除。
将 SVN 标记迁移到 Git 标记
cd c:\new-bare.git
git for-each-ref --format='%(refname)' refs/heads/svn/tags | % { $_.Replace('refs/heads/svn/tags/','') } | % { git tag $_ "refs/heads/svn/tags/$_"; git branch -D "svn/tags/$_" }
高级迁移
将所有 SVN 分支创建为适当的 Git 分支
虽然将所有 SVN 分支创建为适当的 Git 分支很容易,但建议先评估以下几点,然后再继续:
如果有功能分支:是否可以等到它们集成到 trunk 再迁移?
如果有发布分支:保留 SVN 进行维护是否有意义? 如果迁移功能分支,是否准备好从 Git 中提供分支维护?
如果仍要迁移现有分支,请运行以下 PowerShell 命令:
git for-each-ref --format='%(refname)' refs/remotes | % { $_.Replace('refs/remotes/','') } | % { git branch "$_" "refs/remotes/$_"; git branch -r -d "$_"; }
注意
此命令可能需要几分钟到几个小时,具体取决于 SVN 存储库的大小。 完成后,你将拥有存储库的 Git 签出。
仅迁移特定修订
如果未指定,git-svn clone
会将第一个提交 (r1) 中的所有修订迁移到 HEAD。 如果只需要迁移一组特定的修订,应将 git-svn clone
的命令追加到 -r
选项中。
例如,如果需要从 rev 100 迁移到 HEAD,则命令如下所示:
git svn clone ["SVN repo URL"] --prefix=svn/ --no-metadata --authors-file "authors-transform.txt" --stdlayout c:\mytempdir -r100:HEAD
更新工作流
从集中式版本控制系统迁移到 Git 不仅仅是迁移代码。 团队需要通过培训来了解 Git 与现有版本控制系统的差异,以及这些差异如何影响日常工作。 了解详细信息。
参考信息
作者:Hosam Kamel 和 William H. Salazar |查找本文来源和联系 ALM | DevOps Rangers 分支指南
(c) 2017 Microsoft Corporation。 保留所有权利。 本文档“按原样”提供。本文档中表达的信息和观点(包括 URL 和其他 Internet 网站引用)如有更改,恕不另行通知。 您自行承担其使用风险。
本文档不提供针对任何 Microsoft 产品中任何知识产权的任何法律权利。 您可为了内部参考目的复制和使用本文档。