git基础学习笔记

一个由浅入深,学完后能立刻上手的Git教程,对于初学者来说,有一定的参考价值。

一、基本概念

1. 什么是Git

Git是一个开源的分布式版本控制系统。

Git的分布式:Git采用了分布式版本库的方式,不一定要服务器端软件支持即可在本地完成版本控制工作。

Git的版本控制:是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。

Git的最基本理解:简单的说Git就是用于保存文件在每次修改时的快照的一个管理系统,这些快照构成了一个个可以来回切换的版本。所以你可以通过使用git来切换快照实现文件恢复,这使得我们管理大型项目代码或者文件时得到了安全的保障机制。当然git的功能不只是快照的来回切换那么简单。它竟然是一种系统,那必然有着很多其它管理性的功能,如分支合并(或者说是快照合并)、各版本文件差异对照、本地仓库和远程仓库的连接和互动、从本地库推送到远程库,从远程库克隆到本地等等。

2. 安装Git

在使用Git前我们需要先安装 Git。Git 目前支持 Linux/Unix、Solaris、Mac和 Windows 平台上运行。

Git 各平台安装包下载地址为:http://git-scm.com/downloads

安装的过程就是傻瓜式下一步。然后打开git bash就可以输入git命令行执行Git操作。

执行命令的时候首先需要配置一下git用户信息:

修改用户名和邮箱地址:

1
2
3
4
5
6
7
#配置全局git用户信息
$ git config --global user.name "qcmoke"
$ git config --global user.email "qcmoke@gmail.com"

#如果想对某个单独的项目自定义git用户信息的话,只要将--global全局参数去掉即可,如下:
$ git config user.name "qcmoke"
$ git config user.email "qcmoke@gmail.com"

查看git用户名和邮箱地址命令:

1
2
$ git config user.name
$ git config user.email

3. git的文件管理机制

git把数据看作是小型文件系统的一组快照。每次提交更新时git都会对当前的全部文件制作一个快照并保存这个快照的索引。为了高效,如果文件没有修改,git不再重新存该文件,而是只保留一个链接指针指向之前存储的文件。所以git的工作方式可以称之为快照流。

4. git的工作流程

(1)在工作区中添加、修改文件

(2)将需要进行版本管理的文件存入暂存区

(3)将暂存区的文件提交到git仓库

5. git版本控制区域的情况

(1)工作区:就是你在电脑里能看到的目录。

(2)暂存区:英文叫stage, 或index。一般存放在 “.git目录下” 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。

(3)版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。

1539864917453

6. git管理下文件的状态

(1)已修改(modified)

(2)已暂存(staged)

(3)已提交(committed)

二、初始化git版本仓库

1
$ git init 		 #初始化git版本库,会自动创建了唯一master分支,并进入此分支

案例:

1
2
3
$ mkdir project  #创建文件目录
$ cd project #进入工作目录中
$ git init #初始化git版本库,会自动创建了唯一master分支,并进入此分支

project是所谓的工作区,工作区有一个隐藏目录.git,这个不算工作区,而是git的版本库。

git的版本库里存了很多文件,其中其中.git/index就是所谓的暂存区,即stage(或者叫index),它是一个二进制文件,还有指向master分支的一个指针文件HEAD等。

三、给暂存区添加文件

1
$ git add files   #将files添加到暂存区

案例:

1
2
$ echo "demo" >> demo.txt #创建一个内容为“demo”的测试文件
$ git add demo.txt #将demo.txt添加到暂存区

四、给git版本库提交文件

1
$ git commit -m "提交说明"  #一次性将添加到暂存区的所有文件提交到版本库中的master分支,-m后的信息是提交说明

案例:

1
2

$ git commit -m "commit demo.txt" #一次性将添加到暂存区的所有文件提交到版本库中的master分支,-m后的信息是提交说明

五、查看git文件状态

1
2
$ git status 	 #查看项目的当前状态信息。"AM" 状态的意思是,这个文件在我们将它添加到缓存之后又有改动。
$ git status -s #加了-s 参数,以获得简短项目的当前状态信息。

案例:

以下将从无文件改动或者添加---->创建文件----->添加文件到暂存区----->提交暂存区的所有文件到git版本库这几个不同阶段的文件状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#无文件改动或者添加的情况查看git状态:
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status
On branch master

