为什么需要Git

大家一定有过这样的经历,在写论文的时候,由于各种各样的原因,改了又改,改了又改,每次的版本又需要留个底备用,于是便有了如下情况:

由于我们对论文的多次版本迭代,产生了大量的文件,最终,我们只会选取一份最终的文件提交。同时为了记录版本的迭代历史,以及随时回滚到之前的版本,实现回溯,我们需要一个版本控制工具。当然,这份论文也许是由多个小伙伴一起完成的,那么就需要多人同时进行编辑,最终的结果又要汇总到一个地方,并且能够解决出现的冲突,也就是说,我们需要一个分布式的版本控制工具,对于代码而言,更是如此,而Git,正是解决这种问题的一个工具。

什么是Git

git是一个分布式版本控制软件,最初由林纳斯·托瓦兹创作,于2005年以GPL许可协议发布。最初目的是为了更好地管理Linux内核开发而设计。(Wikipedia

首先放出一张图,有助于后面理解Git里面的各种概念:

可以看到,它大致分为4个板块:

  • 工作目录:存放我们正在写的代码(当我们新版本开发完成之后,就可以进行新版本的提交)
  • 暂存区:暂时保存待提交的内容(新版本提交后会存放到本地仓库)
  • 本地仓库:位于我们电脑上的一个版本控制仓库(存放的就是当前项目各个版本代码的增删信息)
  • 远程仓库:位于服务器上的版本控制仓库(服务器上的版本信息可以由本地仓库推送上去,也可以从服务器抓取到本地仓库)

快速开始

官方文档

更详细的教程可以到官方文档查询学习,本文仅用于快速入门

文档地址:https://git-scm.com/docs

《Pro Git》:https://git-scm.com/book/zh/v2

安装git

下载地址:https://git-scm.com/download

选择你使用的系统并安装之,安装过程略

接下来本文将以Windows 11为例,所有命令将在 git bash中进行

设置你的用户名和邮箱

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

初始化仓库

在任意文件夹下执行:

git init

这一步将会在当前目录下生成一个 .git目录,该目录是隐藏目录,包含了这个仓库的所有信息

添加到暂存区

在当前目录下新建一个 hello.txt文件夹,并随便编辑后保存

执行如下:

git add hello.txt

当然,如果有多个文件,可以执行如下命令,将会把当前目录下所有文件都放入暂存区

git add .

执行后,hello.txt文件就被添加到了暂存区

提交

git commit -m 'First commit'

执行后,得到如下提示,hello.txt文件就被添加到了本地仓库

[master (root-commit) a7e0219] First commit
 1 file changed, 1 insertion(+)
 create mode 100644 hello.txt

其中:First commit表示当前 commit的信息,用于简单描述提交的内容

查看提交记录

git log                        #查看提交记录
git log --graph                #以 ASCII 图形显示提交记录
git log --graph --oneline    #以 ASCII 图形显示提交记录,并且每条记录一行显示
$ git log
commit a7e02198703e06bcbe786e59e85cc57aa750fa3a (HEAD -> master)
Author: kakkk <kakkk@live.com>
Date:   Sat Jan 15 22:27:41 2022 +0800

    First commit

$ git log --graph
* commit a7e02198703e06bcbe786e59e85cc57aa750fa3a (HEAD -> master)
  Author: kakkk <kakkk@live.com>
  Date:   Sat Jan 15 22:27:41 2022 +0800

      First commit

$ git log --graph --oneline
* a7e0219 (HEAD -> master) First commit

回滚

仓库提交记录如下:

* 2bc7ac9 (HEAD -> master) Second commit
* a7e0219 First commit

如果我们需要回滚到 a7e0219分支,执行如下命令:

git reset --hard a7e0219

其中:--hard表示回滚的模式,a7e0219表示commit的ID

关于回滚的模式,具体可到官方文档了解:https://git-scm.com/docs/git-reset/en

分支管理

看到这里,你已经完成了最基本的操作,接下来,我们来了解下GIt的分支管理

首先我们来查看下当前分支:

git branch

一般情况下,git默认创建的是 master分支

本文将涉及以下几个操作:

  • 创建分支
  • 合并分支
  • 变基分支
  • 优选

创建分支

git branch test        #创建名为test的分支
git branch -d test    #删除名为test的分支

切换分支,也叫签出(checkout)

git checkout test

添加文件并提交:

git add BranchTest.txt
git commit -m 'commited by test'

查看当前记录:

$ git log --all --graph --oneline
* d269daa (HEAD -> test) commited by test
* 2bc7ac9 (master) Second commit
* a7e0219 First commit

合并分支

首先我们回到 master分支

git checkout master

合并分支

git merge test

如果没有冲突,将会自动完成分支的合并:

Updating 2bc7ac9..d269daa
Fast-forward
 BranchTest.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 BranchTest.txt

如果存在冲突,则需要先进行冲突的解决

解决冲突

首先我们需要制造冲突,很简单,同时在两个分支修改同一个文件并提交即可

制造冲突后,分支情况如下:

$ git log --all --graph --oneline
* 99e8b90 (test) update hello by test
| * 63244d2 (HEAD -> master) update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

此时,回到 master分支,合并 test分支,会得到如下的提示“

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

打开 hello.txt,内容如下:

<<<<<<< HEAD
hello master
=======
hello test
>>>>>>> test

在Git中,使用 <<<<<<<=======>>>>>>>标记出不同分支的内容,这里我们采纳两个分支的内容,也就是把 hello.txt修改成如下:

hello master
hello test

当然,也可以不采纳 test分支的提交,也就是把 hello test删掉,具体怎么处理冲突需要看实际需求,这里只做演示

修改好之后,进行一次 commit

git add hello.txt
git commit -m 'merge on conflict'

重新查看分支情况:

$ git log --all --graph --oneline
*   c0b47bf (HEAD -> master) merge on conflict
|\
| * 99e8b90 (test) update hello by test
* | 63244d2 update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

发现合并已完成

变基分支

所谓变基分支,就是修改分支的开始位置

比如,当前我正在 test分支写代码,而 master分支更新了一个文件,恰好我需要这份文件的更改,我就可以将我的 test分支开始的位置定位到 master分支最新的位置。

说起来比较抽象,举个例子:

首先在 master新建一个 aaa.txt并提交,切换到 test分支,新建一个 bbb.txt并提交

查看当前分支情况:

$ git log --all --graph --oneline
* 9b9cfae (HEAD -> test) add bbb.txt
| * c37cf6b (master) add aaa.txt
| *   c0b47bf merge on conflict
| |\
| |/
|/|
* | 99e8b90 update hello by test
| * 63244d2 update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

可以看到,test分支的开始位置在 99e8b90,此时,我们需要 master分支中的 bbb.txt,但是又不想合并,我们可以使用变基分支进行操作:

git rebase master

重新查看分支情况:

$ git log --all --graph --oneline
* f3d4f66 (HEAD -> test) add bbb.txt
* c37cf6b (master) add aaa.txt
*   c0b47bf merge on conflict
|\
| * 99e8b90 update hello by test
* | 63244d2 update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

此时,目录下已经出现了 aaa.txt

优选

优选,即 charrypick操作,可以单独合并某一次提交,比如,在 master分支中,其他人创建了 ccc.txtddd.txt文件,此时,我只需要 ddd.txt文件,并不需要 ccc.txt文件,并且我既不想进行分支合并,也不想进行分支变基,这个时候,就可以使用优选操作。

首先当然是在 master分支中创建并提交 ccc.txtddd.txt文件,此时,分支情况如下:

$ git log --all --graph --oneline
* 6fb1632 (HEAD -> master) add ddd
* a4e8ed0 add ccc
| * f3d4f66 (test) add bbb.txt
|/
* c37cf6b add aaa.txt
*   c0b47bf merge on conflict
|\
| * 99e8b90 update hello by test
* | 63244d2 update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

切换到 test分支,进行优选操作:

git cherry-pick 6fb1632

其中,6fb1632表示需要优选的commit ID

查看分支情况:

$ git log --all --graph --oneline
* f17beb6 (HEAD -> test) add ddd
* f3d4f66 add bbb.txt
| * 6fb1632 (master) add ddd
| * a4e8ed0 add ccc
|/
* c37cf6b add aaa.txt
*   c0b47bf merge on conflict
|\
| * 99e8b90 update hello by test
* | 63244d2 update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

远程仓库

远程仓库,可以理解为位于远程服务器的仓库,可以实现多人合作进行项目开发。

远程仓库有公有的,也有私有的,共有仓库有Github、Gitee、Coding等,私有的一般使用Gitlab进行搭建,在公司中,一般使用私有仓库,只对公司内部开放

本文以Github为例,因此需要先注册一个Github账号。

创建远程仓库

在Github中,找到New Repository选项,填写你的仓库地址和名称,设置成共有或私有,点击创建即可

添加远程仓库并推送

接下来,复制你的仓库地址:

回到你的仓库中,添加仓库地址:

git remote add origin git@github.com:kakkk/test.git

其中,origin为远程仓库名称,git@github.com:kakkk/test.git为远程仓库地址

添加身份验证

这里可以使用SSH,也可以使用token进行身份验证,本文使用SSH的方式

首先,在本地生成一个rsa公钥

ssh-keygen -t rsa

一路回车,最后将会提示你在User目录生成了你的公钥和私钥

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/kakkk/.ssh/id_rsa):
Created directory '/c/Users/kakkk/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /c/Users/kakkk/.ssh/id_rsa
Your public key has been saved in /c/Users/kakkk/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:L72E1ARiVnog33fcNBd6KPn1KZvBxQkLq6azeOXGlU0 kakkk@kakkk-VM
The key's randomart image is:
+---[RSA 3072]----+
|    . =.o  . .o.o|
|     = = . .+o*o.|
|      o o o+o+.* |
|       . +..+E+ o|
|        So. += ..|
|       .o= o .=  |
|       o* =  o   |
|      ..o* .     |
|     .... .      |
+----[SHA256]-----+

