Devil May Code...

Vergil's Blog

分支的操作

在进行多个并行作业时,我们会用到分支。在这类并行开发的过程中,往往同时存在多个最新代码状态。如图所示,从master分支创建feature-A分支和fix-B分支后,每个分支中都拥有自己的最新代码。

master是Git默认创建的分支,因此基本上所有开发都是以这个分支为中心进行的。

从master分支创建feature-A和fix-B分支

从master分支创建feature-A和fix-B分支

不同分支中,可以同时进行完全不同的作业。等该分支的作业完成之后在于master合并。比如feature-A分支作业结束后与master合并,如图所示:

feature-A分支作业结束后的状态

feature-A分支作业结束后的状态

通过灵活运用分支,可以让多人同时高效地进行并行开发。

git branch —— 显示分支一览表

git branch命令可以将分支名列表显示,同时可以确认当前所在分支。

➜  git-test git:(master) git branch 
* master

可以看到master分支左侧标有“*”(星号),表示这是当前所在的分支。也就是说,我们正在master分支下进行开发。结果中没有显示其他分支名,表示本地仓库中只存在master一个分支。

git checkout -b —— 创建、切换分支

如果想以当前的master分支为基础创建新的分支,需要用到git checkout -b命令

切换到feature-A分支并进行提交

执行下面的命令,创建名为feature-A的分支:

➜  git-test git:(master) git checkout -b feature-A
Switched to a new branch 'feature-A'

实际上,连续执行下面两条命令也能收到同样的效果:

➜  git-test git:(feature-A) git branch feature-A
➜  git-test git:(feature-A) git checkout feature-A
Switched to branch 'feature-A'

创建feature-A分支,并将当前分支切换为feature-A分支。这时再来查看分支列表,会显示处于feature-A分支下

➜  git-test git:(feature-A) git branch            
* feature-A
  master

feature-A分支左侧标有“*”,表示当前分支为feature-A。在这个状态下像正常开发那样修改代码、执行git add命令并进行提交的话,代码就会提交至feature-A分支。像这样不断对一个分支(例如feature-A)进行提交的操作,我们称为“培育分支”。

下面来实际操作一下,在README.md文件中添加一行:

➜  git-test git:(feature-A) echo "feature-A" >> README.md

这里我添加了feature-A这样一行字母,然后进行提交。

➜  git-test git:(feature-A) ✗ git add README.md 
➜  git-test git:(feature-A) ✗ git commit -m "Add feature-A"
[feature-A ad5a4ba] Add feature-A
 1 file changed, 1 insertion(+)

于是,这一行就添加了feature-A分支中了。

切换到master分支

现在再来看一下master分支有没有受到影响。首先切换至master分支。

➜  git-test git:(feature-A) git checkout master 
Switched to branch 'master'

然后查看README.md文件,会发现README.md文件仍然保持原先的状态,并没有被添加文字。

➜  git-test git:(master) cat README.md 
# Git基本操作

feature-A分支的更改不会影响到master分支,这正是在开发中创建分支的优点。只要创建多个分支,就可以在不互相影响的情况下同时进行多个功能的开发。

切换回上一个分支

现在,我们再切换回feature-A分支

➜  git-test git:(master) git checkout -
Switched to branch 'feature-A'

像上面这样用“-”(连字符)代替分支名,就可以切换至上一个分支。当然,将“-”替换成“feature-A”同样可以切换到feature-A分支。

特性分支

Git和Subversion(SVN)等集中型版本管理系统不同,创建分支时不需要连接中央仓库,所以能够相对轻松地创建分支。因此,当今大部分工作流程中都用到了特性(Topic)分支。

特性分支顾名思义,是集中实现单一特性(主题),除此之外不进行任何作业的分支。在日常开发中,往往会创建数个特性分支,同时在此之外再保留一个随时可以发布软件的稳定分支。稳定分支的角色通常由master分支担当。

之前我们创建了feature-A分支,这一分支主要实现feature-A,除此实现之外不进行任何作业。即便在开发过程中发现了BUG,也需要再创建新的分支,在新分支中进行修正。

基于特定主题的作业在特性分支中进行,主题完成后再与master分支合并。只要保持这样一个开发流程,就能保证master分支可以随时供人查看。这样一来,其他开发者也可以放心大胆地从master分支创建新的特性分支。

主干分支

主干分支时刚才的特性分支的原点,同时也是合并的终点。通常人们会用master分支作为主干分支。主干分支中并没有开发到一半的代码,可以随时供他人查看。

有时需要让这个主干分支总是配置在正式环境中,有时又需要用标签Tag等创建版本信息,同时管理多个版本发布。拥有多个版本发布时,主干分支也有多个。

git merge —— 合并分支

接下来,我们假设feature-A已经实现完毕,想要将它合并到主干分支master中。首先切换到master分支。

➜  git-test git:(feature-A) git checkout master
Switched to branch 'master'

