分布式的工作流程

假设Alice现在开始了一个新项目,在/home/alice/project建了一个新的git 仓库(repository);另外Bob的工作目录也在同一台机器,他要提交代码。

Bob 执行了这样的命令:

$ git clone /home/alice/project myrepo

这就建了一个新的叫"myrepo"的目录,这个目录里包含了一份Alice的仓库的 克隆(clone). 这份克隆和原始的项目一模一样,并且拥有原始项目的历史记 录。

Bob 做了一些修改并且提交(commit)它们:

(edit files)
$ git commit -a
(repeat as necessary)

当他准备好了,他告诉Alice从仓库/home/bob/myrepo中把他的修改给拉 (pull)下来。她执行了下面几条命令:

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

这就把Bob的主(master)分支合并到了Alice的当前分支里了。如果Alice在 Bob修改文件内容的同时也做了修改的话,她可能需要手工去修复冲突. (注意:"master"参数在上面的命令中并不一定是必须的,因为这是一个 默认参数)

git pull命令执行两个操作: 它从远程分支(remote branch)抓取修改 的内容,然后把它合并进当前的分支。

如果你要经常操作远程分支(remote branch),你可以定义它们的缩写:

$ git remote add bob /home/bob/myrepo

这样,Alic可以用"git fetch"来执行"git pull"前半部分的工作, 但是这条命令并不会把抓下来的修改合并到当前分支里。

$ git fetch bob

我们用git remote命令建立了Bob的运程仓库的缩写,用这个(缩写) 名字我从Bob那得到所有远程分支的历史记录。在这里远程分支的名 字就叫bob/master.

$ git log -p master..bob/master

上面的命令把Bob从Alice的主分支(master)中签出后所做的修改全部显示出来。

当检查完修改后,Alice就可以把修改合并到她的主分支中。

$ git merge bob/master

这种合并(merge)也可以用pull来完成,就像下面的命令一样:

$ git pull . remotes/bob/master

注意:git pull 会把远程分支合并进当前的分支里,而不管你在命令 行里指定什么。

其后,Bob可以更新它的本地仓库--把Alice做的修改拉过来(pull):

$ git pull

如果Bob从Alice的仓库克隆(clone),那么他就不需要指定Alice仓库的地 址;因为Git把Alice仓库的地址存储到Bob的仓库配库文件,这个地址就是 在git pull时使用:

$ git config --get remote.origin.url
/home/alice/project

(如果要查看git clone创建的所有配置参数,可以使用"git config -l", git config 的帮助文件里解释了每个参数的含义.)

Git同时也保存了一份最初(pristine)的Alice主分支(master),在 "origin/master"下面。

$ git branch -r
  origin/master

如果Bob打算在另外一台主机上工作,他可以通过ssh协议来执行"clone" 和"pull"操作:

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

git有他自带的协议(native protocol),还可以使用rsync或http; 你可以点 这里 git pull 看一看更詳細的用法。

Git也可以像CVS一样来工作:有一个中心仓库,不同的用户向它推送(push) 自己所作的修改;你可以看看这里: git push gitcvs-migration.

公共Git仓库

另外一个提交修改的办法,就是告诉项目的维护者(maintainer)用 git pull 命令从你的仓库里把修改拉下来。这和从主仓库"里更新代码类似,但是是从 另外一个方向来更新的。

如果你和维护者(maintainer)都在同一台机器上有帐号,那么你们可以互相从对 方的仓库目录里直接拉(pull)所作的修改;git命令里的仓库地址也可以是本地 的某个目录名:

$ git clone /path/to/repository
$ git pull /path/to/other/repository

也可以是一个ssh地址:

$ git clone ssh://yourhost/~you/repository

如果你的项目只有很少几个开发者,或是只需要同步很少的几个私有仓库, 上面的方法也许够你用的。

然而,更通用的作法是维护几个不同的公开仓库(public repository). 这样可以把每个人的工作进度和公开仓库清楚的分开。

你还是每天在你的本地私人仓库里工作,但是会定期的把本地的修改推(push) 到你的公开仓库中;其它开发者就可以从这个公开仓库来拉(pull)最新的代码。 如果其它开发者也有他自己的公共仓库,那么他们之间的开发流程就如下图 所示:

                        you push
  your personal repo ------------------> your public repo
    ^                                     |
    |                                     |
    | you pull                            | they pull
    |                                     |
    |                                     |
        |               they push             V
  their public repo <------------------- their repo

将修改推到一个公共仓库

通过http或是git协议,其它维护者可以抓取(fetch)你最近的修改,但是他们 没有写权限。这样,这需要将本地私有仓库的最近修改上传公共仓库中。

译者注: 通过http的WebDav协议是可以有写权限的,也有人配置了git over http.

最简单的办法就是用 git push命令 和ssh协议; 用你本地的"master" 分支去更新远程的"master"分支,执行下面的命令:

$ git push ssh://yourserver.com/~you/proj.git master:master

or just

或是:

$ git push ssh://yourserver.com/~you/proj.git master

和git-fetch命令一样git-push如果命令的执行结果不是"快速向前"(fast forward) 就会报错; 下面的章节会讲如何处理这种情况.

推(push)命令的目地仓库一般是个裸仓库(bare respository). 你也可以推到一 个签出工作目录树(checked-out working tree)的仓库,但是工作目录中内 容不会被推命令所更新。如果你把自己的分支推到一个已签出的分支里,这 会导致不可预知的后果。

在用git-fetch命令时,你也可以修改配置参数,让你少打字:)。

下面这些是例子:

$ cat >>.git/config <<EOF
[remote "public-repo"]
    url = ssh://yourserver.com/~you/proj.git
EOF

你可以用下面的命令来代替前面复杂的命令:

$ git push public-repo master

你可以点击这里: git config,查看remote..url, branch..remote, 和remote..push等选项的解释.

当推送代码失败时要怎么办

如果推送(push)结果不是"快速向前"(fast forward),那么它 可能会报像下面一样的错误:

error: remote 'refs/heads/master' is not an ancestor of
local  'refs/heads/master'.
Maybe you are not up-to-date and need to pull first?
error: failed to push to 'ssh://yourserver.com/~you/proj.git'

这种情况通常由以下的原因产生:

- 用 `git-reset --hard` 删除了一个已经发布了的一个提交,或是

- 用 `git-commit --amend` 去替换一个已经发布的提交,或是

- 用 `git-rebase` 去rebase一个已经发布的提交.  

你可以强制git-push在上传修改时先更新,只要在分支名前面加一个加号。

$ git push ssh://yourserver.com/~you/proj.git +master

通常不论公共仓库的分支是否被修改,他都被修改为指向原来指向的提交(commit) 跟随的下一个提交(commit)。如果在这种情况下强制地推送,你就破坏了之前的约定。

尽管如此,这也是一种通常的用法来简单地发布一系列正在修正的补丁,并且只要你 通知了其他的开发者你打算怎样操作这个分支,这也是一种可以接受的折中办法。

一个推送(push)也可能因为其他人有向这个仓库(repository)推送的权利而失败。 在这种情况下,正确地解决办法是首先用"pull"命令或者"fetch"命令和"rebase" 命令更新你的代码,然后重新尝试推送(push);更详细的了解请看下一部分和 gitcvs-migration

gitcast:c8-dist-workflow



github logo