标签
标签 (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 的垃圾回收过程
2023/03/08写这种笔记真的好累人啊,要是不做笔记应该早就看完这本书了,不过自己这样写一写确实加深了很多印象
不过这种技术类的笔记其实不保值,最实惠的学习方式还是读官方文档,我写的博客或者是别人写的属总有一天会因为某次更新而变成 useless
就剩两章了,远端仓库 和 git flow,很快就能接受掉这本书看 spring 去了