No commits yet

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



#创建文件并查看git状态:
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ echo "demo" >>demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status
On branch master

No commits yet

Untracked files: #提示文件未被git跟踪,即此文件还没开始正式接受git的版本控制,一旦提交到暂存区开始到git版本库,就受到git的跟踪和版本控制。
(use "git add <file>..." to include in what will be committed)

demo.txt

nothing added to commit but untracked files present (use "git add" to track)

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)


#添加文件到暂存区并查看git状态:
#此时git开始跟踪提交的文件。
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git add demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status
On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)# 可以通过git rm --cached <file>清除提交的文件,git又不会跟踪文件了。或者通过git rm --cached * 清空暂存区的所以文件

new file: demo.txt



#提交暂存区的所有文件到git版本库并查看git状态:
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git commit -m "commit demo.txt"
[master (root-commit) 0e22aaf] commit demo.txt
1 file changed, 1 insertion(+)
create mode 100644 demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status
On branch master
nothing to commit, working tree clean #此时提示没有暂存区里没有任何文件需要提交到git版本库了

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)

img

六、查看历史提交记录

git的提交对象:文件对象存放在树对象里,树对象又存放在提交对象里。所以查看提交时的提交id值实际是所有提交文件组合而成的某种形式的哈希值。

1540408480201

1
2
3
4
5
$ git log  		#输出完整历史提交记录
$ git log --pretty=oneline #每次提交以一行输出历史提交记录
$ git log --oneline #每次提交以一行输出简短型历史提交记录
$ git log --decorate --all --oneline --graph #按分支情况查看提交记录
$ git reflog #查看历史所以commit id,并且能够显示到某个版本的步数,如:HEAD@{2}

注意:当提交记录后,git是无法再删除提交记录的(即版本仓库快照),除非删除了git仓库(.git目录),如果想要实现类似删除的效果只能通过移动HEDA指针的指向来实现。

案例:

以下就以修改demo.txt为例,进行历史提交记录查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git log
commit 0e22aafab1b3951a989105af18d3028b0bd6dc81 (HEAD -> master) #版本快照的id号,id值是通过哈希算法计算出来的哈希值,文件内容不同,哈希值必定不同。
Author: qcmoke <1667164190@qq.com>
Date: Thu Oct 18 21:47:18 2018 +0800

commit demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ vim demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git add demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git commit -m "modify demo.txt"
[master c3f6e58] modify demo.txt
1 file changed, 1 insertion(+), 1 deletion(-)

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git log
commit c3f6e587497f49870ab974ca4433a8e13467088d (HEAD -> master)
Author: qcmoke <1667164190@qq.com>
Date: Fri Oct 19 00:11:11 2018 +0800

modify demo.txt

commit 0e22aafab1b3951a989105af18d3028b0bd6dc81
Author: qcmoke <1667164190@qq.com>
Date: Thu Oct 18 21:47:18 2018 +0800

commit demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)

七、版本快照回滚操作

1539879799935

为了效果,在以上已经创建并修改了demo.txt的基础上,下面继续添加readme.txt文件,并对之进行一次修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#创建一个readme.txt文件,内容为"hello world !"
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ echo "hello world !" >> readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git add readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git commit -m "commit readme.txt"
[master 5b144ee] commit readme.txt
1 file changed, 1 insertion(+)
create mode 100644 readme.txt

#修改内容,向文件里添加"I am a student !"字符串
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ echo "I am a student !" >> readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git add readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git commit -m "modify readme.txt"
[master a10b617] modify readme.txt
1 file changed, 1 insertion(+), 1 deletion(-)



#查看历史提交记录
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git log
commit a10b617a02aa30ddd4e895d0653d33c7a28dcfad (HEAD -> master)
Author: qcmoke <1667164190@qq.com>
Date: Sat Oct 20 20:09:17 2018 +0800

modify readme.txt

commit 5b144ee3e670f710091d7441bcde01ceaf8c622e
Author: qcmoke <1667164190@qq.com>
Date: Sat Oct 20 20:06:38 2018 +0800

commit readme.txt

