加载中…
个人资料
手香的不得了
手香的不得了
  • 博客等级:
  • 博客积分:0
  • 博客访问:81,025
  • 关注人气:9
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
正文 字体大小:

Git 中文教程_2

(2009-08-05 14:32:58)
标签:

it

分类: linux

通过电子邮件交换工作

读过上一节之后,有的朋友可能要问,如果版本库是通过单向的下载协议发布的,如 HTTP, 我们就无法将工作上传到公共的版本库中。别人也不能访问我的机器来抓取我的工作,那怎么办呢?

不必担心,我们还有 email !别忘了 git 本来就是为了管理 Linux 的内核开发而设计的。 所以,它非常适合像 Linux Kernel 这样的开发组织形式高度分散,严重依赖 email 来进行交流的项目。

下面模拟你参加到《Git 中文教程》的编写工作中来,看看我们可以怎么通过 email 进行工作交流。 你可以通过下面的命令下载这个项目的版本库。

$ git-clone http://www.bitsun.com/git/gittutorcn.git

之后,你会在当前目录下得到一个叫 gittutorcn 的目录, 这就是你的项目的工作目录了。默认地,它会有两个分支: masterorigin,你可以直接在 master 下展开工作, 也可以创建你自己的工作分支,但是千万不要修改 origin 分支,切记! 因为它是公共版本库的镜像,如果你修改了它, 那么就不能生成正确的对公共版本库的 patch 文件了。
Note
如果你的确修改过 origin 分支的内容,那么在生成 patch 文件之前, 请用 git-reset --hard 命令将它逆转到最原始的,没经过任何修改的状态。

你可以直接在 master 下开展工作,也可以创建你自己的工作分支。 当你对项目做了一定的工作,并提交到库中。我们用 git-show-branch 命令先看下库的状态。

* [master] your buddy's contribution
 ! [origin] degining of git-format-patch example
--
*  [master] your buddy's contribution
*+ [origin] degining of git-format-patch example

上面就假设你已经提交了一个叫 "your buddy's contribution" 的工作。 现在我们来看看怎么通过 email 来交流工作了。

$ git-fetch origin    (1)
$ git-rebase origin    (2)
$ git-format-patch origin     (3)

(1)更新 origin 分支,防止 origin 分支不是最新的公共版本,产生错误的补丁文件;
(2)将你在 master 上提交的工作迁移到新的源版本库的状态的基础上;
(3)生成补丁文件;

上面的几个命令,会在当前目录下生成一个大概名为 0001-your-buddy-s-contribution.txt 补丁文件, 建议你用文本工具查看一下这个文件的具体形式,然后将这个文件以附件的形式发送到项目维护者的邮箱: vortune@gmail.com

当项目的维护者收到你的邮件后,只需要用 git-am 命令,就可以将你的工作合并到项目中来。

$ git-checkout -b buddy-incomming
$ git-am /path/to/0001-your-buddy-s-contribution.txt

用 Git 协同工作

假设 Alice 在一部机器上自己的个人目录中创建了一个项目 /home/alice/project, Bob 想在同一部机器自己的个人目录中为这个项目做点什么。

Bob 首先这样开始:

$ git-clone /home/alice/project myrepo

这样就创建了一个保存着 Alice 的版本库的镜像的新目录 "myrepo"。 这个镜像保存着原始项目的起点和它的发展历程。

接着 Bob 对项目做了些更改并提交了这些更改:

(编辑一些文件)
$ git-commit -a 
(如果需要的话再重复这个步骤)

当他搞定之后,他告诉 Alice 将他的东西从 /home/bob/myrepo 中引入,她只需要这样:

$ cd /home/alice/project
$ git pull /home/bob/myrepo

这样就将 Bob 的版本库中的 "master" 分支的变化引入了。 Alice 也可以通过在 pull 命令的后面加入参数的方式来引入其他的分支。

