-- 故国神游,多情应笑我,早生华发。

Git是什么?

Git是一个版本控制工具,代码管理工具,团队协作工具。它跟SVN等传统工具实现同样的目的;但从某种程度来说,它更快,更灵活。我想绝大多数读者都已经在接触这个工具了,并且用于日常的项目中去了。我的这篇文章,不是作为一个Git入门教程,也不是作为一本大块头的教科书。(说到教科书,我推荐下面的这本。这本书确实好,很全面。我的这篇文章,其实就是这本书的读书笔记而已。)

Pro Git -- http://git.oschina.net/progit/

接着说。我的这篇文章,主旨在于启发大家超越平时的使用局限,从另一种视角去看待Git和使用Git,让人有一种“啊哈,Git也能这么用“的感觉。这有点王婆卖瓜的味道了,这我必须承认。

Git最基本的用法

很多项目组,对于刚入职的程序员,会像下面这样对他们普及Git的知识:

#使用git clone克隆项目的代码
git clone https://your-project-address #使用git add添加修改的文件
git add your-filename #或者使用git add --all添加所有修改的文件
git add --all #使用git commit提交代码到本地的代码库
git commit -m some-description-message #使用git push提交代码到远程的代码库
git push #使用git pull拉下远程代码库中更新的代码
git pull

仅使用这五个命令,确实就足够进行团队协作了。这是使用Git的一种模式,而且无可厚非。不过,这样做有种仅仅把Git当成了纯粹的代码共享库的感觉,多多少少显得有点不大规范。其实,Git有更加丰富的能力,它能让你的项目开发有更加规范的流程,整个项目历史有更加清晰的追溯,分工协作上也更加井然有序。

Git的特色

Git的特色有哪些呢?

本地代码库

Git的最大特色可能就是作为一款分布式代码管理工具了。Git的绝大部分操作都是在本地进行的,这使得Git更快,而且更灵活。更灵活是因为在你的改动被提交到远程代码库之前,你可以在本地进行任意的操作。另一方面,这也意味着改动提交到远程被共享之后,就不方便再改动旧的提交历史了。所以,请慎重上传改动到远程。

历史

代码的提交历史是一个项目开发历程的记录。这其中不仅仅是情怀的记录。更重要的是,提交历史给了我们项目回溯的能力,从而让我们能够找出问题代码的来源,并修正它。Git的提交历史很强大,我们可以去翻看历史,还能附加上丰富的筛选条件;甚至可以修改有问题的历史。为了更好地利用Git的历史功能,我们要时刻保持提交历史的细致与干净。这意味着,我们要恰到好处地提交我们的代码,以及给它一个恰到好处的描述。

分支

Git的另一特色就是分支了。简单地说,分支就是不同的代码故事。它们相互区别又互有联系,渐行渐远又能在某一处汇合。使用分支,我们能够控制代码的不同走向,从而很好地安排我们的开发工作。灵活使用分支可以简化我们的很多工作。

简单

说Git简单,听起来有点怪怪的。如果你要了解Git 内部原理,那确实够复杂的。不过,Git的特色在于,即使你不了解这些原理,你仍然可以灵活运用它。只用去学Git的一些基本的概念就足够了,例如仓库,暂存,提交,历史,分支等。说实话,我到现在是说不出这些概念的具体定义的,而且也只是浅要地了解了下这些概念的原理。但是,这并不影响我去使用它。Git的特色在于,学些基本的概念和原理;剩下的,去用就够了。这让我想起了很多的Unix工具,如Vim,LaTex等,以及Unix哲学。

Git哲学

所谓Git哲学,是指使用Git的思维方式。下面是我觉得的Git哲学中最要紧的几点:

1. 保持细致清晰的提交历史

如果说提交历史是一个版本控制工具最核心的模块,我觉得一点也不为过。代码的提交历史要涵盖到具体的每一次改动,并且要有清晰一致的描述。这要求我们:

  1. 分成一个个小的提交,而不是一次大的提交
  2. 每次提交要给到一个简洁明了的描述

