史上最浅显易懂的 Git 教程

写在前面

讲道理,这次不是标题党。没错,就是这么猖狂。我为什么敢这么说?看完就懂了,彩蛋在文末。

Git 简介

Git 是目前世界最先进的分布式版本控制系统(没有之一)。那什么是版本控制?简单来说就是能记录文件的每次改动。那为什么有分布式一说?这里就要先说集中式版本控制系统了。

  • 集中式版本控制系统

版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的东西推送给中央服务器。然而集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网还好,带宽够大,速度够快。但是在互联网上,遇到网速很慢的话,就很尴尬了。

  • 分布式版本控制系统

分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在自己的电脑上。既然每个人电脑上都有一个完整的版本库,那么多个人怎么协作呢?比如说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。和集中式版本控制系统相比,分布式版本控制系统的安全性更高,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那么复制一份就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。当然,Git 的优势不单是不必联网这么简单,后面我们还会看到Git及其强大的分支管理,把SVN(一种集中式版本控制系统)等远远抛在身后。

安装 Git

现在 Git 可以在 Linux、Unix、Mac、Windows 这几大平台上正常运行了。

这里我在 Win 上操作示范。

官网下载 : https://git-for-windows.github.io

网盘下载 : http://pan.baidu.com/s/1mhB40fm

安装完之后,鼠标右键会自动添加两个 Git GUI Here 和 Git bash Here。点击 Git GUI Here 后输入命令 git 可以看到参数信息,即说明安装成功。

安装之后,还需要最后一步设置,在命令行输入 :

1
2
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

因为 Git 是分布式版本控制系统,所以每个机器都必须自报家门 : 你的名字和邮箱。这里需要注意 git config 命令的 –global 参数,表示你这台机器所有的 Git 仓库都会使用这个配置,当然,你也可以对某个仓库指定不同的用户名和Email地址。

创建版本库

  • 创建版本库

版本库又名仓库,Repository 。可以简单地理解为一个目录,这个目录里面的所有东西都可以被 Git 管理起来,每个文件的修改、删除,Git 都能跟踪,以便在任何时刻都可以追踪历史,或者再将某个时刻还原。

右键点击 Git GUI Here 可以 点击 Creat New Repository 新建一个仓库。这里最好确保目录名不要包含中文。然后就是把这个目录变成Git 可以管理的仓库。

1
2
$ git init
Initialized empty Git repository in C:/Users/ssc/Desktop/.git/

瞬间 Git 就把仓库创建好了,而且告诉你是一个空仓库。ls -ah 可以看到当前目录下多一个 .git 目录。

1
2
3
$ ls -ah
./ ../ .git/ desktop.ini Repository/
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
  • 把文件添加到版本库

这里首先在明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如 TXT,网页,所有的程序代码等等,Git 也不例外。版本控制系统可以告诉你每次的改动,比如在第五行添加一个单词 “Omooo” ,在第八行删除一个单词“Test”。而图片这些二进制文件,虽然也能由版本控制系统管理,但是没法追踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从 100KB 改成了 200KB,但是改了啥,版本控制系统不知道,也没法知道。

不幸的是,微软的Word格式是二进制格式。因此版本控制系统没法跟踪Word文件的改动,前面我们举得例子只是为了演示,如果要真的使用版本控制系统,就要以纯文本方式编写文件。

因为文本是有编码的,比如中文常有的GBK编码,如果没有历史遗留问题,强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。

特别注意 :

千万不要使用 Windows 自带的记事本编辑任何文本文件。原因是微软开发记事本的团队在保存UTF-8编码的文件时候,会默认在文件开头添加 0xefbbbf (十六进制)的字符。建议使用Notepad++代替记事本。记得把Notepad++的默认编码设置为UTF-8 without BOM即可。

然后我们在桌面写一个 TXT 文件,因为我的仓库文件夹是放在桌面上的,所以我就把写的这个 TXT 文件也放在桌面上了。

第一步 : 用命令 git add 告诉 GIt ,把文件添加到仓库

