简陋的Git教程(也算是学习Git的个人总结吧)

覆盖了Git中很基础的一部分。命令行中输入的代码用深蓝色表示,输出的代码用棕色表示

首先安装Git:

$ sudo apt-get install git

$ sudo apt-get install git-doc git-svn git-email git-gui gitk

首先要设置好用户的姓名和邮箱(为保证提交时提交者和作者信息的正确性):

$ git config --global user.name “xx”

$ git config --global user.email “xx@xx.com”

然后进入工作目录gitTest

$ cd gitTest

$ git init

创建版本库,这时会发现工作目录文件夹下多了一个.git的目录

创建一个文件test.txt,内容为hello world

$ git add test.txt 提交到版本库之前需要进行的行为

$ git commit -m “test1” 提交到版本库,提交说明为test1

[master(root-commit) 1e96add] test1

1files changed, 1 insertions(+), 0 deletions(-)

create mode 100644 test.txt

修改文件test.txt,添加一行hello

$ git diff可以看到修改后的文件与版本库中的文件的差异

diff --git a/test.txt b/test.txt

index 3b18e51..48d1c4e 100644

--- a/test.txt

+++ b/test.txt

@@ -1 +1,2 @@

hello world

+hello

$ git status -s 查看文件状态,-s表示查看精简状态

M test.txt 注意:M前面有个空格,M处在第二个位置上  

如果要提交修改的话:

git commit -m "add new line"  
# On branch master  
# Changes not staged for commit:  
#   (use "git add <file>..." to update what will be committed)  
#   (use "git checkout -- <file>..." to discard changes in working directory)  
#  
#modified:   test.txt  
#  
no changes added to commit (use "git add" and/or "git commit -a")

说明修改后不能直接提交,要先对文件执行git add命令,将修改的文件添加到暂存区,然后才能提交

$ git add test.txt

这时$ git diff看不到差异

$ git diff HEAD 或 $ git diff master 会发现有差异,HEAD为当前版本库的头指针,master分支为当前的工作分支

diff --git a/test.txt b/test.txt  
index 3b18e51..48d1c4e 100644  
--- a/test.txt  
+++ b/test.txt  
@@ -1 +1,2 @@  
hello world  
+hello  

$ git status -s

M  test.txt这时M前面没有空格,M处于第一个位置上  

第一个位置代表版本库中的文件与处于中间状态(提交暂存区)中的文件相比有改动;第二个位置代表工作区当前的文件与处于中间状态(提交暂存区)中的文件相比有改动。

若在test.txt中再添加一行world


$ git status -s

MM test.txt  

$ git diff 工作区与暂存区的差异

diff --git a/test.txt b/test.txt  
index 48d1c4e..ecd874d 100644  
--- a/test.txt  
+++ b/test.txt  
@@ -1,2 +1,3 @@  
hello world  
hello  
+world  

$ git diff master、$ git diff HEAD 工作区和版本库的差异

diff --git a/test.txt b/test.txt  
index 3b18e51..ecd874d 100644  
--- a/test.txt  
+++ b/test.txt  
@@ -1 +1,3 @@  
hello world  
+hello  
+world  

$ git diff --cached 暂存区和版本库的差异

diff --git a/test.txt b/test.txt  
index 3b18e51..48d1c4e 100644  
--- a/test.txt  
+++ b/test.txt  
@@ -1 +1,2 @@  
hello world  
+hello  

这时如果直接提交的话,提交上去的test.txt只有两行,因为后面添加的world一行还未添加到暂存区

$ git commit -m "2 lines" 

[master 584d55d] 2 lines  
1 files changed, 1 insertions(+), 0 deletions(-)  

$ git add test.txt

$ git commit -m "3 lines"

成功提交三行的test.txt文件

[master 2b6b6e3] 3 lines  
1 files changed, 1 insertions(+), 0 deletions(-)  

修改test.txt,添加一行abc

$ git checkout -- test.txt

会发现新添加的行abc不见了,checkout会撤销工作区中test.txt文件尚未提交的修改,若添加到暂存区,则不能撤销。相当于取消自上次执行add以来的本地修改

$ git branch 可以查看分支

* master *号表示当前分支

$ git log 查看日志

commit ec615c08858af2e90de2fe7ba1911a00011360d0  
Author: xx <xx@xx.com>  
Date:   Fri Aug 10 15:56:11 2012 +0800  
  
  
   3 lines  
  
  
commit 584d55d6eee909a33130a8528326e2998dc82450  
Author: xx <xx@xx.com>  
Date:   Fri Aug 10 15:53:46 2012 +0800  
  
  
   2 lines  
  
  
commit 1e96add73eef18106747d7127f3279ff950f262d  
Author: xx <xx@xx.com>  
Date:   Fri Aug 10 13:23:36 2012 +0800  
  
  
   test1  

一长串字母和数字的组合是提交ID,由40个十六进制的数字组成,是SHA1哈希值。这种方法使得发生冲突的几率变得很小。


$ git rev-parse HEAD 显示引用对应的提交ID

