Git同步(Syncing)
在SVN中开发者使用一个中央仓库来合作开发。大家通过将代码在工作区和中央仓库之间传输来合作开发。这跟git的合作模式完全不同,git中开发者拥有仓库的完整拷贝,提交历史、分支结构都是完整的。用户通常提交一系列本地提交而不是单一的修改到服务器。git可以让你在不同仓库间分享所有分支。
下面的命令用来管理所有仓库连接。通过push分支到中央仓库来提交修改,通过pull远程仓库到本地仓库来查看其他人的修改。
git remote
git remote 命令让你可以创建、查看和删除同其他仓库的连接。远程连接类似浏览器中的标签(bookmark)而不是直接链接其他仓库。git为了方便大家使用别名代替URL
。例如,下面的表格向你展示连接至中央仓库和其他开发者仓库的两个连接,他们在git命令中传入origin和john而不是完整的URL。
如何使用
git remote
列出你本地所有其他仓库的连接
git remote -v
和之前的命令类似,不过这个命令执行结果包含每个连接的URL。
git remote add <name> <url>
创建一个远程仓库的连接。在添加完连接后,你可以在其他命令中使用
git remote rm <name>
删除一个叫
git remote rename <old-name> <new-name>
重命名一个远程连接。使用新名字代替旧名。
讨论
Git的设计让开发者拥有完全独立的开发环境。这意味着信息不会自动在仓库之间传输。而需要开发者手动拉取上游的提交到本地仓库,或者推送回本地提交到中央仓库。git remote命令是传递URL给分享(sharing)命令的更容易的方式。
The origin Remote
当你使用clone命令clone一个仓库时,它自动回创建一个叫做origin指向被克隆的远程仓库。这个对开发者在创建一个远程仓库拷贝非常有用,因为它为开发者提供了很容易的方式来拉取上游改变或者发布本地提交。这也是为什么大多数采取git管理源代码的远程仓库被称作origin。
Repository URLs
Git支持许多连接远程仓库的方式。两个最简单的连接远程仓库的方式是通过http和ssh协议。http可以很容易的允许匿名访问,只读访问一个仓库,比如:
http://host/path/to/repo.git
但是,它通常不太容易推送提交到http地址(通常我们不希望允许匿名推送)。为了拥有读写访问,你应当使用ssh协议替代:
ssh://user@host/path/to/repo.git
你将需要在主机上有一个有效的ssh账号,git支持通过ssh授权访问。
示例
在origin之外,也很容易添加你队友的仓库连接。比如,如果你的同事小明,维护一个对外发布的可访问仓库dev.example.com/john.git,你可以通过下面这条命令添加一个连接:
git remote add john http://dev.example.com/john.git
可以添加某个开发者的仓库,让git可以在中央仓库之外进行合作。这对小团队在大型项目中合作非常有用。
git fetch
git fetch命令会从远程仓库倒入提交到本地仓库。最终提交结果存储在远程分支而不是一般的本地工作分支。这让你有机会在将修改集成到你的项目拷贝前评审(review)代码。
使用
git fetch <remote>
获取远程仓库所有分支。这个命令同时会下载这个仓库的所有需要下载的提交和文件。
git fetch <remote> <branch>
可前面的命令类似,但是它只获取指定的分支。
讨论
获取(fetching)可以让你知道其他同事写了哪些代码。因为获取的内容存储在一个远程分支上。它不会影响你本地开发工作。获取可以让你将代码集成到本地仓库之前安全地评审(review)修改。这个和svn update类似,可以让你知道中央仓库的提交历史的进度,但是它并不强制合并这些修改到你的仓库。
Remote Branches
远程仓库就像本地仓库一样,除了他们代表其他人仓库的提交。你可以像本地仓库一样 检出(checkout)
一个远程仓库。但是这让你进入分离头(detached HEAD)
状态(类似检出到一个以前的提交一样)。你可以把远程分支看成不可修改的分支。你可以在git branch
命令中加上-r
参数来查看原称分支。查看到的远程分支会在前面加上他们所属的远程仓库,这样你就不会将他们和本地分支混淆。例如,下面的命令块展示了在执行fetch命令后所有origin下的分支。
git branch -r
#origin/master
#origin/develop
#origin/some-feature
再说一次,你可以使用git checkout
和git log
命令来查看分支内容。如果你觉得远程分支的修改没有问题,你可以使用git merge
命令把他们合并进本地分支了。因此和SVN不同,git同步你的本地仓库和远程仓库需要两步过程:获取和合并。git pull
命令是这两步骤的结合,让这两步变成一步。
举例
这个例子展示一个典型的工作流中是如何同步你的本地仓库和你中央仓库的master分支。
git fetch origin
这个命令会展示下载的分支。
a1e8fb5..45e66a4 master -> origin/master
a1e8fb5..9e8ab1c develop -> origin/develop
* [new branch] some-feature -> origin/some-feature
下方表格中的方框代表远程分支的提交,如图所示,git fetch
让你拥有访问其他仓库整个分支结构的权限。
为了查看上游的master分支增加哪些提交,你可以使用git log
,并使用origin/master
作为条件。
git log --oneline master..origin/master
为了通过修改并合并他们到你本地的master,你可以使用如下命令:
git checkout master
git log origin/master
然后我们能够使用git merge origin/master
git merge origin/master
这样origin/master
分支和master
分支就指向了同意个提交,并完成了和上游开发的同步了。
git pull
本地仓库和上游修改合并是git合作开发流的常见任务。我们已经知道了如何使用git fetch
加上 git merge
来完成这项工作。但是git pull 一条命令就可以搞定。
使用
git pull <remote>
获取指定远程仓库的当前分支同名的拷贝,并立即和当前分支合并。这和git fetch <remote>
加上git merge origin/<current-branch>
作用等同。
git pull --rebase <remote>
和上面的命令类似。但是它使用git rebase
代替git merge
来集成合并远程分支。
讨论
你可以把git pull
看作git版的svn update
,它让同步本地仓库和上游修改变的很容易。下图的表格展示了合并每一步的进行过程。
刚开始你的仓库是同步的,但是在你git fetch
之后origin
指向的仓库跟你上一次检查已经有了新的内容。然后你使用git merge
立即将远程master
集成合并到本地master
。
Pulling 和 Rebase对比
--rebase
能够确保线性分支历史,没有不必要的合并提交。许多开发者喜欢rebasing代替merging。因为它就好比告诉你:“我想让我的修改保持在其他人修改的上方”。从这个角度讲,使用 git pull
加上--rebase
标志相比简单的git pull
更像svn update
事实上,使用--rebase
进行拉取非常普遍,以至于有一个它的专门的配置项:
git config --global branch.autosetuprebase always
执行这个命令之后,所有的git pull
命令将会使用git rebase命令代替git merge
。
举例
下面这个命令展示如何和中央仓库的master分支进行同步:
git checkout master
git pull --rebase origin
这个命令会将你本地的修改追加到其他同事修改的上面。
git push
推送(push)用于把本地仓库提交传输到远程仓库。它和git fetch
作用相反。但是鉴于获取导入远程提交到本地分支,推送输出提交到远程分支,它有可能会覆盖修改,因此你需要小心的使用它。下面我们来讨论这些问题。
使用
git push <remote> <branch>
推送必要的的提交和内部对象到远程指定分支。这个将在目标仓库创建一个本地分支。为了阻止重写提交,当你推送会导致远程仓库出现non-fast-forward合并,git会阻止这样的推送。
git push <remote> --force
和上面的命令类似,但是不管是否会导致non-fast-forward合并它会强制推送。不要使用–force标志,除非你绝对清楚你这样做没有问题。
git push <remote> --all
推送所有的本地分支到指定远程仓库。
git push <remote> --tags
当你推送分支时标签(tags)不会自动推送,除非你使用了–tags选项。–tags将会发送所有的本地标签到远程。
讨论
git push通常用来将本地修改发布到远程中央仓库。当你在本地积累了几个提交之后并打算把他们发布出去,以便其他团队成员可以使用。你可以使用可交互的rebase精简提交,然后再推送到远程分支。
上图展示了你本地和远程master的差异。然后你通过git push origin master
命令推送修改。注意git push
的本质是在远程仓库内部执行了git merge master
。
强制推送(Force Pushing)
当使用git push会导致non-fast-forword合并时,git为了防止复写中央仓库提交,会拒绝推送。因此,如果远程提交历史和你的本地提交历史不同,你需要拉取远程分支,合并到到本地分支,然后才能再次尝试推送。这跟SVN类似,你需要通过svn update
和中央仓库同步,然后才能提交修改。
使用--force
参数可以代替上面的步骤,让远程分支和本地分支匹配。在你上次推送之后,删除任何上游分支的修改有可能发生。只有当你知道你的提交有问题,然后你在本地用一个git commit --amend
命令做了修改或者一个可交互的rebase
之后,你可以使用强制推送(force push)。你必须确定在你使用--force
选项之前没有其他同事拉取那些你提交的有问题的提交。
只推送到裸仓库
额外补充,你应当仅仅推送到使用--bare
标志创建的仓库。因为推送会搞乱远程分支的结构。不用推送到另外一个开发者的仓库这一点很重要。但是裸仓库不包含任何工作目录,所以不太可能打断其他人的开发。
举例
下面的例子描述一个标准的发布本地代码到中央仓库的方法。首先,通过拉取中央仓库拷贝确保你本地分支最新,然后通过reabse
合并你的修改到分支提交历史最上面。使用交互式合并(interactive rebase)也是在对外公开你的代码前清理提交的一个好机会。然后,git push
命令将你所有本地仓库的提交发送到远程仓库。
git checkout master
git fetch origin master
git rebase -i origin/master
# Squash commits, fix up commit messages etc.
git push origin master
因为你已经确保了本地master分支是最新的,所以结果是fast-forward
合并,git push
不会报出任何我们之前讨论的non-fast-forward
问题。