提交到 Subversion

有了可以开展工作的(本地)仓库以后,你可以开始对该项目做出贡献并向上游仓库提交内容了,Git 这时相当于一个 SVN 客户端。假如编辑了一个文件并进行提交,那么这次提交仅存在于本地的 Git 而非 Subversion 服务器上。

$ git commit -am ‘Adding git-svn instructions to the README’

[master 97031e5] Adding git-svn instructions to the README

1 files changed, 1 insertions(+), 1 deletions(-)

接下来,可以将作出的修改推送到上游。值得注意的是,Subversion 的使用流程也因此改变了——你可以在离线状态下进行多次提交然后一次性的推送到 Subversion 的服务器上。向 Subversion 服务器推送的命令是 git svn dcommit

$ git svn dcommit

Committing to file:///tmp/test-svn/trunk …

M README.txt

Committed r79

M README.txt

r79 = 938b1a547c2cc92033b74d32030e86468294a5c8 (trunk)

No changes between current HEAD and refs/remotes/trunk

Resetting to the latest refs/remotes/trunk

所有在原 Subversion 数据基础上提交的 commit 会一一提交到 Subversion,然后你本地 Git 的 commit 将被重写,加入一个特别标识。这一步很重要,因为它意味着所有 commit 的 SHA-1 指都会发生变化。这也是同时使用 Git 和 Subversion 两种服务作为远程服务不是个好主意的原因之一。检视以下最后一个 commit,你会找到新添加的 git-svn-id (译注:即本段开头所说的特别标识):

$ git log -1

commit 938b1a547c2cc92033b74d32030e86468294a5c8

Author: schacon schacon@4c93b258-373f-11de-be05-5f7a86268029

Date: Sat May 2 22:06:44 2009 +0000

Adding git-svn instructions to the README

git-svn-id: file:///tmp/test-svn/trunk@79 4c93b258-373f-11de-be05-5f7a86268029

注意看,原本以 97031e5 开头的 SHA-1 校验值在提交完成以后变成了 938b1a5 。如果既要向 Git 远程服务器推送内容,又要推送到 Subversion 远程服务器,则必须先向 Subversion 推送(dcommit),因为该操作会改变所提交的数据内容。

拉取最新进展

如果要与其他开发者协作,总有那么一天你推送完毕之后,其他人发现他们推送自己修改的时候(与你推送的内容)产生冲突。这些修改在你合并之前将一直被拒绝。在 git svn 里这种情况形似:

$ git svn dcommit

Committing to file:///tmp/test-svn/trunk …

Merge conflict during commit: Your file or directory ‘README.txt’ is probably \

out-of-date: resource out of date; try updating at /Users/schacon/libexec/git-\

core/git-svn line 482

为了解决该问题,可以运行 git svn rebase ,它会拉取服务器上所有最新的改变,再次基础上衍合你的修改:

$ git svn rebase

M README.txt

r80 = ff829ab914e8775c7c025d741beb3d523ee30bc4 (trunk)

First, rewinding head to replay your work on top of it…

Applying: first user change

现在,你做出的修改都发生在服务器内容之后,所以可以顺利的运行 dcommit

$ git svn dcommit

Committing to file:///tmp/test-svn/trunk …

M README.txt

Committed r81

M README.txt

r81 = 456cbe6337abe49154db70106d1836bc1332deed (trunk)

No changes between current HEAD and refs/remotes/trunk

Resetting to the latest refs/remotes/trunk

需要牢记的一点是,Git 要求我们在推送之前先合并上游仓库中最新的内容,而 git svn 只要求存在冲突的时候才这样做。假如有人向一个文件推送了一些修改,这时你要向另一个文件推送一些修改,那么 dcommit 将正常工作:

$ git svn dcommit

Committing to file:///tmp/test-svn/trunk …

M configure.ac

Committed r84

M autogen.sh

r83 = 8aa54a74d452f82eee10076ab2584c1fc424853b (trunk)

M configure.ac

r84 = cdbac939211ccb18aa744e581e46563af5d962d0 (trunk)

W: d2f23b80f67aaaa1f6f5aaef48fce3263ac71a92 and refs/remotes/trunk differ, \

using rebase:

:100755 100755 efa5a59965fbbb5b2b0a12890f1b351bb5493c18 \

015e4c98c482f0fa71e4d5434338014530b37fa6 M autogen.sh

First, rewinding head to replay your work on top of it…

Nothing to do.