第二步 : 用命令 git commit 告诉 Git ,把文件提交到仓库

1
2
3
4
5
6
7
8
9
10
11
12
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ pwd
/c/Users/ssc/Desktop
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ git add readme.txt
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ git commit -m "wrote a readme.txt file"
[master (root-commit) 5be3ee9] wrote a readme.txt file
1 file changed, 1 insertion(+)
create mode 100644 readme.txt

简单解释下 git commit 命令,-m 后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的。

执行完命令之后,可以看到 Git 给我返回了信息,一个文件被改动,插入了一行内容。

当然,可以一次提交多个文件,这也就解释了要用到两步。如下 :

1
2
3
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."

时光穿梭机

我们已经成功提交了一个readme.txt文件,现在我们修改readme.txt文件,修改(加一行内容)里面的内容。在运行 git status :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ 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 direc
modified: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
Notepad++.lnk
desktop.ini
no changes added to commit (use "git add" and/or "git commit -a")

git status 命令可以让我们时刻掌握仓库当前的状态,上面的返回信息告诉我们,readme.txt被修改了,但是还没有提交改动。

虽然 Git 告诉我们文件被修改了,但是并没有告诉我们具体修改了什么内容,可以使用 git diff 命令查看修改了什么。

1
2
3
4
5
6
7
8
9
10
11
$ git diff
diff --git a/readme.txt b/readme.txt
index 5e1c309..2d57c34 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,2 @@
-Hello World
\ No newline at end of file
+Hello World
+Omooo
\ No newline at end of file

可以看到我们在第二行添加了字符串 “Omooo”,红色竖杠是我们换行的标志 。也就是说,减号是之前的版本,加号是修改之后的版本。

知道修改什么了,在提交仓库就放心多了。第一步 : git add readme.txt 在执行第二步之前使用命令 git status 看看当前仓库的状态。它会给我们反馈我们将要提交readme.txt,然后就可以放心的执行第二步了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ git add readme.txt
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
Notepad++.lnk
desktop.ini
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ git commit -m "add Omooo"
[master 7c8295f] add Omooo
1 file changed, 2 insertions(+), 1 deletion(-)

版本回退

现在我们已经学会了修改文件并提交到GIt版本库,我们再练习一下,在往readme.txt文件里添加第三行”love”,然后提交。

就像这样,我们不断对文件进行修改,然后不断提交修改。我们不可能每次都记住修改了什么,可以用 git log 命令查看我们提交修改的记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git log
commit fe980eab425d9e2f2469313231c2916a6c335028
Author: TestOmooo <2270334821@qq.com>
Date: Sun Apr 9 16:17:13 2017 +0800
add love
commit 7c8295fc12616f4c012cd4672dc498a5acb53c18
Author: TestOmooo <2270334821@qq.com>
Date: Sun Apr 9 16:03:20 2017 +0800
add Omooo
commit 5be3ee9f32a76a292daef68269918d6fa54f5be0
Author: TestOmooo <2270334821@qq.com>
Date: Sun Apr 9 15:37:05 2017 +0800
wrote a readme.txt file

可以看到我们做了三次提交。嫌输出信息太多眼花缭乱,可以加上 –pretty=oneline 参数。

1
2
3
4
5
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ git log --pretty=oneline
fe980eab425d9e2f2469313231c2916a6c335028 add love
7c8295fc12616f4c012cd4672dc498a5acb53c18 add Omooo
5be3ee9f32a76a292daef68269918d6fa54f5be0 wrote a read me.txt file

前面一段很长的字符串其实是 commit id (版本号)。是由SHA1计算出来的一个非常大的数字,用十六进制表示。为什么需要这么一大串数字呢?因为 Git 是分布式的版本控制系统,如果大家都用1,2,3等等还做为版本号,就产生冲突了。

然而有时候,我们想要回到以前的版本,怎么办呢?这里我们回到 add Omooo 那个版本。