接下来,用记事本或任一文本编辑器打开你的公钥,即 id_rsa.pub

打开Github,点击头像,Setting

选择右侧的 SSH and GPG keys,点击 New SSH key

将刚刚得到的公钥复制到里面,添加即可

添加完成后,使用如下命令测试以下:

ssh -T git@github.com

输入 yes,回车,出现如下内容说明连接成功:

Hi kakkk! You've successfully authenticated, but GitHub does not provide shell access.

推送

搞定了身份验证,就可以推送到远程仓库了

git push origin master 
$ git push origin master
Enumerating objects: 26, done.
Counting objects: 100% (26/26), done.
Delta compression using up to 4 threads
Compressing objects: 100% (16/16), done.
Writing objects: 100% (26/26), 1.97 KiB | 336.00 KiB/s, done.
Total 26 (delta 3), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (3/3), done.
To github.com:kakkk/test.git
 * [new branch]      master -> master

查看远程仓库:

克隆项目

就是把远程仓库的项目克隆下来:

git clone 远程仓库地址

抓取/拉取项目

当其他同学在这个仓库修改了代码之后,你需要将它抓取或拉取下来,抓取和拉取的区别:

  • 抓取(fetch):只获取远程仓库分支,不进行自动合并,需要手动合并才能提交
  • 拉去(pull):获取并自动合并远程分支