在导入了 Bob 的工作之后,用 "git-whatchanged" 命令可以查看有什么信的提交对象。 如果这段时间里以来,Alice 也对项目做过自己的修改,当 Bob 的修改被合并进来的时候, 那么她需要手动修复所有的合并冲突。

谨慎的 Alice 在导入 Bob 的工作之前,希望先检查一下。 那么她可以先将 Bob 的工作导入到一个新创建的临时分支中, 以方便研究 Bob 的工作:

$ git fetch /home/bob/myrepo master:bob-incoming

这个命令将 Bob 的 master 分支的导入到名为 bob-incoming 的分支中( 不同于 git-pull 命令,git-fetch 命令只是取得 Bob 的开发工作的拷贝, 而不是合并经来)。接着:

$ git whatchanged -p master..bob-incoming

这会列出 Bob 自取得 Alice 的 master 分支之后开始工作的所有变化。 检查过这些工作,并做过必须的调整之后, Alice 就可以将变化导入到她的 master 分支中:

$ git-checkout master
$git-pull . bob-incoming

最后的命令就是将 "bob-incoming" 分支的东西导入到 Alice 自己的版本库中的, 稍后,Bob 就可以通过下面的命令同步 Alice 的最新变化。

$ git-pull

注意不需为这个命令加入 Alice 的版本库的路径,因为当 Bob 克隆 Alice 的版本库的时候, git 已经将这个路径保存到 .git/remote/origin 文件中,它将会是所以的导入操作的默认路径。

Bob 可能已经注意到他并没有在他的版本库中创建过分支(但是分支已经存在了):

$ git branch
* master
  origin

"origin" 分支,它是运行 "git-clone" 的时候自动创建的,他是 Alice 的 master 分支的原始镜像, Bob 应该永远不要向这个分支提交任何东西。

如果 Bob 以后决定在另外一部主机上开展工作,那么他仍然需要通过 SSH 协议从新克隆和导入( Alice 的版本库):

$ git-clone alice.org:/home/alice/project/ myrepo

我们可以使用 git 自然协议,或者是 rsync, http 等协议的任何一种,详情请参考 git-pull

Git 同样可以建立类似 CVS 那样的开发模式,也就是所有开发者都向中心版本库提交工作的方式, 详情参考 git_pushgit for CVS users

为版本库打包

在前面,我们已经看到在 .git/objects/??/ 目录中保存着我们创建的每一个 git 对象。 这样的方式对于自动和安全地创建对象很有效,但是对于网络传输则不方便。 git 对象一旦创建了,就不能被改变,但有一个方法可以优化对象的存储,就是将他们“打包到一起”。

$ git repack

上面的命令让你做到这点,如果你一直是做着我们的例子过来的, 你现在大约会在 .git/objects/??/ 目录下积累了17个对象。 git-repack 会告诉你有几个对象被打包了, 并且将他们保存在 .git/objects/pack 目录当中。

Note
你将会看到两个文件,pack-*.pack and pack-*.idx.git/objects/pack 目录。他们的关系是很密切的, 如果你手动将他们拷贝到别的版本库中的话,你要决定将他们一起拷贝。 前者是保存着所有被打包的数据的文件,后者是随机访问的索引。

如果你是个偏执狂,就运行一下 git-verity-pack 命令来检查一下有缺陷的包吧, 不过,其实你无须太多担心,我们的程序非常出色 ;-).

一旦你已经对那些对象打包了,那么那些已经被打过包的原始的对象,就没有必要保留了。

$ git prune-packed

会帮你清楚他们。

如果你好奇的话,你可以在执行 git-prune-repacked 命令之前和之后, 都运行一下 find .git/objects -type f,这样你就能看到有多少没有打包的对象, 以及节省了多少磁盘空间。

Note
git pull git-pull 对于 HTTP 传输来说,一个打包过的版本库会将一定数量的 相关联的对象放进一个有关联性的打包中。如果你设想多次从 HTTP 公共版本库中导入数据, 你也许要频繁地 reapck & prune,要么就干脆从不这样做。

