Merge pull request #21 from LitoMore/revise-docs

修改文案格式
This commit is contained in:
Zhongyi Tong
2017-03-23 10:25:22 +08:00
committed by GitHub
22 changed files with 1204 additions and 1501 deletions

View File

@ -1,19 +1,19 @@
# git-recipes
高质量的Git中文教程来自国外社区的优秀文章和个人实践
高质量的 Git 中文教程,来自国外社区的优秀文章和个人实践
## [目录](https://github.com/geeeeeeeeek/git-recipes/wiki/)
git-recipes的所有文章都以Wiki的方式呈现请移步[项目Wiki](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看目录和详细内容。
git-recipes 的所有文章都以 Wiki 的方式呈现,请移步[项目Wiki](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看目录和详细内容。
## 我为什么要做这份菜单
在整理Git资料的时候我发现社区贡献了非常多高质量的博客文章、指南等等。尤其英文的那些资料除了大家熟知的《Git图解》还有好多优秀的文章仍无人翻译。此外这些资料往往只涉及某些特定的话题如果能有一份菜单将这些菜谱以特定的方式串起来那么对于Git学习者来说将会是极大的便利。尤其对于我这样热爱查阅社区资料胜过出版物的懒人 : ) 随着我的学习节奏还会不断有新的菜谱加入进来,或许不会很频繁,不过也没有确定的终点。
在整理 Git 资料的时候我发现社区贡献了非常多高质量的博客文章、指南等等。尤其英文的那些资料除了大家熟知的《Git 图解》,还有好多优秀的文章仍无人翻译。此外,这些资料往往只涉及某些特定的话题,如果能有一份菜单将这些菜谱以特定的方式串起来,那么对于 Git 学习者来说将会是极大的便利。尤其对于我这样热爱查阅社区资料胜过出版物的懒人 :] 随着我的学习节奏还会不断有新的菜谱加入进来,或许不会很频繁,不过也没有确定的终点。
## 版权说明
除非另行注明,这个项目中的所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享BY 童仲毅(geeeeeeeeek@github)
除非另行注明,这个项目中的所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享BY 童仲毅geeeeeeeeek@github
不少文章在原基础上翻译或演绎而来,页面上方均标明了原作者、原文链接以及原文采用的协议。如仍有版权疑问,请在issue中提出。
不少文章在原基础上翻译或演绎而来,页面上方均标明了原作者、原文链接以及原文采用的协议。如仍有版权疑问,请在 Issue 中提出。
欢迎通过issue或者pr推荐你认为合适的资料,让这份菜单更充实一些。
欢迎通过 Issue 或者 Pull Request 推荐你认为合适的资料,让这份菜单更充实一些。

View File

@ -1,26 +1,26 @@
# Git log高级用法
# Git log 高级用法
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/git-log)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/git-log)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
每一个版本控制系统的出现都是为了让你记录代码的变化。你可以看到项目的历史记录——谁贡献了什么、bug是什么时候引入的还可以撤回有问题的更改。但是首先你得知道如何使用它。这也就是为什么会有`git log` 这个命令。
每一个版本控制系统的出现都是为了让你记录代码的变化。你可以看到项目的历史记录——谁贡献了什么、bug 是什么时候引入的,还可以撤回有问题的更改。但是,首先你得知道如何使用它。这也就是为什么会有 `git log` 这个命令。
到现在为止,你应该已经知道如何用`git log` 命令来显示最基本的提交信息。但除此之外,你还可以传入各种不同的参数来获得不一样的输出。
到现在为止,你应该已经知道如何用 `git log` 命令来显示最基本的提交信息。但除此之外,你还可以传入各种不同的参数来获得不一样的输出。
`git log`有两个高级用法:一是自定义提交的输出格式,二是过滤输出哪些提交。这两个用法合二为一,你就可以找到你项目中你需要的任何信息。
`git log` 有两个高级用法:一是自定义提交的输出格式,二是过滤输出哪些提交。这两个用法合二为一,你就可以找到你项目中你需要的任何信息。
## 格式化Log输出
## 格式化 Log 输出
首先,这篇文章会展示几种`git log`格式化输出的例子。大多数例子只是通过标记向`git log`请求或多或少的信息。
首先,这篇文章会展示几种 `git log` 格式化输出的例子。大多数例子只是通过标记向 `git log` 请求或多或少的信息。
如果你不喜欢默认的`git log`格式,你可以用`git config`的别名功能来给你想要的格式创建一个快捷方式。
如果你不喜欢默认的 `git log` 格式,你可以用 `git config` 的别名功能来给你想要的格式创建一个快捷方式。
### Oneline
`--oneline`标记把每一个提交压缩到了一行中。它默认只显示提交ID和提交信息的第一行。`git log --oneline`的输出一般是这样的:
`--oneline` 标记把每一个提交压缩到了一行中。它默认只显示提交ID和提交信息的第一行。`git log --oneline` 的输出一般是这样的:
```
```
0e25143 Merge branch 'feature'
ad8621a Fix a bug in the feature
16b36c6 Add a new feature
@ -31,28 +31,28 @@ ad8621a Fix a bug in the feature
### Decorate
很多时候,知道每个提交关联的分支或者标签很有用。`--decorate`标记让`git log`显示指向这个提交的所有引用(比如说分支、标签等)。
很多时候,知道每个提交关联的分支或者标签很有用。`--decorate` 标记让 `git log` 显示指向这个提交的所有引用(比如说分支、标签等)。
这可以和另一个配置项一起使用。比如,执行`git log --oneline --decorate` 会将提交历史格式化成这样:
这可以和另一个配置项一起使用。比如,执行 `git log --oneline --decorate` 会将提交历史格式化成这样:
```
```
0e25143 (HEAD, master) Merge branch 'feature'
ad8621a (feature) Fix a bug in the feature
16b36c6 Add a new feature
23ad9ad (tag: v0.9) Add the initial code base
```
在这个例子中通过HEAD标记可以看到最上面那个提交已经被checkout了而且它还是master分支的尾端。第二个提交有另一个feature分支指向它以及最后那个提交带有v0.9标签。
在这个例子中通过HEAD标记可以看到最上面那个提交已经被 checkout 了,而且它还是 master 分支的尾端。第二个提交有另一个 feature 分支指向它,以及最后那个提交带有 v0.9 标签。
分支、标签、HEAD还有提交历史是你Git仓库中包含的所有信息。因此这个命令让你更完整地观察项目结构。
分支、标签、HEAD 还有提交历史是你 Git 仓库中包含的所有信息。因此,这个命令让你更完整地观察项目结构。
### Diff
`git log`提供了很多选项来显示两个提交之间的差异。其中最常用的两个是`--stat`和`-p`
`git log` 提供了很多选项来显示两个提交之间的差异。其中最常用的两个是 `--stat``-p`
`--stat`选项显示每次提交的文件增删数量注意修改一行记作增加一行且删去一行当你想要查看提交引入的变化时这会非常有用。比如说下面这个提交在hello.py文件中增加了67行删去了38行。
`--stat` 选项显示每次提交的文件增删数量(注意:修改一行记作增加一行且删去一行),当你想要查看提交引入的变化时这会非常有用。比如说,下面这个提交在 hello.py 文件中增加了 67 行,删去了 38 行。
```
```
commit f2a238924e89ca1d4947662928218a06d39068c3
Author: John <john@example.com>
Date: Fri Jun 25 17:30:28 2014 -0500
@ -63,9 +63,9 @@ Date: Fri Jun 25 17:30:28 2014 -0500
1 file changed, 67 insertion(+), 38 deletions(-)
```
文件名后面+和-的数量是这个提交造成的更改中增删的相对比例。它给你一个直观的感觉,关于这次提交有多少改动。如果你想知道每次提交删改的绝对数量,你可以将`-p` 选项传入`git log` 。这样提交所有的删改都会被输出:
文件名后面+和-的数量是这个提交造成的更改中增删的相对比例。它给你一个直观的感觉,关于这次提交有多少改动。如果你想知道每次提交删改的绝对数量,你可以将 `-p` 选项传入`git log`。这样提交所有的删改都会被输出:
```
```
commit 16b36c697eb2d24302f89aa22d9170dfe609855b
Author: Mary <mary@example.com>
Date: Fri Jun 25 17:31:57 2014 -0500
@ -81,15 +81,15 @@ index 18ca709..c673b40 100644
+print("Hello, Git!")
```
对于改动很多的提交来说,这个输出会变得又长又大。一般来说,当你输出所有删改的时候,你是想要查找某一具体的改动,这时你就要用到`pickaxe` 选项。
对于改动很多的提交来说,这个输出会变得又长又大。一般来说,当你输出所有删改的时候,你是想要查找某一具体的改动,这时你就要用到 `pickaxe` 选项。
### Shortlog
`git shortlog`是一种特殊的`git log` ,它是为创建发布声明设计的。它把每个提交按作者分类,显示提交信息的第一行。这样可以容易地看到谁做了什么。
`git shortlog` 是一种特殊的 `git log`,它是为创建发布声明设计的。它把每个提交按作者分类,显示提交信息的第一行。这样可以容易地看到谁做了什么。
比如说,两个开发者为项目贡献了5个提交,那么`git shortlog` 输出会是这样的:
比如说,两个开发者为项目贡献了 5 个提交,那么 `git shortlog` 输出会是这样的:
```
```
Mary (2):
Fix a bug in the feature
Fix a serious security hole in our framework
@ -100,13 +100,13 @@ John (3):
Merge branch 'feature'
```
默认情况下,`git shortlog`把输出按作者名字排序,但你可以传入`-n`选项来按每个作者提交数量排序。
默认情况下,`git shortlog` 把输出按作者名字排序,但你可以传入 `-n` 选项来按每个作者提交数量排序。
### Graph
`--graph` 选项绘制一个ASCII图像来展示提交历史的分支结构。它经常和 `--oneline`和 `--decorate`两个选项一起使用,这样会更容易查看哪个提交属于哪个分支:
`--graph` 选项绘制一个 ASCII 图像来展示提交历史的分支结构。它经常和 `--oneline``--decorate` 两个选项一起使用,这样会更容易查看哪个提交属于哪个分支:
```
```
git log --graph --oneline --decorate
For a simple repository with just 2 branches, this will produce the following:
@ -120,17 +120,17 @@ For a simple repository with just 2 branches, this will produce the following:
* 160e224 Add the initial code base
```
星号表明这个提交所在的分支,所以上图的意思是`23ad9ad`和`16b36c6`这两个提交在topic分支上其余的在master分支上。
星号表明这个提交所在的分支,所以上图的意思是 `23ad9ad``16b36c6` 这两个提交在 topic 分支上,其余的在 master 分支上。
虽然这对简单的项目来说是个很好用的选择但你可能会更喜欢gitkSourceTree这些更强大的可视化工具来分析大型项目。
虽然这对简单的项目来说是个很好用的选择,但你可能会更喜欢 gitkSourceTree 这些更强大的可视化工具来分析大型项目。
### 自定义格式
对于其他的`git log`格式需求,你都可以使用`--pretty=format:"<string>"`选项。它允许你使用像printf一样的占位符来输出提交。
对于其他的 `git log` 格式需求,你都可以使用 `--pretty=format:"<string>"` 选项。它允许你使用像 printf 一样的占位符来输出提交。
比如,下面命令中的`%cn`、`%h` 和`%cd`这三种占位符会被分别替换为作者名字、缩略标识和提交日期。
比如,下面命令中的 `%cn``%h``%cd` 这三种占位符会被分别替换为作者名字、缩略标识和提交日期。
```
```
git log --pretty=format:"%cn committed %h on %cd"
This results in the following format for each commit:
@ -142,131 +142,131 @@ John committed f12ca28 on Wed Jun 22 13:50:31 2014 -0500
完整的占位符清单可以在文档中找到。
除了让你只看到关注的信息,这个`--pretty=format:"<string>"` 选项在你想要在另一个命令中使用日志内容是尤为有用的。
除了让你只看到关注的信息,这个 `--pretty=format:"<string>"` 选项在你想要在另一个命令中使用日志内容是尤为有用的。
## 过滤提交历史
格式化提交输出只是`git log`其中的一个用途。另一半是理解如何浏览整个提交历史。接下来的文章会介绍如何用`git log`选择项目历史中的特定提交。所有的用法都可以和上面讨论过的格式化选项结合起来。
格式化提交输出只是 `git log` 其中的一个用途。另一半是理解如何浏览整个提交历史。接下来的文章会介绍如何用 `git log` 选择项目历史中的特定提交。所有的用法都可以和上面讨论过的格式化选项结合起来。
### 按数量
`git log`最基础的过滤选项是限制显示的提交数量。当你只对最近几次提交感兴趣时,它可以节省你一页一页查看的时间。
`git log` 最基础的过滤选项是限制显示的提交数量。当你只对最近几次提交感兴趣时,它可以节省你一页一页查看的时间。
你可以在后面加上`-<n>`选项。比如说,下面这个命令会显示最新的3次提交:
你可以在后面加上 `-<n>` 选项。比如说,下面这个命令会显示最新的 3 次提交:
```
```
git log -3
```
### 按日期
如果你想要查看某一特定时间段内的提交,你可以使用`--after` 或 `--before` 标记来按日期筛选。它们都接受好几种日期格式作为参数。比如说下面的命令会显示2014年7月1日后(含)的提交:
如果你想要查看某一特定时间段内的提交,你可以使用 `--after``--before` 标记来按日期筛选。它们都接受好几种日期格式作为参数。比如说,下面的命令会显示 2014 年 7 月 1 日后(含)的提交:
```
```
git log --after="2014-7-1"
```
你也可以传入相对的日期,比如一周前("`1 week ago`")或者昨天("`yesterday`"
你也可以传入相对的日期,比如一周前(`"1 week ago"`)或者昨天(`"yesterday"`
```
```
get log --after="yesterday"
```
你可以同时提供`--before` 和 `--after` 来检索两个日期之间的提交。比如为了显示2014年7月1日到2014年7月4日之间的提交,你可以这么写:
你可以同时提供`--before``--after` 来检索两个日期之间的提交。比如,为了显示 2014 年 7 月 1 日到 2014 年 7 月 4 日之间的提交,你可以这么写:
```
```
git log --after="2014-7-1" --before="2014-7-4"
```
注意`--since` 、`--until` 标记和`--after` 、`--before`标记分别是等价的。
注意 `--since``--until` 标记和 `--after``--before` 标记分别是等价的。
### 按作者
当你只想看某一特定作者的提交的时候,你可以使用`--author`标记。它接受正则表达式,返回所有作者名字满足这个规则的提交。如果你知道那个作者的确切名字你可以直接传入文本字符串:
当你只想看某一特定作者的提交的时候,你可以使用 `--author` 标记。它接受正则表达式,返回所有作者名字满足这个规则的提交。如果你知道那个作者的确切名字你可以直接传入文本字符串:
```
```
git log --author="John"
```
它会显示所有作者叫John的提交。作者名不一定是全匹配只要包含那个子串就会匹配。
它会显示所有作者叫 John 的提交。作者名不一定是全匹配,只要包含那个子串就会匹配。
你也可以用正则表达式来创建更复杂的检索。比如下面这个命令检索名叫MaryJohn的作者的提交。
你也可以用正则表达式来创建更复杂的检索。比如,下面这个命令检索名叫 MaryJohn 的作者的提交。
```
```
git log --author="John\|Mary"
```
注意作者的邮箱地址也算作是作者的名字,所以你也可以用这个选项来按邮箱检索。
如果你的工作流区分提交者和作者,`--committer`也能以相同的方式使用。
如果你的工作流区分提交者和作者,`--committer` 也能以相同的方式使用。
### 按提交信息
按提交信息来过滤提交,你可以使用`--grep`标记。它和上面的`--author`标记差不多,只不过它搜索的是提交信息而不是作者。
按提交信息来过滤提交,你可以使用 `--grep` 标记。它和上面的 `--author` 标记差不多,只不过它搜索的是提交信息而不是作者。
比如说你的团队规范要求在提交信息中包括相关的issue编号你可以用下面这个命令来显示这个issue相关的所有提交
比如说你的团队规范要求在提交信息中包括相关的issue编号你可以用下面这个命令来显示这个 issue 相关的所有提交:
```
```
git log --grep="JRA-224:"
```
你也可以传入`-i`参数来忽略大小写匹配。
你也可以传入 `-i` 参数来忽略大小写匹配。
### 按文件
很多时候,你只对某个特定文件的更改感兴趣。为了显示某个特定文件的历史,你只需要传入文件路径。比如说,下面这个命令返回所有和`foo.py`和`bar.py`文件相关的提交:
很多时候,你只对某个特定文件的更改感兴趣。为了显示某个特定文件的历史,你只需要传入文件路径。比如说,下面这个命令返回所有和 `foo.py``bar.py` 文件相关的提交:
```
```
git log -- foo.py bar.py
```
`--`告诉`git log`接下来的参数是文件路径而不是分支名。如果分支名和文件名不可能冲突,你可以省略`--`
`--` 告诉 `git log` 接下来的参数是文件路径而不是分支名。如果分支名和文件名不可能冲突,你可以省略 `--`
### 按内容
我们还可以根据源代码中某一行的增加和删除来搜索提交。这被称为pickaxe它接受形如`-S"<string>"`的参数。比如说,当你想要知道`Hello, World!`字符串是什么时候加到项目中哪个文件中去的,你可以使用下面这个命令:
我们还可以根据源代码中某一行的增加和删除来搜索提交。这被称为 pickaxe它接受形如 `-S"<string>"` 的参数。比如说,当你想要知道 `Hello, World!` 字符串是什么时候加到项目中哪个文件中去的,你可以使用下面这个命令:
```
```
git log -S "Hello, World!"
```
如果你想用正则表达式而不是字符串来搜索,你可以使用`-G"<regex>"`标记。
如果你想用正则表达式而不是字符串来搜索,你可以使用 `-G"<regex>"` 标记。
这是一个非常强大的调试工具,它能让你定位到所有影响代码中特定一行的提交。它甚至可以让你看到某一行是什么时候复制或者移动到另一个文件中去的。
### 按范围
你可以传入范围来筛选提交。这个范围由下面这样的格式指定,其中`<since>`和`<until>`是提交的引用:
你可以传入范围来筛选提交。这个范围由下面这样的格式指定,其中 `<since>``<until>` 是提交的引用:
```
```
git log <since>..<until>
```
这个命令在你使用分支引用作为参数时特别有用。这是显示两个分支之间区别最简单的方式。看看下面这个命令:
```
```
git log master..feature
```
其中的master..feature范围包含了在feature分支而不在master分支中所有的提交。换句话说这个命令可以看出从master分支forkfeature分支后发生了哪些变化。它可以这样可视化
其中的 `master..feature` 范围包含了在 feature 分支而不在 master 分支中所有的提交。换句话说,这个命令可以看出从 master 分支 forkfeature 分支后发生了哪些变化。它可以这样可视化:
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/git-log/01.svg)
注意如果你更改范围的前后顺序(feature..master)你会获取到master分支而非feature分支上的所有提交。如果`git log`输出了全部两个分支的提交,这说明你的提交历史已经分叉了。
注意如果你更改范围的前后顺序feature..master,你会获取到 master 分支而非 feature 分支上的所有提交。如果 `git log` 输出了全部两个分支的提交,这说明你的提交历史已经分叉了。
### 过滤合并提交
`git log`输出时默认包括合并提交。但是如果你的团队采用强制合并策略意思是merge你修改的上游分支而不是将你的分支rebase到上游分支你的项目历史中会有很多外来的提交。
`git log` 输出时默认包括合并提交。但是,如果你的团队采用强制合并策略(意思是 merge 你修改的上游分支而不是将你的分支 rebase 到上游分支),你的项目历史中会有很多外来的提交。
你可以通过`--no-merges`标记来排除这些提交:
你可以通过 `--no-merges` 标记来排除这些提交:
```
```
git log --no-merges
```
另一方面,如果你只对合并提交感兴趣,你可以使用`--merges`标记:
另一方面,如果你只对合并提交感兴趣,你可以使用 `--merges` 标记:
```
```
git log --merges
```
@ -274,14 +274,14 @@ git log --merges
## 总结
你现在应该对使用`git log`来格式化输出和选择你要显示的提交的用法比较熟悉了。它允许你查看你项目历史中任何需要的内容。
你现在应该对使用 `git log` 来格式化输出和选择你要显示的提交的用法比较熟悉了。它允许你查看你项目历史中任何需要的内容。
这些技巧是你Git工具箱中重要的部分不过注意`git log`往往和其他Git命令连着使用。当你找到了你要的提交你把它传给`git checkout` 、`git revert` 或是其他控制提交历史的工具。所以请继续坚持Git高级用法的学习。
这些技巧是你 Git 工具箱中重要的部分,不过注意 `git log` 往往和其他 Git 命令连着使用。当你找到了你要的提交,你把它传给 `git checkout``git revert` 或是其他控制提交历史的工具。所以,请继续坚持 Git 高级用法的学习。
> 这篇文章是[**git-recipes**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:***Fork***:fork_and_knife:。
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
> 这篇文章是[**git-recipes**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2:***Fork***:fork_and_knife:。
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,10 +1,10 @@
# Git图解
# Git 图解
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)基础上演绎的文章。原作者[Mark Lodato](lodatom@gmail.com),译者[wych](ellrywych@gmail.com)。原文采用[创用CC 姓名标示-非商业性-相同方式分享3.0 美国授权条款](https://creativecommons.org/licenses/by-nc-sa/3.0/us/)授权。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)基础上演绎的文章。原作者 [Mark Lodato](lodatom@gmail.com),译者 [wych](ellrywych@gmail.com)。原文采用[创用 CC 姓名标示-非商业性-相同方式分享 3.0 美国授权条款](https://creativecommons.org/licenses/by-nc-sa/3.0/us/)授权。
此页图解git中的最常用命令。如果你稍微理解git的工作原理这篇文章能够让你理解的更透彻。
此页图解 git 中的最常用命令。如果你稍微理解 git 的工作原理,这篇文章能够让你理解的更透彻。
@ -12,19 +12,19 @@
![enter image description here](http://marklodato.github.io/visual-git-guide/basic-usage.svg)
上面的四条命令在工作目录、stage缓存(也叫做索引)和commit历史之间复制文件。
上面的四条命令在工作目录、stage 缓存(也叫做索引)和 commit 历史之间复制文件。
* `git add files` 把工作目录中的文件加入stage缓存
* `git commit` 把stage缓存生成一次commit并加入commit历史
* `git reset -- files` 撤销最后一次`git add files`,你也可以用`git reset` 撤销所有stage缓存文件
* `git checkout -- files` 把文件从stage缓存复制到工作目录用来丢弃本地修改
* `git add files` 把工作目录中的文件加入 stage 缓存
* `git commit` stage 缓存生成一次 commit并加入 commit 历史
* `git reset -- files` 撤销最后一次 `git add files`,你也可以用 `git reset` 撤销所有 stage 缓存文件
* `git checkout -- files` 把文件从 stage 缓存复制到工作目录,用来丢弃本地修改
你可以用 `git reset -p``git checkout -p``git add -p`进入交互模式也可以跳过stage缓存直接从commit历史取出文件或者直接提交代码。
你可以用 `git reset -p``git checkout -p``git add -p` 进入交互模式,也可以跳过 stage 缓存直接从 commit历史取出文件或者直接提交代码。
![enter image description here](http://marklodato.github.io/visual-git-guide/basic-usage-2.svg)
* `git commit -a ` 相当于运行`git add`把所有当前目录下的文件加入stage缓存再运行`git commit`
* `git commit files` 进行一次包含最后一次提交加上工作目录中文件快照的提交并且文件被添加到stage缓存。
* `git commit -a ` 相当于运行 `git add` 把所有当前目录下的文件加入 stage 缓存再运行 `git commit`
* `git commit files` 进行一次包含最后一次提交加上工作目录中文件快照的提交,并且文件被添加到 stage 缓存。
* `git checkout HEAD -- files` 回滚到复制最后一次提交。
## 约定
@ -33,9 +33,9 @@
![enter image description here](http://marklodato.github.io/visual-git-guide/conventions.svg)
绿色的5位字符表示提交的ID分别指向父节点。分支用橙色显示分别指向特定的提交。当前分支由附在其上的_HEAD_标识。
绿色的5位字符表示提交的 ID分别指向父节点。分支用橙色显示分别指向特定的提交。当前分支由附在其上的 `_HEAD_` 标识。
这张图片里显示最后5次提交_ed489_是最新提交。 _master_分支指向此次提交另一个_maint_分支指向祖父提交节点。
这张图片里显示最后 5 次提交,`_ed489_` 是最新提交。 `_master_` 分支指向此次提交,另一个 `_maint_` 分支指向祖父提交节点。
## 命令详解
@ -47,19 +47,19 @@
### Commit
提交时,gitstage缓存中的文件创建一个新的提交并把此时的节点设为父节点。然后把当前分支指向新的提交节点。下图中当前分支是_master_。
提交时,Gitstage 缓存中的文件创建一个新的提交,并把此时的节点设为父节点。然后把当前分支指向新的提交节点。下图中,当前分支是 `_master_`
在运行命令之前_master_指向_ed489_提交后_master_指向新的节点_f0cec_并以_ed489_作为父节点。
在运行命令之前,`_master_` 指向 `_ed489_`,提交后,`_master_` 指向新的节点`_f0cec_` 并以 `_ed489_` 作为父节点。
![enter image description here](http://marklodato.github.io/visual-git-guide/commit-master.svg)
即便当前分支是某次提交的祖父节点,git会同样操作。下图中在_master_分支的祖父节点_maint_分支进行一次提交生成了_1800b_。
即便当前分支是某次提交的祖父节点,Git 会同样操作。下图中,在 `_master_` 分支的祖父节点 `_maint_` 分支进行一次提交,生成了 `_1800b_`
这样_maint_分支就不再是_master_分支的祖父节点。此时[merge](#merge) 或者 [rebase](#rebase) 是必须的。
这样,`_maint_ `分支就不再是 `_master_` 分支的祖父节点。此时,[merge](#merge) 或者 [rebase](#rebase) 是必须的。
![enter image description here](http://marklodato.github.io/visual-git-guide/commit-maint.svg)
如果想更改一次提交,使用 `git commit --amend`git会使用与当前提交相同的父节点进行一次新提交旧的提交会被取消。
如果想更改一次提交,使用 `git commit --amend`Git 会使用与当前提交相同的父节点进行一次新提交,旧的提交会被取消。
![enter image description here](http://marklodato.github.io/visual-git-guide/commit-amend.svg)
@ -67,89 +67,86 @@
### Checkout
checkout命令用于从历史提交或者stage缓存中拷贝文件到工作目录也可用于切换分支。
`git checkout` 命令用于从历史提交(或者 stage 缓存)中拷贝文件到工作目录,也可用于切换分支。
当给定某个文件名(或者打开-p选项,或者文件名和-p选项同时打开git会从指定的提交中拷贝文件到stage缓存和工作目录。比如`git checkout HEAD~ foo.c`会将提交节点_HEAD~_即当前提交节点的父节点中的`foo.c`复制到工作目录并且加到stage缓存中。如果命令中没有指定提交节点则会从stage缓存中拷贝内容。注意当前分支不会发生变化。
当给定某个文件名(或者打开 `-p` 选项,或者文件名和-p选项同时打开Git 会从指定的提交中拷贝文件到 stage 缓存和工作目录。比如,`git checkout HEAD~ foo.c` 会将提交节点 `_HEAD~_`(即当前提交节点的父节点)中的 `foo.c` 复制到工作目录并且加到 stage 缓存中。如果命令中没有指定提交节点,则会从 stage 缓存中拷贝内容。注意当前分支不会发生变化。
![enter image description here](http://marklodato.github.io/visual-git-guide/checkout-files.svg)
当不指定文件名而是给出一个本地分支时那么_HEAD_标识会移动到那个分支也就是说我们切换到那个分支了然后stage缓存和工作目录中的内容会和_HEAD_对应的提交节点一致。新提交节点下图中的a47c3中的所有文件都会被复制到stage缓存和工作目录中只存在于老的提交节点ed489中的文件会被删除不属于上述两者的文件会被忽略不受影响。
当不指定文件名,而是给出一个(本地)分支时,那么 `_HEAD_` 标识会移动到那个分支(也就是说,我们切换到那个分支了),然后 stage 缓存和工作目录中的内容会和 `_HEAD_` 对应的提交节点一致。新提交节点(下图中的 `a47c3`)中的所有文件都会被复制(到 stage 缓存和工作目录中);只存在于老的提交节点(`ed489`)中的文件会被删除;不属于上述两者的文件会被忽略,不受影响。
![enter image description here](http://marklodato.github.io/visual-git-guide/checkout-branch.svg)
如果既没有指定文件名也没有指定分支名而是一个标签、远程分支、SHA-1值或者是像_master~3_类似的东西就得到一个匿名分支称作_detached HEAD_被分离的_HEAD_标识。这样可以很方便地在历史版本之间互相切换。比如说你想要编译1.6.6.1版本的git你可以运行`git checkout v1.6.6.1`(这是一个标签,而非分支名),编译,安装,然后切换回另一个分支,比如说`git checkout master`。然而,当提交操作涉及到分离的HEAD时,其行为会略有不同,详情见在[下面](#detached)。
如果既没有指定文件名也没有指定分支名而是一个标签、远程分支、SHA-1 值或者是像 `_master~3_` 类似的东西,就得到一个匿名分支,称作 `_detached HEAD_`(被分离的 `_HEAD_` 标识)。这样可以很方便地在历史版本之间互相切换。比如说你想要编译 1.6.6.1 版本的 Git你可以运行 `git checkout v1.6.6.1`(这是一个标签,而非分支名),编译,安装,然后切换回另一个分支,比如说 `git checkout master`。然而,当提交操作涉及到分离的 HEAD时,其行为会略有不同,详情见在[下面](#detached)。
![enter image description here](http://marklodato.github.io/visual-git-guide/checkout-detached.svg)
### HEAD标识处于分离状态时的提交操作
### HEAD 标识处于分离状态时的提交操作
当_HEAD_处于分离状态不依附于任一分支提交操作可以正常进行但是不会更新任何已命名的分支。你可以认为这是在更新一个匿名分支。
`_HEAD_` 处于分离状态(不依附于任一分支)时,提交操作可以正常进行,但是不会更新任何已命名的分支。你可以认为这是在更新一个匿名分支。
![enter image description here](http://marklodato.github.io/visual-git-guide/commit-detached.svg)
一旦此后你切换到别的分支比如说_master_那么这个提交节点可能再也不会被引用到然后就会被丢弃掉了。注意这个命令之后就不会有东西引用_2eecb_。
一旦此后你切换到别的分支,比如说 `_master_`,那么这个提交节点(可能)再也不会被引用到,然后就会被丢弃掉了。注意这个命令之后就不会有东西引用 `_2eecb_`
![enter image description here](http://marklodato.github.io/visual-git-guide/checkout-after-detached.svg)
但是,如果你想保存这个状态,可以用命令`git checkout -b name`来创建一个新的分支。
但是,如果你想保存这个状态,可以用命令 `git checkout -b name` 来创建一个新的分支。
![enter image description here](http://marklodato.github.io/visual-git-guide/checkout-b-detached.svg)
### Reset
reset命令把当前分支指向另一个位置并且有选择的变动工作目录和索引。也用来在从历史commit历史中复制文件到索引而不动工作目录。
`git reset` 命令把当前分支指向另一个位置并且有选择的变动工作目录和索引。也用来在从历史commit历史中复制文件到索引而不动工作目录。
如果不给选项,那么当前分支指向到那个提交。如果用`--hard`选项,那么工作目录也更新,如果用`--soft`选项,那么都不变。
如果不给选项,那么当前分支指向到那个提交。如果用 `--hard` 选项,那么工作目录也更新,如果用 `--soft` 选项,那么都不变。
![enter image description here](http://marklodato.github.io/visual-git-guide/reset-commit.svg)
如果没有给出提交点的版本号那么默认用_HEAD_。这样分支指向不变但是索引会回滚到最后一次提交如果用`--hard`选项,工作目录也同样。
如果没有给出提交点的版本号,那么默认用 `_HEAD_`。这样,分支指向不变,但是索引会回滚到最后一次提交,如果用 `--hard` 选项,工作目录也同样。
![enter image description here](http://marklodato.github.io/visual-git-guide/reset.svg)
如果给了文件名(或者 `-p`选项), 那么工作效果和带文件名的[checkout](#checkout)差不多,除了索引被更新。
如果给了文件名(或者 `-p` 选项), 那么工作效果和带文件名的[checkout](#checkout)差不多,除了索引被更新。
![enter image description here](http://marklodato.github.io/visual-git-guide/reset-files.svg)
### Merge
merge 命令把不同分支合并起来。合并前,索引必须和当前提交相同。如果另一个分支是当前提交的祖父节点,那么合并命令将什么也不做。
`git merge` 命令把不同分支合并起来。合并前,索引必须和当前提交相同。如果另一个分支是当前提交的祖父节点,那么合并命令将什么也不做。
另一种情况是如果当前提交是另一个分支的祖父节点就导致_fast-forward_合并。指向只是简单的移动并生成一个新的提交。
另一种情况是如果当前提交是另一个分支的祖父节点,就导致 `_fast-forward_` 合并。指向只是简单的移动,并生成一个新的提交。
![enter image description here](http://marklodato.github.io/visual-git-guide/merge-ff.svg)
否则就是一次真正的合并。默认把当前提交(_ed489_ 如下所示)和另一个提交(_33104_)以及他们的共同祖父节点(_b325c_)进行一次[三方合并](http://en.wikipedia.org/wiki/Three-way_merge)。结果是先保存当前目录和索引然后和父节点_33104_一起做一次新提交。
否则就是一次真正的合并。默认把当前提交`_ed489_` 如下所示和另一个提交`_33104_`以及他们的共同祖父节点`_b325c_`进行一次[三方合并](http://en.wikipedia.org/wiki/Three-way_merge)。结果是先保存当前目录和索引,然后和父节点 `_33104_` 一起做一次新提交。
![enter image description here](http://marklodato.github.io/visual-git-guide/merge.svg)
### Cherry Pick
cherry-pick命令"复制"一个提交节点并在当前分支做一次完全一样的新提交。
`git cherry-pick` 命令复制一个提交节点并在当前分支做一次完全一样的新提交。
![enter image description here](http://marklodato.github.io/visual-git-guide/cherry-pick.svg)
### Rebase
rebase是合并命令的另一种选择。合并把两个父分支合并进行一次提交提交历史不是线性的。rebase在当前分支上重演另一个分支的历史提交历史是线性的。
`git rebase` 是合并命令的另一种选择。合并把两个父分支合并进行一次提交提交历史不是线性的。rebase 在当前分支上重演另一个分支的历史,提交历史是线性的。
本质上,这是线性化的自动的 [cherry-pick](#cherry-pick)。
![enter image description here](http://marklodato.github.io/visual-git-guide/rebase.svg)
上面的命令都在_topic_分支中进行而不是_master_分支在_master_分支上重演并且把分支指向新的节点。注意旧提交没有被引用将被回收。
上面的命令都在 `_topic_` 分支中进行,而不是 `_master_` 分支,在 `_master_` 分支上重演,并且把分支指向新的节点。注意旧提交没有被引用,将被回收。
要限制回滚范围,使用`--onto`选项。下面的命令在_master_分支上重演当前分支从_169a6_以来的最近几个提交即_2c33a_。
要限制回滚范围,使用 `--onto` 选项。下面的命令在 `_master_` 分支上重演当前分支从 `_169a6_` 以来的最近几个提交,即 `_2c33a_`
![enter image description here](http://marklodato.github.io/visual-git-guide/rebase-onto.svg)
同样有`git rebase --interactive`让你更方便的完成一些复杂操作,比如丢弃、重排、修改、合并提交。
同样有 `git rebase --interactive` 让你更方便的完成一些复杂操作,比如丢弃、重排、修改、合并提交。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:。
>
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,22 +1,22 @@
# Git提交引用和引用日志
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/refs-and-the-reflog)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/refs-and-the-reflog)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
提交是Git的精髓所在你无时不刻不在创建和缓存提交、查看以前的提交或者用各种Git命令在仓库间转移你的提交。大多数的命令都对同一个提交操作而有些会接受提交的引用作为参数。比如你可以给`git checkout`传入一个引用来查看以前的提交,或者传入一个分支名来切换到对应的分支。
提交是 Git 的精髓所在你无时不刻不在创建和缓存提交、查看以前的提交或者用各种Git命令在仓库间转移你的提交。大多数的命令都对同一个提交操作而有些会接受提交的引用作为参数。比如你可以给 `git checkout` 传入一个引用来查看以前的提交,或者传入一个分支名来切换到对应的分支。
![引用一次提交的各种方式](https://www.atlassian.com/git/images/tutorials/advanced/refs-and-the-reflog/01.svg)
知道提交的各种引用方式之后Git的命令就会变得更加强大。在这章中我们研究提交的各种引用方式来一窥`git checkout``git branch``git push`等命令的工作原理。
知道提交的各种引用方式之后Git 的命令就会变得更加强大。在这章中,我们研究提交的各种引用方式,来一窥 `git checkout``git branch``git push` 等命令的工作原理。
我们还会学到如何使用Git的引用日志查看似乎已被删除的提交。
我们还会学到如何使用 Git 的引用日志查看似乎已被删除的提交。
## 哈希字串
引用一个提交最直接的方式是通过SHA-1的哈希字串这是每个提交唯一的ID。你可以在`git log`的输出中找到提交的哈希字串。
引用一个提交最直接的方式是通过 SHA-1 的哈希字串,这是每个提交唯一的 ID。你可以在 `git log` 的输出中找到提交的哈希字串。
```
```
commit 0c708fdec272bc4446c6cabea4f0022c2b616eba
Author: Mary Johnson <mary@example.com>
Date: Wed Jul 9 16:37:42 2014 -0500
@ -24,27 +24,27 @@ Date: Wed Jul 9 16:37:42 2014 -0500
一些提交信息
```
在Git命令中传递时你只需要提供足以确定那个提交的哈希子串即可。比如你可以这样用`git show`的命令显示上面的提交:
Git 命令中传递时,你只需要提供足以确定那个提交的哈希子串即可。比如,你可以这样用 `git show` 的命令显示上面的提交:
```
```
git show 0c708f
```
有时,我们需要把分支、标签或者其他间接的引用转变成对应提交的哈希。`git rev-parse`命令正是你需要的。下面这个命令返回master分支提交的哈希字串
有时,我们需要把分支、标签或者其他间接的引用转变成对应提交的哈希。`git rev-parse` 命令正是你需要的。下面这个命令返回 master 分支提交的哈希字串:
```
```
git rev-parse master
```
当你写的自定义脚本中需要将提交引用作为参数时,这个命令非常有用。你可以让`git rev-parse`帮你处理转换,而不用手动做这件事。
当你写的自定义脚本中需要将提交引用作为参数时,这个命令非常有用。你可以让 `git rev-parse` 帮你处理转换,而不用手动做这件事。
## 引用
ref是提交的间接引用。你可以把它当做哈希字串的别名但对用户更友好。这就是Git内部表示分支和标签的机制。
ref 是提交的间接引用。你可以把它当做哈希字串的别名,但对用户更友好。这就是 Git 内部表示分支和标签的机制。
引用以一段普通的文本存在于`.git/refs`目录中,就是我们平时说的那个`.git`。你去`.git/refs`文件夹查看仓库中的引用。你可以看到下面这样的结构,但具体的文件取决于你的仓库中有什么分支和标签,以及你的远程仓库。
引用以一段普通的文本存在于 `.git/refs` 目录中,就是我们平时说的那个 `.git`。你去 `.git/refs` 文件夹查看仓库中的引用。你可以看到下面这样的结构,但具体的文件取决于你的仓库中有什么分支和标签,以及你的远程仓库。
```
```
.git/refs/
heads/
master
@ -56,9 +56,9 @@ ref是提交的间接引用。你可以把它当做哈希字串的别名
v0.9
```
`heads`目录定义了你本地仓库中的所有分支。每一个文件名和你的分支名一一对应文件中包含一个提交的哈希字串。这个就是分支顶端的所在位置。为了验证这一点试试在Git根目录运行下面这两个命令
`heads`目录定义了你本地仓库中的所有分支。每一个文件名和你的分支名一一对应,文件中包含一个提交的哈希字串。这个就是分支顶端的所在位置。为了验证这一点,试试在 Git 根目录运行下面这两个命令:
```
```
# 输出`refs/heads/master`文件内容
cat .git/refs/heads/master
@ -66,168 +66,168 @@ cat .git/refs/heads/master
git log -1 master
```
`cat`命令返回的哈希字串和`git log`命令显示的哈希字串应该是一致的。
`cat` 命令返回的哈希字串和 `git log` 命令显示的哈希字串应该是一致的。
如果要改变master分支的位置Git只需要更改`refs/heads/master`的文件内容。同样地创建新的分支也只需要将当前提交的哈希字串写入到新的文件中。这也是为什么Git分支比SVN轻量那么多的其中一个原因。
如果要改变 master 分支的位置Git 只需要更改 `refs/heads/master` 的文件内容。同样地,创建新的分支也只需要将当前提交的哈希字串写入到新的文件中。这也是为什么 Git 分支比 SVN 轻量那么多的其中一个原因。
`tags`目录也是以相同的方式存储,只不过其中存的是标签而不是分支。`remotes`目录将你之前用`git remote`命令创建的所有远程仓库以子目录的形式一一列出。在每个文件夹中你可以找到所有fetch到本地仓库的远程分支。
`tags` 目录也是以相同的方式存储,只不过其中存的是标签而不是分支。`remotes` 目录将你之前用 `git remote` 命令创建的所有远程仓库以子目录的形式一一列出。在每个文件夹中,你可以找到所有 fetch 到本地仓库的远程分支。
### 指定引用
当你向Git命令传入引用的时候你既可以指定引用完整的名称也可以使用缩写然后让Git来寻找匹配。你应该已经对引用的缩写很熟悉了每次你通过名称引用分支的时候都会这么做。
当你向 Git 命令传入引用的时候,你既可以指定引用完整的名称,也可以使用缩写,然后让 Git 来寻找匹配。你应该已经对引用的缩写很熟悉了,每次你通过名称引用分支的时候都会这么做。
```
```
git show some-feature
```
这里的`some-feature`参数其实是分支名的缩写。Git在使用前将它解析成`refs/heads/some-feature`。你也可以在命令行中指定引用的全称,就像这样:
这里的 `some-feature` 参数其实是分支名的缩写。Git 在使用前将它解析成 `refs/heads/some-feature`。你也可以在命令行中指定引用的全称,就像这样:
```
```
git show refs/heads/some-feature
```
这避免了引用可能产生的所有歧义。这是非常必要的,比如你同时有一个标签和分支都叫`some-feature`。然而,如果使用正常的命名规范,你不应该有这样的歧义。
这避免了引用可能产生的所有歧义。这是非常必要的,比如你同时有一个标签和分支都叫 `some-feature`。然而,如果使用正常的命名规范,你不应该有这样的歧义。
我们会在refspec一节见到更多引用名称。
我们会在 refspec 一节见到更多引用名称。
## 打包引用目录
对于大型仓库Git会周期性地执行垃圾回收来移除不需要的对象将所有引用文件压缩成单个文件来获得更好的性能。你可以使用这个命令强制垃圾回收来执行压缩
对于大型仓库Git 会周期性地执行垃圾回收来移除不需要的对象,将所有引用文件压缩成单个文件来获得更好的性能。你可以使用这个命令强制垃圾回收来执行压缩:
```
```
git gc
```
这个命令把`refs`文件夹中所有单独的分支和标签移动到了`.git`根目录下的`packed-refs`文件中。如果你打开这个文件,你会发现提交的哈希字串和引用之间的映射关系:
这个命令把 `refs` 文件夹中所有单独的分支和标签移动到了 `.git` 根目录下的 `packed-refs` 文件中。如果你打开这个文件,你会发现提交的哈希字串和引用之间的映射关系:
```
```
00f54250cf4e549fdfcafe2cf9a2c90bc3800285 refs/heads/feature
0e25143693cfe9d5c2e83944bbaf6d3c4505eb17 refs/heads/master
bb883e4c91c870b5fed88fd36696e752fb6cf8e6 refs/tags/v0.9
```
另一方面正常的Git功能不会受到任何影响。但如果你好奇你的`.git/refs`文件夹为什么是空的,这一节告诉你了答案。
另一方面,正常的 Git 功能不会受到任何影响。但如果你好奇你的 `.git/refs` 文件夹为什么是空的,这一节告诉你了答案。
## 特殊的引用
除了`refs`文件夹外,`.git`根目录还有一些特殊的引用。如下所示:
除了 `refs` 文件夹外,`.git` 根目录还有一些特殊的引用。如下所示:
- HEAD 当前所在的提交或分支。
- FETCH_HEAD 远程仓库中fetch到的最新一次提交。
- ORIG_HEAD HEAD的备份引用避免损坏。
- MERGE_HEAD 你通过`git merge`并入当前分支的引用(们)
- CHERRY_PICK_HEAD 你`cherry pick`使用的引用。
- FETCH_HEAD 远程仓库中 fetch 到的最新一次提交。
- ORIG_HEAD HEAD 的备份引用,避免损坏。
- MERGE_HEAD 你通过 `git merge` 并入当前分支的引用(们)
- CHERRY_PICK_HEAD `cherry pick` 使用的引用。
这些引用由Git在需要时创建和更新。比如说`git pull`命令首先运行`git fetch`,而`FETCH_HEAD`引用随之改变。然后,运行`git merge FETCH_HEAD`来将fetch到的分支最终并入仓库。当然你也可以使用其他任何引用因为我相信你已经对`HEAD`很熟悉了。
这些引用由 Git 在需要时创建和更新。比如说,`git pull` 命令首先运行 `git fetch`,而 `FETCH_HEAD` 引用随之改变。然后,运行 `git merge FETCH_HEAD` 来将 fetch 到的分支最终并入仓库。当然,你也可以使用其他任何引用,因为我相信你已经对 `HEAD` 很熟悉了。
这些文件包含的内容取决于它们的类型和你的仓库状态。`HEAD` 引用可以包含符号链接指向另一个引用而不是哈希字串或是提交的哈希字串。比如说看看当你在master分支上时`HEAD`的内容:
这些文件包含的内容取决于它们的类型和你的仓库状态。`HEAD` 引用可以包含符号链接(指向另一个引用而不是哈希字串),或是提交的哈希字串。比如说,看看当你在 master 分支上时 `HEAD` 的内容:
```
```
git checkout master
cat .git/HEAD
```
这个命令会输出`ref: refs/heads/master`也就是说HEAD指向`refs/heads/master`这个引用。这也正是Git如何知道现在所在的是master分支。如果你要切换分支`HEAD`的内容将会被更新到新的分支。但如果你要切换到一个提交而不是分支,`HEAD`会包含一个提交的哈希而不是符号引用。这就是Git如何知道现在`HEAD`处于分离状态。
这个命令会输出 `ref: refs/heads/master`,也就是说 HEAD 指向 `refs/heads/master` 这个引用。这也正是 Git 如何知道现在所在的是 master 分支。如果你要切换分支,`HEAD` 的内容将会被更新到新的分支。但如果你要切换到一个提交而不是分支,`HEAD` 会包含一个提交的哈希而不是符号引用。这就是 Git 如何知道现在 `HEAD` 处于分离状态。
在大多数情况下,`HEAD`是你唯一用得到的引用。其它引用一般只在写底层脚本接触到Git内部的工作机制时才会用到。
在大多数情况下,`HEAD` 是你唯一用得到的引用。其它引用一般只在写底层脚本,接触到 Git 内部的工作机制时才会用到。
## refspec
refspec将本地分支和远程分支对应起来。我们可以通过它用本地的Git命令管理远程分支设置一些高级的`git push`和`git fetch`行为。
refspec 将本地分支和远程分支对应起来。我们可以通过它用本地的 Git 命令管理远程分支,设置一些高级的 `git push``git fetch` 行为。
refspec的定义是这样的`[+]<src>:<dst>`。`<src>`参数是本地的源分支,`<dst>`是远程的目标分支。可选的`+`号强制远程仓库采用非快速向前的更新策略。
refspec 的定义是这样的:`[+]<src>:<dst>``<src>` 参数是本地的源分支,`<dst>` 是远程的目标分支。可选的 `+` 号强制远程仓库采用非快速向前的更新策略。
refspec可以和`git push`一起使用用来指定远程的分支的名称。比如下面这个命令将master分支推送到远程origin就像一般的`git push`一样但它使用qa-master作为远程仓库中的分支名。对于QA团队来说这个方法非常有用。
refspec 可以和 `git push` 一起使用,用来指定远程的分支的名称。比如,下面这个命令将 master 分支推送到远程 origin就像一般的 `git push` 一样,但它使用 qa-master 作为远程仓库中的分支名。对于 QA 团队来说,这个方法非常有用。
```
```
git push origin master:refs/heads/qa-master
```
你也可以用refspec来删除远程分支。feature分支的工作流经常会遇到这种情况将feature分支推送到远程仓库中比如说为了备份。你删除本地的feature分支之后远程的feature分支依然存在虽然现在我们已经不再需要它。你可以push一个`<src>`参数为空的refspec来删除它们就像这样
你也可以用 refspec 来删除远程分支。feature 分支的工作流经常会遇到这种情况,将 feature 分支推送到远程仓库中(比如说为了备份)。你删除本地的 feature 分支之后,远程的 feature 分支依然存在,虽然现在我们已经不再需要它。你可以 push 一个 `<src>` 参数为空的 refspec 来删除它们,就像这样:
```
```
git push origin:some-feature
```
这非常方便因为你不需要登录到你的远程仓库然后手动删除这些远程分支。注意在Git v1.7.0之后你可以用`--delete`标记代替上面这个方法。下面这个命令和上面的命令作用相同:
这非常方便,因为你不需要登录到你的远程仓库然后手动删除这些远程分支。注意,在 Git v1.7.0 之后你可以用 `--delete` 标记代替上面这个方法。下面这个命令和上面的命令作用相同:
```
```
git push origin --delete some-feature
```
在Git配置文件中增加几行你就可以更改`git fetch`的行为。默认地,`git fetch`会fetch远程仓库中所有分支。原因就是`.git/config`文件的这段配置:
Git 配置文件中增加几行,你就可以更改 `git fetch` 的行为。默认地,`git fetch`fetch 远程仓库中所有分支。原因就是 `.git/config` 文件的这段配置:
```
```
[remote "origin"]
url = https://git@github.com:mary/example-repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
```
fetch这一行告诉`git fetch`从origin仓库中下载所有分支。但是一些工作流不需要所有分支。比如很多持续集成工作流只关心master分支。为了做到这一点我们需要将fetch这行改成下面这样
fetch 这一行告诉 `git fetch`origin 仓库中下载所有分支。但是,一些工作流不需要所有分支。比如,很多持续集成工作流只关心 master 分支。为了做到这一点,我们需要将 fetch 这行改成下面这样:
```
```
[remote "origin"]
url = https://git@github.com:mary/example-repo.git
fetch = +refs/heads/master:refs/remotes/origin/master
```
你还可以类似地修改`git push`的配置。比如如果你总是将master分支推送到origin仓库的qa-master分支就像我们之前做的一样你要把配置文件改成这样
你还可以类似地修改 `git push` 的配置。比如,如果你总是将 master 分支推送到 origin 仓库的 qa-master 分支(就像我们之前做的一样),你要把配置文件改成这样:
```
```
[remote "origin"]
url = https://git@github.com:mary/example-repo.git
fetch = +refs/heads/master:refs/remotes/origin/master
push = refs/heads/master:refs/heads/qa-master
```
refspec给了你完全的掌控权可以定制Git命令如何在仓库之间转移分支。你可以重命名或是删除你的本地分支fetch或是push不同的分支名修改`git push`和`git fetch`的设置,只对你想要的分支进行操作。
refspec 给了你完全的掌控权,可以定制 Git 命令如何在仓库之间转移分支。你可以重命名或是删除你的本地分支fetch 或是 push 不同的分支名,修改 `git push``git fetch` 的设置,只对你想要的分支进行操作。
## 相对引用
你还可以通过提交之间的相对关系来引用。`~`符号让你访问父节点的提交。比如说,下面这个命令显示`HEAD`祖父节点的提交:
你还可以通过提交之间的相对关系来引用。`~` 符号让你访问父节点的提交。比如说,下面这个命令显示 `HEAD` 祖父节点的提交:
```
```
git show HEAD~2
```
但是,面对合并提交(merge commit)的时候,事情就会变得有些复杂。因为合并提交有多个父节点,所以你可以找到多条回溯的路径。对于3路合并,第一个父节点是你执行合并时的分支,第二个父节点是你传给`git merge`命令的分支。
但是,面对合并提交merge commit的时候,事情就会变得有些复杂。因为合并提交有多个父节点,所以你可以找到多条回溯的路径。对于 3 路合并,第一个父节点是你执行合并时的分支,第二个父节点是你传给 `git merge` 命令的分支。
`~`符号总是选择合并提交的第一个父节点。如果你想选择其他父节点,你需要用`^`符号来指定。比如说,`HEAD`是一个合并提交,下面这个命令返回`HEAD`的第二个父节点:
`~` 符号总是选择合并提交的第一个父节点。如果你想选择其他父节点,你需要用 `^` 符号来指定。比如说,`HEAD` 是一个合并提交,下面这个命令返回 `HEAD` 的第二个父节点:
```
```
git show HEAD^2
```
你可以使用不止一个`^`来查看超过一层的节点。比如,下面的命令显示的是`HEAD`的祖父节点,也就是`HEAD`第二个父节点的父节点。
你可以使用不止一个 `^` 来查看超过一层的节点。比如,下面的命令显示的是 `HEAD` 的祖父节点,也就是 `HEAD` 第二个父节点的父节点。
```
```
git show HEAD^2^1
```
为了阐明`~`和`^`是如何工作的,下面这张图告诉你如何使用相对引用,来指向任意的提交。有的提交可以通过多种方式引用。
为了阐明 `~``^` 是如何工作的,下面这张图告诉你如何使用相对引用,来指向任意的提交。有的提交可以通过多种方式引用。
![Accessing commits using relative refs](https://www.atlassian.com/git/images/tutorials/advanced/refs-and-the-reflog/02.svg)
相对引用在命令中的用法和普通的引用相同。比如,下面所有命令中使用的都是相对引用:
```
```
# 只列出合并提交的第二个父节点的父节点
git log HEAD^2
# 移除当前分支最新的3个提交
# 移除当前分支最新的 3 个提交
git reset HEAD~3
# 交互式rebase当前分支最新的3个提交
# 交互式rebase当前分支最新的 3 个提交
git rebase -i HEAD~3
```
## 引用日志
引用日志是Git的安全网。它记录了你在仓库中做的所有更改不管你有没有提交。你也可以认为这是你本地更改的完整历史记录。运行`git reflog`命令查看引用日志。它应该会打印出像下面这样的信息:
引用日志是 Git 的安全网。它记录了你在仓库中做的所有更改,不管你有没有提交。你也可以认为这是你本地更改的完整历史记录。运行 `git reflog` 命令查看引用日志。它应该会打印出像下面这样的信息:
```
```
400e4b7 HEAD@{0}: checkout: moving from master to HEAD~2
0e25143 HEAD@{1}: commit (amend): 将一些很赞的新特性引入`master`
00f5425 HEAD@{2}: commit (merge): 合并'feature'分支
@ -236,42 +236,40 @@ ad8621a HEAD@{3}: commit: 结束feature分支开发
说人话就是:
- 你刚刚切换到`HEAD~2`
- 你刚刚切换到 `HEAD~2`
- 你刚刚修改了一个提交信息
- 你刚刚把feature分支合并到了master分支
- 你刚刚把 feature 分支合并到了 master 分支
- 你刚刚提交了一份缓存
`HEAD{<n>}`语法允许你引用保存在日志中的提交。这和上一节的`HEAD~<n>`引用差不多,不过`<n>`指的是引用日志中的对象,而不是提交历史。
`HEAD{<n>}` 语法允许你引用保存在日志中的提交。这和上一节的 `HEAD~<n>` 引用差不多,不过 `<n>` 指的是引用日志中的对象,而不是提交历史。
你可以用办法回到之前可能已经丢失的状态。比如,你刚刚用`git reset`方法粉碎了新的feature分支。你的引用日志看上去可能会是这样的
你可以用办法回到之前可能已经丢失的状态。比如,你刚刚用 `git reset` 方法粉碎了新的 feature 分支。你的引用日志看上去可能会是这样的:
```
```
ad8621a HEAD@{0}: reset: moving to HEAD~3
298eb9f HEAD@{1}: commit: 一些提交信息
bbe9012 HEAD@{2}: commit: 继续开发
9cb79fa HEAD@{3}: commit: 开始新特性开发
```
`git reset`前的三个提交现在都成了悬挂的了,也就是说除了引用日志之外没有办法再引用到它们。现在,假设你意识到了你不应该丢掉你全部的工作。你只需要切换到`HEAD@{1}`这个提交就能回到你运行`git reset`之前仓库的状态。
`git reset` 前的三个提交现在都成了悬挂的了,也就是说除了引用日志之外没有办法再引用到它们。现在,假设你意识到了你不应该丢掉你全部的工作。你只需要切换到 `HEAD@{1}` 这个提交就能回到你运行 `git reset` 之前仓库的状态。
```
```
git checkout HEAD@{1}
```
这会让你处于`HEAD`分离的状态。你可以从这里开始,创建新的分支,继续你的工作。
这会让你处于 `HEAD` 分离的状态。你可以从这里开始,创建新的分支,继续你的工作。
## 总结
你现在对Git提交的引用应该已经相当熟悉了。我们知道了分支和标签是如何存在于`.git`的子文件夹refs中如何读取打包的引用文件如何使用refspec来进行更高级的pushfetch操作如何使用`~`和`^`符号来遍历分支结构。
你现在对 Git 提交的引用应该已经相当熟悉了。我们知道了分支和标签是如何存在于 `.git` 的子文件夹 refs 中,如何读取打包的引用文件,如何使用 refspec 来进行更高级的 pushfetch 操作,如何使用 `~``^` 符号来遍历分支结构。
我们还了解了引用日志,来引用到其他方式已经不存在的提交。这是一种很好的恢复误删提交的方法。
它的意义在于在任何开发场景下你都能找到你需要的特定提交。你很容易就可以把这些技巧用在你一有的Git知识中因为很多常用的命令都接受引用作为参数包括`git log`、`git show`、`git checkout`、`git reset`、`git revert`、`git rebase`等等。
它的意义在于:在任何开发场景下,你都能找到你需要的特定提交。你很容易就可以把这些技巧用在你一有的 Git 知识中,因为很多常用的命令都接受引用作为参数,包括 `git log``git show``git checkout``git reset``git revert``git rebase` 等等。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)

View File

@ -1,23 +1,23 @@
# Git简易指南(上)
# Git 简易指南(上)
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。[git-guide](https://github.com/rogerdudler/git-guide/) 项目对本文亦有贡献。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。[git-guide](https://github.com/rogerdudler/git-guide/) 项目对本文亦有贡献。
这节是完全面向入门者的我假设你从零开始创建一个项目并且想用Git来进行版本控制因此本文会避开分支这些相对复杂的概念。
这节是完全面向入门者的,我假设你从零开始创建一个项目并且想用 Git 来进行版本控制,因此本文会避开分支这些相对复杂的概念。
在这节中我会介绍如何在你的个人项目中使用Git我们会讨论Git最基本的操作——如何初始化你的项目如何管理新的或者已有的文件如何在远端仓库中储存你的代码。
在这节中,我会介绍如何在你的个人项目中使用 Git我们会讨论 Git 最基本的操作——如何初始化你的项目,如何管理新的或者已有的文件,如何在远端仓库中储存你的代码。
## 安装Git
## 安装 Git
- Mac用户Xcode Command Line Tools自带Git (`xcode-select --install` )
- Linux用户`sudo apt-get install git`
- Windows用户下载[Git SCM](git-for-windows.github.io)
```
- 对于Windows用户安装后如果希望在全局的cmd中使用git需要把git.exe加入PATH环境变量中或在Git Bash中使用Git。
- Mac 用户Xcode Command Line Tools 自带 Git`xcode-select --install`
- Linux 用户:`sudo apt-get install git`
- Windows 用户:下载 [Git SCM](git-for-windows.github.io)
```
- 对于 Windows 用户,安装后如果希望在全局的 cmd 中使用 Git需要把 git.exe 加入 PATH 环境变量中,或在 Git Bash 中使用 Git。
```
## 检出仓库
@ -28,75 +28,72 @@
如果是远端服务器上的仓库,你的命令会是这个样子:
`git clone username@host:/path/to/repository` 通过SSH
`git clone username@host:/path/to/repository` (通过 SSH
或者:
`git clone https:/path/to/repository.git` 通过https
`git clone https:/path/to/repository.git` (通过 https
比如说`git clone https://github.com/geeeeeeeeek/git-recipes.git`可以将git教程clone到你指定的目录。
比如说 `git clone https://github.com/geeeeeeeeek/git-recipes.git` 可以将 git 教程 clone 到你指定的目录。
## 创建新仓库
创建新文件夹,打开,然后执行 `git init`以创建新的 git 仓库。
创建新文件夹,打开,然后执行 `git init` 以创建新的 git 仓库。
> 下面每一步中,你都可以通过`git status`来查看你的git仓库状态。
> 下面每一步中,你都可以通过 `git status` 来查看你的git仓库状态。
## 工作流
你的本地仓库由 git 维护的三棵“树”组成。第一个是你的 `工作目录`,它持有实际文件;第二个是 `缓存区(Index)`,它像个缓存区域,临时保存你的改动;最后是 `HEAD`,指向你最近一次提交后的结果。
你的本地仓库由 Git 维护的三棵「树」组成。第一个是你的 `工作目录`,它持有实际文件;第二个是 `缓存区Index`,它像个缓存区域,临时保存你的改动;最后是 `HEAD`,指向你最近一次提交后的结果。
![enter image description here](http://www.bootcss.com/p/git-guide/img/trees.png)
> 事实上第三个阶段是commit history的图。HEAD一般是指向最新一次commit的引用。现在暂时不必究其细节。
> 事实上,第三个阶段是 commit history 的图。HEAD 一般是指向最新一次 commit 的引用。现在暂时不必究其细节。
## 添加与提交
你可以计划改动(把它们添加到缓存区),使用如下命令:
```
```
git add < filename >
git add *
```
这是 git 基本工作流程的第一步。使用如下命令以实际提交改动:
这是 Git 基本工作流程的第一步。使用如下命令以实际提交改动:
```
```
git commit -m "代码提交信息"
```
现在,你的改动已经提交到了 HEAD但是还没到你的远端仓库。
> 在开发时良好的习惯是根据工作进度及时commit并务必注意附上有意义的commit message。创建完项目目录后第一次提交的commit message一般为"Initial commit."
> 在开发时,良好的习惯是根据工作进度及时 commit并务必注意附上有意义的 commit message。创建完项目目录后第一次提交的 commit message 一般为Initial commit
## 推送改动
你的改动现在已经在本地仓库的 HEAD 中了。执行如下命令以将这些改动提交到远端仓库:
```
```
git push origin master
```
可以把 master 换成你想要推送的任何分支。
可以把 master 换成你想要推送的任何分支。
如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:
```
```
git remote add origin <server>
```
如此你就能够将你的改动推送到所添加的服务器上去了。
> - 这里origin是< server >的别名取什么名字都可以你也可以在push时将< server >替换为origin。但为了以后push方便我们第一次一般都会先remote add。
> - 如果你还没有git仓库可以在Github等代码托管平台上创建一个空(不要自动生成README.md)的repository然后将代码push到远端仓库。
> - 这里 origin 是 &lt;server&gt; 的别名,取什么名字都可以,你也可以在 push 时将 &lt;jserver&gt; 替换为 origin。但为了以后 push 方便,我们第一次一般都会先 remote add。
> - 如果你还没有 Git 仓库,可以在 Github 等代码托管平台上创建一个空不要自动生成 README.md)的仓库,然后将代码 push 到远端仓库。
##### 至此,你应该可以顺利地提交你的项目了。在下一节中,我们将涉及更多的命令,来完成更有用的操作。比如从远端的仓库拉取更新并且合并到你的本地,如何通过分支多人协作,如何处理不同分支的冲突等等。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:。
>
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,28 +1,28 @@
# Git钩子自定义你的工作流
# Git 钩子:自定义你的工作流
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/git-hooks)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/git-hooks)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
Git钩子是在Git仓库中特定事件发生时自动运行的脚本。它可以让你自定义Git内部的行为在开发周期中的关键点触发自定义的行为。
Git 钩子是在 Git 仓库中特定事件发生时自动运行的脚本。它可以让你自定义 Git 内部的行为,在开发周期中的关键点触发自定义的行为。
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/git-hooks/01.svg)
Git钩子最常见的使用场景包括推行提交规范根据仓库状态改变项目环境和接入持续集成工作流。但是因为脚本可以完全定制你可以用Git钩子来自动化或者优化你开发工作流中任意部分。
Git 钩子最常见的使用场景包括推行提交规范,根据仓库状态改变项目环境,和接入持续集成工作流。但是,因为脚本可以完全定制,你可以用 Git 钩子来自动化或者优化你开发工作流中任意部分。
在这篇文章中我们会先简要介绍Git钩子是如何工作的。然后我们会审视一些本地和远端仓库使用最流行的钩子。
在这篇文章中,我们会先简要介绍 Git 钩子是如何工作的。然后,我们会审视一些本地和远端仓库使用最流行的钩子。
# 概述
Git钩子是仓库中特定事件发生时Git自动运行的普通脚本。因此Git钩子安装和配置也非常容易。
Git 钩子是仓库中特定事件发生时 Git 自动运行的普通脚本。因此Git 钩子安装和配置也非常容易。
钩子在本地或服务端仓库都可以部署,且只会在仓库中事件发生时被执行。在文章后面我们会具体地研究各种钩子。接下来所讲的配置对本地和服务端钩子都起作用。
### 安装钩子
钩子存在于每个Git仓库的`.git/hooks`目录中。当你初始化仓库时Git自动生成这个目录和一些示例脚本。当你观察`.git/hooks`时,你会看到下面这些文件:
钩子存在于每个 Git 仓库的 `.git/hooks` 目录中。当你初始化仓库时Git 自动生成这个目录和一些示例脚本。当你观察 `.git/hooks` 时,你会看到下面这些文件:
```
```
applypatch-msg.sample pre-push.sample
commit-msg.sample pre-rebase.sample
post-update.sample prepare-commit-msg.sample
@ -30,33 +30,33 @@ pre-applypatch.sample update.sample
pre-commit.sample
```
这里已经包含了大部分可用的钩子了,但是`.sample`拓展名防止它们默认被执行。为了安装一个钩子,你只需要去掉`.sample`拓展名。或者你要写一个新的脚本,你只需添加一个文件名和上述匹配的新文件,去掉`.sample`拓展名。
这里已经包含了大部分可用的钩子了,但是 `.sample` 拓展名防止它们默认被执行。为了安装一个钩子,你只需要去掉 `.sample` 拓展名。或者你要写一个新的脚本,你只需添加一个文件名和上述匹配的新文件,去掉 `.sample` 拓展名。
比如说,试试安装一个`prepare-commit-msg`钩子。去掉脚本的`.sample`拓展名,在文件中加上下面这两行:
比如说,试试安装一个 `prepare-commit-msg` 钩子。去掉脚本的 `.sample` 拓展名,在文件中加上下面这两行:
```
```
#!/bin/sh
echo "# Please include a useful commit message!" > $1
```
钩子需要能被执行,所以如果你创建了一个新的脚本文件,你需要修改它的文件权限。比如说,为了确保`prepare-commit-msg`可执行,运行下面这个命令:
钩子需要能被执行,所以如果你创建了一个新的脚本文件,你需要修改它的文件权限。比如说,为了确保 `prepare-commit-msg` 可执行,运行下面这个命令:
```
```
chmod +x prepare-commit-msg
```
接下来你每次运行`git commit`时,你会看到默认的提交信息都被替换了。我们会在准备提交信息一节中细看它是如何工作的。现在我们已经可以定制Git的内部功能你只需要坐和放宽。
接下来你每次运行 `git commit` 时,你会看到默认的提交信息都被替换了。我们会在准备提交信息一节中细看它是如何工作的。现在我们已经可以定制 Git 的内部功能,你只需要坐和放宽。
内置的样例脚本是非常有用的参考资料,因为每个钩子传入的参数都有非常详细的说明(不同钩子不一样)。
### 脚本语言
内置的脚本大多是shell和PERL语言的但你可以使用任何脚本语言只要它们最后能编译到可执行文件。每次脚本中的`#!/bin/sh`定义了你的文件将被如何解释。比如使用其他语言时你只需要将path改为你的解释器的路径。
内置的脚本大多是 shell和 PERL 语言的,但你可以使用任何脚本语言,只要它们最后能编译到可执行文件。每次脚本中的 `#!/bin/sh` 定义了你的文件将被如何解释。比如,使用其他语言时你只需要将 path 改为你的解释器的路径。
比如说,你可以在`prepare-commit-msg`中写一个可执行的Python脚本。下面这个钩子和上一节的shell脚本做的事完全一样。
比如说,你可以在 `prepare-commit-msg` 中写一个可执行的 Python 脚本。下面这个钩子和上一节的 shell 脚本做的事完全一样。
```
```
#!/usr/bin/env python
import sys, os
@ -66,23 +66,23 @@ with open(commit_msg_filepath, 'w') as f:
f.write("# Please include a useful commit message!")
```
注意第一行改成了Python解释器的路径。此外这里用`sys.argv[1]`而不是`$1`来获取第一个参数(这个也后面再讲)。
注意第一行改成了 Python 解释器的路径。此外,这里用 `sys.argv[1]` 而不是 `$1` 来获取第一个参数(这个也后面再讲)。
这个特性非常强大因为你可以用任何你喜欢的语言来编写Git钩子。
这个特性非常强大,因为你可以用任何你喜欢的语言来编写 Git 钩子。
### 钩子的作用域
对于任何Git仓库来说钩子都是本地的而且它不会随着`git clone`一起复制到新的仓库。而且,因为钩子是本地的,任何能接触得到仓库的人都可以修改。
对于任何 Git 仓库来说钩子都是本地的,而且它不会随着 `git clone` 一起复制到新的仓库。而且,因为钩子是本地的,任何能接触得到仓库的人都可以修改。
对于开发团队来说,这有很大的影响。首先,你要确保你们成员之间的钩子都是最新的。其次,你也不能强行让其他人用你喜欢的方式提交——你只能鼓励他们这样做。
在开发团队中维护钩子是比较复杂的,因为`.git/hooks`目录不随你的项目一起拷贝,也不受版本控制影响。一个简单的解决办法是把你的钩子存在项目的实际目录中(在`.git` 外)。这样你就可以像其他文件一样进行版本控制。为了安装钩子,你可以在`.git/hooks`中创建一个符号链接,或者简单地在更新后把它们复制到`.git/hooks`目录下。
在开发团队中维护钩子是比较复杂的,因为 `.git/hooks` 目录不随你的项目一起拷贝,也不受版本控制影响。一个简单的解决办法是把你的钩子存在项目的实际目录中(在 `.git` 外)。这样你就可以像其他文件一样进行版本控制。为了安装钩子,你可以在 `.git/hooks` 中创建一个符号链接,或者简单地在更新后把它们复制到 `.git/hooks` 目录下。
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/git-hooks/02.svg)
作为备选方案Git同样提供了一个模板目录机制来更简单地自动安装钩子。每次你使用`git init` 或`git clone`时,模板目录文件夹下的所有文件和目录都会被复制到`.git` 文件夹。
作为备选方案Git 同样提供了一个模板目录机制来更简单地自动安装钩子。每次你使用 `git init``git clone` 时,模板目录文件夹下的所有文件和目录都会被复制到 `.git` 文件夹。
所有的下面讲到的本地钩子都可以被更改或者彻底删除只要你是项目的参与者。这完全取决于你的团队成员想不想用这个钩子。所以记住最好把Git钩子当成一个方便的开发者工具而不是一个严格强制的开发规范。
所有的下面讲到的本地钩子都可以被更改或者彻底删除,只要你是项目的参与者。这完全取决于你的团队成员想不想用这个钩子。所以记住,最好把 Git 钩子当成一个方便的开发者工具而不是一个严格强制的开发规范。
也就是说,用服务端钩子来拒绝没有遵守规范的提交是完全可行的。后面我们会再讨论这个问题。
@ -90,7 +90,7 @@ with open(commit_msg_filepath, 'w') as f:
本地钩子只影响它们所在的仓库。当你在读这一节的时候,记住开发者可以修改他们本地的钩子,所以不要用它们来推行强制的提交规范。不过,它们确实可以让开发者更易于接受这些规范。
在这一节中,我们会探讨6个最有用的本地钩子:
在这一节中,我们会探讨 6 个最有用的本地钩子:
- pre-commit
- prepare-commit-msg
@ -99,19 +99,19 @@ with open(commit_msg_filepath, 'w') as f:
- post-checkout
- pre-rebase
前四个钩子让你介入完整的提交生命周期,后两个允许你执行一些额外的操作,分别为`git checkout`和`git rebase`的安全检查。
前四个钩子让你介入完整的提交生命周期,后两个允许你执行一些额外的操作,分别为 `git checkout``git rebase` 的安全检查。
所有带`pre-` 的钩子允许你修改即将发生的操作,而带`post-` 的钩子只能用于通知。
所有带 `pre-` 的钩子允许你修改即将发生的操作,而带 `post-` 的钩子只能用于通知。
我们也会看到处理钩子的参数和用底层Git命令获取仓库信息的实用技巧。
我们也会看到处理钩子的参数和用底层 Git 命令获取仓库信息的实用技巧。
### pre-commit
`pre-commit`脚本在每次你运行`git commit`命令时Git向你询问提交信息或者生产提交对象时被执行。你可以用这个钩子来检查即将被提交的代码快照。比如说你可以运行一些自动化测试保证这个提交不会破坏现有的功能。
`pre-commit` 脚本在每次你运行 `git commit` 命令时Git 向你询问提交信息或者生产提交对象时被执行。你可以用这个钩子来检查即将被提交的代码快照。比如说,你可以运行一些自动化测试,保证这个提交不会破坏现有的功能。
`pre-commit`不需要任何参数以非0状态退出时将放弃整个提交。让我们看一个简化了的和更详细的内置`pre-commit`钩子。只要检测到不一致时脚本就放弃这个提交,就像`git diff-index`命令定义的那样只要词尾有空白字符、只有空白字符的行、行首一个tab后紧接一个空格就被认为错误
`pre-commit` 不需要任何参数以非0状态退出时将放弃整个提交。让我们看一个简化了的和更详细的内置 `pre-commit` 钩子。只要检测到不一致时脚本就放弃这个提交,就像 `git diff-index` 命令定义的那样(只要词尾有空白字符、只有空白字符的行、行首一个 tab 后紧接一个空格就被认为错误)。
```
```
#!/bin/sh
# 检查这是否是初始提交
@ -136,29 +136,29 @@ else
fi
```
使用`git diff-index`时我们要指出和哪次提交进行比较。一般来说是HEAD但HEAD在创建第一次提交时不存在所以我们的第一个任务是解决这个极端情形。我们通过`git rev-parse --verify`来检查HEAD是否是一个合法的引用。`>/dev/null 2>&1`这部分屏蔽了`git rev-parse`任何输出。HEAD或者一个新的提交对象被储存在`against`变量中供`git diff-index`使用。`4b825d...`这个哈希字串代表一个空白提交的ID。
使用 `git diff-index` 时我们要指出和哪次提交进行比较。一般来说是 HEAD HEAD 在创建第一次提交时不存在,所以我们的第一个任务是解决这个极端情形。我们通过 `git rev-parse --verify` 来检查 HEAD 是否是一个合法的引用。`>/dev/null 2>&1` 这部分屏蔽了 `git rev-parse` 任何输出。HEAD 或者一个新的提交对象被储存在 `against` 变量中供 `git diff-index` 使用。`4b825d...` 这个哈希字串代表一个空白提交的 ID。
`git diff-index --cached`命令将提交和缓存区比较。通过传入`-check`选项我们要求它在更改引入空白字符错误时警告我们。如果它这么做了我们返回状态1来放弃这次提交否则返回状态0提交工作流正常进行。
`git diff-index --cached` 命令将提交和缓存区比较。通过传入 `-check` 选项我们要求它在更改引入空白字符错误时警告我们。如果它这么做了我们返回状态1来放弃这次提交否则返回状态 0提交工作流正常进行。
这只是`pre-commit`的其中一个例子。它恰好使用了已有的Git命令来根据提交带来的更改进行测试但你可以在`pre-commit`中做任何你想做的事比如执行其它脚本、运行第三方测试集、用Lint检查代码风格。
这只是 `pre-commit` 的其中一个例子。它恰好使用了已有的 Git 命令来根据提交带来的更改进行测试,但你可以在 `pre-commit` 中做任何你想做的事,比如执行其它脚本、运行第三方测试集、用 Lint 检查代码风格。
### prepare-commit-msg
`prepare-commit-msg`钩子在`pre-commit`钩子在文本编辑器中生成提交信息之后被调用。这被用来方便地修改自动生成的squashmerge提交。
`prepare-commit-msg` 钩子在 `pre-commit` 钩子在文本编辑器中生成提交信息之后被调用。这被用来方便地修改自动生成的 squashmerge 提交。
`prepare-commit-msg`脚本的参数可以是下列三个:
`prepare-commit-msg` 脚本的参数可以是下列三个:
- 包含提交信息的文件名。你可以在原地更改提交信息。
- 提交类型。可以是信息(`-m`或`-F`选项),模板(`-t`选项merge如果是个合并提交或squash如果这个提交插入了其他提交
- 相关提交的SHA1哈希字串。只有当`-c``-C`,或`--amend`选项出现时才需要。
- 提交类型。可以是信息(`-m``-F` 选项),模板(`-t` 选项merge如果是个合并提交 squash如果这个提交插入了其他提交
- 相关提交的 SHA1 哈希字串。只有当 `-c``-C``--amend` 选项出现时才需要。
和`pre-commit`一样以非0状态退出会放弃提交。
`pre-commit` 一样以非0状态退出会放弃提交。
我们已经看过一个修改提交信息的简单例子现在我们来看一个更有用的脚本。使用issue跟踪器时我们通常在单独的分支上处理issue。如果你在分支名中包含了issue编号你可以使用`prepare-commit-msg`钩子来自动地将它包括在那个分支的每个提交信息中。
我们已经看过一个修改提交信息的简单例子,现在我们来看一个更有用的脚本。使用 issue 跟踪器时,我们通常在单独的分支上处理 issue。如果你在分支名中包含了 issue 编号,你可以使用 `prepare-commit-msg` 钩子来自动地将它包括在那个分支的每个提交信息中。
```
```
#!/usr/bin/env python
import sys, os, re
@ -193,31 +193,31 @@ if branch.startswith('issue-'):
f.write("ISSUE-%s %s" % (issue_number, content))
```
首先,上面的`prepare-commit-msg`钩子告诉你如何收集传入脚本的所有参数。接下来,它调用了`git symbolic-ref --short HEAD`来获取对应HEAD的分支名。如果分支名以`issue-`开头它会重写提交信息文件在第一行加上issue编号。比如你的分支名`issue-224` ,下面的提交信息将会生成:
首先,上面的 `prepare-commit-msg` 钩子告诉你如何收集传入脚本的所有参数。接下来,它调用了 `git symbolic-ref --short HEAD` 来获取对应 HEAD 的分支名。如果分支名以 `issue-` 开头,它会重写提交信息文件,在第一行加上 issue 编号。比如你的分支名 `issue-224`,下面的提交信息将会生成:
```
ISSUE-224
```
ISSUE-224
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch issue-224
# Changes to be committed:
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch issue-224
# Changes to be committed:
# modified: test.txt
```
有一点要记住的是即使用户用`-m`传入提交信息,`prepare-commit-msg` 也会运行。也就是说,上面这个脚本会自动插入`ISSUE-[#]`字符串,而用户无法更改。你可以检查第二个参数是否是提交类型来处理这个情况。
有一点要记住的是即使用户用 `-m` 传入提交信息,`prepare-commit-msg` 也会运行。也就是说,上面这个脚本会自动插入 `ISSUE-[#]` 字符串,而用户无法更改。你可以检查第二个参数是否是提交类型来处理这个情况。
但是,如果没有`-m`选项,`prepare-commit-msg`钩子允许用户修改生成后的提交信息。所以脚本的目的是为了方便,而不是推行强制的提交信息规范。如果你要这么做,你需要下一节所讲的`commit-msg`钩子。
但是,如果没有 `-m` 选项,`prepare-commit-msg` 钩子允许用户修改生成后的提交信息。所以脚本的目的是为了方便,而不是推行强制的提交信息规范。如果你要这么做,你需要下一节所讲的 `commit-msg` 钩子。
### commit-msg
`commit-msg`钩子和`prepare-commit-msg`钩子很像,但它会在用户输入提交信息之后被调用。这适合用来提醒开发者他们的提交信息不符合你团队的规范。
`commit-msg` 钩子和 `prepare-commit-msg` 钩子很像,但它会在用户输入提交信息之后被调用。这适合用来提醒开发者他们的提交信息不符合你团队的规范。
传入这个钩子唯一的参数是包含提交信息的文件名。如果它不喜欢用户输入的提交信息,它可以在原地修改这个文件(和`prepare-commit-msg`一样),或者它会以非0状态退出,放弃这个提交。
传入这个钩子唯一的参数是包含提交信息的文件名。如果它不喜欢用户输入的提交信息,它可以在原地修改这个文件(和 `prepare-commit-msg` 一样),或者它会以非 0 状态退出,放弃这个提交。
比如说,下面这个脚本确认用户没有删除`prepare-commit-msg`脚本自动生成的`ISSUE-[#]`字符串。
比如说,下面这个脚本确认用户没有删除 `prepare-commit-msg` 脚本自动生成的 `ISSUE-[#]` 字符串。
```
```
#!/usr/bin/env python
import sys, os, re
@ -244,17 +244,17 @@ if branch.startswith('issue-'):
sys.exit(1)
```
虽然用户每次创建提交时,这个脚本都会运行。但你还是应该避免做检查提交信息之外的事情。如果你需要通知其他服务一个快照已经被提交了,你应该使用`post-commit`这个钩子。
虽然用户每次创建提交时,这个脚本都会运行。但你还是应该避免做检查提交信息之外的事情。如果你需要通知其他服务一个快照已经被提交了,你应该使用 `post-commit` 这个钩子。
### post-commit
`post-commit`钩子在`commit-msg`钩子之后立即被运行 。它无法更改`git commit`的结果,所以这主要用于通知用途。
`post-commit` 钩子在 `commit-msg` 钩子之后立即被运行 。它无法更改 `git commit` 的结果,所以这主要用于通知用途。
这个脚本没有参数,而且退出状态不会影响提交。对于大多数`post-commit`脚本来说,你只是想访问你刚刚创建的提交。你可以用`git rev-parse HEAD`来获得最近一次提交的SHA1哈希字串或者你可以用`git log -l HEAD`获取完整的信息。
这个脚本没有参数,而且退出状态不会影响提交。对于大多数 `post-commit` 脚本来说,你只是想访问你刚刚创建的提交。你可以用 `git rev-parse HEAD` 来获得最近一次提交的SHA1哈希字串或者你可以用 `git log -l HEAD` 获取完整的信息。
比如说,如果你需要每次提交快照时向老板发封邮件(也许对于大多数工作流来说这不是个好的想法),你可以加上下面这个`post-commit`钩子。
比如说,如果你需要每次提交快照时向老板发封邮件(也许对于大多数工作流来说这不是个好的想法),你可以加上下面这个 `post-commit` 钩子。
```
```
#!/usr/bin/env python
import smtplib
@ -285,21 +285,21 @@ session.sendmail(msg['From'], msg['To'], msg.as_string())
session.quit()
```
你虽然可以用`post-commit`来触发本地的持续集成系统,但大多数时候你想用的是`post-receive`这个钩子。它运行在服务端而不是用户的本地机器,它同样在任何开发者推送代码时运行。那里更适合你进行持续集成。
你虽然可以用 `post-commit` 来触发本地的持续集成系统,但大多数时候你想用的是 `post-receive` 这个钩子。它运行在服务端而不是用户的本地机器,它同样在任何开发者推送代码时运行。那里更适合你进行持续集成。
### post-checkout
`post-checkout`钩子和`post-commit`钩子很像,但它在你用`git checkout`查看引用的时候被调用。这是用来清理你的工作目录中可能会令人困惑的生成文件。
`post-checkout` 钩子和 `post-commit` 钩子很像,但它在你用 `git checkout` 查看引用的时候被调用。这是用来清理你的工作目录中可能会令人困惑的生成文件。
这个钩子接受三个参数,它的返回状态不影响`git checkout`命令。
这个钩子接受三个参数,它的返回状态不影响 `git checkout` 命令。
- HEAD前一次提交的引用
- 新的HEAD的引用
- 1或0分别代表是分支checkout还是文件checkout。
- HEAD 前一次提交的引用
- 新的 HEAD 的引用
- 1 或 0分别代表是分支 checkout 还是文件 checkout。
Python程序员经常遇到的问题是切换分支后那些之前生成的`.pyc`文件。解释器有时使用`.pyc`而不是`.py`文件。为了避免歧义,你可以在每次用`post-checkout`切换到新的分支的时候,删除所有`.pyc`文件。
Python 程序员经常遇到的问题是切换分支后那些之前生成的 `.pyc` 文件。解释器有时使用 `.pyc` 而不是 `.py` 文件。为了避免歧义,你可以在每次用 `post-checkout` 切换到新的分支的时候,删除所有 `.pyc` 文件。
```
```
#!/usr/bin/env python
import sys, os, re
@ -322,19 +322,19 @@ for root, dirs, files in os.walk('.'):
os.unlink(os.path.join(root, filename))
```
钩子脚本当前的工作目录总是位于仓库的根目录下,所以`os.walk('.')`调用遍历了仓库中所有文件。接下来,我们检查它的拓展名,如果是`.pyc`就删除它。
钩子脚本当前的工作目录总是位于仓库的根目录下,所以 `os.walk('.')` 调用遍历了仓库中所有文件。接下来,我们检查它的拓展名,如果是 `.pyc` 就删除它。
通过`post-checkout`钩子你还可以根据你切换的分支来来更改工作目录。比如说你可以在代码库外面使用一个插件分支来储存你所有的插件。如果这些插件需要很多二进制文件而其他分支不需要你可以选择只在插件分支上build。
通过 `post-checkout` 钩子,你还可以根据你切换的分支来来更改工作目录。比如说,你可以在代码库外面使用一个插件分支来储存你所有的插件。如果这些插件需要很多二进制文件而其他分支不需要,你可以选择只在插件分支上 build。
### pre-rebase
`pre-rebase`钩子在`git rebase`发生更改之前运行,确保不会有什么糟糕的事情发生。
`pre-rebase` 钩子在 `git rebase` 发生更改之前运行,确保不会有什么糟糕的事情发生。
这个钩子有两个参数frok之前的上游分支将要rebase的下游分支。如果rebase当前分支则第二个参数为空。以非0状态退出会放弃这次rebase。
这个钩子有两个参数frok 之前的上游分支,将要 rebase 的下游分支。如果 rebase 当前分支则第二个参数为空。以非 0 状态退出会放弃这次 rebase。
比如说如果你想彻底禁用rebase操作你可以使用下面的`pre-rebase`脚本:
比如说,如果你想彻底禁用 rebase 操作,你可以使用下面的 `pre-rebase` 脚本:
```
```
#!/bin/sh
# 禁用所有rebase
@ -342,14 +342,14 @@ echo "pre-rebase: Rebasing is dangerous. Don't do it."
exit 1
```
每次运行`git rebase`,你都会看到下面的信息:
每次运行 `git rebase`,你都会看到下面的信息:
```
```
pre-rebase: Rebasing is dangerous. Don't do it.
The pre-rebase hook refused to rebase.
```
内置的`pre-rebase.sample`脚本是一个更复杂的例子。它在何时阻止rebase这方面更加智能。它会检查你当前的分支是否已经合并到了下一个分支中去也就是主分支。如果是的话rebase可能会遇到问题脚本会放弃这次rebase。
内置的 `pre-rebase.sample` 脚本是一个更复杂的例子。它在何时阻止 rebase 这方面更加智能。它会检查你当前的分支是否已经合并到了下一个分支中去也就是主分支。如果是的话rebase 可能会遇到问题,脚本会放弃这次 rebase。
# 服务端钩子
@ -361,25 +361,25 @@ The pre-rebase hook refused to rebase.
- update
- post-receive
这些钩子都允许你对`git push`的不同阶段做出响应。
这些钩子都允许你对 `git push` 的不同阶段做出响应。
服务端钩子的输出会传送到客户端的控制台中,所以给开发者发送信息是很容易的。但你要记住这些脚本在结束完之前都不会返回控制台的控制权,所以你要小心那些长时间运行的操作。
### pre-receive
`pre-receive`钩子在有人用`git push`向仓库推送代码时被执行。它只存在于远端仓库中,而不是原来的仓库中。
`pre-receive` 钩子在有人用 `git push` 向仓库推送代码时被执行。它只存在于远端仓库中,而不是原来的仓库中。
这个钩子在任意引用被更新钱被执行,所以这是强制推行开发规范的好地方。如果你不喜欢推送的那个人(多大仇= =),提交信息的格式,或者提交的更改,你都可以拒绝这次提交。虽然你不能阻止开发者写出糟糕的代码,但你可以用`pre-receive`防止这些代码流入官方的代码库。
这个钩子在任意引用被更新钱被执行,所以这是强制推行开发规范的好地方。如果你不喜欢推送的那个人(多大仇 = =),提交信息的格式,或者提交的更改,你都可以拒绝这次提交。虽然你不能阻止开发者写出糟糕的代码,但你可以用 `pre-receive` 防止这些代码流入官方的代码库。
这个脚本没有参数,但每一个推送上来的引用都会以下面的格式传入脚本的单独一行:
```
```
<old-value> <new-value> <ref-name>
```
你可以看到这个钩子做了非常简单的事,就是读取推送上来的引用并且把它们打印出来。
```
```
#!/usr/bin/env python
import sys
@ -393,30 +393,30 @@ for line in fileinput.input():
# sys.exit(1)
```
这和其它钩子相比略微有些不同,因为信息是通过标准输入而不是命令行传入的。在远端仓库的`.git/hooks`中加上这个脚本推送到master分支你会看到下面这些信息打印出来
这和其它钩子相比略微有些不同,因为信息是通过标准输入而不是命令行传入的。在远端仓库的 `.git/hooks` 中加上这个脚本,推送到 master 分支,你会看到下面这些信息打印出来:
```
```
b6b36c697eb2d24302f89aa22d9170dfe609855b 85baa88c22b52ddd24d71f05db31f4e46d579095 refs/heads/master
```
你可以用SHA1哈希字串或者底层的Git命令来检查将要引入的更改。一些常见的使用包括
你可以用 SHA1 哈希字串,或者底层的 Git 命令,来检查将要引入的更改。一些常见的使用包括:
- 拒绝将上游分支rebase的更改
- 拒绝将上游分支 rebase 的更改
- 防止错综复杂的合并(非快速向前,会造成项目历史非线性)
- 检查用户是否有正确的权限来做这些更改大多用于中心化的Git工作流中
- 如果多个引用被推送,在`pre-receive`中返回非0状态,拒绝所有提交。如果你想一个个接受或拒绝分支,你需要使用`update`钩子
- 检查用户是否有正确的权限来做这些更改(大多用于中心化的 Git 工作流中)
- 如果多个引用被推送,在 `pre-receive` 中返回非 0 状态,拒绝所有提交。如果你想一个个接受或拒绝分支,你需要使用 `update` 钩子
### update
`update`钩子在`pre-receive`之后被调用用法也差不多。它也是在实际更新前被调用的但它可以分别被每个推送上来的引用分别调用。也就是说如果用户尝试推送到4个分支`update`会被执行4次。和`pre-receive`不一样,这个钩子不需要读取标准输入。事实上,它接受三个参数:
`update` 钩子在 `pre-receive` 之后被调用用法也差不多。它也是在实际更新前被调用的但它可以分别被每个推送上来的引用分别调用。也就是说如果用户尝试推送到4个分支`update` 会被执行 4 次。和 `pre-receive` 不一样,这个钩子不需要读取标准输入。事实上,它接受三个参数:
- 更新的引用名称
- 引用中存放的旧的对象名称
- 引用中存放的新的对象名称
这些信息和`pre-receive`相同,但因为每次引用都会分别触发更新,你可以拒绝一些引用而接受另一些。
这些信息和 `pre-receive` 相同,但因为每次引用都会分别触发更新,你可以拒绝一些引用而接受另一些。
```
```
#!/usr/bin/env python
import sys
@ -435,20 +435,18 @@ print "Moving '%s' from %s to %s" % (branch, old_commit, new_commit)
### post-receive
`post-receive`钩子在成功推送后被调用,适合用于发送通知。对很多工作流来说,这是一个比`post-commit`更好的发送通知的地方,因为这些更改在公共的服务器而不是用户的本地机器上。给其他开发者发送邮件或者触发一个持续集成系统都是`post-receive`常用的操作。
`post-receive` 钩子在成功推送后被调用,适合用于发送通知。对很多工作流来说,这是一个比 `post-commit` 更好的发送通知的地方,因为这些更改在公共的服务器而不是用户的本地机器上。给其他开发者发送邮件或者触发一个持续集成系统都是 `post-receive` 常用的操作。
这个脚本没有参数,但和`pre-receive`一样通过标准输入读取。
这个脚本没有参数,但和 `pre-receive` 一样通过标准输入读取。
## 总结
在这篇文章中我们学习了如果用Git钩子来修改内部行为当仓库中特定的事件发生时接受消息。钩子是存在于`git/hooks`仓库中的普通脚本,因此也非常容易安装和定制。
在这篇文章中,我们学习了如果用 Git 钩子来修改内部行为,当仓库中特定的事件发生时接受消息。钩子是存在于 `git/hooks` 仓库中的普通脚本,因此也非常容易安装和定制。
我们还看了一些常用的本地和服务端的钩子。这使得我们能够介入到整个开发生命周期中去。我们现在知道了如何在创建提交或推送的每个阶段执行自定义的操作。有了这些简单的脚本知识你就可以对Git仓库为所欲为了 : )
我们还看了一些常用的本地和服务端的钩子。这使得我们能够介入到整个开发生命周期中去。我们现在知道了如何在创建提交或推送的每个阶段执行自定义的操作。有了这些简单的脚本知识,你就可以对 Git 仓库为所欲为了 : )
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)

View File

@ -1,201 +1,201 @@
# 代码合并Merge、Rebase的选择
# 代码合并Merge、Rebase 的选择
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/merging-vs-rebasing)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/merging-vs-rebasing)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
`git rebase` 这个命令经常被人认为是一种Git巫术初学者应该避而远之。但如果使用得当的话它能给你的团队开发省去太多烦恼。在这篇文章中我们会比较`git rebase`和类似的`git merge`命令找到Git工作流中rebase的所有用法。
`git rebase` 这个命令经常被人认为是一种 Git 巫术,初学者应该避而远之。但如果使用得当的话,它能给你的团队开发省去太多烦恼。在这篇文章中,我们会比较 `git rebase` 和类似的 `git merge` 命令,找到 Git 工作流中 rebase 的所有用法。
## 概述
你要知道的第一件事是,`git rebase``git merge` 做的事其实是一样的。它们都被设计来将一个分支的更改并入另一个分支,只不过方式有些不同。
想象一下你刚创建了一个专门的分支开发新功能然后团队中另一个成员在master分支上添加了新的提交。这就会造成提交历史被Fork一份用Git来协作的开发者应该都很清楚。
想象一下,你刚创建了一个专门的分支开发新功能,然后团队中另一个成员在 master 分支上添加了新的提交。这就会造成提交历史被 fork 一份,用 Git 来协作的开发者应该都很清楚。
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/01.svg)
现在如果master中新的提交和你的工作是相关的。为了将新的提交并入你的分支你有两个选择mergerebase。
现在,如果 master 中新的提交和你的工作是相关的。为了将新的提交并入你的分支你有两个选择mergerebase。
### Merge
将master分支合并到feature分支最简单的办法就是用下面这些命令
master 分支合并到 feature 分支最简单的办法就是用下面这些命令:
```
```
git checkout feature
git merge master
```
或者,你也可以把它们压缩在一行里。
```
```
git merge master feature
```
feature分支中新的合并提交(merge commit)将两个分支的历史连在了一起。你会得到下面这样的分支结构:
feature 分支中新的合并提交merge commit将两个分支的历史连在了一起。你会得到下面这样的分支结构:
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/02.svg)
Merge好在它是一个安全的操作。现有的分支不会被更改避免了rebase潜在的缺点后面会说
Merge 好在它是一个安全的操作。现有的分支不会被更改,避免了 rebase 潜在的缺点(后面会说)。
另一方面这同样意味着每次合并上游更改时feature分支都会引入一个外来的合并提交。如果master非常活跃的话这或多或少会污染你的分支历史。虽然高级的`git log` 选项可以减轻这个问题,但对于开发者来说,还是会增加理解项目历史的难度。
另一方面,这同样意味着每次合并上游更改时 feature 分支都会引入一个外来的合并提交。如果 master 非常活跃的话,这或多或少会污染你的分支历史。虽然高级的 `git log` 选项可以减轻这个问题,但对于开发者来说,还是会增加理解项目历史的难度。
### Rebase
作为merge的替代选择你可以像下面这样将feature分支并入master分支
作为 merge 的替代选择,你可以像下面这样将 feature 分支并入 master 分支:
```
```
git checkout feature
git rebase master
```
它会把整个feature分支移动到master分支的后面有效地把所有master分支上新的提交并入过来。但是rebase为原分支上每一个提交创建一个新的提交重写了项目历史并且不会带来合并提交。
它会把整个 feature 分支移动到 master 分支的后面,有效地把所有 master 分支上新的提交并入过来。但是rebase 为原分支上每一个提交创建一个新的提交,重写了项目历史,并且不会带来合并提交。
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/03.svg)
rebase最大的好处是你的项目历史会非常整洁。首先它不像`git merge` 那样引入不必要的合并提交。其次如上图所示rebase导致最后的项目历史呈现出完美的线性——你可以从项目终点到起点浏览而不需要任何的Fork。这让你更容易使用`git log` 、`git bisect` 和`gitk` 来查看项目历史。
rebase最大的好处是你的项目历史会非常整洁。首先它不像 `git merge` 那样引入不必要的合并提交。其次如上图所示rebase 导致最后的项目历史呈现出完美的线性——你可以从项目终点到起点浏览而不需要任何的 fork。这让你更容易使用 `git log``git bisect``gitk` 来查看项目历史。
不过,这种简单的提交历史会带来两个后果:安全性和可跟踪性。如果你违反了Rebase黄金法则重写项目历史可能会给你的协作工作流带来灾难性的影响。此外rebase不会有合并提交中附带的信息——你看不到feature分支中并入了上游的哪些更改。
不过,这种简单的提交历史会带来两个后果:安全性和可跟踪性。如果你违反了 rebase 黄金法则重写项目历史可能会给你的协作工作流带来灾难性的影响。此外rebase 不会有合并提交中附带的信息——你看不到 feature 分支中并入了上游的哪些更改。
### 交互式的rebase
### 交互式的 rebase
交互式的rebase允许你更改并入新分支的提交。这比自动的rebase更加强大因为它提供了对分支上提交历史完整的控制。一般来说这被用于将feature分支并入master分支之前清理混乱的历史。
交互式的 rebase 允许你更改并入新分支的提交。这比自动的 rebase 更加强大,因为它提供了对分支上提交历史完整的控制。一般来说,这被用于将 feature 分支并入 master 分支之前,清理混乱的历史。
把`-i` 传入`git rebase` 选项来开始一个交互式的rebase过程
`-i` 传入 `git rebase` 选项来开始一个交互式的rebase过程
```
```
git checkout feature
git rebase -i master
```
它会打开一个文本编辑器,显示所有将被移动的提交:
```
```
pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
```
这个列表定义了rebase将被执行后分支会是什么样的。更改`pick` 命令或者重新排序,这个分支的历史就能如你所愿了。比如说,如果第二个提交修复了第一个提交中的小问题,你可以用`fixup` 命令把它们合到一个提交中:
这个列表定义了 rebase 将被执行后分支会是什么样的。更改 `pick` 命令或者重新排序,这个分支的历史就能如你所愿了。比如说,如果第二个提交修复了第一个提交中的小问题,你可以用 `fixup` 命令把它们合到一个提交中:
```
```
pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3
```
保存后关闭文件Git会根据你的指令来执行rebase项目历史看上去会是这样
保存后关闭文件Git 会根据你的指令来执行 rebase项目历史看上去会是这样
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/04.svg)
忽略不重要的提交会让你的feature分支的历史更清晰易读。这是`git merge` 做不到的。
忽略不重要的提交会让你的 feature 分支的历史更清晰易读。这是 `git merge` 做不到的。
## Rebase的黄金法则
## Rebase 的黄金法则
当你理解rebase是什么的时候最重要的就是什么时候 *不能* 用rebase。`git rebase` 的黄金法则便是,绝不要在公共的分支上使用它。
当你理解 rebase 是什么的时候,最重要的就是什么时候 *不能* rebase。`git rebase` 的黄金法则便是,绝不要在公共的分支上使用它。
比如说如果你把master分支rebase到你的feature分支上会发生什么
比如说,如果你把 master 分支 rebase 到你的 feature 分支上会发生什么:
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/05.svg)
这次rebasemaster分支上的所有提交都移到了feature分支后面。问题是它只发生在你的代码仓库中其他所有的开发者还在原来的master上工作。因为rebase引起了新的提交Git会认为你的master分支和其他人的master已经分叉了。
这次 rebasemaster 分支上的所有提交都移到了 feature 分支后面。问题是它只发生在你的代码仓库中,其他所有的开发者还在原来的 master 上工作。因为 rebase 引起了新的提交Git 会认为你的 master 分支和其他人的 master 已经分叉了。
同步两个master分支的唯一办法是把它们merge到一起导致一个额外的合并提交和两堆包含同样更改的提交。不用说这会让人非常困惑。
同步两个 master 分支的唯一办法是把它们 merge 到一起,导致一个额外的合并提交和两堆包含同样更改的提交。不用说,这会让人非常困惑。
所以,在你运行`git rebase` 之前,一定要问问你自己有没有别人正在这个分支上工作?。如果答案是肯定的,那么把你的爪子放回去,重新找到一个无害的方式(如`git revert`)来提交你的更改。不然的话,你可以随心所欲地重写历史。
所以,在你运行 `git rebase` 之前,一定要问问你自己有没有别人正在这个分支上工作?。如果答案是肯定的,那么把你的爪子放回去,重新找到一个无害的方式(如 `git revert`)来提交你的更改。不然的话,你可以随心所欲地重写历史。
### 强制推送
如果你想把rebase之后的master分支推送到远程仓库Git会阻止你这么做因为两个分支包含冲突。但你可以传入`--force` 标记来强行推送。就像下面一样:
如果你想把 rebase 之后的 master 分支推送到远程仓库Git 会阻止你这么做,因为两个分支包含冲突。但你可以传入 `--force` 标记来强行推送。就像下面一样:
```
```
# 小心使用这个命令!
git push --force
```
它会重写远程的master分支来匹配你仓库中rebase之后的master分支对于团队中其他成员来说这看上去很诡异。所以务必小心这个命令只有当你知道你在做什么的时候再使用。
它会重写远程的 master 分支来匹配你仓库中 rebase 之后的 master 分支,对于团队中其他成员来说这看上去很诡异。所以,务必小心这个命令,只有当你知道你在做什么的时候再使用。
仅有的几个强制推送的使用场景之一是,当你在想向远程仓库推送了一个私有分支之后,执行了一个本地的清理(比如说为了回滚)。这就像是在说其实我并不想推送之前那个feature分支的。用我现在的版本替换掉吧。同样你要注意没有别人正在这个feature分支上工作。
仅有的几个强制推送的使用场景之一是,当你在想向远程仓库推送了一个私有分支之后,执行了一个本地的清理(比如说为了回滚)。这就像是在说哦,其实我并不想推送之前那个 feature 分支的。用我现在的版本替换掉吧。同样,你要注意没有别人正在这个 feature 分支上工作。
## 工作流
rebase可以或多或少应用在你们团队的Git工作流中。在这一节中我们来看看在feature分支开发的各个阶段中rebase有哪些好处。
rebase 可以或多或少应用在你们团队的 Git 工作流中。在这一节中,我们来看看在 feature 分支开发的各个阶段中rebase 有哪些好处。
第一步是在任何和`git rebase` 有关的工作流中为每一个feature专门创建一个分支。它会给你带来安全使用rebase的分支结构
第一步是在任何和 `git rebase` 有关的工作流中为每一个 feature 专门创建一个分支。它会给你带来安全使用 rebase 的分支结构:
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/06.svg)
### 本地清理
在你工作流中使用rebase最好的用法之一就是清理本地正在开发的分支。隔一段时间执行一次交互式rebase你可以保证你feature分支中的每一个提交都是专注和有意义的。你在写代码时不用担心造成孤立的提交——因为你后面一定能修复。
在你工作流中使用 rebase 最好的用法之一就是清理本地正在开发的分支。隔一段时间执行一次交互式 rebase你可以保证你 feature 分支中的每一个提交都是专注和有意义的。你在写代码时不用担心造成孤立的提交——因为你后面一定能修复。
调用`git rebase` 的时候,你有两个基(base)可以选择上游分支比如master或者你feature分支中早先的一个提交。我们在交互式rebase一节看到了第一种的例子。后一种在当你只需要修改最新几次提交时也很有用。比如说,下面的命令对最新的3次提交进行了交互式rebase
调用 `git rebase` 的时候,你有两个基base可以选择:上游分支(比如 master或者你 feature 分支中早先的一个提交。我们在交互式 rebase一节看到了第一种的例子。后一种在当你只需要修改最新几次提交时也很有用。比如说,下面的命令对最新的 3 次提交进行了交互式 rebase
```
```
git checkout feature
git rebase -i HEAD~3
```
通过指定`HEAD~3`作为新的基提交,你实际上没有移动分支——你只是将之后的3次提交重写了。注意它不会把上游分支的更改并入到feature分支中。
通过指定 `HEAD~3` 作为新的基提交,你实际上没有移动分支——你只是将之后的 3 次提交重写了。注意它不会把上游分支的更改并入到 feature 分支中。
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/07.svg)
如果你想用这个方法重写整个feature分支`git merge-base` 命令非常方便地找出feature分支开始分叉的基。下面这段命令返回基提交的ID你可以接下来将它传给`git rebase`
如果你想用这个方法重写整个 feature 分支,`git merge-base` 命令非常方便地找出 feature 分支开始分叉的基。下面这段命令返回基提交的 ID你可以接下来将它传给 `git rebase`
```
```
git merge-base feature master
```
交互式rebase是在你工作流中引入`git rebase` 的的好办法,因为它只影响本地分支。其他开发者只能看到你已经完成的结果,那就是一个非常整洁、易于追踪的分支历史。
交互式 rebase 是在你工作流中引入 `git rebase` 的的好办法,因为它只影响本地分支。其他开发者只能看到你已经完成的结果,那就是一个非常整洁、易于追踪的分支历史。
但同样的这只能用在私有分支上。如果你在同一个feature分支和其他开发者合作的话这个分支是公开的你不能重写这个历史。
但同样的,这只能用在私有分支上。如果你在同一个 feature 分支和其他开发者合作的话,这个分支是公开的,你不能重写这个历史。
用带有交互式的rebase清理本地提交这是无法用`git merge` 命令代替的。
用带有交互式的 rebase 清理本地提交,这是无法用 `git merge` 命令代替的。
### 将上游分支上的更改并入feature分支
在概览一节我们看到了feature分支如何通过`git merge` 或`git rebase` 来并入上游分支。merge是保留你完整历史的安全选择rebase将你的feature分支移动到master分支后面创建一个线性的历史。
在概览一节,我们看到了 feature 分支如何通过 `git merge``git rebase` 来并入上游分支。merge 是保留你完整历史的安全选择rebase 将你的 feature 分支移动到 master 分支后面,创建一个线性的历史。
`git rebase` 的用法和本地清理非常类似而且可以同时使用但之间并入了master上的上游更改。
`git rebase` 的用法和本地清理非常类似(而且可以同时使用),但之间并入了 master 上的上游更改。
记住rebase到远程分支而不是master也是完全合法的。当你和另一个开发者在同一个feature分之上协作的时候你会用到这个用法将他们的更改并入你的项目。
记住rebase 到远程分支而不是 master 也是完全合法的。当你和另一个开发者在同一个 feature 分之上协作的时候,你会用到这个用法,将他们的更改并入你的项目。
比如说,如果你和另一个开发者——John——往feature分支上添加了几个提交在从John的仓库中fetch之后你的仓库可能会像下面这样
比如说,如果你和另一个开发者 Johnfeature 分支上添加了几个提交,在从 John 的仓库中 fetch 之后,你的仓库可能会像下面这样:
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/08.svg)
就和并入master上的上游更改一样你可以这样解决这个Fork要么merge你的本地分支和John的分支把你的本地分支rebaseJohn的分支后面。
就和并入 master 上的上游更改一样,你可以这样解决这个 fork要么 merge 你的本地分支和 John 的分支,要把你的本地分支 rebaseJohn 的分支后面。
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/09.svg)
注意这里的rebase没有违反Rebase黄金法则因为只有你的本地分支上的commit被移动了之前的所有东西都没有变。这就像是在说把我的改动加到John的后面去。在大多数情况下,这比通过合并提交来同步远程分支更符合直觉。
注意,这里的 rebase 没有违反 rebase 黄金法则,因为只有你的本地分支上的 commit 被移动了,之前的所有东西都没有变。这就像是在说把我的改动加到 John 的后面去。在大多数情况下,这比通过合并提交来同步远程分支更符合直觉。
默认情况下,`git pull` 命令会执行一次merge但你可以传入`--rebase` 来强制它通过rebase来整合远程分支。
### 用Pull Request进行审查
### 用 Pull Request 进行审查
如果你将pull request作为你代码审查过程中的一环你需要避免在创建pull request之后使用`git rebase`。只要你发起了pull request其他开发者能看到你的代码也就是说这个分支变成了公共分支。重写历史会造成Git和你的同事难以找到这个分支接下来的任何提交。
如果你将 Pull Request 作为你代码审查过程中的一环,你需要避免在创建 Pull Request 之后使用 `git rebase`。只要你发起了 Pull Request其他开发者能看到你的代码也就是说这个分支变成了公共分支。重写历史会造成 Git 和你的同事难以找到这个分支接下来的任何提交。
来自其他开发者的任何更改都应该用`git merge` 而不是`git rebase` 来并入。
来自其他开发者的任何更改都应该用 `git merge` 而不是 `git rebase` 来并入。
因此,在提交pull request前用交互式的rebase进行代码清理通常是一个好的做法。
因此,在提交 Pull Request前用交互式的 rebase 进行代码清理通常是一个好的做法。
### 并入通过的功能分支
如果某个功能被你们团队通过了你可以选择将这个分支rebasemaster分支之后或是使用`git merge` 来将这个功能并入主代码库中。
如果某个功能被你们团队通过了,你可以选择将这个分支 rebasemaster 分支之后,或是使用 `git merge` 来将这个功能并入主代码库中。
这和将上游改动并入feature分支很相似但是你不可以在master分支重写提交你最后需要用`git merge` 来并入这个feature。但是在merge之前执行一次rebase你可以确保merge是一直向前的最后生成的是一个完全线性的提交历史。这样你还可以加入pull request之后的提交。
这和将上游改动并入 feature 分支很相似,但是你不可以在 master 分支重写提交,你最后需要用 `git merge` 来并入这个 feature。但是 merge 之前执行一次 rebase你可以确保 merge 是一直向前的,最后生成的是一个完全线性的提交历史。这样你还可以加入 Pull Request 之后的提交。
![enter image description here](https://www.atlassian.com/git/images/tutorials/advanced/merging-vs-rebasing/10.svg)
如果你还没有完全熟悉`git rebase`你还可以在一个临时分支中执行rebase。这样的话如果你意外地弄乱了你feature分支的历史你还可以查看原来的分支然后重试。
如果你还没有完全熟悉 `git rebase`,你还可以在一个临时分支中执行 rebase。这样的话如果你意外地弄乱了你 feature 分支的历史,你还可以查看原来的分支然后重试。
比如说:
```
```
git checkout feature
git checkout -b temporary-branch
git rebase -i master
@ -206,14 +206,12 @@ git merge temporary-branch
## 总结
你使用rebase之前需要知道的知识点都在这了。如果你想要一个干净的、线性的提交历史没有不必要的合并提交你应该使用`git rebase` 而不是`git merge` 来并入其他分支上的更改。
你使用 rebase 之前需要知道的知识点都在这了。如果你想要一个干净的、线性的提交历史,没有不必要的合并提交,你应该使用 `git rebase` 而不是 `git merge` 来并入其他分支上的更改。
另一方面如果你想要保存项目完整的历史并且避免重写公共分支上的commit 你可以使用`git merge`。两种选项都很好用,但至少你现在多了`git rebase`这个选择。
另一方面,如果你想要保存项目完整的历史,并且避免重写公共分支上的 commit 你可以使用 `git merge`。两种选项都很好用,但至少你现在多了 `git rebase` 这个选择。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)

View File

@ -1,115 +1,103 @@
# 使用分支
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/using-branches)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/using-branches)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
这份教程是Git分支的综合介绍。首先我们简单讲解如何创建分支就像请求一份新的项目历史一样。然后我们会看到git checkout是如何切换分支的。最后学习一下git merge是如何整合独立分支的历史。
这份教程是 Git 分支的综合介绍。首先,我们简单讲解如何创建分支,就像请求一份新的项目历史一样。然后,我们会看到 git checkout 是如何切换分支的。最后,学习一下 git merge 是如何整合独立分支的历史。
我们已经知道Git分支和SVN分支不同。SVN分支只被用来记录偶尔大规模的开发效果而Git分支是你日常工作流中不可缺失的一部分。
我们已经知道Git 分支和 SVN 分支不同。SVN 分支只被用来记录偶尔大规模的开发效果,而 Git 分支是你日常工作流中不可缺失的一部分。
## git branch
分支代表了一条独立的开发流水线。分支是我们在第二篇中讨论过的编辑/缓存/提交流程的抽象。你可以把它看作请求全新工作目录、缓存区、项目历史的一种方式。新的提交被存放在当前分支的历史中导致了项目历史被fork了一份。
分支代表了一条独立的开发流水线。分支是我们在第二篇中讨论过的编辑/缓存/提交流程的抽象。你可以把它看作请求全新工作目录、缓存区、项目历史的一种方式。新的提交被存放在当前分支的历史中,导致了项目历史被 fork 了一份。
`git branch`命令允许你创建、列出、重命名和删除分支。它不允许你切换分支或是将被fork的历史放回去。因此`git branch``git checkout``git merge`这两个命令通常紧密地结合在一起使用。
`git branch` 命令允许你创建、列出、重命名和删除分支。它不允许你切换分支或是将被 fork 的历史放回去。因此,`git branch``git checkout``git merge` 这两个命令通常紧密地结合在一起使用。
### 用法
```
```
git branch
```
列出仓库中所有分支。
```
```
git branch <branch>
```
创建一个名为`<branch>`的分支。*不会* 自动切换到那个分支去。
创建一个名为 `<branch>` 的分支。*不会* 自动切换到那个分支去。
```
```
git branch -d <branch>
```
删除指定分支。这是一个安全的操作Git会阻止你删除包含未合并更改的分支。
删除指定分支。这是一个安全的操作Git 会阻止你删除包含未合并更改的分支。
```
```
git branch -D <branch>
```
强制删除指定分支,即使包含未合并更改。如果你希望永远删除某条开发线的所有提交,你应该用这个命令。
```
```
git branch -m <branch>
```
将当前分支命名为`<branch>`
将当前分支命名为 `<branch>`
### 讨论
在Git中分支是你日常开发流程中的一部分。当你想要添加一个新的功能或是修复一个bug时——不管bug是大是小——你都应该新建一个分支来封装你的修改。这确保了不稳定的代码永远不会被提交到主代码库中它同时给了你机会在并入主分支前清理你feature分支的历史。
Git 中,分支是你日常开发流程中的一部分。当你想要添加一个新的功能或是修复一个 bug 时——不管 bug 是大是小——你都应该新建一个分支来封装你的修改。这确保了不稳定的代码永远不会被提交到主代码库中,它同时给了你机会,在并入主分支前清理你 feature 分支的历史。
![Git Tutorial: git branch](https://www.atlassian.com/git/images/tutorials/collaborating/using-branches/01.svg)
比如,上图将一个拥有两条独立开发线的仓库可视化,其中一条是一个不起眼的功能,另一条是长期运行的功能。使用分支开发时,不仅可以同时在两条线上工作,还可以保持主要的`master branch`混入奇怪的代码。
比如,上图将一个拥有两条独立开发线的仓库可视化,其中一条是一个不起眼的功能,另一条是长期运行的功能。使用分支开发时,不仅可以同时在两条线上工作,还可以保持主要的 `master branch` 混入奇怪的代码。
#### 分支的顶端
Git分支背后的实现远比SVN的模型要轻量。与其在目录之间复制文件Git将分支存为指向提交的引用。换句话说分支代表了一系列提交的 *顶端* ——而不是提交的 *容器* 。分支历史通过提交之间的关系来推断。
Git 分支背后的实现远比 SVN 的模型要轻量。与其在目录之间复制文件Git 将分支存为指向提交的引用。换句话说,分支代表了一系列提交的 *顶端* ——而不是提交的 *容器* 。分支历史通过提交之间的关系来推断。
这使得Git的合并模型变成了动态的。SVN中的合并是基于文件的而Git让你在更抽象的提交层面操作。事实上你可以看到项目历史中的合并其实是将两个独立的提交历史连接起来。
这使得 Git 的合并模型变成了动态的。SVN 中的合并是基于文件的而Git 让你在更抽象的提交层面操作。事实上,你可以看到项目历史中的合并其实是将两个独立的提交历史连接起来。
### 栗子
#### 创建分支
分支只是指向提交的 *指针* 理解这一点很重要。当你创建一个分支是Git只需要创建一个新的指针——仓库不会受到任何影响。因此如果你最开始有这样一个仓库
分支只是指向提交的 *指针* 理解这一点很重要。当你创建一个分支是Git 只需要创建一个新的指针——仓库不会受到任何影响。因此,如果你最开始有这样一个仓库:
![Git Tutorial: repository without any branches](https://www.atlassian.com/git/images/tutorials/collaborating/using-branches/02.svg)
接下来你用下面的命令创建了一个分支:
```
```
git branch crazy-experiment
```
仓库历史保持不变。你得到的是一个指向当前提交的新的指针:
![Git Tutorial: Create new branch](https://www.atlassian.com/git/images/tutorials/collaborating/using-branches/03.svg)
注意,这只会 *创建* 一个新的分支。要开始在上面添加提交,你需要用`git checkout`来选中这个分支,然后使用标准的`git addh`和`git commit`命令。
注意,这只会 *创建* 一个新的分支。要开始在上面添加提交,你需要用 `git checkout` 来选中这个分支,然后使用标准的 `git addh``git commit` 命令。
#### 删除分支
一旦你完成了分支上的工作,准备将它并入主代码库,你可以自由地删除这个分支,而不丢失项目历史:
```
```
git branch -d crazy-experiment
```
然后,如果分支还没有合并,下面的命令会产生一个错误信息:
```
```
error: The branch 'crazy-experiment' is not fully merged.
If you are sure you want to delete it, run 'git branch -D crazy-experiment'.
```
Git保护你不会丢失这些提交的引用或者说丢失访问整条开发线的入口。如果你 *真的* 想要删除这个分支(比如说这是一个失败的实验),你可以用大写的`-D`标记:
Git 保护你不会丢失这些提交的引用,或者说丢失访问整条开发线的入口。如果你 *真的* 想要删除这个分支(比如说这是一个失败的实验),你可以用大写的 `-D` 标记:
```
```
git branch -D crazy-experiment
```
@ -117,85 +105,73 @@ git branch -D crazy-experiment
## git checkout
`git checkout`命令允许你切换用`git branch`创建的分支。查看一个分支会更新工作目录中的文件以符合分支中的版本它还告诉Git记录那个分支上的新提交。将它看作一个选中你正在进行的开发的一种方式。
`git checkout` 命令允许你切换用 `git branch` 创建的分支。查看一个分支会更新工作目录中的文件,以符合分支中的版本,它还告诉 Git 记录那个分支上的新提交。将它看作一个选中你正在进行的开发的一种方式。
在上一篇中,我们看到了如何用`git checkout`来查看旧的提交。查看分支”和“将工作目录更新到选中的版本/修改很类似;但是,新的更改 *会* 保存在项目历史中——这不是一个只读的操作。
在上一篇中,我们看到了如何用 `git checkout` 来查看旧的提交。查看分支」和「将工作目录更新到选中的版本/修改很类似;但是,新的更改 *会* 保存在项目历史中——这不是一个只读的操作。
### 用法
```
```
git checkout <existing-branch>
```
查看特定分支,分支应该已经通过`git branch`创建。这使得<existing-branch>成为当前的分支,并更新工作目录的版本。
查看特定分支,分支应该已经通过 `git branch` 创建。这使得 `<existing-branch>` 成为当前的分支,并更新工作目录的版本。
```
```
git checkout -b <new-branch>
```
创建并查看<new-branch>`-b`选项是一个方便的标记告诉Git在运行`git checkout <new-branch>`之前运行`git branch <new-branch>`
创建并查看 `<new-branch>``-b` 选项是一个方便的标记告诉Git在运行 `git checkout <new-branch>` 之前运行 `git branch <new-branch>`
```
```
git checkout -b <new-branch> <existing-branch>
```
和上一条相同,但将<existing-branch>作为新分支的基,而不是当前分支。
和上一条相同,但将 `<existing-branch>` 作为新分支的基,而不是当前分支。
### 讨论
`git checkout`和`git branch`是一对好基友。当你想要创建一个新功能时,你用`git branch`创建分支,然后用`git checkout`查看。你可以在一个仓库中用`git checkout`切换分支,同时开发几个功能。
`git checkout``git branch` 是一对好基友。当你想要创建一个新功能时,你用 `git branch` 创建分支,然后用 `git checkout` 查看。你可以在一个仓库中用 `git checkout` 切换分支,同时开发几个功能。
![Git Tutorial: Switch between multiple features in a single repo with git checkout.](https://www.atlassian.com/git/images/tutorials/collaborating/using-branches/04.svg)
每个功能专门一个分支对于传统 SVN 工作流来说是一个巨大的转变。这使得尝试新的实验超乎想象的简单,不用担心毁坏已有的功能,并且可以同时开发几个不相关的功能。另外,分支可以促进了不同的协作工作流。
#### 分离的 `HEAD`
每个功能专门一个分支对于传统SVN工作流来说是一个巨大的转变。这使得尝试新的实验超乎想象的简单不用担心毁坏已有的功能并且可以同时开发几个不相关的功能。另外分支可以促进了不同的协作工作流
#### 分离的`HEAD`
现在我们已经看到了`git checkout`最主要的三种用法,我们可以讨论上一篇中提到的“分离`HEAD`”状态了。
记住,`HEAD`是Git指向当前快照的引用。`git checkout`命令内部只是更新`HEAD`指向特定分支或提交。当它指向分支时Git不会报错但当你check out提交时它会进入“分离`HEAD`”状态。
现在我们已经看到了 `git checkout` 最主要的三种用法,我们可以讨论上一篇中提到的「分离 `HEAD`」状态了
记住,`HEAD` 是 Git 指向当前快照的引用。`git checkout` 命令内部只是更新 `HEAD`指向特定分支或提交。当它指向分支时Git 不会报错,但当你 check out 提交时,它会进入「分离 `HEAD`」状态。
![Git Tutorial: Attached vs Detached Head](https://www.atlassian.com/git/images/tutorials/collaborating/using-branches/05.svg)
有个警告会告诉你所做的更改和项目的其余历史处于“分离”的状态。如果你在分离`HEAD`状态开始开发新功能没有分支可以让你回到之前的状态。当你不可避免地checkout到了另一个分支比如你的更改并入了这个分支你将不再能够引用你的feature分支
有个警告会告诉你所做的更改和项目的其余历史处于「分离」的状态。如果你在分离 `HEAD` 状态开始开发新功能,没有分支可以让你回到之前的状态。当你不可避免地 checkout 到了另一个分支(比如你的更改并入了这个分支),你将不再能够引用你的 feature 分支:
![Git Tutorial: Detached Head state](https://www.atlassian.com/git/images/tutorials/collaborating/using-branches/06.svg)
重点是,你应该永远在分支上开发——而绝不在分离的 `HEAD` 上。这样确保你一直可以引用到你的新提交。不过,如果你只是想查看旧的提交,那么是否处于分离 `HEAD` 状态并不重要。
### 例子
重点是,你应该永远在分支上开发——而绝不在分离的`HEAD`上。这样确保你一直可以引用到你的新提交。不过,如果你只是想查看旧的提交,那么是否处于分离`HEAD`状态并不重要。
下面的例子演示了基本的 Git 分支流程。当你想要开发新功能时,你创建一个专门的分支,切换过去:
### 栗子
下面的栗子演示了基本的Git分支流程。当你想要开发新功能时你创建一个专门的分支切换过去
```
```
git branch new-feature
git checkout new-feature
```
接下来,你可以和以往一样提交新的快照:
```
```
# 编辑文件
git add <file>
git commit -m "Started work on a new feature"
# 周而复始…
```
这些操作都被记录在`new-feature`上,和`master`完全独立。你想添加多少提交就可以添加多少,不用关心你其它分支的修改。当你想要回到“主”代码库时只要check out到`master`分支即可:
这些操作都被记录在 `new-feature` 上,和 `master` 完全独立。你想添加多少提交就可以添加多少,不用关心你其它分支的修改。当你想要回到「主」代码库时,只要 check out`master` 分支即可:
```
```
git checkout master
```
@ -203,19 +179,19 @@ git checkout master
## git merge
合并是Git将被fork的历史放回到一起的方式。`git merge`命令允许你将`git branch`创建的多条分支合并成一个。
合并是 Git 将被 fork 的历史放回到一起的方式。`git merge` 命令允许你将 `git branch` 创建的多条分支合并成一个。
注意,下面所有命令将更改 *并入* 当前分支。当前分支会被更新,以响应合并操作,但目标分支完全不受影响。也就是说`git merge`经常和`git checkout`一起使用,选择当前分支,然后用`git branch -d`删除废弃的目标分支。
注意,下面所有命令将更改 *并入* 当前分支。当前分支会被更新,以响应合并操作,但目标分支完全不受影响。也就是说 `git merge` 经常和 `git checkout` 一起使用,选择当前分支,然后用 `git branch -d` 删除废弃的目标分支。
### 用法
```
```
git merge <branch>
```
将指定分支并入当前分支。Git会决定使用哪种合并算法下文讨论
将指定分支并入当前分支。Git 会决定使用哪种合并算法(下文讨论)。
```
```
git merge --no-ff <branch>
```
@ -223,33 +199,25 @@ git merge --no-ff <branch>
### 讨论
一旦你在单独的分支上完成了功能的开发重要的是将它放回主代码库。取决于你的仓库结构Git有几种不同的算法来完成合并快速向前合并或者三路合并。
当当前分支顶端到目标分支路径是线性之时,我们可以采取 **快速向前合并** 。Git只需要将当前分支顶端快速向前地移动到目标分支顶端即可整合两个分支的历史而不需要“真正”合并分支。它在效果上合并了历史因为目标分支上的提交现在在当前分支可以访问到。比如`some-feature`到`master`分支的快速向前合并会是这样的:
一旦你在单独的分支上完成了功能的开发重要的是将它放回主代码库。取决于你的仓库结构Git 有几种不同的算法来完成合并:快速向前合并或者三路合并。
当当前分支顶端到目标分支路径是线性之时,我们可以采取 **快速向前合并** 。Git 只需要将当前分支顶端(快速向前地)移动到目标分支顶端,即可整合两个分支的历史,而不需要“真正”合并分支。它在效果上合并了历史,因为目标分支上的提交现在在当前分支可以访问到。比如,`some-feature``master` 分支的快速向前合并会是这样的:
![Git Tutorial: Fast-forward merge](https://www.atlassian.com/git/images/tutorials/collaborating/using-branches/07.svg)
但是如果分支已经分叉了那么就无法进行快速向前合并。当和目标分支之间的路径不是线性之时Git只能执行 **三路合并** 。三路合并使用一个专门的提交来合并两个分支的历史。这个术语取自这样一个事实Git使用 *三个* 提交来生成合并提交:两个分支顶端和它们共同的祖先。
但是如果分支已经分叉了那么就无法进行快速向前合并。当和目标分支之间的路径不是线性之时Git 只能执行 **三路合并** 。三路合并使用一个专门的提交来合并两个分支的历史。这个术语取自这样一个事实Git 使用 *三个* 提交来生成合并提交:两个分支顶端和它们共同的祖先。
![Git Tutorial: Three way merge](https://www.atlassian.com/git/images/tutorials/collaborating/using-branches/08.svg)
但你可以选择使用哪一种合并策略时很多开发者喜欢使用快速向前合并搭配rebase使用来合并微小的功能或者修复bug使用三路合并来整合长期运行的功能。后者导致的合并提交作为两个分支的连接标志。
但你可以选择使用哪一种合并策略时,很多开发者喜欢使用快速向前合并(搭配 rebase 使用)来合并微小的功能或者修复 bug使用三路合并来整合长期运行的功能。后者导致的合并提交作为两个分支的连接标志。
#### 解决冲突
如果你尝试合并的两个分支同一个文件的同一个部分Git将无法决定使用哪个版本。当这种情况发生时它会停在合并提交让你手动解决这些冲突。
如果你尝试合并的两个分支同一个文件的同一个部分Git 将无法决定使用哪个版本。当这种情况发生时,它会停在合并提交,让你手动解决这些冲突。
Git的合并流程令人称赞的一点是它使用我们熟悉的编辑/缓存/提交工作流来解决冲突。当你遇到合并冲突时,运行`git status`命令来查看哪些文件存在需要解决的冲突。比如,如果两个分支都修改了`hello.py`的同一处,你会看到下面的信息:
Git 的合并流程令人称赞的一点是,它使用我们熟悉的编辑/缓存/提交工作流来解决冲突。当你遇到合并冲突时,运行 `git status` 命令来查看哪些文件存在需要解决的冲突。比如,如果两个分支都修改了 `hello.py` 的同一处,你会看到下面的信息:
```
```
# On branch master
# Unmerged paths:
# (use "git add/rm ..." as appropriate to mark resolution)
@ -258,17 +226,17 @@ Git的合并流程令人称赞的一点是它使用我们熟悉的“编辑/
#
```
接下来,你可以自己修复这个合并。当你准备结束合并时,你只需对冲突的文件运行`git add`告诉Git冲突已解决。然后运行`git commit`生成一个合并提交。这和提交一个普通的快照有着完全相同的流程,也就是说,开发者能够轻而易举地管理他们的合并。
接下来,你可以自己修复这个合并。当你准备结束合并时,你只需对冲突的文件运行 `git add` 告诉 Git 冲突已解决。然后,运行 `git commit` 生成一个合并提交。这和提交一个普通的快照有着完全相同的流程,也就是说,开发者能够轻而易举地管理他们的合并。
注意,提交冲突只会出现在三路合并中。在快速向前合并中,我们不可能出现冲突的更改。
###
###
#### 快速向前合并
我们第一个🌰演示了快速向前合并。下面的代码创建了一个分支,在后面添加了两个提交,然后使用快速向前合并将它并入主分支。
我们第一个例子演示了快速向前合并。下面的代码创建了一个分支,在后面添加了两个提交,然后使用快速向前合并将它并入主分支。
```
```
# 开始新功能
git checkout -b new-feature master
@ -288,13 +256,13 @@ git branch -d new-feature
对于临时存在、用作独立开发环境而不是组织长期运行功能的工具的分支来说,这是一种常见的工作流。
同时注意,运行`git branch -d`时Git不应该产生错误提示因为`new-feature`现在可以在主分支上访问了。
同时注意,运行 `git branch -d`Git 不应该产生错误提示,因为 `new-feature` 现在可以在主分支上访问了。
#### 三路合并
下一个子很相似,但需要进行三路合并,因为`master`在这个功能开发时取得了新进展。这是复杂功能和多个开发者同时工作时常见的情形。
下一个子很相似,但需要进行三路合并,因为 `master` 在这个功能开发时取得了新进展。这是复杂功能和多个开发者同时工作时常见的情形。
```
```
# 开始新功能
git checkout -b new-feature master
@ -318,14 +286,12 @@ git merge new-feature
git branch -d new-feature
```
注意Git现在无法进行快速向前合并因为无法将`master`直接移动到`new-feature`。
注意Git 现在无法进行快速向前合并,因为无法将 `master` 直接移动到 `new-feature`
对大多数工作流来说,`new-feature`会是一个需要一段时间来开发的复杂功能,这也是为什么同时`master`会有新的提交出现。如果你的分支上的功能像上面的一样简单你会更想将它rebase到`master`,使用快速向前合并。它会通过整理项目历史来避免多余的合并提交。
对大多数工作流来说,`new-feature` 会是一个需要一段时间来开发的复杂功能,这也是为什么同时 `master` 会有新的提交出现。如果你的分支上的功能像上面的一样简单,你会更想将它 rebase`master`,使用快速向前合并。它会通过整理项目历史来避免多余的合并提交。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)

View File

@ -1,131 +1,123 @@
# 保存你的更改
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/saving-changes)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/saving-changes)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
## git add
`git add`命令将工作目录中的变化添加到缓存区。它告诉Git你想要在下一次提交时包含这个文件的更新。但是`git add`不会怎么影响你的仓库——在你运行`git commit`前更改都不会被记录。
`git add` 命令将工作目录中的变化添加到缓存区。它告诉Git你想要在下一次提交时包含这个文件的更新。但是`git add` 不会怎么影响你的仓库——在你运行 `git commit` 前更改都不会被记录。
使用这些命令之时,你还需要`git status`来查看工作目录和缓存区的状态。
使用这些命令之时,你还需要 `git status` 来查看工作目录和缓存区的状态。
### 用法
```
```
git add <file>
```
将`<file>`中的更改加入下次提交的缓存。
`<file>` 中的更改加入下次提交的缓存。
```
```
git add <directory>
```
将`<directory>`下的更改加入下次提交的缓存。
`<directory>` 下的更改加入下次提交的缓存。
```
```
git add -p
```
开始交互式的缓存,你可以选择文件的一部分加入到下次提交缓存。它会向你展示一堆更改,等待你输入一个命令。`y`将这块更改加入缓存,`n`忽略这块更改,`s`将它分割成更小的块,`e`手动编辑这块更改,以及`q`退出。
开始交互式的缓存,你可以选择文件的一部分加入到下次提交缓存。它会向你展示一堆更改,等待你输入一个命令。`y` 将这块更改加入缓存,`n` 忽略这块更改,`s` 将它分割成更小的块,`e` 手动编辑这块更改,以及 `q` 退出。
### 讨论
`git add`和`git commit`这两个命令组成了最基本的Git工作流。每一个Git用户都需要理解这两个命令不管他们团队的协作模型是如何的。我有一千种方式可以将项目版本记录在仓库的历史中。
在一个只有编辑、缓存、提交这样基本流程的项目上开发。首先,你要在工作目录中编辑你的文件。当你准备备份项目的当前状态时,你通过`git add`来缓存更改。当你对缓存的快照满意之后,你通过`git commit`将它提交到你的项目历史中去。
`git add``git commit` 这两个命令组成了最基本的Git工作流。每一个Git用户都需要理解这两个命令不管他们团队的协作模型是如何的。我有一千种方式可以将项目版本记录在仓库的历史中。
在一个只有编辑、缓存、提交这样基本流程的项目上开发。首先,你要在工作目录中编辑你的文件。当你准备备份项目的当前状态时,你通过 `git add` 来缓存更改。当你对缓存的快照满意之后,你通过 `git commit` 将它提交到你的项目历史中去。
![Git Tutorial: git add Snapshot](https://www.atlassian.com/git/images/tutorials/getting-started/saving-changes/01.svg)
`git add`命令不能和 `svn add`混在一起理解,后者将文件添加到仓库中。而`git add` 发生于更抽象的 *更改* 层面。也就是说,`git add`在每次你修改一个文件时都需要被调用,而`svn add`只需要每个文件调用一次。这听上去很多余,但这样的工作流使得一个项目更容易组织。
`git add` 命令不能和 `svn add` 混在一起理解,后者将文件添加到仓库中。而 `git add` 发生于更抽象的 *更改* 层面。也就是说,`git add` 在每次你修改一个文件时都需要被调用,而 `svn add` 只需要每个文件调用一次。这听上去很多余,但这样的工作流使得一个项目更容易组织。
#### 缓存区
缓存区是Git更为独特的地方之一如果你是从SVN甚至是Mercurial迁移而来那你可得花点时间理解了。你可以简单地把它想成是工作目录和项目历史之间的缓冲区。
缓存区是 Git 更为独特的地方之一,如果你是从 SVN甚至是 Mercurial迁移而来那你可得花点时间理解了。你可以简单地把它想成是工作目录和项目历史之间的缓冲区。
缓存允许你在实际提交到项目历史之前将相关的更改组合成一份高度专注的快照而不是将你上次提交以后产生的所有更改一并提交。也就是说你可以更改各种不相关的文件然后回过去将它们按逻辑切分将相关的更改添加到缓存一份一份提交。在任何修改控制系统中很重要的一点是提交必须是原子性的以便于追踪bug并用最小的代价回滚更改。
缓存允许你在实际提交到项目历史之前,将相关的更改组合成一份高度专注的快照,而不是将你上次提交以后产生的所有更改一并提交。也就是说你可以更改各种不相关的文件,然后回过去将它们按逻辑切分,将相关的更改添加到缓存,一份一份提交。在任何修改控制系统中,很重要的一点是提交必须是原子性的,以便于追踪 bug并用最小的代价回滚更改。
### 栗子
当你开始新项目的时候,`git add`和`svn import`类似。为了创建当前目录的初始提交,使用下面两个命令:
当你开始新项目的时候,`git add``svn import` 类似。为了创建当前目录的初始提交,使用下面两个命令:
```
```
git add .
git commit
```
当你项目设置好之后,新的文件可以通过路径传递给`git add`来添加:
当你项目设置好之后,新的文件可以通过路径传递给 `git add` 来添加:
```
```
git add hello.py
git commit
```
上面的命令同样可以用于记录已有文件的更改。重复一次Git不会区分缓存的更改来自新文件还是仓库中已有的文件。
上面的命令同样可以用于记录已有文件的更改。重复一次Git 不会区分缓存的更改来自新文件,还是仓库中已有的文件。
## git commit
`git commit`命令将缓存的快照提交到项目历史。提交的快照可以认为是项目安全的版本——Git永远不会改变它们除非你这么要求。和`git add`一样这是最重要的Git命令之一。
`git commit`命令将缓存的快照提交到项目历史。提交的快照可以认为是项目安全的版本Git 永远不会改变它们,除非你这么要求。和 `git add` 一样,这是最重要的 Git 命令之一。
尽管和它和`svn commit`名字一样但实际上它们毫无关联。快照被提交到本地仓库不会和其他Git仓库有任何交互。
尽管和它和 `svn commit` 名字一样,但实际上它们毫无关联。快照被提交到本地仓库,不会和其他 Git 仓库有任何交互。
### 用法
```
```
git commit
```
提交已经缓存的快照。它会运行文本编辑器,等待你输入提交信息。当你输入信息之后,保存文件,关闭编辑器,创建实际的提交。
```
```
git commit -m "<message>"
```
提交已经缓存的快照。但将`<message>`作为提交信息,而不是运行文本编辑器。
提交已经缓存的快照。但将 `<message>` 作为提交信息,而不是运行文本编辑器。
```
```
git commit -a
```
提交一份包含工作目录所有更改的快照。它只包含跟踪过的文件的更改(那些之前已经通过`git add`添加过的文件)。
提交一份包含工作目录所有更改的快照。它只包含跟踪过的文件的更改(那些之前已经通过 `git add` 添加过的文件)。
### 讨论
快照总是提交到 *本地* 仓库。这一点和SVN截然不同后者的工作拷贝提交到中央仓库。而Git不会强制你和中央仓库进行交互直到你准备好了。就像缓存区是工作目录和项目历史之间的缓冲地带每个开发者的本地仓库是他们贡献的代码和中央仓库之间的缓冲地带。
快照总是提交到 *本地* 仓库。这一点和 SVN 截然不同,后者的工作拷贝提交到中央仓库。而 Git 不会强制你和中央仓库进行交互,直到你准备好了。就像缓存区是工作目录和项目历史之间的缓冲地带,每个开发者的本地仓库是他们贡献的代码和中央仓库之间的缓冲地带。
这一点改变了Git用户基本的开发模型。Git开发者可以在本地仓库中积累一些提交而不是一发生更改就直接提交到中央仓库。这对于SVN风格的协作有着诸多优点更容易将功能切分成原子性的提交让相关的提交组合在一起发布到中央仓库之前整理好本地的历史。开发者得以在一个隔离的环境中工作直到他们方便的时候再整合代码。
这一点改变了 Git 用户基本的开发模型。Git 开发者可以在本地仓库中积累一些提交,而不是一发生更改就直接提交到中央仓库。这对于 SVN 风格的协作有着诸多优点:更容易将功能切分成原子性的提交,让相关的提交组合在一起,发布到中央仓库之前整理好本地的历史。开发者得以在一个隔离的环境中工作,直到他们方便的时候再整合代码。
#### 记录快照,而不是记录差异
SVNGit除了使用上存在巨大差异它们底层的实现同样遵循截然不同的设计哲学。SVN追踪文件的 *变化* 而Git的版本控制模型基于 *快照* 。比如说一个SVN提交由仓库中原文件相比的差异(diff)组成。而Git在每次提交中记录文件的 *完整内容* 。
SVNGit 除了使用上存在巨大差异它们底层的实现同样遵循截然不同的设计哲学。SVN 追踪文件的 *变化* ,而 Git 的版本控制模型基于 *快照* 。比如说,一个 SVN 提交由仓库中原文件相比的差异diff组成。而 Git 在每次提交中记录文件的 *完整内容*
![Git Tutorial: Snapshots, Not Differences](https://www.atlassian.com/git/images/tutorials/getting-started/saving-changes/02.svg)
这让很多 Git 操作比 SVN 来的快得多,因为文件的某个版本不需要通过版本间的差异组装得到——每个文件完整的修改能立刻从 Git 的内部数据库中得到。
这让很多Git操作比SVN来的快得多因为文件的某个版本不需要通过版本间的差异组装得到——每个文件完整的修改能立刻从Git 的内部数据库中得到。
Git的快照模型对它版本控制模型的方方面面都有着深远的影响从分支到合并工具再到协作工作流以至于影响了所有特性。
Git 的快照模型对它版本控制模型的方方面面都有着深远的影响,从分支到合并工具,再到协作工作流,以至于影响了所有特性。
### 栗子
下面这个栗子假设你编辑了`hello.py`文件的一些内容,并且准备好将它提交到项目历史。首先,你需要用`git add`缓存文件,然后提交缓存的快照。
下面这个栗子假设你编辑了 `hello.py` 文件的一些内容,并且准备好将它提交到项目历史。首先,你需要用 `git add` 缓存文件,然后提交缓存的快照。
```
```
git add hello.py
git commit
```
它会打开一个文件编辑器(可以通过`git config`设置) 询问提交信息,同时列出将被提交的文件。
它会打开一个文件编辑器(可以通过 `git config` 设置) 询问提交信息,同时列出将被提交的文件。
```
```
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
@ -135,9 +127,9 @@ git commit
#modified: hello.py
```
Git对提交信息没有特定的格式限制但约定俗成的格式是在第一行用50个以内的字符总结这个提交留一空行然后详细阐述具体的更改。比如
Git 对提交信息没有特定的格式限制,但约定俗成的格式是:在第一行用 50 个以内的字符总结这个提交,留一空行,然后详细阐述具体的更改。比如:
```
```
Change the message displayed by hello.py
- Update the sayHello() function to output the user's name
@ -146,11 +138,8 @@ Change the message displayed by hello.py
注意,很多开发者倾向于在提交信息中使用一般现在时态。这样看起来更像是对仓库进行的操作,让很多改写历史的操作更加符合直觉。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:。
>
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,86 +1,82 @@
# 保持同步
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/syncing)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/syncing)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
SVN使用唯一的中央仓库作为开发者之间沟通的桥梁在开发者的工作拷贝和中央仓库之间传递变更集合(changeset)协作得以发生。这和Git的协作模型有所不同Git给予每个开发者一份自己的仓库拷贝拥有自己完整的本地历史和分支结构。用户通常共享一系列的提交而不是单个变更集合。Git允许你在仓库间共享整个分支而不是从工作副本提交一个差异集合到中央仓库。
SVN 使用唯一的中央仓库作为开发者之间沟通的桥梁,在开发者的工作拷贝和中央仓库之间传递变更集合changeset协作得以发生。这和Git的协作模型有所不同Git 给予每个开发者一份自己的仓库拷贝拥有自己完整的本地历史和分支结构。用户通常共享一系列的提交而不是单个变更集合。Git 允许你在仓库间共享整个分支,而不是从工作副本提交一个差异集合到中央仓库。
下面的命令让你管理仓库之间的连接,将分支推送到其他仓库来发布本地历史,或是将分支拉取到本地仓库来查看其它开发者的贡献。
下面的命令让你管理仓库之间的连接,将分支推送到其他仓库来发布本地历史,或是将分支拉取到本地仓库来查看其它开发者的贡献。
## git remote
`git remote`命令允许你创建、查看和删除和其它仓库之间的连接。远程连接更像是书签而不是直接跳转到其他仓库的链接。它用方便记住的别名引用不那么方便记住的URL而不是提供其他仓库的实时连接。
例如下图显示了你的仓库和中央仓库以及另一个开发者仓库之间的远程连接。你可以向Git命令传递origin和john的别名来引用这些仓库替代完整的URL。
`git remote` 命令允许你创建、查看和删除和其它仓库之间的连接。远程连接更像是书签,而不是直接跳转到其他仓库的链接。它用方便记住的别名引用不那么方便记住的 URL而不是提供其他仓库的实时连接。
例如,下图显示了你的仓库和中央仓库以及另一个开发者仓库之间的远程连接。你可以向 Git 命令传递 origin 和 john 的别名来引用这些仓库,替代完整的 URL。
![Git Tutorial: git remote](https://www.atlassian.com/git/images/tutorials/collaborating/syncing/01.svg)
### 用法
```
```
git remote
```
列出你和其他仓库之间的远程连接。
```
```
git remote -v
```
和上个命令相同但同时显示每个连接的URL。
和上个命令相同,但同时显示每个连接的 URL。
```
```
git remote add <name> <url>
```
创建一个新的远程仓库连接。在添加之后,你可以将<name>作为<url>便捷的别名在其他Git命令中使用。
创建一个新的远程仓库连接。在添加之后,你可以将 `<name>` 作为 `<url>` 便捷的别名在其他 Git 命令中使用。
```
```
git remote rm <name>
```
移除名为<name>的远程仓库的连接。
```
```
git remote rename <old-name> <new-name>
```
将远程连接从<old-name>重命名为<new-name>。
将远程连接从 `<old-name>` 重命名为 `<new-name>`
### 讨论
Git被设计为给每个开发者提供完全隔离的开发环境。也就是说信息并不是自动地在仓库之间传递。开发者需要手动将上游提交拉取到本地或手动将本地提交推送到中央仓库中去。`git remote`命令正是将URL传递给这些共享命令的快捷方式。
Git 被设计为给每个开发者提供完全隔离的开发环境。也就是说信息并不是自动地在仓库之间传递。开发者需要手动将上游提交拉取到本地,或手动将本地提交推送到中央仓库中去。`git remote` 命令正是将 URL 传递给这些共享命令的快捷方式。
#### 名为origin的远程连接
#### 名为 origin 的远程连接
当你用`git clone`克隆仓库时它自动创建了一个名为origin的远程连接指向被克隆的仓库。当开发者创建中央仓库的本地副本时非常有用因为它提供了拉取上游更改和发布本地提交的快捷方式。这也是为什么大多数基于Git的项目将它们的中央仓库取名为origin。
当你用 `git clone` 克隆仓库时,它自动创建了一个名为 origin 的远程连接,指向被克隆的仓库。当开发者创建中央仓库的本地副本时非常有用,因为它提供了拉取上游更改和发布本地提交的快捷方式。这也是为什么大多数基于 Git 的项目将它们的中央仓库取名为 origin。
#### 仓库的URL
#### 仓库的 URL
Git支持多种方式来引用一个远程仓库。其中两种最简单的方式便是HTTPSSH协议。HTTP是允许匿名、只读访问仓库的简易方式。比如
Git 支持多种方式来引用一个远程仓库。其中两种最简单的方式便是 HTTPSSH 协议。HTTP 是允许匿名、只读访问仓库的简易方式。比如:
```
```
http://host/path/to/repo.git
```
但是直接将提交推送到一个HTTP地址一般是不可行的你不太可能希望匿名用户也能随意推送。如果希望对仓库进行读写你需要使用SSH协议
但是,直接将提交推送到一个 HTTP 地址一般是不可行的(你不太可能希望匿名用户也能随意推送)。如果希望对仓库进行读写,你需要使用 SSH 协议:
```
```
ssh://user@host/path/to/repo.git
```
你需要在托管的服务器上有一个有效的SSH账户但不用麻烦了Git支持开箱即用的SSH认证连接。
你需要在托管的服务器上有一个有效的 SSH 账户但不用麻烦了Git 支持开箱即用的 SSH 认证连接。
### 栗子
除了origin之外添加你同事的仓库连接通常会带来一些便利。比如如果你的同事John在`dev.example.com/john.git`上维护了一个公开的仓库,你可以这样添加连接:
除了 origin 之外,添加你同事的仓库连接通常会带来一些便利。比如,如果你的同事 John`dev.example.com/john.git` 上维护了一个公开的仓库,你可以这样添加连接:
```
```
git remote add john http://dev.example.com/john.git
```
@ -88,17 +84,17 @@ git remote add john http://dev.example.com/john.git
## git fetch
`git fetch`命令将提交从远程仓库导入到你的本地仓库。拉取下来的提交储存为远程分支,而不是我们一直使用的普通的本地分支。你因此可以在整合进你的项目副本之前查看更改。
`git fetch` 命令将提交从远程仓库导入到你的本地仓库。拉取下来的提交储存为远程分支,而不是我们一直使用的普通的本地分支。你因此可以在整合进你的项目副本之前查看更改。
### 用法
```
```
git fetch <remote>
```
拉取仓库中所有的分支。同时会从另一个仓库中下载所有需要的提交和文件。
```
```
git fetch <remote> <branch>
```
@ -106,38 +102,38 @@ git fetch <remote> <branch>
### 讨论
当你希望查看其他人的工作进展时你需要fetch。fetch下来的内容表示为一个远程分支因此不会影响你的本地开发。这是一个安全的方式在整合进你的本地仓库之前检查那些提交。类似于svn update你可以看到中央仓库的历史进展如何但它不会强制你将这些进展合并入你的仓库。
当你希望查看其他人的工作进展时,你需要 fetch。fetch 下来的内容表示为一个远程分支,因此不会影响你的本地开发。这是一个安全的方式,在整合进你的本地仓库之前,检查那些提交。类似于 svn update你可以看到中央仓库的历史进展如何但它不会强制你将这些进展合并入你的仓库。
#### 远程分支
远程分支和本地分支一样只不过它们代表这些提交来自于其他人的仓库。你可以查看像查看本地分支一样查看远程分支但你会处于分离HEAD状态就像查看旧的提交时一样。你可以把它们视作只读的分支。如果想要查看远程分支只需要向`git branch`命令传入`-r`参数。远程分支拥有remote的前缀所以你不会将它们和本地分支混起来。比如下面的代码片段显示了从origin拉取之后你可能想要查看的分支
远程分支和本地分支一样,只不过它们代表这些提交来自于其他人的仓库。你可以查看像查看本地分支一样查看远程分支,但你会处于分离 HEAD 状态(就像查看旧的提交时一样)。你可以把它们视作只读的分支。如果想要查看远程分支,只需要向 `git branch` 命令传入 `-r` 参数。远程分支拥有 remote 的前缀,所以你不会将它们和本地分支混起来。比如,下面的代码片段显示了从 origin 拉取之后,你可能想要查看的分支:
```
```
git branch -r
# origin/master
# origin/develop
# origin/some-feature
```
同样,你可以用寻常的`git checkout`和 `git log`命令来查看这些分支。如果你接受远程分支包含的更改,你可以使用`git merge`将它并入本地分支。所以不像SVN同步你的本地仓库和远程仓库事实上是一个分两步的操作先fetch然后merge。`git pull`命令是这个过程的快捷方式。
同样,你可以用寻常的 `git checkout` 和 `git log` 命令来查看这些分支。如果你接受远程分支包含的更改,你可以使用 `git merge` 将它并入本地分支。所以,不像 SVN同步你的本地仓库和远程仓库事实上是一个分两步的操作 fetch然后 merge。`git pull` 命令是这个过程的快捷方式。
### 栗子
这个例子回顾了同步本地和远程仓库`master`分支的常见工作流:
这个例子回顾了同步本地和远程仓库 `master` 分支的常见工作流:
```
```
git fetch origin
```
它会显示会被下载的分支:
```
```
a1e8fb5..45e66a4 master -> origin/master
a1e8fb5..9e8ab1c develop -> origin/develop
* [new branch] some-feature -> origin/some-feature
```
在下图中,远程分支中的提交显示为方块,而不是圆圈。正如你所见,`git fetch`让你看到了另一个仓库完整的分支结构。
在下图中,远程分支中的提交显示为方块,而不是圆圈。正如你所见,`git fetch` 让你看到了另一个仓库完整的分支结构。
@ -145,75 +141,71 @@ a1e8fb5..9e8ab1c develop -> origin/develop
若想查看添加到上游master上的提交你可以运行`git log`,用 `origin/master`过滤:
若想查看添加到上游 master 上的提交,你可以运行 `git log`,用 `origin/master` 过滤:
```
```
git log --oneline master..origin/master
```
用下面这些命令接受更改并并入你的本地`master`分支:
用下面这些命令接受更改并并入你的本地 `master` 分支:
```
```
git checkout master
git log origin/master
```
我们可以使用`git merge origin/master`
我们可以使用 `git merge origin/master`
```
```
git merge origin/master
```
origin/mastermaster分支现在指向了同一个提交你已经和上游的更新保持了同步。
origin/mastermaster 分支现在指向了同一个提交,你已经和上游的更新保持了同步。
## git pull
在基于Git的协作工作流中将上游更改合并到你的本地仓库是一个常见的工作。我们已经知道应该使用`git fetch`,然后是`git merge`,但是`git pull`将这两个命令合二为一。
在基于 Git 的协作工作流中,将上游更改合并到你的本地仓库是一个常见的工作。我们已经知道应该使用 `git fetch`,然后是 `git merge`,但是 `git pull` 将这两个命令合二为一。
### 用法
```
```
git pull <remote>
```
拉取当前分支对应的远程副本中的更改,并立即并入本地副本。效果和`git fetch `后接`git merge origin/.`一致。
拉取当前分支对应的远程副本中的更改,并立即并入本地副本。效果和 `git fetch` 后接 `git merge origin/.` 一致。
```
```
git pull --rebase <remote>
```
和上一个命令相同,但使用`git rebase`合并远程分支和本地分支,而不是使用`git merge`。
和上一个命令相同,但使用 `git rebase` 合并远程分支和本地分支,而不是使用 `git merge`
### 讨论
你可以将`git pull`当做Git中对应`svn update`的命令。这是同步你本地仓库和上游更改的简单方式。下图结束了pull过程中的每一步。
你可以将 `git pull` 当做 Git 中对应 `svn update` 的命令。这是同步你本地仓库和上游更改的简单方式。下图结束了 pull 过程中的每一步。
![Git Tutorial: git pull](https://www.atlassian.com/git/images/tutorials/collaborating/syncing/03.svg)
你认为你的仓库已经同步了,但 `git fetch` 发现 origin 中 `master` 的版本在上次检查后已经有了新进展。 接着 `git merge` 立即将 `remote master` 并入本地的分支。
#### 基于 Rebase 的 Pull
你认为你的仓库已经同步了,但`git fetch`发现origin中`master`的版本在上次检查后已经有了新进展。 接着`git merge`立即将`remote master`并入本地的分支
`--rebase` 标记可以用来保证线性的项目历史防止合并提交merge commits的产生。很多开发者倾向于使用 rebase 而不是 merge因为「我想要把我的更改放在其他人完成的工作之后」。这种情况下使用带有 `--rebase` 标记的 `git pull` 甚至更像 svn update与普通的 `git pull` 相比而言
#### 基于Rebase的Pull
事实上,使用 `--rebase` 的 pull 的工作流是如此普遍,以致于你可以直接在配置项中设置它:
`--rebase`标记可以用来保证线性的项目历史,防止合并提交(merge commits)的产生。很多开发者倾向于使用rebase而不是merge因为“我想要把我的更改放在其他人完成的工作之后”。这种情况下使用带有`--rebase`标记的`git pull`甚至更像svn update与普通的`git pull`相比而言。
事实上,使用`--rebase`的pull的工作流是如此普遍以致于你可以直接在配置项中设置它
```
```
git config --global branch.autosetuprebase always # In git < 1.7.9
git config --global pull.rebase true # In git >= 1.7.9
```
在运行这个命令之后,所有的`git pull`命令将使用`git rebase,`而不是`git merge`。
在运行这个命令之后,所有的 `git pull` 命令将使用 `git rebase` 而不是 `git merge`
### 栗子
下面的栗子演示了如何和一个中央仓库的`master branch`同步:
下面的栗子演示了如何和一个中央仓库的 `master branch` 同步:
```
```
git checkout master
git pull --rebase origin
```
@ -222,37 +214,37 @@ git pull --rebase origin
## git push
Push是你将本地仓库中的提交转移到远程仓库中时要做的事。它和`git fetch`正好相反fetch将提交导入到本地分支而push将提交导出到远程分支。它可以覆盖已有的更改所以你需要小心使用。这些情况请见下面的讨论。
Push 是你将本地仓库中的提交转移到远程仓库中时要做的事。它和 `git fetch` 正好相反fetch 将提交导入到本地分支,而 push 将提交导出到远程分支。它可以覆盖已有的更改,所以你需要小心使用。这些情况请见下面的讨论。
### 用法
```
```
git push <remote> <branch>
```
将指定的分支推送到<remote>上包括所有需要的提交和提交对象。它会在目标仓库中创建一个本地分支。为了防止你覆盖已有的提交如果会导致目标仓库非快速向前合并时Git不允许你push。
将指定的分支推送到 `<remote>` 包括所有需要的提交和提交对象。它会在目标仓库中创建一个本地分支。为了防止你覆盖已有的提交如果会导致目标仓库非快速向前合并时Git 不允许你 push。
```
```
git push <remote> --force
```
和上一个命令相同,但即使会导致非快速向前合并也强制推送。除非你确定你所做的事,否则不要使用`--force`标记。
和上一个命令相同,但即使会导致非快速向前合并也强制推送。除非你确定你所做的事,否则不要使用 `--force` 标记。
```
```
git push <remote> --all
```
将所有本地分支推送到指定的远程仓库。
```
```
git push <remote> --tags
```
当你推送一个分支或是使用`--all`选项时,标签不会被自动推送上去。`--tags`将你所有的本地标签推送到远程仓库中去。
当你推送一个分支或是使用 `--all` 选项时,标签不会被自动推送上去。`--tags` 将你所有的本地标签推送到远程仓库中去。
### 讨论
`git push`最常见的用法是将你的本地更改发布到中央仓库。在你积累了一些本地提交准备和同事们共享时可以用交互式rebase来清理你的提交然后推送到中央仓库去。
`git push` 最常见的用法是将你的本地更改发布到中央仓库。在你积累了一些本地提交,准备和同事们共享时,你(可以)用交互式 rebase 来清理你的提交,然后推送到中央仓库去。
@ -260,23 +252,23 @@ git push <remote> --tags
上图显示了当你本地的master分支进展超过了中央仓库的`master`分支,当你运行`git push origin master`发布更改时发生的事情。注意,`git push`和在远程仓库内部运行`git merge master`事实上是一样的。
上图显示了当你本地的 master 分支进展超过了中央仓库的 `master` 分支,当你运行 `git push origin master` 发布更改时发生的事情。注意,`git push` 和在远程仓库内部运行 `git merge master` 事实上是一样的。
#### 强制推送
Git为了防止你覆盖中央仓库的历史会拒绝你会导致非快速向前合并的推送请求。所以如果远程历史和你本地历史已经分叉你需要将远程分支pull下来在本地合并后再尝试推送。这和SVN让你在提交更改集合之前要和中央仓库同步是类似的。
Git 为了防止你覆盖中央仓库的历史,会拒绝你会导致非快速向前合并的推送请求。所以,如果远程历史和你本地历史已经分叉,你需要将远程分支 pull 下来,在本地合并后再尝试推送。这和 SVN 让你在提交更改集合之前要和中央仓库同步是类似的。
`--force`这个标记覆盖了这个行为让远程仓库的分支符合你的本地分支删除你上次pull之后可能的上游更改。只有当你意识到你刚刚共享的提交不正确并用`git commit --amend`或者交互式rebase修复之后你才需要用到强制推送。但是你必须绝对确定在你使用`--force`标记前你的同事们都没有pull这些提交。
`--force` 这个标记覆盖了这个行为,让远程仓库的分支符合你的本地分支,删除你上次 pull 之后可能的上游更改。只有当你意识到你刚刚共享的提交不正确,并用 `git commit --amend` 或者交互式 rebase 修复之后,你才需要用到强制推送。但是,你必须绝对确定在你使用 `--force` 标记前你的同事们都没有 pull 这些提交。
#### 只推送到裸仓库
此外,你只应该推送到那些用`--bare`标记初始化的仓库。因为推送会弄乱远程分支结构,很重要的一点是,永远不要推送到其他开发者的仓库。但因为裸仓库没有工作目录,不会发生打断别人的开发之类的事情。
此外,你只应该推送到那些用 `--bare` 标记初始化的仓库。因为推送会弄乱远程分支结构,很重要的一点是,永远不要推送到其他开发者的仓库。但因为裸仓库没有工作目录,不会发生打断别人的开发之类的事情。
### 栗子
下面的栗子描述了将本地提交推送到中央仓库的一些标准做法。首先,确保你本地的`master`和中央仓库的副本是一致的提前fetch中央仓库的副本并在上面rebase。交互式rebase同样是共享之前清理提交的好机会。接下来`git push`命令将你本地`master`分支上的所有提交发送给中央仓库.
下面的栗子描述了将本地提交推送到中央仓库的一些标准做法。首先,确保你本地的 `master` 和中央仓库的副本是一致的,提前 fetch 中央仓库的副本并在上面 rebase。交互式 rebase 同样是共享之前清理提交的好机会。接下来,`git push` 命令将你本地 `master` 分支上的所有提交发送给中央仓库.
```
```
git checkout master
git fetch origin master
git rebase -i origin/master
@ -284,12 +276,10 @@ git rebase -i origin/master
git push origin master
```
因为我们已经确信本地的`master`分支是最新的,它应该导致快速向前的合并,`git push`不应该抛出非快速向前之类的问题。
因为我们已经确信本地的 `master` 分支是最新的,它应该导致快速向前的合并,`git push` 不应该抛出非快速向前之类的问题。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:。
>
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,189 +1,183 @@
# 创建Pull Request
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/making-a-pull-request)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
>
> 原文以Bitbucket为例考虑到[git-recipes](https://github.com/geeeeeeeeek/git-recipes/)主要面向Github用户因此栗子替换成了Github。Pull RequestGitLab等平台上也有用法和本教程基本一致。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/making-a-pull-request)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
>
> 原文以 Bitbucket 为例,考虑到[git-recipes](https://github.com/geeeeeeeeek/git-recipes/)主要面向 GitHub 用户,因此栗子替换成了 GitHub。Pull RequestGitLab 等平台上也有,用法和本教程基本一致。
Pull request是开发者使用Github进行协作的利器。这个功能为用户提供了友好的页面让提议的更改在并入官方项目之前可以得到充分的讨论。
Pull Request 是开发者使用 GitHub 进行协作的利器。这个功能为用户提供了友好的页面,让提议的更改在并入官方项目之前,可以得到充分的讨论。
![qq20160127-0](https://cloud.githubusercontent.com/assets/7262715/12608331/8ece897e-c516-11e5-91c0-f7a44434478f.png)
最简单地来说,pull request是一种机制让开发者告诉项目成员一个功能已经完成。一旦feature分支开发完毕开发者使用Github账号提交一个pull request。它告诉所有参与者他们需要审查代码并将代码并入`master`分支。
最简单地来说,Pull Request 是一种机制,让开发者告诉项目成员一个功能已经完成。一旦 feature 分支开发完毕,开发者使用 GitHub 账号提交一个 Pull Request。它告诉所有参与者他们需要审查代码并将代码并入 `master` 分支。
但是,pull request不只是一个通知还是一个专注于某个提议功能的讨论版。 如果更改导致了任何问题,团队成员可以在pull request下发布反馈甚至推送后续提交来修改这个pull request。所有的活动都在这个pull request里之间追踪。
但是,Pull Request 不只是一个通知,还是一个专注于某个提议功能的讨论版。 如果更改导致了任何问题,团队成员可以在 Pull Request 下发布反馈,甚至推送后续提交来修改这个 Pull Request。所有的活动都在这个 Pull Request里之间追踪。
![Git Workflows: Activity inside a pull request](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/02.svg)
![Git Workflows: Activity inside a Pull Request](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/02.svg)
和其他协作模型相比这种共享提交的解决方案形成了更加线性的工作流。SVNGit都能通过一个简单的脚本发送通知邮件但是如果要讨论更改开发者不得不在邮件里回复。这会变得愈发杂乱无章尤其是后续提交出现时。Pull request将所有这些功能放入了一个友好的网页在每个Github仓库上方都能找到。
和其他协作模型相比这种共享提交的解决方案形成了更加线性的工作流。SVNGit 都能通过一个简单的脚本发送通知邮件但是如果要讨论更改开发者不得不在邮件里回复。这会变得愈发杂乱无章尤其是后续提交出现时。Pull Request 将所有这些功能放入了一个友好的网页,在每个 GitHub 仓库上方都能找到。
### 剖析一个Pull Request
### 剖析一个 Pull Request
当你提交一个pull request的时候你做的事情是 *请求 (request)* 另一个开发者(比如项目维护者)来 *拉取 (pull)* 你仓库中的一个分支到他们的仓库。也就是说你需要提供4个信息来完成一个pull request源仓库、源分支、目标仓库、目标分支。
当你提交一个 Pull Request 的时候,你做的事情是 *请求request* 另一个开发者(比如项目维护者)来 *拉取pull* 你仓库中的一个分支到他们的仓库。也就是说你需要提供 4 个信息来完成一个 Pull Request源仓库、源分支、目标仓库、目标分支。
![Git Workflows: Pull Requests](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/03.svg)
Github会机智地帮你将一些值设为默认值。但是取决于你的协作工作流你的团队可能需要设置不同的值。上图显示了一个请求从feature分支合并到官方master分支的一个pull request但除此之外还有好多种使用pull request的方式。
GitHub 会机智地帮你将一些值设为默认值。但是,取决于你的协作工作流,你的团队可能需要设置不同的值。上图显示了一个请求从 feature 分支合并到官方 master分支的一个 Pull Request但除此之外还有好多种使用 Pull Request 的方式。
## Pull Request是如何工作的
Pull request可以和feature分支工作流、Gitflow工作流或者Fork工作流一起使用。但pull request需要两个不同的分支或是两个不同的仓库因此它们不能和中心化的工作流一起使用。在不同的工作流中使用pull request有些不同但大致的流程如下
Pull Request 可以和 feature 分支工作流、GitFlow 工作流或者 Fork 工作流一起使用。但 Pull Request 需要两个不同的分支或是两个不同的仓库,因此它们不能和中心化的工作流一起使用。在不同的工作流中使用 Pull Request 有些不同,但大致的流程如下:
1. 开发者在他们的本地仓库中为某个功能创建一个专门的分支。
2. 开发者将分支推送到公共的Github仓库。
3. 开发者用Github发起一个pull request。
2. 开发者将分支推送到公共的 GitHub 仓库。
3. 开发者用 GitHub 发起一个 Pull Request。
4. 其余的团队成员审查代码,讨论并且做出修改。
5. 项目维护者将这个功能并入官方的仓库,然后关闭这个pull request。
5. 项目维护者将这个功能并入官方的仓库,然后关闭这个 Pull Request。
下面的章节讨论pull request在不同的协作工作流中有哪些不同。
下面的章节讨论 Pull Request 在不同的协作工作流中有哪些不同。
### Feature分支工作流中的Pull Request
### Feature 分支工作流中的 Pull Request
Feature分支工作流使用共享的Github仓库来管理协作开发者在单独的feature分支中添加功能。开发者在将代码并入主代码库之前应该发起一个pull request来启动这个功能的讨论而不是直接将它们合并到`master`
Feature 分支工作流使用共享的 GitHub 仓库来管理协作,开发者在单独的 feature 分支中添加功能。开发者在将代码并入主代码库之前,应该发起一个 Pull Request 来启动这个功能的讨论,而不是直接将它们合并到 `master`
![Pull Request: Feature Branch workflow](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/04.svg)
在Feature分支工作流中只有一个公共的仓库因此pull request的目标和源仓库永远是同一个。一般来说开发者会将他们的feature分支作为源分支`master`作为目标分支。
Feature 分支工作流中只有一个公共的仓库,因此 Pull Request 的目标和源仓库永远是同一个。一般来说,开发者会将他们的 feature分支作为源分支`master` 作为目标分支。
在收到pull request之后项目维护者将会做出决定。如果这个功能可以立即发布他们只需要将代码合并进`master`,然后关闭pull request即可。但是如果提议的更改有一些问题他们可以在pull request下发布反馈。后续提交将会显示在相关评论的下方。
在收到 Pull Request 之后,项目维护者将会做出决定。如果这个功能可以立即发布,他们只需要将代码合并进 `master`,然后关闭 Pull Request 即可。但是,如果提议的更改有一些问题,他们可以在 Pull Request 下发布反馈。后续提交将会显示在相关评论的下方。
你也可以发布一个未完成功能的pull request。例如如果开发者在实现一个特殊的需求时遇到了问题同样可以发布一个包含工作进展的pull request。其他开发者可以在这个pull request后面提供建议甚至自己发布后续的提交来解决这个问题。
你也可以发布一个未完成功能的 Pull Request。例如如果开发者在实现一个特殊的需求时遇到了问题同样可以发布一个包含工作进展的 Pull Request。其他开发者可以在这个 Pull Request 后面提供建议,甚至自己发布后续的提交来解决这个问题。
### Gitflow工作流中的Pull Request
### GitFlow 工作流中的 Pull Request
Gitflow工作流和Feature分支工作流类似但定义了围绕项目发布的一个严格的分支模型。在Gitflow工作流之上添加pull request使得开发者方便地讨论发布分支或是所在的维护分支。
GitFlow 工作流和 Feature 分支工作流类似,但定义了围绕项目发布的一个严格的分支模型。在 GitFlow 工作流之上添加 Pull Request 使得开发者方便地讨论发布分支或是所在的维护分支。
![Pull Requests: Gitflow Workflow](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/05.svg)
![Pull Requests: Gitflow Workflow 2](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/06.svg)
在Gitflow工作流中的Pull request和上一节中的完全一致开发者只需在功能、发布或是快速修复分支需要审查时发布一个pull requestGithub会通知到其余的团队成员。
GitFlow 工作流中的 Pull Request 和上一节中的完全一致:开发者只需在功能、发布或是快速修复分支需要审查时发布一个 Pull RequestGitHub 会通知到其余的团队成员。
功能一般都会合并到`develop`分支,而发布和快速修复分支会被同时合并到`develop``master`当中。 Pull request可以用来妥善管理这些合并。
功能一般都会合并到 `develop` 分支,而发布和快速修复分支会被同时合并到 `develop``master` 当中。 Pull Request 可以用来妥善管理这些合并。
### Fork工作流中的Pull Request
### Fork 工作流中的 Pull Request
在Fork工作流中开发者将一个完成的功能推送到 *他们自己的* 仓库,而不是公共的仓库。在这之后,他们发布一个pull request告诉项目维护者代码需要审查了。
Fork 工作流中,开发者将一个完成的功能推送到 *他们自己的* 仓库,而不是公共的仓库。在这之后,他们发布一个 Pull Request告诉项目维护者代码需要审查了。
在这个工作流中,pull request的通知作用显得非常有用因为项目维护者无法获知其他开发者什么时候向他们自己的Github仓库中添加了提交。
在这个工作流中,Pull Request 的通知作用显得非常有用,因为项目维护者无法获知其他开发者什么时候向他们自己的 GitHub 仓库中添加了提交。
![Pull Requests: Forking workflow](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/07.svg)
因为每个开发者都有他们自己的公共仓库,pull request的源仓库和目标仓库不是同一个。源仓库是开发者的公开仓库源分支是包含提议更改的那一个。如果开发者想要将功能合并到主代码库目标仓库便是官方的项目仓库目标分支为`master`
因为每个开发者都有他们自己的公共仓库,Pull Request 的源仓库和目标仓库不是同一个。源仓库是开发者的公开仓库,源分支是包含提议更改的那一个。如果开发者想要将功能合并到主代码库,目标仓库便是官方的项目仓库,目标分支为 `master`
Pull request还可以用来和官方项目之外的开发者进行协作。比如说一个开发者正在和同事一起开发一个功能他们可以向 *同事的* Github仓库发起一个pull request而不是官方仓库。他们将feature分支同时作为源分支和目标分支。
Pull Request 还可以用来和官方项目之外的开发者进行协作。比如说,一个开发者正在和同事一起开发一个功能,他们可以向 *同事的* GitHub 仓库发起一个 Pull Request而不是官方仓库。他们将 feature 分支同时作为源分支和目标分支。
![Pull Requests: Forking workflow](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/08.svg)
两个开发者可以在pull request中讨论和开发分支。当功能完成时其中一位可以发起另一个pull request请求将功能合并到官方的master分支中去。这种灵活性使得pull request成为了Fork工作流中尤为强大的协作工具。
两个开发者可以在 Pull Request 中讨论和开发分支。当功能完成时,其中一位可以发起另一个 Pull Request请求将功能合并到官方的 master 分支中去。这种灵活性使得 Pull Request 成为了 Fork 工作流中尤为强大的协作工具。
## 栗子
下面的🌰演示了如何将pull request用在Fork工作流中。小团队中的开发和向一个开源项目贡献代码都可以这样做。
下面的🌰演示了如何将 Pull Request 用在 Fork 工作流中。小团队中的开发和向一个开源项目贡献代码都可以这样做。
在这个栗子中Mary是一位开发者John是项目的维护者。他们都有自己公开的Github仓库John的仓库之一便是下面的官方项目。
在这个栗子中Mary 是一位开发者John 是项目的维护者。他们都有自己公开的 GitHub 仓库John 的仓库之一便是下面的官方项目。
### Mary fork了官方项目
![Pull Requests: Fork the project](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/09.svg)
为了参与这个项目Mary首先要做的是fork JohnGithub仓库。她需要注册登录Github找到John的仓库点击Fork按钮。
为了参与这个项目Mary 首先要做的是 fork 属于 JohnGitHub 仓库。她需要注册登录 GitHub找到 John 的仓库,点击 Fork 按钮。
> 下图显示的是geeeeeeeeekWeChatLuckyMoney仓库。
> 下图显示的是 geeeeeeeeekWeChatLuckyMoney 仓库。
![qq20160127-1](https://cloud.githubusercontent.com/assets/7262715/12614812/232fb7f2-c53d-11e5-80cb-68d2af29d6e9.png)
选好fork的目标位置之后她在服务端就有了一个项目的副本。
选好 fork 的目标位置之后,她在服务端就有了一个项目的副本。
### Mary克隆了她的Github仓库
### Mary 克隆了她的 GitHub 仓库
![Pull Request: Clone the Bitbucket repo](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/11.svg)
接下来Mary需要将她刚刚forkGithub仓库克隆下来。她在本地会有一份项目的副本。她需要运行下面这个命令
接下来Mary 需要将她刚刚 forkGitHub 仓库克隆下来。她在本地会有一份项目的副本。她需要运行下面这个命令:
```
```
git clone https://github.com/user/repo.git
```
请记住,`git clone`自动创建了一个名为`origin`的远端连接指向Mary fork的仓库。
请记住,`git clone` 自动创建了一个名为 `origin` 的远端连接,指向 Mary fork 的仓库。
### Mary开发了一个新功能
### Mary 开发了一个新功能
![Pull Requests: develop a new feature](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/12.svg)
在她写任何代码之前Mary需要为这个功能创建一个新的分支。这个分支将是她随后发起pull request时要用到的源分支。
在她写任何代码之前Mary 需要为这个功能创建一个新的分支。这个分支将是她随后发起 Pull Request 时要用到的源分支。
```
```
git checkout -b some-feature
# 编辑一些代码
git commit -a -m "新功能的一些草稿"
```
为了完成这个新功能Mary想创建多少个提交都可以。如果feature分支的历史有些乱她可以使用交互式的rebase来移除或者拼接不必要的提交。对于大项目来说清理feature的项目历史使得项目维护者更容易看清楚pull request的所处的进展。
为了完成这个新功能Mary 想创建多少个提交都可以。如果 feature 分支的历史有些乱,她可以使用交互式的 rebase 来移除或者拼接不必要的提交。对于大项目来说,清理 feature 的项目历史使得项目维护者更容易看清楚 Pull Request 的所处的进展。
### Mary将feature分支推送到了她的Github仓库
### Mary将feature分支推送到了她的GitHub仓库
![Pull Requests: Push feature to Bitbucket repository](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/13.svg)
在功能完成后Mary使用简单的`git push`将feature分支推送到了她自己的Github仓库上不是官方的仓库
在功能完成后Mary 使用简单的 `git push`feature 分支推送到了她自己的 GitHub 仓库上(不是官方的仓库):
```
```
git push origin some-branch
```
这样她的更改就可以被项目维护者看到了(或者任何有权限的协作者)。
### Mary创建了一个pull request
### Mary创建了一个Pull Request
![Pull Request: Create Pull Request](https://www.atlassian.com/git/images/tutorials/collaborating/making-a-pull-request/14.svg)
Github上已经有了她的feature分支之后Mary可以找到被她fork的仓库点击项目简介下的 *New Pull request* 按钮用她的Github账号创建一个pull request。Mary的仓库会被默认设置为源仓库(head fork),询问她指定源分支(compare)、目标仓库(base fork)和目标分支(base)
GitHub 上已经有了她的 feature 分支之后Mary 可以找到被她 fork 的仓库,点击项目简介下的 *New Pull Request* 按钮,用她的 GitHub 账号创建一个 Pull Request。Mary 的仓库会被默认设置为源仓库head fork,询问她指定源分支compare、目标仓库base fork和目标分支base
Mary想要将她的功能并入主代码库所以源分支就是她的feature分支目标仓库就是John的公开仓库目标分支为`master`。她还需要提供一个pull request的标题和简介。
Mary 想要将她的功能并入主代码库,所以源分支就是她的 feature 分支,目标仓库就是 John 的公开仓库,目标分支为 `master`。她还需要提供一个 Pull Request 的标题和简介。
> 下图展示的是将0492wzl/WeChatLuckyMoney(源仓库)的stable(源分支)合并到geeeeeeeeek/WeChatLuckyMoney(目标仓库)的stable(目标分支)。
![qq20160127-2](https://cloud.githubusercontent.com/assets/7262715/12615530/3088775a-c541-11e5-914e-d8232037e741.png)
在她创建了pull request之后Github会给John发送一条通知。
在她创建了 Pull Request 之后GitHub 会给 John 发送一条通知。
### John审查了这个pull request
### John审查了这个Pull Request
![qq20160127-3](https://cloud.githubusercontent.com/assets/7262715/12615618/b1373fd0-c541-11e5-9b91-47d40f8083ba.png)
John可以在他自己的Github仓库下的 *Pull Request* 选项卡中看到所有的pull request。点击Mary的pull request会显示这个pull request的简介、feature分支的提交历史以及包含的更改。
John 可以在他自己的 GitHub 仓库下的 *Pull Request* 选项卡中看到所有的 Pull Request。点击 Mary 的 Pull Request 会显示这个 Pull Request 的简介、feature 分支的提交历史,以及包含的更改。
如果他认为feature分支已经可以合并了他只需点击*Merge Pull Request*按钮来通过这个pull request将Maryfeature分支并入他的`master`分支。
如果他认为 feature 分支已经可以合并了,他只需点击 *Merge Pull Request* 按钮来通过这个 Pull Request Maryfeature分支并入他的 `master` 分支。
但是在这里栗子中假设John发现了Mary代码中的一个小bug需要她在合并前修复。他可以评论整个pull request也可以评论feature分支中某个特定的提交。
但是,在这里栗子中,假设 John 发现了 Mary 代码中的一个小 bug需要她在合并前修复。他可以评论整个 Pull Request也可以评论 feature 分支中某个特定的提交。
![qq20160127-4](https://cloud.githubusercontent.com/assets/7262715/12615732/67c8c872-c542-11e5-9734-71751b83f63c.png)
### Mary添加了一个后续提交
### Mary 添加了一个后续提交
如果Mary对这个反馈感到困惑她可以在pull request后回复把这里当做是她的功能的讨论版。
如果 Mary 对这个反馈感到困惑,她可以在 Pull Request 后回复,把这里当做是她的功能的讨论版。
为了修复错误Mary在她的feature分支后面添加了另一个提交并将它推送到了她的Github仓库就像她之前做的一样。这个提交被自动添加到原来的pull request后面John可以在他的评论下方再次审查这些修改。
为了修复错误Mary 在她的 feature 分支后面添加了另一个提交,并将它推送到了她的 GitHub 仓库,就像她之前做的一样。这个提交被自动添加到原来的 Pull Request 后面John 可以在他的评论下方再次审查这些修改。
### John接受了pull request
### John 接受了 Pull Request
最后John接受了这些修改将feature分支并入了master分支关闭了这个pull request。功能现在已经整合到了项目中其他在master分支上工作的开发者可以使用标准的`git pull`命令将这些修改拉取到自己的本地仓库。
最后John 接受了这些修改,将 feature 分支并入了 master 分支,关闭了这个 Pull Request。功能现在已经整合到了项目中其他在 master 分支上工作的开发者可以使用标准的 `git pull` 命令将这些修改拉取到自己的本地仓库。
> 如果你希望实践一下,可以按照上面的流程向这个项目发起一个pull request修改任何你发现的错误:smile:
> 如果你希望实践一下,可以按照上面的流程向这个项目发起一个 Pull Request修改任何你发现的错误 :smile:
## 接下来怎么做?
你现在应该已经掌握了如何将你的pull request整合到你的工作流。记住pull request不是替代任何Git工作流的万金油而是一种让队员间协作锦上添花的工具。
你现在应该已经掌握了如何将你的 Pull Request 整合到你的工作流。记住,Pull Request 不是替代任何 Git 工作流的万金油,而是一种让队员间协作锦上添花的工具。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)

View File

@ -1,22 +1,18 @@
# 创建代码仓库
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/setting-up-a-repository)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
这一章简要地带你了解一些最重要的 Git 命令。在这节中我会向你介绍开始一个新的版本控制项目需要的所有工具后面的几节包含了你每天都会用到的Git操作。
这一章简要地带你了解一些最重要的Git命令。在这节中我会向你介绍开始一个新的版本控制项目需要的所有工具后面的几节包含了你每天都会用到的Git操作。
在这节之后你应该能够创建一个新的Git仓库缓存你的项目以免丢失以及查看你项目的历史。
在这节之后,你应该能够创建一个新的 Git 仓库,缓存你的项目以免丢失,以及查看你项目的历史。
## git init
`git init`命令创建一个新的Git仓库。它用来将已存在但还没有版本控制的项目转换成一个Git仓库或者创建一个空的新仓库。大多数Git命令在未初始化的仓库中都是无法使用的所以这就是你运行新项目的第一个命令了。
`git init` 命令创建一个新的 Git 仓库。它用来将已存在但还没有版本控制的项目转换成一个 Git 仓库或者创建一个空的新仓库。大多数Git命令在未初始化的仓库中都是无法使用的所以这就是你运行新项目的第一个命令了。
运行`git init`命令会在你项目的根目录下创建一个新的`.git`目录,其中包含了你项目必需的所有元数据。除了`.git`目录之外已经存在的项目不会被改变就像SVN一样Git不强制每个子目录中都有一个`.git`目录)。
运行 `git init` 命令会在你项目的根目录下创建一个新的 `.git` 目录,其中包含了你项目必需的所有元数据。除了 `.git` 目录之外,已经存在的项目不会被改变(就像 SVN 一样Git 不强制每个子目录中都有一个 `.git` 目录)。
### 用法
@ -24,53 +20,51 @@
git init
```
将当前的目录转换成一个Git仓库。它在当前的目录下增加了一个`.git`文件夹,于是就可以开始记录项目版本了。
将当前的目录转换成一个 Git 仓库。它在当前的目录下增加了一个 `.git` 目录,于是就可以开始记录项目版本了。
``` shell
git init <directory>
```
在指定目录创建一个空的Git仓库。运行这个命令会创建一个名为`directory`,只包含`.git`子目录的空文件夹
在指定目录创建一个空的 Git 仓库。运行这个命令会创建一个名为 `directory`,只包含 `.git` 子目录的空目录
``` shell
git init --bare <directory>
```
初始化一个裸的Git仓库但是忽略工作目录。共享的仓库应该总是用`--bare`标记创建(见下面的讨论)。一般来说,用`—bare`标记初始化的仓库以`.git`结尾。比如,一个叫`my-project`的仓库,它的空版本应该保存在`my-project.git`目录下。
初始化一个裸的 Git 仓库,但是忽略工作目录。共享的仓库应该总是用 `--bare` 标记创建(见下面的讨论)。一般来说,用 `—bare` 标记初始化的仓库以 `.git` 结尾。比如,一个叫`my-project`的仓库,它的空版本应该保存在 `my-project.git` 目录下。
### 讨论
和SVN相比`git init`命令是一个创建新的版本控制项目非常简单的途径。Git不需要你创建仓库导入文件检查正在修改的拷贝。你只需要`cd`到你的项目目录下,运行`git init`你就有了一个功能强大的Git仓库。
SVN 相比,`git init` 命令是一个创建新的版本控制项目非常简单的途径。Git 不需要你创建仓库,导入文件,检查正在修改的拷贝。你只需要 `cd` 到你的项目目录下,运行 `git init`,你就有了一个功能强大的 Git 仓库。
但是,对大多数项目来说,`git init`只需要在创建中央仓库时执行一次——开发者通常不会使用`git init`来创建他们的本地仓库。他们往往使用`git clone`来将已存在的仓库拷贝到他们的机器中去。
但是,对大多数项目来说,`git init` 只需要在创建中央仓库时执行一次——开发者通常不会使用 `git init` 来创建他们的本地仓库。他们往往使用 `git clone` 来将已存在的仓库拷贝到他们的机器中去。
#### 裸仓库
`—bare`标记创建了一个没有工作目录的仓库,这样我们在仓库中更改文件并且提交了。中央仓库应该总是创建成裸仓库,因为向非裸仓库推送分支有可能会覆盖已有的代码变动。将`—bare`看成是用来将仓库标记为储存设施而不是一个开发环境。也就是说对于所有的Git工作流中央仓库是裸仓库开发者的本地仓库是非裸仓库。
`-—bare` 标记创建了一个没有工作目录的仓库,这样我们在仓库中更改文件并且提交了。中央仓库应该总是创建成裸仓库,因为向非裸仓库推送分支有可能会覆盖已有的代码变动。将`-—bare`看成是用来将仓库标记为储存设施,而不是一个开发环境。也就是说,对于所有的 Git 工作流,中央仓库是裸仓库,开发者的本地仓库是非裸仓库。
![](https://www.atlassian.com/git/images/tutorials/getting-started/setting-up-a-repository/01.svg)
### 栗子
因为`git clone`创建项目的本地拷贝更为方便,`git init`最常见的使用情景就是用于创建中央仓库:
因为 `git clone` 创建项目的本地拷贝更为方便,`git init` 最常见的使用情景就是用于创建中央仓库:
``` shell
ssh <user>@<host>
cd path/above/repo
cd path/above/repo
git init --bare my-project.git
```
首先你用SSH连入存放中央仓库的服务器。然后来到任何你想存放项目的地方最后使用`—bare`标记来创建一个中央存储仓库。开发者会将`my-project.git` 克隆到本地的开发环境中。
首先你用SSH连入存放中央仓库的服务器。然后来到任何你想存放项目的地方最后使用 `-—bare` 标记来创建一个中央存储仓库。开发者会将 `my-project.git` 克隆到本地的开发环境中。
## git clone
`git clone`命令拷贝整个Git仓库。这个命令就像`svn checkout`一样,除了工作副本是一个完备的Git仓库——它包含自己的历史管理自己的文件以及环境和原仓库完全隔离。
`git clone` 命令拷贝整个 Git 仓库。这个命令就像 `svn checkout` 一样,除了工作副本是一个完备的Git仓库——它包含自己的历史管理自己的文件以及环境和原仓库完全隔离。
为了方便起见,`clone`自动创建了一个名为`origin`的远程连接,指向原有仓库。这让和中央仓库之间的交互更加简单。
为了方便起见,`clone` 自动创建了一个名为 `origin` 的远程连接,指向原有仓库。这让和中央仓库之间的交互更加简单。
### 用法
@ -78,49 +72,49 @@ git init --bare my-project.git
git clone <repo>
```
将位于`<repo>`的仓库克隆到本地机器。原仓库可以在本地文件系统中或是通过HTTPSSH连接的远程机器。
将位于 `<repo>` 的仓库克隆到本地机器。原仓库可以在本地文件系统中,或是通过 HTTPSSH 连接的远程机器。
``` shell
git clone <repo> <directory>
```
将位于`<repo>`的仓库克隆到本地机器上的`<directory>`文件夹
将位于 `<repo>` 的仓库克隆到本地机器上的 `<directory>` 目录
### 讨论
如果项目在远程仓库已经设置完毕,`git clone`是用户获取开发副本最常见的方式。和`git init`相似,`clone`通常也是一次性的操作——只要开发者获得了一份工作副本,所有版本控制操作和协作管理都是在本地仓库中完成的。
如果项目在远程仓库已经设置完毕,`git clone` 是用户获取开发副本最常见的方式。和 `git init`相似,`clone` 通常也是一次性的操作——只要开发者获得了一份工作副本,所有版本控制操作和协作管理都是在本地仓库中完成的。
#### 仓库间协作
这一点很重要你要理解Git中”工作副本的概念和SVN仓库check out下来的工作副本是很不一样的。和SVN不同的是Git不会区分工作副本和中央仓库——它们都是功能完备的Git仓库。
这一点很重要,你要理解 Git 中「工作副本的概念和 SVN 仓库 check out 下来的工作副本是很不一样的。和 SVN 不同的是Git 不会区分工作副本和中央仓库——它们都是功能完备的 Git 仓库。
这就似的Git的协作和SVN截然不同。SVN依赖于中央仓库和工作副本之间的关系而Git协作模型是基于仓库和仓库之间的交互的。相对于SVN的提交流程你可以在Git仓库之间`push``pull`提交。
这就似的 Git 的协作和 SVN 截然不同。SVN 依赖于中央仓库和工作副本之间的关系,而 Git 协作模型是基于仓库和仓库之间的交互的。相对于 SVN 的提交流程,你可以在 Git 仓库之间 `push``pull` 提交。
![](https://www.atlassian.com/git/images/tutorials/getting-started/setting-up-a-repository/03.svg)
![](https://www.atlassian.com/git/images/tutorials/getting-started/setting-up-a-repository/02.svg)
当然你也完全可以给予某个特定的仓库一些特殊的含义。比如指定某个Git仓库为中央仓库你就可以用Git进行中央化的工作流。重点是这是通过约定实现的而不是写死在版本控制系统本身。
当然,你也完全可以给予某个特定的仓库一些特殊的含义。比如,指定某个 Git 仓库为中央仓库,你就可以用 Git 进行中央化的工作流。重点是,这是通过约定实现的,而不是写死在版本控制系统本身。
### 栗子
下面这个例子演示用SSH用户名john连接到example.com获取远程服务器上中央仓库的本地副本
下面这个例子演示用 SSH 用户名 john 连接到 example.com获取远程服务器上中央仓库的本地副本
``` shell
git clone ssh://john@example.com/path/to/my-project.git
git clone ssh://john@example.com/path/to/my-project.git
cd my-project
# 开始工作
```
第一行命令在本地机器的`my-project`文件夹下初始化了一个新的Git仓库并且导入了中央仓库中的文件。接下来你`cd`到项目目录,开始编辑文件、缓存提交、和其它仓库交互。同时注意`.git`拓展名克隆时会被去除。它表明了本地副本的非裸状态。
第一行命令在本地机器的 `my-project` 目录下初始化了一个新的 Git 仓库,并且导入了中央仓库中的文件。接下来,你 `cd` 到项目目录,开始编辑文件、缓存提交、和其它仓库交互。同时注意 `.git` 拓展名克隆时会被去除。它表明了本地副本的非裸状态。
``` shell
git config
```
`git config`命令允许你在命令行中配置你的Git安装或是一个独立仓库。这个命令定义了所有配置从用户信息到仓库行为等等。一些常见的配置命令如下所列。
`git config` 命令允许你在命令行中配置你的 Git 安装(或是一个独立仓库)。这个命令定义了所有配置,从用户信息到仓库行为等等。一些常见的配置命令如下所列。
### 用法
@ -128,7 +122,7 @@ git config
git config user.name <name>
```
定义当前仓库所有提交使用的作者姓名。通常来说,你希望使用`--global`标记设置当前用户的配置项。
定义当前仓库所有提交使用的作者姓名。通常来说,你希望使用 `--global` 标记设置当前用户的配置项。
``` sh
git config --global user.name <name>
@ -152,7 +146,7 @@ git config --global alias.<alias-name> <git-command>
git config --system core.editor <editor>
```
定义当前机器所有用户使用命令时用到的文本编辑器,如`git commit`。`<editor>`参数用编辑器的启动命令如vi替代。
定义当前机器所有用户使用命令时用到的文本编辑器,如 `git commit`。`<editor>` 参数用编辑器的启动命令(如 vi替代。
``` sh
git config --global --edit
@ -162,14 +156,14 @@ git config --global --edit
### 讨论
所有配置项都储存在纯文本文件中,所以`git config`命令其实只是一个提供便捷的命令行接口。通常你只需要在新机器上配置一次Git安装以及你通常会想要使用`--global`标记。
所有配置项都储存在纯文本文件中,所以 `git config` 命令其实只是一个提供便捷的命令行接口。通常,你只需要在新机器上配置一次 Git 安装,以及,你通常会想要使用 `--global` 标记。
Git将配置项保存在三个单独的文件中允许你分别对单个仓库、用户和整个系统设置。
Git 将配置项保存在三个单独的文件中,允许你分别对单个仓库、用户和整个系统设置。
- <repo>/.git/config 特定仓库的设置。
- ~/.gitconfig 特定用户的设置。这也是`--global`标记的设置项存放的位置。
- ~/.gitconfig 特定用户的设置。这也是 `--global` 标记的设置项存放的位置。
- $(prefix)/etc/gitconfig 系统层面的设置。
@ -177,7 +171,7 @@ Git将配置项保存在三个单独的文件中允许你分别对单个仓
当这些文件中的配置项冲突时,本地仓库设置覆盖用户设置,用户设置覆盖系统设置。如果你打开期中一份文件,你会看到下面这些:
``` sh
[user]
[user]
name = John Smith
@ -200,11 +194,11 @@ ci = commit
editor = vim
```
你可以用`git config`手动编辑这些值。
你可以用 `git config` 手动编辑这些值。
### 栗子
你在安装Git之后想要做的第一件事是告诉它你的名字和邮箱个性化一些默认设置。一般初始的设置过程看上去是这样的
你在安装 Git 之后想要做的第一件事是告诉它你的名字和邮箱,个性化一些默认设置。一般初始的设置过程看上去是这样的:
``` sh
# 告诉Git你是谁
@ -230,13 +224,10 @@ git config --global alias.up rebase
git config --global alias.ci commit
```
它会生成上一节中所说的`~/.gitconfig`文件。
它会生成上一节中所说的 `~/.gitconfig` 文件。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:。
>
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,37 +1,35 @@
# 代码回滚Reset、Checkout、Revert的选择
# 代码回滚Reset、Checkout、Revert 的选择
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
`git reset``git checkout``git revert`是你的Git工具箱中最有用的一些命令。它们都用来撤销代码仓库中的某些更改而前两个命令不仅可以作用于提交还可以作用于特定文件。
`git reset``git checkout``git revert` 是你的 Git 工具箱中最有用的一些命令。它们都用来撤销代码仓库中的某些更改,而前两个命令不仅可以作用于提交,还可以作用于特定文件。
因为它们非常相似,所以我们经常会搞混,不知道什么场景下该用哪个命令。在这篇文章中,我们会比较`git reset``git checkout``git revert`最常见的用法。希望你在看完后能游刃有余地使用这些命令来管理你的仓库。
因为它们非常相似,所以我们经常会搞混,不知道什么场景下该用哪个命令。在这篇文章中,我们会比较 `git reset``git checkout``git revert` 最常见的用法。希望你在看完后能游刃有余地使用这些命令来管理你的仓库。
![Git repo的主要组成](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/01.svg)
Git仓库有三个主要组成——工作目录缓存区和提交历史。这张图有助于理解每个命令到底产生了哪些影响。当你阅读的时候牢记这张图。
Git 仓库有三个主要组成——工作目录,缓存区和提交历史。这张图有助于理解每个命令到底产生了哪些影响。当你阅读的时候,牢记这张图。
## 提交层面的操作
你传给`git reset``git checkout`的参数决定了它们的作用域。如果你没有包含文件路径,这些操作对所有提交生效。我们这一节要探讨的就是提交层面的操作。注意,`git revert`没有文件层面的操作。
你传给 `git reset``git checkout` 的参数决定了它们的作用域。如果你没有包含文件路径,这些操作对所有提交生效。我们这一节要探讨的就是提交层面的操作。注意,`git revert` 没有文件层面的操作。
### Reset
在提交层面上reset将一个分支的末端指向另一个提交。这可以用来移除当前分支的一些提交。比如下面这两条命令让hotfix分支向后回退了两个提交。
在提交层面上reset 将一个分支的末端指向另一个提交。这可以用来移除当前分支的一些提交。比如,下面这两条命令让 hotfix 分支向后回退了两个提交。
```
```
git checkout hotfix
git reset HEAD~2
```
hotfix分支末端的两个提交现在变成了悬挂提交。也就是说下次Git执行垃圾回收的时候这两个提交会被删除。换句话说如果你想扔掉这两个提交你可以这么做。reset操作如下图所示
hotfix 分支末端的两个提交现在变成了悬挂提交。也就是说,下次 Git 执行垃圾回收的时候这两个提交会被删除。换句话说如果你想扔掉这两个提交你可以这么做。reset 操作如下图所示:
![把hotfix分支reset到HEAD~2](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/02.svg)
如果你的更改还没有共享给别人,`git reset`是撤销这些更改的简单方法。当你开发一个功能的时候发现糟糕,我做了什么?我应该重新来过!reset就像是go-to命令一样。
如果你的更改还没有共享给别人,`git reset` 是撤销这些更改的简单方法。当你开发一个功能的时候发现糟糕,我做了什么?我应该重新来过!reset 就像是 go-to 命令一样。
除了在当前分支上操作,你还可以通过传入这些标记来修改你的缓存区或工作目录:
@ -39,41 +37,41 @@ hotfix分支末端的两个提交现在变成了悬挂提交。也就是说
- --mixed 默认选项。缓存区和你指定的提交同步,但工作目录不受影响
- --hard 缓存区和工作目录都同步到你指定的提交
把这些标记想成定义`git reset`操作的作用域就容易理解多了。
把这些标记想成定义 `git reset` 操作的作用域就容易理解多了。
![git rese的定义域](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/03.svg)
这些标记往往和HEAD作为参数一起使用。比如`git reset --mixed HEAD` 将你当前的改动从缓存区中移除,但是这些改动还留在工作目录中。另一方面,如果你想完全舍弃你没有提交的改动,你可以使用`git reset --hard HEAD`。这是`git reset`最常用的两种用法。
这些标记往往和 HEAD 作为参数一起使用。比如,`git reset --mixed HEAD` 将你当前的改动从缓存区中移除,但是这些改动还留在工作目录中。另一方面,如果你想完全舍弃你没有提交的改动,你可以使用 `git reset --hard HEAD`。这是 `git reset` 最常用的两种用法。
当你传入HEAD以外的其他提交的时候要格外小心因为reset操作会重写当前分支的历史。正如Rebase黄金法则所说的在公共分支上这样做可能会引起严重的后果。
当你传入 HEAD 以外的其他提交的时候要格外小心,因为 reset 操作会重写当前分支的历史。正如 rebase 黄金法则所说的,在公共分支上这样做可能会引起严重的后果。
### Checkout
你应该已经非常熟悉提交层面的`git checkout`。当传入分支名时,可以切换到那个分支。
你应该已经非常熟悉提交层面的 `git checkout`。当传入分支名时,可以切换到那个分支。
```
```
git checkout hotfix
```
上面这个命令做的不过是将HEAD移到一个新的分支然后更新工作目录。因为这可能会覆盖本地的修改Git强制你提交或者缓存工作目录中的所有更改不然在checkout的时候这些更改都会丢失。和`git reset`不一样的是,`git checkout`没有移动这些分支。
上面这个命令做的不过是将HEAD移到一个新的分支然后更新工作目录。因为这可能会覆盖本地的修改Git 强制你提交或者缓存工作目录中的所有更改,不然在 checkout 的时候这些更改都会丢失。和 `git reset` 不一样的是,`git checkout` 没有移动这些分支。
![将 HEAD 从 master 移到 hotfix](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/04.svg)
除了分支之外你还可以传入提交的引用来checkout到任意的提交。这和checkout到另一个分支是完全一样的把HEAD移动到特定的提交。比如下面这个命令会checkout到当前提交的祖父提交。
除了分支之外,你还可以传入提交的引用来 checkout 到任意的提交。这和 checkout 到另一个分支是完全一样的:把 HEAD 移动到特定的提交。比如,下面这个命令会 checkout 到当前提交的祖父提交。
```
```
git checkout HEAD~2
```
![将HEAD移动到任意commit](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/05.svg)
![HEAD 移动到任意 commit](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/05.svg)
这对于快速查看项目旧版本来说非常有用。但如果你当前的HEAD没有任何分支引用那么这会造成HEAD分离。这是非常危险的如果你接着添加新的提交然后切换到别的分支之后就没办法回到之前添加的这些提交。因此在为分离的HEAD添加新的提交的时候你应该创建一个新的分支。
这对于快速查看项目旧版本来说非常有用。但如果你当前的 HEAD 没有任何分支引用,那么这会造成 HEAD 分离。这是非常危险的,如果你接着添加新的提交,然后切换到别的分支之后就没办法回到之前添加的这些提交。因此,在为分离的 HEAD 添加新的提交的时候你应该创建一个新的分支。
### Revert
Revert撤销一个提交的同时会创建一个新的提交。这是一个安全的方法因为它不会重写提交历史。比如下面的命令会找出倒数第二个提交然后创建一个新的提交来撤销这些更改然后把这个提交加入项目中。
Revert 撤销一个提交的同时会创建一个新的提交。这是一个安全的方法,因为它不会重写提交历史。比如,下面的命令会找出倒数第二个提交,然后创建一个新的提交来撤销这些更改,然后把这个提交加入项目中。
```
```
git checkout hotfix
git revert HEAD~2
```
@ -82,67 +80,65 @@ git revert HEAD~2
![revert到倒数第二个commit](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/06.svg)
相比`git reset`,它不会改变现在的提交历史。因此,`git revert`可以用在公共分支上,`git reset`应该用在私有分支上。
相比 `git reset`,它不会改变现在的提交历史。因此,`git revert` 可以用在公共分支上,`git reset` 应该用在私有分支上。
你也可以把`git revert`当作撤销已经提交的更改,而`git reset HEAD`用来撤销没有提交的更改。
你也可以把 `git revert` 当作撤销已经提交的更改,而 `git reset HEAD` 用来撤销没有提交的更改。
就像`git checkout` 一样,`git revert` 也有可能会重写文件。所以Git会在你执行revert之前要求你提交或者缓存你工作目录中的更改。
就像 `git checkout` 一样,`git revert` 也有可能会重写文件。所以Git 会在你执行 revert 之前要求你提交或者缓存你工作目录中的更改。
## 文件层面的操作
`git reset`和`git checkout` 命令也接受文件路径作为参数。这时它的行为就大为不同了。它不会作用于整份提交,参数将它限制于特定文件。
`git reset``git checkout` 命令也接受文件路径作为参数。这时它的行为就大为不同了。它不会作用于整份提交,参数将它限制于特定文件。
### Reset
当检测到文件路径时,`git reset` 将缓存区同步到你指定的那个提交。比如下面这个命令会将倒数第二个提交中的foo.py加入到缓存区中供下一个提交使用。
当检测到文件路径时,`git reset` 将缓存区同步到你指定的那个提交。比如,下面这个命令会将倒数第二个提交中的 `foo.py` 加入到缓存区中,供下一个提交使用。
```
```
git reset HEAD~2 foo.py
```
和提交层面的`git reset`一样通常我们使用HEAD而不是某个特定的提交。运行`git reset HEAD foo.py` 会将当前的foo.py从缓存区中移除出去而不会影响工作目录中对foo.py的更改。
和提交层面的 `git reset` 一样通常我们使用HEAD而不是某个特定的提交。运行 `git reset HEAD foo.py` 会将当前的 `foo.py` 从缓存区中移除出去,而不会影响工作目录中对 `foo.py` 的更改。
![将一个文件从commit历史中移动到stage缓存中](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/07.svg)
![将一个文件从 commit 历史中移动到 stage 缓存中](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/07.svg)
--soft、--mixed和--hard对文件层面的`git reset`毫无作用,因为缓存区中的文件一定会变化,而工作目录中的文件一定不变。
`--soft``--mixed``--hard` 对文件层面的 `git reset` 毫无作用,因为缓存区中的文件一定会变化,而工作目录中的文件一定不变。
### Checkout
Checkout一个文件和带文件路径`git reset` 非常像除了它更改的是工作目录而不是缓存区。不像提交层面的checkout命令它不会移动HEAD引用也就是你不会切换到别的分支上去。
Checkout 一个文件和带文件路径 `git reset` 非常像,除了它更改的是工作目录而不是缓存区。不像提交层面的 checkout 命令,它不会移动 HEAD引用也就是你不会切换到别的分支上去。
![将文件从提交历史移动到工作目录中](https://www.atlassian.com/git/images/tutorials/advanced/resetting-checking-out-and-reverting/08.svg)
比如下面这个命令将工作目录中的foo.py同步到了倒数第二个提交中的foo.py。
比如,下面这个命令将工作目录中的 `foo.py` 同步到了倒数第二个提交中的 `foo.py`
```
```
git checkout HEAD~2 foo.py
```
和提交层面相同的是,它可以用来检查项目的旧版本,但作用域被限制到了特定文件。
如果你缓存并且提交了checkout的文件它具备将某个文件回撤到之前版本的效果。注意它撤销了这个文件后面所有的更改而`git revert` 命令只撤销某个特定提交的更改。
如果你缓存并且提交了 checkout 的文件,它具备将某个文件回撤到之前版本的效果。注意它撤销了这个文件后面所有的更改,而 `git revert` 命令只撤销某个特定提交的更改。
和`git reset` 一样这个命令通常和HEAD一起使用。比如`git checkout HEAD foo.py`等同于舍弃foo.py没有缓存的更改。这个行为和`git reset HEAD --hard`很像,但只影响特定文件。
`git reset` 一样,这个命令通常和 HEAD 一起使用。比如 `git checkout HEAD foo.py` 等同于舍弃 `foo.py` 没有缓存的更改。这个行为和 `git reset HEAD --hard` 很像,但只影响特定文件。
## 总结
你现在已经掌握了Git仓库中撤销更改的所有工具。`git reset`、`git checkout`、和 `git revert`命令比较容易混淆,但当你想起它们对工作目录、缓存区和提交历史的不同影响,就会容易判断现在应该用哪个命令。
你现在已经掌握了 Git 仓库中撤销更改的所有工具。`git reset``git checkout``git revert` 命令比较容易混淆,但当你想起它们对工作目录、缓存区和提交历史的不同影响,就会容易判断现在应该用哪个命令。
下面这个表格总结了这些命令最常用的使用场景。记得经常对照这个表格因为你使用Git时一定会经常用到。
下面这个表格总结了这些命令最常用的使用场景。记得经常对照这个表格,因为你使用 Git 时一定会经常用到。
| 命令 | 作用域 | 常用情景 |
| :----------: | :--: | :---------------- |
| git reset | 提交层面 | 在私有分支上舍弃一些没有提交的更改 |
| git reset | 文件层面 | 将文件从缓存区中移除 |
| git checkout | 提交层面 | 切换分支或查看旧版本 |
| git checkout | 文件层面 | 舍弃工作目录中的更改 |
| git revert | 提交层面 | 在公共分支上回滚更改 |
| git revert | 文件层面 | (然而并没有) |
| 命令 | 作用域 | 常用情景 |
| :-: | :-: | :- |
| git reset | 提交层面 | 在私有分支上舍弃一些没有提交的更改 |
| git reset | 文件层面 | 将文件从缓存区中移除 |
| git checkout | 提交层面 | 切换分支或查看旧版本 |
| git checkout | 文件层面 | 舍弃工作目录中的更改 |
| git revert | 提交层面 | 在公共分支上回滚更改 |
| git revert | 文件层面 | (然而并没有) |
> 这篇文章是[**git-recipes**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:***Fork***:fork_and_knife:。
>
> 这篇文章是[**git-recipes**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2:***Fork***:fork_and_knife:。
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,54 +1,48 @@
## 回滚错误的修改
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/undoing-changes)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/undoing-changes)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
这章教程提供了和项目旧版本打交道所需要的所有技巧。首先,你会知道如何浏览旧的提交,然后了解回滚项目历史中的公有提交和回滚本地机器上的私有更改之间的区别。
## git checkout
见上一章[2.5 检出之前的提交](https://github.com/geeeeeeeeek/git-recipes/wiki/2.5-%E6%A3%80%E5%87%BA%E4%B9%8B%E5%89%8D%E7%9A%84%E6%8F%90%E4%BA%A4)。
见上一章[2.5 检出之前的提交](https://github.com/geeeeeeeeek/git-recipes/wiki/2.5-%E6%A3%80%E5%87%BA%E4%B9%8B%E5%89%8D%E7%9A%84%E6%8F%90%E4%BA%A4)。
## git revert
`git revert`命令用来撤销一个已经提交的快照。但是,它是通过搞清楚如何撤销这个提交引入的更改,然后在最后加上一个撤销了更改的 *新* 提交而不是从项目历史中移除这个提交。这避免了Git丢失项目历史这一点对于你的版本历史和协作的可靠性来说是很重要的。
`git revert` 命令用来撤销一个已经提交的快照。但是,它是通过搞清楚如何撤销这个提交引入的更改,然后在最后加上一个撤销了更改的 *新* 提交而不是从项目历史中移除这个提交。这避免了Git丢失项目历史这一点对于你的版本历史和协作的可靠性来说是很重要的。
![Git Tutorial: git revert](https://www.atlassian.com/git/images/tutorials/getting-started/undoing-changes/03.svg)
### 用法
```
```
git revert <commit>
```
生成一个撤消了`<commit>`引入的修改的新提交,然后应用到当前分支。
生成一个撤消了 `<commit>` 引入的修改的新提交,然后应用到当前分支。
### 讨论
撤销(revert)应该用在你想要在项目历史中移除一整个提交的时候。比如说你在追踪一个bug然后你发现它是由一个提交造成的这时候撤销就很有用。与其说自己去修复它然后提交一个新的快照不如用`git revert`,它帮你做了所有的事情。
#### 撤销(revert)和重设(reset)对比
理解这一点很重要——`git revert`回滚了『单独一个提交』——它没有移除后面的提交然后回到项目之前的状态。在Git中后者实际上被称为`reset`,而不是`revert`。
撤销revert应该用在你想要在项目历史中移除一整个提交的时候。比如说,你在追踪一个 bug然后你发现它是由一个提交造成的这时候撤销就很有用。与其说自己去修复它然后提交一个新的快照不如用 `git revert`,它帮你做了所有的事情。
#### 撤销revert和重设reset对比
理解这一点很重要。`git revert` 回滚了「单独一个提交」,它没有移除后面的提交,然后回到项目之前的状态。在 Git 中,后者实际上被称为 `reset`,而不是 `revert`
![Git Tutorial: Revert vs Reset](https://www.atlassian.com/git/images/tutorials/getting-started/undoing-changes/04.svg)
撤销和重设相比有两个重要的优点。首先,它不会改变项目历史,对那些已经发布到共享仓库的提交来说这是一个安全的操作。至于为什么改变共享的历史是危险的,请参阅`git reset`一节。
撤销和重设相比有两个重要的优点。首先,它不会改变项目历史,对那些已经发布到共享仓库的提交来说这是一个安全的操作。至于为什么改变共享的历史是危险的,请参阅 `git reset` 一节。
其次,`git revert`可以针对历史中任何一个提交,而`git reset`只能从当前提交向前回溯。比如,你想用`git reset`重设一个旧的提交,你不得不移除那个提交后的所有提交,再移除那个提交,然后重新提交后面的所有提交。不用说,这并不是一个优雅的回滚方案。
其次,`git revert` 可以针对历史中任何一个提交,而 `git reset` 只能从当前提交向前回溯。比如,你想用 `git reset` 重设一个旧的提交,你不得不移除那个提交后的所有提交,再移除那个提交,然后重新提交后面的所有提交。不用说,这并不是一个优雅的回滚方案。
### 栗子
下面的这个栗子是`git revert`一个简单的演示。它提交了一个快照,然后立即撤销这个操作。
下面的这个栗子是 `git revert` 一个简单的演示。它提交了一个快照,然后立即撤销这个操作。
```
```
# 编辑一些跟踪的文件
# 提交一份快照
@ -60,87 +54,75 @@ git revert HEAD
这个操作可以用下图可视化:
![Git Tutorial: git revert Example](https://www.atlassian.com/git/images/tutorials/getting-started/undoing-changes/05.svg)
注意第四个提交在撤销后依然在项目历史中。`git revert`在后面增加了一个提交来撤销修改,而不是删除它。 因此,第三和第五个提交表示同样的代码,而第四个提交依然在历史中,以备以后我们想要回到这个提交。
注意第四个提交在撤销后依然在项目历史中。`git revert` 在后面增加了一个提交来撤销修改,而不是删除它。 因此,第三和第五个提交表示同样的代码,而第四个提交依然在历史中,以备以后我们想要回到这个提交。
## git reset
如果说`git revert`是一个撤销更改安全的方式,你可以将`git reset`看做一个 *危险* 的方式。当你用`git reset`来重设更改时(提交不再被任何引用或引用日志所引用),我们无法获得原来的样子——这个撤销是永远的。使用这个工具的时候务必要小心,因为这是少数几个可能会造成工作丢失的命令之一。
如果说 `git revert` 是一个撤销更改安全的方式,你可以将 `git reset` 看做一个 *危险* 的方式。当你用 `git reset` 来重设更改时(提交不再被任何引用或引用日志所引用),我们无法获得原来的样子——这个撤销是永远的。使用这个工具的时候务必要小心,因为这是少数几个可能会造成工作丢失的命令之一。
和`git checkout`一样,`git reset`有很多种用法。它可以被用来移除提交快照,尽管它通常被用来撤销缓存区和工作目录的修改。不管是哪种情况,它应该只被用于 *本地* 修改——你永远不应该重设和其他开发者共享的快照。
`git checkout` 一样,`git reset` 有很多种用法。它可以被用来移除提交快照,尽管它通常被用来撤销缓存区和工作目录的修改。不管是哪种情况,它应该只被用于 *本地* 修改——你永远不应该重设和其他开发者共享的快照。
### 用法
```
```
git reset <file>
```
从缓存区移除特定文件,但不改变工作目录。它会取消这个文件的缓存,而不覆盖任何更改。
```
```
git reset
```
重设缓冲区,匹配最近的一次提交,但工作目录不变。它会取消 *所有* 文件的缓存,而不会覆盖任何修改,给你了一个重设缓存快照的机会。
```
```
git reset --hard
```
重设缓冲区和工作目录,匹配最近的一次提交。除了取消缓存之外,`--hard` 标记告诉Git还要重写所有工作目录中的更改。换句话说它清除了所有未提交的更改所以在使用前确定你想扔掉你所有本地的开发。
重设缓冲区和工作目录,匹配最近的一次提交。除了取消缓存之外,`--hard` 标记告诉 Git 还要重写所有工作目录中的更改。换句话说:它清除了所有未提交的更改,所以在使用前确定你想扔掉你所有本地的开发。
```
```
git reset <commit>
```
将当前分支的末端移到`<commit>`,将缓存区重设到这个提交,但不改变工作目录。所有`<commit>`之后的更改会保留在工作目录中,这允许你用更干净、原子性的快照重新提交项目历史。
将当前分支的末端移到 `<commit>`,将缓存区重设到这个提交,但不改变工作目录。所有 `<commit>` 之后的更改会保留在工作目录中,这允许你用更干净、原子性的快照重新提交项目历史。
```
```
git reset --hard <commit>
```
将当前分支的末端移到`<commit>`,将缓存区和工作目录都重设到这个提交。它不仅清除了未提交的更改,同时还清除了`<commit>`之后的所有提交。
将当前分支的末端移到 `<commit>`,将缓存区和工作目录都重设到这个提交。它不仅清除了未提交的更改,同时还清除了 `<commit>` 之后的所有提交。
### 讨论
上面所有的调用都是用来移除仓库中的修改。没有`--hard`标记时`git reset`通过取消缓存或取消一系列的提交,然后重新构建提交来清理仓库。而加上`--hard`标记对于作了大死之后想要重头再来尤其方便。
上面所有的调用都是用来移除仓库中的修改。没有 `--hard` 标记时 `git reset` 通过取消缓存或取消一系列的提交,然后重新构建提交来清理仓库。而加上 `--hard` 标记对于作了大死之后想要重头再来尤其方便。
撤销(revert)被设计为撤销 *公开* 的提交的安全方式,`git reset`被设计为重设 *本地* 更改。因为两个命令的目的不同,它们的实现也不一样:重设完全地移除了一堆更改,而撤销保留了原来的更改,用一个新的提交来实现撤销。
![Git Tutorial: Revert vs Reset](https://www.atlassian.com/git/images/tutorials/getting-started/undoing-changes/06.svg)
#### 不要重设公共历史
当有`<commit>`之后的提交被推送到公共仓库后,你绝不应该使用`git reset`。发布一个提交之后,你必须假设其他开发者会依赖于它。
移除一个其他团队成员在上面继续开发的提交在协作时会引发严重的问题。当他们试着和你的仓库同步时,他们会发现项目历史的一部分突然消失了。下面的序列展示了如果你尝试重设公共提交时会发生什么。`origin/master`是你本地`master`分支对应的中央仓库中的分支。
当有 `<commit>` 之后的提交被推送到公共仓库后,你绝不应该使用 `git reset`。发布一个提交之后,你必须假设其他开发者会依赖于它。
移除一个其他团队成员在上面继续开发的提交在协作时会引发严重的问题。当他们试着和你的仓库同步时,他们会发现项目历史的一部分突然消失了。下面的序列展示了如果你尝试重设公共提交时会发生什么。`origin/master` 是你本地 `master` 分支对应的中央仓库中的分支。
![Git Tutorial: Resetting an Public Commit](https://www.atlassian.com/git/images/tutorials/getting-started/undoing-changes/07.svg)
一旦你在重设之后又增加了新的提交Git 会认为你的本地历史已经和 `origin/master` 分叉了同步你的仓库时的合并提交merge commit会使你的同事困惑。
一旦你在重设之后又增加了新的提交Git会认为你的本地历史已经和`origin/master`分叉了,同步你的仓库时的合并提交(merge commit)会使你的同事困惑。
重点是,确保你只对本地的修改使用`git reset`——而不是公共更改。如果你需要修复一个公共提交,`git revert`命令正是被设计来做这个的。
重点是,确保你只对本地的修改使用 `git reset`,而不是公共更改。如果你需要修复一个公共提交,`git revert` 命令正是被设计来做这个的。
### 栗子
#### 取消文件缓存
`git reset`命令在准备缓存快照时经常被用到。下面的例子假设你有两个文件,`hello.py`和`main.py`它们已经被加入了仓库中。
`git reset` 命令在准备缓存快照时经常被用到。下面的例子假设你有两个文件,`hello.py``main.py`它们已经被加入了仓库中。
```
```
# 编辑了hello.py和main.py
# 缓存了目录下所有文件
@ -160,13 +142,13 @@ git add main.py
git commit -m "Edit main.py"
```
如你所见,`git reset`帮助你取消和这次提交无关的修改,让提交能够专注于某一特定的范围。
如你所见,`git reset` 帮助你取消和这次提交无关的修改,让提交能够专注于某一特定的范围。
#### 移除本地修改
下面的这个栗子显示了一个更高端的用法。它展示了你作了大死之后应该如何扔掉那几个更新。
```
```
# 创建一个叫`foo.py`的新文件,增加代码
# 提交到项目历史
@ -182,59 +164,59 @@ git commit -a -m "Continue my crazy feature"
git reset --hard HEAD~2
```
`git reset HEAD~2`命令将当前分支向前倒退两个提交,相当于在项目历史中移除刚创建的这两个提交。记住,这种重设只能用在 *非公开* 的提交中。绝不要在将提交推送到共享仓库之后执行上面的操作。
`git reset HEAD~2` 命令将当前分支向前倒退两个提交,相当于在项目历史中移除刚创建的这两个提交。记住,这种重设只能用在 *非公开* 的提交中。绝不要在将提交推送到共享仓库之后执行上面的操作。
## git clean
`git clean`命令将未跟踪的文件从你的工作目录中移除。它只是提供了一条捷径,因为用`git status`查看哪些文件还未跟踪然后手动移除它们也很方便。和一般的`rm`命令一样,`git clean`是无法撤消的,所以在删除未跟踪的文件之前想清楚,你是否真的要这么做。
`git clean` 命令将未跟踪的文件从你的工作目录中移除。它只是提供了一条捷径,因为用 `git status` 查看哪些文件还未跟踪然后手动移除它们也很方便。和一般的 `rm` 命令一样,`git clean` 是无法撤消的,所以在删除未跟踪的文件之前想清楚,你是否真的要这么做。
`git clean`命令经常和`git reset --hard`一起使用。记住reset只影响被跟踪的文件所以还需要一个单独的命令来清理未被跟踪的文件。这个两个命令相结合你就可以将工作目录回到之前特定提交时的状态。
`git clean` 命令经常和 `git reset --hard` 一起使用。记住reset 只影响被跟踪的文件,所以还需要一个单独的命令来清理未被跟踪的文件。这个两个命令相结合,你就可以将工作目录回到之前特定提交时的状态。
### 用法
```
```
git clean -n
```
执行一次git clean的『演习』。它会告诉你那些文件在命令执行后会被移除而不是真的删除它。
```
```
git clean -f
```
移除当前目录下未被跟踪的文件。`-f`(强制)标记是必需的,除非`clean.requireForce`配置项被设为了`false` (默认为`true`)。它 *不会* 删除 `.gitignore`中指定的未跟踪的文件。
移除当前目录下未被跟踪的文件。`-f`强制标记是必需的,除非 `clean.requireForce` 配置项被设为了 `false`(默认为 `true`。它 *不会* 删除 `.gitignore` 中指定的未跟踪的文件。
```
```
git clean -f <path>
```
移除未跟踪的文件,但限制在某个路径下。
```
```
git clean -df
```
移除未跟踪的文件,以及目录。
```
```
git clean -xf
```
移除当前目录下未跟踪的文件以及Git一般忽略的文件。
移除当前目录下未跟踪的文件,以及 Git 一般忽略的文件。
### 讨论
如果你在本地仓库中作死之后想要毁尸灭迹,`git reset --hard`和`git clean -f`是你最好的选择。运行这两个命令使工作目录和最近的提交相匹配,让你在干净的状态下继续工作。
如果你在本地仓库中作死之后想要毁尸灭迹,`git reset --hard``git clean -f` 是你最好的选择。运行这两个命令使工作目录和最近的提交相匹配,让你在干净的状态下继续工作。
`git clean`命令对于build后清理工作目录十分有用。比如它可以轻易地删除C编译器生成的`.o`和`.exe`二进制文件。这通常是打包发布前需要的一步。`-x`命令在这种情况下特别方便。
`git clean` 命令对于 build 后清理工作目录十分有用。比如,它可以轻易地删除 C 编译器生成的 `.o``.exe` 二进制文件。这通常是打包发布前需要的一步。`-x` 命令在这种情况下特别方便。
请牢记,和`git reset`一样, `git clean`是仅有的几个可以永久删除提交的命令之一所以要小心使用。事实上它太容易丢掉重要的修改了以至于Git厂商 *强制* 你用`-f`标志来进行最基本的操作。这可以避免你用一个`git clean`就不小心删除了所有东西。
请牢记,和 `git reset` 一样, `git clean` 是仅有的几个可以永久删除提交的命令之一,所以要小心使用。事实上,它太容易丢掉重要的修改了,以至于 Git 厂商 *强制* 你用 `-f` 标志来进行最基本的操作。这可以避免你用一个 `git clean` 就不小心删除了所有东西。
### 栗子
下面的栗子清除了工作目录中的所有更改,包括新建还没加入缓存的文件。它假设你已经提交了一些快照,准备开始一些新的实验。
```
```
# 编辑了一些文件
# 新增了一些文件
# 『糟糕』
@ -246,15 +228,12 @@ git reset --hard
git clean -df
```
在执行了reset/clean的流程之后工作目录和缓存区和最近一次提交看上去一模一样而`git status`会认为这是一个干净的工作目录。你可以重新来过了。
在执行了 reset/clean 的流程之后,工作目录和缓存区和最近一次提交看上去一模一样,而 `git status`会认为这是一个干净的工作目录。你可以重新来过了。
注意,不像`git reset`的第二个栗子,新的文件没有被加入到仓库中。因此,它们不会受到`git reset --hard`的影响,需要`git clean`来删除它们。
注意,不像 `git reset` 的第二个栗子,新的文件没有被加入到仓库中。因此,它们不会受到 `git reset --hard` 的影响,需要 `git clean` 来删除它们。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:。
>
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,10 +1,10 @@
# 常见工作流比较
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/comparing-workflows)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/comparing-workflows)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
多种多样的工作流使得在项目中实施Git时变得难以选择。这份教程提供了一个出发点调查企业团队最常见的Git工作流。
多种多样的工作流使得在项目中实施 Git 时变得难以选择。这份教程提供了一个出发点,调查企业团队最常见的 Git 工作流。
阅读的时候,请记住工作流应该是一种规范而不是金科玉律。我们希望向你展示所有工作流,让你融会贯通,因地制宜。
@ -17,147 +17,109 @@
## 中心化的工作流
![Git Workflows: SVN-style Workflow](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/01.svg)
过渡到分布式分版本控制系统看起来是个令人恐惧的任务,但你不必为了利用 Git 的优点而改变你现有的工作流。你的团队仍然可以用以前SVN的方式开发项目。
然而,使用 Git 来驱动你的开发工作流显示出了一些SVN没有的优点。首先它让每个开发者都有了自己 *本地* 的完整项目副本。隔离的环境使得每个开发者的工作独立于项目的其它修改——他们可以在自己的本地仓库中添加提交,完全无视上游的开发,直到需要的时候。
过渡到分布式分版本控制系统看起来是个令人恐惧的任务但你不必为了利用Git的优点而改变你现有的工作流。你的团队仍然可以用以前SVN的方式开发项目
然而使用Git来驱动你的开发工作流显示出了一些SVN没有的优点。首先它让每个开发者都有了自己 *本地* 的完整项目副本。隔离的环境使得每个开发者的工作独立于项目的其它修改——他们可以在自己的本地仓库中添加提交,完全无视上游的开发,直到需要的时候。
第二它让你接触到了Git鲁棒的分支和合并模型。和SVN不同Git分支被设计为一种故障安全的机制用来在仓库之间整合代码和共享更改。
第二,它让你接触到了 Git 鲁棒的分支和合并模型。和 SVN 不同Git 分支被设计为一种故障安全的机制,用来在仓库之间整合代码和共享更改
## 如何工作
和Subversion一样中心化的工作流将中央仓库作为项目中所有修改的唯一入口。和`trunk`不同,默认的开发分支叫做`master`,所有更改都被提交到这个分支。这种工作流不需要`master`之外的其它分支。
Subversion 一样,中心化的工作流将中央仓库作为项目中所有修改的唯一入口。和 `trunk` 不同,默认的开发分支叫做`master`,所有更改都被提交到这个分支。这种工作流不需要 `master` 之外的其它分支。
开发者将中央仓库克隆到本地后开始工作。在他们的本地项目副本中他们可以像SVN一样修改文件和提交更改不过这些新的提交被保存在 *本地* ——它们和中央仓库完全隔离。这使得开发者可以将和上游的同步推迟到他们方便的时候。
为了向官方项目发布修改,开发者将他们的本地`master`分支推送到中央仓库。这一步等同于`svn commit`除了Git添加的是所有不在中央`master`分支上的本地提交。
为了向官方项目发布修改,开发者将他们的本地 `master` 分支推送到中央仓库。这一步等同于 `svn commit`除了Git添加的是所有不在中央 `master` 分支上的本地提交。
![Central and local repositories](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/02.svg)
### 管理冲突
中央仓库代表官方项目因此它的提交历史应该被视作神圣不可更改的。如果开发者的本地提交和中央仓库分叉了Git会拒绝将他们的修改推送上去因为这会覆盖官方提交。
中央仓库代表官方项目因此它的提交历史应该被视作神圣不可更改的。如果开发者的本地提交和中央仓库分叉了Git 会拒绝将他们的修改推送上去,因为这会覆盖官方提交。
![Managing Conflicts](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/03.svg)
在开发者发布他们的功能之前,他们需要 fetch 更新的中央提交,在它们之上 rebase 自己的更改。这就像是「我想要在其他人的工作进展之上添加我的修改」,它会产生完美的线性历史,就像和传统的 SVN 工作流一样。
在开发者发布他们的功能之前他们需要fetch更新的中央提交在它们之上rebase自己的更改。这就像是“我想要在其他人的工作进展之上添加我的修改。”它会产生完美的线性历史就像和传统的SVN工作流一样。
如果本地修改和上游提交冲突时Git会暂停rebase流程给你机会手动解决这些冲突。Git很赞的一点是它将[`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)和[`git add`](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-add)命令同时用来生成提交和解决合并冲突。这使得开发者能够轻而易举地管理他们的合并。另外如果他们改错了什么Git让他们轻易地退出rebase过程然后重试或者找人帮忙
如果本地修改和上游提交冲突时Git 会暂停 rebase 流程给你机会手动解决这些冲突。Git 很赞的一点是,它将 [`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) 和 [`git add`](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-add) 命令同时用来生成提交和解决合并冲突。这使得开发者能够轻而易举地管理他们的合并。另外如果他们改错了什么Git 让他们轻易地退出 rebase 过程,然后重试(或者找人帮忙)。
## 栗子
让我们一步步观察一个普通的小团队是如何使用这种工作流协作的。我们有两位开发者JohnMary分别在开发两个功能他们通过中心化的仓库共享代码。
让我们一步步观察一个普通的小团队是如何使用这种工作流协作的。我们有两位开发者JohnMary分别在开发两个功能他们通过中心化的仓库共享代码。
### 一人初始化了中央仓库
![Git Workflows: Initialize Central Bare Repository](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/04.svg)
首先需要有人在服务器上创建中央仓库。如果这是一个新项目你可以初始化一个空的仓库。不然你需要导入一个已经存在的Git或SVN项目。
首先,需要有人在服务器上创建中央仓库。如果这是一个新项目,你可以初始化一个空的仓库。不然,你需要导入一个已经存在的 Git 或 SVN 项目。
中央仓库应该永远是裸仓库(没有工作目录),可以这样创建:
```
```
ssh user@host git init --bare /path/to/repo.git
```
但确保你使用的SSH用户名`user`、服务器`host`的域名或IP地址、储存仓库的地址`/path/to/repo.git`是有效的。注意`.git`约定俗成地出现在仓库名的后面,表明这是一个裸仓库。
但确保你使用的 SSH 用户名 `user`、服务器 `host` 的域名或 IP 地址、储存仓库的地址 `/path/to/repo.git` 是有效的。注意 `.git` 约定俗成地出现在仓库名的后面,表明这是一个裸仓库。
### 所有人将仓库克隆到本地
![Git Workflows: Clone Central Repo](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/05.svg)
接下来,每个开发者在本地创建一份完整项目的副本。使用 [`git clone`](https://github.com/geeeeeeeeek/git-recipes/wiki/2.2-%E5%88%9B%E5%BB%BA%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93#git-clone) 命令:
接下来,每个开发者在本地创建一份完整项目的副本。使用[`git clone`](https://github.com/geeeeeeeeek/git-recipes/wiki/2.2-%E5%88%9B%E5%BB%BA%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93#git-clone)命令:
```
```
git clone ssh://user@host/path/to/repo.git
```
当你克隆仓库时Git自动添加了一个名为`origin`的远程连接,指向“父”仓库,以便你以后和这个仓库交换数据。
### John在开发他的功能
当你克隆仓库时Git 自动添加了一个名为 `origin` 的远程连接,指向「父」仓库,以便你以后和这个仓库交换数据。
### John 在开发他的功能
![Git Workflows: Edit Stage Commit Feature Process ](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/06.svg)
在他的本地仓库中John 可以用标准的 Git 提交流程开发功能:编辑、缓存、提交。如果你对缓存区还不熟悉,你也可以不用记录工作目录中每次的变化。于是你创建了一个高度集中的提交,即使你已经在本地做了很多修改。
在他的本地仓库中John可以用标准的Git提交流程开发功能编辑、缓存、提交。如果你对缓存区还不熟悉你也可以不用记录工作目录中每次的变化。于是你创建了一个高度集中的提交即使你已经在本地做了很多修改。
```
```
git status # 查看仓库状态
git add <some-file> # 缓存一个文件
git commit # 提交一个文件</some-file>
```
记住这些命令创建的是本地提交John可以周而复始地重复这个过程而不用考虑中央仓库。对于庞大的功能需要切成更简单、原子化的片段时这个特性就很有用。
### Mary在开发她的功能
记住这些命令创建的是本地提交John 可以周而复始地重复这个过程,而不用考虑中央仓库。对于庞大的功能,需要切成更简单、原子化的片段时,这个特性就很有用。
### Mary 在开发她的功能
![Git Workflows: Edit Stage Commit Feature](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/07.svg)
同时Mary 在她自己的本地仓库用相同的编辑/缓存/提交流程开发她的功能。和 John 一样,她不需要关心中央仓库的进展,她也 *完全* 不关心 John 在他自己仓库中做的事,因为所有本地仓库都是私有的。
同时Mary在她自己的本地仓库用相同的编辑/缓存/提交流程开发她的功能。和John一样她不需要关心中央仓库的进展她也 *完全* 不关心John在他自己仓库中做的事因为所有本地仓库都是私有的。
### John发布了他的功能
### John 发布了他的功能
![Git Workflows: Publish Feature](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/08.svg)
一旦 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)命令,就像:
一旦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)命令,就像:
```
```
git push origin master
```
记住,`origin`是John克隆中央仓库时指向它的远程连接。`master`参数告诉Git试着将`origin`的`master`分支变得和他本地的`master`分支一样。中央仓库在John克隆之后还没有进展因此这个推送如他所愿没有产生冲突。
### Mary试图发布她的功能
记住,`origin`John 克隆中央仓库时指向它的远程连接。`master` 参数告诉 Git 试着将 `origin``master` 分支变得和他本地的 `master` 分支一样。中央仓库在 John 克隆之后还没有进展,因此这个推送如他所愿,没有产生冲突。
### Mary as试图发布她的功能
![Git Workflows: Push Command Error](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/09.svg)
John 已经成功地将他的更改发布到了中央仓库上,看看当 Mary 试着将她的功能推送到上面时会发生什么。她可以使用同一个推送命令:
John已经成功地将他的更改发布到了中央仓库上看看当Mary试着将她的功能推送到上面时会发生什么。她可以使用同一个推送命令
```
```
git push origin master
```
但是她的本地历史和中央仓库已经分叉了Git会拒绝这个请求并显示一段冗长的错误信息
但是她的本地历史和中央仓库已经分叉了Git 会拒绝这个请求,并显示一段冗长的错误信息:
```
```
error: failed to push some refs to '/path/to/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
@ -165,57 +127,43 @@ hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
Git防止Mary覆盖官方的修改。她需要将John的更新拉取到她的仓库和她的本地修改整合后然后重试。
Git 防止 Mary 覆盖官方的修改。她需要将 John 的更新拉取到她的仓库,和她的本地修改整合后,然后重试。
### Mary在John的提交之上rebase
![Git Workflows: Git Pull Rebase](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/10.svg)
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的本地仓库并和她的本地提交一起整合
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的本地仓库并和她的本地提交一起整合
```
```
git pull --rebase origin master
```
`--rebase`选项告诉Git在同步了中央仓库的修改之后将Mary所有的提交移到`master`分支的顶端,如下图所示:
`--rebase` 选项告诉 Git在同步了中央仓库的修改之后 Mary 所有的提交移到 `master` 分支的顶端,如下图所示:
![Rebasing to Master](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/11.svg)
如果你忽略这个选项拉取同样会成功只不过你每次和中央仓库同步时都会多出一个「合并提交」。在这种工作流中rebase 和生成一个合并提交相比,总是一个更好的选择。
如果你忽略这个选项拉取同样会成功只不过你每次和中央仓库同步时都会多出一个“合并提交”。在这种工作流中rebase和生成一个合并提交相比总是一个更好的选择。
### Mary解决了合并冲突
### Mary 解决了合并冲突
![Git Workflows: Rebasing on Commits](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/12.svg)
Rebase 的工作是将每个本地提交一个个转移到更新后的 `master` 分支。也就是说你可以一个个提交分别解决合并冲突而不是在一个庞大的合并提交中解决。它会让你的每个提交保持专注并获得一个干净的项目历史。另一方面你更容易发现bug是在哪引入的如果有必要的话用最小的代价回滚这些修改。
如果 Mary 和 John 开发的功能没有关联rebase的过程不太可能出现冲突。但如果出现冲突时Git 在当前提交会暂停 rebase输出下面的信息和一些相关的指令
Rebase的工作是将每个本地提交一个个转移到更新后的`master`分支。也就是说你可以一个个提交分别解决合并冲突而不是在一个庞大的合并提交中解决。它会让你的每个提交保持专注并获得一个干净的项目历史。另一方面你更容易发现bug是在哪引入的如果有必要的话用最小的代价回滚这些修改。
如果Mary和John开发的功能没有关联rebase的过程不太可能出现冲突。但如果出现冲突时Git在当前提交会暂停rebase输出下面的信息和一些相关的指令
```
```
CONFLICT (content): Merge conflict in <some-file>
```
![Conflict Resolution](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/13.svg)
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) 就可以发现问题是什么。冲突的文件会出现在未合并路径中:
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)就可以发现问题是什么。冲突的文件会出现在未合并路径中:
```
```
# Unmerged paths:
# (use "git reset HEAD <some-file>..." to unstage)
# (use "git add/rm <some-file>..." as appropriate to mark resolution)
@ -223,251 +171,199 @@ Git的优点在于 *每个人* 都能解决他们自己的合并冲突。在这
# both modified: <some-file>
```
接下来,修改这些文件。如果她对结果满意了,和往常一样缓存这些文件,然后让[`git rebase`](https://github.com/geeeeeeeeek/git-recipes/wiki/2.7-%E9%87%8D%E5%86%99%E9%A1%B9%E7%9B%AE%E5%8E%86%E5%8F%B2#git-rebase)完成接下来的工作:
接下来,修改这些文件。如果她对结果满意了,和往常一样缓存这些文件,然后让 [`git rebase`](https://github.com/geeeeeeeeek/git-recipes/wiki/2.7-%E9%87%8D%E5%86%99%E9%A1%B9%E7%9B%AE%E5%8E%86%E5%8F%B2#git-rebase) 完成接下来的工作:
```
```
git add <some-file>
git rebase --continue
```
就是这样。Git会继续检查下个提交对冲突的提交重复这个流程。
就是这样。Git 会继续检查下个提交,对冲突的提交重复这个流程。
如果你这时候发现不知道自己做了什么,不要惊慌。只要运行下面的命令,你就会回到开始之前的状态:
```
```
git rebase --abort
```
### Mary成功发布了她的分支
### Mary 成功发布了她的分支
![Git Workflows: Synchronize Central Repo](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/centralized-workflow/14.svg)
在她和中央仓库同步之后Mary 可以成功地发布她的修改:
在她和中央仓库同步之后Mary可以成功地发布她的修改
```
```
git push origin master
```
## 接下来该怎么做
正如你所见使用一丢丢Git命令来复制一套传统的Subversion开发环境也是可行的。这对于从SVN转变而来的团队来说很棒但这样没有利用到Git分布式的本质。
如果你的团队已经习惯了中心化的工作流,但希望提高协作效率,那么探索[Feature分支工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#feature%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)的好处是完全值当的。每个功能在专门的独立分支上进行,在代码并入官方项目之前就可以启动围绕新修改的深度讨论。
## Feature分支的工作流
正如你所见,使用一丢丢 Git 命令来复制一套传统的 Subversion 开发环境也是可行的。这对于从 SVN 转变而来的团队来说很棒,但这样没有利用到 Git 分布式的本质。
如果你的团队已经习惯了中心化的工作流,但希望提高协作效率,那么探索 [Feature 分支工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#feature%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81) 的好处是完全值当的。每个功能在专门的独立分支上进行,在代码并入官方项目之前就可以启动围绕新修改的深度讨论。
## Feature 分支的工作流
![Feature Branch Workflow](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/feature-branch-workflow/01.svg)
一旦你掌握了 [中心化工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#%E4%B8%AD%E5%BF%83%E5%8C%96%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81) 的使用方法,在你的开发流程中添加功能分支是一个简单的方式,来促进协作和开发者之间的交流。这种封装使得多个开发者专注自己的功能而不会打扰主代码库。它还保证 `master` 分支永远不会包含损坏的代码,给持续集成环境带来了是很大的好处。
一旦你掌握了[中心化工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#%E4%B8%AD%E5%BF%83%E5%8C%96%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)的使用姿势,在你的开发流程中添加功能分支是一个简单的方式,来促进协作和开发者之间的交流。这种封装使得多个开发者专注自己的功能而不会打扰主代码库。它还保证`master`分支永远不会包含损坏的代码,给持续集成环境带来了是很大的好处。
封装功能的开发使得[pull request](https://github.com/geeeeeeeeek/git-recipes/wiki/3.3-%E5%88%9B%E5%BB%BAPull-Request)的使用成为可能用来启动围绕一个分支的讨论。它给了其他开发者在功能并入主项目之前参与决策的机会。或者如果你开发功能时卡在一半你可以发起一个pull request向同事寻求建议。重点是pull request使得你的团队在评论其他人的工作时变得非常简单。
封装功能的开发使得 [Pull Request](https://github.com/geeeeeeeeek/git-recipes/wiki/3.3-%E5%88%9B%E5%BB%BAPull-Request) 的使用成为可能,用来启动围绕一个分支的讨论。它给了其他开发者在功能并入主项目之前参与决策的机会。或者,如果你开发功能时卡在一半,你可以发起一个 Pull Request向同事寻求建议。重点是Pull Request 使得你的团队在评论其他人的工作时变得非常简单。
## 如何工作
Feature分支工作流同样使用中央仓库`master`同样代表官方的项目历史。但是,与其直接提交在本地的`master`分支开发者每次进行新的工作时创建一个新的分支。Feature分支应该包含描述性的名称比如`animated-menu-items`(菜单项动画)或`issue-#1061`。每个分支都应该有一个清晰、高度集中的目的。
Feature 分支工作流同样使用中央仓库,`master` 同样代表官方的项目历史。但是,与其直接提交在本地的 `master` 分支开发者每次进行新的工作时创建一个新的分支。Feature 分支应该包含描述性的名称,比如 `animated-menu-items`菜单项动画)或 `issue-#1061`。每个分支都应该有一个清晰、高度集中的目的。
Git在技术上无法区别`master`和功能分支所以开发者可以在feature分支上编辑、缓存、提交就和中心化工作流中一样。
Git 在技术上无法区别 `master` 和功能分支,所以开发者可以在 feature 分支上编辑、缓存、提交,就和中心化工作流中一样。
此外feature分支可以也应该被推送到中央仓库。这使得你和其他开发者共享这个功能而又不改变官方代码。既然`master`只是一个“特殊”的分支在中央仓库中储存多个feature分支不会引出什么问题。当然了这也是备份每个开发者本地提交的好办法。
此外feature 分支可以(也应该)被推送到中央仓库。这使得你和其他开发者共享这个功能,而又不改变官方代码。既然 `master` 只是一个“特殊”的分支,在中央仓库中储存多个 feature 分支不会引出什么问题。当然了,这也是备份每个开发者本地提交的好办法。
### Pull Request
除了隔离功能开发之外,分支使得通过[pull request](https://github.com/geeeeeeeeek/git-recipes/wiki/3.3-%E5%88%9B%E5%BB%BAPull-Request)讨论修改成为可能。一旦有人完成了一个功能,他们不会立即将它并入`master`。他们将feature分支推送到中央服务器上发布一个pull request请求将他们的修改并入`master`。这给了其他开发者在修改并入主代码库之前审查的机会。
除了隔离功能开发之外,分支使得通过 [Pull Request](https://github.com/geeeeeeeeek/git-recipes/wiki/3.3-%E5%88%9B%E5%BB%BAPull-Request) 讨论修改成为可能。一旦有人完成了一个功能,他们不会立即将它并入`master`。他们将 feature 分支推送到中央服务器上,发布一个 Pull Request请求将他们的修改并入 `master`。这给了其他开发者在修改并入主代码库之前审查的机会。
代码审查是pull request的主要好处但他们事实上被设计为成为讨论代码的一般场所。你可以把pull request看作是专注某个分支的讨论版。也就是说他们可以用于开发流程之前。比如一个开发者在某个功能上需要帮助他只需发起一个pull request。感兴趣的小伙伴会自动收到通知看到相关提交中的问题。
代码审查是 Pull Request 的主要好处,但他们事实上被设计为成为讨论代码的一般场所。你可以把 Pull Request 看作是专注某个分支的讨论版。也就是说他们可以用于开发流程之前。比如,一个开发者在某个功能上需要帮助,他只需发起一个 Pull Request。感兴趣的小伙伴会自动收到通知看到相关提交中的问题。
一旦pull request被接受了发布功能的行为和中心化的工作流是一样的。首先确定你本地的`master`和上游的`master`已经同步。然后将feature分支并入`master`,将更新的`master`推送回中央仓库。
一旦 Pull Request 被接受了,发布功能的行为和中心化的工作流是一样的。首先,确定你本地的 `master` 和上游的 `master` 已经同步。然后,将 feature分支并入 `master`,将更新的 `master` 推送回中央仓库。
## 栗子
下面这🌰演示了代码审查使用到的pull request但记住pull request有多种用途。
下面这🌰演示了代码审查使用到的 Pull Request但记住 Pull Request 有多种用途。
### Mary开始了一个新功能
### Mary 开始了一个新功能
![New Feature Branch](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/feature-branch-workflow/02.svg)
在她开始开发一个功能之前Mary 需要一个独立的分支。她可以用下面的命令[创建新分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-checkout)
![New Feature Branch](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/feature-branch-workflow/02.svg)
在她开始开发一个功能之前Mary需要一个独立的分支。她可以用下面的命令[创建新分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-checkout)
```
```
git checkout -b marys-feature master
```
一个基于`master`、名为`marys-feature`的分支将会被checkout`-b`标记告诉Git在分支不存在时创建它。在这个分支上Mary和往常一样编辑、缓存、提交更改用足够多的提交来构建这个功能
一个基于 `master`、名为 `marys-feature` 的分支将会被checkout`-b` 标记告诉Git在分支不存在时创建它。在这个分支上Mary和往常一样编辑、缓存、提交更改用足够多的提交来构建这个功能
```
```
git status
git add <some-file>
git commit
```
### Mary去吃饭了
### Mary 去吃饭了
![Git Workflows: Feature Commits](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/feature-branch-workflow/03.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和其他开发者一起协作他们也可以看到她的初始提交了。
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和其他开发者一起协作他们也可以看到她的初始提交了。
```
```
git push -u origin marys-feature
```
这个命令将`marys-feature`推送到中央仓库(`origin`)`-u`标记将它添加为远程跟踪的分支。在设置完跟踪的分支之后Mary调用不带任何参数的`git push`来推送她的功能。
### Mary完成了她的工作
这个命令将 `marys-feature` 推送到中央仓库`origin``-u` 标记将它添加为远程跟踪的分支。在设置完跟踪的分支之后Mary 调用不带任何参数的 `git push` 来推送她的功能。
### Mary 完成了她的工作
![Git Workflows: Pull Request](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/feature-branch-workflow/04.svg)
当 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让其他的团队成员知道她所做的工作。但首先她应该保证中央仓库包含了她最新的提交
当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让其他的团队成员知道她所做的工作。但首先她应该保证中央仓库包含了她最新的提交
```
```
git push
```
然后她在她的Git界面上发起了一个pull request请求将`marys-feature`合并进`master`团队成员会收到自动的通知。Pull request的好处是评论显示在相关的提交正下方方便讨论特定的修改。
### Bill收到了pull request
然后,她在她的 Git 界面上发起了一个 Pull Request请求将 `marys-feature` 合并进 `master`团队成员会收到自动的通知。Pull Request 的好处是,评论显示在相关的提交正下方,方便讨论特定的修改。
### Bill 收到了 Pull Request
![Git Workflows: Feature Pull Requests](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/feature-branch-workflow/05.svg)
Bill 收到了 Pull Request并且查看了 `marys-feature`。他决定在并入官方项目之前做一些小修改,通过 Pull Request 和 Mary 进行了沟通。
Bill收到了pull request并且查看了`marys-feature`。他决定在并入官方项目之前做一些小修改通过pull request和Mary进行了沟通。
### Mary作了修改
### Mary 作了修改
![Git Workflows: Central Repository Push](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/feature-branch-workflow/06.svg)
为了做这些更改Mary 重复了之前创建功能时相同的流程,她编辑、缓存、提交、将更新推送到中央仓库。她所有的活动显示在 Pull Request 中Bill 可以一直评论。
如果 Bill 想要的话,也可以将 `marys-feature` 分支 pull 到他自己的本地仓库,继续工作。后续的任何提交都会显示在 Pull Request 上。
为了做这些更改Mary重复了之前创建功能时相同的流程她编辑、缓存、提交、将更新推送到中央仓库。她所有的活动显示在pull request中Bill可以一直评论。
如果Bill想要的话也可以将`marys-feature`pull到他自己的本地仓库继续工作。后续的任何提交都会显示在pull request上。
### Mary发布了她的功能
### Mary 发布了她的功能
![Merging a Feature Branch](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/feature-branch-workflow/07.svg)
一旦 Bill 准备接受这个 Pull Request某个人Bill 或者 Mary 都可)需要将功能并入稳定的项目:
一旦Bill准备接受这个pull request某个人Bill或者Mary都可需要将功能并入稳定的项目
```
```
git checkout master
git pull
git pull origin marys-feature
git push
```
首先,不管是谁在执行合并,都要保证他们的`master`分支是最新的。然后,运行`git pull origin marys-feature`合并中央仓库的`marys-feature`副本。你也可以使用简单的`git merge marys-feature`,但之前的命令保证你拉取下来的一定是功能分支最新的版本。最后,更新的`master`需要被推送回`origin`。
首先,不管是谁在执行合并,都要保证他们的 `master` 分支是最新的。然后,运行 `git pull origin marys-feature` 合并中央仓库的 `marys-feature` 副本。你也可以使用简单的 `git merge marys-feature`,但之前的命令保证你拉取下来的一定是功能分支最新的版本。最后,更新的 `master` 需要被推送回 `origin`
这个过程导致了一个合并提交。一些开发者喜欢它因为它是功能和其余代码合并的标志。但如果你希望得到线性的历史你可以在执行merge之前将功能rebase到`master`分支的顶端,产生一个快速向前的合并。
这个过程导致了一个合并提交。一些开发者喜欢它,因为它是功能和其余代码合并的标志。但,如果你希望得到线性的历史,你可以在执行 merge 之前将功能 rebase`master` 分支的顶端,产生一个快速向前的合并。
一些界面会自动化接受pull request的流程只需点击一下Merge Pull Request。如果你的没有的话,它至少在合并之后应该可以自动地关闭pull request。
一些界面会自动化接受 Pull Request 的流程,只需点击一下Merge Pull Request。如果你的没有的话,它至少在合并之后应该可以自动地关闭 Pull Request。
### 同时John以同样的方式工作着
### 同时John 以同样的方式工作着
MaryBill一起开发`marys-feature`在pull request上讨论的同时John还在开发他自己的feature分支。通过将功能用不同分支隔离开来每个人可以独立地工作但很容易和其他开发者共享修改。
MaryBill 一起开发 `marys-feature`,在 Pull Request 上讨论的同时John 还在开发他自己的 feature分支。通过将功能用不同分支隔离开来每个人可以独立地工作但很容易和其他开发者共享修改。
## 接下来该怎么做
为了彻底了解Github上的功能分支你应该查看[使用分支一章](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF)。现在,你应该已经看到了功能分支极大地增强了[中心化工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#%E4%B8%AD%E5%BF%83%E5%8C%96%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)中单一`master`分支的作用。除此之外,功能分支还便利了pull request的使用在版本控制界面上直接讨论特定的提交。Gitflow工作流是管理功能开发、发布准备、维护的常见模式。
## Gitflow工作流
为了彻底了解 GitHub 上的功能分支,你应该查看[使用分支一章](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF)。现在,你应该已经看到了功能分支极大地增强了[中心化工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#%E4%B8%AD%E5%BF%83%E5%8C%96%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)中单一 `master` 分支的作用。除此之外,功能分支还便利了 Pull Request 的使用在版本控制界面上直接讨论特定的提交。GitFlow 工作流是管理功能开发、发布准备、维护的常见模式。
## GitFlow 工作流
![Gitflow Workflow](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/01.svg)
下面的 [GitFlow 工作流](http://nvie.com/posts/a-successful-git-branching-model/)一节源于 [nvie](http://nvie.com/) 网站上的作者 Vincent Driessen。
GitFlow 工作流围绕项目发布定义了一个严格的分支模型。有些地方比[功能分支工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#feature%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)更复杂,为管理大型项目提供了鲁棒的框架。
下面的[Gitflow工作流](http://nvie.com/posts/a-successful-git-branching-model/)一节源于[nvie](http://nvie.com/)网站上的作者Vincent Driessen
Gitflow工作流围绕项目发布定义了一个严格的分支模型。有些地方比[功能分支工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#feature%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)更复杂,为管理大型项目提供了鲁棒的框架。
和功能分支工作流相比这种工作流没有增加任何新的概念或命令。它给不同的分支指定了特定的角色定义它们应该如何、什么时候交流。除了功能分支之外它还为准备发布、维护发布、记录发布分别使用了单独的分支。当然你还能享受到功能分支工作流带来的所有好处pull request、隔离实验和更高效的协作。
和功能分支工作流相比这种工作流没有增加任何新的概念或命令。它给不同的分支指定了特定的角色定义它们应该如何、什么时候交流。除了功能分支之外它还为准备发布、维护发布、记录发布分别使用了单独的分支。当然你还能享受到功能分支工作流带来的所有好处Pull Request、隔离实验和更高效的协作
## 如何工作
Gitflow工作流仍然使用中央仓库作为开发者沟通的中心。和[其他工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83)一样,开发者在本地工作,将分支推送到中央仓库。唯一的区别在于项目的分支结构。
GitFlow 工作流仍然使用中央仓库作为开发者沟通的中心。和[其他工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83)一样,开发者在本地工作,将分支推送到中央仓库。唯一的区别在于项目的分支结构。
### 历史分支
和单独的`master`分支不同,这种工作流使用两个分支来记录项目历史。`master`分支储存官方发布历史,`develop`分支用来整合功能分支。同时,这还方便了在`master`分支上给所有提交打上版本号标签。
和单独的 `master` 分支不同,这种工作流使用两个分支来记录项目历史。`master` 分支储存官方发布历史,`develop` 分支用来整合功能分支。同时,这还方便了在 `master` 分支上给所有提交打上版本号标签。
![Historical Branches](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/02.svg)
工作流剩下的部分围绕这两个分支的差别展开。
### 功能分支
每个新功能都放置在自己的分支中,可以[在备份/协作时推送到中央仓库](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`上交互。
每个新功能都放置在自己的分支中,可以[在备份/协作时推送到中央仓库](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` 上交互。
![Feature Branches](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/03.svg)
注意,功能分支加上`develop`分支就是我们之前所说的功能分支工作流。但是Gitflow工作流不止于此。
注意,功能分支加上 `develop` 分支就是我们之前所说的功能分支工作流。但是GitFlow 工作流不止于此。
### 发布分支
![Release Branches](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/04.svg)
一旦 `develop`分支的新功能足够发布(或者预先确定的发布日期即将到来),你可以从 `develop` 分支 fork 一个发布分支。这个分支的创建开始了下个发布周期,只有和发布相关的任务应该在这个分支进行,如修复 bug、生成文档等。一旦准备好了发布发布分支将合并进 `master`,打上版本号的标签。另外,它也应该合并回 `develop`,后者可能在发布启动之后有了新的进展。
一旦`develop`分支的新功能足够发布(或者预先确定的发布日期即将到来),你可以从`develop`分支fork一个发布分支。这个分支的创建开始了下个发布周期只有和发布相关的任务应该在这个分支进行如修复bug、生成文档等。一旦准备好了发布发布分支将合并进`master`,打上版本号的标签。另外,它也应该合并回`develop`,后者可能在发布启动之后有了新的进展。
使用一个专门的分支来准备发布确保一个团队完善当前的发布其他团队可以继续开发下一个发布的功能。它还建立了清晰的开发阶段比如说“这周我们准备4.0版本的发布”,而我们在仓库的结构中也能看到这个阶段)。
使用一个专门的分支来准备发布确保一个团队完善当前的发布,其他团队可以继续开发下一个发布的功能。它还建立了清晰的开发阶段(比如说,「这周我们准备 4.0 版本的发布」,而我们在仓库的结构中也能看到这个阶段)。
通常我们约定:
- 从`develop`创建分支
- 合并进`master`分支
- 命名规范`release-* or release/*`
-`develop` 创建分支
- 合并进 `master` 分支
- 命名规范 `release-* or release/*`
### 维护分支
![Maintenance Branches](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/05.svg)
维护或者「紧急修复」分支用来快速给产品的发布打上补丁。这是唯一可以从 `master` 上 fork 的分支。一旦修复完成了,它应该被并入 `master``develop` 分支(或者当前的发布分支),`master` 应该打上更新的版本号的标签。
维护或者“紧急修复”分支用来快速给产品的发布打上补丁。这是唯一可以从`master`上fork的分支。一旦修复完成了它应该被并入`master`和`develop`分支(或者当前的发布分支),`master`应该打上更新的版本号的标签。
有一个专门的bug修复开发线使得你的团队能够处理issues而不打断其他工作流或是要等到下一个发布周期。你可以将维护分支看作在`master`分支上工作的临时发布分支。
有一个专门的 bug 修复开发线使得你的团队能够处理 issues而不打断其他工作流或是要等到下一个发布周期。你可以将维护分支看作在 `master` 分支上工作的临时发布分支。
## 栗子
@ -475,61 +371,49 @@ Gitflow工作流仍然使用中央仓库作为开发者沟通的中心。和[其
### 创建一个开发分支
![Create a Develop Branch](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/06.svg)
你要做的第一步是为默认的 `master` 分支创建一个互补的 `develop` 分支。最简单的办法是[在本地创建一个空的 develop 分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-branch),将它推送到服务器上:
你要做的第一步是为默认的`master`分支创建一个互补的`develop`分支。最简单的办法是[在本地创建一个空的`develop`分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-branch),将它推送到服务器上:
```
```
git branch develop
git push -u origin develop
```
这个分支将会包含项目中所有的历史,而`master`将包含不完全的版本。其他开发者应该[将中央仓库克隆到本地](https://github.com/geeeeeeeeek/git-recipes/wiki/2.2-%E5%88%9B%E5%BB%BA%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93#git-clone)创建一个分支来追踪develop分支
这个分支将会包含项目中所有的历史,而 `master` 将包含不完全的版本。其他开发者应该[将中央仓库克隆到本地](https://github.com/geeeeeeeeek/git-recipes/wiki/2.2-%E5%88%9B%E5%BB%BA%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93#git-clone),创建一个分支来追踪 develop 分支:
```
```
git clone ssh://user@host/path/to/repo.git
git checkout -b develop origin/develop
```
现在所有人都有了一份历史分支的本地副本。
### MaryJohn开始了新功能
### MaryJohn 开始了新功能
![New Feature Branches](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/07.svg)
我们的栗子从 John 和 Mary 在不同分支上工作开始。他们都要为自己的功能创建单独的分支。[他们的功能分支都应该基于`develop`](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-checkout),而不是 `master`
我们的栗子从John和Mary在不同分支上工作开始。他们都要为自己的功能创建单独的分支。[他们的功能分支都应该基于`develop`](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-checkout),而不是`master`
```
```
git checkout -b some-feature develop
```
他们都使用编辑、缓存、提交的一般约定来向功能分支添加提交:
他们都使用编辑、缓存、提交的一般约定来向功能分支添加提交:
```
```
git status
git add <some-file>
git commit
```
### Mary完成了她的功能
### Mary 完成了她的功能
![Merging a Feature Branch](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/08.svg)
在添加了一些提交之后Mary 确信她的功能以及准备好了。如果她的团队使用 Pull Request现在正是发起 Pull Request 的好时候,请求将她的功能并入 `develop` 分支。否则,她可以向下面一样,将它并入本地的 `develop` 分支,推送到中央仓库:
在添加了一些提交之后Mary确信她的功能以及准备好了。如果她的团队使用pull request现在正是发起pull request的好时候请求将她的功能并入`develop`分支。否则,她可以向下面一样,将它并入本地的`develop`分支,推送到中央仓库:
```
```
git pull origin develop
git checkout develop
git merge some-feature
@ -537,37 +421,29 @@ git push
git branch -d some-feature
```
第一个命令在尝试并入功能分支之前确保`develop`分支已是最新。注意,功能绝不该被直接并入`master`。冲突的处理方式和[中心化工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#%E4%B8%AD%E5%BF%83%E5%8C%96%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)相同。
### Mary开始准备发布
第一个命令在尝试并入功能分支之前确保 `develop` 分支已是最新。注意,功能绝不该被直接并入 `master`。冲突的处理方式和[中心化工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#%E4%B8%AD%E5%BF%83%E5%8C%96%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)相同。
### Mary 开始准备发布
![Preparing a Release](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/09.svg)
当 John 仍然在他的功能分支上工作时Mary s开始准备项目的第一个官方发布。和开发功能一样她新建了一个分支来封装发布的准备工作。这也正是发布的版本号创建的一步
当John仍然在他的功能分支上工作时Mary开始准备项目的第一个官方发布。和开发功能一样她新建了一个分支来封装发布的准备工作。这也正是发布的版本号创建的一步
```
```
git checkout -b release-0.1 develop
```
这个分支用来整理提交,充分测试,更新文档,为即将到来的发布做各种准备。它就像是一个专门用来完善发布的功能分支。
一旦Mary创建了这个分支推送到中央仓库这次发布的功能便被锁定了。不在`develop`分支中的功能将被推迟到下个发布周期。
一旦 Mary 创建了这个分支,推送到中央仓库,这次发布的功能便被锁定了。不在 `develop` 分支中的功能将被推迟到下个发布周期。
### Mary完成了她的发布
![Merging Release into Master](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/10.svg)
一旦发布准备稳妥Mary 将它并入 `master``develop`,然后删除发布分支。合并回 `develop` 很重要,因为可能已经有关键的更新添加到了发布分支上,而开发新功能需要用到它们。同样的,如果 Mary 的团队重视代码审查,现在将是发起 Pull Request 的完美时机。
一旦发布准备稳妥Mary将它并入`master`和`develop`,然后删除发布分支。合并回`develop`很重要因为可能已经有关键的更新添加到了发布分支上而开发新功能需要用到它们。同样的如果Mary的团队重视代码审查现在将是发起pull request的完美时机。
```
```
git checkout master
git merge release-0.1
git push
@ -577,26 +453,22 @@ git push
git branch -d release-0.1
```
发布分支是功能开发(`develop`)和公开发布(master)之间的过渡阶段。不论什么时候将提交并入`master`时,你应该为提交打上方便引用的标签:
发布分支是功能开发`develop`和公开发布`master`之间的过渡阶段。不论什么时候将提交并入 `master` 时,你应该为提交打上方便引用的标签:
```
```
git tag -a 0.1 -m "Initial public release" master
git push --tags
```
Git提供了许多钩子即仓库中特定事件发生时被执行的脚本。当你向中央仓库推送`master`分支或者标签时,你可以配置一个钩子来自动化构建公开发布。
### 终端用户发现了一个bug
Git 提供了许多钩子,即仓库中特定事件发生时被执行的脚本。当你向中央仓库推送 `master` 分支或者标签时,你可以配置一个钩子来自动化构建公开发布。
### 终端用户发现了一个 bug
![Maintenance Branch](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/gitflow-workflow/11.svg)
正式发布之后Mary 回过头来和 John 一起为下一个发布开发功能。这时,一个终端用户开了一个 issue 抱怨说当前发布中存在一个 bug。为了解决这个 bugMary或 John`master` 创建了一个维护分支,用几个提交修复这个 issue然后直接合并回 `master`
正式发布之后Mary回过头来和John一起为下一个发布开发功能。这时一个终端用户开了一个issue抱怨说当前发布中存在一个bug。为了解决这个bugMary或John从`master`创建了一个维护分支用几个提交修复这个issue然后直接合并回`master`。
```
```
git checkout -b issue-#001 master
# Fix the bug
git checkout master
@ -604,9 +476,9 @@ git merge issue-#001
git push
```
和发布分支一样,维护分支包含了`develop`中需要的重要更新因此Mary同样需要执行这个合并。接下来她可以[删除这个分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-branch)了:
和发布分支一样,维护分支包含了 `develop` 中需要的重要更新,因此 Mary 同样需要执行这个合并。接下来,她可以[删除这个分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-branch)了:
```
```
git checkout develop
git merge issue-#001
git push
@ -615,100 +487,84 @@ git branch -d issue-#001
## 接下来该怎么做
现在,希望你已经很熟悉[中心化的工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#%E4%B8%AD%E5%BF%83%E5%8C%96%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)、[功能分支工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#feature%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)和Gitflow工作流。你应该已经可以抓住本地仓库、推送/拉取模式和Git鲁棒的分支和合并模型的无限潜力。
现在,希望你已经很熟悉[中心化的工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#%E4%B8%AD%E5%BF%83%E5%8C%96%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)、[功能分支工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#feature%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)和 GitFlow 工作流。你应该已经可以抓住本地仓库、推送/拉取模式,和 Git 鲁棒的分支和合并模型的无限潜力。
请记住教程中呈现的工作流只是可行的实践——而非工作中使用Git的金科玉律。因此尽情地取其精华去其糟粕吧。不变的是要让Git为你所用而不是相反。
## Fork工作流
Fork工作流和教程中讨论的其它工作流截然不同。与其使用唯一的服务端仓库作为”中央“代码库它给予 *每个* 开发者一个服务端仓库。也就是说每个贡献者都有两个Git仓库而不是一个一个私有的本地仓库和一个公开的服务端仓库。
请记住,教程中呈现的工作流只是可行的实践——而非工作中使用 Git 的金科玉律。因此,尽情地取其精华,去其糟粕吧。不变的是要让 Git 为你所用,而不是相反。
## Fork 工作流
Fork 工作流和教程中讨论的其它工作流截然不同。与其使用唯一的服务端仓库作为「中央」代码库,它给予 *每个* 开发者一个服务端仓库。也就是说每个贡献者都有两个 Git 仓库,而不是一个:一个私有的本地仓库和一个公开的服务端仓库。
![Git Workflows: Forking](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/forking-workflow/01.svg)
Fork工作流的主要优点在于贡献可以轻易地整合进项目而不需要每个人都推送到单一的中央仓库。开发者推送到他们 *自己的* 服务端仓库,只有项目管理者可以推送到官方仓库。这使得管理者可以接受任何开发者的提交,却不需要给他们中央仓库的权限。
Fork 工作流的主要优点在于贡献可以轻易地整合进项目,而不需要每个人都推送到单一的中央仓库。开发者推送到他们 *自己的* 服务端仓库,只有项目管理者可以推送到官方仓库。这使得管理者可以接受任何开发者的提交,却不需要给他们中央仓库的权限。
结论是,这种分布式的工作流为大型、组织性强的团队(包括不可信的第三方)提供了安全的协作方式。它同时也是开源项目理想的工作流。
## 如何工作
和其它Git工作流一样Fork工作流以一个储存在服务端的官方公开项目开场。但新的开发者想参与项目时他们不直接克隆官方项目。
和其它 Git 工作流一样Fork 工作流以一个储存在服务端的官方公开项目开场。但新的开发者想参与项目时,他们不直接克隆官方项目。
取而代之地他们fork一份官方项目在服务端创建一份副本。这份新建的副本作为他们私有的公开仓库——没有其他开发者可以在上面推送但他们可以从上面拉取修改在后面我们会讨论为什么这一点很重要。在他们创建了服务端副本之后开发者执行`git clone`操作,在他们的本地机器上复制一份。这是他们私有的开发环境,正如其他工作流中一样。
取而代之地,他们 fork 一份官方项目,在服务端创建一份副本。这份新建的副本作为他们私有的公开仓库——没有其他开发者可以在上面推送,但他们可以从上面拉取修改(在后面我们会讨论为什么这一点很重要)。在他们创建了服务端副本之后,开发者执行 `git clone` 操作,在他们的本地机器上复制一份。这是他们私有的开发环境,正如其他工作流中一样。
当他们准备好发布本地提交时,他们将提交推送到自己的公开仓库——而非官方仓库。然后,他们向主仓库发起一个pull request让项目维护者知道一个更新做好了合并的准备。如果贡献的代码有什么问题的话Pull request可以作为一个方便的讨论版。
当他们准备好发布本地提交时,他们将提交推送到自己的公开仓库——而非官方仓库。然后,他们向主仓库发起一个 Pull Request让项目维护者知道一个更新做好了合并的准备。如果贡献的代码有什么问题的话Pull Request 可以作为一个方便的讨论版。
我为了将功能并入官方代码库,维护者将贡献者的修改拉取到他们的本地仓库,确保修改不会破坏项目,将它[合并到本地的`master`分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-merge),然后将`master`分支[推送](https://github.com/geeeeeeeeek/git-recipes/wiki/3.2-%E4%BF%9D%E6%8C%81%E5%90%8C%E6%AD%A5#git-push)到服务端的官方仓库。贡献现在已是项目的一部分,其他开发者应该从官方仓库拉取并同步他们的本地仓库。
我为了将功能并入官方代码库,维护者将贡献者的修改拉取到他们的本地仓库,确保修改不会破坏项目,将它[合并到本地的 master 分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-merge),然后将 `master` 分支[推送](https://github.com/geeeeeeeeek/git-recipes/wiki/3.2-%E4%BF%9D%E6%8C%81%E5%90%8C%E6%AD%A5#git-push)到服务端的官方仓库。贡献现在已是项目的一部分,其他开发者应该从官方仓库拉取并同步他们的本地仓库。
### 中央仓库
官方仓库这个概念在Fork工作流中只是一个约定理解这一点很重要。从技术的角度Git并看不出每个开发者和官方的公开仓库有什么区别。事实上官方仓库唯一官方的原因是它是项目维护者的仓库。
官方仓库这个概念在 Fork 工作流中只是一个约定理解这一点很重要。从技术的角度Git 并看不出每个开发者和官方的公开仓库有什么区别。事实上,官方仓库唯一官方的原因是,它是项目维护者的仓库。
### Fork工作流中的分支
### Fork 工作流中的分支
所有这些个人的公开仓库只是一个在开发者之间共享分支的约定。每个人仍然可以使用分支来隔离功能,就像在[功能分支工作流](](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#feature%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81))和[Gitflow工作流中](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#gitflow%E5%B7%A5%E4%BD%9C%E6%B5%81)一样。唯一的区别在于这些分支是如何开始的。在Fork工作流中它们从另一个开发者的本地仓库拉取而来而在功能分支和Gitflow分支它们被推送到官方仓库。
所有这些个人的公开仓库只是一个在开发者之间共享分支的约定。每个人仍然可以使用分支来隔离功能,就像在[功能分支工作流](](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#feature%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81))和 [GitFlow 工作流中](https://github.com/geeeeeeeeek/git-recipes/wiki/3.5-%E5%B8%B8%E8%A7%81%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%AF%94%E8%BE%83#gitflow%E5%B7%A5%E4%BD%9C%E6%B5%81)一样。唯一的区别在于这些分支是如何开始的。在 Fork 工作流中,它们从另一个开发者的本地仓库拉取而来,而在功能分支和 GitFlow 分支它们被推送到官方仓库。
## 栗子
### 项目维护者初始化了中央仓库
![Forking Workflow: Shared Repository](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/forking-workflow/02.svg)
和任何基于Git的项目一样第一步是在服务端创建一个可以被所有项目成员访问到的官方仓库。一般来说这个仓库同时还是项目维护者的公开仓库。
和任何基于 Git 的项目一样,第一步是在服务端创建一个可以被所有项目成员访问到的官方仓库。一般来说,这个仓库同时还是项目维护者的公开仓库。
[公开的仓库应该永远是裸的](https://github.com/geeeeeeeeek/git-recipes/wiki/2.2-%E5%88%9B%E5%BB%BA%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93#git-init),不管它们是否代表官方代码库。所以项目维护者应该运行下面这样的命令来设置官方仓库:
```
```
ssh user@host
git init --bare /path/to/repo.git
```
Github同时提供了一个图形化界面来替代上面的操作。这和教程中其它工作流设置中央仓库的流程完全一致。如果有必要的话项目维护者应该将已有的代码库推送到这个仓库中。
### 开发者fork仓库
GitHub 同时提供了一个图形化界面来替代上面的操作。这和教程中其它工作流设置中央仓库的流程完全一致。如果有必要的话,项目维护者应该将已有的代码库推送到这个仓库中。
### 开发者 fork 仓库
![Forking Workflow: Forking the official repository.](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/forking-workflow/03.svg)
接下来所有开发者需要fork官方仓库。你可以用SSH到服务器运行`git clone`将它复制到服务器的另一个地址——fork其实只是服务端的clone。但同样地Github上开发者只需点一点按钮就可以fork仓库。
接下来,所有开发者需要 fork 官方仓库。你可以用 SSH 到服务器,运行 `git clone` 将它复制到服务器的另一个地址—— fork 其实只是服务端的 clone。但同样地GitHub上开发者只需点一点按钮就可以 fork 仓库。
在这步之后,每个开发者应该都有了自己的服务端仓库。像官方仓库一样,所有这些仓库都应该是裸仓库。
### 开发者将fork的仓库克隆到本地
### 开发者将 fork 的仓库克隆到本地
![Forking Workflow: Cloning the forked repositories](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/forking-workflow/04.svg)
接下来开发者需要克隆他们自己的公开仓库。他们可以用熟悉的 `git clone` 命令来完成这一步。
我们的栗子假设使用他们使用 GitHub 来托管仓库。记住,在这种情况下,每个开发者应该有他们自己的 GitHub 账号,应该用下面的命令克隆服务端仓库:
接下来开发者需要克隆他们自己的公开仓库。他们可以用熟悉的`git clone`命令来完成这一步。
我们的栗子假设使用他们使用Github来托管仓库。记住在这种情况下每个开发者应该有他们自己的Github账号应该用下面的命令克隆服务端仓库
```
```
git clone https://user@github.com/user/repo.git
```
而教程中的其他工作流使用单一的`origin`远程连接指向中央仓库Fork工作流需要两个远程连接一个是中央仓库另一个是开发者个人的服务端仓库。你可以给这些远端取任何名字约定的做法是将`origin`作为你fork后的仓库的远端运行`git clone`是会自动创建)和`upstream`作为官方项目。
而教程中的其他工作流使用单一的 `origin` 远程连接指向中央仓库Fork 工作流需要两个远程连接,一个是中央仓库,另一个是开发者个人的服务端仓库。你可以给这些远端取任何名字,约定的做法是将 `origin` 作为你 fork 后的仓库的远端(运行 `git clone` 是会自动创建)和 `upstream` 作为官方项目。
```
```
git remote add upstream https://github.com/maintainer/repo
```
你需要使用上面的命令来创建上游仓库的远程连接。它使得你轻易地保持本地仓库和官方仓库的进展同步。注意如果你的上游仓库开启了认证(比如它没有开源),你需要提供一个用户名,就像这样:
```
```
git remote add upstream https://user@bitbucket.org/maintainer/repo.git
```
@ -716,23 +572,19 @@ git remote add upstream https://user@bitbucket.org/maintainer/repo.git
### 开发者进行自己的开发
![Forking Workflow: Developers work on features](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/forking-workflow/05.svg)
在他们刚克隆的本地仓库中,开发者可以编辑代码、[提交更改](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.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF#git-branch)
```
```
git checkout -b some-feature
# 编辑代码
git commit -a -m "Add first draft of some feature"
```
他们所有的更改在推送到公开仓库之前都是完全私有的。而且,如果官方项目已经向前进展了,他们可以用`git pull`获取新的提交:
他们所有的更改在推送到公开仓库之前都是完全私有的。而且,如果官方项目已经向前进展了,他们可以用 `git pull` 获取新的提交:
```
```
git pull upstream master
```
@ -740,78 +592,64 @@ git pull upstream master
### 开发者发布他们的功能
![Forking Workflow: Developers publish features](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/forking-workflow/06.svg)
一旦开发者准备好共享他们的新功能,他们需要做两件事情。第一,他们必须将贡献的代码推送到自己的公开仓库,让其他开发者能够访问到。他们的 `origin` 远端应该已经设置好了,所以他们只需要:
一旦开发者准备好共享他们的新功能,他们需要做两件事情。第一,他们必须将贡献的代码推送到自己的公开仓库,让其他开发者能够访问到。他们的`origin`远端应该已经设置好了,所以他们只需要:
```
```
git push origin feature-branch
```
这和其他工作流不同之处在于,`origin`远端指向开发者个人的服务端仓库,而不是主代码库。
这和其他工作流不同之处在于,`origin` 远端指向开发者个人的服务端仓库,而不是主代码库。
第二他们需要通知项目维护者他们想要将功能并入官方代码库。Github提供了一个New Pull Request按钮,跳转到一个网页,让你指明想要并入主仓库的分支。一般来说,你希望将功能分支并入上游远端的`master`分支。
第二他们需要通知项目维护者他们想要将功能并入官方代码库。GitHub 提供了一个New Pull Request按钮,跳转到一个网页,让你指明想要并入主仓库的分支。一般来说,你希望将功能分支并入上游远端的 `master` 分支。
### 项目维护者整合他们的功能
![Forking Workflow: Integrate Features](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/forking-workflow/07.svg)
当项目维护者收到 Pull Request 时,他们的工作是决定是否将它并入官方的代码库。他们可以使用下面两种方式之一:
当项目维护者收到pull request时他们的工作是决定是否将它并入官方的代码库。他们可以使用下面两种方式之一
1. 直接检查pull request中检查代码
1. 直接检查 Pull Request 中检查代码
2. 将代码拉取到本地仓库然后手动合并
第一个选项更简单,让维护者查看修改前后的差异,在上面评论,然后通过图形界面执行合并。然而,如果pull request会导致合并冲突第二个选项就有了必要。在这个情况中维护者需要从开发者的服务端仓库fetch功能分支合并到他们本地的`master`分支,然后解决冲突:
第一个选项更简单,让维护者查看修改前后的差异,在上面评论,然后通过图形界面执行合并。然而,如果 Pull Request 会导致合并冲突,第二个选项就有了必要。在这个情况中,维护者需要从开发者的服务端仓库 fetch 功能分支,合并到他们本地的 `master` 分支,然后解决冲突:
```
```
git fetch https://bitbucket.org/user/repo feature-branch
# 检查修改
git checkout master
git merge FETCH_HEAD
```
一旦修改被整合进本地的`master`,维护者需要将它推送到服务器上的官方仓库,这样其他开发者也可以访问它:
一旦修改被整合进本地的 `master`,维护者需要将它推送到服务器上的官方仓库,这样其他开发者也可以访问它:
```
```
git push origin master
```
记住,维护者的`origin`指向他们的公开仓库,也就是项目的官方代码库。开发者的贡献现在完全并入了项目。
记住,维护者的 `origin` 指向他们的公开仓库,也就是项目的官方代码库。开发者的贡献现在完全并入了项目。
### 开发者和中央仓库保持同步
![Forking Workflow: Synchronize with the official repository](https://www.atlassian.com/git/images/tutorials/collaborating/comparing-workflows/forking-workflow/08.svg)
因为主代码库已经取得了新的进展,其他开发者应该和官方仓库同步:
```
```
git pull upstream master
```
## 接下来该怎么做
如果你从SVN迁移而来Fork工作流看上去是一个比较大的转变。但不要害怕——它只是在Feature分支工作流之上引入了一层抽象。贡献的代码发布到开发者在服务端自己的仓库而不是在唯一的中央仓库中直接共享分支。
如果你从 SVN 迁移而来Fork 工作流看上去是一个比较大的转变。但不要害怕——它只是在 Feature 分支工作流之上引入了一层抽象。贡献的代码发布到开发者在服务端自己的仓库,而不是在唯一的中央仓库中直接共享分支。
这篇文章解释了一次代码贡献是如何从一个开发者流入官方的`master`分支的,但相同的方法可以用在将代码贡献整合进任何仓库。比如,如果你团队的一部分成员在一个特定功能上协作,他们可以用自己约定的行为共享修改——而不改变主仓库。
这篇文章解释了一次代码贡献是如何从一个开发者流入官方的 `master` 分支的,但相同的方法可以用在将代码贡献整合进任何仓库。比如,如果你团队的一部分成员在一个特定功能上协作,他们可以用自己约定的行为共享修改——而不改变主仓库。
这使得Fork工作流对于松散的团队来说是个非常强大的工具。任何开发者都可以轻而易举地和其他开发者共享修改任何分支都能高效地并入主代码库。
这使得 Fork 工作流对于松散的团队来说是个非常强大的工具。任何开发者都可以轻而易举地和其他开发者共享修改,任何分支都能高效地并入主代码库。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)

View File

@ -1,77 +1,77 @@
# 什么是Git
> BY houkensjtu([houkensjtu@github](https://github.com/houkensjtu))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/what-is-git)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY houkensjtu[houkensjtu@github](https://github.com/houkensjtu)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/what-is-git)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
Git是目前世界上被最广泛使用的现代软件版本管理系统。Git本身亦是一个成熟并处于活跃开发状态的开源项目它最初是由Linux操作系统内核的创造者Linus Torvalds2005年创造。今天惊人数量的软件项目依赖Git进行版本管理这些项目包括开源以及各种商业软件。Git在职业软件开发者中拥有良好的声誉Git目前支持绝大多数的操作系统以及IDE(Integrated Development Environments)
Git 是目前世界上被最广泛使用的现代软件版本管理系统。Git 本身亦是一个成熟并处于活跃开发状态的开源项目,它最初是由 Linux 操作系统内核的创造者 Linus Torvalds2005 年创造。今天惊人数量的软件项目依赖 Git 进行版本管理这些项目包括开源以及各种商业软件。Git 在职业软件开发者中拥有良好的声誉Git 目前支持绝大多数的操作系统以及 IDEIntegrated Development Environments
Git使用分散式架构是分散式版本管理DVCS(Distributed Version Control System)的代表。相较于例如CVS或者Subversion等集中式版本管理软件Git并不是将代码的所有修改历史保存在中心服务器中。在Git中取而代之的是所有参与项目的开发者都拥有各自的代码完全拷贝并在自己的拷贝上进行软件开发。
Git 使用分散式架构,是分散式版本管理 DVCSDistributed Version Control System的代表。相较于例如 CVS 或者 Subversion 等集中式版本管理软件Git 并不是将代码的所有修改历史保存在中心服务器中。在 Git 中取而代之的是,所有参与项目的开发者都拥有各自的代码完全拷贝,并在自己的拷贝上进行软件开发。
除了分散式的特点之外Git的设计也针对性能安全性和柔软性作了特别优化。
除了分散式的特点之外Git 的设计也针对性能,安全性和柔软性作了特别优化。
### 性能
Git的底层性能相较于其他版本管理软件有强大的优势。在Git中所有的操作包括提交修改创建分支融合分支以及求取差分都经过了性能优化。这些优化来自于Git的开发者对实际一般代码开发模式的深度认识和广泛知识。
Git 的底层性能相较于其他版本管理软件有强大的优势。在 Git 中所有的操作包括提交修改,创建分支,融合分支,以及求取差分都经过了性能优化。这些优化来自于 Git 的开发者对实际一般代码开发模式的深度认识和广泛知识。
不同于某些版本管理软件Git在决定代码修改历史以及保存形式的时候不会被文件名的变化所愚弄Git关注的是文件的内容本身。在实际操作中代码文件经历频繁的再命名分解和合并。Git使用一种混合了差分编码delta encoding仅保存代码修改的差分压缩直接保存以及版本元数据version metadata objects的管理方式。
不同于某些版本管理软件Git 在决定代码修改历史以及保存形式的时候不会被文件名的变化所愚弄Git 关注的是文件的内容本身。在实际操作中代码文件经历频繁的再命名分解和合并。Git 使用一种混合了差分编码delta encoding仅保存代码修改的差分压缩直接保存以及版本元数据version metadata objects的管理方式。
分散式的架构也给Git带来了极大的性能优势。
分散式的架构也给 Git 带来了极大的性能优势。
比如说现有一名开发成员Alice对代码进行了一些改动添加了一些在2.0版本中准备公开的功能然后将这些修改以及一份简单的说明进行了提交。随后她又增加了一些另外的新功能并又作了一次新提交。显然这两次修改在版本历史中被分开各自进行了保存。在这之后Alice把代码切换到了1.3版本修复了一些旧版本中的Bug这和她新添加的功能没有关系。这次修复的目的是为了让团队可以在公开2.0版本之前释放一个1.3.1版本来解决1.3版本中的Bug问题。Alice马上又可以回到她之前进行的2.0版本功能开发之中通过切换代码分支这些操作由于Git的分散属性都不需要通过网络来连接到中央服务器进行她甚至可以在飞机中完成这一切。当她完成所有工作只需要向远程的代码库推送Push自己的修改即可。
比如说,现有一名开发成员 Alice 对代码进行了一些改动添加了一些在2.0版本中准备公开的功能,然后将这些修改以及一份简单的说明进行了提交。随后她又增加了一些另外的新功能,并又作了一次新提交。显然这两次修改在版本历史中被分开各自进行了保存。在这之后 Alice 把代码切换到了 1.3 版本,修复了一些旧版本中的 Bug这和她新添加的功能没有关系。这次修复的目的是为了让团队可以在公开 2.0 版本之前,释放一个 1.3.1 版本来解决 1.3 版本中的 Bug 问题。Alice 马上又可以回到她之前进行的 2.0 版本功能开发之中(通过切换代码分支),这些操作由于 Git 的分散属性都不需要通过网络来连接到中央服务器进行她甚至可以在飞机中完成这一切。当她完成所有工作只需要向远程的代码库推送Push自己的修改即可。
### 安全性
Git将保持所管理代码的整合性作为首要要务。所有的文件内容文件相互关系以及文件目录结构版本标签以及修改都经过加密哈希校验算法SHA1的保护。这可以防止各种意外的代码修改失误或者是第三者的恶意修改使得代码修改历史完全可追迹。
Git 将保持所管理代码的整合性作为首要要务。所有的文件内容文件相互关系以及文件目录结构版本标签以及修改都经过加密哈希校验算法SHA1的保护。这可以防止各种意外的代码修改失误或者是第三者的恶意修改使得代码修改历史完全可追迹。
使用Git你可以确信你拥有代码的完整修改历史。
使用 Git 你可以确信你拥有代码的完整修改历史。
某些其他的版本管理软件对发布后的代码不进行任何保护。这对于完全依赖于软件开发的团队来说可以是一种非常严重的安全脆弱性问题。
### 柔软性
Git的关键设计目标之一就是保持柔软性。Git在以下方面都展现出了其柔软性支持各种非线性的开发工作流程对或大或小的软件项目都可以良好支持以及兼容各种操作系统和协议。
Git 的关键设计目标之一就是保持柔软性。Git 在以下方面都展现出了其柔软性:支持各种非线性的开发工作流程,对或大或小的软件项目都可以良好支持,以及兼容各种操作系统和协议。
Git支持将分支和标签作为一级基本对象不同于SVN所以所有对分支和标签的操作也都会被保存到修改历史中。并不是所有的版本管理软件支持这一层面的追迹。
Git 支持将分支和标签作为一级基本对象(不同于 SVN所以所有对分支和标签的操作也都会被保存到修改历史中。并不是所有的版本管理软件支持这一层面的追迹。
### 使用Git进行版本管理
### 使用 Git 进行版本管理
Git对今天绝大多数软件开发团队来说都是最佳的选择。虽然每个团队都有各自的特点和目标但是这里我们依然可以列举一些对他们来说Git优于其他选择的理由
Git 对今天绝大多数软件开发团队来说都是最佳的选择。虽然每个团队都有各自的特点和目标但是这里我们依然可以列举一些对他们来说Git优于其他选择的理由
#### Git很棒
#### Git 很棒
Git兼备了功能性高性能安全性和柔软性这些是很多软件开发团队以及个人所需要的要素。我们已经在上面详细讨论了这些特性。对很多软件开发团队来说以以上标准货比三家的最终结果都是选择Git。
Git 兼备了功能性高性能安全性和柔软性这些是很多软件开发团队以及个人所需要的要素。我们已经在上面详细讨论了这些特性。对很多软件开发团队来说以以上标准货比三家的最终结果都是选择Git。
#### Git已经成为了默认的行业标准
#### Git 已经成为了默认的行业标准
Git是受到最广泛使用和支持的版本管理软件。这使得Git在以下这些方面具有极大的吸引力。我们在Atlassian此Tutorial作者所处的公司的大多数代码都是用Git来进行管理的。
Git 是受到最广泛使用和支持的版本管理软件。这使得 Git 在以下这些方面具有极大的吸引力。我们在 Atlassian Tutorial 作者所处的公司)的大多数代码都是用 Git 来进行管理的。
绝大多数的软件开发者都有过Git的使用经历很大一部分在校或者刚刚毕业的学生甚至只用过Git进行版本管理。虽然在一些公司开发成员可能在从其他版本管理软件迁移到Git的过程中要经历比较陡峭的学习曲线但是大多数开发者以及他们未来的潜在开发者学生都已经具备了使用Git的基本技能这就意味着他们不再需要额外的培训。
绝大多数的软件开发者都有过 Git 的使用经历,很大一部分在校或者刚刚毕业的学生甚至只用过 Git 进行版本管理。虽然在一些公司开发成员可能在从其他版本管理软件迁移到 Git 的过程中要经历比较陡峭的学习曲线,但是大多数开发者以及他们未来的潜在开发者(学生)都已经具备了使用 Git 的基本技能,这就意味着他们不再需要额外的培训。
Git的普及还带来很多其他的好处Git的市场占有率意味着很多第三方的服务和IDE都开始默认支持Git。比如我们的DVCS客户端[Source
Tree](https://www.atlassian.com/software/sourcetree),项目开发管理软件[JIRA](https://www.atlassian.com/software/jira),以及代码托管服务[Bitbucket](https://www.atlassian.com/software/bitbucket)。
Git 的普及还带来很多其他的好处Git 的市场占有率意味着很多第三方的服务和 IDE 都开始默认支持 Git。比如我们的 DVCS 客户端 [Source
Tree](https://www.atlassian.com/software/sourcetree),项目开发管理软件 [JIRA](https://www.atlassian.com/software/jira),以及代码托管服务 [Bitbucket](https://www.atlassian.com/software/bitbucket)。
如果你是一个开发新手并期待在未来构建自己的专业开发技能Git毫无疑问是你在版本管理上的第一选择。
如果你是一个开发新手并期待在未来构建自己的专业开发技能Git 毫无疑问是你在版本管理上的第一选择。
#### Git是一个高质量的开源项目
#### Git 是一个高质量的开源项目
Git本身是一个拥有良好支持和管理的开源软件项目。Git的开发者在过去的十年中展现了良好的公平性成熟的开发手段以保障其用户将来的需求以及定期的更新以保持其可用性和功能性。开源的特性使得项目代码本身受到无微不至的检查现在有无数的企业都依赖于Git的超高软件开发质量。
Git 本身是一个拥有良好支持和管理的开源软件项目。Git 的开发者在过去的十年中展现了良好的公平性,成熟的开发手段以保障其用户将来的需求,以及定期的更新以保持其可用性和功能性。开源的特性使得项目代码本身受到无微不至的检查,现在有无数的企业都依赖于 Git 的超高软件开发质量。
Git同时享有极好的社区支持和庞大的用户群体。你可以找到各种内容深入浅出的学习资料包括书籍教程以及专题网站。甚至还有广播以及视频教程存在。
Git 同时享有极好的社区支持和庞大的用户群体。你可以找到各种内容深入浅出的学习资料,包括书籍,教程,以及专题网站。甚至还有广播以及视频教程存在。
保持开源降低了编程爱好者的投入成本因为他们不需要花一分钱来使用Git。在开源项目中Git无疑扮演了前一世代版本管理软件比如SVNCVS的成功接班人。
保持开源降低了编程爱好者的投入成本因为他们不需要花一分钱来使用 Git。在开源项目中Git 无疑扮演了前一世代版本管理软件,比如 SVNCVS的成功接班人。
#### 对于Git的批评意见
#### 对于 Git 的批评意见
对于Git的一个常见批评是它非常难以掌握。Git中的某些术语对刚上手的朋友或者是使用其他系统的朋友可能会比较陌生比如说`revert`这个命令在Git中和在SVN或者CVS中具有不同的含义。不过尽管如此Git依然向用户提供了非常强大的功能。学习掌握这些功能也许会花一些时间但是一旦你学会了这些技能它们会帮助你大大提高团队的开发效率。
对于 Git 的一个常见批评是它非常难以掌握。Git 中的某些术语对刚上手的朋友或者是使用其他系统的朋友可能会比较陌生,比如说,`revert` 这个命令在 Git 中和在 SVN 或者 CVS 中具有不同的含义。不过尽管如此Git 依然向用户提供了非常强大的功能。学习掌握这些功能也许会花一些时间,但是一旦你学会了这些技能,它们会帮助你大大提高团队的开发效率。
对于曾经使用非分散式版本管理的团队来说保存代码的中央服务器可能是他们所不想舍去的。不过虽然Git的确是分散式的架构设计但是你依然可以设立一个官方的代码库来强制保存所有的修改。使用Git时由于所有的开发者都拥有完整的代码库拷贝所以他们的工作不会被中央服务器的性能甚至有无左右。即便他们下线或者在外他们依然可以随时查看代码库的修改历史。得益于Git的分散式特性你可以保持自己原有的工作方式但得到Git带来的额外好处有时候你甚至会发现自己不曾意识到有些事居然还可以这样干。
对于曾经使用非分散式版本管理的团队来说,保存代码的中央服务器可能是他们所不想舍去的。不过,虽然 Git 的确是分散式的架构设计,但是你依然可以设立一个官方的代码库来强制保存所有的修改。使用 Git 时,由于所有的开发者都拥有完整的代码库拷贝,所以他们的工作不会被中央服务器的性能甚至有无左右。即便他们下线或者在外,他们依然可以随时查看代码库的修改历史。得益于 Git 的分散式特性,你可以保持自己原有的工作方式但得到 Git 带来的额外好处,有时候你甚至会发现自己不曾意识到有些事居然还可以这样干。
现在你明白了什么是版本管理什么是Git以及为什么我们要选择使用Git接下来你可以选择阅读下一篇文章在那里我们将解释Git给团队和业务所带来的好处。
现在你明白了什么是版本管理,什么是 Git 以及为什么我们要选择使用 Git ,接下来你可以选择阅读下一篇文章,在那里我们将解释 Git 给团队和业务所带来的好处。
> 这篇文章是[**git-recipes**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:***Fork***:fork_and_knife:。
>
> 这篇文章是[**git-recipes**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2:***Fork***:fork_and_knife:。
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,16 +1,16 @@
# 检查仓库状态
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/inspecting-a-repository/git-log)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/inspecting-a-repository/git-log)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
## git status
`git status`命令显示工作目录和缓存区的状态。你可以看到哪些更改被缓存了哪些还没有以及哪些还未被Git追踪。status的输出 *不会* 告诉你任何已提交到项目历史的信息。如果你想看的话,应该使用`git log`命令。
`git status` 命令显示工作目录和缓存区的状态。你可以看到哪些更改被缓存了,哪些还没有,以及哪些还未被 Git 追踪。status 的输出 *不会* 告诉你任何已提交到项目历史的信息。如果你想看的话,应该使用 `git log` 命令。
### 用法
```
```
git status
```
@ -18,9 +18,9 @@ git status
### 讨论
`git status`是一个相对简单的命令。 它告诉你`git add`和`git commit`的进展。status信息还包括了添加缓存和移除缓存的相关指令。样例输出显示了三类主要的`git status`输出:
`git status` 是一个相对简单的命令。 它告诉你 `git add``git commit` 的进展。status 信息还包括了添加缓存和移除缓存的相关指令。样例输出显示了三类主要的 `git status` 输出:
```
```
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
@ -41,11 +41,11 @@ git status
#### 忽略文件
未追踪的文件通常有两类。它们要么是项目新增但还未提交的文件,要么是像`.pyc` `.obj`  `.exe`等编译后的二进制文件。显然前者应该出现在`git status`的输出中,而后者会让我们困惑究竟发生了什么。
未追踪的文件通常有两类。它们要么是项目新增但还未提交的文件,要么是像 `.pyc``.obj``.exe` 等编译后的二进制文件。显然前者应该出现在 `git status` 的输出中,而后者会让我们困惑究竟发生了什么。
因此Git允许你完全忽略这些文件只需要将路径放在一个特定的`.gitignore`文件中。所有想要忽略的文件应该分别写在单独一行,*字符用作通配符。比如,将下面这行加入项目根目录的`.gitignore`文件可以避免编译后的Python模块出现在`git status`中:
因此Git 允许你完全忽略这些文件,只需要将路径放在一个特定的 `.gitignore` 文件中。所有想要忽略的文件应该分别写在单独一行,`*` 字符用作通配符。比如,将下面这行加入项目根目录的`.gitignore`文件可以避免编译后的Python模块出现在`git status`中:
```
```
*.pyc
```
@ -53,7 +53,7 @@ git status
在提交更改前检查仓库状态是一个良好的实践,这样你就不会不小心提交什么奇怪的东西。这个例子显示了缓存和提交快照前后的仓库状态:
```
```
# Edit hello.py
git status
# hello.py is listed under "Changes not staged for commit"
@ -65,118 +65,113 @@ git status
# nothing to commit (working directory clean)
```
第一个status的输出显示文件还未缓存。`git add`操作会影响第二个`git status`最后的status输出告诉你已经没有可以提交的东西了——工作目录和最近的提交一致。一些Git命令比如`git merge`)需要工作目录整洁,以免意外覆盖更改。
第一个 status 的输出显示文件还未缓存。`git add` 操作会影响第二个 `git status`,最后的 status 输出告诉你已经没有可以提交的东西了——工作目录和最近的提交一致。一些 Git 命令(比如 `git merge`)需要工作目录整洁,以免意外覆盖更改。
## git log
`git log` 命令显示已提交的快照。你可以列出项目历史,筛选,以及搜索特定更改。`git status` 允许你查看工作目录和缓存区,而`git log`只作用于提交的项目历史。
`git log` 命令显示已提交的快照。你可以列出项目历史,筛选,以及搜索特定更改。`git status` 允许你查看工作目录和缓存区,而 `git log` 只作用于提交的项目历史。
![Git Tutorial: git status vs. git log](https://www.atlassian.com/git/images/tutorials/getting-started/inspecting-a-repository/01.svg)
log输出可以有很多种自定义的方式从简单地筛选提交到用完全自定义的格式显示。其中一些最常用的`git log`配置如下所示。
log 输出可以有很多种自定义的方式,从简单地筛选提交,到用完全自定义的格式显示。其中一些最常用的 `git log` 配置如下所示。
### 用法
```
```
git log
```
使用默认格式显示完整地项目历史。如果输出超过一屏,你可以用`空格键`来滚动,按`q`退出。
使用默认格式显示完整地项目历史。如果输出超过一屏,你可以用 `空格键` 来滚动,按 `q` 退出。
```
```
git log -n <limit>
```
用`<limit>`限制提交的数量。比如`git log -n 3`只会显示3个提交。
`<limit>` 限制提交的数量。比如 `git log -n 3` 只会显示 3 个提交。
```
```
git log --oneline
```
将每个提交压缩到一行。当你需要查看项目历史的上层情况时这会很有用。
```
```
git log --stat
```
除了`git log`信息之外,包含哪些文件被更改了,以及每个文件相对的增删行数。
除了 `git log` 信息之外,包含哪些文件被更改了,以及每个文件相对的增删行数。
```
```
git log -p
```
显示代表每个提交的一堆信息。显示每个提交全部的差异(diff),这也是项目历史中最详细的视图。
显示代表每个提交的一堆信息。显示每个提交全部的差异diff,这也是项目历史中最详细的视图。
```
```
git log --author="<pattern>"
```
搜索特定作者的提交。`<pattern>`可以是字符串或正则表达式。
搜索特定作者的提交。`<pattern>` 可以是字符串或正则表达式。
```
```
git log --grep="<pattern>"
```
搜索提交信息匹配特定`<pattern>`的提交。`<pattern>`可以是字符串或正则表达式。
搜索提交信息匹配特定 `<pattern>` 的提交。`<pattern>` 可以是字符串或正则表达式。
```
```
git log <since>..<until>
```
只显示发生在`<since>`和`<until>`之间的提交。两个参数可以是提交ID、分支名、`HEAD`或是任何一种引用。
只显示发生在 `<since>``<until>` 之间的提交。两个参数可以是提交 ID、分支名、`HEAD` 或是任何一种引用。
```
```
git log <file>
```
只显示包含特定文件的提交。查找特定文件的历史这样做会很方便。
```
```
git log --graph --decorate --oneline
```
还有一些有用的选项。`--graph`标记会绘制一幅字符组成的图形,左边是提交,右边是提交信息。`--decorate`标记会加上提交所在的分支名称和标签。`--oneline`标记将提交信息显示在同一行,一目了然。
还有一些有用的选项。`--graph` 标记会绘制一幅字符组成的图形,左边是提交,右边是提交信息。`--decorate` 标记会加上提交所在的分支名称和标签。`--oneline` 标记将提交信息显示在同一行,一目了然。
### 讨论
`git log`命令是Git查看项目历史的基本工具。当你要寻找项目特定的一个版本或者弄明白合并功能分支时引入了哪些变化你就会用到这个命令。
`git log` 命令是 Git 查看项目历史的基本工具。当你要寻找项目特定的一个版本或者弄明白合并功能分支时引入了哪些变化,你就会用到这个命令。
```
```
commit 3157ee3718e180a9476bf2e5cab8e3f1e78a73b7
Author: John Smith
```
大多数时候都很简单直接。但是,第一行需要解释下。`commit`后面40个字的字符串是提交内容的SHA-1校验总和(checksum)。它有两个作用。一是保证提交的正确性——如果它被损坏了提交会生成一个不同的校验总和。第二它是提交唯一的标识ID。
大多数时候都很简单直接。但是,第一行需要解释下。`commit` 后面 40 个字的字符串是提交内容的 SHA-1 校验总和checksum。它有两个作用。一是保证提交的正确性——如果它被损坏了,提交会生成一个不同的校验总和。第二,它是提交唯一的标识 ID。
这个ID可以用于`git log ..`这样的命令中来引用具体的提交。比如,`git log 3157e..5ab91`会显示所有ID在 `3157e`和`5ab91`之间的提交。除了校验总和之外分支名、HEAD关键字也是常用的引用提交的方法。`HEAD`总是指向当前的提交,无论是分支还是特定提交也好。
这个 ID 可以用于 `git log` 这样的命令中来引用具体的提交。比如,`git log 3157e..5ab91` 会显示所有ID在 `3157e``5ab91` 之间的提交。除了校验总和之外分支名、HEAD 关键字也是常用的引用提交的方法。`HEAD` 总是指向当前的提交,无论是分支还是特定提交也好。
~字符用于表示提交的父节点的相对引用。比如,`3157e~1`指向`3157e`前一个提交,`HEAD~3`是当前提交的回溯3个节点的提交。
~字符用于表示提交的父节点的相对引用。比如,`3157e~1` 指向 `3157e` 前一个提交,`HEAD~3` 是当前提交的回溯3个节点的提交。
所有这些标识方法的背后都是为了让你对特定提交进行操作。`git log`命令一般是这些交互的起点,因为它让你找到你想要的提交。
所有这些标识方法的背后都是为了让你对特定提交进行操作。`git log` 命令一般是这些交互的起点,因为它让你找到你想要的提交。
### 栗子
*用法* 一节提供了`git log`很多的栗子,但请记住,你可以将很多选项用在同一个命令中:
*用法* 一节提供了 `git log` 很多的栗子,但请记住,你可以将很多选项用在同一个命令中:
```
```
git log --author="John Smith" -p hello.py
```
这个命令会显示`John Smith`作者对`hello.py`文件所做的所有更改的差异比较(diff)
这个命令会显示 `John Smith` 作者对 `hello.py` 文件所做的所有更改的差异比较diff
..句法是比较分支很有用的工具。下面的栗子显示了在`some-feature`分支而不在`master`分支的所有提交的概览。
..句法是比较分支很有用的工具。下面的栗子显示了在 `some-feature` 分支而不在 `master` 分支的所有提交的概览。
```
```
git log --oneline master..some-feature
```
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:。
>
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,110 +1,99 @@
## 检出之前的提交
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/viewing-old-commits)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/viewing-old-commits)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
## git checkout
`git checkout`这个命令有三个不同的作用:检出文件、检出提交和检出分支。在这一章中,我们只关心前两种用法。
`git checkout` 这个命令有三个不同的作用:检出文件、检出提交和检出分支。在这一章中,我们只关心前两种用法。
检出提交会使工作目录和这个提交完全匹配。你可以用它来查看项目之前的状态,而不改变当前的状态。检出文件使你能够查看某个特定文件的旧版本,而工作目录中剩下的文件不变。
### 用法
```
```
git checkout master
```
回到master分支。分支会在下一节中讲到而现在你只需要将它视为回到项目当前状态的一种方式。
回到 master 分支。分支会在下一节中讲到,而现在,你只需要将它视为回到项目当前状态的一种方式。
```
```
git checkout <commit> <file>
```
查看文件之前的版本。它将工作目录中的`<file>`文件变成`<commit>`中那个文件的拷贝,并将它加入缓存区。
查看文件之前的版本。它将工作目录中的 `<file>` 文件变成 `<commit>` 中那个文件的拷贝,并将它加入缓存区。
```
```
git checkout <commit>
```
更新工作目录中的所有文件,使得和某个特定提交中的文件一致。你可以将提交的哈希字串,或是标签作为`<commit>`参数。这会使你处在分离HEAD的状态。
更新工作目录中的所有文件,使得和某个特定提交中的文件一致。你可以将提交的哈希字串,或是标签作为 `<commit>` 参数。这会使你处在分离HEAD的状态。
### 讨论
版本控制系统背后的思想就是安全地储存项目的拷贝,这样你永远不用担心什么时候不可复原地破坏了你的代码库。当你建立了项目历史之后,`git checkout`是一种便捷的方式,来将保存的快照加载到你的开发机器上去。
检出之前的提交是一个只读操作。在查看旧版本的时候绝不会损坏你的仓库。你项目『当前』的状态在 `master`上不会变化。在开发的正常阶段,`HEAD`一般指向master或是其他的本地分支但当你检出之前提交的时候`HEAD`就不再指向一个分支了——它直接指向一个提交。这被称为『分离`HEAD`』状态 ,可以用下图可视化:
版本控制系统背后的思想就是安全地储存项目的拷贝,这样你永远不用担心什么时候不可复原地破坏了你的代码库。当你建立了项目历史之后,`git checkout` 是一种便捷的方式,来将保存的快照加载到你的开发机器上去。
检出之前的提交是一个只读操作。在查看旧版本的时候绝不会损坏你的仓库。你项目「当前」的状态在 `master` 上不会变化。在开发的正常阶段,`HEAD` 一般指向 master 或是其他的本地分支,但当你检出之前提交的时候,`HEAD` 就不再指向一个分支了——它直接指向一个提交。这被称为「分离 `HEAD`」状态 ,可以用下图可视化:
![Git Tutorial: Checking out a previous commit](https://www.atlassian.com/git/images/tutorials/getting-started/viewing-old-commits/01.svg)
在另一方面,检出旧文件不影响你仓库的当前状态。你可以在新的快照中像其他文件一样重新提交旧版本。所以,在效果上,`git checkout`的这个用法可以用来将单个文件回滚到旧版本 。
在另一方面,检出旧文件不影响你仓库的当前状态。你可以在新的快照中像其他文件一样重新提交旧版本。所以,在效果上,`git checkout` 的这个用法可以用来将单个文件回滚到旧版本 。
![Git Training: Checking out a previous version of a file](https://www.atlassian.com/git/images/tutorials/getting-started/viewing-old-commits/02.svg)
### 栗子
#### 查看之前的版本
这个栗子假定你开始了一个疯狂的实验但你不确定你是否想要保留它。为了帮助你决定你想看一看你开始实验之前的项目状态。首先你需要找到你想要看的那个版本的ID。
这个栗子假定你开始了一个疯狂的实验,但你不确定你是否想要保留它。为了帮助你决定,你想看一看你开始实验之前的项目状态。首先,你需要找到你想要看的那个版本的 ID。
```
```
git log --oneline
```
假设你的项目历史看上去和下面一样:
```
```
b7119f2 继续做些丧心病狂的事
872fa7e 做些丧心病狂的事
a1e8fb5 对hello.py做了一些修改
435b61d 创建hello.py
a1e8fb5 对 hello.py 做了一些修改
435b61d 创建 hello.py
9773e52 初始导入
```
你可以这样使用`git checkout`来查看『对hello.py做了一些修改这个提交:
你可以这样使用 `git checkout` 来查看「对 hello.py 做了一些修改这个提交:
```
```
git checkout a1e8fb5
```
这让你的工作目录和`a1e8fb5`提交所处的状态完全一致。你可以查看文件,编译项目,运行测试,甚至编辑文件而不需要考虑是否会影响项目的当前状态。你所做的一切 *都不会* 被保存到仓库中。为了继续开发,你需要回到你项目的当前状态:
这让你的工作目录和 `a1e8fb5` 提交所处的状态完全一致。你可以查看文件,编译项目,运行测试,甚至编辑文件而不需要考虑是否会影响项目的当前状态。你所做的一切 *都不会* 被保存到仓库中。为了继续开发,你需要回到你项目的当前状态:
```
```
git checkout master
```
这里假定了你默认在master分支上开发我们会在以后的分支模型中详细讨论。
这里假定了你默认在 master 分支上开发,我们会在以后的分支模型中详细讨论。
一旦你回到master分支之后你可以使用 `git revert`或`git reset`来回滚任何不想要的更改。
一旦你回到 master 分支之后,你可以使用 `git revert``git reset` 来回滚任何不想要的更改。
#### 检出文件
如果你只对某个文件感兴趣,你也可以用`git checkout`来获取它的一个旧版本。比如说,如果你只想从之前的提交中查看`hello.py`文件,你可以使用下面的命令:
如果你只对某个文件感兴趣,你也可以用 `git checkout` 来获取它的一个旧版本。比如说,如果你只想从之前的提交中查看 `hello.py` 文件,你可以使用下面的命令:
```
```
git checkout a1e8fb5 hello.py
```
记住,和检出提交不同,这里 *确实* 会影响你项目的当前状态。旧的文件版本会显示为需要提交的更改,允许你回滚到文件之前的版本。如果你不想保留旧的版本,你可以用下面的命令检出到最近的版本:
记住,和检出提交不同,这里 *确实* 会影响你项目的当前状态。旧的文件版本会显示为需要提交的更改,允许你回滚到文件之前的版本。如果你不想保留旧的版本,你可以用下面的命令检出到最近的版本:
```
```
git checkout HEAD hello.py
```
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:。
>
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。

View File

@ -1,26 +1,26 @@
# 重写项目历史
> BY 童仲毅([geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/))
>
> 这是一篇在[原文(BY atlassian)](https://www.atlassian.com/git/tutorials/rewriting-history)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享。
> BY 童仲毅[geeeeeeeeek@github](https://github.com/geeeeeeeeek/git-recipes/)
>
> 这是一篇在[原文BY atlassian](https://www.atlassian.com/git/tutorials/rewriting-history)基础上演绎的译文。除非另行注明,页面上所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享。
## 概述
GIt的主要职责是保证你不会丢失提交的修改。但是它同样被设计成让你完全掌控开发工作流。这包括了让你自定义你的项目历史而这也创造了丢失提交的可能性。Git提供了可以重写项目历史的命令但也警告你这些命令可能会让你丢失内容。
Git 的主要职责是保证你不会丢失提交的修改。但是它同样被设计成让你完全掌控开发工作流。这包括了让你自定义你的项目历史而这也创造了丢失提交的可能性。Git 提供了可以重写项目历史的命令,但也警告你这些命令可能会让你丢失内容。
这份教程讨论了重写提交快照的一些常见原因,并告诉你如何避免不好的影响。
## git commit --amend
`git commit --amend`命令是修复最新提交的便捷方式。它允许你将缓存的修改和之前的提交合并到一起,而不是提交一个全新的快照。它还可以用来简单地编辑上一次提交的信息而不改变快照。
`git commit --amend` 命令是修复最新提交的便捷方式。它允许你将缓存的修改和之前的提交合并到一起,而不是提交一个全新的快照。它还可以用来简单地编辑上一次提交的信息而不改变快照。
![Git Tutorial: git commit --amend](https://www.atlassian.com/git/images/tutorials/getting-started/rewriting-history/01.svg)
但是amend不只是修改了最新的提交——它进行了一次替换。对于Git来说这看上去像一个全新的提交即上图中用星号表示的那一个。在公共仓库工作时一定要牢记这一点。
但是amend 不只是修改了最新的提交——它进行了一次替换。对于 Git 来说,这看上去像一个全新的提交,即上图中用星号表示的那一个。在公共仓库工作时一定要牢记这一点。
### 用法
```
```
git commit --amend
```
@ -28,7 +28,7 @@ git commit --amend
### 讨论
仓促的提交在你日常开发过程中时常会发生。很容易就忘记了缓存一个文件或者弄错了提交信息的格式。`--amend`标记是修复这些小意外的便捷方式。
仓促的提交在你日常开发过程中时常会发生。很容易就忘记了缓存一个文件或者弄错了提交信息的格式。`--amend` 标记是修复这些小意外的便捷方式。
#### 不要修复公共提交
@ -38,9 +38,9 @@ git commit --amend
### 栗子
下面这个🌰展示了Git开发工作流中的一个常见情形。我们编辑了一些希望在同一个快照中提交的文件但我们忘记添加了其中的一个。修复错误只需要缓存那个文件并且用`--amend`标记提交:
下面这个🌰展示了 Git 开发工作流中的一个常见情形。我们编辑了一些希望在同一个快照中提交的文件,但我们忘记添加了其中的一个。修复错误只需要缓存那个文件并且用 `--amend` 标记提交:
```
```
# 编辑 hello.py 和 main.py
git add hello.py
git commit
@ -50,62 +50,54 @@ git add main.py
git commit --amend --no-edit
```
编辑器会弹出上一次提交的信息,加入`--no-edit`标记会修复提交但不修改提交信息。需要的话你可以修改,不然的话就像往常一样保存并关闭文件。完整的提交会替换之前不完整的提交,看上去就像我们在同一个快照中提交了`hello.py`和`main.py`。
编辑器会弹出上一次提交的信息,加入 `--no-edit` 标记会修复提交但不修改提交信息。需要的话你可以修改,不然的话就像往常一样保存并关闭文件。完整的提交会替换之前不完整的提交,看上去就像我们在同一个快照中提交了 `hello.py``main.py`
## git rebase
变基(rebase, 事实上这个名字十分诡异, 所以在大多数时候直接用英文术语)是将分支移到一个新的基提交的过程。过程一般如下所示:
变基rebase, 事实上这个名字十分诡异, 所以在大多数时候直接用英文术语是将分支移到一个新的基提交的过程。过程一般如下所示:
![Git Tutorial: Rebase to maintain a linear project history.](https://www.atlassian.com/git/images/tutorials/getting-started/rewriting-history/02.svg)
从内容的角度来看rebase只不过是将分支从一个提交移到了另一个。但从内部机制来看Git是通过在选定的基上创建新提交来完成这件事的——它事实上重写了你的项目历史。理解这一点很重要尽管分支看上去是一样的但它包含了全新的提交。
从内容的角度来看rebase 只不过是将分支从一个提交移到了另一个。但从内部机制来看Git 是通过在选定的基上创建新提交来完成这件事的——它事实上重写了你的项目历史。理解这一点很重要,尽管分支看上去是一样的,但它包含了全新的提交。
### 用法
```
```
git rebase <base>
```
将当前分支rebase到`<base>`这里可以是任何类型的提交引用ID、分支名、标签或是`HEAD`的相对引用)。
将当前分支 rebase`<base>`这里可以是任何类型的提交引用ID、分支名、标签或是 `HEAD` 的相对引用)。
### 讨论
rebase的主要目的是为了保持一个线性的项目历史。比如说当你在feature分支工作时master分支取得了一些进展
rebase 的主要目的是为了保持一个线性的项目历史。比如说,当你在 feature 分支工作时 master 分支取得了一些进展:
![Git Rebase Branch onto Master](https://www.atlassian.com/git/images/tutorials/getting-started/rewriting-history/03.svg)
要将你的feature分支整合进`master`分支你有两个选择直接merge或者先rebase后merge。前者会产生一个三路合并(3-way merge)和一个合并提交而后者产生的是一个快速向前的合并以及完美的线性历史。下图展示了为什么rebase到`master`分支会促成一个快速向前的合并。
要将你的 feature 分支整合进 `master` 分支,你有两个选择:直接 merge或者先 rebase 后 merge。前者会产生一个三路合并3-way merge和一个合并提交而后者产生的是一个快速向前的合并以及完美的线性历史。下图展示了为什么 rebase 到 `master` 分支会促成一个快速向前的合并。
![Git Tutorial: Fast-forward merge](https://www.atlassian.com/git/images/tutorials/getting-started/rewriting-history/04.svg)
rebase是将上游更改合并进本地仓库的通常方法。你每次想查看上游进展时用`git merge`拉取上游更新会导致一个多余的合并提交。在另一方面rebase就好像是说:“我想将我的更改建立在其他人的进展之上。
rebase 是将上游更改合并进本地仓库的通常方法。你每次想查看上游进展时,用 `git merge` 拉取上游更新会导致一个多余的合并提交。在另一方面rebase 就好像是说我想将我的更改建立在其他人的进展之上
#### 不要rebase公共历史
#### 不要 rebase 公共历史
和我们讨论过的`git commit --amend`和`git reset`一样你永远不应该rebase那些已经推送到公共仓库的提交。rebase会用新的提交替换旧的提交你的项目历史会像突然消失了一样。
和我们讨论过的 `git commit --amend``git reset` 一样,你永远不应该 rebase 那些已经推送到公共仓库的提交。rebase 会用新的提交替换旧的提交,你的项目历史会像突然消失了一样。
### 栗子
下面这个🌰同时使用git rebasegit merge来保持线性的项目历史。这是一个确认你的合并都是快速向前的方法。
下面这个🌰同时使用 git rebasegit merge 来保持线性的项目历史。这是一个确认你的合并都是快速向前的方法。
```
```
# 开始新的功能分支
git checkout -b new-feature master
# 编辑文件
git commit -a -m "Start developing a feature"
```
在feature分支开发了一半的时候我们意识到项目中有一个安全漏洞:
feature 分支开发了一半的时候,我们意识到项目中有一个安全漏洞:
```
```
# 基于master分支创建一个快速修复分支
git checkout -b hotfix master
# 编辑文件
@ -116,43 +108,43 @@ git merge hotfix
git branch -d hotfix
```
将hotfix分支并回之后master我们有了一个分叉的项目历史。我们用rebase整合feature分支以获得线性的历史而不是使用普通的git merge。
hotfix 分支并回之后 master我们有了一个分叉的项目历史。我们用 rebase 整合 feature 分支以获得线性的历史,而不是使用普通的 git merge。
```
```
git checkout new-feature
git rebase master
```
它将new-feature分支移到了master分支的末端现在我们可以在master上进行标准的快速向前合并了:
它将 new-feature 分支移到了 master 分支的末端,现在我们可以在 master 上进行标准的快速向前合并了:
```
```
git checkout master
git merge new-feature
```
## git rebase -i
用`-i`标记运行`git rebase`开始交互式rebase。交互式rebase给你在过程中修改单个提交的机会而不是盲目地将所有提交都移到新的基上。你可以移除、分割提交更改提交的顺序。它就像是打了鸡血的`git commit --amend`一样。
`-i` 标记运行 `git rebase` 开始交互式 rebase。交互式 rebase 给你在过程中修改单个提交的机会,而不是盲目地将所有提交都移到新的基上。你可以移除、分割提交,更改提交的顺序。它就像是打了鸡血的 `git commit --amend` 一样。
### 用法
```
```
git rebase -i <base>
```
将当前分支rebase到`base`但使用可交互的形式。它会打开一个编辑器你可以为每个将要rebase的提交输入命令见后文。这些命令决定了每个提交将会怎样被转移到新的基上去。你还可以对这些提交进行排序。
将当前分支 rebase`base`,但使用可交互的形式。它会打开一个编辑器,你可以为每个将要 rebase 的提交输入命令(见后文)。这些命令决定了每个提交将会怎样被转移到新的基上去。你还可以对这些提交进行排序。
### 讨论
交互式rebase给你了控制项目历史的完全掌控。它给了开发人员很大的自由因为他们可以提交一个混乱的历史而只需专注于写代码,然后回去恢复干净。
交互式 rebase 给你了控制项目历史的完全掌控。它给了开发人员很大的自由,因为他们可以提交一个混乱的历史而只需专注于写代码,然后回去恢复干净。
大多数开发者喜欢在并入主代码库之前用交互式rebase来完善他们的feature分支。他们可以将不重要的提交合在一起删除不需要的确保所有东西在提交到正式的项目历史前都是整齐的。对其他人来说,这个功能的开发看上去是由一系列精心安排的提交组成的。
大多数开发者喜欢在并入主代码库之前用交互式 rebase 来完善他们的 feature 分支。他们可以将不重要的提交合在一起,删除不需要的,确保所有东西在提交到正式的项目历史前都是整齐的。对其他人来说,这个功能的开发看上去是由一系列精心安排的提交组成的。
###
###
下面这个🌰是`非交互式rebae`一节中🌰的可交互升级版本。
下面这个🌰是 `非交互式rebae` 一节中🌰的可交互升级版本。
```
```
# 开始新的功能分支
git checkout -b new-feature master
# 编辑文件
@ -160,95 +152,91 @@ git commit -a -m "Start developing a feature"
# 编辑更多文件
git commit -a -m "Fix something from the previous commit"
# 直接在master上添加文件
# 直接在 master 上添加文件
git checkout master
# 编辑文件
git commit -a -m "Fix security hole"
# 开始交互式rebase
# 开始交互式 rebase
git checkout new-feature
git rebase -i master
```
最后的那个命令会打开一个编辑器包含new-feature的两个提交和一些指示
最后的那个命令会打开一个编辑器,包含 new-feature 的两个提交,和一些指示:
```
```
pick 32618c4 Start developing a feature
pick 62eed47 Fix something from the previous commit
```
你可以更改每个提交前的pick命令来决定在rebase时提交移动的方式。在我们的例子中我们只需要用squash命令把两个提交并在一起就可以了
你可以更改每个提交前的 pick 命令来决定在 rebase 时提交移动的方式。在我们的例子中,我们只需要用 squash 命令把两个提交并在一起就可以了:
```
```
pick 32618c4 Start developing a feature
squash 62eed47 Fix something from the previous commit
```
保存并关闭编辑器以开始rebase。另一个编辑器会打开询问你合并后的快照的提交信息。在定义了提交信息之后rebase就完成了你可以在`git log`输出中看到那个提交。整个过程可以用下图可视化:
保存并关闭编辑器以开始 rebase。另一个编辑器会打开询问你合并后的快照的提交信息。在定义了提交信息之后rebase 就完成了,你可以在 `git log` 输出中看到那个提交。整个过程可以用下图可视化:
![Git Tutorial: git rebase -i example](https://www.atlassian.com/git/images/tutorials/getting-started/rewriting-history/05.svg)
注意缩并的提交和原来的两个提交的ID都不一样告诉我们这确实是个新的提交。
注意缩并的提交和原来的两个提交的 ID 都不一样,告诉我们这确实是个新的提交。
最后你可以执行一个快速向前的合并来将完善的feature分支整合进主代码库
最后,你可以执行一个快速向前的合并,来将完善的 feature 分支整合进主代码库:
```
```
git checkout master
git merge new-feature
```
交互式rebase强大的能力可以从整合后的master分支看出——额外的`62eed47`提交找不到了。对其他人来说,你就像是一个天才,用完美数量的提交完成了`new-feature`。这就是交互式提交如何保持项目历史干净和合意。
交互式 rebase 强大的能力可以从整合后的 master 分支看出——额外的 `62eed47` 提交找不到了。对其他人来说,你就像是一个天才,用完美数量的提交完成了 `new-feature`。这就是交互式提交如何保持项目历史干净和合意。
## git reflog
Git用引用日志这种机制来记录分支顶端的更新。它允许你回到那些不被任何分支或标签引用的更改。在重写历史后引用日志包含了分支旧状态的信息有需要的话你可以回到这个状态。
Git 用引用日志这种机制来记录分支顶端的更新。它允许你回到那些不被任何分支或标签引用的更改。在重写历史后,引用日志包含了分支旧状态的信息,有需要的话你可以回到这个状态。
### 用法
```
```
git reflog
```
显示本地仓库的引用日志。
```
```
git reflog --relative-date
```
用相对的日期显示引用日志。(如2周前)。
用相对的日期显示引用日志。(如 2 周前)。
### 讨论
每次当前的HEAD更新时如切换分支、拉取新更改、重写历史或只是添加新的提交引用日志都会添加一个新条目。
每次当前的 HEAD 更新时(如切换分支、拉取新更改、重写历史或只是添加新的提交),引用日志都会添加一个新条目。
### 栗子
为了理解`git reflog`,我们来看一个🌰。
为了理解 `git reflog`,我们来看一个🌰。
```
```
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
c10f740 HEAD@{2}: checkout: moving from master to 2.2
```
上面的引用日志显示了master2.2 branch之间的相互切换。还有对一个更老的提交的强制重设。最近的活动用`HEAD@{0}`标记在上方显示。
上面的引用日志显示了 master2.2 branch 之间的相互切换。还有对一个更老的提交的强制重设。最近的活动用 `HEAD@{0}` 标记在上方显示。
如果事实上你是不小心切换回去的引用日志包含了你意外地丢掉两个提交之前master指向的提交0254ea7。
如果事实上你是不小心切换回去的,引用日志包含了你意外地丢掉两个提交之前 master 指向的提交 0254ea7。
```
```
git reset --hard 0254ea7
```
使用[`git reset`](https://github.com/geeeeeeeeek/git-recipes/wiki/2.6-%E5%9B%9E%E6%BB%9A%E9%94%99%E8%AF%AF%E7%9A%84%E4%BF%AE%E6%94%B9#git-reset)就有可能能将master变回之前的那个提交。它提供了一张安全网以防历史发生意外更改。
使用 [`git reset`](https://github.com/geeeeeeeeek/git-recipes/wiki/2.6-%E5%9B%9E%E6%BB%9A%E9%94%99%E8%AF%AF%E7%9A%84%E4%BF%AE%E6%94%B9#git-reset)就有可能能将master变回之前的那个提交。它提供了一张安全网以防历史发生意外更改。
务必记住,引用日志提供的安全网只对提交到本地仓库的更改有效,而且只有移动操作会被记录。
> 这篇文章是[**『git-recipes』**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:或***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
> 这篇文章是[**「git-recipes」**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
>
> 如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2: 或 ***Fork***:fork_and_knife:
>
> 如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)

View File

@ -1,19 +1,19 @@
git-recipes
====
高质量的Git中文教程来自国外社区的优秀文章和个人实践
高质量的 Git 中文教程,来自国外社区的优秀文章和个人实践
目录
---
git-recipes的所有文章都以Wiki的方式呈现请移步[项目Wiki](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看目录和详细内容。
git-recipes 的所有文章都以 Wiki 的方式呈现,请移步[项目Wiki](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看目录和详细内容。
我为什么要做这份菜单
---
在整理Git资料的时候我发现社区贡献了非常多高质量的博客文章、指南等等。尤其英文的那些资料除了大家熟知的《Git图解》还有好多优秀的文章仍无人翻译。此外这些资料往往只涉及某些特定的话题如果能有一份菜单将这些菜谱以特定的方式串起来那么对于Git学习者来说将会是极大的便利。尤其对于我这样热爱查阅社区资料胜过出版物的懒人 : ) 随着我的学习节奏还会不断有新的菜谱加入进来,或许不会很频繁,不过也没有确定的终点。
在整理 Git 资料的时候我发现社区贡献了非常多高质量的博客文章、指南等等。尤其英文的那些资料除了大家熟知的《Git 图解》,还有好多优秀的文章仍无人翻译。此外,这些资料往往只涉及某些特定的话题,如果能有一份菜单将这些菜谱以特定的方式串起来,那么对于 Git 学习者来说将会是极大的便利。尤其对于我这样热爱查阅社区资料胜过出版物的懒人 :] 随着我的学习节奏还会不断有新的菜谱加入进来,或许不会很频繁,不过也没有确定的终点。
版权说明
---
除非另行注明,这个项目中的所有内容采用知识共享-署名([CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh))协议共享,童仲毅(geeeeeeeeek@github)版权所有。
除非另行注明,这个项目中的所有内容采用知识共享-署名[CC BY 2.5 AU](http://creativecommons.org/licenses/by/2.5/au/deed.zh)协议共享,童仲毅geeeeeeeeek@github版权所有。
其中不少资料是在原基础上翻译或演绎而来的,我都在页面最上方标明了原作者、原文链接以及原文采用的协议。如仍有版权疑问,请在issue中提出。
其中不少资料是在原基础上翻译或演绎而来的,我都在页面最上方标明了原作者、原文链接以及原文采用的协议。如仍有版权疑问,请在 Issue 中提出。
欢迎通过issue或者pr推荐你认为合适的资料,让这份菜单更充实一些。
欢迎通过 Issue 或者 Pull Request 推荐你认为合适的资料,让这份菜单更充实一些。

View File

@ -4,18 +4,18 @@
**第2篇 从零搭建本地代码仓库**
本篇完全面向入门者。我假设你从零开始创建一个项目并且想用 Git 来进行版本控制,我们会讨论如何在你的个人项目中使用 Git ,比如如何初始化你的项目,如何管理新的或者已有的文件,如何在远端仓库中储存你的代码。
本篇完全面向入门者。我假设你从零开始创建一个项目并且想用 Git 来进行版本控制,我们会讨论如何在你的个人项目中使用 Git比如如何初始化你的项目如何管理新的或者已有的文件如何在远端仓库中储存你的代码。
- **第1章** [快速指南](https://github.com/geeeeeeeeek/git-recipes/wiki/2.1-%E5%BF%AB%E9%80%9F%E6%8C%87%E5%8D%97)
- **第2章** [创建代码仓库](https://github.com/geeeeeeeeek/git-recipes/wiki/2.2-%E5%88%9B%E5%BB%BA%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93)
- **第3章** [保存你的更改](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)
- **第4章** [检查仓库状态](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)
- **第5章** [检出之前的提交](https://github.com/geeeeeeeeek/git-recipes/wiki/2.5-%E6%A3%80%E5%87%BA%E4%B9%8B%E5%89%8D%E7%9A%84%E6%8F%90%E4%BA%A4)
- **第5章** [检出之前的提交](https://github.com/geeeeeeeeek/git-recipes/wiki/2.5-%E6%A3%80%E5%87%BA%E4%B9%8B%E5%89%8D%E7%9A%84%E6%8F%90%E4%BA%A4)
- **第6章** [回滚错误的修改](https://github.com/geeeeeeeeek/git-recipes/wiki/2.6-%E5%9B%9E%E6%BB%9A%E9%94%99%E8%AF%AF%E7%9A%84%E4%BF%AE%E6%94%B9)
- **第7章** [重写项目历史](https://github.com/geeeeeeeeek/git-recipes/wiki/2.7-%E9%87%8D%E5%86%99%E9%A1%B9%E7%9B%AE%E5%8E%86%E5%8F%B2)
**第3篇 远程团队协作和管理**
- **第1章** 快速指南
- **第1章** 快速指南
- **第2章** [保持同步](https://github.com/geeeeeeeeek/git-recipes/wiki/3.2-%E4%BF%9D%E6%8C%81%E5%90%8C%E6%AD%A5)
- **第3章** [创建 Pull Request](https://github.com/geeeeeeeeek/git-recipes/wiki/3.3-%E5%88%9B%E5%BB%BAPull-Request)
- **第4章** [使用分支](https://github.com/geeeeeeeeek/git-recipes/wiki/3.4-%E4%BD%BF%E7%94%A8%E5%88%86%E6%94%AF)
@ -25,26 +25,26 @@
- **第1章** [图解 Git 命令](https://github.com/geeeeeeeeek/git-recipes/wiki/4.1-%E5%9B%BE%E8%A7%A3Git%E5%91%BD%E4%BB%A4)
如果你稍微理解git的工作原理这篇文章能够让你理解的更透彻。
如果你稍微理解 Git 的工作原理,这篇文章能够让你理解的更透彻。
**第5篇 Git 实用贴士**
- **第1章** [代码合并: Merge 、 Rebase 的选择](https://github.com/geeeeeeeeek/git-recipes/wiki/5.1-%E4%BB%A3%E7%A0%81%E5%90%88%E5%B9%B6%EF%BC%9AMerge%E3%80%81Rebase%E7%9A%84%E9%80%89%E6%8B%A9)
`git rebase``git merge` 都是用来合并分支,只不过方式不太相同。`git rebase` 经常被人认为是一种 Git 巫术初学者应该避而远之。但如果使用得当它能省去太多烦恼。在这篇文章中我们会通过比较找到Git工作流中所有可以使用rebase的机会。
`git rebase``git merge` 都是用来合并分支,只不过方式不太相同。`git rebase` 经常被人认为是一种 Git 巫术,初学者应该避而远之。但如果使用得当,它能省去太多烦恼。在这篇文章中,我们会通过比较找到 Git 工作流中所有可以使用 rebase 的机会。
- **第2章** [代码回滚: ResetCheckoutRevert 的选择](https://github.com/geeeeeeeeek/git-recipes/wiki/5.2-%E4%BB%A3%E7%A0%81%E5%9B%9E%E6%BB%9A%EF%BC%9AReset%E3%80%81Checkout%E3%80%81Revert%E7%9A%84%E9%80%89%E6%8B%A9)
- **第2章** [代码回滚: ResetCheckoutRevert 的选择](https://github.com/geeeeeeeeek/git-recipes/wiki/5.2-%E4%BB%A3%E7%A0%81%E5%9B%9E%E6%BB%9A%EF%BC%9AReset%E3%80%81Checkout%E3%80%81Revert%E7%9A%84%E9%80%89%E6%8B%A9)
git resetgit checkout 和 git revert 都是用来撤销代码仓库中的某些更改,所以我们经常弄混。在这篇文章中,我们比较最常见的用法,分析在什么场景下该用哪个命令。
git resetgit checkout 和 git revert 都是用来撤销代码仓库中的某些更改,所以我们经常弄混。在这篇文章中,我们比较最常见的用法,分析在什么场景下该用哪个命令。
- **第3章** [Git log 高级用法](https://github.com/geeeeeeeeek/git-recipes/wiki/5.3-Git-log%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95)
任何一个版本控制系统设计的目的都是为了记录你代码的变化——谁贡献了什么找出bug是什么时候引入的以及撤回一些有问题的更改。`git log` 可以格式化 commit 输出的形式,或过滤输出的 commit 从而找到项目中你需要的任何信息。
任何一个版本控制系统设计的目的都是为了记录你代码的变化——谁贡献了什么,找出 bug 是什么时候引入的,以及撤回一些有问题的更改。`git log` 可以格式化 commit 输出的形式,或过滤输出的 commit 从而找到项目中你需要的任何信息。
- **第4章** [Git 钩子:自定义你的工作流](https://github.com/geeeeeeeeek/git-recipes/wiki/5.4-Git%E9%92%A9%E5%AD%90%EF%BC%9A%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BD%A0%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81)
Git 钩子是在 Git 仓库中特定事件发生时自动运行的脚本。它可以让你自定义Git内部的行为在开始周期中的关键点触发自定义的行为自动化或者优化你开发工作流中任意部分。
Git 钩子是在 Git 仓库中特定事件发生时自动运行的脚本。它可以让你自定义 Git 内部的行为,在开始周期中的关键点触发自定义的行为,自动化或者优化你开发工作流中任意部分。
- **第5章** [Git 提交引用和引用日志](https://github.com/geeeeeeeeek/git-recipes/wiki/5.5-Git%E6%8F%90%E4%BA%A4%E5%BC%95%E7%94%A8%E5%92%8C%E5%BC%95%E7%94%A8%E6%97%A5%E5%BF%97)
提交是 Git 的精髓所在你无时不刻不在创建和缓存提交、查看以前的提交或者用各种Git命令在仓库间转移你的提交。在这章中我们研究提交的各种引用方式以及涉及到的 Git 命令的工作原理。我们还会学到如何使用 Git 的引用日志查看看似已经删除的提交。
提交是 Git 的精髓所在,你无时不刻不在创建和缓存提交、查看以前的提交,或者用各种 Git 命令在仓库间转移你的提交。在这章中,我们研究提交的各种引用方式,以及涉及到的 Git 命令的工作原理。我们还会学到如何使用 Git 的引用日志查看看似已经删除的提交。

View File

@ -1,5 +1,5 @@
这篇文章是[**git-recipes**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
这篇文章是[**git-recipes**](https://github.com/geeeeeeeeek/git-recipes/)的一部分,点击[**目录**](https://github.com/geeeeeeeeek/git-recipes/wiki/)查看所有章节。
如果你觉得文章对你有帮助,欢迎点击右上角的***Star***:star2:​或***Fork***:fork_and_knife:​。
如果你觉得文章对你有帮助,欢迎点击右上角的 ***Star***:star2:***Fork***:fork_and_knife:​。
如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。
如果你发现了错误,或是想要加入协作,请参阅[Wiki协作说明](https://github.com/geeeeeeeeek/git-recipes/issues/1)。