commit c3f6e587497f49870ab974ca4433a8e13467088d
Author: qcmoke <1667164190@qq.com>
Date: Fri Oct 19 00:11:11 2018 +0800

modify demo.txt

commit 0e22aafab1b3951a989105af18d3028b0bd6dc81
Author: qcmoke <1667164190@qq.com>
Date: Thu Oct 18 21:47:18 2018 +0800

commit demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)

以上操作完成后当前的情况如下图:

1540444596041

1. 回滚到上一个版本快照

1
$ git reset HEAD~   # 回滚到上个版本并且暂存区会被回滚到的仓库版本文件所覆盖。~表示上一个版本快照,~~表示上上个快照,以此类推,也可以用数字代替,如~10则表示前10个的版本
  • git reset --mixed HEAD~

    • 默认选项

    • 移动HEAD的指向,将其指向上一个版本快照

    • 将HEAD移动后指向的快照回滚到暂存区(暂存区会被回滚到的仓库版本文件所覆盖)

  • git reset --soft HEAD~

    • 移动HEAD的指向,将其指向上一个快照(不回滚到暂存区)
  • git reset --hard HEAD~

    • 移动HEAD的指向,将其指向上一个版本快照
    • 将HEAD移动后指向的快照回滚到暂存区
    • 将暂存区的文件还原到工作目录(工作目录会被暂存区的文件所覆盖)

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git reset HEAD~
Unstaged changes after reset:
M readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git log
commit 5b144ee3e670f710091d7441bcde01ceaf8c622e (HEAD -> master)
Author: qcmoke <1667164190@qq.com>
Date: Sat Oct 20 20:06:38 2018 +0800

commit readme.txt

commit c3f6e587497f49870ab974ca4433a8e13467088d
Author: qcmoke <1667164190@qq.com>
Date: Fri Oct 19 00:11:11 2018 +0800

modify demo.txt

commit 0e22aafab1b3951a989105af18d3028b0bd6dc81
Author: qcmoke <1667164190@qq.com>
Date: Thu Oct 18 21:47:18 2018 +0800

commit demo.txt
#可以发现记录里没有“modify readme.txt”的内容了,因为已经回滚到了上一个版本,而上一个版本并没有这条提交记录。


wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status
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: readme.txt

no changes added to commit (use "git add" and/or "git commit -a")
#可以发现一个有趣的事情,那就是git提示要向暂存区添加文件,并提交文件到版本库。原因是执行回滚后工作区的readme.txt要比暂存区的readme.txt新,相对于工作区对readme.txt做了修改。
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$

1540040885722

2. 回滚到特定版本快照

1
$ git reset 版本快照的id号

案例:

1
2
3
4
如本例中要从第四个版本回滚到第三个版本:
$ git reset c3f6e587497f49870ab974ca4433a8e13467088d
或者简写(能识别即可):
$ git reset c3f6e58

3. 回滚快照中的个别文件

1
$ git reset 版本快照  文件名/文件路径   #HEAD指针不移动,只回滚个别文件

4. 往新版本回滚

类似的只要记住版本快照的id号即可往往新版本回滚。

1
$ git reset 版本快照的id号

但往旧版本回滚后并且关闭了shell,如果在回退以后又想再次回到之前的版本,用git log会查不到版本id号。可以通过以下命令查看所有commit记录。

1
$ git reflog   #查看历史所以commit id

八、恢复工作区

1. 没有add的情况

1
2
3
4
5
#检出,只能清空全部已修改的问题件, 但是对于新建的文件和文件夹无法清空, 必须组合下面命令。如果只作用个别文件用参数 -- <file>
$ git checkout .

#清空所有新建的文件和文件夹
$ git clean -df

git clean的参数说明:

-f #删除 一些 没有 git add 的 文件
-df #删除 一些 没有 git add 的 文件和目录
-n #显示将要删除的文件或者目录

2. 已经add但没有commit的情况

1
2
3
$ git reset . #重置,覆盖暂存,但不覆盖本地工作区
$ git checkout .
$ git clean -df

注: 这种情git reset不允许使用--soft和--hard选项

3. 已经add并且commit的情况

1
$ git reset --hard HEAD~ #重置,覆盖暂存和本地工作区