ec615c08858af2e90de2fe7ba1911a00011360d0  

$ git rev-parse HEAD^HEAD的父提交

584d55d6eee909a33130a8528326e2998dc82450  

语法:master代表分支master中最新的提交;HEAD代表版本库中最近的一次提交;^指代父提交;^2代表第二个父提交;~可以指代祖先提交(父亲的父亲的...);ID^{tree}访问提交对应的树对象;ID:path/to/file访问某一次提交对应的文件对象;:path/to/file访问暂存区中的文件对象

$ git log --pretty=raw --graph通过提交对象之间的相互关联,可以识别出一条跟踪链,可以通过--graph参数看到

* commit ec615c08858af2e90de2fe7ba1911a00011360d0  
| tree abb0df6cfbbab361ce0002af3fd29c0d7237fa9c  
| parent 584d55d6eee909a33130a8528326e2998dc82450  
| author xx <xx@xx.com> 1344585371 +0800  
| committer xx <xx@xx.com> 1344585607 +0800  
|   
|     3 lines  
|    
* commit 584d55d6eee909a33130a8528326e2998dc82450  
| tree c1566beea63b0cc08e940916b5ae835d4ef86b92  
| parent 1e96add73eef18106747d7127f3279ff950f262d  
| author xx <xx@xx.com> 1344585226 +0800  
| committer xx <xx@xx.com> 1344585226 +0800  
|   
|     2 lines  
|    
* commit 1e96add73eef18106747d7127f3279ff950f262d  
 tree c3b8bb102afeca86037d5b5dd89ceeb0090eae9d  
 author xx <xx@xx.com> 1344576216 +0800  
 committer xx <xx@xx.com> 1344576216 +0800  
 
     test1  

$ git cat-file ec615c查看此提交ID(在不冲突的前提下取至少4位)对应的信息,需要加参数 -t类型 -s大小 -p详细信息

在原来基础上再添加abc一行,add并commit,提交说明设为"4 lines"

由日志可以看到提交ID为12e76d61f5c66af0a6c9e6302b2362bb36203bb1

$ git reset --hard HEAD^ 人为修改游标,将master重置到上一个老的提交上(--hard参数会替换引用指向、暂存区、工作区;--soft替换引用指向;--mixed即默认替换引用的指向、暂存区)

HEAD is now at ec615c0 3 lines  

$ cat test.txt

hello world  
hello  
world  

重置让提交历史也改变了

$ git log

commit ec615c08858af2e90de2fe7ba1911a00011360d0  
Author: xx <xx@xx.com>  
Date:   Fri Aug 10 15:56:11 2012 +0800  
  
  
   3 lines  
  
  
commit 584d55d6eee909a33130a8528326e2998dc82450  
Author: xx <xx@xx.com>  
Date:   Fri Aug 10 15:53:46 2012 +0800  
  
  
   2 lines  
  
  
commit 1e96add73eef18106747d7127f3279ff950f262d  
Author: xx <xx@xx.com>  
Date:   Fri Aug 10 13:23:36 2012 +0800  
  
  
   test1  

挽救错误的重置需要用reflog。

$ tail -5 .git/logs/refs/heads/master这个文件记录了master分支指向的变迁,最新的改变追加到文件的末尾。

1e96add73eef18106747d7127f3279ff950f262d 584d55d6eee909a33130a8528326e2998dc82450 xx <xx@xx.com> 1344585226 +0800commit: 3 lines  
584d55d6eee909a33130a8528326e2998dc82450 2b6b6e3554ddf8b79759294403b9c31c88bcf986 xx <xx@xx.com> 1344585371 +0800commit: 2 lines  
2b6b6e3554ddf8b79759294403b9c31c88bcf986 ec615c08858af2e90de2fe7ba1911a00011360d0 xx <xx@xx.com> 1344585607 +0800commit (amend): 3 lines  
ec615c08858af2e90de2fe7ba1911a00011360d0 12e76d61f5c66af0a6c9e6302b2362bb36203bb1 xx <xx@xx.com> 1344594757 +0800commit: 4 lines  
12e76d61f5c66af0a6c9e6302b2362bb36203bb1 ec615c08858af2e90de2fe7ba1911a00011360d0 xx <xx@xx.com> 1344595145 +0800HEAD^: updating HEAD  

$ git reflog show master | head -6 

ec615c0 master@{0}: HEAD^: updating HEAD  
12e76d6 master@{1}: commit: 4 lines  
ec615c0 master@{2}: commit (amend): 3 lines 这个amend是当时不小心用了一个commit的--amend参数,现在暂时忽略这个amend  
2b6b6e3 master@{3}: commit: 3 lines  
584d55d master@{4}: commit: 2 lines  
1e96add master@{5}: commit (initial): test1  

从这里可以看出消失的提交说明为"4 lines"的提交又出现了

$ git reset --hard master@{1}

HEAD is now at 12e76d6 4 lines  

$ cat test.txt

hello world  
hello  
world  
abc  

