读书笔记 | GitHub入门与实践2
本篇是 <<GitHub入门与实践>> 第4-7章节的笔记
第四章 通过实际操作学习Git
在本章中,我们将学习Git 相关的基本知识与操作方法。已经将Git实际运用于开发的读者大可跳过本章。本章中将要解说的,是理解本书内容所必不可少的一些Git 操作。请随着我们的解说,一边实际操作,一边学习并掌握Git。
基本操作
git init——初始化仓库
git status——查看仓库的状态
$ git status
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)结果显示了我们当前正处于master 分支下。关于分支我们会在不久后讲到,现在不必深究。接着还显示了没有可提交的内容。所谓提交(Commit),是指“记录工作树中所有文件的当前状态”。
尚没有可提交的内容,就是说当前我们建立的这个仓库中还没有记录任何文件的任何状态。这里,我们建立README.md 文件作为管理对象,为第一次提交做前期准备。
$ touch README.md
$ git status
# On branch master
#
# Initial commit
## Untracked files:# (use "git add <file>..." to include in what will
be committed)#
# README.md
nothing added to commit but untracked files present (use "git add" to
track)git add——向暂存区中添加文件
$ git add README.md
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md
#将README.md 文件加入暂存区后,git status命令的显示结果发生了变化。可以看到,README.md 文件显示在Changes to becommitted 中了。
git commit——保存仓库的历史记录
git commit命令可以将当前暂存区中的文件实际保存到仓库的历史记录中。通过这些记录,我们就可以在工作树中复原文件。
记述一行提交信息
我们来实际运行一下git commit命令。
$ git commit -m "First commit"
[master (root-commit) 9f129ba] First commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README.md-m 参数后的”First commit”称作提交信息,是对这个提交的概述。
记述详细提交信息
刚才我们只简洁地记述了一行提交信息,如果想要记述得更加详细,请不加-m,直接执行git commit命令。执行后编辑器就会启动,并显示如下结果。
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md
#在编辑器中记述提交信息的格式如下。
● 第一行:用一行文字简述提交的更改内容
● 第二行:空行
● 第三行以后:记述更改的原因和详细内容只要按照上面的格式输入,今后便可以通过确认日志的命令或工具看到这些记录。在以#(井号)标为注释的Changes to be committed(要提交的更改)栏中,可以查看本次提交中包含的文件。将提交信息按格式记述完毕后,请保存并关闭编辑器,以#(井号)标为注释的行不必删除。随后,刚才记述的提交信息就会被提交。
中止提交
如果在编辑器启动后想中止提交,请将提交信息留空并直接关闭编辑器,随后提交就会被中止。
查看提交后的状态
执行完git commit命令后再来查看当前状态。
$ git status
# On branch master
nothing to commit, working directory clean当前工作树处于刚刚完成提交的最新状态,所以结果显示没有更改。
git log——查看提交日志
git log命令可以查看以往仓库中提交的日志。包括可以查看什么人在什么时候进行了提交或合并,以及操作前后有怎样的差别。关于合并我们会在后面解说。
我们先来看看刚才的git commit命令是否被记录了。
$ git log
commit 9f129bae19b2c82fb4e98cde5890e52a6c546922
Author: hirocaster <hohtsuka@gmail.com>
Date: Sun May 5 16:06:49 2013 +0900
First commit只显示提交信息的第一行
如果只想让程序显示第一行简述信息,可以在git log命令后加上–pretty=short。这样一来开发人员就能够更轻松地把握多个提交。
$ git log --pretty=short
commit 9f129bae19b2c82fb4e98cde5890e52a6c546922
Author: hirocaster <hohtsuka@gmail.com>
First commit只显示指定目录、文件的日志
只要在git log命令后加上目录名,便会只显示该目录下的日志。如果加的是文件名,就会只显示与该文件相关的日志。
$ git log README.md
显示文件的改动
如果想查看提交所带来的改动,可以加上-p参数,文件的前后差别就会显示在提交信息之后。
$ git log -p
git diff——查看更改前后的差别
git diff命令可以查看工作树、暂存区、最新提交之间的差别。单从字面上可能很难理解,各位不妨跟着笔者的解说亲手试一试。我们在刚刚提交的README.md 中写点东西。
# Git教程
查看工作树和暂存区的差别
执行git diff命令,查看当前工作树与暂存区的差别。
$ git diff
diff --git a/README.md b/README.md
index e69de29..cb5dc9f 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# Git教程由于我们尚未用git add命令向暂存区添加任何东西,所以程序只会显示工作树与最新提交状态之间的差别。
查看工作树和最新提交的差别
如果现在执行git diff命令,由于工作树和暂存区的状态并无差别,结果什么都不会显示。要查看与最新提交的差别,请执行以下命令。
$ git diff HEAD
diff --git a/README.md b/README.md
index e69de29..cb5dc9f 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# Git教程不妨养成这样一个好习惯:在执行git commit命令之前先执行git diff HEAD命令,查看本次提交与上次提交之间有什么差别,等确认完毕后再进行提交。这里的HEAD 是指向当前分支中最新一次提交的指针。
由于我们刚刚确认过两个提交之间的差别,所以直接运行git commit命令。
$ git commit -m "Add index"
[master fd0cbf0] Add index
1 file changed, 1 insertion(+)保险起见,我们查看一下提交日志,确认提交是否成功。
$ git log
commit fd0cbf0d4a25f747230694d95cac1be72d33441d
Author: hirocaster <hohtsuka@gmail.com>
Date: Sun May 5 16:10:15 2013 +0900
Add index
commit 9f129bae19b2c82fb4e98cde5890e52a6c546922
Author: hirocaster <hohtsuka@gmail.com>
Date: Sun May 5 16:06:49 2013 +0900
First commit成功查到了第二个提交。
小结
…or create a new repository on the command line
echo "# Git" >> README.md |
…or push an existing repository from the command line
git remote add origin https://github.com/fanygOfficial/Git.git |
分支的操作
在进行多个并行作业时,我们会用到分支。在这类并行开发的过程中,往往同时存在多个最新代码状态。如图4.1 所示,从master 分支创建feature-A 分支和fix-B 分支后,每个分支中都拥有自己的最新代码。master 分支是Git 默认创建的分支,因此基本上所有开发都是以这个分支为中心进行的。

git branch——显示分支一览表
git branch命令可以将分支名列表显示,同时可以确认当前所在分支。让我们来实际运行git branch命令。
$ git branch |
可以看到master 分支左侧标有“*”(星号),表示这是我们当前所在的分支。也就是说,我们正在master 分支下进行开发。结果中没有显示其他分支名,表示本地仓库中只存在master 一个分支。
git checkout -b——创建、切换分支
如果想以当前的master 分支为基础创建新的分支,我们需要用到git checkout -b命令。
切换到feature-A 分支并进行提交
执行下面的命令,创建名为feature-A 的分支。
$ git checkout -b feature-A
Switched to a new branch 'feature-A'实际上,连续执行下面两条命令也能收到同样效果。
$ git branch feature-A
$ git checkout feature-A创建feature-A 分支,并将当前分支切换为feature-A 分支。这时再来查看分支列表,会显示我们处于feature-A 分支下。
$ git branch
* feature-A
masterfeature-A 分支左侧标有“*”,表示当前分支为feature-A。在这个状态下像正常开发那样修改代码、执行git add命令并进行提交的话,代码就会提交至feature-A 分支。像这样不断对一个分支(例如feature-A)进行提交的操作,我们称为“培育分支”。
下面来实际操作一下。在README.md 文件中添加一行。
# Git教程
- feature-A这里我们添加了feature-A 这样一行字母,然后进行提交。
$ git add README.md
$ git commit -m "Add feature-A"
[feature-A 8a6c8b9] Add feature-A
1 file changed, 2 insertions(+)于是,这一行就添加到feature-A 分支中了。
切换到master 分支
现在我们再来看一看master 分支有没有受到影响。首先切换至master 分支。
$ git checkout master
Switched to branch 'master'然后查看README.md 文件,会发现README.md 文件仍然保持原先的状态,并没有被添加文字。feature-A 分支的更改不会影响到master 分支,这正是在开发中创建分支的优点。只要创建多个分支,就可以在不互相影响的情况下同时进行多个功能的开发。
切换回上一个分支
现在,我们再切换回feature-A 分支。
$ git checkout -
Switched to branch 'feature-A'
特性分支
Git 与Subversion(SVN)等集中型版本管理系统不同,创建分支时不需要连接中央仓库,所以能够相对轻松地创建分支。因此,当今大部分工作流程中都用到了特性(Topic)分支。特性分支顾名思义,是集中实现单一特性(主题),除此之外不进行任何作业的分支。在日常开发中,往往会创建数个特性分支,同时在此之外再保留一个随时可以发布软件的稳定分支。稳定分支的角色通常由master 分支担当(图4.3)。