九、checkoutreset的区别

  • 恢复文件

    当用checkoutreset来恢复指定快照中的指定文件时,两种的命令都不会改变HEAD指针的指向。

    他们的区别是:checkout命令会同时覆盖暂存区和工作区;而reset命令默认只是将指定快照的指定文件恢复到暂存区(--mixed)。

    *注意:在来恢复指定快照中的指定文件时使用git reset不允许使用--soft和--hard选项*

  • 恢复快照

    当用checkoutreset来恢复指定快照时,两种的命令都会改变HEAD指针的指向。

    他们的区别是:checkout命令只移动HEAD自身指向其他分支,并不移动HEAD所在的分支指针;而reset命令会移动HEAD自身指向其他分支并且会移动HEAD所在的分支指针指向其他版本库快照。

十、文件差异比较

1. 比较暂存区与工作区的文件差异

1
$ git diff file

案例:

我们对demo.txt的内容做以下修改。改成以下:

1
2
3
4
5
6
7
#include <iostream>
int main()
{
using namespace std;
cout << "Hello World !"<< endl;
return 0;
}

并添加到暂存区。

1
$ git add demo.txt

然后在工作区再对demo.txt做修改。修改为以下:

1
2
3
4
5
6
7
#include <iostream>
using namespace std;
int main()
{
cout << "Hello Git World !"<< endl;
return 0;
}
1
$ git diff demo.txt  #比较暂存区与工作区的demo.txt差异

打印的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
diff --git a/demo.txt b/demo.txt  #a/demo.txt是暂存区的demo.txt,b/demo.txt是工作区的
index ab28497..f6e1eb1 100644 #ab28497..f6e1eb1是文件id,100644是文件类型和权限
--- a/demo.txt #---表示旧文件
+++ b/demo.txt #+++表示新文件
@@ -2,6 +2,6 @@ #-表示旧文件,+表示新文件,数字表示开始行号和联连续的行数
using namespace std; #没有+或者-表示共有的内容
int main()
{
- cout << "Hello World !"<< endl; #-的内容表示旧文件所特有的内容
+ cout << "Hello Git World !"<< endl; #+的内容表示新文件所特有的内容
return 0;
}
\ No newline at end of file

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)

2. 比较两个历史快照的差异

1
$ git diff 快照id1  快照id2  file

3. 比较工作区和版本快照的差异

1
2
$ git diff 快照id file #工作区和具体的某个快照进行对比
$ git diff HEAD file #工作区和HEAD所指向的版本快照进行对比

4. 比较暂存区和版本快照的差异

1
2
$ git diff --cached file           #暂存区和HEAD所指向的版本快照进行对比
$ git diff --cached 版本快照id file #暂存区和具体的某个快照进行对比

十一、提交修改

需求:提交暂存区的所以文件到版本库后(如 git commit -m “commit all files as sunday !”),之后又改动了本地的某个文件,但不想因为此个例再另外提交一次而生成一个版本快照(即生成一个提交log),只想要让这个修改的文件添加到上次的提交中(当前最新的版本快照中)。

可以使用以下命令来实现。

1
$ git commit --amend  #提交暂存区的内容,但不产生新的版本快照,只是对原本的快照进行修改。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git add demo.txt #先添加修改过的demo.txt到暂存区

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: demo.txt


wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git commit --amend #对当前最新的快照进行修改,使暂存区的demo.txt文件覆盖最新版本仓库快照中的demo.txt。
[master 319082e] commit readme.txt
Date: Sat Oct 20 20:06:38 2018 +0800
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status
On branch master
nothing to commit, working tree clean

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git log #发现没有另外生成一个新的版本仓库快照。
commit 319082ee50a29e9f33a69d084b5f8acd17ac5b09 (HEAD -> master)
Author: qcmoke <1667164190@qq.com>
Date: Sat Oct 20 20:06:38 2018 +0800

commit readme.txt

commit c3f6e587497f49870ab974ca4433a8e13467088d
Author: qcmoke <1667164190@qq.com>
Date: Fri Oct 19 00:11:11 2018 +0800

modify demo.txt

commit 0e22aafab1b3951a989105af18d3028b0bd6dc81
Author: qcmoke <1667164190@qq.com>
Date: Thu Oct 18 21:47:18 2018 +0800

