简体中文 ▾ 主题 ▾ 最新版本 ▾ git-svn 最后更新于 2.52.0

名称

git-svn - Subversion 仓库与 Git 之间的双向操作

概要

git svn <command> [<options>] [<arguments>]

描述

git svn 是 Subversion 和 Git 之间变更集的简单管道。它在 Subversion 和 Git 仓库之间提供双向的变更流。

通过 --stdlayout 选项,git svn 可以跟踪标准的 Subversion 仓库,遵循常见的“trunk/branches/tags”布局。它也可以通过 -T/-t/-b 选项跟踪任何布局的分支和标签(参见下文 init 的选项,以及 clone 命令)。

一旦开始跟踪 Subversion 仓库(使用上述任何方法),就可以通过 fetch 命令从 Subversion 更新 Git 仓库,并通过 dcommit 命令从 Git 更新 Subversion。

命令

init

初始化一个空的 Git 仓库,并为 git svn 添加额外的元数据目录。Subversion URL 可以作为命令行参数指定,也可以作为 -T/-t/-b 的完整 URL 参数。可选地,可以将要操作的目标目录指定为第二个参数。通常此命令会初始化当前目录。

-T<trunk-subdir>
--trunk=<trunk-subdir>
-t<tags-subdir>
--tags=<tags-subdir>
-b<branches-subdir>
--branches=<branches-subdir>
-s
--stdlayout