发现abc一行又回来了,提交历史也回来了,恢复master的操作页记录在了日志中。

$ git reflog show master | head -7

12e76d6 master@{0}: master@{1}: updating HEAD  
ec615c0 master@{1}: HEAD^: updating HEAD  
12e76d6 master@{2}: commit: 4 lines  
ec615c0 master@{3}: commit (amend): 3 lines  
2b6b6e3 master@{4}: commit: 3 lines  
584d55d master@{5}: commit: 2 lines  
1e96add master@{6}: commit (initial): test1  

如果再向test.txt中添加一行abcd

$ git add test.txt但不commit

$ git reset HEAD test.txt相当于取消之前执行的git add命令,是用指定提交状态(HEAD)下的文件test.txt替换掉暂存区中的文件

$ git diff会发现工作区和暂存区内容不一样了,暂存区的内容和版本库统一了

diff --git a/test.txt b/test.txt  
index e0593e8..d76f881 100644  
--- a/test.txt  
+++ b/test.txt  
@@ -2,3 +2,4 @@ hello world  
hello  
world  
abc  
+abcd

$ git reset --hard HEAD
~~~ 统一工作区、暂存区、版本库的内容,为下一步做准备

$ cat .git/HEAD查看当前HEAD的指向,指向master

ref: refs/heads/master

$ git branch -v

  • master 12e76d6 4 lines

$ git checkout 12e76d6^检出该ID的父提交

Note: checking out "12e76d6^".

分离头指针状态,指的是头指针指向了一个具体的提交ID,而不是一个引用(分支)  

You are in "detached HEAD" state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b new_branch_name

HEAD is now at ec615c0... 3 lines


$ git reflog可以看到HEAD的指针被更改了

ec615c0 HEAD@{0}: checkout: moving from master to 12e76d6^
12e76d6 HEAD@{1}: master@{1}: updating HEAD
ec615c0 HEAD@{2}: HEAD^: updating HEAD
12e76d6 HEAD@{3}: commit: 4 lines
ec615c0 HEAD@{4}: commit (amend): 3 lines
2b6b6e3 HEAD@{5}: commit: 3 lines
584d55d HEAD@{6}: commit: 2 lines
1e96add HEAD@{7}: commit (initial): test1

$ git rev-parse HEAD master发现HEAD和master的指向不一样了

ec615c08858af2e90de2fe7ba1911a00011360d0
12e76d61f5c66af0a6c9e6302b2362bb36203bb1

$ git branch现在不处于任何分支上

* (no branch)  
 master

这时若修改文件并提交,切换到master分支上以后,改动会消失,此提交虽然暂时在版本库中,但没被任何分支跟踪到,因此不能保证这个提交会永久存在。在文件中添加一行no branch

$ git add test.txt

$ git commit -m "no branch"

[detached HEAD 6014965] no branch
1 files changed, 1 insertions(+), 0 deletions(-)

$ git checkout master切换到master分支上,发现改动消失了。若为git checkout branch -- filename 则维持HEAD指向不变,用branch所指向的提交中的filename替换暂存区和工作区中相应的文件;git checkout -- .或git checkout .会取消所有本地相对于暂存区的修改。

$ git merge 6014965合并到当前分支

Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

$ cat test.txt

hello world
hello
world
HEAD

no branch
6014965


需要修改文件以解决冲突并add、commit,才能合并

$ git log --graph --pretty=oneline 会发现出现了不一样的分支

  •   29744ad4a7688770729de3c7c7cf00895025a89e to merge
    |  
    | * 60149653b9d2488e423782a7ae5cdf710e1cd5cb no branch
  • | 12e76d61f5c66af0a6c9e6302b2362bb36203bb1 4 lines
    |/  
  • ec615c08858af2e90de2fe7ba1911a00011360d0 3 lines
  • 584d55d6eee909a33130a8528326e2998dc82450 2 lines
  • 1e96add73eef18106747d7127f3279ff950f262d test1
    
    

最新提交会有两个父提交

新建test2.txt文件并add、commit,用于下文要介绍的删除命令。

若直接在工作区中删除文件,则对暂存区和版本库均不起作用。

$ git rm test2.txt 则此文件在工作区和暂存区被删除,更改名字也类似,git mv命令


$ git commit -m "use git rm to delete file"

[master 756e9d5] use git rm to delete file  
0 files changed, 0 insertions(+), 0 deletions(-)  
delete mode 100644 test2.txt  

新建test3.txt文件并add、commit,用于另一个删除方法。

首先在本地删除test3.txt


$ git status -s

D test3.txt

$ git add -u将本地有改动(包括修改和删除)的文件标记到暂存区,-A则是把工作区的所有改动及新增文件添加到暂存区。-i提供交互式界面,可以进行选择性添加

$ git status -s

D  test3.txt  

$ git commit -m "delete file test3.txt"

[master 8ce4b0e] delete file test3.txt  
0 files changed, 0 insertions(+), 0 deletions(-)  
delete mode 100644 test3.txt
文章导航