这一点需要牢记,因为它的结果是推送之后项目处于一个不完整存在与任何主机上的状态。如果做出的修改无法兼容但没有产生冲突,则可能造成一些很难确诊的难题。这和使用 Git 服务器是不同的——在 Git 世界里,发布之前,你可以在客户端系统里完整的测试项目的状态,而在 SVN 永远都没法确保提交前后项目的状态完全一样。

即使还没打算进行提交,你也应该用这个命令从 Subversion 服务器拉取最新修改。sit svn fetch 能获取最新的数据,不过 git svn rebase 才会在获取之后在本地进行更新 。

$ git svn rebase

M generate_descriptor_proto.sh

r82 = bd16df9173e424c6f52c337ab6efa7f7643282f1 (trunk)

First, rewinding head to replay your work on top of it…

Fast-forwarded master to refs/remotes/trunk.

不时地运行一下 git svn rebase 可以确保你的代码没有过时。不过,运行该命令时需要确保工作目录的整洁。如果在本地做了修改,则必须在运行 git svn rebase 之前或暂存工作,或暂时提交内容——否则,该命令会发现衍合的结果包含着冲突因而终止。

Git 分支问题

习惯了 Git 的工作流程以后,你可能会创建一些特性分支,完成相关的开发工作,然后合并他们。如果要用 git svn 向 Subversion 推送内容,那么最好是每次用衍合来并入一个单一分支,而不是直接合并。使用衍合的原因是 Subversion 只有一个线性的历史而不像 Git 那样处理合并,所以 Git svn 在把快照转换为 Subversion 的 commit 时只能包含第一个祖先。

假设分支历史如下:创建一个 experiment 分支,进行两次提交,然后合并到 master 。在 dcommit 的时候会得到如下输出:

$ git svn dcommit

Committing to file:///tmp/test-svn/trunk …

M CHANGES.txt

Committed r85

M CHANGES.txt

r85 = 4bfebeec434d156c36f2bcd18f4e3d97dc3269a2 (trunk)

No changes between current HEAD and refs/remotes/trunk

Resetting to the latest refs/remotes/trunk

COPYING.txt: locally modified

INSTALL.txt: locally modified

M COPYING.txt

M INSTALL.txt

Committed r86

M INSTALL.txt

M COPYING.txt

r86 = 2647f6b86ccfcaad4ec58c520e369ec81f7c283c (trunk)

No changes between current HEAD and refs/remotes/trunk

Resetting to the latest refs/remotes/trunk

在一个包含了合并历史的分支上使用 dcommit 可以成功运行,不过在 Git 项目的历史中,它没有重写你在 experiment 分支中的两个 commit ——另一方面,这些改变却出现在了 SVN 版本中同一个合并 commit 中。

在别人克隆该项目的时候,只能看到这个合并 commit 包含了所有发生过的修改;他们无法获知修改的作者和时间等提交信息。

Subversion 分支

Subversion 的分支和 Git 中的不尽相同;避免过多的使用可能是最好方案。不过,用 git svn 创建和提交不同的 Subversion 分支仍是可行的。

创建新的 SVN 分支

要在 Subversion 中建立一个新分支,需要运行 git svn branch [分支名]

$ git svn branch opera

Copying file:///tmp/test-svn/trunk at r87 to file:///tmp/test-svn/branches/opera…

Found possible branch point: file:///tmp/test-svn/trunk => \

file:///tmp/test-svn/branches/opera, 87

Found branch parent: (opera) 1f6bfe471083cbca06ac8d4176f7ad4de0d62e5f

Following parent with do_switch

Successfully followed parent

r89 = 9b6fe0b90c5c9adf9165f700897518dbc54a7cbf (opera)

这相当于在 Subversion 中的 svn copy trunk branches/opera 命令,并会对 Subversion 服务器进行相关操作。值得注意的是它没有检出和转换到那个分支;如果现在进行提交,将提交到服务器上的 trunk, 而非 opera

切换当前分支

Git 通过搜寻提交历史中 Subversion 分支的头部来决定 dcommit 的目的地——而它应该只有一个,那就是当前分支历史中最近一次包含 git-svn-id 的提交。

如果需要同时在多个分支上提交,可以通过导入 Subversion 上某个其他分支的 commit 来建立以该分支为 dcommit 目的地的本地分支。比如你想拥有一个并行维护的 opera 分支,可以运行

$ git branch opera remotes/opera