如果你此时再次运行 git-repack,它就会说 "Nothing to pack"。 要是你继续开发,并且积累了一定数量的变迁,再运行 git-repack 将会创建一个新的包, 它会包含你自上次对库打包以来创建的对象。我们建议你尽快在初始化提交之后打包一下你的版本库( 除非你现在的项目是个涂鸦式的草稿项目),并且在项目经历过一段很活跃的时期时, 再运行 git-repack 一下。

当一个版本库通过 git-pushgit-pull 命令来同步源版本库中打包过的对像的时候, 通常保存到目标版本库中的是解包了的对象,除非你使用的是 rsync(远程同步协议)协议的传输方式。 正是这种容许你在两头的版本库中有不同的打包策略的方式,他意味着你也许在过一段时间之后, 需要在两头的版本库中都重新打包一下。

将工作捆绑到一起

通过 git 的分支功能,你可以非常容易地做到好像在同一时间进行许多“相关-或-无关”的工作一样。

我们已经通过前面的 "fun and work" 使用两个分支的例子,看到分支是怎么工作的。 这样的思想在多于两个的分支的时候也是一样的,比方说,你现在在 master 的头, 并有些新的代码在 master 中,另外还有两个互不相关的补丁分别在 "commit-fix" 和 "diff-fix" 两个分支中。

$ git show-branch
! [commit-fix] Fix commit message normalization.
 ! [diff-fix] Fix rename detection.
  * [master] Release candidate #1
---
 +  [diff-fix] Fix rename detection.
 +  [diff-fix~1] Better common substring algorithm.
+   [commit-fix] Fix commit message normalization.
  * [master] Release candidate #1
++* [diff-fix~2] Pretty-print messages.

两个补丁我们都测试好了,到这里,你想将他们俩合并起来, 于是你可以先合并 diff-fix ,然后再合并 commit-fix,像这样:

$ git merge 'Merge fix in diff-fix' master diff-fix
$ git merge 'Merge fix in commit-fix' master commit-fix

结果如下:

$ git show-branch
! [commit-fix] Fix commit message normalization.
 ! [diff-fix] Fix rename detection.
  * [master] Merge fix in commit-fix
---
  - [master] Merge fix in commit-fix
+ * [commit-fix] Fix commit message normalization.
  - [master~1] Merge fix in diff-fix
 +* [diff-fix] Fix rename detection.
 +* [diff-fix~1] Better common substring algorithm.
  * [master~2] Release candidate #1
++* [master~3] Pretty-print messages.

然而,当你确信你手头上的确是一堆互不相关的项目变化时,就没有任何理由将这堆东西一个个地合并( 假如他们的先后顺序很重要,那么他们就不应该被定以为无关的变化), 你可以一次性将那两个分支合并到当前的分支中,首先我们将我们刚刚做过的事情逆转一下, 我们需要通过将 master 分支重置到 master~2 位置的方法来将它逆转到合并那两个分支之前的状态。

$ git reset --hard master~2

