Git/git-svn
Subversion 是一个非常流行的版本控制系统,许多 OSS 和专有项目都使用它。 Git 带有一个优秀的实用程序 git-svn
,它允许用户跟踪在 subversion 仓库中维护的项目,以及参与项目。用户可以生成本地补丁并发送到邮件列表,甚至将更改直接提交回仓库(当然,前提是他们有提交权限)。
要开始使用 git 与 subversion 托管的项目进行交互,您必须为您的文件创建一个本地仓库,以及配置 git-svn
。通常,您会使用类似以下命令的命令
mkdir project
cd project
git-svn init <url to repository root> -T/path/to/trunk
git-svn fetch -r <first rev>:HEAD
通常在使用 subversion 仓库时,您会得到完整的项目 URL。要确定仓库根目录的 URL,您可以执行以下命令
svn info <full project URL>
输出中将有一行显示仓库根目录。trunk 的路径只是 URL 中紧随其后的其余部分。可以简单地将完整的项目 URL 提供给 git-svn
,但这样做可以让您在最终使用 subversion 分支时获得更大的灵活性。
还要注意 “first rev” 参数。您可以简单地使用 “1”,因为这保证可以工作,但可能会花费很长时间(特别是如果仓库位于速度缓慢的网络上)。通常,对于一个成熟的项目,最近的 10-50 个版本就足够了。同样,svn info
会告诉您最新的版本号。
如果您使用 git 而不是 svn 与 subversion 仓库进行交互,那么很可能是因为您想利用离线提交。如果您牢记一些注意事项,这将非常容易做到。最重要的是,在您计划最终从某个分支运行 git-svn dcommit
的情况下,切勿使用 “git pull”。合并提交往往会让 git-svn 感到困惑,而且您能够在没有 git pull 的情况下完成大多数需要做的事情。
我执行的最常见的任务是将我正在处理的更改与上游的 subversion 更改合并。这相当于 svn update
。以下是完成操作的方法
git stash # stash any changes so you have a clean tree
git-svn fetch # bring down the latest changes
git rebase trunk
git stash apply
如果您的树是干净的,则第一步和最后一步是不必要的。这将 “git rebase trunk” 作为主要操作。如果您不熟悉变基,您应该阅读 git-rebase 的文档。它的本质是您的本地提交现在位于 svn HEAD 的顶部。
您想要在仓库文件中进行一些更改,但不希望传播这些更改,这种情况经常发生。通常这种情况发生在配置文件中,但它也可能是一些额外的调试语句或其他任何内容。提交这些更改的危险在于您将在分支中运行 “git-svn dcommit”,而没有剔除您的更改。另一方面,如果您未提交更改,您将失去 git 对这些更改的特性,而且您将不得不处理其他分支与这些更改的冲突。这是一个两难的境地!
解决此问题有两种方法。哪种方法更有效,与其说是技术问题,不如说是个人喜好问题。第一种方法是为每个想要保留本地更改的分支保留一个 “local” 分支。例如,如果您想在分支 “foo” 中保留本地更改,您将创建一个包含要保留的更改的提交的分支 “foo-local”。然后,您可以使用变基将 “foo” 保留在 “foo-local” 的顶部。例如
git rebase trunk foo-local
git rebase foo-local foo
正如示例代码所示,您仍然会在大多数情况下检出 “foo”,而不是 “foo-local”。如果您决定进行新的更改,并且想要保留在本地,您将再次面临两种选择。您可以检出 “foo-local” 并进行提交,或者您可以在 “foo” 上进行提交,然后从 foo-local 中挑选提交。然后,您需要使用 git-reset 从 “foo” 中删除提交。
作为以变基为中心的方法的替代方法,有一种基于合并的方法。您仍然将本地更改保留在单独的分支上,如前所述。但是,使用这种方法,您不必使用变基将 “foo” 保留在 “foo-local” 的顶部。这是一个优势,因为 1) 键入更多内容,以及 2) 历史上,如果第一次变基期间出现任何冲突,变基通常会要求您两次解决同一个冲突。
因此,您不是使用变基,而是创建另一个分支。我将此分支称为 “build” 分支。您将在想要测试的任何提交处开始构建分支。然后,您可以 “git merge” 本地分支,将所有更改合并到一个树中。“但是我认为您应该避免合并?”您可能会问。我之所以喜欢将此分支称为 “build” 分支,是为了避免我从它运行 “git-svn dcommit”。只要您不打算从该分支运行 dcommit,合并的使用是可以接受的。
这种方法实际上可以更进一步,让您不必每天将主题分支 “foo” 变基到 trunk 的顶部。如果您有多个主题分支,这种频繁的变基会变成一件苦差事。相反
git checkout build
git reset --hard trunk # Make sure you dont have any important changes
git merge foo foo-local # Octopus merges are fun
现在 build 包含来自 trunk、foo 和 foo-local 的更改!我经常会保留多个本地分支。也许一个分支包含您的本地配置更改,另一个分支包含额外的调试语句。您还可以使用这种方法在一个树中同时构建多个主题分支
git merge topic1 topic2 config debug...
不幸的是,章鱼合并对解决冲突非常笨拙。如果您遇到任何冲突,您将不得不一次执行一个合并
git merge topic1
git merge topic2
git merge local
...
最终,您将希望将精心制作的主题分支和补丁系列集成到上游。如果您有幸获得了提交权限,您可以运行 “git-svn dcommit”。这将获取当前分支中的每个本地提交,并将它们提交到 subversion。如果您有三个本地提交,在 dcommit 之后,subversion 中将有三个新的提交。
对于不太幸运的人来说,您的补丁可能必须提交到邮件列表或错误跟踪器。为此,您可以使用 git-format-patch。例如,继续上面的三个本地提交的场景
git format-patch HEAD~3..
结果将在 $PWD 中生成三个文件,0001-commit-name.patch、0002-commit-name.patch 和 0003-commit-name.patch。然后,您可以随意将这些补丁发送或附加到 Bugzilla 中的错误。但是,如果您要通过邮件发送补丁,git 还可以进一步帮助您。有一个专门针对这种情况的 git-send-email 实用程序
git send-email *.patch
该程序会问您一些问题,其中最重要的是将补丁发送到哪里,然后将它们发送出去。小菜一碟!
当然,这一切都假设您的补丁系列按预期工作。如果不是这样,您应该阅读有关 “git rebase -i” 的内容。
获取 Pywikipedia
$ git svn init http://svn.wikimedia.org/svnroot/pywikipedia/trunk/pywikipedia/
Initialized empty Git repository in .../.git/
$ git svn fetch -r 1:HEAD
...
r370 = 318fb412e5d1f1136a92d079f3607ac23bde2c34 (refs/remotes/git-svn)
D treelang_all.py
D treelang.py
W: -empty_dir: treelang.py
W: -empty_dir: treelang_all.py
r371 = e8477f292b077f023e4cebad843e0d36d3765db8 (refs/remotes/git-svn)
D parsepopular.py
W: -empty_dir: parsepopular.py
r372 = 8803111b0411243af419868388fc8c7398e8ab9d (refs/remotes/git-svn)
D getlang.py
W: -empty_dir: getlang.py
r373 = ad935dd0472db28379809f150fcf53678630076c (refs/remotes/git-svn)
A splitwarning.py
...
获取 AWB(AutoWikiBrowser)
$ git svn init svn://svn.code.sf.net/p/autowikibrowser/code/
Initialized empty Git repository in .../.git/
$ git svn fetch -r 1:HEAD
...
r15 = 086d4ff454a9ddfac92edb4013ec845f65e14ace (refs/remotes/git-svn)
M AWB/AWB/Main.cs
M AWB/WikiFunctions/WebControl.cs
r16 = 14f49de6b3c984bb8a87900e8be42a6576902a06 (refs/remotes/git-svn)
M AWB/AWB/ExitQuestion.Designer.cs
M AWB/WikiFunctions/GetLists.cs
M AWB/WikiFunctions/Tools.cs
r17 = 8b58f6e5b21c91f0819bea9bc9a8110c2cab540d (refs/remotes/git-svn)
M AWB/AWB/Main.Designer.cs
M AWB/AWB/Main.cs
M AWB/WikiFunctions/GetLists.cs
r18 = 51683925cedb8effb274fadd2417cc9b1f860e3c (refs/remotes/git-svn)
M AWB/AWB/specialFilter.Designer.cs
M AWB/AWB/specialFilter.cs
r19 = 712edb32a20d6d2ab4066acf056f14daa67a9d4b (refs/remotes/git-svn)
M AWB/WikiFunctions/WPEditor.cs
r20 = 3116588b52a8e27e1dc72d25b1981d181d6ba203 (refs/remotes/git-svn)
...
注意,此下载操作可能需要一小时。