然后,如果要把 opera 分支并入 trunk (本地的 master 分支),可以使用普通的 git merge。不过最好提供一条描述提交的信息(通过 -m),否则这次合并的记录是 Merge branch opera ,而不是任何有用的东西。

记住,虽然使用了 git merge 来进行这次操作,并且合并过程可能比使用 Subversion 简单一些(因为 Git 会自动找到适合的合并基础),这并不是一次普通的 Git 合并提交。最终它将被推送回 commit 无法包含多个祖先的 Subversion 服务器上;因而在推送之后,它将变成一个包含了所有在其他分支上做出的改变的单一 commit。把一个分支合并到另一个分支以后,你没法像在 Git 中那样轻易的回到那个分支上继续工作。提交时运行的 dcommit 命令擦除了全部有关哪个分支被并入的信息,因而以后的合并基础计算将是不正确的—— dcommit 让 git merge 的结果变得类似于 git merge --squash。不幸的是,我们没有什么好办法来避免该情况—— Subversion 无法储存这个信息,所以在使用它作为服务器的时候你将永远为这个缺陷所困。为了不出现这种问题,在把本地分支(本例中的 opera)并入 trunk 以后应该立即将其删除。

对应 Subversion 的命令

git svn 工具集合了若干个与 Subversion 类似的功能,对应的命令可以简化向 Git 的转化过程。下面这些命令能实现 Subversion 的这些功能。

SVN 风格的历史

习惯了 Subversion 的人可能想以 SVN 的风格显示历史,运行 git svn log 可以让提交历史显示为 SVN 格式:

$ git svn log


r87 | schacon | 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009) | 2 lines

autogen change


r86 | schacon | 2009-05-02 16:00:21 -0700 (Sat, 02 May 2009) | 2 lines

Merge branch ‘experiment’


r85 | schacon | 2009-05-02 16:00:09 -0700 (Sat, 02 May 2009) | 2 lines

updated the changelog

关于 git svn log ,有两点需要注意。首先,它可以离线工作,不像 svn log 命令,需要向 Subversion 服务器索取数据。其次,它仅仅显示已经提交到 Subversion 服务器上的 commit。在本地尚未 dcommit 的 Git 数据不会出现在这里;其他人向 Subversion 服务器新提交的数据也不会显示。等于说是显示了最近已知 Subversion 服务器上的状态。

SVN 日志

类似 git svn loggit log 的模拟,svn annotate 的等效命令是 git svn blame [文件名]。其输出如下:

$ git svn blame README.txt

2 temporal Protocol Buffers - Google’s data interchange format

2 temporal Copyright 2008 Google Inc.

2 temporal http://code.google.com/apis/protocolbuffers/

2 temporal

22 temporal C++ Installation - Unix

22 temporal =======================

2 temporal

79 schacon Committing in git-svn.

78 schacon

2 temporal To build and install the C++ Protocol Buffer runtime and the Protocol

2 temporal Buffer compiler (protoc) execute the following:

2 temporal

同样,它不显示本地的 Git 提交以及 Subversion 上后来更新的内容。

SVN 服务器信息

还可以使用 git svn info 来获取与运行 svn info 类似的信息:

$ git svn info

Path: .

URL: https://schacon-test.googlecode.com/svn/trunk

Repository Root: https://schacon-test.googlecode.com/svn

Repository UUID: 4c93b258-373f-11de-be05-5f7a86268029

Revision: 87

Node Kind: directory

Schedule: normal

Last Changed Author: schacon

Last Changed Rev: 87

Last Changed Date: 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009)

它与 blamelog 的相同点在于离线运行以及只更新到最后一次与 Subversion 服务器通信的状态。

略 Subversion 之所略

假如克隆了一个包含了 svn:ignore 属性的 Subversion 仓库,就有必要建立对应的 .gitignore 文件来防止意外提交一些不应该提交的文件。git svn 有两个有益于改善该问题的命令。第一个是 git svn create-ignore,它自动建立对应的 .gitignore 文件,以便下次提交的时候可以包含它。

第二个命令是 git svn show-ignore,它把需要放进 .gitignore 文件中的内容打印到标准输出,方便我们把输出重定向到项目的黑名单文件:

$ git svn show-ignore > .git/info/exclude

这样一来,避免了 .gitignore 对项目的干扰。如果你是一个 Subversion 团队里唯一的 Git 用户,而其他队友不喜欢项目包含 .gitignore,该方法是你的不二之选。

Git-Svn 总结