然后合并feature-A分支。为了在历史记录中却名记录下本次分支合并,我们需要创建合并提交。因此,在合并时加上--no-ff参数

➜  git-test git:(master) git merge --no-ff feature-A

随后编辑器会启动,用于录入合并提交的信息。

Merge branch 'feature-A'

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

默认信息中已包含了是从feature-A分支合并过来的相关内容,所以可以不做任何更改。将编辑器中显示的内容保存,关闭编辑器,然后就会看到下面的结果。

Merge made by the 'recursive' strategy.
 README.md | 1 +
 1 file changed, 1 insertion(+)

这样一来,feature-A分支的内容就合并到了master分支中了。

git log --graph —— 以图表形式查看分支

git log --graph命令进行查看的话,能清楚地看到特性分支(feature-A)提交的内容已被合并。除此之外,特性分支的创建以及合并也都清楚明了。

➜  git-test git:(master) git log --graph 

*   commit c00815d1247d0ef656b3ea31c8cd2079e04bf52b
|\  Merge: 7c1e8df ad5a4ba
| | Author: Vergil <vergil@vip.163.com>
| | Date:   Mon Sep 21 00:21:55 2015 +0800
| | 
| |     Merge branch 'feature-A'
| |   
| * commit ad5a4baec2ac4125c489b2461cb203ec14cd2658
|/  Author: Vergil <vergil@vip.163.com>
|   Date:   Sun Sep 20 23:48:30 2015 +0800
|   
|       Add feature-A
|  
* commit 7c1e8dfd917605d9e6d7451f77a2e02bb7a2966b
| Author: Vergil <vergil@vip.163.com>
| Date:   Sun Sep 20 21:20:27 2015 +0800
| 
|     Add index
|  
* commit 69ca7dddd02bca848adccd5d00a62903081de979
  Author: Vergil <vergil@vip.163.com>
  Date:   Sun Sep 20 20:34:43 2015 +0800
  
      First commit

基本操作

git init —— 初始化仓库

要使用Git进行版本管理,必须先初始化仓库。使用git init命令进行初始化。

建立一个目录并初始化仓库:(以下示例shell环境使用oh-my-zsh

➜  ~  mkdir git-test
➜  ~  cd git-test 
➜  git-test  git init
Initialized empty Git repository in /Users/vergil/git-test/.git/
➜  git-test git:(master) ls -al
total 0
drwxr-xr-x   3 vergil  staff   102  9 20 19:08 .
drwxr-xr-x+ 59 vergil  staff  2006  9 20 19:08 ..
drwxr-xr-x  10 vergil  staff   340  9 20 19:08 .git

如果初始化成功,执行了git init命令的目录下就会生成.git目录。这个.git目录里存储着管理当前目录内容所需的仓库数据。
在Git中,我们讲这个目录的内容称为“附属于该仓库的工作树”。文件的编辑等操作在工作树中进行,然后记录到仓库中,以此管理文件的历史快照。如果想将文件文件恢复到原先的状态,可以从仓库中调取之前的快照,在工作树中打开。开发者可以通过这种方式获取以往的文件。

git status —— 查看仓库状态

git status命令用于显示Git仓库的状态。这是一个十分常用的命令。
工作树和仓库在被操作的过程中,状态会不断发生变化。在Git操作过程中时常用git status命令查看当前状态。下面,让我们来实际查看一下当前状态:

➜  git-test git:(master) git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

结果显示了当前正处于master分支下。接着还显示了没有可提交的内容。所谓提交(Commit),是指“记录工作树中所有文件的当前状态”。
尚没有可提交的内容,就是说当前我们建立的这个仓库中还没有记录任何文件的状态。这里,建立一个README.md文件作为管理对象,为第一次提交做前期准备。

➜  git-test git:(master) touch README.md
➜  git-test git:(master) ✗ 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)

可以看到在Untracked files中显示了README.md文件。类似地,只要对Git的工作树或仓库进行操作,git status命令的显示结果就会发生变化。

git add —— 向缓存区中添加文件

如果只是用Git仓库的工作树创建了文件,那么该文件并不会被记录入Git仓库的版本管理对象中。因此,用git status命令查看README.md文件时,它会显示在Untracked files里。

要想让文件成功Git仓库的管理对象,就需要用git add命令将其加入暂存区(Stage或者Index)中。暂存区是提交之前一个临时区域。

➜  git-test git:(master) ✗ git add README.md 
➜  git-test git:(master) ✗ 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 be committed中了。

git commit —— 保存仓库的历史记录

git commit命令可以将当前暂存区中的文件实际保存到仓库的历史记录中。通过这些记录,我们就可以在工作树中复原文件。

记述一行提交信息

来实际运行一下git commit命令:

➜  git-test git:(master) ✗ git commit -m "First commit"
[master (root-commit) 69ca7dd] 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
# Changes to be committed:
#       new file:   README.md
#

在编辑器中记述提交信息的格式如下:

  • 第一行:用一行文字简述提交的内容
  • 第二行:空行
  • 第三行以后:记述更改的原因和详细内容