commit demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)

十二、删除各区的文件

1
2
3
4
$ git rm 文件名  #只删除工作区和暂存区的该文件,git rm . 则表示清空工作区和暂存区的文件而不是指定的单个文件
$ git rm -f 文件名 #当工作区和暂存区的内容不一样时,执行此命令来强制删除工作区和暂存区的该文件
$ git rm -cached 文件名 #只删除暂存区的文件,但保留工作区的文件。
$ git reset--hard HEAD~ #如果要删除文件同时要清除记录,可以采取回滚的方法

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ ls
demo.txt readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git rm demo.txt #删除工作区和暂存区的demo.txt
rm 'demo.txt'

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ ls
readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status #暂存区中提示已经删除demo.txt
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

deleted: demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git commit -m "deleted demo.txt" #提交本次修改
[master 65ca766] deleted demo.txt
1 file changed, 7 deletions(-)
delete mode 100644 demo.txt

十三、重命名文件

1
2
$ git mv 旧文件名 新文件名  #重命名工作区的文件,暂存区的文件也被重命名
$ mv 旧文件名 新文件名 #只重命名工作区的文件,暂存区的文件没有重命名

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ ls
readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git mv readme.txt Readme.txt #将工作区和暂存区的readme.txt重命名为Readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ ls
Readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

renamed: readme.txt -> Readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$ git commit -m "renamed readme.txt -> Readme.txt" #提交本次修改
[master 330b8d7] renamed readme.txt -> Readme.txt
1 file changed, 0 insertions(+), 0 deletions(-)
rename readme.txt => Readme.txt (100%)

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project (master)
$

十四、分支管理

常用命令

git branch # 查看分支
git branch <name> #创建分支
git checkout <name> #切换分支
git checkout -b <name> #创建+切换分支
git merge <name> #合并某分支到当前分支
git branch -d <name> #删除分支

1. 创建分支

初始化git仓库时,HEAD指针默认指向master分支。如果需要其他分支则需要创建和切换。用下面命令来实现此需求:

1
2
$ git branch dev  #创建dev分支,不切换分支
$ git checkout dev #切换到dev分支

或者

1
$ git checkout -b dev  #创建dev分支,并切换到dev分支

当我们创建新的分支,例如dev时,git会新建了一个指针叫dev,指向master相同的版本仓库快照,再把HEAD指向dev,就表示当前分支在dev上。

1540314110963

从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变。1540314143868

假如我们在dev上的工作完成了,就可以把dev合并到master上。最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

1540317847182

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉:

1540317908951

案例:

为了更好管理我们重新建立一个工作区和git仓库。

1
$ mkdir project2 && cd project2 && git init

并且第一次创建一个demo.txt文件并提交。第二次创建一个readme.txt文件并提交。第三次创建一个hello.txt文件并提交。

1
2
3
$ echo "I am demo.txt" >> demo.txt && git add demo.txt && git commit -m "add demo.txt"
$ echo "I am readme.txt" >> readme.txt && git add readme.txt && git commit -m "add readme.txt"
$ echo "I am hello.txt" >> hello.txt && git add hello.txt && git commit -m "add hello.txt"

查看各版本仓库快照的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git log
commit 2aaa0ee34690e39464debddfabf8199e961bd93f (HEAD -> master)
Author: qcmoke <1667164190@qq.com>
Date: Wed Oct 24 00:12:44 2018 +0800

add hello.txt

commit 5528f0a95659dcf6e5e13adc596f92bf837c327d
Author: qcmoke <1667164190@qq.com>
Date: Wed Oct 24 00:11:25 2018 +0800

add readme.txt

commit 9a9f62b0ddbb03ad3c6252442ec860412caa1033
Author: qcmoke <1667164190@qq.com>
Date: Wed Oct 24 00:10:13 2018 +0800

add demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$

版本库的快照历史情况如下图:

1540311560781

需求:创建dev并切换到该分支, 修改hello.txt,并提交生成新的版本仓库快照,使dev指针指向这个新的节点。

  1. 创建dev分支和切换分支:
1
2
3
4
5
6
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ git branch dev #创建dev分支,不切换分支

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ git checkout dev #切换到dev分支
Switched to branch 'dev'

创建dev分支和切换分支后的快照历史情况如下图:

1540311896958

  1. 修改hello.txt,并提交生成新的版本仓库快照,使dev指针指向这个新的节点。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ echo " world !" > hello.txt && git add hello.txt && git commit -m "modify hello.txt"
[dev c898832] modify hello.txt #修改hello.txt文件
1 file changed, 1 insertion(+)

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ git log
commit c8988323c83aebfe22f50e80f084089255bd00fc (HEAD -> dev)
Author: qcmoke <1667164190@qq.com>
Date: Wed Oct 24 00:42:19 2018 +0800

modify hello.txt

commit 2aaa0ee34690e39464debddfabf8199e961bd93f (master)
Author: qcmoke <1667164190@qq.com>
Date: Wed Oct 24 00:12:44 2018 +0800

add hello.txt

commit 5528f0a95659dcf6e5e13adc596f92bf837c327d
Author: qcmoke <1667164190@qq.com>
Date: Wed Oct 24 00:11:25 2018 +0800

add readme.txt

commit 9a9f62b0ddbb03ad3c6252442ec860412caa1033
Author: qcmoke <1667164190@qq.com>
Date: Wed Oct 24 00:10:13 2018 +0800

add demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$

当前版本仓库快照历史情况如下图:

1540313873959

当我们如果再切换回master分支时hello.txt又恢复到了原来的样子。即I am hello.txt。这是因为demo.txt是在dev分支上修改的。当切换回master分支时,而切换回来后HEDA所指向的版本库快照中的hello.txt是修改前的文件。

2. 查看当前分支

1
$ git branch   #查看版本仓库中的所以分支

案例:

1
2
3
4
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ git branch
* dev #星号表示当前所在的分支
master

3. 合并分支

1
$ git merge <name>  #合并某分支到当前分支,故需要提前切换到要合并到的目标分支

案例:合并dev分支到master分支。

1
2
3
4
5
6
7
8
9
10
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ git checkout master
Switched to branch 'master' #HEAD指针指向master

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git merge dev #合并dev到当前分支,即master分支
Updating 2aaa0ee..c898832
Fast-forward
hello.txt | 1 +
1 file changed, 1 insertion(+)

对于以上案例的合并原理是:HEAD指向的master指针移动到了dev指针所指的节点即完成了合并。

1540405021121

4. 删除分支

1
$ git branch -d <name>

案例:删除dev分支。需要切换到非dev的分支,否则会冲突。

1
2
3
4
5
6
7
8
9
10
11
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ git checkout master
Switched to branch 'master' #切换到master分支

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git branch -d dev #删除dev分支
Deleted branch dev (was c898832).

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git branch
* master

1540405047783

5. 解决分支合并冲突问题

解决:

第一步、编辑文件,删除特殊符号

第二步、把文件修改到满意的程度,保存好。

第三步、git add demo.txt

第四步、git commit -m "提交说明"

案例需求:

  1. 创建一个dev分支。并切换到dev分支,在这个分支里修改demo.txt并提交。
  2. 切换回master分支,同样在这个分支里修改demo.txt并提交(与在dev分支上的修改的内容不一样)。
  3. 合并dev分支到master分支。
  • 在dev上:

(1)创建一个dev分支。并切换到这个分支:

1
2
3
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git branch dev && git checkout dev
Switched to branch 'dev'

(2)修改demo.txt的内容为以下:

1
2
3
I am demo.txt
hello world !
modify by dev !

(3)添加并提交到版本库。

1
2
3
4
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ git add . && git commit -m "modify demo.txt by dev"
[dev 434700a] modify demo.txt by dev
1 file changed, 2 insertions(+)
  • 在master分支上:

(1)切换回master分支:

1
2
3
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (dev)
$ git checkout master
Switched to branch 'master'

(2)修改demo.txt的内容为以下:

1
2
3
I am demo.txt
hello world !
modify demo.txt by master !

(3)添加并提交到版本库。

1
2
3
4
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git add . && git commit -m "modify demo.txt by master"
[master c0b3eea] modify demo.txt by master
1 file changed, 2 insertions(+)

