git 学习笔记 9
start at 2023/03/08.

标签

标签 (Tag) 也是 git 的四大对象之一,它和分支类似,是用来贴在某个 commit 上的,只不过和分支有一点不一样,那就是它不会随着 commit 的移动而移动,可以想象成一动不动的分支吧,更像是一扇定点的传送门

标签的分类

标签有两种

  • lightweight tag, 轻量标签
  • annotated tag, 附注标签

官方有对这两种标签的描述

Annotated tags are meant for release

while lightweight tags are meant for private or temporary object labels.

意思就是附注标签用来记载标签信息(比如贴这张标签的原因等),而轻量标签的作用是个人使用或者暂时贴一下。一般来说附注标签都是用来标注版本号的,如果不是很在意标签的信息,只需要使用轻量标签即可

使用标签

$ git tag xxx

和分支很像,这样就能在当前 commit 上贴上一张轻量标签,你也可以指定这张标签贴到哪条 commit 上

$ git log --oneline
fb1c451 (HEAD -> master, tag: test) add b.txt
34456ef add a.txt
$ git tag helloA 34456e
$ git log --oneline
fb1c451 (HEAD -> master, tag: test) add b.txt
34456ef (tag: helloA) add a.txt

如果你想贴一张附注标签只需要加上 -a 的选项即可,按回车之后会和提交 commit 类似,弹出一个 vim 编辑器出来,填上你要填的信息之后 :wq 即可,同样的道理你也可以使用 -m "info" 直接在命令里写下信息

$ git tag helloC -a
$ git log --oneline
8d1f70f (HEAD -> master, tag: helloC) add c.txt
fb1c451 (tag: test) add b.txt
34456ef (tag: helloA) add a.txt

表面看上去会和轻量标签很像,查看标签信息的话要使用 git show

$ git show test
commit fb1c4512daa8af1b014d99e3bac67a6c3b0287d8 (tag: test)
Author: paradoxskin <1312269430@qq.com>
Date:   Wed Mar 8 16:19:07 2023 +0800

    add b.txt

diff --git a/b.txt b/b.txt
new file mode 100644
index 0000000..a71ea33
--- /dev/null
+++ b/b.txt
@@ -0,0 +1 @@
+print("hello, b")
$ git show helloC
tag helloC
Tagger: paradoxskin <1312269430@qq.com>
Date:   Wed Mar 8 16:22:23 2023 +0800

just text

commit 8d1f70fe0a8ab0205d556a65e1c831cfacc0ae59 (HEAD -> master, tag: helloC)
Author: paradoxskin <1312269430@qq.com>
Date:   Wed Mar 8 16:22:05 2023 +0800

    add c.txt

diff --git a/c.txt b/c.txt
new file mode 100644
index 0000000..b95cfac
--- /dev/null
+++ b/c.txt
@@ -0,0 +1 @@
+print("hello c")

可以看到附注标签的内容多出了一块附注的内容

和分支的存储方式类似,标签除了对象之外也在 .git/refs/tags 目录下存在,指向某个对象,其中轻量标签指向的是某个 commit, 而附注标签指向的是 tag 对象,毕竟要存储信息嘛

删除标签

和删除分支很像,直接用 -d 删除即可

$ git tag -d test
Deleted tag 'test' (was fb1c451)
$ git log --oneline
8d1f70f (HEAD -> master, tag: helloC) add c.txt
fb1c451 add b.txt
34456ef (tag: helloA) add a.txt

标签的用途

标签是不会动的分支,一般会用表示开发软件时候的里程碑,比如说软件版本号之类的 (1.0.0),我反正是不咋用的

Stash简要

我之前还以为这本书的时间太早了,git 还没更新出 stash, 原来作者没有单独错位标题名,只是作为中途要去处理其他分支时候的一种解决方案,而且作者也不是很喜欢用 stash

使用 stash 的场景很常见:做事做了一半的时候突然要处理其他事,为了不丢失之前的进度,要短暂的保存一下进度

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   d.txt

$ git stash
Saved working directory and index state WIP on master: 8d1f70f add c.txt
$ git status
On branch master
nothing to commit, working tree clean

要注意 stash 默认是不暂存 Untracked 状态的文件,需要额外使用 -u 参数,stash 之后发现文件都消失了,别担心,文件没有消失,他们至少被存进了一个栈的结构

$ git stash list
stash@{0}: WIP on master: 8d1f70f add c.txt

如果在往这个 list 里加东西,stash@{0} 会变成 stash@{1}

$ git stash list
stash@{0}: WIP on master: 8ac6b40 temp2
stash@{1}: WIP on master: 8d1f70f add c.txt

为什么说是栈呢,这就要说到怎么回收 stash