你可以用 git-show-branch 来确认一下的确是回到了两次 git-merge 的状态了。 现在你可以用一行命令将那两个分支导入的方式来替代两次运行( 也就是所谓的 炮制章鱼 -- making an Octopusgit-merge

$ git pull . commit-fix diff-fix
$ git show-branch
! [commit-fix] Fix commit message normalization.
 ! [diff-fix] Fix rename detection.
  * [master] Octopus merge of branches 'diff-fix' and 'commit-fix'
---
  - [master] Octopus merge of branches 'diff-fix' and 'commit-fix'
+ * [commit-fix] Fix commit message normalization.
 +* [diff-fix] Fix rename detection.
 +* [diff-fix~1] Better common substring algorithm.
  * [master~1] Release candidate #1
++* [master~2] Pretty-print messages.

注意那些不适合制作章鱼的场合,尽管你可以那样做。一只“章鱼”往往可以使项目的提交历史更具可读性, 前提是你在同一时间导入的两份以上的变更是互不关联的。 然而,如果你在合并任何分支的过程中出现合并冲突,并且需要手工解决的话, 那意味着这些分支当中有相互干涉的开发工作在进行,那么你就应该将这个两个冲突先合并, 并且记录下你是如何解决这个冲突,以及你首先处理他们的理由。(译者按:处理完冲突之后, 你就可以放心制作“章鱼”了) 否则的话将会造成项目的发展历史很难跟踪。

管理版本库

版本库的管理员可以用下面的工具来建立和维护版本库。

  • git-daemon(1) 容许匿名下载版本库。

  • git-shell(1) 面向中心版本库模式 的用户的类似 受限的 shell 的命令。

update hook howto 一个很好的管理中心版本库的例子。

例子

在 /pub/scm 上运行 git 守护进程
$ grep git /etc/inet.conf
git     stream  tcp     nowait  nobody \
  /usr/bin/git-daemon git-daemon --inetd --syslog --export-all /pub/scm

这个配置行应该在配置文件中用一行来写完。

仅给开发者 push/pull 的访问权限。
$ grep git /etc/passwd (1)
alice:x:1000:1000::/home/alice:/usr/bin/git-shell
bob:x:1001:1001::/home/bob:/usr/bin/git-shell
cindy:x:1002:1002::/home/cindy:/usr/bin/git-shell
david:x:1003:1003::/home/david:/usr/bin/git-shell
$ grep git /etc/shells (2)
/usr/bin/git-shell
 
(1) 将用户的登录 shell 设定为 /usr/bin/git-shell,
它除了运行 "git-push" 和 "git-pull" 不能做任何事。
这样用户就可以通过 ssh 来访问机器。
(2) 许多的发行版需要在 /etc/shells 配置文件中列明要用什么 shell 来作为登录 shell。

CVS - 模式的公共库。
$ grep git /etc/group (1)
git:x:9418:alice,bob,cindy,david
$ cd /home/devo.git
$ ls -l (2)
  lrwxrwxrwx   1 david git    17 Dec  4 22:40 HEAD -> refs/heads/master
  drwxrwsr-x   2 david git  4096 Dec  4 22:40 branches
  -rw-rw-r--   1 david git    84 Dec  4 22:40 config
  -rw-rw-r--   1 david git    58 Dec  4 22:40 description
  drwxrwsr-x   2 david git  4096 Dec  4 22:40 hooks
  -rw-rw-r--   1 david git 37504 Dec  4 22:40 index
  drwxrwsr-x   2 david git  4096 Dec  4 22:40 info
  drwxrwsr-x   4 david git  4096 Dec  4 22:40 objects
  drwxrwsr-x   4 david git  4096 Nov  7 14:58 refs
  drwxrwsr-x   2 david git  4096 Dec  4 22:40 remotes
$ ls -l hooks/update (3)
  -r-xr-xr-x   1 david git  3536 Dec  4 22:40 update
$ cat info/allowed-users (4)
refs/heads/master       alice\|cindy
refs/heads/doc-update   bob
refs/tags/v[0-9]*       david
(1) 将所有的开发人员都作为 git 组的成员。
(2) 并且给予他们公共版本库的写权限。
(3) 用一个在 Documentation/howto/ 中的 Carl 写的例子来实现版本库的分支控制策略。
(4) Alice 和 Cindy 可以提交入 master 分支,只有 Bob 能提交入 doc-update 分支,
David 则是发行经理只有他能创建并且 push 版本标签。

支持默协议传输的 HTTP 服务器。
dev$ git update-server-info (1)
dev$ ftp user@isp.example.com (2)
ftp> cp -r .git /home/user/myproject.git
(1) 保证 info/refs 和 object/info/packs 是最新的。
(2) 上传到你的 HTTP 服务器主机。

项目开发的模式推介

尽管 git 是一个正式项目发布系统,它却可以方便地将你的项目建立在松散的开发人员组织形式上。 Linux 内核的开发,就是按这样的模式进行的。在 Randy Dunlap 的著作中("Merge to Mainline" 第17页) 就有很好的介绍(http:///a2jdg)。

需要强调的是正真的非常规的开发组织形式, git 这种组织形式,意味着对于工作流程的约束,没有任何强迫性的原则。 你不必从唯一一个远程版本库中导入(工作目录)。

项目领导人(project lead)的工作推介

  1. 在你自己的本地机器上准备好主版本库。你的所有工作都在这里完成。

  2. 准备一个能让大家访问的公共版本库。

    如果其他人是通过默协议的方式(http)来导入版本库的,那么你有必要保持这个 默协议的友好性git-init-db 之后,复制自标准模板库的 $GIT_DIR/hooks/post-update 将包含一个对 git-update-server-info 的调用,但是 post-update 默认是不能唤起它自身的。 通过 chmod +x post-update 命令使能它。这样让 git-update-server-info 保证那些必要的文件是最新的。

  3. 将你的主版本库推入公共版本库。

  4. git-repack 公共版本库。这将建立一个包含初始化提交对象集的打包作为项目的起始线, 可能的话,执行一下 git-prune,要是你的公共库是通过 pull 操作来从你打包过的版本库中导入的。

  5. 在你的主版本库中开展工作,这些工作可能是你自己的最项目的编辑, 可能是你由 email 收到的一个补丁,也可能是你从这个项目的“子系统负责人” 的公共库中导入的工作等等。

    你可以在任何你喜欢的时候重新打包你的这个私人的版本库。

  6. 将项目的进度推入公共库中,并给大家公布一下。

  7. 尽管一段时间以后,"git-repack" 公共库。并回到第5步继续工作。

项目的子系统负责人(subsystem maintainer)也有自己的公共库,工作流程大致如下:

  1. 准被一个你自己的工作目录,它通过 git-clone 克隆自项目领导人的公共库。 原始的克隆地址(URL)将被保存在 .git/remotes/origin 中。

  2. 准备一个可以给大家访问的公共库,就像项目领导人所做的那样。

  3. 复制项目领导人的公共库中的打包文件到你的公共库中, 除非你的公共库和项目领导人的公共库是在同一部主机上。 以后你就可以通过 objects/info/alternates 文件的指向来 浏览它所指向的版本库了。

  4. 将你的主版本库推入你的公共版本库,并运行 git-repack, 如果你的公共库是通过的公共库是通过 pull 来导入的数据的话, 再执行一下 git-prune
  5. 在你的主版本库中开展工作。这些工作可能包括你自己的编辑,来自 email 的补丁, 从项目领导人,“下一级子项目负责人”的公共库哪里导入的工作等等。

    你可以在任何时候重新打包你的私人版本库。

  6. 将你的变更推入公共库中,并且请“项目领导人”和“下级子系统负责人”导入这些变更。

  7. 每隔一段时间之后,git-repack 公共库。回到第 5 步继续工作。

“一般开发人员”无须自己的公共库,大致的工作方式是:

  1. 准备你的工作库,它应该用 git-clone 克隆自“项目领导人”的公共库( 如果你只是开发子项目,那么就克隆“子项目负责人”的)。 克隆的源地址(URL)会被保存到 .git/remotes/origin 中。

  2. 在你的个人版本库中的 master 分支中开展工作。

  3. 每隔一段时间,向上游的版本库运行一下 git-fetch origin 。 这样只会做 git-pull 一半的操作,即只克隆不合并。 公共版本库的新的头就会被保存到 .git/refs/heads/origins

  4. git-cherry origin 命令,看一下你有什么补丁被接纳了。 并用 git-rebase origin 命令将你以往的变更迁移到最新的上游版本库的状态中。 (关于 git-rebase 命令,请参考 git-rebase

  5. git-format-patch origin 生成 email 形式的补丁并发给上游的维护者。 回到第二步接着工作。

//http://www.linuxsir.org/main/doc/git/gittutorcn.htm

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
后一篇:VIM常用命令
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

    后一篇 >VIM常用命令
      

    新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有