之前我们创建了feature-A 分支,这一分支主要实现feature-A,除feature-A 的实现之外不进行任何作业。即便在开发过程中发现了BUG,也需要再创建新的分支,在新分支中进行修正。
基于特定主题的作业在特性分支中进行,主题完成后再与master 分支合并。只要保持这样一个开发流程,就能保证master 分支可以随时供人查看。这样一来,其他开发者也可以放心大胆地从master 分支创建新的特性分支。
主干分支
主干分支是刚才我们讲解的特性分支的原点,同时也是合并的终点。通常人们会用master 分支作为主干分支。主干分支中并没有开发到一半的代码,可以随时供他人查看。
有时我们需要让这个主干分支总是配置在正式环境中,有时又需要用标签Tag 等创建版本信息,同时管理多个版本发布。拥有多个版本发布时,主干分支也有多个。
git merge——合并分支
接下来,我们假设feature-A 已经实现完毕,想要将它合并到主干分支master 中。首先切换到master 分支。
$ git checkout master |
然后合并feature-A 分支。为了在历史记录中明确记录下本次分支合并,我们需要创建合并提交。因此,在合并时加上–no-ff参数。
$ git merge --no-ff feature-A |
随后编辑器会启动,用于录入合并提交的信息。
Merge branch 'feature-A' |
默认信息中已经包含了是从feature-A 分支合并过来的相关内容,所以可不必做任何更改。将编辑器中显示的内容保存,关闭编辑器,然后就会看到下面的结果。
Merge made by the 'recursive' strategy. |
这样一来,feature-A 分支的内容就合并到master 分支中了。
git log –graph——以图表形式查看分支
用git log –graph命令进行查看的话,能很清楚地看到特性分支(feature-A)提交的内容已被合并。除此以外,特性分支的创建以及合并也都清楚明了。
git log –graph命令可以用图表形式输出提交日志,非常直观,请大家务必记住。
更改提交的操作
git reset——回溯历史版本
通过前面学习的操作,我们已经学会如何在实现功能后进行提交,累积提交日志作为历史记录,借此不断培育一款软件。
Git 的另一特征便是可以灵活操作历史版本。借助分散仓库的优势,可以在不影响其他仓库的前提下对历史版本进行操作。在这里,为了让各位熟悉对历史版本的操作,我们先回溯历史版本,创建一个名为fix-B 的特性分支(图4.4)。

回溯到创建feature-A 分支前
让我们先回溯到上一节feature-A 分支创建之前,创建一个名为fix-B 的特性分支。
要让仓库的HEAD、暂存区、当前工作树回溯到指定状态,需要用到git rest –hard命令。只要提供目标时间点的哈希值A,就可以完全恢复至该时间点的状态。事不宜迟,让我们执行下面的命令。
git rest --hard 6dd583499177dcf9a84aa2b1cae1e45795a7cfb2
创建fix-B 分支
现在我们来创建特性分支(fix-B)。
$ git checkout -b fix-B
Switched to a new branch 'fix-B'作为这个主题的作业内容,我们在README.md 文件中添加一行文字。
# Git教程
- fix-B然后直接提交README.md 文件。
现在的状态如图4.5 所示。接下来我们的目标是图4.6 中所示的状态,即主干分支合并feature-A 分支的修改后,又合并了fix-B 的修改。


推进至feature-A 分支合并后的状态
首先恢复到feature-A 分支合并后的状态。不妨称这一操作为“推进历史”。
git log命令只能查看以当前状态为终点的历史日志。所以这里要使用git reflog命令,查看当前仓库的操作日志。在日志中找出回溯历史之前的哈希值,通过git reset –hard命令恢复到回溯历史前的状态。首先执行git reflog 命令,查看当前仓库执行过的操作的日志。$ git reflog
4096d9e HEAD@{0}: commit: Fix B
fd0cbf0 HEAD@{1}: checkout: moving from master to fix-B
fd0cbf0 HEAD@{2}: reset: moving to fd0cbf0d4a25f747230694d95cac1be72d33441d
83b0b94 HEAD@{3}: merge feature-A: Merge made by the 'recursive' strategy.
fd0cbf0 HEAD@{4}: checkout: moving from feature-A to master
8a6c8b9 HEAD@{5}: checkout: moving from master to feature-A
fd0cbf0 HEAD@{6}: checkout: moving from feature-A to master
8a6c8b9 HEAD@{7}: commit: Add feature-A
fd0cbf0 HEAD@{8}: checkout: moving from master to feature-A
fd0cbf0 HEAD@{9}: commit: Add index
9f129ba HEAD@{10}: commit (initial): First commit在日志中,我们可以看到commit、checkout、reset、merge 等Git 命令的执行记录。只要不进行Git 的GC(Garbage Collection,垃圾回收),就可以通过日志随意调取近期的历史状态,就像给时间机器指定一个时间点,在过去未来中自由穿梭一般。即便开发者错误执行了Git 操作,基本也都可以利用git reflog命令恢复到原先的状态,所以请各位读者务必牢记本部分。
从上面数第四行表示feature-A 特性分支合并后的状态,对应哈希值为83b0b94A。我们将HEAD、暂存区、工作树恢复到这个时间点的状态。
$ git checkout master
$ git reset --hard 83b0b94
HEAD is now at 83b0b94 Merge branch 'feature-A'tips: 哈希值只要输入4 位以上就可以执行。

