Fix some images link error (#63)
This commit is contained in:
@ -250,7 +250,7 @@ git log master..feature
|
||||
|
||||
其中的 `master..feature` 范围包含了在 feature 分支而不在 master 分支中所有的提交。换句话说,这个命令可以看出从 master 分支 fork 到 feature 分支后发生了哪些变化。它可以这样可视化:
|
||||
|
||||

|
||||

|
||||
|
||||
注意如果你更改范围的前后顺序(feature..master),你会获取到 master 分支而非 feature 分支上的所有提交。如果 `git log` 输出了全部两个分支的提交,这说明你的提交历史已经分叉了。
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
提交是 Git 的精髓所在,你无时不刻不在创建和缓存提交、查看以前的提交,或者用各种Git命令在仓库间转移你的提交。大多数的命令都对同一个提交操作,而有些会接受提交的引用作为参数。比如,你可以给 `git checkout` 传入一个引用来查看以前的提交,或者传入一个分支名来切换到对应的分支。
|
||||
|
||||

|
||||

|
||||
|
||||
知道提交的各种引用方式之后,Git 的命令就会变得更加强大。在这章中,我们研究提交的各种引用方式,来一窥 `git checkout`、`git branch`、`git push` 等命令的工作原理。
|
||||
|
||||
@ -208,7 +208,7 @@ git show HEAD^2^1
|
||||
|
||||
为了阐明 `~` 和 `^` 是如何工作的,下面这张图告诉你如何使用相对引用,来指向任意的提交。有的提交可以通过多种方式引用。
|
||||
|
||||

|
||||

|
||||
|
||||
相对引用在命令中的用法和普通的引用相同。比如,下面所有命令中使用的都是相对引用:
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
Git 钩子是在 Git 仓库中特定事件发生时自动运行的脚本。它可以让你自定义 Git 内部的行为,在开发周期中的关键点触发自定义的行为。
|
||||
|
||||

|
||||

|
||||
|
||||
Git 钩子最常见的使用场景包括推行提交规范,根据仓库状态改变项目环境,和接入持续集成工作流。但是,因为脚本可以完全定制,你可以用 Git 钩子来自动化或者优化你开发工作流中任意部分。
|
||||
|
||||
@ -78,7 +78,7 @@ with open(commit_msg_filepath, 'w') as f:
|
||||
|
||||
在开发团队中维护钩子是比较复杂的,因为 `.git/hooks` 目录不随你的项目一起拷贝,也不受版本控制影响。一个简单的解决办法是把你的钩子存在项目的实际目录中(在 `.git` 外)。这样你就可以像其他文件一样进行版本控制。为了安装钩子,你可以在 `.git/hooks` 中创建一个符号链接,或者简单地在更新后把它们复制到 `.git/hooks` 目录下。
|
||||
|
||||

|
||||

|
||||
|
||||
作为备选方案,Git 同样提供了一个模板目录机制来更简单地自动安装钩子。每次你使用 `git init` 或 `git clone` 时,模板目录文件夹下的所有文件和目录都会被复制到 `.git` 文件夹。
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ git branch -m <branch>
|
||||
|
||||
在 Git 中,分支是你日常开发流程中的一部分。当你想要添加一个新的功能或是修复一个 bug 时——不管 bug 是大是小——你都应该新建一个分支来封装你的修改。这确保了不稳定的代码永远不会被提交到主代码库中,它同时给了你机会,在并入主分支前清理你 feature 分支的历史。
|
||||
|
||||

|
||||

|
||||
|
||||
比如,上图将一个拥有两条独立开发线的仓库可视化,其中一条是一个不起眼的功能,另一条是长期运行的功能。使用分支开发时,不仅可以同时在两条线上工作,还可以保持主要的 `master branch` 不混入奇怪的代码。
|
||||
|
||||
@ -66,7 +66,7 @@ Git 分支背后的实现远比 SVN 的模型要轻量。与其在目录之间
|
||||
|
||||
分支只是指向提交的 *指针* ,理解这一点很重要。当你创建一个分支是,Git 只需要创建一个新的指针——仓库不会受到任何影响。因此,如果你最开始有这样一个仓库:
|
||||
|
||||

|
||||

|
||||
|
||||
接下来你用下面的命令创建了一个分支:
|
||||
|
||||
@ -76,7 +76,7 @@ git branch crazy-experiment
|
||||
|
||||
仓库历史保持不变。你得到的是一个指向当前提交的新的指针:
|
||||
|
||||

|
||||

|
||||
|
||||
注意,这只会 *创建* 一个新的分支。要开始在上面添加提交,你需要用 `git checkout` 来选中这个分支,然后使用标准的 `git add` 和 `git commit` 命令。
|
||||
|
||||
@ -147,7 +147,7 @@ git checkout -b <new-branch> <existing-branch>
|
||||
|
||||
有个警告会告诉你所做的更改和项目的其余历史处于「分离」的状态。如果你在分离 `HEAD` 状态开始开发新功能,没有分支可以让你回到之前的状态。当你不可避免地 checkout 到了另一个分支(比如你的更改并入了这个分支),你将不再能够引用你的 feature 分支:
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
重点是,你应该永远在分支上开发——而绝不在分离的 `HEAD` 上。这样确保你一直可以引用到你的新提交。不过,如果你只是想查看旧的提交,那么是否处于分离 `HEAD` 状态并不重要。
|
||||
|
||||
@ -203,11 +203,11 @@ git merge --no-ff <branch>
|
||||
|
||||
当当前分支顶端到目标分支路径是线性之时,我们可以采取 **快速向前合并** 。Git 只需要将当前分支顶端(快速向前地)移动到目标分支顶端,即可整合两个分支的历史,而不需要“真正”合并分支。它在效果上合并了历史,因为目标分支上的提交现在在当前分支可以访问到。比如,`some-feature` 到 `master` 分支的快速向前合并会是这样的:
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
但是,如果分支已经分叉了,那么就无法进行快速向前合并。当和目标分支之间的路径不是线性之时,Git 只能执行 **三路合并** 。三路合并使用一个专门的提交来合并两个分支的历史。这个术语取自这样一个事实,Git 使用 *三个* 提交来生成合并提交:两个分支顶端和它们共同的祖先。
|
||||
|
||||

|
||||

|
||||
|
||||
但你可以选择使用哪一种合并策略时,很多开发者喜欢使用快速向前合并(搭配 rebase 使用)来合并微小的功能或者修复 bug,使用三路合并来整合长期运行的功能。后者导致的合并提交作为两个分支的连接标志。
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ git add -p
|
||||
|
||||
在一个只有编辑、缓存、提交这样基本流程的项目上开发。首先,你要在工作目录中编辑你的文件。当你准备备份项目的当前状态时,你通过 `git add` 来缓存更改。当你对缓存的快照满意之后,你通过 `git commit` 将它提交到你的项目历史中去。
|
||||
|
||||

|
||||

|
||||
|
||||
`git add` 命令不能和 `svn add` 混在一起理解,后者将文件添加到仓库中。而 `git add` 发生于更抽象的 *更改* 层面。也就是说,`git add` 在每次你修改一个文件时都需要被调用,而 `svn add` 只需要每个文件调用一次。这听上去很多余,但这样的工作流使得一个项目更容易组织。
|
||||
|
||||
@ -100,7 +100,7 @@ git commit -a
|
||||
|
||||
SVN 和 Git 除了使用上存在巨大差异,它们底层的实现同样遵循截然不同的设计哲学。SVN 追踪文件的 *变化* ,而 Git 的版本控制模型基于 *快照* 。比如说,一个 SVN 提交由仓库中原文件相比的差异(diff)组成。而 Git 在每次提交中记录文件的 *完整内容* 。
|
||||
|
||||

|
||||

|
||||
|
||||
这让很多 Git 操作比 SVN 来的快得多,因为文件的某个版本不需要通过版本间的差异组装得到——每个文件完整的修改能立刻从 Git 的内部数据库中得到。
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ SVN 使用唯一的中央仓库作为开发者之间沟通的桥梁,在开发
|
||||
|
||||
例如,下图显示了你的仓库和中央仓库以及另一个开发者仓库之间的远程连接。你可以向 Git 命令传递 origin 和 john 的别名来引用这些仓库,替代完整的 URL。
|
||||
|
||||

|
||||

|
||||
|
||||
### 用法
|
||||
|
||||
@ -137,7 +137,7 @@ a1e8fb5..9e8ab1c develop -> origin/develop
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
@ -184,7 +184,7 @@ git pull --rebase <remote>
|
||||
|
||||
你可以将 `git pull` 当做 Git 中对应 `svn update` 的命令。这是同步你本地仓库和上游更改的简单方式。下图揭示了 pull 过程中的每一步。
|
||||
|
||||

|
||||

|
||||
|
||||
你认为你的仓库已经同步了,但 `git fetch` 发现 origin 中 `master` 的版本在上次检查后已经有了新进展。 接着 `git merge` 立即将 `remote master` 并入本地的分支。
|
||||
|
||||
@ -248,7 +248,7 @@ git push <remote> --tags
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ Pull Request 是开发者使用 GitHub 进行协作的利器。这个功能为
|
||||
|
||||
但是,Pull Request 不只是一个通知,还是一个专注于某个提议功能的讨论版。 如果更改导致了任何问题,团队成员可以在 Pull Request 下发布反馈,甚至推送后续提交来修改这个 Pull Request。所有的活动都在这个 Pull Request里之间追踪。
|
||||
|
||||

|
||||

|
||||
|
||||
和其他协作模型相比,这种共享提交的解决方案形成了更加线性的工作流。SVN 和 Git 都能通过一个简单的脚本发送通知邮件;但是,如果要讨论更改,开发者不得不在邮件里回复。这会变得愈发杂乱无章,尤其是后续提交出现时。Pull Request 将所有这些功能放入了一个友好的网页,在每个 GitHub 仓库上方都能找到。
|
||||
|
||||
@ -22,7 +22,7 @@ Pull Request 是开发者使用 GitHub 进行协作的利器。这个功能为
|
||||
|
||||
当你提交一个 Pull Request 的时候,你做的事情是 *请求(request)* 另一个开发者(比如项目维护者)来 *拉取(pull)* 你仓库中的一个分支到他们的仓库。也就是说你需要提供 4 个信息来完成一个 Pull Request:源仓库、源分支、目标仓库、目标分支。
|
||||
|
||||

|
||||

|
||||
|
||||
GitHub 会机智地帮你将一些值设为默认值。但是,取决于你的协作工作流,你的团队可能需要设置不同的值。上图显示了一个请求从 feature 分支合并到官方 master分支的一个 Pull Request,但除此之外还有好多种使用 Pull Request 的方式。
|
||||
|
||||
@ -42,7 +42,7 @@ Pull Request 可以和 feature 分支工作流、GitFlow 工作流或者 Fork
|
||||
|
||||
Feature 分支工作流使用共享的 GitHub 仓库来管理协作,开发者在单独的 feature 分支中添加功能。开发者在将代码并入主代码库之前,应该发起一个 Pull Request 来启动这个功能的讨论,而不是直接将它们合并到 `master`。
|
||||
|
||||

|
||||

|
||||
|
||||
在 Feature 分支工作流中只有一个公共的仓库,因此 Pull Request 的目标和源仓库永远是同一个。一般来说,开发者会将他们的 feature分支作为源分支,`master` 作为目标分支。
|
||||
|
||||
@ -54,9 +54,9 @@ Feature 分支工作流使用共享的 GitHub 仓库来管理协作,开发者
|
||||
|
||||
GitFlow 工作流和 Feature 分支工作流类似,但定义了围绕项目发布的一个严格的分支模型。在 GitFlow 工作流之上添加 Pull Request 使得开发者方便地讨论发布分支或是所在的维护分支。
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
在 GitFlow 工作流中的 Pull Request 和上一节中的完全一致:开发者只需在功能、发布或是快速修复分支需要审查时发布一个 Pull Request,GitHub 会通知到其余的团队成员。
|
||||
|
||||
@ -68,13 +68,13 @@ GitFlow 工作流和 Feature 分支工作流类似,但定义了围绕项目发
|
||||
|
||||
在这个工作流中,Pull Request 的通知作用显得非常有用,因为项目维护者无法获知其他开发者什么时候向他们自己的 GitHub 仓库中添加了提交。
|
||||
|
||||

|
||||

|
||||
|
||||
因为每个开发者都有他们自己的公共仓库,Pull Request 的源仓库和目标仓库不是同一个。源仓库是开发者的公开仓库,源分支是包含提议更改的那一个。如果开发者想要将功能合并到主代码库,目标仓库便是官方的项目仓库,目标分支为 `master`。
|
||||
|
||||
Pull Request 还可以用来和官方项目之外的开发者进行协作。比如说,一个开发者正在和同事一起开发一个功能,他们可以向 *同事的* GitHub 仓库发起一个 Pull Request,而不是官方仓库。他们将 feature 分支同时作为源分支和目标分支。
|
||||
|
||||

|
||||

|
||||
|
||||
两个开发者可以在 Pull Request 中讨论和开发分支。当功能完成时,其中一位可以发起另一个 Pull Request,请求将功能合并到官方的 master 分支中去。这种灵活性使得 Pull Request 成为了 Fork 工作流中尤为强大的协作工具。
|
||||
|
||||
@ -86,7 +86,7 @@ Pull Request 还可以用来和官方项目之外的开发者进行协作。比
|
||||
|
||||
### Mary fork了官方项目
|
||||
|
||||

|
||||

|
||||
|
||||
为了参与这个项目,Mary 首先要做的是 fork 属于 John 的 GitHub 仓库。她需要注册登录 GitHub,找到 John 的仓库,点击 Fork 按钮。
|
||||
|
||||
@ -98,7 +98,7 @@ Pull Request 还可以用来和官方项目之外的开发者进行协作。比
|
||||
|
||||
### Mary 克隆了她的 GitHub 仓库
|
||||
|
||||

|
||||

|
||||
|
||||
接下来,Mary 需要将她刚刚 fork 的 GitHub 仓库克隆下来。她在本地会有一份项目的副本。她需要运行下面这个命令:
|
||||
|
||||
@ -110,7 +110,7 @@ git clone https://github.com/user/repo.git
|
||||
|
||||
### Mary 开发了一个新功能
|
||||
|
||||

|
||||

|
||||
|
||||
在她写任何代码之前,Mary 需要为这个功能创建一个新的分支。这个分支将是她随后发起 Pull Request 时要用到的源分支。
|
||||
|
||||
@ -124,7 +124,7 @@ git commit -a -m "新功能的一些草稿"
|
||||
|
||||
### Mary 将 feature 分支推送到了她的 GitHub 仓库
|
||||
|
||||

|
||||

|
||||
|
||||
在功能完成后,Mary 使用简单的 `git push` 将 feature 分支推送到了她自己的 GitHub 仓库上(不是官方的仓库):
|
||||
|
||||
@ -136,7 +136,7 @@ git push origin some-branch
|
||||
|
||||
### Mary 创建了一个 Pull Request
|
||||
|
||||

|
||||

|
||||
|
||||
GitHub 上已经有了她的 feature 分支之后,Mary 可以找到被她 fork 的仓库,点击项目简介下的 *New Pull Request* 按钮,用她的 GitHub 账号创建一个 Pull Request。Mary 的仓库会被默认设置为源仓库(head fork),询问她指定源分支(compare)、目标仓库(base fork)和目标分支(base)。
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ git init --bare <directory>
|
||||
|
||||
`-—bare` 标记创建了一个没有工作目录的仓库,这样我们在仓库中更改文件并且提交了。中央仓库应该总是创建成裸仓库,因为向非裸仓库推送分支有可能会覆盖已有的代码变动。将`-—bare`看成是用来将仓库标记为储存设施,而不是一个开发环境。也就是说,对于所有的 Git 工作流,中央仓库是裸仓库,开发者的本地仓库是非裸仓库。
|
||||
|
||||

|
||||

|
||||
|
||||
### 栗子
|
||||
|
||||
@ -90,9 +90,9 @@ git clone <repo> <directory>
|
||||
|
||||
这就使得 Git 的协作和 SVN 截然不同。SVN 依赖于中央仓库和工作副本之间的关系,而 Git 协作模型是基于仓库和仓库之间的交互的。相对于 SVN 的提交流程,你可以在 Git 仓库之间 `push` 或 `pull` 提交。
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
当然,你也完全可以给予某个特定的仓库一些特殊的含义。比如,指定某个 Git 仓库为中央仓库,你就可以用 Git 进行中央化的工作流。重点是,这是通过约定实现的,而不是写死在版本控制系统本身。
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
因为它们非常相似,所以我们经常会搞混,不知道什么场景下该用哪个命令。在这篇文章中,我们会比较 `git reset`、`git checkout` 和 `git revert` 最常见的用法。希望你在看完后能游刃有余地使用这些命令来管理你的仓库。
|
||||
|
||||

|
||||

|
||||
|
||||
Git 仓库有三个主要组成——工作目录,缓存区和提交历史。这张图有助于理解每个命令到底产生了哪些影响。当你阅读的时候,牢记这张图。
|
||||
|
||||
@ -27,7 +27,7 @@ git reset HEAD~2
|
||||
|
||||
hotfix 分支末端的两个提交现在变成了悬挂提交。也就是说,下次 Git 执行垃圾回收的时候,这两个提交会被删除。换句话说,如果你想扔掉这两个提交,你可以这么做。reset 操作如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
如果你的更改还没有共享给别人,`git reset` 是撤销这些更改的简单方法。当你开发一个功能的时候发现「糟糕,我做了什么?我应该重新来过!」时,reset 就像是 go-to 命令一样。
|
||||
|
||||
@ -55,7 +55,7 @@ git checkout hotfix
|
||||
|
||||
上面这个命令做的不过是将HEAD移到一个新的分支,然后更新工作目录。因为这可能会覆盖本地的修改,Git 强制你提交或者缓存工作目录中的所有更改,不然在 checkout 的时候这些更改都会丢失。和 `git reset` 不一样的是,`git checkout` 没有移动这些分支。
|
||||
|
||||

|
||||

|
||||
|
||||
除了分支之外,你还可以传入提交的引用来 checkout 到任意的提交。这和 checkout 到另一个分支是完全一样的:把 HEAD 移动到特定的提交。比如,下面这个命令会 checkout 到当前提交的祖父提交。
|
||||
|
||||
@ -63,7 +63,7 @@ git checkout hotfix
|
||||
git checkout HEAD~2
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
这对于快速查看项目旧版本来说非常有用。但如果你当前的 HEAD 没有任何分支引用,那么这会造成 HEAD 分离。这是非常危险的,如果你接着添加新的提交,然后切换到别的分支之后就没办法回到之前添加的这些提交。因此,在为分离的 HEAD 添加新的提交的时候你应该创建一个新的分支。
|
||||
|
||||
@ -78,7 +78,7 @@ git revert HEAD~2
|
||||
|
||||
如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
相比 `git reset`,它不会改变现在的提交历史。因此,`git revert` 可以用在公共分支上,`git reset` 应该用在私有分支上。
|
||||
|
||||
@ -100,7 +100,7 @@ git reset HEAD~2 foo.py
|
||||
|
||||
和提交层面的 `git reset` 一样,通常我们使用HEAD而不是某个特定的提交。运行 `git reset HEAD foo.py` 会将当前的 `foo.py` 从缓存区中移除出去,而不会影响工作目录中对 `foo.py` 的更改。
|
||||
|
||||

|
||||

|
||||
|
||||
`--soft`、`--mixed` 和 `--hard` 对文件层面的 `git reset` 毫无作用,因为缓存区中的文件一定会变化,而工作目录中的文件一定不变。
|
||||
|
||||
@ -108,7 +108,7 @@ git reset HEAD~2 foo.py
|
||||
|
||||
Checkout 一个文件和带文件路径 `git reset` 非常像,除了它更改的是工作目录而不是缓存区。不像提交层面的 checkout 命令,它不会移动 HEAD引用,也就是你不会切换到别的分支上去。
|
||||
|
||||

|
||||

|
||||
|
||||
比如,下面这个命令将工作目录中的 `foo.py` 同步到了倒数第二个提交中的 `foo.py`。
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
`git revert` 命令用来撤销一个已经提交的快照。但是,它是通过搞清楚如何撤销这个提交引入的更改,然后在最后加上一个撤销了更改的 *新* 提交,而不是从项目历史中移除这个提交。这避免了Git丢失项目历史,这一点对于你的版本历史和协作的可靠性来说是很重要的。
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
### 用法
|
||||
|
||||
@ -32,7 +32,7 @@ git revert <commit>
|
||||
|
||||
理解这一点很重要。`git revert` 回滚了「单独一个提交」,它没有移除后面的提交,然后回到项目之前的状态。在 Git 中,后者实际上被称为 `reset`,而不是 `revert`。
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
撤销和重设相比有两个重要的优点。首先,它不会改变项目历史,对那些已经发布到共享仓库的提交来说这是一个安全的操作。至于为什么改变共享的历史是危险的,请参阅 `git reset` 一节。
|
||||
|
||||
@ -110,7 +110,7 @@ git reset --hard <commit>
|
||||
|
||||
移除一个其他团队成员在上面继续开发的提交在协作时会引发严重的问题。当他们试着和你的仓库同步时,他们会发现项目历史的一部分突然消失了。下面的序列展示了如果你尝试重设公共提交时会发生什么。`origin/master` 是你本地 `master` 分支对应的中央仓库中的分支。
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
一旦你在重设之后又增加了新的提交,Git 会认为你的本地历史已经和 `origin/master` 分叉了,同步你的仓库时的合并提交(merge commit)会使你的同事困惑。
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
## 中心化的工作流
|
||||
|
||||

|
||||

|
||||
|
||||
过渡到分布式分版本控制系统看起来是个令人恐惧的任务,但你不必为了利用 Git 的优点而改变你现有的工作流。你的团队仍然可以用以前SVN的方式开发项目。
|
||||
|
||||
@ -33,13 +33,13 @@
|
||||
|
||||
为了向官方项目发布修改,开发者将他们的本地 `master` 分支「推送」到中央仓库。这一步等同于 `svn commit`,除了Git添加的是所有不在中央 `master` 分支上的本地提交。
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
### 管理冲突
|
||||
|
||||
中央仓库代表官方项目,因此它的提交历史应该被视作神圣不可更改的。如果开发者的本地提交和中央仓库分叉了,Git 会拒绝将他们的修改推送上去,因为这会覆盖官方提交。
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
在开发者发布他们的功能之前,他们需要 fetch 更新的中央提交,在它们之上 rebase 自己的更改。这就像是「我想要在其他人的工作进展之上添加我的修改」,它会产生完美的线性历史,就像和传统的 SVN 工作流一样。
|
||||
|
||||
@ -77,7 +77,7 @@ git clone ssh://user@host/path/to/repo.git
|
||||
|
||||
### John 在开发他的功能
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
在他的本地仓库中,John 可以用标准的 Git 提交流程开发功能:编辑、缓存、提交。如果你对缓存区还不熟悉,你也可以不用记录工作目录中每次的变化。于是你创建了一个高度集中的提交,即使你已经在本地做了很多修改。
|
||||
|
||||
@ -91,13 +91,13 @@ git commit # 提交一个文件</some-file>
|
||||
|
||||
### Mary 在开发她的功能
|
||||
|
||||

|
||||

|
||||
|
||||
同时,Mary 在她自己的本地仓库用相同的编辑/缓存/提交流程开发她的功能。和 John 一样,她不需要关心中央仓库的进展,她也 *完全* 不关心 John 在他自己仓库中做的事,因为所有本地仓库都是私有的。
|
||||
|
||||
### John 发布了他的功能
|
||||
|
||||

|
||||

|
||||
|
||||
一旦 John 完成了他的功能,他应该将本地提交发布到中央仓库,这样其他项目成员就可以访问了。他可以使用[`git push`](https://github.com/geeeeeeeeek/git-recipes/wiki/3.2-%E4%BF%9D%E6%8C%81%E5%90%8C%E6%AD%A5#git-push)命令,就像:
|
||||
|
||||
@ -109,7 +109,7 @@ git push origin master
|
||||
|
||||
### Mary as试图发布她的功能
|
||||
|
||||

|
||||

|
||||
|
||||
John 已经成功地将他的更改发布到了中央仓库上,看看当 Mary 试着将她的功能推送到上面时会发生什么。她可以使用同一个推送命令:
|
||||
|
||||
@ -131,7 +131,7 @@ Git 防止 Mary 覆盖官方的修改。她需要将 John 的更新拉取到她
|
||||
|
||||
### Mary在John的提交之上rebase
|
||||
|
||||

|
||||

|
||||
|
||||
Mary 可以使用 [`git pull`](https://github.com/geeeeeeeeek/git-recipes/wiki/3.2-%E4%BF%9D%E6%8C%81%E5%90%8C%E6%AD%A5#git-pull) 来将上游修改并入她的仓库。这个命令和 `svn update` 很像——它拉取整个上游提交历史到Mary的本地仓库,并和她的本地提交一起整合:
|
||||
|
||||
@ -141,14 +141,14 @@ git pull --rebase origin master
|
||||
|
||||
`--rebase` 选项告诉 Git,在同步了中央仓库的修改之后,将 Mary 所有的提交移到 `master` 分支的顶端,如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
如果你忽略这个选项拉取同样会成功,只不过你每次和中央仓库同步时都会多出一个「合并提交」。在这种工作流中,rebase 和生成一个合并提交相比,总是一个更好的选择。
|
||||
|
||||
### Mary 解决了合并冲突
|
||||
|
||||

|
||||

|
||||
|
||||
Rebase 的工作是将每个本地提交一个个转移到更新后的 `master` 分支。也就是说,你可以一个个提交分别解决合并冲突,而不是在一个庞大的合并提交中解决。它会让你的每个提交保持专注,并获得一个干净的项目历史。另一方面,你更容易发现bug是在哪引入的,如果有必要的话,用最小的代价回滚这些修改。
|
||||
|
||||
@ -158,7 +158,7 @@ Rebase 的工作是将每个本地提交一个个转移到更新后的 `master`
|
||||
CONFLICT (content): Merge conflict in <some-file>
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
Git 的优点在于 *每个人* 都能解决他们自己的合并冲突。在这个例子中,Mary 只需运行一下 [`git status`](https://github.com/geeeeeeeeek/git-recipes/wiki/2.4-%E6%A3%80%E6%9F%A5%E4%BB%93%E5%BA%93%E7%8A%B6%E6%80%81#git-status) 就可以发现问题是什么。冲突的文件会出现在未合并路径中:
|
||||
@ -188,7 +188,7 @@ git rebase --abort
|
||||
|
||||
### Mary 成功发布了她的分支
|
||||
|
||||

|
||||

|
||||
|
||||
在她和中央仓库同步之后,Mary 可以成功地发布她的修改:
|
||||
|
||||
@ -232,7 +232,7 @@ Git 在技术上无法区别 `master` 和功能分支,所以开发者可以在
|
||||
|
||||
### Mary 开始了一个新功能
|
||||
|
||||

|
||||

|
||||
|
||||
在她开始开发一个功能之前,Mary 需要一个独立的分支。她可以用下面的命令[创建新分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-checkout):
|
||||
|
||||
@ -250,7 +250,7 @@ git commit
|
||||
|
||||
### Mary 去吃饭了
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
Mary 在早上[给她的功能添加了一些提交](https://github.com/geeeeeeeeek/git-recipes/wiki/2.3-%E4%BF%9D%E5%AD%98%E4%BD%A0%E7%9A%84%E6%9B%B4%E6%94%B9#git-commit)。在她去吃午饭前,[将她的分支推送到中央仓库](https://github.com/geeeeeeeeek/git-recipes/wiki/3.2-%E4%BF%9D%E6%8C%81%E5%90%8C%E6%AD%A5#git-push)是个不错的想法。这是一种方便的备份,但如果Mary和其他开发者一起协作,他们也可以看到她的初始提交了。
|
||||
|
||||
@ -262,7 +262,7 @@ git push -u origin marys-feature
|
||||
|
||||
### Mary 完成了她的工作
|
||||
|
||||

|
||||

|
||||
|
||||
当 Mary 吃完午饭回来,她完成了她的功能。在[并入 `master`](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-merge) 之前,她需要发布一个 Pull Request,让其他的团队成员知道她所做的工作。但首先,她应该保证中央仓库包含了她最新的提交:
|
||||
|
||||
@ -274,13 +274,13 @@ git push
|
||||
|
||||
### Bill 收到了 Pull Request
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
Bill 收到了 Pull Request,并且查看了 `marys-feature`。他决定在并入官方项目之前做一些小修改,通过 Pull Request 和 Mary 进行了沟通。
|
||||
|
||||
### Mary 作了修改
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
为了做这些更改,Mary 重复了之前创建功能时相同的流程,她编辑、缓存、提交、将更新推送到中央仓库。她所有的活动显示在 Pull Request 中,Bill 可以一直评论。
|
||||
|
||||
@ -288,7 +288,7 @@ Bill 收到了 Pull Request,并且查看了 `marys-feature`。他决定在并
|
||||
|
||||
### Mary 发布了她的功能
|
||||
|
||||

|
||||

|
||||
|
||||
一旦 Bill 准备接受这个 Pull Request,某个人(Bill 或者 Mary 都可)需要将功能并入稳定的项目:
|
||||
|
||||
@ -331,7 +331,7 @@ GitFlow 工作流仍然使用中央仓库作为开发者沟通的中心。和[
|
||||
|
||||
和单独的 `master` 分支不同,这种工作流使用两个分支来记录项目历史。`master` 分支储存官方发布历史,`develop` 分支用来整合功能分支。同时,这还方便了在 `master` 分支上给所有提交打上版本号标签。
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
工作流剩下的部分围绕这两个分支的差别展开。
|
||||
|
||||
@ -339,13 +339,13 @@ GitFlow 工作流仍然使用中央仓库作为开发者沟通的中心。和[
|
||||
|
||||
每个新功能都放置在自己的分支中,可以[在备份/协作时推送到中央仓库](https://github.com/geeeeeeeeek/git-recipes/wiki/3.2-%E4%BF%9D%E6%8C%81%E5%90%8C%E6%AD%A5#git-push)。但是,与其合并到 `master`,功能分支将开发分支作为父分支。当一个功能完成时,它将被[合并回 `develop`](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-merge)。功能永远不应该直接在 `master` 上交互。
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
注意,功能分支加上 `develop` 分支就是我们之前所说的功能分支工作流。但是,GitFlow 工作流不止于此。
|
||||
|
||||
### 发布分支
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
一旦 `develop`分支的新功能足够发布(或者预先确定的发布日期即将到来),你可以从 `develop` 分支 fork 一个发布分支。这个分支的创建开始了下个发布周期,只有和发布相关的任务应该在这个分支进行,如修复 bug、生成文档等。一旦准备好了发布,发布分支将合并进 `master`,打上版本号的标签。另外,它也应该合并回 `develop`,后者可能在发布启动之后有了新的进展。
|
||||
|
||||
@ -359,7 +359,7 @@ GitFlow 工作流仍然使用中央仓库作为开发者沟通的中心。和[
|
||||
|
||||
### 维护分支
|
||||
|
||||

|
||||
.svg)
|
||||
|
||||
维护或者「紧急修复」分支用来快速给产品的发布打上补丁。这是唯一可以从 `master` 上 fork 的分支。一旦修复完成了,它应该被并入 `master` 和 `develop` 分支(或者当前的发布分支),`master` 应该打上更新的版本号的标签。
|
||||
|
||||
@ -538,7 +538,7 @@ GitHub 同时提供了一个图形化界面来替代上面的操作。这和教
|
||||
|
||||
### 开发者 fork 仓库
|
||||
|
||||

|
||||

|
||||
|
||||
接下来,所有开发者需要 fork 官方仓库。你可以用 SSH 到服务器,运行 `git clone` 将它复制到服务器的另一个地址—— fork 其实只是服务端的 clone。但同样地,GitHub上开发者只需点一点按钮就可以 fork 仓库。
|
||||
|
||||
@ -592,7 +592,7 @@ git pull upstream master
|
||||
|
||||
### 开发者发布他们的功能
|
||||
|
||||

|
||||

|
||||
|
||||
一旦开发者准备好共享他们的新功能,他们需要做两件事情。第一,他们必须将贡献的代码推送到自己的公开仓库,让其他开发者能够访问到。他们的 `origin` 远端应该已经设置好了,所以他们只需要:
|
||||
|
||||
|
||||
@ -71,7 +71,7 @@ git status
|
||||
|
||||
`git log` 命令显示已提交的快照。你可以列出项目历史,筛选,以及搜索特定更改。`git status` 允许你查看工作目录和缓存区,而 `git log` 只作用于提交的项目历史。
|
||||
|
||||

|
||||

|
||||
|
||||
log 输出可以有很多种自定义的方式,从简单地筛选提交,到用完全自定义的格式显示。其中一些最常用的 `git log` 配置如下所示。
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ Git 的主要职责是保证你不会丢失提交的修改。但是,它同样
|
||||
|
||||
`git commit --amend` 命令是修复最新提交的便捷方式。它允许你将缓存的修改和之前的提交合并到一起,而不是提交一个全新的快照。它还可以用来简单地编辑上一次提交的信息而不改变快照。
|
||||
|
||||

|
||||

|
||||
|
||||
但是,amend 不只是修改了最新的提交——它进行了一次替换。对于 Git 来说,这看上去像一个全新的提交,即上图中用星号表示的那一个。在公共仓库工作时一定要牢记这一点。
|
||||
|
||||
@ -56,7 +56,7 @@ git commit --amend --no-edit
|
||||
|
||||
变基(rebase, 事实上这个名字十分诡异, 所以在大多数时候直接用英文术语)是将分支移到一个新的基提交的过程。过程一般如下所示:
|
||||
|
||||

|
||||

|
||||
|
||||
从内容的角度来看,rebase 只不过是将分支从一个提交移到了另一个。但从内部机制来看,Git 是通过在选定的基上创建新提交来完成这件事的——它事实上重写了你的项目历史。理解这一点很重要,尽管分支看上去是一样的,但它包含了全新的提交。
|
||||
|
||||
@ -72,7 +72,7 @@ git rebase <base>
|
||||
|
||||
rebase 的主要目的是为了保持一个线性的项目历史。比如说,当你在 feature 分支工作时 master 分支取得了一些进展:
|
||||
|
||||

|
||||

|
||||
|
||||
要将你的 feature 分支整合进 `master` 分支,你有两个选择:直接 merge,或者先 rebase 后 merge。前者会产生一个三路合并(3-way merge)和一个合并提交,而后者产生的是一个快速向前的合并以及完美的线性历史。下图展示了为什么 rebase 到 `master` 分支会促成一个快速向前的合并。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user