git svn 工具集在当前不得不使用 Subversion 服务器或者开发环境要求使用 Subversion 服务器的时候格外有用。不妨把它看成一个跛脚的 Git,然而,你还是有可能在转换过程中碰到一些困惑你和合作者们的迷题。为了避免麻烦,试着遵守如下守则:

  • 保持一个不包含由 git merge 生成的 commit 的线性提交历史。将在主线分支外进行的开发通通衍合回主线;避免直接合并。

  • 不要单独建立和使用一个 Git 服务来搞合作。可以为了加速新开发者的克隆进程建立一个,但是不要向它提供任何不包含 git-svn-id 条目的内容。甚至可以添加一个 pre-receive 挂钩来在每一个提交信息中查找 git-svn-id 并拒绝提交那些不包含它的 commit。

如果遵循这些守则,在 Subversion 上工作还可以接受。然而,如果能迁徙到真正的 Git 服务器,则能为团队带来更多好处。

8.2 迁移到 Git


如果在其他版本控制系统中保存了某项目的代码而后决定转而使用 Git,那么该项目必须经历某种形式的迁移。本节将介绍 Git 中包含的一些针对常见系统的导入脚本,并将展示编写自定义的导入脚本的方法。

导入

你将学习到如何从专业重量级的版本控制系统中导入数据—— Subversion 和 Perforce —— 因为据我所知这二者的用户是(向 Git)转换的主要群体,而且 Git 为此二者附带了高质量的转换工具。

Subversion

读过前一节有关 git svn 的内容以后,你应该能轻而易举的根据其中的指导来 git svn clone 一个仓库了;然后,停止 Subversion 的使用,向一个新 Git server 推送,并开始使用它。想保留历史记录,所花的时间应该不过就是从 Subversion 服务器拉取数据的时间(可能要等上好一会就是了)。

然而,这样的导入并不完美;而且还要花那么多时间,不如干脆一次把它做对!首当其冲的任务是作者信息。在 Subversion,每个提交者在都在主机上有一个用户名,记录在提交信息中。上节例子中多处显示了 schacon ,比如 blame 的输出以及 git svn log。如果想让这条信息更好的映射到 Git 作者数据里,则需要 从 Subversion 用户名到 Git 作者的一个映射关系。建立一个叫做 user.txt 的文件,用如下格式表示映射关系:

schacon = Scott Chacon schacon@geemail.com

selse = Someo Nelse selse@geemail.com

通过该命令可以获得 SVN 作者的列表:

$ svn log --xml | grep -P “^<author” | sort -u | \

perl -pe ‘s/(.*?)</author>/$1 = /’ > users.txt

它将输出 XML 格式的日志——你可以找到作者,建立一个单独的列表,然后从 XML 中抽取出需要的信息。(显而易见,本方法要求主机上安装了grepsortperl.)然后把输出重定向到 user.txt 文件,然后就可以在每一项的后面添加相应的 Git 用户数据。

git svn 提供该文件可以然它更精确的映射作者数据。你还可以在 clone 或者 init后面添加 --no-metadata 来阻止 git svn 包含那些 Subversion 的附加信息。这样 import 命令就变成了:

$ git-svn clone http://my-project.googlecode.com/svn/ \

–authors-file=users.txt --no-metadata -s my_project

现在 my_project 目录下导入的 Subversion 应该比原来整洁多了。原来的 commit 看上去是这样:

commit 37efa680e8473b615de980fa935944215428a35a

Author: schacon schacon@4c93b258-373f-11de-be05-5f7a86268029

Date: Sun May 3 00:12:22 2009 +0000

fixed install - go to trunk

git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de-

be05-5f7a86268029

现在是这样:

commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2

Author: Scott Chacon schacon@geemail.com

Date: Sun May 3 00:12:22 2009 +0000

fixed install - go to trunk

不仅作者一项干净了不少,git-svn-id 也就此消失了。

你还需要一点 post-import(导入后) 清理工作。最起码的,应该清理一下 git svn 创建的那些怪异的索引结构。首先要移动标签,把它们从奇怪的远程分支变成实际的标签,然后把剩下的分支移动到本地。

要把标签变成合适的 Git 标签,运行

