欧阳亮的博客

编程不止是一份工作,还是一种乐趣!!!

Git笔记

前前后后看了Git好多遍,一段时间不用又忘了,年纪大了记性不好,还是写点笔记^_^

git reset命令


在Git中,HEAD指向当前分支的最近一次commit。而git reset命令的作用就是重置HEAD到另外一个commit。


git reset --hard HEAD^ 撤销HEAD、index与working copy

Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit
f141537 (HEAD -> master) 3c
3e1d16d 2c
3c21b03 1c
Liangs-MacBook-Pro:test john$ git reset --hard HEAD^
HEAD is now at 3e1d16d 2c
Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit
3e1d16d (HEAD -> master) 2c
3c21b03 1c
Liangs-MacBook-Pro:test john$ git status -s
Liangs-MacBook-Pro:test john$

从输出可以看到,带–hard参数的撤销,会覆盖HEAD、index与working copy三个区域,是个非常危险的操作。


git reset --soft HEAD^ 只撤销HEAD

Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit
f141537 (HEAD -> master) 3c
3e1d16d 2c
3c21b03 1c
Liangs-MacBook-Pro:test john$ git reset --soft HEAD^
Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit
3e1d16d (HEAD -> master) 2c
3c21b03 1c
Liangs-MacBook-Pro:test john$ git status -s
M  file.txt
Liangs-MacBook-Pro:test john$

从输出可以看到,带–soft参数的撤销,只会覆盖HEAD,index与working copy保持不变。


git reset --mixed HEAD^ 撤销HEAD与index

Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit
f141537 (HEAD -> master) 3c
3e1d16d 2c
3c21b03 1c
Liangs-MacBook-Pro:test john$ git reset --mixed HEAD^
Unstaged changes after reset:
M	file.txt
Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit
3e1d16d (HEAD -> master) 2c
3c21b03 1c
Liangs-MacBook-Pro:test john$ git status -s
 M file.txt
 Liangs-MacBook-Pro:test john$

从输出可以看到,带–mixed参数(默认)的撤销,会覆盖HEAD与index,但working copy保持不变。


git diff命令


Liangs-MacBook-Pro:test john$ cat file.txt
1
2
3
Liangs-MacBook-Pro:test john$ echo "4" >> file.txt
Liangs-MacBook-Pro:test john$ git add file.txt
Liangs-MacBook-Pro:test john$ echo "5" >> file.txt
Liangs-MacBook-Pro:test john$ git status -s
MM file.txt
Liangs-MacBook-Pro:test john$

文件file.txt在HEAD中的值是123,在index中是1234,在working copy中是12345。


git diff file.txt 比较working copy与index中的差异

Liangs-MacBook-Pro:test john$ git diff file.txt
diff --git a/file.txt b/file.txt
index 94ebaf9..8a1218a 100644
--- a/file.txt
+++ b/file.txt
@@ -2,3 +2,4 @@
 2
 3
 4
+5
Liangs-MacBook-Pro:test john$


git diff --cached file.txt 比较HEAD与index中的差异

Liangs-MacBook-Pro:test john$ git diff --cached file.txt
diff --git a/file.txt b/file.txt
index 01e79c3..94ebaf9 100644
--- a/file.txt
+++ b/file.txt
@@ -1,3 +1,4 @@
 1
 2
 3
+4
Liangs-MacBook-Pro:test john$


git diff HEAD file.txt 比较HEAD与working copy中的差异

Liangs-MacBook-Pro:test john$ git diff HEAD file.txt
diff --git a/file.txt b/file.txt
index 01e79c3..8a1218a 100644
--- a/file.txt
+++ b/file.txt
@@ -1,3 +1,5 @@
 1
 2
 3
+4
+5
Liangs-MacBook-Pro:test john$


git rebase命令

在开发过程中,一般的场景是这样的:当完成需要的开发时,我们会进行一次commit,提交给QA人员测试。当发现bug时,我们会对代码进行修改,然后再次提交。最终测试通过后,我们会把开发分支合并到master分支,然后上线。