合并dev分支到master分支(合并dev指针所指向的仓库版本快照到master指针所指向的仓库版本快照)

1
2
3
4
5
6
7
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git merge dev #合并dev分支到master分支
Auto-merging demo.txt
CONFLICT (content): Merge conflict in demo.txt
Automatic merge failed; fix conflicts and then commit the result. #git提示合并冲突

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master|MERGING) #解决冲突完成前会一直保持在MERGING状态

此时master分支上的demo.txt的内容被git修改成了以下内容:

1
2
3
4
5
6
7
I am demo.txt
hello world !
<<<<<<< HEAD
modify demo.txt by master !
=======
modify by dev !
>>>>>>> dev

这是git根据两个分支的demo.txt内容不同,在合并时遇到冲突而修改的,这是git遇到合并冲突的表现。git要求我们对这个文件进行修改,修改成最终使用的内容,通过人为的方式来解决两个分支的冲突。

我们改demo.txt为如下内容:

1
2
3
4
I am demo.txt
hello world !
modify demo.txt by master !
modify by dev !

修改好以上demo.txt文件里的冲突内容后,再执行以下命令即可解决冲突:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master|MERGING)
$ 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: demo.txt

no changes added to commit (use "git add" and/or "git commit -a")

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master|MERGING)
$ git add demo.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master|MERGING)
$ git status
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)

Changes to be committed:

modified: demo.txt


wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master|MERGING)
$ git commit -m "resolve conflict" #此处和一般提交不同的是不能加文件名
[master 2f63fce] resolve conflict

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git status
On branch master
nothing to commit, working tree clean

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$

以上案例的图解如下:

1540444410771

6. 合并冲突分析

  • (1)合并不会冲突情形:

在原分支上的某一原节点创建第二个分支,此时原分支和新创建的分支指向这个原节点。从这个原节点往第二个分支的方向开发(修改)得到新的节点(在第二个分支上),再将第二个分支的新节点合并到原节点(在原分支上),这种合并不会发生冲突。
理解:“合并节点的快照”比“被合并节点的快照”的版本新,合并不会冲突。

情况如图:

1540444441499

  • (2)合并会冲突情形:

同一节点分叉开发后合并到其中一个分支上会合并冲突。
在原分支上的某一原节点创建第二个分支,此时原分支和新创建的分支指向这个原节点,在原分支的这个原节点上开始修改得到新的节点。在第二个分支的这个原节点上修改得到另一个新的节点,此时合并一个分支到另一个分支都会冲突。
理解:“合并节点的快照”和“被合并节点的快照”的版本一样新,合并会冲突。

1540444461052

十五、远程仓库

1. 添加远程仓库地址

从远程git服务器获取git远程仓库的地址,然后添加远程git仓库到本地,并为git远程仓库的地址另起别名。

1
$ git remote add 远程仓库的地址别名 远程仓库的地址

案例:

1
$ git remote add origin git@github.com:qcmoke/test.git

其中:git@github.com:qcmoke/test.git是远程仓库的地址。origin是远程仓库的别名。

2. 修改远程仓库地址

1
2
3
4
5
6
#修改本地仓库所对应的新远程仓库地址。
$ git remote set-url origin <new url>
#如果您想使用 HTTPS 协议,您使用的命令应形如
$ git remote set-url origin https://xxx.git。
#如果您想使用 SSH 协议,您使用的命令应形如
$ git remote set-url origin git@xxx:xxx.git。

3. 查看远程库

1
2
3
4
#列出你指定的每一个远程服务器的简写
$ git remote
#会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL
$ git remote -v

4. 推送本地库到远程库

1
$ git push 远程仓库的地址别名 远程库分支

案例:

1
$ git push origin master

如果使用的是git push,而不加 “远程仓库的地址别名 远程库分支”,那么需要设置本地当前分支指定到远程对应的提交分支。如果是通过克隆的方式下载的远程仓库到本地,那么默认本地当前分支已经是master分支了,所以不需要指定远程分支,默认也是能把本地当前分支推送到了远程仓库的master分支的。但如果本地当前分支是新建的dev分支,那么则需要通过 git push --set-upstream origin dev设置远程对应的提交分支。最后再用git push提交。