首先,Git 必须知道当前版本是哪个版本,在 Git 中,用HEAD 表示当前版本,也就是最新提交的版本。上一个版本就是 HEAD^,上上个版本就是HEAD^^,当然往上100个版本写一百个^说不过去,所以写成了HEAD~100。

现在我们可以使用 git reset 命令还回到上一个版本。

1
2
3
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ git reset --hard HEAD^
HEAD is now at 7c8295f add Omooo

这时候,readme.txt文件里面的内容已经修改为上个版本的内容了。在使用git log 可以看到我们最新的那个 add love 版本已经不存在了。

1
2
3
4
5
6
7
8
9
10
11
12
$ git log
commit 7c8295fc12616f4c012cd4672dc498a5acb53c18
Author: TestOmooo <2270334821@qq.com>
Date: Sun Apr 9 16:03:20 2017 +0800
add Omooo
commit 5be3ee9f32a76a292daef68269918d6fa54f5be0
Author: TestOmooo <2270334821@qq.com>
Date: Sun Apr 9 15:37:05 2017 +0800
wrote a readme.txt file

可是又怎么再回到那个add love版本呢?办法其实还是有的,只有上面的命令行窗口还没关闭,你就可以往上找呀找呀,找到那个 add love 的 commit id 的是fe980ea……,版本号没必要写全,Git会自动帮你找。使用命令 git reset –hard fe980ea 就可以回到那个 add love 版本了。

1
2
$ git reset --hard fe980ea
HEAD is now at fe980ea add love

蛤蛤,我胡汉三又回来了。

Git 的版本回退速度很快的,因为Git 在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅只是把HEAD重定向了。

然而当命令窗口关闭了怎么办?或者说回退到某个版本后关掉了电脑,但是第二天又想恢复到新版本怎么办?找不到新版本的 commit id 怎么办?

在 Git 中,总有后悔药可吃的。Git 提供了一个命令 git reflog 用来记录你的每一次命令。

1
2
3
4
5
6
$ git reflog
fe980ea HEAD@{0}: reset: moving to fe980ea
7c8295f HEAD@{1}: reset: moving to HEAD^
fe980ea HEAD@{2}: commit: add love
7c8295f HEAD@{3}: commit: add Omooo
5be3ee9 HEAD@{4}: commit (initial): wrote a readme.txt file

工作区和暂存区

Git 和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。

  • 工作区(Working Directory)

就是在电脑里能看到的目录。

  • 版本库(Repository)

工作区有一个隐藏目录 .git ,这个就是 Git 的版本库。

Git 的版本库里面存了很多东西,其中最重要的就是 stage(或者叫 index) 的暂存区了,还有 Git 为我们自动创建的第一个分支 master,以及指向 master 的一个指针 HEAD 。

前面讲了我们把文件添加到Git版本仓库的时候,是分两步执行的:

第一步是用 git add 把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用 git commit 提交更改,实际上就是把暂存区所有的内容提交到当前分支。

管理修改

为什么 Git 比其他版本控制系统设计的优秀,是因为 Git 跟踪并管理的是修改,而非文件。

撤销修改