使用 git stash pop 会默认回收出序号最小的 stash

$ git stash pop
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   d.txt

Dropped refs/stash@{0} (8653ffcc50f4b3aad1d24acbf878f138bb48ddfc)

默认弹出了刚才的 stash@{0},这就是说它是栈的原因,当然其实没有这么绝对,你可以指定弹出哪个 stash

$ git stash list
stash@{0}: WIP on master: 8ac6b40 temp2
stash@{1}: WIP on master: 8d1f70f add c.txt
$ git stash pop stash@{1}
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   d.txt

Dropped stash@{1} (ee3fba3714cd72a06be16f7821509cdfa93e9019)

如果你不打算使用那个 stash 可以直接使用 git stash drop 来丢弃 stash,另外还有一条 git stash apply,它的作用是只应用 stash 而不在 list 中删除那个 stash,也就是说你可以吧 git stash pop 看成 git stash apply + git stash drop

我和作者的观点一致,觉得 git stash 的功能不是很喜欢,更喜欢直接 commit,回来的时候 git reset HEAD^ 软回退就好了

push隐私文件后应急

典型的事故有不小心 push 了包含数据库密码的配置文件,因为 git 能够查看历史,所以即便你在之后的 commit 中将那个文件删除也可以通过回溯的方式找到那个文件,那么第一时间应该做什么呢?

修改密码

修改密码能让你急剧地降低风险,然后再去解决提交历史的问题

最简单的方法,直接把整个 .git 文件夹给删了,然后把密码文件给删了之后再 commit

除了这么简单粗暴的方法,书中还介绍了另一种指令 git filter-branch

由于构造例子很费灵感和时间,所以我就不本地构造环境了,就讲一下功能

如果你选择手动处理所有涉及到这个密码文件的 commit (因为有时候你发现问题的时候已经过了很多个 commit 了)

那么 rebase 的时候可能就很痛苦了,要一条一条 commit 的修改,中间可能还会遇到很多冲突,而这条命令可以一次修改大量的 commit,不过这个命令貌似很不常用,网上评价它为 git 中的核武器(?

使用用法如下

$ git filter-branch --tree-filter "rm -f config/database.yml"
$ git reset refs/original/refs/heads/master --hard

下面那行是用来后悔之前的fliter-branch 行为的,这个命令的具体用法还是自己查查吧,感觉参数很多的样子,对所有的 commit 批量执行后面双引号里面的命令之后自动 commit 掉,除了删除配置文件,删除很大的二进制文件的时候也有奇效,修改完之后就要用 git push -f 强推仓库了

书中还提了下 git cherry-pick SHA-1 这个命令,主要用来复制指定 commit 的命令,也是小众命令,应该不常用,就不提起了

git的垃圾回收

文件进入了 git 中,那么想走是没那么容易的,想要全部清理感觉不是容易的事

上文中使用 filter-branch 来清理密码配置文件,然而下面又可以用 git reset 恢复,这说明其实这个动作并没有把密码文件给清理感觉,无法回档才能算干净地清理

自然是有深度清理的方法

$ git filter-branch -f --tree-filter "rm -f config/database.yml"

增加一个 -f 是因为要强行覆盖掉 filter-branch 的备份点,但还没有处理完,下面这条命令能把刚刚 filter-branch 的备份点给删了,禁止再往回跳

$ rm .git/refs/original/refs/heads/master

除了这个以外还有一个 git reflog 也要清理

$ git reflog expire --all --expire=now

这能让 reflog 立即过期(默认30天),使用 git fsck --unreachable 能看到很多 unreachable 状态的对象,最后叫垃圾车来回收那些没用的对象

$ git gc --prune=now

这样之后就把不该继续存在的对象给彻底清理了,就再也 reset 不回去了

上面那一系列行为就可以看出 git 的整个垃圾回收机制了,那些 unreachable 的对象正是 git 需要回收的垃圾,在 git gc 到来运送垃圾之前,那些对象处于未被回收状态,就还能被我们回溯到,而 git gc --prune=now 能让垃圾车迅速到来运走垃圾,所谓的 unreachable 被作者称作 “没人爱” 的边缘对象,指的就是 git 底层文件夹里那些没有被任何指针指向的对象,在没有被回收之前我们都能对这些对象进行 “拯救”

这便是 git 的垃圾回收过程

写这种笔记真的好累人啊,要是不做笔记应该早就看完这本书了,不过自己这样写一写确实加深了很多印象

不过这种技术类的笔记其实不保值,最实惠的学习方式还是读官方文档,我写的博客或者是别人写的属总有一天会因为某次更新而变成 useless

就剩两章了,远端仓库 和 git flow,很快就能接受掉这本书看 spring 去了

2023/03/08
> CLICK TO back <