了解如何从 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 存储库来托管转换后的 SVN 存储库,可以按照在项目中创建新的 Git 存储库进行操作

将源 SVN 存储库转换为本地 Git 存储库

此步骤的目标是将源 Subversion 存储库转换为本地的空 Git 存储库。 空 Git 存储库没有可更改文件的本地工作签出,而是只包含存储库的历史记录和有关存储库本身的元数据。 建议采用此格式来使用 Azure Repos 等服务上托管的远程存储库共享 Git 存储库。

提示

空 Git 存储库的结构不同,并且由于它没有工作目录,因此无法直接提交到存储库。

空 Git 存储库

检索所有 Subversion 作者的列表

Subversion 仅使用每次提交的用户名,而 Git 存储真实姓名和电子邮件地址。 默认情况下,git-svn 工具将在作者和电子邮件字段中列出 SVN 用户名。 但是,可以为 SVN 用户创建映射文件及其相应的 Git 名称和电子邮件。

Subversion 用户

Subversion 用户

Git 用户

Git 用户

若要从本地 Subversion 签出根目录提取所有 SVN 用户的列表,请运行以下 PowerShell 命令:

svn.exe log --quiet | ? { $_ -notlike '-*' } | % { "{0} = {0} <{0}>" -f ($_ -split ' \| ')[1] } | Select-Object -Unique | Sort-Object | Out-File 'authors-transform.txt' -Encoding utf8NoBOM

此命令将检索所有日志消息、提取用户名、消除任何重复用户名、对用户名进行排序,并将其放入 UTF-8 格式的 authors-transform.txt 文件中。 然后,可以编辑文件中的每一行,以创建 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/

如果使用标准 trunk、分支、标记布局,只需使用 --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 的 trunk 分支名称相匹配。

  1. 创建空 Git 存储库

    git init --bare c:\new-bare.git
    cd c:\new-bare.git
    git symbolic-ref HEAD refs/heads/svn/trunk
    
  2. 将本地 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 
    
  3. trunk 分支重命名为 main。 主开发分支将命名为“trunk”,与 Subversion 中对应项的名称匹配。 你需要使用以下命令将其重命名为 Git 的标准 main 分支:

    cd c:\new-bare.git
    git branch -m svn/trunk main
    
  4. 清理分支和标记 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 产品中任何知识产权的任何合法权利。 您可为了内部参考目的复制和使用本文档。