在准备提交之前,我们可以使用命令 git checkout – file 来把文件在工作区的修改全部撤销。

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
$ cat readme.txt
Hello World
Omooo
love
Almost
Sad
Mylover
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (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
Untracked files:
(use "git add <file>..." to include in what will be committed)
Notepad++.lnk
desktop.ini
no changes added to commit (use "git add" and/or "git commit -a")
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ git checkout -- readme.txt
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ cat readme.txt
Hello World
Omooo
love
Almost
Sad

文件果然又复原了。

如果修改之后还 git add 到暂存区了,我们可以使用命令 git reser HEAD file 吧暂存区的修改撤销掉,然后在丢弃工作区的修改。

删除文件

我们先添加一个新文件 test.txt 到仓库中。然后使用 rm 命令 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ rm test.txt
ssc@WIN-PIUGTH323DL MINGW64 ~/Desktop (master)
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
Notepad++.lnk
desktop.ini
no changes added to commit (use "git add" and/or "git commit -a")

这时候 Git 要知道你要删除文件了,git status 命令也会告诉你要删除哪些文件了。现在有两个选择 :

  1. 确定要删除这个文件。 git rm 删掉后 git commit 提交。
  2. 删错了,可以使用命令 git checkout – test.txt 恢复到最新版本。

远程仓库

到目前为止,我们已经掌握了如何在 Git 仓库里对一个文件进行时光穿梭,你再也不用担心文件备份或者丢失的问题了。

可是有用过集中式版本控制系统 SVN 的小伙伴们可能会站出来说,这些功能在 SVN 里面早就有了,没看出来 Git 有什么特别的地方。

没错,如果只是在一个仓库里管理文件历史,Git 和 SVN 这没有什么区别。但是,从本章开始,我们将介绍 Git 的史诗级爆炸功能。

Git 是分布式版本控制系统,同一个 Git 仓库,可以分布到不同的机器上。 那么怎么分布呢?最早,肯定只有一台机器和一个原始的版本库。此后,别的机器可以克隆这个原始库,而且每台机器的版本库其实是一样的,并没有主次之分。

你肯定会想,至少需要两台机器才能玩远程仓库不是吗?但是我只有一台,怎么玩?

其实一台电脑也是可以克隆多个版本库的,只有不在同一个目录下。不过,现实生活中是不会有人这么傻在一台机器上搞几个远程库玩,因为一台机器搞几个远程库是没有意义的,而且硬盘挂了会导致所有的库都挂掉。

实际情况下是这样的,找一台电脑充当服务器的角色,每天二十四小时开机,其他每个人都从这个服务器仓库克隆一份到自己的电脑上,并把各自的提交推送到服务器仓=仓库里,也能从服务器中拉取别人的提交。

完全可以自己搭建一个运行 Git 的服务器,不过现阶段,为了学 Git 先搭一个服务器绝对是小题大做。好在这个世界上有个叫 GitHub 的网站,他就是提供 Git 仓库托管服务的,所以只要注册一个 Github 账号,就可以免费获得 Git 远程仓库。

再继续阅读后续内容前,请自行注册 Github 账号。由于我们本地 Git仓库和 Github 仓库之间的传递是通过 SSH 加密的,所以,需要一点设置:

  1. 创建 SSH Key。在用户主目录下,看看有没有 ssh 目录,如果有,在看看这个目录下有没有 id_rsa 和 id_rsa.pub 这两个文件,如果有了,可以直接跳过下一步。如果没有,打开 Git Bash,创建 SSH Key:
1
$ ssh-keygen -t rsa -C "youemail@example.com"

你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个 Key 也不是用于军事目的,所以也无需设置密码。

如果一切顺利的话,可以在用户主目录下找到 .ssh 目录,里面有 id_rsa 和 id_rsa.pub 两个文件,其中,id_rsa 是私钥,不能泄露出去,id_rsa.pub 是公钥,可以放心的告诉别人。

  1. 登录 Github ,打开 ”Account setting“,”SSH Keys“ 页面:

然后点 “Add SSH Key”,填上任意的 Title,在 Key 文本框里黏贴 id_rsa.pub 文件的内容。

最后想说

如果你看到这了,说明你耐心不错,我也是闲的无聊写的这篇博文。当然,我可没有那么厉害,所有的一切都是参考廖雪峰写的Git教程。他是在 MAC 演示的,我是从 Win 上演示的,实际上差别很小。最开始写的时候,其实就是想认真系统学一遍 Git 教程,自己动手做一遍肯定印象深刻。那为什么不继续写了呢?说出来你可能不信,六合彩中了一百万。

开个玩笑,实际上我物理机上早就装了 Github。但是为了害怕万一操作傻逼,所以就在虚拟机上进行演示了。写到远程仓库那一章,发现要注册 Github并且配置,就很烦了。当然,码了那么多字也很烦了,所以决定不再写了,想看的小伙伴可以点击 Here

我们一直都向往,面朝大海,春暖花开。 但是几人能做到,心中有爱,四季不败?