5. 克隆远程库到本地

1
$ git clone 远程仓库的地址 [本地仓库名]

案例:

1
$ git clone git@github.com:qcmoke/test.git pro

克隆的效果:

完整的把远程库下载到本地
创建origin远程仓库地址别名
初始化本地库

6. 拉取远程库到本地库

1
$ git pull  #取回远程主机某个分支的更新,再与本地的指定分支合并

git pull = git fetch 远程库url + git merge 远程库url别名/远程库默认分支

git fetch只是下载远程库到本地,但没有覆盖掉到本地库,需要用git merge来合并远程库的新节点到本地库的旧结点。

比如git pull命令默认会被解析成如下:

1
2
git fetch origin master #从远程主机的master分支拉取最新内容 
git merge FETCH_HEAD #将拉取下来的最新内容合并到当前所在的分支中

7. 解除远程仓库关联

比如要解除和远程仓库『origin』的关联,运行:

1
$ git remote rm origin

注意,此命令是解除了本地仓库和远程仓库的关联,并不是删除了远程仓库的数据。

8. 解决推送本地库到远程库冲突的问题

当有两个本地库都连接着同一个远程库,当有一个修改了文件并且推送到了远程库后。另外有个仓库也修改到了同样的文件。如果内容两个推送的修改不一致,那么就造成了冲突。

比如根据之前的例子,本地有pro和project2两个仓库。

(1)修改project2的readme.txt文件的内容为以下:

1
2
I am readme.txt
modify readme.txt by project2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git add readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git commit -m "modify readme.txt by project2"
[master 17938ff] modify readme.txt by project2
1 file changed, 2 insertions(+), 1 deletion(-)

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/project2 (master)
$ git push origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 292 bytes | 146.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:qcmoke/test.git
12f4d1d..17938ff master -> master

(2)在project2修改并提交readme.txt到远程库后,又在本地的pro仓库修改readme.txt,同样提交readme.txt到远程库。

本地的pro修改readme.txt的内容为以下:

1
2
I am readme.txt
modify readme.txt by pro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/pro (master)
$ git add readme.txt

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/pro (master)
$ git commit -m "modify readme.txt by pro"
[master 32cde6b] modify readme.txt by pro
1 file changed, 2 insertions(+), 1 deletion(-)

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/pro (master)
$ git push origin master
To github.com:qcmoke/test.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'git@github.com:qcmoke/test.git'
hint: Updates were rejected because the remote contains work that you
do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/pro (master)
$

git推送失败,原因是推送的内容与本地仓库project2的推送有冲突,project2推送时,已经更新了一个版本。而pro此时比远程仓库旧了一个版本,当推送时就与远程库的版本一样了,但问题是同样推送到同一个远程仓库版本,而远程仓库的这个版本中的readme.txt已经被project2修改了。git远程仓库无法确定要哪个本地库的修改,所以就冲突了。这要求在被其他本地库修改之前就要更新本地库pro到与远程库相同的版本快照,才能进行修改后推送。

通过以下命令来更新本地库pro到与远程库相同的版本快照:

1
$ git pull origin master

然后再将本地pro的readme.txt的内容修改为以下:

1
2
I am readme.txt
modify readme.txt by pro

然后再推送添加提交到本地仓库,再将本地仓库的最新版本推送到远程仓库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/pro (master)
$ git pull origin master
From github.com:qcmoke/test
* branch master -> FETCH_HEAD
Already up to date.

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/pro (master)
$ git push origin master
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 606 bytes | 202.00 KiB/s, done.
Total 5 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:qcmoke/test.git
17938ff..830af40 master -> master

wen@DESKTOP-BHMU9KJ MINGW64 ~/Desktop/pro (master)
$

十六、团队协同开发

两种方式:

  1. 项目管理者发送请求邀请其他成员加入到项目里,需要管理者通过用户名或者邮箱邀请其他成员,同时其他成员也要同意接受才能加入到项目中,如此其他成员就能git push。
  2. 其他成员先fork项目成为自己的仓库,然后git push到自己的仓库后,发送Pull Requests请求给管理者。管理者同意并merge合并其他成员的修改内容到自己的仓库里。

详细步骤待完成… 😂