这些是 init 的可选命令行选项。每个标记都可以指向相对仓库路径(--tags=project/tags)或完整 URL(--tags=https://foo.org/project/tags)。如果您的 Subversion 仓库将标签或分支置于多个路径下,您可以指定多个 --tags 和/或 --branches 选项。选项 --stdlayout 是将 trunk,tags,branches 设置为相对路径的简写,这是 Subversion 的默认设置。如果同时给出了其他任何选项,则以其他选项为准。

--no-metadata

在 [svn-remote] 配置中设置 noMetadata 选项。不推荐使用此选项,在使用此选项之前,请阅读本手册页的 svn.noMetadata 章节。

--use-svm-props

在 [svn-remote] 配置中设置 useSvmProps 选项。

--use-svnsync-props

在 [svn-remote] 配置中设置 useSvnsyncProps 选项。

--rewrite-root=<URL>

在 [svn-remote] 配置中设置 rewriteRoot 选项。

--rewrite-uuid=<UUID>

在 [svn-remote] 配置中设置 rewriteUUID 选项。

--username=<user>

对于由 SVN 处理身份验证的传输协议(http、https 和纯 svn),指定用户名。对于其他传输协议(例如 svn+ssh://),必须在 URL 中包含用户名,例如 svn+ssh://foo@svn.bar.com/project

--prefix=<prefix>

如果指定了 trunk/branches/tags,此选项允许指定一个前缀,该前缀将添加到远程名称之前。该前缀不会自动包含结尾斜杠,因此如果需要,请确保在参数中包含斜杠。如果指定了 --branches/-b,则前缀必须包含结尾斜杠。在任何情况下,强烈建议设置前缀(带有结尾斜杠),因为这样您的 SVN 跟踪引用将位于 "refs/remotes/$prefix/",这与 Git 自身的远程跟踪引用布局(refs/remotes/$remote/)兼容。如果您希望跟踪共享同一仓库的多个项目,设置前缀也很有用。默认情况下,前缀设置为 origin/

注意
在 Git v2.0 之前,默认前缀是 ""(无前缀)。这意味着 SVN 跟踪引用被放置在 "refs/remotes/*",这与 Git 自身远程跟踪引用的组织方式不兼容。如果您仍然想要旧的默认设置,可以通过在命令行中传递 --prefix "" 来获取(如果您的 Perl Getopt::Long 版本低于 v2.37,--prefix="" 可能不起作用)。
--ignore-refs=<regex>

当传递给 initclone 时,此正则表达式将作为配置键保留。有关 --ignore-refs 的描述,请参阅 fetch

--ignore-paths=<regex>

当传递给 initclone 时,此正则表达式将作为配置键保留。有关 --ignore-paths 的描述,请参阅 fetch

--include-paths=<regex>

当传递给 initclone 时,此正则表达式将作为配置键保留。有关 --include-paths 的描述,请参阅 fetch

--no-minimize-url

在跟踪多个目录(使用 --stdlayout、--branches 或 --tags 选项)时,git svn 将尝试连接到 Subversion 仓库的根目录(或允许的最高级别)。如果整个项目在仓库内移动,此默认设置允许更好地跟踪历史记录,但在实施了读取访问限制的仓库上可能会导致问题。传递 --no-minimize-url 将允许 git svn 按原样接受 URL,而不尝试连接到更高级别的目录。当仅跟踪一个 URL/分支时,此选项默认关闭(因为用处不大)。

fetch

从我们跟踪的 Subversion 远程仓库获取未获取的修订版本。$GIT_DIR/config 文件中 [svn-remote "…​"] 部分的名称可以作为可选的命令行参数指定。

如果需要,这会自动更新 rev_map(详情请参阅下面“文件”章节中的 $GIT_DIR/svn/**/.rev_map.*)。

--localtime

以本地时区而非 UTC 存储 Git 提交时间。这使得 git log(即使不带 --date=local)显示的时间与 svn log 在本地时区显示的时间一致。

这不会干扰与您克隆自的 Subversion 仓库的互操作,但如果您希望您的本地 Git 仓库能够与他人的本地 Git 仓库互操作,请不要使用此选项,或者你们应该都在相同的本地时区使用它。

--parent

仅从当前 HEAD 的 SVN 父节点获取。

--ignore-refs=<regex>

忽略与 Perl 正则表达式匹配的分支或标签引用。可以使用像 ^refs/remotes/origin/(?!tags/wanted-tag|wanted-branch).*$ 这样的“负向先行断言”来仅允许特定的引用。

config key: svn-remote.<name>.ignore-refs

如果设置了 ignore-refs 配置键,并且也给出了命令行选项,则两个正则表达式都将被使用。

--ignore-paths=<regex>

这允许指定一个 Perl 正则表达式,该表达式将导致从 SVN 检出时跳过所有匹配的路径。--ignore-paths 选项应该在给定仓库的每次 fetch(包括由 clonedcommitrebase 等触发的自动获取)中都匹配。

config key: svn-remote.<name>.ignore-paths

如果设置了 ignore-paths 配置键,并且也给出了命令行选项,则两个正则表达式都将被使用。

示例

每次获取时跳过 "doc*" 目录
--ignore-paths="^doc"
跳过一级目录下的 "branches" 和 "tags"
--ignore-paths="^[^/]+/(?:branches|tags)"
--include-paths=<regex>

这允许指定一个 Perl 正则表达式,该表达式将导致从 SVN 检出时仅包含匹配的路径。--include-paths 选项应该在给定仓库的每次 fetch(包括由 clonedcommitrebase 等触发的自动获取)中都匹配。--ignore-paths 优先级高于 --include-paths

config key: svn-remote.<name>.include-paths
--log-window-size=<n>

扫描 Subversion 历史记录时,每次请求获取 <n> 条日志条目。默认值为 100。对于非常大的 Subversion 仓库,可能需要更大的值才能使 clone/fetch 在合理的时间内完成。但过大的值可能会导致更高的内存使用量和请求超时。

clone

运行 initfetch。它将根据传递给它的 URL 的基名自动创建一个目录;或者如果传递了第二个参数,它将创建一个目录并在其中工作。它接受 initfetch 命令接受的所有参数,但 --fetch-all--parent 除外。克隆仓库后,fetch 命令将能够更新修订版本而不影响工作树,而 rebase 命令将能够使用最新更改更新工作树。

--preserve-empty-dirs

为从 Subversion 获取的每个空目录在本地 Git 仓库中创建一个占位文件。这包括由于删除 Subversion 仓库中所有条目(但未删除目录本身)而变空的目录。占位文件也会被跟踪,并在不再需要时被删除。

--placeholder-filename=<filename>

设置由 --preserve-empty-dirs 创建的占位文件的名称。默认值:".gitignore"

rebase

这从当前 HEAD 的 SVN 父节点获取修订版本,并将当前(未提交到 SVN)的工作针对其进行变基(rebase)。

这与 svn updategit pull 类似,不同之处在于它使用 git rebase 而非 git merge 来保持线性历史,以便于使用 git svn dcommit 提交。

这接受 git svn fetchgit rebase 接受的所有选项。但是,--fetch-all 仅从当前 [svn-remote] 获取,而不是从所有 [svn-remote] 定义中获取。

git rebase 一样,这要求工作树是干净的,没有未提交的更改。

如果需要,这会自动更新 rev_map(详情请参阅下面“文件”章节中的 $GIT_DIR/svn/**/.rev_map.*)。

-l
--local

不进行远程获取;仅针对从上游 SVN 获取的最后一个提交运行 git rebase

dcommit

将当前分支的每个差异直接提交到 SVN 仓库,然后进行变基或重置(取决于 SVN 和 head 之间是否有差异)。这将在 SVN 中为 Git 中的每个提交创建一个修订版本。

当指定可选的 Git 分支名称(或 Git 提交对象名称)作为参数时,子命令将在指定的分支上操作,而不是在当前分支上。

推荐使用 dcommit 而非 set-tree(见下文)。

--no-rebase

提交后,不进行变基或重置。

--commit-url <URL>

提交到此 SVN URL(完整路径)。这旨在允许重用通过一种传输方式(例如 svn://http:// 用于匿名读取)创建的现有 git svn 仓库,如果用户稍后获得了另一种提交传输方式(例如 svn+ssh://https://)的访问权限。

config key: svn-remote.<name>.commiturl
config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)

请注意,commiturl 配置键的 SVN URL 包含 SVN 分支。如果您想为整个 SVN 仓库设置提交 URL,请改用 svn-remote.<name>.pushurl。

非常强烈建议不要将此选项用于任何其他目的(别问为什么)。

--mergeinfo=<mergeinfo>

在 dcommit 期间添加给定的合并信息(例如 --mergeinfo="/branches/foo:1-10")。所有 svn 服务器版本都可以存储此信息(作为属性),1.5 及更高版本的 svn 客户端可以利用它。要指定来自多个分支的合并信息,请在分支之间使用单个空格字符(--mergeinfo="/branches/foo:1-10 /branches/bar:3,5-6,8"

config key: svn.pushmergeinfo

此选项将导致 git-svn 尝试在可能的情况下自动填充 SVN 仓库中的 svn:mergeinfo 属性。目前,这只能在提交非快进合并时完成,且除第一个父节点外的所有父节点都已推送到 SVN。

--interactive

要求用户确认是否应将补丁集发送到 SVN。对于每个补丁,可以回答 "yes"(接受此补丁)、"no"(丢弃此补丁)、"all"(接受所有补丁)或 "quit"。

如果回答是 "no" 或 "quit",git svn dcommit 会立即返回,不向 SVN 提交任何内容。

branch

在 SVN 仓库中创建一个分支。

-m
--message

允许指定提交消息。

-t
--tag

通过使用 tags_subdir 而不是 git svn init 期间指定的 branches_subdir 来创建一个标签。

-d<path>
--destination=<path>

如果向 initclone 命令提供了多个 --branches(或 --tags)选项,则必须提供您希望在 SVN 仓库中创建的分支(或标签)的位置。<path> 指定用于创建分支或标签的路径,并且应与配置的分支或标签 refspec 之一的左侧模式相匹配。您可以使用以下命令查看这些 refspec:

git config --get-all svn-remote.<name>.branches
git config --get-all svn-remote.<name>.tags

其中 <name> 是通过 init 的 -R 选项指定的 SVN 仓库名称(默认为 "svn")。

--username

指定执行提交的 SVN 用户名。此选项会覆盖 username 配置属性。

--commit-url

使用指定的 URL 连接到目标 Subversion 仓库。这在源 SVN 仓库为只读的情况下很有用。此选项会覆盖配置属性 commiturl

git config --get-all svn-remote.<name>.commiturl
--parents

创建父文件夹。此参数相当于 svn cp 命令中的参数 --parents,对于非标准仓库布局非常有用。

tag

在 SVN 仓库中创建一个标签。这是 branch -t 的简写。

log

当 SVN 用户引用 -r/--revision 编号时,这应该可以方便地查找 svn 日志消息。

支持来自 ‘svn log’ 的以下功能

-r <n>[:<n>]
--revision=<n>[:<n>]

支持数字参数,不支持非数字参数:HEAD、NEXT、BASE、PREV 等 …​

-v
--verbose

它与 svn log 中的 --verbose 输出并不完全兼容,但相当接近。

--limit=<n>

与 --max-count 不一样,不计算已合并/已排除的提交

--incremental

支持

新特性

--show-commit

同时也显示 Git 提交的 sha1

--oneline

我们的 --pretty=oneline 版本

注意
SVN 本身仅以 UTC 形式存储时间。常规 svn 客户端会将 UTC 时间转换为本地时间(或基于 TZ= 环境)。此命令具有相同的行为。

任何其他参数都将直接传递给 git log

blame

显示文件的每一行最后是由哪个修订版本和作者修改的。默认情况下,此模式的输出格式与 ‘svn blame’ 的输出兼容。与 SVN blame 命令一样,工作树中本地未提交的更改将被忽略;将对 HEAD 修订版本中的文件版本进行注释。未知参数直接传递给 git blame

--git-format

以与 git blame 相同的格式生成输出,但使用 SVN 修订号而不是 Git 提交哈希。在这种模式下,尚未提交到 SVN 的更改(包括本地工作副本编辑)显示为修订版本 0。

find-rev

当给出 rN 形式的 SVN 修订号时,返回对应的 Git 提交哈希(后面可以选接一个 tree-ish 以指定应搜索的分支)。当给出 tree-ish 时,返回对应的 SVN 修订号。

-B
--before

如果给出了 SVN 修订版本,不要求精确匹配,而是查找与指定修订版本时 SVN 仓库状态(在当前分支上)相对应的提交。

-A
--after

如果给出了 SVN 修订版本,不要求精确匹配;如果没有精确匹配,则通过在历史记录中向前搜索返回最接近的匹配。

set-tree

您应该考虑使用 dcommit 而不是此命令。将指定的提交或树对象提交到 SVN。这依赖于您导入的获取数据是最新的。在向 SVN 提交时,它绝不尝试进行补丁操作,它只是简单地用树或提交中指定的文件覆盖文件。所有合并都被假定为在 git svn 功能之外独立进行。

create-ignore

递归查找目录上的 svn:ignore 和 svn:global-ignores 属性,并创建匹配的 .gitignore 文件。生成的文件被暂存待提交,但尚未提交。使用 -r/--revision 引用特定修订版本。

show-ignore

递归查找并列出目录上的 svn:ignore 和 svn:global-ignores 属性。输出适合追加到 $GIT_DIR/info/exclude 文件。

mkdirs

尝试根据 $GIT_DIR/svn/<refname>/unhandled.log 文件中的信息,重新创建 Git 核心无法跟踪的空目录。使用 "git svn clone" 和 "git svn rebase" 时会自动重新创建空目录,因此 "mkdirs" 旨在用于 "git checkout" 或 "git reset" 等命令之后。(有关更多信息,请参阅 svn-remote.<name>.automkdirs 配置文件选项。)

commit-diff

从命令行提交两个 tree-ish 参数的差异。此命令不依赖于处于经过 git svn init 初始化的仓库中。此命令接受三个参数:(a) 要进行差异对比的原始树,(b) 新的结果树,(c) 目标 Subversion 仓库的 URL。如果您在支持 git svn 的仓库中工作(已使用 git svn init 初始化),则可以省略最后一个参数(URL)。此操作需要使用 -r<修订版本> 选项。

提交消息可以直接通过 -m-F 选项提供,或者当第二个 tree-ish 代表此类对象时间接从标签或提交中获取,或者通过调用编辑器请求(见下文 --edit 选项)。

-m <msg>
--message=<msg>

使用给定的 msg 作为提交消息。此选项禁用 --edit 选项。

-F <filename>
--file=<filename>

从给定文件中提取提交消息。此选项禁用 --edit 选项。

info

显示关于文件或目录的信息,类似于 ‘svn info’ 提供的信息。目前不支持 -r/--revision 参数。使用 --url 选项仅输出 URL: 字段的值。

proplist

列出 Subversion 仓库中存储的关于给定文件或目录的属性。使用 -r/--revision 引用特定的 Subversion 修订版本。

propget

获取文件的作为第一个参数给出的 Subversion 属性。可以用 -r/--revision 指定特定修订版本。

propset

对于第三个参数给出的文件,将第一个参数给出的 Subversion 属性设置为第二个参数给出的值。

示例

git svn propset svn:keywords "FreeBSD=%H" devel/py-tipper/Makefile

这将为文件 devel/py-tipper/Makefile 设置属性 svn:keywordsFreeBSD=%H

show-externals

显示 Subversion 外部项。使用 -r/--revision 指定特定修订版本。

gc

压缩 $GIT_DIR/svn/<refname>/unhandled.log 文件并删除 $GIT_DIR/svn/<refname>/index 文件。

reset

撤销 fetch 的效果,回到指定的修订版本。这允许您重新 fetch 一个 SVN 修订版本。通常 SVN 修订版本的内容永远不会改变,因此 reset 应该是不必要的。但是,如果 SVN 权限发生变化,或者您修改了 --ignore-paths 选项,fetch 可能会失败,提示 "not found in commit"(文件此前不可见)或 "checksum mismatch"(错过了某次修改)。如果问题文件无法被永久忽略(通过 --ignore-paths),修复仓库的唯一方法就是使用 reset

仅修改 rev_map 和 refs/remotes/git-svn(详情请参阅下面“文件”章节中的 $GIT_DIR/svn/**/.rev_map.*)。在 reset 之后执行 fetch,然后执行 git resetgit rebase 将本地分支移动到新树上。

-r <n>
--revision=<n>

指定要保留的最早修订版本。所有后续修订版本都将被丢弃。

-p
--parent

同时也丢弃指定的修订版本,保留最近的父修订版本。

示例

假设你在 "master" 分支有本地更改,但你需要重新获取 "r2"。

    r1---r2---r3 remotes/git-svn
                \
                 A---B master

首先修复导致 "r2" 不完整的 ignore-paths 或 SVN 权限问题。然后:

git svn reset -r2 -p
git svn fetch
    r1---r2'--r3' remotes/git-svn
      \
       r2---r3---A---B master

然后使用 git rebase 修正 "master"。不要使用 git merge,否则您的历史记录将与未来的 dcommit 不兼容!

git rebase --onto remotes/git-svn A^ master
    r1---r2'--r3' remotes/git-svn
                \
                 A'--B' master

选项

--shared[=(false|true|umask|group|all|world|everybody)]
--template=<template-directory>

仅用于 init 命令。这些直接传递给 git init

-r <arg>
--revision <arg>

用于 fetch 命令。

这允许支持部分/截断历史的修订范围。支持 $NUMBER、$NUMBER1:$NUMBER2(数字范围)、$NUMBER:HEAD 以及 BASE:$NUMBER。

这可以在运行 fetch 时创建部分镜像;但通常不推荐这样做,因为历史记录会被跳过并丢失。

-
--stdin

仅用于 set-tree 命令。

从 stdin 读取提交列表并按相反顺序提交。每行仅读取开头的 sha1,因此可以使用 git rev-list --pretty=oneline 的输出。

--rmdir

仅用于 dcommitset-treecommit-diff 命令。

如果目录中没有文件残留,则从 SVN 树中删除该目录。SVN 可以对空目录进行版本控制,默认情况下,如果没有文件残留,它们不会被删除。Git 无法对空目录进行版本控制。启用此标志将使对 SVN 的提交行为像 Git 一样。

config key: svn.rmdir
-e
--edit

仅用于 dcommitset-treecommit-diff 命令。

在提交到 SVN 之前编辑提交消息。对于提交对象,这默认是关闭的;而在提交树对象时是强制开启的。

config key: svn.edit
-l<num>
--find-copies-harder

仅用于 dcommitset-treecommit-diff 命令。

它们都直接传递给 git diff-tree;有关更多信息,请参阅 git-diff-tree[1]

config key: svn.l
config key: svn.findcopiesharder
-A<filename>
--authors-file=<filename>

语法与 git cvsimport 使用的文件兼容,但可以使用 <> 提供空电子邮件地址。

	loginname = Joe User <user@example.com>

如果指定了此选项且 git svn 遇到作者文件中不存在的 SVN 提交者姓名,git svn 将终止操作。用户随后必须添加相应的条目。在修改作者文件后重新运行之前的 git svn 命令应会继续操作。

config key: svn.authorsfile
--authors-prog=<filename>

如果指定了此选项,对于作者文件中不存在的每个 SVN 提交者姓名,将执行给定文件,并将提交者姓名作为第一个参数。该程序应返回单行格式,如 "Name <email>" 或 "Name <>",这将视同包含在作者文件中。

由于历史原因,对于 initclone,首先相对于当前目录搜索相对 filename;对于 fetch,则相对于工作树根目录搜索。如果未找到 filename,则会像 $PATH 中的任何其他命令一样进行搜索。

config key: svn.authorsProg
-q
--quiet

减少 git svn 的输出。再次指定可进一步减少输出。

-m
--merge
-s<strategy>
--strategy=<strategy>
-p
--rebase-merges

这些仅用于 dcommitrebase 命令。

如果在使用 dcommit 时无法使用 git reset,则直接传递给 git rebase(参见 dcommit)。

-n
--dry-run

这可以用于 dcommitrebasebranchtag 命令。

对于 dcommit,打印出将显示哪些差异将被提交到 SVN 的 Git 参数系列。

对于 rebase,显示与当前分支关联的上游 svn 仓库关联的本地分支,以及将要从中获取数据的 svn 仓库的 URL。

对于 branchtag,显示在创建分支或标签时用于复制的 url。

--use-log-author

在将 svn 提交检索到 Git 时(作为 fetchrebasedcommit 操作的一部分),在日志消息中查找第一个 From: 行或 Signed-off-by 尾注,并将其作为作者字符串。

config key: svn.useLogAuthor
--add-author-from

当从 Git 提交到 svn 时(作为 set-treedcommit 操作的一部分),如果现有日志消息还没有 From:Signed-off-by 尾注,则根据 Git 提交的作者字符串追加一行 From:。如果您使用此选项,那么 --use-log-author 将为所有提交检索到有效的作者字符串。

config key: svn.addAuthorFrom

高级选项

-i<GIT_SVN_ID>
--id <GIT_SVN_ID>

这会设置 GIT_SVN_ID(而不是使用环境变量)。这允许用户在跟踪单个 URL 时覆盖要获取的默认 refname。logdcommit 命令不再需要此开关作为参数。

-R<remote-name>
--svn-remote <remote-name>

指定要使用的 [svn-remote "<remote-name>"] 部分,这允许跟踪多个 SVN 仓库。默认值:"svn"

--follow-parent

此选项仅在我们跟踪分支(使用仓库布局选项 --trunk、--tags、--branches、--stdlayout 之一)时相关。对于每个被跟踪的分支,尝试找出其修订版本是从哪里复制而来的,并在该分支的第一个 Git 提交中设置一个合适的父节点。当我们跟踪一个在仓库中被四处移动的目录时,这尤其有用。如果禁用此功能,由 git svn 创建的分支都将是线性的,不共享任何历史记录,这意味着将没有关于分支从何处分出或合并的信息。然而,追踪漫长/曲折的历史记录可能需要很长时间,因此禁用此功能可能会加快克隆过程。此功能默认开启,使用 --no-follow-parent 可以禁用它。

config key: svn.followparent

仅限配置文件的选项

svn.noMetadata
svn-remote.<name>.noMetadata

这会去掉每个提交末尾的 git-svn-id: 行。

此选项只能用于一次性导入,因为没有元数据,git svn 将无法再次进行 fetch。此外,如果您丢失了 $GIT_DIR/svn/**/.rev_map.* 文件,git svn 将无法重建它们。

git svn log 命令也将无法在使用了此选项的仓库上运行。由于(显而易见的)原因,使用此选项与 useSvmProps 选项冲突。

不推荐使用此选项,因为它使得在现有文档、错误报告和归档中追踪旧的 SVN 修订号引用变得困难。如果您计划最终从 SVN 迁移到 Git,并且确定要丢弃 SVN 历史记录,请考虑使用 git-filter-repo。filter-repo 还允许重新格式化元数据以便阅读,并为非 "svn.authorsFile" 用户重写作者信息。

svn.useSvmProps
svn-remote.<name>.useSvmProps

这允许 git svn 从使用 SVN::Mirror (或 svk) 创建的镜像中重新映射仓库 URL 和 UUID,用于元数据。

如果 SVN 修订版本具有属性 "svm:headrev",则该修订版本很可能是由 SVN::Mirror(SVK 也使用它)创建的。该属性包含仓库 UUID 和修订版本。我们希望使其看起来像是我们在镜像原始 URL,因此引入一个返回原始身份 URL 和 UUID 的辅助函数,并在提交消息中生成元数据时使用它。

svn.useSvnsyncProps
svn-remote.<name>.useSvnsyncprops

与 useSvmProps 选项类似;这是供配合使用 SVN 1.4.x 及更高版本附带的 svnsync(1) 命令的用户使用的。

svn-remote.<name>.rewriteRoot

这允许用户从备用 URL 创建仓库。例如,管理员可以在本地服务器上运行 git svn(通过 file:// 访问),但希望在元数据中以公共 http:// 或 svn:// URL 分发仓库,以便其用户能看到公共 URL。

svn-remote.<name>.rewriteUUID

与 useSvmProps 选项类似;这是为需要手动重新映射 UUID 的用户准备的。在原始 UUID 无法通过 useSvmProps 或 useSvnsyncProps 获取的情况下,这可能会很有用。

svn-remote.<name>.pushurl

类似于 Git 的 remote.<name>.pushurl,此键设计用于 url 通过只读传输指向 SVN 仓库的情况,以提供另一种读写传输方式。假定两个键都指向同一个仓库。与 commiturl 不同,pushurl 是一个基础路径。如果 commiturlpushurl 都可以使用,commiturl 优先。

svn.brokenSymlinkWorkaround

这禁用了可能很昂贵的检查,以规避由损坏的客户端检入 SVN 的损坏符号链接。如果您跟踪的 SVN 仓库有许多不是符号链接的空 blob,请将此选项设置为 "false"。此选项可在 git svn 运行时更改,并对下一次获取的修订版本生效。如果未设置,git svn 假定此选项为 "true"。

svn.pathnameencoding

这指示 git svn 将路径名重新编码为给定的编码。Windows 用户和在非 utf8 区域设置下工作的用户可以使用它,以避免带有非 ASCII 字符的文件名损坏。有效的编码是 Perl 的 Encode 模块支持的编码。

svn-remote.<name>.automkdirs

通常,"git svn clone" 和 "git svn rebase" 命令会尝试重新创建 Subversion 仓库中的空目录。如果将此选项设置为 "false",则仅在显式运行 "git svn mkdirs" 命令时才会创建空目录。如果未设置,git svn 假定此选项为 "true"。

由于 noMetadata、rewriteRoot、rewriteUUID、useSvnsyncProps 和 useSvmProps 选项都会影响 git svn 生成和使用的元数据;它们必须在导入任何历史记录之前在配置文件中设置,并且一旦设置就不应更改。

此外,每个 svn-remote 部分只能使用这些选项中的一个,因为它们会影响 git-svn-id: 元数据行,但 rewriteRoot 和 rewriteUUID 可以一起使用。

基本示例

跟踪并贡献于 Subversion 管理的项目的 trunk(忽略标签和分支)

# Clone a repo (like git clone):
	git svn clone http://svn.example.com/project/trunk
# Enter the newly cloned directory:
	cd trunk
# You should be on master branch, double-check with 'git branch'
	git branch
# Do some work and commit locally to Git:
	git commit ...
# Something is committed to SVN, rebase your local changes against the
# latest changes in SVN:
	git svn rebase
# Now commit your changes (that were committed previously using Git) to SVN,
# as well as automatically updating your working HEAD:
	git svn dcommit
# Append svn:ignore and svn:global-ignores settings to the default Git exclude file:
	git svn show-ignore >> .git/info/exclude

跟踪并贡献于整个 Subversion 管理的项目(包括完整的 trunk、tags 和 branches)

# Clone a repo with standard SVN directory layout (like git clone):
	git svn clone http://svn.example.com/project --stdlayout --prefix svn/
# Or, if the repo uses a non-standard directory layout:
	git svn clone http://svn.example.com/project -T tr -b branch -t tag --prefix svn/
# View all branches and tags you have cloned:
	git branch -r
# Create a new branch in SVN
	git svn branch waldo
# Reset your master to trunk (or any other branch, replacing 'trunk'
# with the appropriate name):
	git reset --hard svn/trunk
# You may only dcommit to one branch/tag/trunk at a time.  The usage
# of dcommit/rebase/show-ignore should be the same as above.

初始的 git svn clone 可能会非常耗时(尤其是对于大型 Subversion 仓库)。如果多个人(或一个拥有多台机器的人)想要使用 git svn 与同一个 Subversion 仓库交互,您可以将初始的 git svn clone 执行到服务器上的仓库,然后让每个人使用 git clone 克隆该仓库

# Do the initial import on a server
	ssh server "cd /pub && git svn clone http://svn.example.com/project [options...]"
# Clone locally - make sure the refs/remotes/ space matches the server
	mkdir project
	cd project
	git init
	git remote add origin server:/pub/project
	git config --replace-all remote.origin.fetch '+refs/remotes/*:refs/remotes/*'
	git fetch
# Prevent fetch/pull from remote Git server in the future,
# we only want to use git svn for future updates
	git config --remove-section remote.origin
# Create a local branch from one of the branches just fetched
	git checkout -b master FETCH_HEAD
# Initialize 'git svn' locally (be sure to use the same URL and
# --stdlayout/-T/-b/-t/--prefix options as were used on server)
	git svn init http://svn.example.com/project [options...]
# Pull the latest changes from Subversion
	git svn rebase

REBASE 与 PULL/MERGE 的对比

建议使用 git svn rebasegit rebase,而不是 git pullgit merge 来同步未整合的提交到 git svn 分支。这样做将使未整合提交的历史记录相对于上游 SVN 仓库保持线性,并允许使用推荐的 git svn dcommit 子命令将未整合的提交推回 SVN。

最初,git svn 推荐开发人员从 git svn 分支进行 pull 或 merge。这是因为作者当时更倾向于使用 git svn set-tree B 来提交单个 head,而不是使用 git svn set-tree A..B 标记来提交多个提交。在将提交存入 SVN 时,配合 git svn set-tree A..B 使用 git pullgit merge 会导致非线性历史被压平,这可能会导致合并提交意外地撤销 SVN 中之前的提交。

合并跟踪

虽然 git svn 可以为采用标准布局的仓库跟踪复制历史记录(包括分支和标签),但它目前还无法将 Git 内部发生的合并历史表示回给上游 SVN 用户。因此,建议用户在 Git 内部尽可能保持历史记录为线性,以简化与 SVN 的兼容性(请参阅下面的“注意事项”章节)。

SVN 分支的处理

如果 git svn 被配置为获取分支(且 --follow-branches 生效),它有时会为一个 SVN 分支创建多个 Git 分支,其中额外的分支名称格式为 branchname@nnn(nnn 是 SVN 修订号)。如果 git svn 找不到 SVN 分支中第一个提交的父提交,就会创建这些额外的分支,以便将该分支连接到其他分支的历史记录中。

通常,SVN 分支中的第一个提交包含一次复制操作。git svn 将读取此提交以获取创建分支时的 SVN 修订版本。然后,它将尝试查找与该 SVN 修订版本相对应的 Git 提交,并将其作为分支的父节点。然而,可能没有合适的 Git 提交可以充当父节点。这可能在以下情况下发生:SVN 分支是某个修订版本的副本,而该修订版本未被 git svn 获取(例如,因为它是一个被 --revision 跳过的旧修订版本),或者在 SVN 中复制了一个未被 git svn 跟踪的目录(例如一个完全未被跟踪的分支,或者被跟踪分支的子目录)。在这些情况下,git svn 仍将创建一个 Git 分支,但它不会使用现有的 Git 提交作为该分支的父节点,而是读取该分支复制自的目录的 SVN 历史记录,并创建适当的 Git 提交。这将由消息 "Initializing parent: <branchname>" 指示。

此外,它将创建一个名为 <branchname>@<SVN-Revision> 的特殊分支,其中 <SVN-Revision> 是该分支复制自的 SVN 修订号。此分支将指向该分支新创建的父提交。如果在 SVN 中该分支被删除并稍后从不同版本重新创建,则会有多个带 @ 符号的此类分支。

请注意,这可能意味着为单个 SVN 修订版本创建了多个 Git 提交。

一个例子:在一个具有标准 trunk/tags/branches 布局的 SVN 仓库中,目录 trunk/sub 在 r.100 中创建。在 r.200 中,通过将 trunk/sub 复制到 branches/ 来创建分支。git svn clone -s 随后将创建一个分支 sub。它还将为 r.100 到 r.199 创建新的 Git 提交,并将这些提交作为分支 sub 的历史记录。因此,对于 r.100 到 r.199 的每个修订版本,将会有两个 Git 提交(一个包含 trunk/,一个包含 trunk/sub/)。最后,它将创建一个指向分支 sub 的新父提交的 sub@200 分支(即 r.200 和 trunk/sub/ 的提交)。

注意事项

为了简单起见以及与 Subversion 的互操作性,建议所有 git svn 用户直接从 SVN 服务器进行 clone、fetch 和 dcommit,并避免在 Git 仓库和分支之间进行任何 git clone/pull/merge/push 操作。在 Git 分支和用户之间交换代码的推荐方法是 git format-patchgit am,或者直接 ‘dcommit’ 到 SVN 仓库。

不建议在您计划 dcommit 的分支上运行 git mergegit pull,因为 Subversion 用户看不到您所做的任何合并。此外,如果您从一个作为 SVN 分支镜像的 Git 分支进行 merge 或 pull,dcommit 可能会提交到错误的分支。

如果您确实进行了合并,请注意以下规则:git svn dcommit 将尝试在由以下命令命名的 SVN 提交之上进行提交:

git log --grep=^git-svn-id: --first-parent -1

因此,您必须确保您要 dcommit 到的分支的最新提交是合并操作的第一个父节点。否则会导致混乱,尤其是当第一个父节点是同一 SVN 分支上的较旧提交时。

git clone 不会克隆 refs/remotes/ 层级下的分支,也不会克隆任何 git svn 元数据或配置。因此,对于使用 git svn 创建和管理的仓库,如果要进行克隆,应使用 rsync

由于 dcommit 内部使用 rebase,因此在 dcommit 之前通过 git push 推送到的任何 Git 分支,都将需要强制覆盖远程仓库上的现有引用。这通常被认为是不良做法,详情请参阅 git-push[1] 文档。

对于已经执行过 dcommit 的更改,不要使用 git-commit[1] 的 --amend 选项。对已经推送到远程仓库供其他用户使用的提交执行 --amend 被认为是不良做法,而向 SVN 执行 dcommit 与此类似。

在克隆 SVN 仓库时,如果不使用任何描述仓库布局的选项(--trunk、--tags、--branches、--stdlayout),git svn clone 将创建一个具有完全线性历史的 Git 仓库,其中分支和标签在工作副本中显示为单独的目录。虽然这是获取完整仓库副本的最简单方法,但对于拥有许多分支的项目,这将导致工作副本比仅包含 trunk 时大许多倍。因此,对于使用标准目录结构(trunk/branches/tags)的项目,建议使用 --stdlayout 选项进行克隆。如果项目使用非标准结构,和/或不需要分支和标签,最简单的方法是只克隆一个目录(通常是 trunk),而不提供任何仓库布局选项。如果需要包含分支和标签的完整历史记录,则必须使用 --trunk / --branches / --tags 选项。

当使用多个 --branches 或 --tags 时,git svn 不会自动处理名称冲突(例如,如果来自不同路径的两个分支具有相同的名称,或者一个分支和一个标签具有相同的名称)。在这些情况下,先使用 init 设置您的 Git 仓库,然后在第一次 fetch 之前,编辑 $GIT_DIR/config 文件,以便将分支和标签与不同的命名空间关联起来。例如:

branches = stable/*:refs/remotes/svn/stable/*
branches = debug/*:refs/remotes/svn/debug/*

配置

git svn 将 [svn-remote] 配置信息存储在仓库的 $GIT_DIR/config 文件中。它类似于 Git 核心的 [remote] 部分,除了 fetch 键不接受 glob 参数;相反,它们由 branchestags 键处理。由于一些 SVN 仓库的配置比较奇特,包含多个项目,因此允许如下所示的 glob 扩展:

[svn-remote "project-a"]
	url = http://server.org/svn
	fetch = trunk/project-a:refs/remotes/project-a/trunk
	branches = branches/*/project-a:refs/remotes/project-a/branches/*
	branches = branches/release_*:refs/remotes/project-a/branches/release_*
	branches = branches/re*se:refs/remotes/project-a/branches/*
	tags = tags/*/project-a:refs/remotes/project-a/tags/*

请记住,本地引用(: 右侧)的 *(星号)通配符必须是路径的最右侧组件;然而,远程通配符可以位于任何位置,只要它是一个独立的路径组件(由 / 包围或位于行尾)。这种类型的配置不会由 init 自动创建,应使用文本编辑器手动输入或使用 git config 设置。

还要注意,每个单词只允许一个星号。例如:

branches = branches/re*se:refs/remotes/project-a/branches/*

将匹配分支 releaseresere123se,然而:

branches = branches/re*s*e:refs/remotes/project-a/branches/*

将产生错误。

还可以通过在大括号内使用逗号分隔的名称列表来获取分支或标签的子集。例如:

[svn-remote "huge-project"]
	url = http://server.org/svn
	fetch = trunk/src:refs/remotes/trunk
	branches = branches/{red,green}/src:refs/remotes/project-a/branches/*
	tags = tags/{1.0,2.0}/src:refs/remotes/project-a/tags/*

支持多个 fetch、branches 和 tags 键

[svn-remote "messy-repo"]
	url = http://server.org/svn
	fetch = trunk/project-a:refs/remotes/project-a/trunk
	fetch = branches/demos/june-project-a-demo:refs/remotes/project-a/demos/june-demo
	branches = branches/server/*:refs/remotes/project-a/branches/*
	branches = branches/demos/2011/*:refs/remotes/project-a/2011-demos/*
	tags = tags/server/*:refs/remotes/project-a/tags/*

在这样的配置中创建分支需要使用 -d 或 --destination 标志来消除歧义,指明要使用的位置:

$ git svn branch -d branches/server release-2-3-0

请注意,git-svn 会跟踪分支或标签出现过的最高修订版本。如果在获取之后更改了分支或标签的子集,则必须手动编辑 $GIT_DIR/svn/.metadata 以删除(或重置)适当的 branches-maxRev 和/或 tags-maxRev。

文件

$GIT_DIR/svn/**/.rev_map.*

Subversion 修订号与 Git 提交名称之间的映射。在未设置 noMetadata 选项的仓库中,这可以从每个提交末尾的 git-svn-id: 行重建(详见上文 svn.noMetadata 章节)。

git svn fetchgit svn rebase 会在 rev_map 缺失或不是最新时自动更新它。git svn reset 会自动将其回滚。

BUG

除了 svn:executable 之外,我们忽略所有 SVN 属性。任何未处理的属性都会记录到 $GIT_DIR/svn/<refname>/unhandled.log 中。

重命名和复制的目录不会被 Git 检测到,因此在向 SVN 提交时不会被跟踪。我不打算为此添加支持,因为处理所有可能的特殊情况非常困难且耗时(Git 也不支持此项)。如果重命名和复制的文件足够相似以供 Git 检测到,则完全支持提交它们。

在 SVN 中,可以(尽管不鼓励)向标签提交更改(因为标签只是一个目录副本,因此在技术上与分支相同)。在克隆 SVN 仓库时,git svn 无法预知将来是否会发生此类针对标签的提交。因此,它采取保守策略,将所有 SVN 标签作为分支导入,并在标签名称前加上 tags/ 前缀。

另请参阅

GIT

Git[1] 套件的一部分