消除冲突
现在只要合并fix-B 分支,就可以得到我们想要的状态。让我们赶快进行合并操作。
$ git merge --no-ff fix-B |
这时,系统告诉我们README.md 文件发生了冲突(Conflict)。系统在合并README.md 文件时,feature-A 分支更改的部分与本次想要合并的fix-B 分支更改的部分发生了冲突。
不解决冲突就无法完成合并,所以我们打开README.md 文件,解决这个冲突。
查看冲突部分并将其解决
用编辑器打开README.md 文件,就会发现其内容变成了下面这个样子。
# Git教程
<<<<<<< HEAD
- feature-A
=======
- fix-B
>>>>>>> fix-B=======以上的部分是当前HEAD 的内容,以下的部分是要合并的fix-B 分支中的内容。我们在编辑器中将其改成想要的样子。# Git教程
- feature-A
- fix-B如上所示,本次修正让feature-A 与fix-B 的内容并存于文件之中。但是在实际的软件开发中,往往需要删除其中之一,所以各位在处理冲突时,务必要仔细分析冲突部分的内容后再行修改。
提交解决后的结果
冲突解决后,执行git add命令与git commit命令。
$ git add README.md
$ git commit -m "Fix conflict"
Recorded resolution for 'README.md'.
[master 6a97e48] Fix conflict由于本次更改解决了冲突,所以提交信息记为”Fix conflict”。
git commit –amend——修改提交信息
要修改上一条提交信息,可以使用git commit –amend命令。
我们将上一条提交信息记为了”Fix conflict”,但它其实是fix-B 分支的合并,解决合并时发生的冲突只是过程之一,这样标记实在不妥。于是,我们要修改这条提交信息。
$ git commit --amend |
执行上面的命令后,编辑器就会启动。
Fix conflict |
编辑器中显示的内容如上所示,其中包含之前的提交信息。请将提交信息的部分修改为Merge branch ‘fix-B’,然后保存文件,关闭编辑器。
[master 2e7db6f] Merge branch 'fix-B' |
随后会显示上面这条结果。现在执行git log –graph命令,可以看到提交日志中的相应内容也已经被修改。
$ git log --graph |
git rebase -i——压缩历史
在合并特性分支之前,如果发现已提交的内容中有些许拼写错误等,不妨提交一个修改,然后将这个修改包含到前一个提交之中,压缩成一个历史记录。这是个会经常用到的技巧,让我们来实际操作体会一下。
创建feature-C 分支
首先,新建一个feature-C 特性分支。
$ git checkout -b feature-C
Switched to a new branch 'feature-C'作为feature-C 的功能实现,我们在README.md 文件中添加一行
文字,并且故意留下拼写错误,以便之后修正。
# Git教程 |
提交这部分内容。这个小小的变更就没必要先执行git add命令再执行git commit命令了,我们用git commit -am命令来一次
完成这两步操作。
$ git commit -am "Add feature-C" |
修正拼写错误
现在来修正刚才预留的拼写错误。请各位自行修正README.md 文件的内容,修正后的差别如下所示。
$ git diff
diff --git a/README.md b/README.md
index ad19aba..af647fd 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,4 @@
- feature-A
- fix-B
- - faeture-C
+ - feature-C
然后进行提交。
$ git commit -am "Fix typo"
[feature-C 6fba227] Fix typo
1 file changed, 1 insertion(+), 1 deletion(-)
错字漏字等失误称作typo,所以我们将提交信息记为”Fix typo”。
实际上,我们不希望在历史记录中看到这类提交,因为健全的历史记录并不需要它们。如果能在最初提交之前就发现并修正这些错误,也就不会出现这类提交了。
更改历史
因此,我们来更改历史。将”Fix typo”修正的内容与之前一次的提交合并,在历史记录中合并为一次完美的提交。为此,我们要用到git rebase命令。
$ git rebase -i HEAD~2
用上述方式执行git rebase命令,可以选定当前分支中包含HEAD(最新提交)在内的两个最新历史记录为对象,并在编辑器中打开。
pick 7a34294 Add feature-C
pick 6fba227 Fix typo
# Rebase 2e7db6f..6fba227 onto 2e7db6f
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out我们将6fba227 的Fix typo 的历史记录压缩到7a34294 的Add feature-C
里。按照下图所示,将6fba227 左侧的pick 部分删除,改写为fixup。pick 7a34294 Add feature-C
fixup 6fba227 Fix typo保存编辑器里的内容,关闭编辑器。
系统显示rebase 成功。也就是以下面这两个提交作为对象,将”Fixtypo”的内容合并到了上一个提交”Add feature-C”中,改写成了一个新的提交。
● 7a34294 Add feature-C
● 6fba227 Fix typo现在再查看提交日志时会发现Add feature-C 的哈希值已经不是7a34294 了,这证明提交已经被更改。
$ git log --graph
* commit 51440c55b23fa7fa50aedf20aa43c54138171137
| Author: hirocaster <hohtsuka@gmail.com>
| Date: Sun May 5 17:07:36 2013 +0900
|
| Add feature-C
|
* commit 2e7db6fb0b576e9946965ea680e4834ee889c9d8
|\ Merge: 83b0b94 4096d9e
| | Author: hirocaster <hohtsuka@gmail.com>
| | Date: Sun May 5 16:58:27 2013 +0900
| |
| | Merge branch 'fix-B'
| |
| * commit 4096d9e856995a1aafa982aabb52bfc0da656b74
| | Author: hirocaster <hohtsuka@gmail.com>
| | Date: Sun May 5 16:50:31 2013 +0900
| |
| | Fix B
| |
省略这样一来,Fix typo 就从历史中被抹去,也就相当于Add feature-C中从来没有出现过拼写错误。这算是一种良性的历史改写。
合并至master 分支
feature-C 分支的使命告一段落,我们将它与master 分支合并。
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff feature-C
Merge made by the 'recursive' strategy.
README.md | 1 +
1 file changed, 1 insertion(+)master 分支整合了feature-C 分支。开发进展顺利。
推送至远程仓库
Git 是分散型版本管理系统,但我们前面所学习的,都是针对单一本地仓库的操作。下面,我们将开始接触远在网络另一头的远程仓库。远程仓库顾名思义,是与我们本地仓库相对独立的另一个仓库。让我们先在GitHub 上创建一个仓库,并将其设置为本地仓库的远程仓库。
请参考第3 章的3.2 节在GitHub 上新建一个仓库。为防止与其他仓库混淆,仓库名请与本地仓库保持一致,即git-tutorial。创建时不要勾选Initialize this repository with a README 选项图4.8)。因为一旦勾选该选项,GitHub 一侧的仓库就会自动生成README 文件,从创建之初便与本地仓库失去了整合性。虽然到时也可以强制覆盖,但为防止这一情况发生还是建议不要勾选该选项,直接点击Create repository 创建仓库。

git remote add——添加远程仓库
在GitHub 上创建的仓库路径为“git@github.com:用户名/git-tutorial.git”。现在我们用git remote add命令将它设置成本地仓库的远程仓库A。
$ git remote add origin git@github.com:github-book/git-tutorial.git |
按照上述格式执行git remote add命令之后,Git 会自动将git@github.com:github-book/git-tutorial.git远程仓库的名称设置为origin(标识符)。
git push——推送至远程仓库
推送至master 分支
如果想将当前分支下本地仓库中的内容推送给远程仓库,需要用到git push命令。现在假定我们在master 分支下进行操作。
$ git push -u origin master
Counting objects: 20, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (10/10), done.
Writing objects: 100% (20/20), 1.60 KiB, done.
Total 20 (delta 3), reused 0 (delta 0)
To git@github.com:github-book/git-tutorial.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.像这样执行git push命令,当前分支的内容就会被推送给远程仓库origin 的master 分支。-u参数可以在推送的同时,将origin 仓库的master 分支设置为本地仓库当前分支的upstream(上游)。添加了这个参数,将来运行git pull命令从远程仓库获取内容时,本地仓库的这个分支就可以直接从origin 的master 分支获取内容,省去了另外添加参数的麻烦。
执行该操作后,当前本地仓库master 分支的内容将会被推送到
GitHub 的远程仓库中。在GitHub 上也可以确认远程master 分支的内容和本地master 分支相同。推送至master 以外的分支
除了master 分支之外,远程仓库也可以创建其他分支。举个例子,我
们在本地仓库中创建feature-D 分支,并将它以同名形式push 至远程仓库。我们在本地仓库中创建了feature-D 分支,现在将它push 给远程仓库并保持分支名称不变。
$ git checkout -b feature-D
Switched to a new branch 'feature-D'
$ git push -u origin feature-D
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:github-book/git-tutorial.git
* [new branch] feature-D -> feature-D
Branch feature-D set up to track remote branch feature-D from origin.现在,在远程仓库的GitHub 页面就可以查看到feature-D 分支了。
从远程仓库获取
上一节中我们把在GitHub 上新建的仓库设置成了远程仓库,并向这个仓库push 了feature-D 分支。现在,所有能够访问这个远程仓库的人都可以获取feature-D 分支并加以修改。本节中我们从实际开发者的角度出发,在另一个目录下新建一个本地仓库,学习从远程仓库获取内容的相关操作。这就相当于我们刚刚执行过push 操作的目标仓库又有了另一名新开发者来共同开发。
git clone——获取远程仓库
获取远程仓库
首先我们换到其他目录下,将GitHub 上的仓库clone 到本地。注意不要与之前操作的仓库在同一目录下。
$ git clone git@github.com:fanygOfficial/git-book.git
Cloning into 'git-book'...
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 20 (delta 3), reused 20 (delta 3)
Receiving objects: 100% (20/20), done.
Resolving deltas: 100% (3/3), done.
$ cd git-book执行git clone命令后我们会默认处于master 分支下,同时系统会自动将origin 设置成该远程仓库的标识符。也就是说,当前本地仓库的master 分支与GitHub 端远程仓库(origin)的master 分支在内容上是完全相同的。
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/feature-A
remotes/origin/feature-B
remotes/origin/feature-D
remotes/origin/master注 git branch -a 而不是 git branch , 加上-a就可以查看到其它分支我们用git branch -a命令查看当前分支的相关信息。添加-a参数可以同时显示本地仓库和远程仓库的分支信息
结果中显示了remotes/origin/feature-D,证明我们的远程仓库中已经有了feature-D 分支。
获取远程的feature-D 分支
$ git checkout -b feature-D origin/feature-D
Switched to a new branch 'feature-D'
Branch 'feature-D' set up to track remote branch 'feature-D' from 'origin'-b 参数的后面是本地仓库中新建分支的名称。为了便于理解,我们仍将其命名为feature-D,让它与远程仓库的对应分支保持同名。新建分支名称后面是获取来源的分支名称。例子中指定了origin/feature-D,就是说以名为origin 的仓库(这里指GitHub 端的仓库)的feature-D 分支为来源,在本地仓库中创建feature-D 分支。
向本地的feature-D 分支提交更改
现在假定我们是另一名开发者,要做一个新的提交。在README.md 文件中添加一行文字,查看更改。
$ git diff
diff --git a/README.md b/README.md
index af647fd..30378c9 100644
--- a/README.md
+++ b/README.md
@@ -3,3 +3,4 @@
- feature-A
- fix-B
- feature-C
+ - feature-D按照之前学过的方式提交即可。
$ git commit -am "Add feature-D"
[feature-D ed9721e] Add feature-D
1 file changed, 1 insertion(+)推送feature-D 分支
$ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 281 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git@github.com:github-book/git-tutorial.git
ca0f98b..ed9721e feature-D -> feature-D从远程仓库获取feature-D 分支,在本地仓库中提交更改,再将feature-D 分支推送回远程仓库,通过这一系列操作,就可以与其他开发者相互合作,共同培育feature-D 分支,实现某些功能。
git pull——获取最新的远程仓库分支
现在我们放下刚刚操作的目录,回到原先的那个目录下。这边的本地仓库中只创建了feature-D 分支,并没有在feature-D 分支中进行任何提交。然而远程仓库的feature-D 分支中已经有了我们刚刚推送的提交。这时我们就可以使用git pull 命令,将本地的feature-D 分支更新到最新状态。当前分支为feature-D 分支。
$ git pull origin feature-D |
GitHub 端远程仓库中的feature-D 分支是最新状态,所以本地仓库中的feature-D 分支就得到了更新。今后只需要像平常一样在本地进行提交再push 给远程仓库,就可以与其他开发者同时在同一个分支中进行作业,不断给feature-D 增加新功能。
如果两人同时修改了同一部分的源代码,push 时就很容易发生冲突。所以多名开发者在同一个分支中进行作业时,为减少冲突情况的发生,建议更频繁地进行push 和pull 操作。
帮助大家深入理解Git 的资料
至此为止,阅读并理解本书所必需的Git 操作已经全部讲解完了。但是在实际的开发现场,往往要用到更加高级的Git 操作。这里,我们向各位介绍一些参考资料,能够帮助各位深入理解Git 的相关知识。
Pro Git
Pro Git 由就职于GitHub 公司的Scott Chacon 执笔,是一部零基础的Git 学习资料。基于知识共享的CC BY-NC-SA 3.0 许可协议,各位可以免费阅读到包括简体中文在内的各国语言版本。
A http://git-scm.com/book/zh/v1
B https://github.com/schaconC http://git-scm.com/book/zh/v2 (目前已经更新到了v2版本)
LearnGitBranching
LearnGitBranching 是学习Git 基本操作的网站(图4.9)。注重树形结构的学习方式非常适合初学者使用,点击右下角的地球标志还可切换各种语言进行学习。