这个过程本身没什么,但是对于强迫症患者来说,代码的提交记录会很难看,feature和bug fix的记录会穿插在一起,比如:

Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit
018e112 (HEAD -> master) fix another bug
1834e4c fix a bug
b403d1f complete requirement
663b76f initial commit


通过git rebase命令,我们可以把b403d1f、1834e4c、018e112这三个commit合并成一个commit。

由于我们要合并的是近三个commit提交记录,可以使用git rebase -i HEAD~3,输入命令后进入一个交互页,如下:

pick b403d1f complete requirement                                               
pick 1834e4c fix a bug
pick 018e112 fix another bug

# Rebase 663b76f..018e112 onto 663b76f (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# 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


下面注释的部分是说明,要把三个commit合并成一个,我们只需要把前三行的commit改为下面这样:

r b403d1f complete requirement                                                  
f 1834e4c fix a bug
f 018e112 fix another bug


之后会弹出另一个页面,让我们输入合并后的commit message

complete requirement with bug fixed.                                            

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Tue Nov 13 20:54:35 2018 +0800
#
# interactive rebase in progress; onto 663b76f
# Last command done (1 command done):
#    reword b403d1f complete requirement
# Next commands to do (2 remaining commands):
#    fixup 1834e4c fix a bug
#    fixup 018e112 fix another bug
# You are currently editing a commit while rebasing branch 'master' on '663b76f'
#
# Changes to be committed:
#       modified:   file.txt
#


输入好新的commit message之后退出,就算合并完成,现在我们再用git log看看commit历史,原来的4条commit记录已经变成了2条。

Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit
a9b48f6 (HEAD -> master) complete requirement with bug fixed.
663b76f initial commit


冲突


当同一个文件在多个分支都被修改了,合并时有可能产生冲突,像下面这样:

Liangs-MacBook-Pro:test john$ git checkout -b newbranch
Switched to a new branch 'newbranch'
Liangs-MacBook-Pro:test john$ echo 'new line added in newbranch' >> file.txt
Liangs-MacBook-Pro:test john$ git add file.txt
Liangs-MacBook-Pro:test john$ git commit -m 'add a line in newbranch'
[newbranch f1517f7] add a line in newbranch
 1 file changed, 1 insertion(+)
Liangs-MacBook-Pro:test john$ git checkout master
Switched to branch 'master'
Liangs-MacBook-Pro:test john$ echo 'new line added in master' >> file.txt
Liangs-MacBook-Pro:test john$ git add file.txt
Liangs-MacBook-Pro:test john$ git commit -m 'add a line in master'
[master 7a09676] add a line in master
 1 file changed, 1 insertion(+)
Liangs-MacBook-Pro:test john$ git merge newbranch
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.
Liangs-MacBook-Pro:test john$


从输出中看出明确的看到file.txt文件在合并时产生了冲容,可以使用git status查询更多的信息:

Liangs-MacBook-Pro:test john$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

	both modified:   file.txt

no changes added to commit (use "git add" and/or "git commit -a")
Liangs-MacBook-Pro:test john$


both modified: file.txt告诉我们两个分支都对文件进行了修改。可以使用命令git merge --abort取消这次合并,或者手动解决冲突。我们看一下文件的内容:

Liangs-MacBook-Pro:test john$ cat file.txt
initial content
<<<<<<< HEAD
new line added in master
=======
new line added in newbranch
>>>>>>> newbranch
Liangs-MacBook-Pro:test john$


作为示例,我们简单的保留两个分支上的内容,然后通过一次commit解决冲突:

Liangs-MacBook-Pro:test john$ vim file.txt
Liangs-MacBook-Pro:test john$ cat file.txt
initial content
new line added in master
new line added in newbranch
Liangs-MacBook-Pro:test john$ git add file.txt
Liangs-MacBook-Pro:test john$ git commit -m 'fix conflict'
[master 0ef06b9] fix conflict
Liangs-MacBook-Pro:test john$ git log --pretty=oneline --abbrev-commit --graph
*   0ef06b9 (HEAD -> master) fix conflict
|\  
| * f1517f7 (newbranch) add a line in newbranch
* | 7a09676 add a line in master
|/  
* e00a6eb initial commit

通过带参数的git log命令可以很直观的看到分支的合并情况。