只要按照上面的格式输入,今后便可以通过确认日志的命令货工具看到这些记录。

在以#(井号)标为注释的Changes to be committed(要提交的更改)栏中,可以查看本次提交中包含的文件。将提交信息按格式记述完毕后,保存并关闭编辑器,以#标为注释的行不必删除。随后,刚才记述的提交信息就会被提交。

中止提交

如果在编辑器启动后想中止提交,将提交信息留空并直接关闭编辑器,随后提交就会被中止。

查看提交后的状态

执行完git commit命令后再来看当前状态:

➜  git-test git:(master) git status
On branch master
nothing to commit, working directory clean

git log —— 查看提交日志

git log命令可以查看以往仓库中提交的日志。包括可以查看什么人在什么时候进行了提交或合并,以及操作前后有怎样的差别。

先来看看刚才的git commit命令是否被记录了。

➜  git-test git:(master) git log

commit 69ca7dddd02bca848adccd5d00a62903081de979
Author: Vergil <vergil@vip.163.com>
Date:   Sun Sep 20 20:34:43 2015 +0800

First commit

如上所示,显示了刚刚的提交操作。commit栏旁边显示的“69ca7dddd...”是指向这个提交的哈希值。Git的其他命令中,在指向提交时会用到这个哈希值,

Author栏中显示的是给Git设置的用户命和邮箱地址。Date栏中显示提交执行的日期和时间。再往下就是该提交的提交概述。

只显示提交信息的第一行

如果指向让程序显示第一行简述信息,可以在git log命令后加上--pretty=short。这样一来开发人员就能够轻松地把握多个提交。

➜  git-test git:(master) git log --pertty=short

只显示指定目录、文件的日志

只要在git log命令后面加上目录(文件)命,便会只显示该目录下的日志。如果加的是文件名,就会只显示与该文件相关的日志。

➜  git-test git:(master) git log README.md

显示文件的改动

如果想要查看提交所带来的改动,可以加上-p参数,文件的前后差别就会显示在提交信息之后。

➜  git-test git:(master) git log -p

commit 69ca7dddd02bca848adccd5d00a62903081de979
Author: Vergil <vergil@vip.163.com>
Date:   Sun Sep 20 20:34:43 2015 +0800

    First commit

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29

比如,执行下面的命令,就可以只看README.md文件的提交日志以及提交前后的区别。

➜  git-test git:(master) git log -p README.md 

如上所述,git log命令可以利用多种参数帮助开发者把握以往提交的内容。不必勉强自己一次记下全部参数,每当有想查看的日志就积极去查,慢慢就能得心应手了。

git diff —— 查看更改前后的差别

git diff命令可以查看工作树、暂存区、最新提交之间的差别。

单从字面上可能很难理解,大家不妨亲手试一试。

在刚刚提交的README.md中写点东西:

➜  git-test git:(master) echo "# Git基本操作" >> README.md 

这里用Markdown语法谢了一行题目。

查看工作树和暂存区的差别

执行git diff命令,查看当前工作树与暂存区的差别。

➜  git-test git:(master) ✗ git diff


diff --git a/README.md b/README.md
index e69de29..3d71f6a 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# Git基本操作

由于还没用git add命令向暂存区添加任何东西,所以程序只会显示工作树与最新提交状态之间的差别。

这里解析一下显示的内容。“+”号标出的是添加的行,被删除的行则用“-”号标出。可以看到,这次只添加了一行。

git add命令将README.md文件加入暂存区。

➜  git-test git:(master) ✗ git add README.md 

查看工作树和最新提交的差别

如果现在执行git diff命令,由于工作树和暂存区的状态并无差别,结果什么都不会显示。

要查看与最新提交的差别,请执行git diff HEAD

➜  git-test git:(master) ✗ git diff HEAD 

diff --git a/README.md b/README.md
index e69de29..3d71f6a 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# Git基本操作

不妨养成这样一个好习惯:在执行git commit命令之前先执行git diff HEAD命令,查看本次提交与上次提交有什么差别,等确认完毕再进行提交。这里的HEAD是指向当前分支中最新一次提交的指针。

由于刚刚确认过两个提交之间的差别,所以直接运行git commit命令。

➜  git-test git:(master) ✗ git commit -m "Add index"
[master 7c1e8df] Add index
 1 file changed, 1 insertion(+)

保险起见,查看以下提交日志,确认提交是否成功。

➜  git-test git:(master) git log


commit 7c1e8dfd917605d9e6d7451f77a2e02bb7a2966b
Author: Vergil <vergil@vip.163.com>
Date:   Sun Sep 20 21:20:27 2015 +0800

    Add index

commit 69ca7dddd02bca848adccd5d00a62903081de979
Author: Vergil <vergil@vip.163.com>
Date:   Sun Sep 20 20:34:43 2015 +0800

    First commit

成功查到了第二个提交。


Powered by Typecho.