$ cp -Rf .git/refs/remotes/tags/* .git/refs/tags/

$ rm -Rf .git/refs/remotes/tags

该命令将原本以 tag/ 开头的远程分支的索引变成真正的(轻巧的)标签。

接下来,把 refs/remotes 下面剩下的索引变成本地分支:

$ cp -Rf .git/refs/remotes/* .git/refs/heads/

$ rm -Rf .git/refs/remotes

现在所有的旧分支都变成真正的 Git 分支,所有的旧标签也变成真正的 Git 标签。最后一项工作就是把新建的 Git 服务器添加为远程服务器并且向它推送。下面是新增远程服务器的例子:

$ git remote add origin git@my-git-server:myrepository.git

为了让所有的分支和标签都得到上传,我们使用这条命令:

$ git push origin --all

所有的分支和标签现在都应该整齐干净的躺在新的 Git 服务器里了。

Perforce

你将了解到的下一个被导入的系统是 Perforce. Git 发行的时候同时也附带了一个 Perforce 导入脚本,不过它是包含在源码的 contrib 部分——而不像 git svn 那样默认可用。运行它之前必须获取 Git 的源码,可以在 git.kernel.org 下载:

$ git clone git://git.kernel.org/pub/scm/git/git.git

$ cd git/contrib/fast-import

在这个 fast-import 目录下,应该有一个叫做 git-p4 的 Python 可执行脚本。主机上必须装有 Python 和 p4 工具该导入才能正常进行。例如,你要从 Perforce 公共代码仓库(译注: Perforce Public Depot,Perforce 官方提供的代码寄存服务)导入 Jam 工程。为了设定客户端,我们要把 P4PORT 环境变量 export 到 Perforce 仓库:

$ export P4PORT=public.perforce.com:1666

运行 git-p4 clone 命令将从 Perforce 服务器导入 Jam 项目,我们需要给出仓库和项目的路径以及导入的目标路径:

$ git-p4 clone //public/jam/src@all /opt/p4import

Importing from //public/jam/src@all into /opt/p4import

Reinitialized existing Git repository in /opt/p4import/.git/

Import destination: refs/remotes/p4/master

Importing revision 4409 (100%)

现在去 /opt/p4import 目录运行一下 git log ,就能看到导入的成果:

$ git log -2

commit 1fd4ec126171790efd2db83548b85b1bbbc07dc2

Author: Perforce staff support@perforce.com

Date: Thu Aug 19 10:18:45 2004 -0800

Drop ‘rc3’ moniker of jam-2.5. Folded rc2 and rc3 RELNOTES into

the main part of the document. Built new tar/zip balls.

Only 16 months later.

[git-p4: depot-paths = “//public/jam/src/”: change = 4409]

commit ca8870db541a23ed867f38847eda65bf4363371d

Author: Richard Geiger rmg@perforce.com

Date: Tue Apr 22 20:51:34 2003 -0800

Update derived jamgram.c

[git-p4: depot-paths = “//public/jam/src/”: change = 3108]

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

文末

我将这三次阿里面试的题目全部分专题整理出来,并附带上详细的答案解析,生成了一份PDF文档

  • 第一个要分享给大家的就是算法和数据结构

网易严选Java开发三面面经:HashMap+JVM+索引+消息队列

  • 第二个就是数据库的高频知识点与性能优化

网易严选Java开发三面面经:HashMap+JVM+索引+消息队列

  • 第三个则是并发编程(72个知识点学习)

网易严选Java开发三面面经:HashMap+JVM+索引+消息队列

  • 最后一个是各大JAVA架构专题的面试点+解析+我的一些学习的书籍资料

网易严选Java开发三面面经:HashMap+JVM+索引+消息队列

还有更多的Redis、MySQL、JVM、Kafka、微服务、Spring全家桶等学习笔记这里就不一一列举出来

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
1712101958007)]

[外链图片转存中…(img-WXnY1uSc-1712101958007)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

文末

我将这三次阿里面试的题目全部分专题整理出来,并附带上详细的答案解析,生成了一份PDF文档

  • 第一个要分享给大家的就是算法和数据结构

[外链图片转存中…(img-yH0kfCLs-1712101958008)]

  • 第二个就是数据库的高频知识点与性能优化

[外链图片转存中…(img-pfRK8Bet-1712101958008)]

  • 第三个则是并发编程(72个知识点学习)

[外链图片转存中…(img-AmeaLTIe-1712101958008)]

  • 最后一个是各大JAVA架构专题的面试点+解析+我的一些学习的书籍资料

[外链图片转存中…(img-E5pp3uNZ-1712101958009)]

还有更多的Redis、MySQL、JVM、Kafka、微服务、Spring全家桶等学习笔记这里就不一一列举出来

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