这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记。
Git的使用
创建版本库
版本库,可以简称为库,英文名为repository,就是一个目录,这个目录里的所有东西都被Git管理起来,你要是对其中的文件进行增删改查等操作,Git就能够追踪到它,以便之后可以还原或是进行什么操作。
创建版本库的操作,就是先指定一个你想要建库的文件夹,当然你想新建一个这个库的文件夹也可以直接使用mkdir <folderName>,其中folderName为你想要的文件夹名。之后cd <folderName>进入到这个文件夹,为了使用这个文件夹作为Git的仓库,需要使用git init将其初始化。
$ mkdir <folderName> # 创建文件夹
$ cd <folderName> # 进入文件夹
$ git init # Git初始化
操作文件
如果你想新建一个文件并添加到Git库中,那么你只需要先在这个文件夹下新建一个文件,然后使用git add <fileName>,当你执行完这个指令没有任何提示,那么就说明你做对了,成功地添加了一个文件到缓存中。当然只是添加到缓存还不够,还需要将其提交到仓库,这个时候就需要使用git commit -m <message>,这里面的-m <message>是本次提交的说明,可以在里面写任何内容,不过需要用双引号把他们包起来,建议是写一些有意义的话语,这样以便之后就可以方便地找到改动记录。
当然,有了添加文件也需要有删除文件,git rm <fileName> 提供了这样的功能,需要注意的是,这里的删除操作,是从缓存区中删除,而非从库中删除这个文件。如果你想彻底删除一个文件,那么首先你需要在文件目录下将其删除,然后通过git rm <fileName>删除对这个文件的操作,这样的话,在下次的版本管理中就彻底没有这个文件了。如果要删除之前修改过或已经放到暂存区的文件,则必须使用强制删除选项 -f(译注:即 force 的首字母)。这是一种安全特性,用于防止误删尚未添加到快照的数据,这样的数据不能被 Git 恢复。
还有一种情况就是,我们想把一个文件从Git仓库中删除,但是我们还想把这个文件放在这个目录中,就是说我们仍然想把这个文件放到这个文件夹,即磁盘当中,但是我们不想让Git对这个文件继续进行追踪,你就可以使用git rm --cached <name>,当然这里不止可以是文件,还可以是文件夹,因而我们使用<name>来表示它。
如果你在Git库中移动了某个文件或者对一个文件重命名,你应该自然而然的想到,机器可能不会这么聪明地想到你是移动了或者重命名,你应该会想到机器会记录为:你删除了一个文件,然后又新增了一个文件。当然在此之前的VCS(version control system,即版本控制系统)确实会这么认为,但是Git很聪明,它可以猜测到发生了什么,但是,Git仍然给了我们一个指令git mv <oldFileName> <newFileName>来重命名一个文件。
$ git add <fileName> # 添加文件到缓存
$ git commit -m <message> # 提交到仓库
$ git rm <fileName> # 删除文件
所以,你应该可以大概了解Git的结构,包含一组元文件,以及在上面的若干次提交记录,每一个提交记录中又包含了对若干个文件的修改。
查看状态
有时候,已经提交了一些东西或是修改了一些东西,想看一下自己对那些东西有哪些改动该怎么办呢?Git也提供了相关的操作,分别是git status和git diff。
我们先来看如何查看当前变动的操作吧,有两个git指令是相关的,分别是git status和git diff。
git status命令用于显示工作目录和暂存区的状态。使用此命令能看到那些修改被暂存到了, 哪些没有, 哪些文件没有被Git tracked到。git status不显示已经commit到项目历史中去的信息。
$ git status
On branch hexo
Your branch is up to date with 'origin/hexo'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: source/Readme.md
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: _config.yml
deleted: source/_posts/008-Git.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
source/_posts/008-Git-01.md
source/_posts/009-Math-02.md
就本次提交而言,git status分为了三个部分,Changes not staged for commit、Untracked files以及Changes to be committed。所以通过英文的意思和前面的解释可以大致了解:Changes to be committed是已经加到缓存中但是还尚未被commit的文件,Changes not staged for commit是已经在Git库中,但是我们对其进行了一些修改还未提交更改到Git库中的文件,Untracked files就是没有添加到缓存中,但是它位于Git库所在的文件夹中的文件。如果你不想看到这些没有添加到Git库中的文件,那么你使用git status --uno就可以忽略这些了。同时,有时候这些更改的名字会比较长,你也可以使用git status -s查看到这些更改的简化版本。
$ git status -s
M _config.yml
A source/Readme.md
D source/_posts/008-Git.md
?? source/_posts/008-Git-01.md
?? source/_posts/009-Math-02.md
后面显而易见是带路径的文件名。前面的表示其状态,可能有四种状态,A、M、AM和???。A表示加进了缓存区中;M表示文件在Git库中但进行了修改;AM表示这个文件在我们将它添加到缓存之后又有改动;???表示这个文件并没有加到Git库中。
如果你没有可视化界面,类似于VS Code的可视化界面,但是仍想看看具体修改了那些内容,那么你就可以使用git diff,显示已写入缓存与已修改但尚未写入缓存的改动的区别,可以查看git status的详细内容,具体到修改了那个字节。不过如果你的终端不支持UTF-8,那么显示中文就会出现乱码。git diff也有对应的后缀拓展的功能:
- 尚未缓存的改动:
git diff - 查看已缓存的改动:
git diff --cached - 查看已缓存的与未缓存的所有改动:
git diff HEAD - 显示摘要而非整个 diff:
git diff --stat
查看提交历史
当然,在你提交了数个版本之后,有时候想回顾一下过去提交的版本,那么你可以使用git log命令查看。
$ git log
commit 033c067427cb8a79ffc13acd2c7ff819967950b1 (HEAD -> hexo, origin/hexo, origin/HEAD)
Author: my0sotis <35601060+my0sotis@users.noreply.github.com>
Date: Sun Mar 22 19:34:34 2020 +0800
Add Aliyun ECS Address
commit b89e97b95fb958a3721738d78ae904fd4d09af8f
Author: my0sotis <35601060+my0sotis@users.noreply.github.com>
Date: Sun Mar 22 00:48:51 2020 +0800
Security Fix
由于这个提交记录比较多,这里就截取一段,我们可以看到包括提交的哈希码、作者、日期,最下面一行为git commit -m <message>中的信息。如果你想要一个更简洁的版本,那么你可以加上--oneline选项来查看历史记录的简洁的版本。
033c067 (HEAD -> hexo, origin/hexo, origin/HEAD) Add Aliyun ECS Address
b89e97b Security Fix
不喜欢正着看?那么你可以使用--reverse选项来逆向查看工作日志,这里的逆向指的是,从Git库的第一个提交开始到最后一个。
92822fa First commit
678f164 Site updated: 2019-10-02 12:34:53
显示太多了你不喜欢看?那么你可以使用-<num>选项来控制查看近几条记录,比如-2就只看近两条记录。
你甚至可以用--author=<author>来指定看某个作者提交的记录,也可以用--since=<time>、--before=time、--until=time、--after=time,具体的时间之前还是时间之后就看各位的英语功底了,懂得都懂好吧😉。需要细说的是后面时间格式的书写,这个time,你可以用双引号包起来,也可以不包起来。关于时间的格式,你可以直接指定说--after=yesterday,找到昨天以后的所有提交记录,也可以指定某一个日期,比如--after=2020.3.1,这里面年月日的分隔符可以是-也可以是.,要是你喜欢混搭,Git也是可以认出来的。当然,又不是每个人都记得住具体什么时间(比如说我),你也可以指定说--after=1.minute.ago(前一分钟)、--after=1.hour.ago(前一小时)、--after=1.day.ago(前一天)、--after=1.week.ago(前一星期)、--after=1.month.age(前一个月),都是可以的。
你还可以使用--grep=<Description>选项,通过你在git commit -m <message>中的消息来找到对应的提交记录(所以切记要写好描述,最好不要用意义不明的数字字母组合);你还可以使用-- <fileName>选项,要注意这个--的后面有空格,来通过文件名来查找修改了这个文件的记录。你甚至还可以通过修改的内容来找到对应的提交记录,这里要使用-S"<String>"选项,这里的S后面没有=,如果你会一些正则表达式的知识,那么你可以使用-G"<Regex>",你还可以通过分支、标签、是否有merge来查找对应的提交记录,但是毕竟教程还没到那边,所以在这边就先提这么一下。
通过以上描述,你应该能发现,Git给予了使用者相当高的自由度,你不仅仅可以单独使用以上这个过滤器,你还可以将它们组合起来使用。如果使用得当,可以迅速且精准的定位到某一提交记录上。
撤销操作
有时候,你在刚使用git commit完一次后,突然想起来“噢!我刚才少提交了几个文件!”,那又怎么办呢?所幸Git也提供了这样的操作,例如:
$ git add fileA # 提交了一个文件
$ git commit -m "initialize" # 提交
# !我忘记提交一个东西了!
$ git add forget_file # 添加那个忘记的文件
$ git commit --amend # 这是什么意思呢?
这边的这个git commit --amend就是修正上一个提交,往上一个提交记录中在加上本次的修改内容,所以在git log的提示中,并不会出现两次提交记录,这两次提交记录会合为一个。这样的好处就是你的Git提交记录里不会充斥着啊!上个记录忘加了XXX文件或是小修改!这种毫无意义的提交记录,弄乱你的仓库。
又有时候,你刚使用git add <file>将文件提交到缓存中,突然一拍脑袋,“噢!我刚才多提交了几个文件!”,那又该怎么办呢?其实细心的你也许发现了,在上文中提及的git status中Changes not staged for commit之下有这么一条语句:
Changes to be committed:
(use "git restore --staged <file>..." to unstage) (use "git restore --staged <file>..." to unstage)
这句话就提示你,如果一不小心多加了几个文件到缓存中,可以使用git restore --staged <file>来取消误加进缓存里的文件。
亦或者,你改了半天,发现自己改错了,想要回退至一开始的状态怎么办?(实际上这种情况时有发生)需注意的是,这里的改错了的前提是在尚未进行git commit操作的时候。
上文中git status的输出中也给了你一个解决的方法:
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
你可以使用git restore <file>来进行撤销之前所做的修改。
需要注意的是,由于Git版本的不同,撤销操作的具体指令也会略有不同,先前版本Git的是
git checkout -- <file>,所以还是以自己版本的Git为准,可以使用git status下面的提示来确定具体使用哪一条语句。