Git 概述

Git 是一个免费的、开源的分布式版本控制系统,可以快速高效地处理从小型到大型的各种项目。

版本控制

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

版本控制最重要的是可以记录文件修改的历史记录,从而让用户能够查看历史版本,方便版本切换。

通过使用版本控制,可以实现从个人开发过渡到团队协作。

工作机制

Git 中分了 3 个区域:工作区、暂存区和本地库。

工作区就是写代码的地方,是代码存放的磁盘的目录的位置,电脑上的代码都存放在某个文件夹中,这个文件夹就是工作区。

在工作区写了代码后,需要让 Git 追踪到有这么一个代码文件,因此需要把文件添加(add)到暂存区,对应的 Git 命令就是 git add

工作区和暂存区的代码都是可以被删除的,是没有历史记录的。

将暂存区的代码提交(commit)到本地库中,提交后就可以生成对应的历史版本,对应的命令是 git commit

一旦生成历史版本后,代码就无法被删除了。

代码托管中心

代码托管中心是基于网络服务器的远程代码仓库,一般称为远程库。

远程库是基于上述三个区域的更高一层的区域。

本地库的代码可以被推送(push)到远程库中。

Git 常用命令

命令名称 作用
git config —global user.name 用户名 设置用户签名
git config —global user.email 邮箱 设置用户签名
git init 初始化本地库
git status 查看本地库状态
git add 文件名 添加到暂存区
git commit -m “日志信息” 文件名 提交到本地库
git reflog 查看日志信息
git reset —hard 版本号 版本穿梭

设置用户签名

Git 安装好后,需要也只需要设置一次用户签名即可,如果不设置用户签名,将来提交代码时可能报错。

先打开一个文件夹,通过以下命令先将其初始化为本地库,然后设置用户签名:

1
2
3
git init
git config --global user.name Lucas
git config --global user.email lucasetzixun@gmail.com

这里 Git 并不会验证邮箱是否存在。

可以在以下路径中查找用户签名是否设置成功:

1
cd /Users/lucas/.gitconfig

其中 lucas 是电脑用户名的名称。

打开上述文件,就可以找到设置好的用户签名存储在对应文件中。

初始化本地库

要想让 Git 管理本地目录,要先给它对应的权限,这就是初始化本地库要做的事情。

具体过程就是在要进行托管的文件夹中打开终端,然后输入初始化指令。

初始化后,文件夹中会生成一个名为 .git 的隐藏文件,显示隐藏文件后可以看到,其中内容不需要管。

可以在终端中通过 ls 命令查看文件夹中的文件,但若要查看隐藏文件,需要添加参数,完整的指令为 ls -a

查看本地库状态

命令:

1
git status

使用命令后会输出 3 行日志:

1
2
3
4
5
6
7
8
9
On branch master

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store

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

第一行显示的是本地库所在的分支,此处显示为“On branch master”。

第二行“No commits yet”的意思是目前还没有提交过文件,说明这是一个空的库。

第三行“nothing added to commit”的意思是还没有文件需要提交。

但这里还有个“.DS_Store”文件,这是 Mac OS X 操作系统所创造的隐藏文件,目的在于存贮目录的自定义属性,可以忽略。

通过命令 vim hello.txt 命令创建新的文件,并按键盘 i 键进入编辑模式。

在 vim 中复制粘贴的快捷键分别为:yyp

在按快捷键前需要按 ESC 退出编辑模式。

编辑完文件后,通过命令 :wq 保存文件并退出(注意使用英文键盘)。

保存结束出来以后,通过命令 ls 来查看新创建的文件。

通过 cat hello.txt 可以查看文件内容。

通过 tail -1 hello.txt 查看文件最后一行内容。

此时再次查看本地库状态,就会发现多个文件。

1
2
3
4
5
6
7
8
9
10
On branch master

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store
hello.txt

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

现在这个 hello.txt 文件虽然有了,但它只是存在于工作区域内,Git 还从未追踪过这个文件。此时需要利用 git add 命令来把这个文件添加到暂存区。

