Git diff命令
Git diff命令的作用
diff命令的只有一句话:
git-diff - Show changes between commits, commit and working tree, etc
翻译一下就是git-diff—显示提交、提交和工作树等之间的更改。
是不是还是不懂?那就对了,直接看Git手册就能学会Git的,那是人干的事儿吗?手册是被浓缩后的内容,它肯定是对的,但绝对称不上友好,而我现在正在做的事情就是把它精简掉的那些东西再翻出来,让人可以与之友好的对话。
diff命令就是比较内容,那么到底那些内容需要比较呢?让那个我们来好好的捋一捋:
- Git有Work区,里面无疑就是普通的文件和目录了
- Git有Stage区,里面是Git处理过的暂存的文件(Blob)和目录(tree),也可以把它理解为一个暂存的版本
- Git有Commit区,里面就是各个提交的版本了
- Git还可以指定tag,它就相当于一个别名
没错,这些都是可以比较。
测试工程
为了测试git diff命令,我们需要搭建一个特别设计的git工程,它是下面的样子:
简要说明一下这张图:
- 最下面的紫色方块代表工作区
- 工作区上面的绿色方块代表暂存区
- 上面的各个圆圈代码commit
- 蓝色的飘带标记代表命名的tag,这里有a,b,c三个tag
- 黄色的飘带标记代表分支,这里有master,yes和no三个分支
为简单起见,我们处理一个文件readme.txt
要构造这样的git工程并不是一件很困难的事情。
首先要创建工程:
git init
然后做第一次提交,并打上tag:
# commit ver-a
echo a > readme.txt
git add .
git commit -m 'ver-a'
git tag a
同理,提交b和c:
# commit ver-b
echo b > readme.txt
git add .
git commit -m 'ver-b'
git tag b
# commit ver-c
echo c > readme.txt
git add .
git commit -m 'ver-c'
git tag c
在这里创建分支yes
git checkout -b yes
为yes分支做两次提交:
# commit ver-y1
echo -e 'y\n1' > readme.txt
git add .
git commit -m 'ver-yes1'
# commit ver-yes2
echo -e 'yes\n2' > readme.txt
git add .
git commit -m 'ver-yes2'
切换到master分支,再创建分支no:
git checkout master
git checkout -b no
为no分支做两次提交:
# commit ver-no1
echo -e 'no\n1' > readme.txt
git add .
git commit -m 'ver-no1'
# commit ver-no2
echo -e 'no\n2' > readme.txt
git add .
git commit -m 'ver-no2'
再次切换到master分支,这就让HEAD再次指向了master分支
git checkout master
修改一下暂存区的样子:
# stage
echo stage > readme.txt
git add .
最后修改一下工作区的样子:
echo work > readme.txt
搞定。当然,如果你嫌没完没了的敲命令太麻烦,那就搞个sh文件,把这些命令都复制过去。然后执行一次脚本。
Git diff命令的格式
比较工作区
这种最简单,用空参数表示工作区,就是下面的格式:
git diff [] [–] [
例如:比较工作区和暂存区,空参数表示工作区,工作区的上一级就是暂存区,所以也可以为空
$ git diff
diff --git a/readme.txt b/readme.txt
index 0e26ecd..b8f99f5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-stage
+work
这里比较的其实是暂存区的目录树和工作区的对应的目录树。如果只比较少数的文件,可以用-- path的形式。
$ git diff -- readme.txt
diff --git a/readme.txt b/readme.txt
index 0e26ecd..b8f99f5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-stage
+work
例如:比较工作区和commit指向的tree
$ git diff HEAD
diff --git a/readme.txt b/readme.txt
index f2ad6c7..b8f99f5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-c
+work
例如:比较工作区和tag指向的tree
$ git diff a
diff --git a/readme.txt b/readme.txt
index 7898192..b8f99f5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-a
+work
例如:比较工作区和分支指向的tree
$ git diff yes
diff --git a/readme.txt b/readme.txt
index f646a2f..b8f99f5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1 @@
-yes
-2
+work
比较文件
就是不用考虑git中各个对象的因素,直接做文件的比较,就是下面的格式:
git diff [] --no-index [–]
例如:直接比较两个文件
$ echo abc > abc.txt
$ git diff --no-index readme.txt abc.txt
diff --git a/readme.txt b/abc.txt
index b8f99f5..8baef1b 100644
--- a/readme.txt
+++ b/abc.txt
@@ -1 +1 @@
-work
+abc
比较暂存区
暂存区用–staged或–cached,就是下面的格式
git diff [] --cached [] [–] [
例如:比较暂存区和HEAD,因为暂存区的上一级就是提交区,默认就是HEAD:
$ git diff --cached
diff --git a/readme.txt b/readme.txt
index f2ad6c7..0e26ecd 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-c
+stage
例如:比较暂存区和其它commit指向的tree
$ git diff --cached HEAD~1
diff --git a/readme.txt b/readme.txt
index 6178079..0e26ecd 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-b
+stage
例如:比较暂存区和tag指向的tree
$ git diff --cached a
diff --git a/readme.txt b/readme.txt
index 7898192..0e26ecd 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-a
+stage
例如:比较暂存区和分支指向的tree
$ git diff --cached yes
diff --git a/readme.txt b/readme.txt
index f646a2f..0e26ecd 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1 @@
-yes
-2
+stage
比较提交
可以用HEAD标记或tag名称或分支名称指向提交,就是下面的格式:
git diff [] [–] [
git diff [] [–] [
git diff [] … [–] [
这三种格式其实并没有本质的区别。
例如:比较HEAD指向的tree和tag a指向的tree
$ git diff HEAD a
diff --git a/readme.txt b/readme.txt
index f2ad6c7..7898192 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-c
+a
例如:比较HEAD上一个提交指向的tree和分支yes指向的tree
$ git diff HEAD~1 yes
diff --git a/readme.txt b/readme.txt
index 6178079..f646a2f 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,2 @@
-b
+yes
+2
这样的例子有很多,了解规则后完全可以自由发挥。
从公共祖先比较提交
这个格式比较意思,它来源于实际工程需要,并赋予了一种特殊的格式:
git diff [] … [–] [
要演示这个效果,我们需要在当前master分支上再做一次提交:
例如:比较HEAD指向的tree和tag a指向的tree
git add .
git commit -m 'ver-d'
然后我们比较一下yes分支和HEAD:
$ git diff yes HEAD
diff --git a/abc.txt b/abc.txt
new file mode 100644
index 0000000..8baef1b
--- /dev/null
+++ b/abc.txt
@@ -0,0 +1 @@
+abc
diff --git a/readme.txt b/readme.txt
index f646a2f..b8f99f5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1 @@
-yes
-2
+work
这里确实比较出了HEAD和yes分支的所有差异,但是如果我们只想看看yes分支到底做了些什么改变,而不关心master分支在yes分支分叉后做的更多更新,上面的比较结果就显得混乱了。这是一个有实际工程需求的问题,解决起来当然不困难,只需要先找到yes分支开始处的提交,然后让yes分支和这个提交做比较就可以了。但是这样就要做两步才能看到结果。
估计Git是觉得这个需求太常用了,干脆一步到位吧,于是就有了下面的例子:
$ git diff HEAD...yes
diff --git a/readme.txt b/readme.txt
index f2ad6c7..f646a2f 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,2 @@
-c
+yes
+2
这里确实比较出了HEAD和yes分支的所有差异,但是如果我们只想看看yes分支到底做了些什么改变,而不关心master分支在yes分支分叉后做的更多更新,上面的比较结果就显得混乱了。这是一个有实际工程需求的问题,解决起来当然不困难,只需要先找到yes分支开始处的提交,然后让yes分支和这个提交做比较就可以了。但是这样就要做两步才能看到结果。
估计Git是觉得这个需求太常用了,干脆一步到位吧,于是就有了下面的例子:
$ git diff HEAD...yes
diff --git a/readme.txt b/readme.txt
index f2ad6c7..f646a2f 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,2 @@
-c
+yes
+2
可以这样理解…,它是说先别比较HEAD和yes,让我先找找HEAD和yes的公共父节点。好了,找到了,是XXX,然后再比较XXX和yes分支。