tryGit
通过tryGitB 我们可以在Web 上一边操作一边学习Git 的基本功能(图4.10)。很可惜该教程只有英文版。

小结
本章就理解本书所必需的Git 操作进行了讲解。只要掌握了本章的知识,就足以应付日常开发中的大部分操作了。遇到不常用的特殊操作时,还请各位读者查阅本书介绍的参考资料,确保操作的正确性。
第五章 详细解说GitHub的功能
GitHub 为实现社会化编程提供了诸多功能。本章就请各位读者随我们一起边看页面边学习,加深对GitHub 丰富功能的理解。
键盘快捷键
在GitHub 中,很多页面都可以使用键盘快捷键。熟悉键盘操作,能够让GitHub 变得更加便捷。在各个页面按下shift + / 都可以打开键盘快捷键一览表(图5.1),查看当前页面的快捷键。如果想要感受更加快捷的操作,不妨亲自试一试。

工具栏
关于UI
工具栏常驻于各个页面的上端,让我们先来讲解它的相关知识(图5.2)。

1 LOGO
点击GitHub 的LOGO 就会进入控制面板。控制面板的相关知识将在后面讲解。
2 Notifications
这一图标用于提示用户是否有新的通知。当图标为蓝色时表示有未读通知。用户在新建Issue、被评论、进行Pull Request 等时都会收到通知。另外,按照默认设置,用户在GitHub 收到的通知会同时发送到该用户的注册邮箱。邮箱接收通知的相关设置在Account settings 中进行.
3 搜索窗口
在这里输入想找的用户或代码片段,就可以搜索到与之相关的信息。
Explore
字面意思 探索其它GitHub上的热门仓库
- 从各个角度介绍GitHub 上的热门软件。
- GitHub 公司特别介绍的软件(附开发者制作的视频)
- 按语言筛选本日/ 周/ 月的热门仓库/ 开发者
在这里有机会了解到最尖端的技术和软件。作为一名程序员,可以在上面找到许多灵感。建议各位定期关注一下这里。笔者也经常借助语言筛选器查询各语言的顶尖代码库。
5 Gist
Gist 功能主要用于管理及发布一些没必要保存在仓库中的代码,比如小的代码片段等。笔者就经常把一些随便编写的脚本代码等放在Gist中。系统会自动管理更新历史,并且提供了Fork 功能。另外,通过Gist还可以很方便地为同事编写代码示例。在Gist 上添加的代码示例可以嵌入博客中。当然,如果选择了语言,还会自动添加语法高亮。详细介绍请参考附录B。
(存放一些小的代码片段 没必要创建仓库的那种)
6 Blog
这是到GitHub 公司官方博客的超链接,GitHub 公司会在上面发布通知。新功能的通知、新入职员工的介绍、drinkup 聚会的信息等都会在上面定期发布。
7 Help
GitHub 相关的帮助。虽然只有英文版,但用的都是比较简单的英文,遇到不懂的地方不妨在这里查一下。
8 头像、用户名
点击后会显示用户的个人信息页面。个人信息页面将在后面进行讲解。
9 Create a new…
创建新的Git 仓库或Organization,向Organization 添加成员、小组、仓库,为仓库添加Issue 或collaborator 等操作的菜单都聚集在这里。显示内容会根据当前页面不同而改变。
Account settings
Account settings 的图标是一把螺丝刀和一柄锤子,点击它可以打开账户设置页面。在这里可以进行个人信息、安全管理、付费方案的设置,各位在使用GitHub 时请务必浏览一遍。
Sign out
点击这个按钮可以退出GitHub。
tips:补充一个Github Status 这个可以检查GitHub的各个功能板块的运行服务状态 你懂的 当你遇到一些提交或者什么的bug不一定是你的问题 你要来这里看看是不是GitHub的服务器出了问题!
控制面板
本书上的控制面板部分已经过时 现在的控制面板 如下图

Pull Requests
显示用户已进行过的Pull Request。通过这里,开发者可以很方便地追踪Pull Request 的后续情况。
Issues
在这里可以查看用户拥有权限的仓库或分配给自己的Issue。当用户同时进行多个项目时,可以在这里一并查看Issue。
marketplace
GitHub的一些扩展应用 大概…
Explore
可以找到一些热门的存储库和项目