每次的提交得是一个具体的整体。所谓具体的,是指你的提交不能是一次笼统的或概括的改动;所谓整体,是指你的提交是一个已经完成的改动,而不能是悬而未决的。其实,这两点可以从你的提交说明中检验出来:

  1. 如果你的提交说明出现了像”完成用户登录以及实现网站首页布局“这样诸如以及、和这样的词汇,很可能就说明你的一次提交做了不止一件事情,应该分为两次提交。
  2. 如果你的提交说明出现了像”完成用户登录(一)“、”完成用户登录(二)“、”完成用户登录(终)“等这样连续关联的几个提交,说明你的这几个提交都在做一件事情,应该压缩为一个提交。

其实做到这一点也很容易 --

尽可能得多提交,尽可能得早提交,当完成一个小的功能点时就提交。

描述应该简洁明了。简洁是指能够用一句话描述你做的事情,并且不要覆盖其他不必要的信息;明了是指绝对不能含糊,不仅你能看得懂,也要让局外人也能看得懂。我列了几个要点:

  1. 如果不能做到简洁,或者说不能用一句话描述你的提交,很有可能说明你的提交在做不止一件事情。解决办法是分为多次提交。
  2. 不要使用含糊的或者概括的描述。像诸如”修复BUG“这样的描述是要不得的,得说清楚修复了什么BUG。例如说”修复用户登录后无故崩溃的BUG“或者”修复ISSUE 553“。
  3. 不要附加多余的信息,例如Author,Date,Email这些信息不要附加在描述里面了,因为提交历史已经自动包含这些信息了。

由于每个提交都是一个细致的改动单元,当项目进行到一定阶段,整个提交历史一定会显得很长。这时候你可能会觉得提交很多很杂,会淹没一些重要的版本发布提交,例如上线的版本v1.0,v1.1。我想这是不必要的担心,认真地对待提交历史,会让整个提交历史显得多而不杂。而且,完全可以为重要的提交打标签。请记住 git tag 命令,它可以为重要的提交打标签。

Git的提交历史是相当灵活的。你可以查看历史;更厉害的,你还可以去修改它。所以,你可以在任意时刻去修正你的历史。这里有个例外,就是你无法修改远程仓库的提交历史。一方面,有的远程代码仓库不支持提交历史的修改;另一方面,即使支持修改历史,这样的修改也会对其他开发者造成混乱。关于远程提交的准则是:

  1. 万万不得修改远程仓库的提交历史
  2. 在将代码提交到远程仓库之前,再大概检查一下准备push的系列本地提交

2. 即使滥用,也好过从不使用多分支

-- 一个分支就是一个故事,一个剧情。使用分支是为了不让第三者破坏完美的剧情。

要有意识地去使用多分支,而不是去忽略它。可以在下面的情况下考虑使用分支:

  1. 当在主线版本的基础上要加上一个实验性的特性的时候,为了不影响主线的开发进程,可以独立地在另一个分支上开发。这时分开的两个故事可以并行地发展,当实验分支稳定后,两分支可以合并为同一个分支。
  2. 当在主线版本的基础上修复一个BUG,为了不影响主线的正常开发,可以独立地另开一个分支进行。这时分开的两个故事可以并行地发展,当BUG修复后,可以将BUG分支并到主线分支。

这两个使用分支的策略都是本地临时分支使用策略。无论是添加特性分支还是BUG修复分支,它们都有存在周期短的特点,并且都只是为解决某个特定问题而存在的。当这个问题被解决以后,它们就要被并入到主分支,这样就完成了自己的历史使命。使用分支的一种情况是:当你要同时进行多个任务的时候。

无论是主分支,还是上面的特性添加分支或者BUG修复分支,都是你自己一个人的任务。你要同时完成两个或者三个任务。这时候才是分支发挥它最大作用的时候。不要在一个分支里同时做这几件事情,这样会违反第一哲学。记住:

每次只做一个任务;如果不得已要临时切换到其他任务,请使用分支。

