Note on Git - Part 1
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 | git init |
这里 Git 并不会验证邮箱是否存在。
可以在以下路径中查找用户签名是否设置成功:
1 | cd /Users/lucas/.gitconfig |
其中 lucas 是电脑用户名的名称。
打开上述文件,就可以找到设置好的用户签名存储在对应文件中。
初始化本地库
要想让 Git 管理本地目录,要先给它对应的权限,这就是初始化本地库要做的事情。
具体过程就是在要进行托管的文件夹中打开终端,然后输入初始化指令。
初始化后,文件夹中会生成一个名为 .git 的隐藏文件,显示隐藏文件后可以看到,其中内容不需要管。
可以在终端中通过 ls
命令查看文件夹中的文件,但若要查看隐藏文件,需要添加参数,完整的指令为 ls -a
。
查看本地库状态
命令:
1 | git status |
使用命令后会输出 3 行日志:
1 | On branch master |
第一行显示的是本地库所在的分支,此处显示为“On branch master”。
第二行“No commits yet”的意思是目前还没有提交过文件,说明这是一个空的库。
第三行“nothing added to commit”的意思是还没有文件需要提交。
但这里还有个“.DS_Store”文件,这是 Mac OS X 操作系统所创造的隐藏文件,目的在于存贮目录的自定义属性,可以忽略。
通过命令 vim hello.txt
命令创建新的文件,并按键盘 i 键进入编辑模式。
在 vim 中复制粘贴的快捷键分别为:yy
和 p
。
在按快捷键前需要按 ESC 退出编辑模式。
编辑完文件后,通过命令 :wq
保存文件并退出(注意使用英文键盘)。
保存结束出来以后,通过命令 ls
来查看新创建的文件。
通过 cat hello.txt
可以查看文件内容。
通过 tail -1 hello.txt
查看文件最后一行内容。
此时再次查看本地库状态,就会发现多个文件。
1 | On branch master |
现在这个 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 | On branch master |
这表示现在 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 | [master (root-commit) 405a130] first commit |
其中第一行表示:分支 master 被改变,版本的日志为“first commit”,版本号为 405a130。
第二行表示:1 个文件被改变,3 行内容被插入。
在把文件提交到本地库后可以再次查看本地库状态,得到以下信息:
1 | On branch master |
没有了“No commits yet”,表明现在库里有东西了。
查看日志信息
利用 git reflog
查看引用日志信息,得到以下信息:
1 | 405a130 (HEAD -> master) HEAD@{0}: commit (initial): first commit |
这行信息表明,我们在本地库中有一个版本叫做 first commit,它的版本号是 405a130,当前的指针 HEAD 指向的是 master 分支,
利用 git log
可以查看详细的日志信息,得到以下信息:
1 | commit 405a1308e8131f64586a8f8939bdb1d617d98617 (HEAD -> master) |
这里可以看到完整版的版本号、作者信息、修改日期(包括所在时区)和版本日志。
修改文件
利用 vim 指令可以修改文件,假设我们把原来的 hello.txt 文件从原来的 3 行修改为 5 行(每行都是 hello git),再把第一行内容修改为“hello git1”。
yy 复制
p 粘贴
:wq 保存并退出
修改后再次查看本地库状态,会发现本地库状态再次发生改变:
1 | On branch master |
上面的信息告诉我们本地库中的 hello.txt 文件被修改了,并且这个文件的修改没有被添加到暂存区,这就需要用 git add
方法对修改进行追踪。
将修改后的文件添加到暂存区后得到的本地库状态信息为:
1 | On branch master |
之后再利用 git commit -m "日志信息" 文件名
提交到本地库,得到的信息为:
1 | [master 8670144] second commit |
上面可以看出新的版本号、对应的日志信息以及修改的内容。
我们添加了两行,修改了一行,信息显示的是有两处添加,这是因为我们的修改只是对那一行的内容进行了添加。
提交完成后再利用 git reflog
查看日志信息,就可以得到:
1 | 8670144 (HEAD -> master) HEAD@{0}: commit: second commit |
这里有两个版本,但是指针指向的是我们的第二个版本。
此时再利用 cat 命令就可以查看指针指向的版本的文件内容。
之后我们再进行第三次版本提交,这次把第一行改为“hello git123456”。
提交到本地库后得到:
1 | [master cc5efe0] third commit |
这里我们只修改了一行,但显示删去一行,新增一行,这是因为有一行的内容有删减,有一行的内容有新增,但他们是同一行。
再次查看日志信息:
1 | cc5efe0 (HEAD -> master) HEAD@{0}: commit: third commit |
这里有三个版本,指针指向的是我们的第三个版本。
版本穿梭
前面提过:
git reflog
可以查看精简的日志信息;git log
可以查看详细的日志信息。
如果想进行版本穿梭,可以使用的命令是 git reset --hard 版本号
,其中版本号可以在日志中查看。
例如我们将 hello.txt 穿梭回第二版,版本号是 8670144,会得到以下信息:
HEAD is now at 8670144 second commit
再次查看日志信息:
1 | 8670144 (HEAD -> master) HEAD@{0}: reset: moving to 8670144 |
第一行描述了版本的变化信息,后三行是我们的三个版本的信息,指针指向第二个版本。
此时再利用 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 | On branch hot-fix |
然后就可以把文件添加暂存区再提交,或者直接提交。
此时如果从文件夹中打开 hello.txt 文件,会发现它是第四个版本,这是因为我们当前切换到了 hot-fix 分支。
如果切换回 master 分支,再次打开,就又回到了第二个版本。
此时查看日志信息得到以下版本:
1 | 8670144 (HEAD -> master) HEAD@{0}: checkout: moving from hot-fix to master |
合并分支(无冲突)
命令 git merge 分支名
可以把指定分支合并到当前分支上,也就是说如果想把 hot-fix 分支合并到 master 分支上,就需要先切换到 master 上,再使用这个命令。
利用 git merge hot-fix
把 hot-fix 分支合并到 master 分支上,得到以下信息:
1 | Updating 8670144..1f1b97c |
其中 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 | On branch master |
这说明两个分支都对同一个文件进行了修改,所以需要手动合并。
此时需要利用 vim
命令手动打开文件,得到以下内容:
1 | hello git |
前三行内容相同,后两行内容不同。
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,会发现还是版本六。