个人信息
没什么好讲的自己看看吧
访问下述URL,就可以看到各位的个人信息。
https://github.com/用户名
仓库
仓库的URL 形式如下所示。
https://github.com/用户名/仓库名
这个页面可以说是各个软件的大门。循着目录找下去我们就可以查
阅自己想要的文件。如果有相应权限,还可以对文件的内容直接进行编
辑、提交。
● 关于UI
我们以图5.7 为例进行说明。特别重要的项目会在本章后半部分重
新详细讲解。
❶ 用户名(组织名)/ 仓库名
左上角图标旁边显示的是用户名和仓库名。斜线左侧为用户名。使用GitHub 的组织账户时,这一部分则为组织名。斜线右侧是仓库名。❷ Watch/Star/Fork
眼睛图标旁边写着Watch 字样。点击这个按钮就可以Watch 该仓库,今后该仓库的更新信息会显示在用户的公开活动中。Star 旁边的数字表示给这个仓库添加Star 的人数。这个数越高,代表该仓库越受关注。Watch 与Star 有所不同,Watch 之后该仓库的相关信息会在后述的Notifications 中显示,让用户可以追踪仓库的内容,而Star 更像是书签,让用户将来可以在Star 标记的列表中找到该仓库。另外,Star 数还是GitHub 上判断仓库热门程度的指标之一。旁边的分叉图标是Fork 按钮。后面的数字代表该仓库被Fork 至各用户仓库的次数。这个数字越大,表示参与这个仓库开发的人越多。❸ Code
显示该仓库中的文件列表。仓库名下方是该仓库的简单说明和URL。❹ Issue
用于BUG 报告、功能添加、方向性讨论等,将这些以Issue 形式进行管理。Pull Request 时也会创建Issue。旁边显示的数字是当前处于Open 状态的Issue 数。❺ Pull Requests
在Pull Requests 中可以列表查看并管理Pull Request。代码的更改和讨论都可以在这里进行。旁边显示的数字表示尚未Close 的Pull Request❻ Wiki
Wiki 是一种比HTML 语法更简单的页面描述功能。常用于记录开
发者之间应该共享的信息或软件文档。数字表示当前Wiki 的页面数量。❼ Pulse
显示该仓库最近的活动信息。该仓库中的软件是无人问津,还是在
火热地开发之中,从这里可以一目了然。❽ Graphs
以图表形式显示该仓库的各项指标。让用户轻松了解该仓库的活动
倾向。❾ Network
以图表形式直观地显示出当前仓库的状态及Fork 出的仓库的状态。
同时会显示成员列表。❿ Settings
这里可以更改当前仓库的设置。用户必须拥有更改设置的权限才能
看到这个菜单。SSH clone URL
clone 仓库时所需的URL。点击右侧的剪贴板图标可以将URL 复制
到剪贴板中。点击HTTPS、SSH、Subversion 图标可以切换至相应协议
的URL。Clone in Desktop
启动GitHub 专用的客户端应用程序并进行clone。GitHub 专用的客
户端应用程序有Windows 版和Mac 版,详细情况请参考附录A 的讲解。Clone in Desktop
启动GitHub 专用的客户端应用程序并进行clone。GitHub 专用的客
户端应用程序有Windows 版和Mac 版,详细情况请参考附录A 的讲解。Download ZIP
将当前正在阅览的分支中的文件以ZIP 形式打包下载。这种方式与
Git 的clone 不同,只是单纯将文件下载到本地,所以无法通过Git 查看
日志或对仓库进行更改。如果只是想使用仓库中的文件,比较适合用这
种方式下载。a commits
在这里可以查看当前分支的提交历史。左侧的数字表示提交数。b branches
可以查看仓库的分支列表。左侧的数字表示当前拥有的分支数。c releases
显示仓库的标签(Tag)列表。同时可以将标签加入时的文件以归
档形式(ZIP、tar.gz)下载到本地。软件在版本升级时一般都会打标签,
如果需要特定版本的文件,可以从这里寻找。d contributors
显示对该仓库进行过提交的程序员名单。如果您也对该仓库发送过
Pull Request 并且被采纳,那么在这里就能找到自己的名字。左边的数字
是程序员的人数。e Compare & review
可以查看当前显示的分支与其他分支的差别,以便进行审查。点击
这个按钮,会出现一个页面让用户选择比较对象。f branch
显示当前分支的名称。从这里可以切换仓库内分支,查看其他分支
的文件。g path
显示当前文件列表的路径。点击上级目录的链接就可以直接移动至
该目录。h Fork this project and Create a new file
可以在当前仓库的路径下新建文件。新建文件作为一个新的提交,
记录在Fork 出的分支中。
如果用户对该仓库拥有足够权限,该项则显示为Create a new file,
用户可以直接在当前路径下新建文件。i files
可以查看当前分支的文件。顶端为最新提交的相关信息。在文件或
目录的列表中,从左至右分别为文件名称、该文件最新的提交日志、更
新日期。点击目录或文件就可以查看相应内容。
如果当前目录中包含README 文件,那么在文件列表下方会显示
README。一般而言,README 中记录着该仓库中软件的说明或使用
方法以及许可协议等信息,请务必加以阅读。
文件的相关操作

可以在线编辑一些文本文件 也可在线创建分支 文件夹 文件
查看差别
在GitHub 上,直接修改URL 就可以让用户以多种形式查看差别。这里我们以Ruby on Rails 的仓库A 为例,给各位介绍直接修改URL 的一些技巧。
查看分支间的差别
比如我们想查看4-0-stable 分支与3-2-stable 分支之间的差别,可以像下面这样将分支名加到URL 里。
https://github.com/rails/rails/compare/4-0-stable...3-2-stable |
查看与几天前的差别
假如我们想查看master 分支在最近7 天内的差别,可以像下面这样这样将时间加入URL。
https://github.com/rails/rails/compare/master@{7.day.ago}...master |
这样,就可以查看这段期间内的差别。
day
week
month
year
指定期间可以使用以上四个时间单位。如果差别过大则不会列出所有提交,只显示最近的一部分。
查看与指定日期之间的差别
假设我们想查看master 分支2013 年1 月1 日与现在的区别,可以将日期加入URL。
https://github.com/rails/rails/compare/master@{2013-01-01}...master
这样,便可以查看与指定日期之间的差别。但是如果指定日期与现在的差别过大,或者指定日期过于久远,则无法显示。
Issue
在软件开发过程中,开发者们为了跟踪BUG 及进行软件相关讨论,进而方便管理,创建了Issue。管理Issue 的系统称为BTS(Bug TrackingSystem,BUG 跟踪系统)。当今具有代表性的BTS 有RedmineA、TracB、BugzillaC 等。GitHub 也为自身加入了BTS 的功能。在GitHub 上,可以将它作为软件开发者之间的交流工具,多多加以利用。遇到下面几种情况时,各位就可以使用这个功能。
- 发现软件的BUG 并报告
- 有事想向作者询问、探讨
- 事先列出今后准备实施的任务
Issue 除BUG 管理之外还有许多其他用途。在软件开发者的圈子中,将Issue 用于多种用途的情况已经司空见惯。作为GitHub 的功能之一,想必今后会有更多人自然而然地用到它。所以借此机会,让我们来共同学习Issue 的一些简单用法。
简洁且表现力丰富的描述方法
GitHub 的Issue 及评论可以使用GFMA 语法进行描述,从而获得丰富的表现力。比如像图5.10 中那样描述,然后点击Preview,就可以看到图5.11 中那种标记后的效果。

语法高亮
假设我们像下面这样,先指定语言再描述代码。
ruby
def hello_world
puts 'Hello World!'
end这样一来,代码就会如下面所示被添加语法高亮,变得直观易读。
ruby
def hello_world
puts 'Hello World!'
end
添加图片
既然支持markdown语法 那就按markdown来添加图片
添加标签以便整理
Issue 可以通过添加标签(Label)来进行整理。添加标签后,Issue的左侧就会显示标签(图5.15)。点击页面左侧的标签,还可以只显示该类标签的Issue。标签可以自由创建。既可以像图5.15 那样按语言和技术分类,也可以按照BUG、任务、备忘等作业类型分类。各位可以按照需要选择便于整理的标签。
提个小建议:其实在Issue 比较少的情况下不必每个都添加标签,大可等Issue 积攒到一定数量,只有进行筛选才能清晰把握时再添加标签。

添加里程碑以便管理
除标签外,还可以通过添加里程碑来管理Issue。通过图5.16 可以看出,项目距离下一个版本(3.0.0 版)还有6 个Issue 需要实施,整体的96% 已经实施完毕并Close。从这里的链接我们可以查看剩余的Issue。
设置里程碑,就可以用Issue 来管理任务。
tips:了解贡献时的规则!
在描述Issue 时,常常会看到图a中这种贡献规范的链接。在该仓库的根目录下添加CONTRIBUTING.md 文件后该链接就
会显示出来注a。规范的内容一般包括报告时Issue 的描述方法、PullRequest 时的规则或要求、许可证的相关信息等。为了在开源项目开发中能与其他人和谐相处,请务必在贡献之前仔细阅读这些规范。
Tasklist 语法
我们可以使用GFM 的一项独有功能,那就是Tasklist 语法A。首先试着按下面的格式进行描述。
# 本月要做的任务 |
这样一来,这段文字就会被标记成复选列表的样式(图5.17)。这个复选列表可以直接勾选或者取消,不必打开Issue 的编辑页面重新编辑,十分方便,建议各位记住这个功能。