Git既存在本地分支也存在远程分支;擅用分支,实际指的是灵活运用本地分支,而不是滥用远程分支。你可以随意地创建本地分支,频繁地在多个分支的下来回工作。但不要把这一切牵扯进远程分支。一般来说,远程分支是长期分支,是作为项目不同的发展阶段或者发展路线而存在的。例如稳定版、开发版、激进版等。这一般是项目决策者操心的事情。

再啰嗦一句:如果分支不再需要了,就删除它。

3. 开发过程尽可能私有化

私有化意味着封装。类似于面向对象上的封装,团队协作上也有封装的概念。你可以在本地灵活地使用Git的各种操作,而不会对他人造成任何影响。不过这种灵活性应局限于本地,而不要将冗余公开给远程的共享库。这方面有些准则,其中有些是对上面两个哲学的总结,如下:

  1. 为了简化工作,要灵活使用Git的各种本地操作
  2. 本地提交历史可以适当地混乱;但在提交到远程仓库前,一定要整理好
  3. 不要修改远程仓库的提交历史;而且,虽然不大可能,不要随意将新建分支推送到远程

实践Git

体现Git哲学的最好方式就是实践它,用Git的方式去工作,学会使用Git思维。接下来我会涉及到具体的Git操作。

基本操作

【这一部分纯当是复习。】

初始化一个新仓库:

git init

从现有仓库克隆:

git clone <your-project-address>

时刻检查目录状态:

git status

.gitignore 文件可以声明脱离版本控制的文件:

#haha

暂存修改:

git add <new-file-or-modified-file>

git add --all

如果不小心 git add 了某个文件,使用 git rm 从暂存区移除(附带--cached参数移除跟踪但不删除文件,以便稍后在 .gitignore 文件中补上):

git rm --cached <your-added-file>

想知道代码做了哪些改动(会精确到行),使用:

git diff <some-file>

git diff #这会显示所有文件的改动

上面的命令是显示工作目录中当前文件和暂存区域快照之间的差异;如果想知道暂存区域与最近一次提交之间的差异,附加 --cached 参数:

git diff --cached <some-file>

git diff --cached 

提交更新:

git commit #这会为你打开一个文本编辑器

git commit -m <your-commit-message>

提交历史

为了更好地说明用法,我使用了JFinal项目的Git源码作为例子。

查看提交历史

使用 git log 可以查看提交历史。你应该看到类似于下面的结果:

$ git log
commit 121e247032be9e4d6a3c7eb8035914f59857c43d
Author: James <jfinal@.com>
Date: Sun Jul :: + 修改变量名,actoin 改为 action commit d330532f9db493034578d5dac1ece43c65136569
Author: James <jfinal@.com>
Date: Sun Jul :: + 变量名修改

可以看到SHA-1 校验和,作者的信息,时间以及提交说明。Git使用校验和来指代每一次提交,例如输出第一行的 121e247032be9e4d6a3c7eb8035914f59857c43d 。另外,如果不产生歧义的话,可以用校验和的前几个字符来指代提交,例如 121e247032be9e4d6a3c 和 121e247032 。

如果想看某一次具体的提交,指明它的校验和即可:

$ git log d330532f9
commit d330532f9db493034578d5dac1ece43c65136569
Author: James <jfinal@.com>
Date: Sun Jul :: + 变量名修改 commit cdec1c3ceaceecfa4297c72efd8d8095640757f8
Author: James <jfinal@.com>
Date: Thu Jul :: + 修改 Cache.expireAt(...) 方法上的注释

这会显示从指定提交开始往前回溯的提交历史。如果只是想显示指定的这一个提交,用 - 参数( - 则显示两次提交,依次类推):

$ git log d330532f9 -
commit d330532f9db493034578d5dac1ece43c65136569
Author: James <jfinal@.com>
Date: Sun Jul :: + 变量名修改

使用 git log --pretty=oneline 可以显示更紧凑的提交信息,每次提交用一行来显示:

$ git log --pretty=oneline
121e247032be9e4d6a3c7eb8035914f59857c43d 修改变量名,actoin 改为 action
d330532f9db493034578d5dac1ece43c65136569 变量名修改
cdec1c3ceaceecfa4297c72efd8d8095640757f8 修改 Cache.expireAt(...) 方法上的注释
749fbe0d8e9da0e33fe2b30085f7467e86881e17 JFinal 2.0 release ^_^
52633ce2da704cf35a48aa9b8a1afe99d6c2ed1d JFinal 2.0 release ^_^
1e00c07348bd7b1ed16bb5c224e0a0de67b9b13b JFinal 2.0 release ^_^
0330d7ed5f3d742eaca201456927d9cecb40e215 JFinal 2.0 release ^_^
aa4a95af60a1dc12dfd649bd208de473dcfb369f jfinal 1.9 release ^_^
af6469eb6f49c23cba8215f2b0e9c8d51cd5f8c9 jfinal 1.9 release ^_^
4ab71ce41cca8b7d16bef89655b51e6de6548d30 jfinal 1.9 release ^_^
03a3c5a2bdb848ad2f9a30e29eba4f468176497f jfinal 1.9 release ^_^

使用 -p 选项展开每次提交所做的修改。例如我们使用下面的命令展开哈希前缀为 d330532f9 的那次提交的修改:

