〇、引言
这篇博客介绍关于Git除git add/ git push/ git commit(以及git pull与git clone)以外的有效工具,其一是笔者用于记录学习,其二是避免此博客被荒废掉。
在进行协同开发亦或使用仓库储存代码时,均少不了使用Git,但是笔者在正式使用后却发现,日常似乎一直在不停git add .,git commit -m,git push original main并且近乎安于此,于是近日重新对其他的指令进行了学习,试图以此增进工作效率。
一、时间回溯⌚
1.1 不小心提交了不得了的commit?git reset一键回撤
git reset 是 Git 中用于移动 HEAD 指针、重置暂存区(index) 和/或 工作目录 的命令,常用于撤销提交、取消暂存文件或丢弃本地更改。它的行为取决于所使用的选项(--soft、--mixed(default)、--hard)以及指定的提交(commit)
标准写法如下:
1 | git reset [command] <commit> |
三个选项对应着三种不同的强度,其中soft为撤回上一次提交,但是保留修改在暂存区;mixed保留修改,但是清空暂存区,也是reset指令的默认模式;hard指令将代码和提交都恢复到上一次提交时的状态,彻底丢弃。示例如下:
1 | git reset --soft HEAD~1 //HEAD~1表示上一个提交,~2表示上两个,以此类推 |
需要注意,reset可回溯的有限,一般建议不要HEAD~后面太大
当然,reset指令也可以不更改HEAD指针直接针对暂存区的文件,常用如下代码:
1 | git reset -- <filename> //取消暂存一个文件,但不更改HEAD |
git reset如果想用于共享分支需要强制推送,建议使用revert
注意⚠️:
reset虽好,也不要贪多哦
1.2 带着现代的记忆回到过去? revert一键生成“反向commit”
这个指令与git reset有相似之处,但是git revert的使用场景与git reset完全不同,git revert是用于安全撤销某次提交的指令,不删除历史,而是创造一个相反的commit用于抵消之前的commit,基本写法如下:
1 | git revert <commit> |
其中比较常见的用法有以下两个
-n或--no-commit:只应用反向更改到工作区/暂存区,不自动提交(适合组合多次 revert 后统一提交)--edit/-e:强制编辑提交信息(默认会自动生成如 “Revert ‘xxx’”)
commit可以使用HEAD指针或者commit hash,举例如:
1 | git revert HEAD //创建一个新提交与HEAD完全相反 |
1.3 COMMIT打错字好丢人?amend提供“后悔药”
好不容易写完代码提交,push之前却发现不小心漏交文件或者发现打错字?不想再重新写一个垃圾提交污染纯净的信息?git commit 提供了这样一个指令:
1 | git commit --amend <command> |
举例而言,不妨假设我们现在写了commit.c提交
1 | git add commit.c //提交commit.c |
需要注意,
git reset与git commit --amend仅限于push之前的补救,push了还是自求多福罢…
1.3 reset的好帮手:reflog输出变更历史
git reset指令打错了,soft HEAD~1一下成为了hard HEAD~10?rebase失败,似乎自己的时间突然被挖空了一块。不用着急,git 也提供了git reflog指令用来查看历史编辑:只需要
1 | git reflog |
输出实例如下:
1 | n0tv4lid0 HEAD@{0}: reset: moving to HEAD~2 //hash + HEAD + command |
之后只需要找到对应操作的hash,之后使用git reset --hard <commit-hash>即可轻松恢复。
二、建立支部🌲
2.1 main裸奔施展不开?开分支放心搞事!
很多时候写东西总有后顾之忧,担心写坏了还没有备份,每次都时间回溯又嫌弃浪费时间,git checkout解决了这个问题。git checkout的基本指令如下:
1 | git checkout [command] <branch-name> |
比较常用的便是拉取新的分支与切换分支,分别对应:
1 | git checkout -b <new-branch-name> //在当前分支拉取一个新的分支 |
这里checkout -b可替代为下列指令
1 | git branch <new-branch-name> //可替换为branch指令 |
在Git23新推出了
git switch指令,关于二者不同参考此篇博客
2.2 我还没改完怎么让我拉别的分支? Stash暂存未提交修改
git stash的语法很简单,基本上只有两个
1 | git stash //暂存未提交的修改 |
这个十分简单,使用场景也很纯粹:双分支忙不过来不想提交半成品commmit,此指令暂时不作过多讲解。
三、合成大分支🍉
3.1 分支总算写完了…然后呢?
git merge这个指令十分常见,不应作过多解释,但是为了彰显与后续git rebase的区别,还是应当稍加完善。git merge的使用方式如下:
1 | git checkout n0tv4l1d0 //切换分支 |
也可以在main分支反向合并,效果相同:
1 | git checkout main //切换到主分支 |
3.2 唔唔啊…分支多了历史好混乱
git rebase指令对个人开发十分友好,与merge的基本使用方式完全相同,但是rebase指令会重写生成线性历史结构,具体流程与基本指令如下:
- 找到
feature相对于main的新提交。 - 临时移除这些提交,将
feature分支“移到”main的最新提交之后。 - 重新逐个应用(replay)那些提交。
1 | git rebase <branch-name> |
需要注意⚠️: 对已经推送到远程的公共分支执行
rebase会破坏他人历史,切忌在公共场所随地rebase。
3.3 不想要整个分支?cherry-pick选定特定提交
git cherry-pick提供了一种新的指令,允许用户不整合整条分支而是提取某(几)个特定提交,具体指令如下
1 | git cherry-pick <commit-hash> //后面可以包含多个hash |
使用
<hash_old>..<hash_new>时不包括<hash_old>对应版本,如欲包括需要使用<hash_old>^..<hash_new>
四、不做背锅侠🦸♀️
4.1 谁动了我的代码?blame一键找出
git blame的这个名字十分奇怪,虽然无从得知Linus是在何种情形下写出这个指令的,但是这个代码的使用情景十分契合它的名字。git blame主要为找出代码每一行的修改详情,基本指令十分简单,只需要:
1 | git blame n0tvalid0.c |
返回的详情包括每一行修改者,修改时间与对应commit;可以找到代码的家长和创造代码的原因(从commit)
4.2 我都干了什么?Diff一键获取修改
git diff是一个十分实用的指令,用于在commit之前确认自己到底都更新了什么。具体指令如下:
1 | git diff //用于暂存区或上次提交与工作区的diff |
git diff尤其应当作为团队开发的常客,知道自己有无不小心的改动也是为他人成果的负责。
4.3 防患于未然:使用Hook钩子防止一时糊涂
hook是Git提供的钩子系统,用于自动化运行所写的脚本。
hook放于.git/hooks/目录下,Git提供了十余种规范的文件名,包括pre-commit,prepare-commit-msg,commit-msg,post-commit,pre-push,post-checkout,post-merge,举个例子而言,比如写一个pre-commit钩子,只需要在.git/hooks/目录下新建一个名为pre-commit的文件(无后缀),脚本内容示例如下:
1 | !/bin/sh |
与基本.sh写法无异,写之后需要chmod开放执行权限
1 | chmod +x .git/hooks/pre-commit |
4.4 查看Log太丑了?美化的Log用不用?
git log指令作为一个常见指令,因其不易读而令人生畏,原版指令如下:
1 | git log |
但是其实可以通过加上其他功能使得其更易读
1 | git log --oneline --graph --decorate --all |
oneline:提交一行一个,避免拥挤
graph:带树形图
decorate:显示分支/tag
all:显示全部分支
如果不用Linux而用Windows自带的git bash,无法自动补全上次输入的指令导致费时费力,可以选择.gitconfig添加别名(假如以后输入git lg)
1 | [alias] |
五、 清理仓库🧹
5.1 项目没有LTS版本十分混乱? 做好Tag以防后患
Git里比较常用的push指令便是git push origin main,但是一直在main上push难免会导致项目如同Arch Linux一样(I use Arch, btw)容易崩坏,对于相对稳定的版本,发布新的Tags十分关键。基本指令如下:
1 | git tag v0.0.1 |
后续push只需要
1 | git push origin v0.0.1 |
5.2 仓库填满饭桶与肥猪?写好ignore清除没用的东西
.gitignore不是一个很少见的文件,至少如果在GitHub上新建一个repo的时候,一般都会有一个框问是否添加.gitignore。(虽然大部分新手会选择取消勾选)。
.gitignore只需要在文件里加入不上传的CACHE、.env等文件,比如
1 | node_modules/ |
写完之后放在仓库里即可避免垃圾文件堆满整个仓库。