终端常用命令

命令名 功能描述 使用举例
cd 改变当前目录 cd dirname
ls 显示当前目录的内容 ls -la
cat 显示或连接文件 cat filename
tail 显示文件的最后几行 tail -15 filename
head 显示文件的最初几行 head -20 filename
mkdir 创建一个目录 mkdir dirname
rmdir 删除一个目录 rmdir dirname
pwd 显示当前目录的路径名 pwd
clear 清除屏幕或窗口内容 clear

添加暂存区

利用命令 git add hello.txt 将文件添加到暂存区。

如果在 Windows 系统进行操作,则会出现警告提示说 LF 将会被 CRLF 替代。这是因为 Windows 系统和 Linux 系统的换行符不同。

此时再次查看本地库状态,将会发现如下改变:

1
2
3
4
5
6
7
8
9
10
11
On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello.txt

Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store

这表示现在 Git 已经追踪到了这个文件,但这个文件只存在于暂存区中,暂存区中的文件仍是可以被删掉的,用到的命令是 git rm --cached <file>

利用 git rm --cached hello.txt 把 hello.txt 删掉,但这只是把它从暂存区删掉,工作区还是有的,可以通过 ls 查看。

然后可以再次添加回暂存区。

提交本地库

提交本地库的命令为 git commit -m "日志信息" 文件名,其中如果不写 “-m”,就会自动打开一个文件,要求在文件中描述版本信息。所以最好还是主动提交版本信息。

输入命令 git commit -m "first commit" hello.txt 后,会得到以下信息:

1
2
3
[master (root-commit) 405a130] first commit
1 file changed, 3 insertions(+)
create mode 100644 hello.txt

其中第一行表示:分支 master 被改变,版本的日志为“first commit”,版本号为 405a130。

第二行表示:1 个文件被改变,3 行内容被插入。

在把文件提交到本地库后可以再次查看本地库状态,得到以下信息:

1
2
3
4
5
6
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store

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

没有了“No commits yet”,表明现在库里有东西了。

查看日志信息

利用 git reflog 查看引用日志信息,得到以下信息:

1
405a130 (HEAD -> master) HEAD@{0}: commit (initial): first commit

这行信息表明,我们在本地库中有一个版本叫做 first commit,它的版本号是 405a130,当前的指针 HEAD 指向的是 master 分支,

利用 git log 可以查看详细的日志信息,得到以下信息:

1
2
3
4
5
commit 405a1308e8131f64586a8f8939bdb1d617d98617 (HEAD -> master)
Author: Lucas <lucasetzixun@gmail.com>
Date: Mon Apr 4 18:49:14 2022 +0200

first commit

这里可以看到完整版的版本号、作者信息、修改日期(包括所在时区)和版本日志。

修改文件

利用 vim 指令可以修改文件,假设我们把原来的 hello.txt 文件从原来的 3 行修改为 5 行(每行都是 hello git),再把第一行内容修改为“hello git1”。

yy 复制

p 粘贴

:wq 保存并退出

修改后再次查看本地库状态,会发现本地库状态再次发生改变:

1
2
3
4
5
6
7
8
9
10
11
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: hello.txt

Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store

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

上面的信息告诉我们本地库中的 hello.txt 文件被修改了,并且这个文件的修改没有被添加到暂存区,这就需要用 git add 方法对修改进行追踪。

将修改后的文件添加到暂存区后得到的本地库状态信息为:

1
2
3
4
5
6
7
8
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: hello.txt

Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store

之后再利用 git commit -m "日志信息" 文件名 提交到本地库,得到的信息为:

1
2
[master 8670144] second commit
1 file changed, 2 insertions(+)

上面可以看出新的版本号、对应的日志信息以及修改的内容。

我们添加了两行,修改了一行,信息显示的是有两处添加,这是因为我们的修改只是对那一行的内容进行了添加。

提交完成后再利用 git reflog 查看日志信息,就可以得到:

1
2
8670144 (HEAD -> master) HEAD@{0}: commit: second commit
405a130 HEAD@{1}: commit (initial): first commit

这里有两个版本,但是指针指向的是我们的第二个版本。

此时再利用 cat 命令就可以查看指针指向的版本的文件内容。

之后我们再进行第三次版本提交,这次把第一行改为“hello git123456”。

提交到本地库后得到:

1
2
[master cc5efe0] third commit
1 file changed, 1 insertion(+), 1 deletion(-)

这里我们只修改了一行,但显示删去一行,新增一行,这是因为有一行的内容有删减,有一行的内容有新增,但他们是同一行。

再次查看日志信息:

1
2
3
cc5efe0 (HEAD -> master) HEAD@{0}: commit: third commit
8670144 HEAD@{1}: commit: second commit
405a130 HEAD@{2}: commit (initial): first commit

这里有三个版本,指针指向的是我们的第三个版本。

版本穿梭

前面提过:

  1. git reflog 可以查看精简的日志信息;
  2. git log 可以查看详细的日志信息。

如果想进行版本穿梭,可以使用的命令是 git reset --hard 版本号,其中版本号可以在日志中查看。

例如我们将 hello.txt 穿梭回第二版,版本号是 8670144,会得到以下信息:

HEAD is now at 8670144 second commit

再次查看日志信息:

1
2
3
4
8670144 (HEAD -> master) HEAD@{0}: reset: moving to 8670144
cc5efe0 HEAD@{1}: commit: third commit
8670144 (HEAD -> master) HEAD@{2}: commit: second commit
405a130 HEAD@{3}: commit (initial): first commit

第一行描述了版本的变化信息,后三行是我们的三个版本的信息,指针指向第二个版本。

此时再利用 cat 命令查看文件信息,会发现我们的第一行被改回了“hello git1”。

或者我们可以在 .git 目录下,查看 HEAD 文件,会发现它的指针指向 master,说明当前是在 master 这个分支上。

再查看 .git/refs/heads 这个目录,这里有个 master 文件,这个文件中记录的是当前指针指向的版本号。

同理,我们可以实现向前和向后穿越。

Git 分支

在版本控制过程中,有时需要同时推进多个任务,为每个任务,我们可以创建每个任务的单独分支。

使用分支意味着程序员可以把自己的任务从主线上分离出来,开发自己分支的时候,不影响主线分支的运行。

对于初学者,可以把分支简单理解为副本,一个分支就是一个单独的副本(分支底层其实也是指针的应用)。

分支允许我们并行推进多个任务,提升开发效率。同时,如果某个分支开发失败,不会对其他分支有影响,只需要将失败的分支删除并重新开始即可。

常用分支操作

命令名称 作用
git branch 分支名 创建分支
git branch -v 查看分支
git checkout 分支名 切换分支
git merge 分支名 把指定分支合并到当前分支上

查看分支

利用 git branch -v 查看当前本地分支状态,得到以下信息:

* master 8670144 second commit

说明目前只有一个 master 分支,它的版本号是 8670144,日志信息为“second commit”。

创建分支

利用 git branch hot-fix 来创建一个紧急修复分支。

再次查看分支状态,得到以下信息:

hot-fix 8670144 second commit

* master 8670144 second commit

说明我们现在有两个分支,由于 master 前面有星号,说明目前还是在 master 分支上进行操作。

切换分支

利用 git checkout hot-fix 切换到紧急修复分支。

再次查看分支状态,得到以下信息:

* hot-fix 8670144 second commit

master 8670144 second commit

说明现在操作的是紧急修复分支。

修改分支

利用 vim 命令把原来的 hello.txt 文件改为:

hello git

hello git 222

hello git

hello git

hello git

此时查看本地库状态,得到:

1
2
3
4
5
6
7
8
9
10
11
On branch hot-fix
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: hello.txt

Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store

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

然后就可以把文件添加暂存区再提交,或者直接提交。

此时如果从文件夹中打开 hello.txt 文件,会发现它是第四个版本,这是因为我们当前切换到了 hot-fix 分支。