$ git log d330532f9 -p -
commit d330532f9db493034578d5dac1ece43c65136569
Author: James <jfinal@.com>
Date: Sun Jul :: + 变量名修改 diff --git a/.gitignore b/.gitignore
index 416db66..b1e58ba
--- a/.gitignore
+++ b/.gitignore
@@ -, +, @@ integration-repo
/build/ # IDEA metadata and output dirs
+/.idea/^M
*.iml
*.ipr
*.iws
diff --git a/src/com/jfinal/config/Routes.java b/src/com/jfinal/config/Routes.ja
index fab2604..8ad1b03
--- a/src/com/jfinal/config/Routes.java
+++ b/src/com/jfinal/config/Routes.java
@@ -, +, @@ public abstract class Routes {

使用 --stat 选项仅显示代码修改的统计信息(仅显示简要的增删行数统计,特别适合作代码审查):

$ git log --stat
commit 121e247032be9e4d6a3c7eb8035914f59857c43d
Author: James <jfinal@.com>
Date: Sun Jul :: + 修改变量名,actoin 改为 action src/com/jfinal/core/ActionMapping.java | +++---
file changed, insertions(+), deletions(-) commit d330532f9db493034578d5dac1ece43c65136569
Author: James <jfinal@.com>
Date: Sun Jul :: + 变量名修改 .gitignore | +
src/com/jfinal/config/Routes.java | +++---
src/com/jfinal/plugin/activerecord/Sqls.java | ++--
src/com/jfinal/plugin/activerecord/tx/TxByMethods.java | +++++++-------
src/com/jfinal/plugin/redis/RedisInterceptor.java | ++++++++++-
files changed, insertions(+), deletions(-)

我从《Pro Git》中摘出了一些选项及说明:

选项 说明
-p 按补丁格式显示每个更新之间的差异。
--stat 显示每次更新的文件修改统计信息。
--shortstat 只显示 --stat 中最后的行数修改添加移除统计。
--name-only 仅在提交信息后显示已修改的文件清单。
--name-status 显示新增、修改、删除的文件清单。
--abbrev-commit 仅显示 SHA- 的前几个字符,而非所有的 个字符。
--relative-date 使用较短的相对时间显示(比如,“ weeks ago”)。
--graph 显示 ASCII 图形表示的分支合并历史。
--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。

Git提交历史的一大特色在于可以按照条件筛选历史。例如筛选出最近两周到最近一周的提交历史:

$ git log --since=.weeks --until=.weeks
commit cdec1c3ceaceecfa4297c72efd8d8095640757f8
Author: James <jfinal@.com>
Date: Thu Jul :: + 修改 Cache.expireAt(...) 方法上的注释

我也列出了一些能够设置筛选条件的一些选项,同样来源于《Pro Git》:

选项 说明
-(n) 仅显示最近的 n 条提交
--since, --after 仅显示指定时间之后的提交。
--until, --before 仅显示指定时间之前的提交。
--author 仅显示指定作者相关的提交。
--committer 仅显示指定提交者相关的提交。
-- 命令的最后一个选项,后跟路径名表示只关心某一路径下的提交历史

修改提交历史

Git提交历史的另一大特色在于你可以修改它。不过格外注意的是,不要修改那些已经推送到远程版本库的提交历史

首先介绍如何撤销。这多少与修改历史无关,因为此时的历史可能还未形成。

如果只是希望将某个文件的改动从暂存区拿出来,而保持文件原有内容不变,使用命令:

git reset HEAD <your-file>

其中HEAD指代最近一次提交(*)。你可以使用HEAD变量来避免使用哈希值。

下述命令会令整个项目的状态回退(保留修改):

git reset HEAD

如果想将文件恢复到最近一次提交的版本,使用命令 git checkout 。这会使得其从暂存区拿出来(如果已经存入暂存区),并且文件内容恢复到最近一次提交的状态:

git checkout <your-file>

如果想直接撤销所有改动(包括修改),使用命令:

git reset --hard HEAD    #恢复所有改动到最近一次提交

git reset --hard HEAD~1  #恢复所有改动到最近一次提交的上一次提交

git reset --hard HEAD~2  #恢复所有改动到最近一次提交的前两次提交

git reset --hard d330532 #恢复到指定提交

带有 --hard 选项的版本回退是一个不可恢复的操作,请谨慎用之。

当我们完成提交后,发现漏掉了某些改动(例如忘记添加相应的API注释)。这时我们当然可以将改动作为一次新的提交提交一发。不过,还有其他的方案,即将漏掉的改动重新补回到对应的历史提交中,毕竟漏掉的改动确实是那次提交的一部分。Git给了我们修改提交的能力。

如果只是要修改最近的一次提交,使用 git add 命令暂存相应的改动,然后输入命令 git commit --amend 。这会把你代入修改提交说明的编辑器中。保存后退出,我们就完成了我们的提交修改。

如果只是想修改最近一次提交的提交说明,直接输入 git commit --amend ,然后进入编辑器修改提交说明并保存退出即可。

要修改历史中更早的提交,就要用到 git rebase -i 交互式的提交历史修改工具。我在命令行中输入:

git rebase -i HEAD~

它会带我进入一个文本编辑器中(在我的系统里意外地是VIM)。关注前四行,会看到我们可以修改最近的四次提交历史( HEAD~ 指定)。其中提交由早到晚地排列,与 git log 显示顺序相反。

 pick 749fbe0 JFinal 2.0 release ^_^
pick cdec1c3 修改 Cache.expireAt(...) 方法上的注释
pick d330532 变量名修改
pick 121e247 修改变量名,actoin 改为 action

这时我们可以修改某次提交,例如修改第2行的提交,只需将 pick 换成 edit 即可。然后保存并退出编辑器。

pick 749fbe0 JFinal 2.0 release ^_^
edit cdec1c3 修改 Cache.expireAt(...) 方法上的注释
pick d330532 变量名修改
pick 121e247 修改变量名,actoin 改为 action

像之前一样,使用 git commit --amend 修改提交;最后使用 git rebase --continue 完成修改任务。

我们也可以删除某个提交,只需将相应的提交行删掉即可。例如删除第2行的提交:

pick 749fbe0 JFinal 2.0 release ^_^
pick d330532 变量名修改
pick 121e247 修改变量名,actoin 改为 action

重新排列提交也是OK的。只需要重新排列即可。例如下面的编辑把最近一次提交移到最前,最早的一次提交移到最近:

pick 121e247 修改变量名,actoin 改为 action
pick cdec1c3 修改 Cache.expireAt(...) 方法上的注释
pick d330532 变量名修改
pick 749fbe0 JFinal 2.0 release ^_^

一个有用的功能是压制提交,即把多个提交合并成一次提交。例如下面的编辑将多个描述都为”JFinal 2.0 release ^_^“的提交合并为一个提交:

pick 0330d7e JFinal 2.0 release ^_^
squash 1e00c07 JFinal 2.0 release ^_^
squash 52633ce JFinal 2.0 release ^_^
squash 749fbe0 JFinal 2.0 release ^_^
pick cdec1c3 修改 Cache.expireAt(...) 方法上的注释

相应地,可以拆分一个提交。它的技巧在于 edit 某次提交,然后调用 git reset HEAD^ 回到父提交,然后再多次 git commit 即可。

git reset HEAD^
git add file1
git commit -m "add file1"
git add file2
git commit -m "add file2"
git rebase --continue

当准备进入交互式rebase工具时,如果当前的工作区存在修改而没有被提交,则会被禁止进入。此时我们可以先将修改提交;有时又不希望这么做,可能我们的代码改动还不能构成一个完整的提交。我们只希望先保存自己的工作,然后在需要的时候释放出来。这时就用到 git stash 命令了。

我们可以将改动保存在一个栈中,称之为储藏(Stashing)

git stash

然后查看栈状态:

$ git stash list
stash@{}: WIP on master: 121e247 修改变量名,actoin 改为 action

因为是栈,可以多次调用 git stash ,然后查看栈状态:

$ git stash list
stash@{}: WIP on master: 121e247 修改变量名,actoin 改为 action
stash@{}: WIP on master: 121e247 修改变量名,actoin 改为 action
stash@{}: WIP on master: 121e247 修改变量名,actoin 改为 action

然后在适当的时候,释放一个储藏。既可以释放指定的储藏,例如 stash@{} 、 stash@{1} 、 stash@{2},也可以默认释放最近的储藏(stash@{0}):

git stash apply    

git stash apply stash@{}

当储藏不再需要时,删除它:

git stash drop stash@{}

或者立即释放最近的储藏并删除它:

git stash pop

储藏的典型应用场景是当工作区有未提交的修改时,要切回到旧提交去修改( git rebase -i )或者切换到其他分支( git checkout )。

分支

Git的分支是从某个分支引出的一个分叉。在Git中创建分支时,一定是以某个分支为基准,这个分支就是你当前工作的分支。

分支基本操作

要查看当前在哪个分支下工作,输入命令:

git branch

此时会列出所有本地分支,行首标有*号的是当前工作分支。

创建分支:

git branch <new-branch-name>

切换到新分支:

git checkout <new-branch-name>

同时创建及切换分支:

git checkout -b <new-branch-name>

合并分支:

git checkout master
git merge <new-branch-name>

这能保证将新分支的改动合并到主分支。

删除分支:

git branch -d <new-branch-name>

只有已被合并的分支才能顺利删除,否则会提示错误。

如果要强制删除分支,使用命令:

git branch -D <new-branch-name>

分支思维

以上只是分支的基本操作命令。要想活用这些命令,就要知道分支的使用思维。分支的使用思维,我觉得就是一句话,重复之前的一句话:

当你要放下手中的任务,临时切换到其他任务时,使用分支。

我这里想举一个关于分支使用的简单的不能再简单的例子。

首先你在进行master上进行主线开发,实现功能点一。突然你临时接到任务,完成功能点二,并马上上线。此时你不得不放下手中的工作,投入到实现功能点二中去。

这时,你首先要做的是保存你正在进行的工作以便将来可以恢复。可以使用储藏 git stash 或者临时提交你的代码以在将来通过 git commit --amend 修改你的历史。这里假设你使用的是储藏。

然后新建分支并切换到新分支工作:

git checkout -b feature2

当你完成功能点二的开发时,提交你的代码:

git add --all

git commit -m "finish feature 2"

之后切换到主分支,合并feature2分支:

git checkout master

git merge feature2

改动可以提交到远程上线:

git push

然后你可以使用 git stash pop 恢复你的工作。

分支工作流程:

队伍中的长期分支控制了不同的开发进度。一般来说,应该有一个稳定分支和开发分支:你可以将master作为稳定分支,并配有一个develop分支;或者反过来,将master作为开发分支,并配有一个stable分支。这里假设master是稳定分支,而develop是开发分支。develop分支一般比master要超前,并且当测试稳定后才会并入到master分支发布。所以一般的工作流程就是:

在develop分支开发,测试稳定后并入到master分支发布

另外,如果已发布的版本遭遇到一个紧急BUG亟待修复,这时你应该保存develop分支的工作,然后切换到master分支去修复。因为要紧急发布,你应当切换到稳定的master分支完成BUG修复,而不是基于不稳定develop分支。当修复完毕并发布后,再回到develop分支恢复工作。

Git找bug的能力:

利用分支,我们可以很好地分离了我们的不同工作,不让它们相互干扰,从而减少bug的来源;利用历史,我们可以追踪代码的变化,为我们找出问题代码提供了途径。其实Git也为我们提供了其他的好用的工具,利于我们调试。

git blame:谁动了我的代码

你是否会惊奇你的代码为什么突然变成这副模样?没关系,使用 git blame 命令可以显示你的代码是谁最后修改的。命令格式:

git blame -L , simple.rb

可以显示文件simple.rb的第12至22行这块代码最后是谁、什么时候修改并提交的。

二分查找

还有一个有趣的命令是 git bisect 。它可以以二分查找的策略逐步逼近你在意的坏代码的来源。

首先输入 git bisect start 来启动二分查找。

输入 git bisect bad 来告知当前的提交已经是问题提交了。

然后输入 git bisect good <good-commit> 来告知你知道的最晚的正常提交。

接着就可以确定坏代码的来源在<good-commit>和当前提交之间,二分查找策略就可以开启了,这时Git监测处于中间的一个提交。假设从<good-commit>到当前提交一共有12个提交,记编号为0、1、…、11. 这时我们检出的提交应该是6.

你可以输入 git bisect good 来告知这个提交是正常的,这样它会舍弃编号为0、1、…、6的提交;也可以输入 git bisect bad 来告知问题依然存在,这样它会舍弃编号为7、8、…、11的提交。然后继续二分策略…一直到我们只剩下一个问题提交的时候。这个提交就是我们的问题提交的最初来源。

当你完成的时候,应该运行 git bisect reset 重新回到最初的地方。

Git使用总结:

  1. 在调用 git add --all 前,应该使用 git status 查看你要添加的改动,以免添加进不必要的改动。
  2. 要忽略的文件添加到 .gitignore 中去。
  3. git commit 时要给到一个简洁明了的描述。
  4. git push 前要用 git log 检查自己的提交历史。
  5. 必要时要修改本地提交历史,它们的命令是 git commit --amend 和 git rebase -i 。
  6. 不要修改远程提交历史,这往往意味着,你只能修改最近的几个历史。
  7. 要灵活地使用本地分支。它们的命令主要是 git branch 、 git checkout 等。
  8. 切换分支前注意保存自己的工作,用到的工具是 git stash 。
  9. 不要随意将本地分支push到远程。

超越Git

Git是伟大的,但我们依然要超越Git;毕竟Git只是一个工具,而我们是使用工具的人。Git的功能是代码管理和版本控制,但Git的本质在于其重视团队,重视协作的开发精神。它重视协作,但又不束缚个人的创造。它使得每个人都可以自由地编码,同时又保持整个项目开发的协调一致。

Git哲学与使用的更多相关文章

  1. 分布式版本控制系统 Git 教程

    简介 Git 是什么? Git 是一个开源的分布式版本控制系统. 什么是版本控制? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 什么是分布式版本控制系统? 介绍分布 ...

  2. Git让你从入门到精通,看这一篇就够了!

    简介 Git 是什么? Git 是一个开源的分布式版本控制系统. 什么是版本控制? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 什么是分布式版本控制系统? 介绍分布 ...

  3. Git让你从入门到精通,看这一篇就够了

    简介 Git 是什么? Git 是一个开源的分布式版本控制系统. 什么是版本控制? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 什么是分布式版本控制系统? 介绍分布 ...

  4. Git 基础(分布式版本控制系统)

    1.Git 简史 自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标.它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统. 2.Git 基 ...

  5. Git介绍和基本原理

    官方文档:http://git-scm.com/doc 1.1 起步 - 关于版本控制 本章关于开始学习 Git. 我们从介绍有关版本控制工具的一些背景知识开始,然后讲解如何在你的系统运行 Git,最 ...

  6. Git 快速入门--Git 基础

    Git 快速入门 Git 基础 那么,简单地说,Git 究竟是怎样的一个系统呢? 请注意接下来的内容非常重要,若你理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余. 在开始学习 G ...

  7. Git学习1:Git起步

    本系列文章部分原理和命令相关内容是从 https://git-scm.com/book/zh/v2 摘录,软件实际使用是总结自己的实践经验成文. 1. 关于版本控制 版本控制是一种记录一个或若干文件内 ...

  8. Git Pro Book

    目录 2nd Edition (2014) Switch to 1st Edition Download Ebook The entire Pro Git book, written by Scott ...

  9. Git——1.简介

    关于版本控制 Git基础 安装Git 初始运行Git前的配置 获取帮助 关于版本控制 版本控制(VCS)是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 本地版本控制系统 大多都 ...

随机推荐

  1. IT人的自我导向型学习:开篇杂谈

    报考大学时,家人让我报的是计算机系,那个时候,普遍都认为读计算机专业的人将来不用愁找不到工作.为何得出这样的结论不得而知,但是在过去三十年中,的确有很多响当当赚了大钱的IT人在影响着我们. 顺利的考取 ...

  2. Android 学习笔记之AndBase框架学习(二) 使用封装好的进度框,Toast框,弹出框,确认框...

    PS:渐渐明白,在实验室呆三年都不如在企业呆一年... 学习内容: 1.使用AbActivity内部封装的方法实现进度框,Toast框,弹出框,确认框...   AndBase中AbActivity封 ...

  3. HashMap的实现原理

    1.HashMap的数据结构 数组的特点是:寻址容易,插入和删除困难:而链表的特点是:寻址困难,插入和删除容易.那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的, ...

  4. css3照片墙+曲线阴影

    css3照片墙+曲线阴影 最近在学习jquery,晚上想复习下以前学过的知识,看到网上有关于css3照片墙的,感觉挺好玩的,就做了做.(以下图片均来自网络) 一.css3照片墙 html部分: < ...

  5. 安装percona-xtrabackup一直提示依赖冲突的一个解决办法

    我的Mysql是5.6版本,通过自己下载的rpm包执行安装: yum instal percona-xtrabackup-2.1.7-721.rhel6.x86_64.rpm 会出现如下的安装错误提示 ...

  6. sprint3(第四天)

    今天继续完成前台和后台的整合 燃尽图:

  7. 点餐系统web版功能需求

                餐厅到店点餐系统需求分析 (版本v1.0.0)               成文信息 主题词: 需求分析 作  者: 14商软ETC 文档类别: 审  核: 批  准: 文档性 ...

  8. [转]在SqlServer 中解析JSON数据

      在Sqlserver中可以直接处理Xml格式的数据,但因为项目需要所以要保存JSON格式的数据到Sqlserver中在博客:Consuming JSON Strings in SQL Server ...

  9. WebApi传参总动员(三)

    上篇介绍了如何从输入流中获取实体对象.本篇介绍以url形式传递参数.简单的参数不再赘述,这里主要实现形如(string name,Woman woman)这样的参数传递. 本篇及后面几章均涉及js调用 ...

  10. C#中dategridview数据导出为excel文件

    先从数据库中获取数据,绑定在datagridview中,再从dategridview中导出为excel文件 1.新建窗体,把控件datagridview和按钮设置好,如图