通过提交信息操作Issue
在GitHub 上,只要按照特定的格式描述提交信息,就可以像一般BTS 带有的功能那样对Issue 进行操作。
在相关Issue 中显示提交
在Issue 一览表中我们可以看到,每一个Issue 标题的下面都分配了诸如“#24”的编号。只要在提交信息的描述中加入“#24”,就可以如图5.18 所示,在Issue 中显示该提交的相关信息,使关联的提交一目了然。这里只需轻轻点击一下便可以显示相应提交的具体内容,在代码审查时省去了从大量提交日志中搜索相应提交的麻烦,非常方便。

Close Issue
如果一个处于Open 状态的Issue 已经处理完毕,只要在该提交中以下列任意一种格式描述提交信息,对应的Issue 就会被Close。
● fix #24
● fixes #24
● fixed #24
● close #24
● closes #24● closed #24
● resolve #24
● resolves #24
● resolved #24利用这个方法,每次提交并push 之后,就不必再大费周章地到GitHub 的Issue 中寻找相应Issue 再手动Close,省去不少麻烦。
像这样,只要按照特定的格式描述提交信息,GitHub 就会自动识别并处理,让使用GitHub 变得更加轻松。目前,很多GitHub 之外的BTS也实现了这一功能,记住它绝对是有利无弊的。
将特定的Issue 转换为Pull Request
在GitHub 上,如果给Issue 添加源代码,它就会变成我们马上要讲到的Pull Request。Issue 与Pull Request 的编号相互通用,通过GitHub的API 可以将特定的Issue 转换为Pull Request,能够完成这一操作的hub 命令将在本书的8.1 节中讲解。在这里,各位只要先记住Issue 与Pull Request 的编号通用即可。
Pull Request
Pull Request 是用户修改代码后向对方仓库发送采纳请求的功能,也是GitHub 的核心功能(图5.19)。正因为有了这个功能,才会让众多开发者轻松地加入到开源开发的队伍中来。
在Pull Request 页面能够列表查看当前处于Open 状态的PullRequest。通过点击页面左部和上部的选项可以进行筛选和重新排列。在列表中点击特定的Pull Request 就会进入详细页面(图5.20)。页面上方显示着这次是从谁的哪个分支向谁的哪个分支发送了PullRequest。下面,我们对各个标签(Tag)页进行讲解。

Pull Request 的详细页面

tips:专栏:获取diff 格式与patch 格式的文件对长期投身于软件开发的人来说,有时可能会希望以diff 格式文件和patch 格式文件的形式来处理Pull Request。 举个例子,假设Pull Request 的URL 如下所示。 https://github.com/用户名/仓库名/pull/28 如果想获取diff 格式的文件,只要像下面这样在URL 末尾添加.diff 即可。 https://github.com/用户名/仓库名/pull/28.diff 同理, 想要patch 格式的文件, 只需要在URL 末尾添加.patch 即可。 https://github.com/用户名/仓库名/pull/28.patch 想要diff 格式与patch 格式文件的各位请按照上述方法进行操作。
Conversation
在Conversation 标签页中,可以查看与当前Pull Request 相关的所有评论以及提交的历史记录。人们在这里添加评论互相探讨,发送提交落实讨论内容的整个过程会按时间顺序列出,供用户查看。各位在查看过程中如果有自己的想法,不妨积极地添加评论参与探讨。提交日志的右侧有该提交的哈希值,点击链接即可确认相应提交的详细信息。
tips:引用评论 在Conversation 中人们通过添加评论进行对话。这里有一个简单方法可以帮您引用某个人的评论。选中想引用的评论然 后按R 键,被选择的部分就会自动以评论语法写入评论文本框 下图所示

Commits
在Commits 标签页中,按时间顺序列表显示了与当前Pull Request相关的提交(图5.21)。标签上的数字为提交的次数。每个提交右侧的哈希值可以连接到该提交的代码。

tips:评论中是可以添加表情 请自行搜索相关资料
Files Changed
Files Changed 标签页中可以查看当前Pull Request 更改的文件内容以及前后差别。标签上的数字表示新建及被更改的文件数。
将鼠标指针放到被更改行行号的左侧,我们会看到一个加号。点击这个加号可以在代码中插入评论(下图)。这样,评论是针对哪行代码的就一目了然了。这个插入评论的功能让针对代码的讨论变得十分顺畅。特别是在多人协作的软件开发中,这个功能更加不可或缺。

这是最新pr界面外面的内容

这是最新pr界面里面的内容

Wiki
Wiki 是一个使用简单的语法就能编写文档的功能(图5.23)。所有有权限的人都可以对文章进行修改,所以比较适合多人共同编写文章的情况。创建、编辑文档时不必另外启动软件,用起来十分方便,非常适合用来针对更新频率较高的软件进行文档等信息方面的汇总。
与Issue 和Pull Request 相同,Wiki 也支持GFM 语法,所以可以轻松创建表现力丰富的文档。点击页面右上角的New Page 按钮便可以创建新的Wiki 页。
Wiki 功能本身的数据也在Git 中进行管理。点击Clone URL 按钮可以将当前Wiki 的Git 仓库URL 复制到剪贴板中。用户能够通过clone操作获取Wiki 仓库,然后在本地创建、编辑页面,进行提交再push,便可以完成对Wiki 的创建及编辑工作。


History
这个wiki文档还可以查看历史更新 不过我没找到 用到在看吧
Insights
看仓库的一些信息 活跃度什么的

Settings
在这里可以对仓库进行任何设置。用户必须拥有更改设置的权限,才能看到这个页面。
里面的内容自己看看吧
其他功能
GitHub 还提供了其他许多功能。我们在这里只介绍其中的一部分。
GitHub Pages
GitHub Jobs
GitHub Enterprise
GitHub API
GitHub 面向开发者公开了API。特别是在开发面向程序员的Web服务时,能与GitHub 集成绝对有利无弊。详细内容请参照官方网站.
小结
本章中,我们结合GitHub 的实际操作页面给各位讲解了GitHub 上提供的各项功能。其中某些功能的细节可能经常使用GitHub 的人也并不完全清楚。
在GitHub 上与其他人共同进行软件开发时,如果发现同组搭档对功能不够熟悉,不妨按照本书中的介绍为其讲解一番。
第六章 尝试Pull Request(★★★)
按部就班地创建GitHub 账号并公开自己的源代码并不是什么难事。不过,刚刚接触GitHub 的人往往不会或不敢使用Pull Request 功能。
Pull Request 是社会化编程的象征。GitHub 创造的这一功能,可以说给开源开发世界带来了一场革命。不会用这个功能,就等于不会用GitHub。
不过,掌握Pull Request 的难度确实较高,刚刚接触GitHub 的人在发送Pull Request 时,往往会遇到找不到对方的项目或者不知道该如何发送等问题。
所以,本书将为各位创造一个亲自动手发送Pull Request 的机会,
请各位不要错过。
Pull Request 的概要
什么是Pull Request
首先我们来理解什么是Pull Request。Pull Request 是自己修改源代码后,请求对方仓库采纳该修改时采取的一种行为。
Pull Request 的流程
下面来看看具体的例子。现在假设我们在使用GitHub 上的一款开源软件。
在使用这款软件的过程中,我们偶然间发现了BUG。为了继续使用软件,我们手动修复了这个BUG。如果我们修改的这段代码能被该软件的开发仓库采纳,今后与我们同样使用这款软件的人就不会再遇到这个BUG。为此,我们要第一时间发送Pull Request。
在GitHub 上发送Pull Request 后,接收方的仓库会创建一个附带源代码的Issue,我们在这个Issue 中记录详细内容。这就是Pull Request。
tips: Pull Request 在网络上也常常被简称为PR。
发送过去的Pull Request 是否被采纳,要由接收方仓库的管理者进行判断。一般只要代码没有问题,对方都会采纳。如果有问题,我们会收到评论。只要Pull Request 被顺利采纳,我们就会成为这个项目的Contributor(贡献者),我们编写的这段代码也将被全世界的人使用。这正是社会化编程和开源开发的一大乐趣。
我们为本书专门搭设了一个网站,各位可以对其进行修改,尝试发送Pull Request。
发送Pull Request 前的准备