如果切换回 master 分支,再次打开,就又回到了第二个版本。

此时查看日志信息得到以下版本:

1
2
3
4
5
6
7
8670144 (HEAD -> master) HEAD@{0}: checkout: moving from hot-fix to master
1f1b97c (hot-fix) HEAD@{1}: commit: fourth commit
8670144 (HEAD -> master) HEAD@{2}: checkout: moving from master to hot-fix
8670144 (HEAD -> master) HEAD@{3}: reset: moving to 8670144
cc5efe0 HEAD@{4}: commit: third commit
8670144 (HEAD -> master) HEAD@{5}: commit: second commit
405a130 HEAD@{6}: commit (initial): first commit

合并分支(无冲突)

命令 git merge 分支名 可以把指定分支合并到当前分支上,也就是说如果想把 hot-fix 分支合并到 master 分支上,就需要先切换到 master 上,再使用这个命令。

利用 git merge hot-fix 把 hot-fix 分支合并到 master 分支上,得到以下信息:

1
2
3
4
Updating 8670144..1f1b97c
Fast-forward
hello.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

其中 8670144 是我们原来的 master 分支的版本号,1f1b97c 是我们原来的 hot-fix 分支的版本号。

一个文件被修改,有一处删减,有一处新增。

此时利用 cat 命令可以发现 master 分支上的 hello.txt 文件变成了第四版。

这个合并属于正常的合并,并未出现冲突。

合并分支(有冲突)

合并分支时,如果同一个文件有两套完全不同的修改,就说明两个分支有冲突,此时 Git 无法替我们决定要使用哪一个,因此必须认为决定新代码的内容。

注意这里的“完全不同的修改”是与同一个起点开始比较的。

也就是说假如本来 master 和 hot-fix 都是版本二,后来 hot-fix 修改得到版本四,而 master 没有修改,这样合并是没有冲突的,合并后两个分支都是版本四,这也就是上一节的情况。

但是如果 master 在第四个版本后又进行了修改得到版本五,同时 hot-fix 也在第四个版本后进行了修改得到版本六,这样合并就会发生冲突。

接下来演示这个冲突。

先对 master 分支进行修改得到版本五:

hello git

hello git 222

hello git

hello git master

hello git

然后将其提交。

接下来切换到 hot-fix 分支。

利用 vim 命令对其进行修改得到版本六:

hello git

hello git 222

hello git

hello git

hello git hot

然后将其提交。

再切换回 master 分支。

然后利用 git merge hot-fix 合并分支,会得到以下信息:

Auto-merging hello.txt

CONFLICT (content): Merge conflict in hello.txt

Automatic merge failed; fix conflicts and then commit the result.

这表明发生冲突,自动合并失败。

此时查看本地库状态会得到以下信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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: hello.txt

Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store

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

这说明两个分支都对同一个文件进行了修改,所以需要手动合并。

此时需要利用 vim 命令手动打开文件,得到以下内容:

1
2
3
4
5
6
7
8
9
10
hello git
hello git 222
hello git
<<<<<<< HEAD
hello git master
hello git
=======
hello git
hello git hot
>>>>>>> hot-fix

前三行内容相同,后两行内容不同。

HEAD 表示当前分支,HEAD 和一排等号之间的是当前分支的内容,一排等号后面的是 hot-fix 分支的内容。

此时我们只需要删掉我们不想要的内容即可,假设修改后内容如下:

hello git

hello git 222

hello git

hello git master

hello git hot

然后退出编辑模式利用 :wq 退出并保存。

然后需要添加暂存区并提交到本地库。

然而此时提交时不能再带文件名,因为两个分支都修改了,如果提交时带文件名,将会出现如下报错:

fatal: cannot do a partial commit during a merge.

此时只需要利用 git commit -m "merge test" 将其提交,会返回如下信息:

[master 304c11e] merge test

此时利用 cat 命令查看文件,就会发现修改后的文件。

但这只是修改了 master 的文件,切回 hot-fix,会发现还是版本六。