分布式的工作流程

假设 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”,linkgit:git-config[1] 的帮助文件里解释了每个参数的含义)

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;你可以点这里linkgit:git-pull[1]看一看更詳細的用法。

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

公共 Git 仓库

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

如果你和维护者(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。

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

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

或是:

$ 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

你可以点击这里:linkgit:git-config[1],查看remote.<name>.url,branch.<name>.remote和remote.<name>.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);更详细的了解请看下一部分和linkgit:gitcvs-migration[7]。

联系我们

邮箱 626512443@qq.com
电话 18611320371(微信)
QQ群 235681453

Copyright © 2015-2022

备案号:京ICP备15003423号-3