查看要修正的源代码
找到你要提交pr的仓库 fork一份
将fork到自己账号的仓库 clone到本地
$ git clone git@github.com:hirocastest/first-pr.git
Cloning into 'first-pr'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 14 (delta 2), reused 0 (delta 0)
Receiving objects: 100% (14/14), 24.05 KiB, done.
Resolving deltas: 100% (2/2), done.
$ cd first-prfirst-pr 目录下会生成Git 仓库。这个仓库与我们GitHub 账户下的
first-pr 仓库状态相同。现在只要在这个仓库中修改源代码进行push,
GitHub 账户中的仓库就会被修改。在本地 确认分支 git branch -a
$ git branch -a
* gh-pages ←当前分支
remotes/origin/HEAD -> origin/gh-pages
remotes/origin/gh-pages开头加了“remotes/origin/”的是GitHub 端仓库的分支。我们手头的开发环境中只有gh-pages 分支。
创建特性分支
我们创建一个名为work 的分支,用来发送Pull Request。这个work分支就是这次的特性分支。现在创建work 分支并自动切换。
$ git checkout -b work gh-pages
Switched to a new branch 'work'确认是否切换到了work 分支下
$ git branch -a
gh-pages
* work ←当前分支
remotes/origin/HEAD -> origin/gh-pages
remotes/origin/gh-pages查看文件列表,我们可以看到网站中显示的index.html 文件。
$ ls
README.md index.html params.json
images javascripts stylesheets添加代码 用编辑器打开index.html 文件,以HTML 形式添加感想。
省略
<p>请写明这是对本书内容的实践或描述对本书的感想并发送Pull Request。</p>
↓追加的行
<p class="impression"> 这本书读着很有趣。(@HIROCASTER)</p>
省略请自由添加感想并用p 标签(Tag)括起,然后关闭编辑器。
提交修改
用git diff命令查看修改是否已经正确进行。
$ git diff
diff --git a/index.html b/index.html
index f2034b3..91b8ecb 100644
--- a/index.html
+++ b/index.html
@@ -39,6 +39,8 @@
<p>请写明这是对本书内容的实践或描述对本书的感想并发送Pull Request。</p>
+<p class="impression"> 这本书读着很有趣。(@HIROCASTER)</p>
+
省略然后用浏览器打开,查看显示是否正确。然后确认添加的代码,提交至本地仓库。
$ git add index.html
$ git commit -m "Add my impression"
[work 243f28d] Add my impression
1 file changed, 2 insertions(+)创建远程分支
要从GitHub 发送Pull Request,GitHub 端的仓库中必须有一个包含了修改后代码的分支。我们现在就来创建本地work 分支的相应远程分支。
$ git push origin work
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 353 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
To git@github.com:hirocastest/first-pr.git
* [new branch] work -> work查看分支,/origin/work 已被创建。
$ git branch -a
master
* work
remotes/origin/HEAD -> origin/master
remotes/origin/gh-pages
remotes/origin/work ←已被创建请打开GitHub 的“用户名/first-pr”页,确认work 分支是否被创建,以及是否已包含我们添加的代码。
发送Pull Request
发送Pull Request

此时打开 open pr 就可以创建一个pr了
提交前 可以先查看分支间差别的页面
https://github.com/ituring/first-pr/compare/gh-pages...用户名:work

确认想要发送的Pull Request 的内容差别无误后,请点击Create Pull Request。随后显示的表单用于填写请求对方采纳的评论。现在让我们在评论栏中简明扼要地描述本次进行Pull Request 的理由。

ohhhhhhhhhhhhhhhhhhhh!!!
至此,恭喜各位顺利发送了第一次的Pull Request。现在我们发送的源代码还没有被采纳,对方仓库不会有任何变化,所以网页也仍然是原样。
如果想查看已发送Pull Request 的状态,可以登录GitHub,打开自己的控制面板查看Pull Request 标签页。点击自己发送的Pull Request 后会进入如图6.6 的页面,管理者对Pull Request 的评论会发到这里。这些在Conversation 标签页中会按照时间顺序排列显示。只要代码没有问题,我们的Pull Request 就会被采纳。

让Pull Request 更加有效的方法
下面为各位介绍在开发现场如何更有效地运用Pull Request
在软件的设计与实现过程中如果想发起讨论,Pull Request 是个非常好的契机。我们虽然可以像本次示例一样等代码完成后再发送PullRequest,但在实际开发过程中,这样做很可能导致一个功能在完成后才收到设计或实现方面的指正,从而使代码需要大幅更改或重新实现。在GitHub 上,我们可以尽早创建Pull Request,从审查中获得反馈,让大家在设计与实现方面思路一致,借此逐渐提高代码质量。这个方法在团队开发大型项目时尤其有效,已将GitHub 运用到实际开发中的团队请务必试一试。
这个方法执行起来很简单。只要在想发起讨论时发送Pull Request即可,不必等代码最终完成。即便某个功能尚在开发之中,只要在Pull Request 中附带一段简单代码让大家有个大体印象,就能获取不少反馈。如果在Pull Request 中再加入直观易懂的Tasklist(请参照第5 章的“Tasklist 语法”),就能很清楚反映出哪些功能已经实现,将来要做哪些工作。这不但能加快审查者的工作效率,还能作为自己的备忘录使用。
从反馈中,我们不但能获得对自己所提议的新功能的支持和相关改善意见,有时还会被人指出自己没注意到的失误,或者准备编写的代码与其他成员重复等。这样一来,我们最终所完成的代码的质量一定会比原先高出许多。
向发送过Pull Request 的分支添加提交时,该提交会自动添加至已发送的Pull Request 中。这一方法要求尽早发送Pull Request,越早效果越明显。另外还有一件事要记住,就是千万不要在Pull Request 中添加无关的修改。处理与主题无关的作业请另外创建分支,不然会让原本清晰的讨论变得一团糟。
上面懒得看可以看这句
我们最好不要等代码完成在去发送PR, 因为这样可能会撞车. |
明确标出“正在开发过程中”
为防止开发到一半的Pull Request 被误合并,一般都会像图6.7 中所示的那样在标题前加上“[WIP]”字样。WIP 是Work In Progress 的简写,表示仍在开发过程中。等所有功能都实现之后,再消去这个前缀。

不进行Fork 直接从分支发送Pull Request
这个方法也值得在GitHub 上进行开发的团队借鉴。一般说来,在GitHub 上修改对方的代码时,需要先将仓库Fork 到本地,然后再修改代码,发送Pull Request。但是,如果用户对该仓库有编辑权限,则可以直接创建分支,从分支发送Pull Request。利用这一设计,团队开发时不妨为每一名成员赋予编辑权限,免去Fork 仓库的麻烦。这样,成员在有需要时就可以创建自己的分支,然后直接向master分支等发送Pull Request。
其实,这一方法已经被GitHub 实际运用到开发之中。关于这一开发流程的具体内容将在第9 章详细说明。
仓库的维护
Fork 或clone 来的仓库,一旦放置不管就会离最新的源代码越来越远。如果不以最新的源代码为基础进行开发,劳神费力地编写代码也很可能是白费力气。下面就让我们学习如何让仓库保持最新状态。通常来说clone 来的仓库实际上与原仓库并没有任何关系。所以我们需要将原仓库设置为远程仓库,从该仓库获取(fetch)数据与本地仓库进行合并(merge),让本地仓库的源代码保持最新状态(图6.8)