方法如下:

git fetch origin
git pull origin

抓取

比如,在github上面在线编辑以下 hello.txt文件,抓取远程分支:

$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 641 bytes | 49.00 KiB/s, done.
From https://github.com/kakkk/test
   2e0c4ee..0251187  master     -> origin/master

查看分支结构:

$ git log --all --graph --oneline
* 0251187 (origin/master) Update hello.txt
*   2e0c4ee (HEAD -> master) Merge branch 'test' merge test
|\
| * f17beb6 (test) add ddd
| * f3d4f66 add bbb.txt
* | 6fb1632 add ddd
* | a4e8ed0 add ccc
|/
* c37cf6b add aaa.txt
*   c0b47bf merge on conflict
|\
| * 99e8b90 update hello by test
* | 63244d2 update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

合并分支:

$ git merge origin/master
Updating 2e0c4ee..0251187
Fast-forward
 hello.txt | 1 +
 1 file changed, 1 insertion(+)

拉取

同样的,我们在远程再次修改文件,本地拉取并查看分支情况:

$ git pull origin master
From https://github.com/kakkk/test
 * branch            master     -> FETCH_HEAD
Updating 0251187..8685ea1
Fast-forward
 aaa.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git log --all --graph --oneline
* 8685ea1 (HEAD -> master, origin/master) Update aaa.txt
* 0251187 Update hello.txt
*   2e0c4ee Merge branch 'test' merge test
|\
| * f17beb6 (test) add ddd
| * f3d4f66 add bbb.txt
* | 6fb1632 add ddd
* | a4e8ed0 add ccc
|/
* c37cf6b add aaa.txt
*   c0b47bf merge on conflict
|\
| * 99e8b90 update hello by test
* | 63244d2 update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

处理冲突

处理远程仓库冲突的流程和本地仓库合并时处理冲突的流程差不多

这里我们同样在本地和远程修改 bbb.txt制造冲突方便演示

拉取仓库:

$ git pull origin master
From https://github.com/kakkk/test
 * branch            master     -> FETCH_HEAD
Auto-merging bbb.txt
CONFLICT (content): Merge conflict in bbb.txt
Automatic merge failed; fix conflicts and then commit the result.

不出意外,它会提示冲突,这时候我们打开 bbb.txt对冲突进行处理,然后提交,查看分支情况:

$git add bbb.txt

$ git commit -m 'merge origin and fix conflict'
[master abec189] merge origin and fix conflict

$ git log --all --graph --oneline
*   abec189 (HEAD -> master) merge origin and fix conflict
|\
| * 8c30158 (origin/master) Update bbb.txt
* | 1d38a3b update bbb by local
|/
* 8685ea1 Update aaa.txt
* 0251187 Update hello.txt
*   2e0c4ee Merge branch 'test' merge test
|\
| * f17beb6 (test) add ddd
| * f3d4f66 add bbb.txt
* | 6fb1632 add ddd
* | a4e8ed0 add ccc
|/
* c37cf6b add aaa.txt
*   c0b47bf merge on conflict
|\
| * 99e8b90 update hello by test
* | 63244d2 update hello by master
|/
* d269daa commited by test
* 2bc7ac9 Second commit
* a7e0219 First commit

总结时间

至此,我们的Git从入门到入门已经结束,恭喜看到这里的你成功入门。本文偏入门,因此Git的一些其他操作以及本文没有讲到的东西,还需要查阅文档才能使用,但是学到这里,应该已经能够掌握Git的基本使用了。

当然,很多IDE的自带的Git图形化界面相当好用,我也在用,学习Git命令时为了更好地使用图形化界面,毕竟你得了解Git是怎样的一个流程嘛。

好了就这么多,这篇文章居然写了我一个晚上,唉,我还是菜。

最后修改:2022 年 01 月 16 日
如果觉得我的文章对你有用,请随意赞赏