仓库的Fork 与clone
将octocat/Spoon-Knife 作为原仓库,在GitHub 上进行Fork,然后clone。
$ git clone git@github.com:hirocastest/Spoon-Knife.git |
给原仓库设置名称
我们给原仓库设置upstream 的名称,将其作为远程仓库。
$ git remote add upstream git://github.com/octocat/Spoon-Knife.git |
今后,我们的这个仓库将以upstream 作为原仓库的标识符。这个环境下只需要设定一次。
获取最新数据
下面我们从远程仓库实际获取(fetch)最新源代码,与自己仓库的分支进行合并。要让仓库维持最新状态,只需要重复这一工作即可。
$ git fetch upstream |
我们通过 git fetch 命令获取最新的数据,将upstream/master 分支与当前分支(master)合并。虽然本次示例没有可以合并的内容,但这一操作确实可以将最新的源代码合并至当前分支。
这样一来,当前分支(master)就获得了最新的源代码。各位在创建特性分支,编辑源代码之前,建议先将仓库更新到这一状态。一般情况下master 分支都会获取最新代码,很少需要Fork 的开发者亲自进行修正。
小结
本章中我们简单学习了Pull Request 的发送方法。想必各位已经发现,发送Pull Request 时不单要敲一敲代码,还需要进行很多其他工作。在实际开发现场,Pull Request 多少都会与传统的习惯或规范有些冲突。但是,诸多团队的实践表明Pull Request 确实有其显著的效果。作为一名投身于开源开发的程序员,应当尽早适应这一设计。笔者认为,对这种标准的设计或规范采取“总之先试试看”的态度,往往可以给现场带来活力,促进成员成长,给开发带来速度感。建议各位积极地去尝试。
第七章 接收Pull Request
发送过Pull Request 的人不多,接收过Pull Request 的人就更少了。下面让我们来学习接收Pull Request 时的相关知识,以备不时之需。
采纳Pull Request 的方法
接收到Pull Request 后,会如图7.1 中所示,在仓库的Pull Request标签页中显示别人发送过来的Pull Request 的一览表。现在让我们点击Pull Request 查看详细内容。

详细页面与我们发送Pull Request 时的页面大致相同。点击Mergepull request 按钮(图7.2),Pull Request 的内容便会自动合并至仓库。在采纳之前,请尽量将接收到的Pull Request 拿到本地开发环境中进行检查,确认是否能够正常运行以及代码是否安全。或者用将要在第8 章中介绍的Jenkins 等持续集成工具进行自动测试,保证新代码不破坏原有功能之后,再合并进仓库。

这里我们为各位讲解在本地开发环境中检查接收到的Pull Request的流程。
采纳Pull Request 前的准备
除确认Pull Request 送来的代码是否运行正常外,各位还请在代码审查上也多花些心思。GitHub 上可以快速高效地审查代码。下面我们就来介绍这些功能。
学会使用各种各样的功能进行代码审查,要比以往使用工具的审查轻松很多。如果团队中所有人都养成时常审查自己代码的习惯,其叠加效果将不可估量。
代码审查
如图7.3 所示,在GitHub 上可以对Pull Request 的具体的某行代码进行评论。这让代码审查变得十分高效。

查看图片的差别
在GitHub 上不但可以查看代码的差别,还有多种方法供用户查看图片的差别。这些内容在官方博客A 中有详细讲解,我们只在此挑出一些介绍给各位。
官方博客已经介绍了用于演示的仓库B,所以各位实际操作一下该仓库,就会发现这个功能有多么强大C。各位可以通过提交日志的Image View Mode Demo 来体验操作。
2-up
2-up 可以同时显示一张旧图片和一张新图片,从而完成对比(图7.4)。

Swipe
Swipe 可以在分界线左右两侧分别显示旧图片和新图片(图7.5)。鼠标可以拖动分界线左右移动,帮助用户对比细节差异和细微的颜色差异。

Onion Skin
Onion Skin 能够将新旧两张图片重叠放置,分阶段从旧图片慢慢过渡至新图片,用户可以自由调节过渡比例(图7.6)。通过这一功能,用户能够一步步确认新图片相对于旧图片的变化。

Difference
Difference 功能让笔者都感到吃惊,它能够直接抽出两张图片不一样的部分进行比较。如图7.7 所示,Difference 抽出了单片眼镜这一差别之处。要是拿这个功能去玩“大家来找茬”,一定是所向披靡。

像这样,使用GitHub 不但可以比较代码,还能够高效地对比图片。各位不妨让负责美工的同事也来试试。
在本地开发环境中反映Pull Request 的内容
下面我们来讲解收到Pull Request 后在本地开发环境中进行实际检查的流程。在本示例中,Pull Request 接收方的用户名为ituring,发送方的用户名为“PR 发送者”。
将接收方的本地仓库更新至最新状态
首先,将Pull Request 接收方的仓库clone 到本地开发环境中(图7.8 左侧)。如果已经clone 过,那么请进行pull 等操作更新至最新状态。

$ git clone git@github.com:ituring/first-pr.git |
获取发送方的远程仓库
将Pull Request 发送方的仓库设置为本地仓库的远程仓库,获取发送方仓库的数据。在本示例中,我们将图7.8 右上的仓库设置为远程仓库,进行fetch。
$ git remote add PR发送者 git@github.com:PR发送者/first-pr.git
$ git fetch PR发送者
省略
From github.com:PR发送者/first-pr
* [new branch] gh-pages -> PR发送者/gh-pages
* [new branch] master -> PR发送者/master
* [new branch] work -> PR发送者/work现在我们获取了Pull Request 发送方仓库以及分支的数据(PR 发送者/work)。
创建用于检查的分支
前面我们只获取了远程仓库的数据,这些数据尚未反映在任何一个分支中。因此我们需要创建一个分支,用来模拟采纳Pull Request 后的状态。由于这是我们第一个Pull Request,分支名就叫pr1。这一步相当于图7.9 左侧箭头(checkout)代表的操作。现在gh-pages 与pr1 分支的内容完全相同。

$ git checkout -b pr1 |
合并
下面要将已经fetch 完毕的“PR 发送者/work”的修改内容与pr1分支进行合并。也就是图7.9 下侧箭头(merge)代表的操作。
$ git merge PR发送者/work
Updating cc62779..243f28d
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)这样一来,pr1 分支中就加入了“PR 发送者/work”分支的修改内容。本示例中我们只修改了index.html 文件,所以检查一下index.html有没有显示错误即可。在实际开发中,各位需要通过自动测试等手段检查软件是否能正常运行。
删除分支
检查结束后pr1 分支就没用了,可以直接删除。我们切换至pr1 之外的分支,运行下面的代码。
$ git branch -D pr1
Deleted branch pr1 (was 243f28d).tips:如何提升代码管理技术如果能灵活运用分支的创建及合并,便可以在确保安全性的
前提下并行开发多个功能。这一技术在软件开发现场非常有用,
而且团队规模越大效果越好。
笔者认为掌握这一技术的最佳方法就是积累经验。在GitHub
上,可以通过自己给自己的不同分支发送Pull Request 进行练习。
想学会安全又专业的源代码管理,不妨先多多尝试Git 与
GitHub。采纳Pull Request
完成上述内容后,如果Pull Request 的内容没有问题,大可打开浏览器找出相应的Pull Request 页面,点击Merge pull request 按钮,随后Pull Request 的内容会自动合并至仓库(图7.10)

不过,由于我们已经在本地构筑了相同的环境,只要通过CLI 进行合并操作再push 至GitHub,Pull Request 中就会反映出Pull Request 被采纳后的状态(图7.11)。这个状态对应到本示例中就是“PR 发送者/work”分支合并到gh-pages 分支。

合并到主分支
首先,我们切换至gh-pages 分支。
$ git checkout gh-pages |
然后合并“PR 发送者/work”分支的内容。
$ git merge PR送信者/work |
这样一来“PR 发送者/work”分支就合并到了gh-pages 分支中。
push 修改内容
现在只剩下push 一步了,不过为保险起见,我们先查看本地与GitHub 端仓库内代码的差别。
$ git diff origin/gh-pages |
确认没有目的之外的差别后,进行push。
$ git push |
用这种方法处理后,仓库的Pull Request 会自动从Open 状态变为Close 状态(图7.12)。现在我们可以去查看网页,已采纳的源代码应该已经反映出来了。

以上便是安全接收Pull Request 的流程。Git 这种分散型版本管理软件乍看上去非常复杂,但熟悉每一个操作后,运用起来还是很简单的。
小结
本章中我们讲解了如何安全地接收Pull Request。
像本次示例中这种只有几行代码的Pull Request,大可直接打开
GitHub 网页点击合并,但在实际的开发现场中,接收到的Pull Request
往往会更加复杂,有时甚至与多个文件挂钩。所以各位要清楚本次示例
只是为练习而准备,是Pull Request 最简单的情况。
作为仓库的维护者要时刻记得,无法运行的代码绝不可以合入仓
库,否则会失去团队对你的信任。
另外还要注意,不要发布那些无法运行的、没有通过测试的、有语
法错误的源代码。
.jpg)
.jpg)




