今天你 commit 了么?
🧸 代码提交的乐子
提交的类型标签可以参考 gitmoji,常用的如下:
commit 模板 | 说明 | Explain |
---|---|---|
🎉 initial: | 新开项目,初次提交 | Start an adventure |
✨ feat: | 引入新特性/功能 | Introduce new features |
🍻 drunk: | 醉写代码 代码没写完 | Write code drunkenly |
🐛 fix: | 修复bug | Fix a bug |
🚑️ hotfix: | 重要补丁 | Critical hotfix |
📦 chore: | 构建过程或辅助工具的变动 | Other modifications |
🚚 chore: | 移动或重命名文件 | move/rename file |
🎨 style: | 代码格式修改 | Format |
📝 docs: | 修改文档 | Add or update documentation |
🛠 build: | 影响项目构建或依赖项修改 | |
🏗️ architect: | 架构的变动 | Make architectural changes |
🚀 perf: | 优化程序性能 | Improve performance |
🔨 refactor: | 代码重构 | Refactor |
🔬 test: | 测试用例新增、修改 | Add, update, or pass tests |
⬆️ dependencies: | 更新依赖版本 | Upgrade dependencies |
⬇️ dependencies: | 降低依赖版本 | Downgrade dependencies |
📌 dependencies: | 固定依赖版本 | Pin dependencies to specific versions |
✅ release: | 发布新版本 | |
🔄 workflow: | 工作流相关文件修改 | |
🔀 merge: | 合并分支 | Merge branches |
⏪️ revert: | 恢复更改 | Revert changes |
⏳ revert: | 恢复上一次提交 | |
🔖 tag: | 发布版本 | Release / Version tags |
🙈 gitignore: | 添加或更新 .gitignore | Add or update .gitignore |
👷 ci/cd: | 添加 CI 构建系统 / 修复 CI 构建问题 | Fix CI Build |
🔒 safety: | 修复安全问题 | |
🐧 linux: | 修复 Linux 下的问题 | Fix Linux issues |
🍎 macos: | 修复 macOS 下的问题 | Fix macOS issues |
🍏 ios: | 修复 iOS 下的问题 | Fix iOS issues |
🤖 android: | 修复 Android 下的问题 | Fix Android issues |
🚨 lint: | 移除 linter 警告 | Remove linter warnings |
🚧 | 工作进行中 | Working |
🐳 docker: | Docker 相关工作 | Docker |
🌐 i18n: | 国际化相关 | Internationalization |
某知乎网友:“这不是有没有必要,而是有没有意思。如果觉得给我的commit添加一个表情有意思的话,花点时间看看文档又有什么关系呢,又能浪费几分钟时间呢,大不了少花点时间看手机嘛”
提交的时候,按照如下格式
<type>(<scope>): <subject>
// 空一行
<body>
// 空一行
<footer>
type
是必须的,是关于本次提交的类型标签scope
用于说明 commit 影响的范围subject
是必须的, commit 的简短描述,要求:- 义动词开头,使用第一人称现在时(change,而不是changed)
- 第一个字母小写
- 结尾不加句号(.)
body
是对 commit 的详细描述,要求:- 使用第一人称现在时
🔰 Git 的一些概念
Git是一个分布式版本控制系统,它可以帮助你跟踪代码的变化,并与其他开发者协作。
版本控制是一种管理文件历史记录的方法,它可以让你回溯到任何一个时间点的文件状态,比较不同版本之间的差异,以及合并多人的修改。版本控制对于软件开发非常重要,因为它可以帮助你保持代码的完整性和一致性,避免错误和冲突,以及提高团队协作效率。
- 工作区 (Workspace):写代码的地方,在编辑器(vscode)写的每一行代码都是在工作区进行的
- 暂存区 (Index/Stage):对工作区的代码进行暂存并等待提交,进入了暂存区才会真正进入版本控制阶段(
.git/index
中保存了从工作区暂存的更改) - 本地仓库 (Repository):提交后的代码在本地仓库中进行版本控制
- 远程仓库 (Remote):云端仓库,一般存在 Github 上
🔨 Git 配置
配置级别
配置文件有三个级别,其优先级为:local
> global
> system
配置级别 | 配置文件 | 说明 |
---|---|---|
--local | .git/config | 仅对当前仓库有效 |
--global | ~/.gitconfig | 对当前用户有效 |
--system | /etc/gitconfig | 对系统所有用户有效 |
用户配置
开始之前,需要对git进行一些配置,关键是配置用户名和邮箱,这是用于与远端仓库进行联系的钥匙。
配置用户名 user.name
和邮箱 user.email
:
# 配置写入 ~/.gitconfig 对当前用户生效
# ✅ 推荐个人电脑上使用
git config --global user.name "your_name"
git config --global user.email "your_email@gmail.com"
# 配置写入 .git/config 对当前仓库生效
# 需要进入某个 Git 仓库中才能让该选项生效
git config --local user.name "your_name"
git config --local user.email "your_email@gmail.com"
# 配置写入 $(prefix)/etc/gitconfig 对所有用户生效
# ❌ 非常不推荐这种写法
git config --system user.name "your_name"
git config --system user.email "your_email@gmail.com"
查看全部配置,也可以查看特定的配置:
git config --list
git config user.name
默认编辑器
- 需要提前说明的是,git 的操作分为两种,一种是命令行进行操作 (例如
git rebase --interactive/-i
),另一种是在编辑器 (例如 vscode/Jetbrains) 中进行操作,使用命令行的方式是明确自己在做什么,熟悉之后再使用编辑器进行操作,可以提高效率。 - 在 vscode 中的 pull (拉取) 操作,默认就是采用 rebase 的方式,而用户有时候是希望采用 merge 的方式,因此需要在 vscode 中进行配置,因此在使用编辑器之前请确保了解每一次点击的操作具体在做什么
- Jetbrains 的 git 会比较好用,但是收费;所以我选 vscode + 命令行
配置默认编辑器 为 VScode
git config --global core.editor code # 全局 VScode
# git config --global core.editor vim / nano / $(which code)
查看当前设置的编辑器
git config --get core.editor
git config --get --global core.editor # 获取全局
🔨 Git 基本操作
开始一个新项目
在开一个新项目时候,需要初始化一下,并且进行一次提交
git init # 初始化仓库,生成 .git 目录
touch README.md # 给项目新建一个说明文件吧
git add . # (或者)添加文件到暂存区。
git commit # 将暂存区内容添加到仓库中。
git commit -m "🎉 initial: Start an adventure" # (或者)将暂存区内容添加到仓库中,并且添加注释
添加文件 add
将工作区的文件添加至暂存区
git add file1 file2 # 添加一个或多个文件到暂存区
git add dir # 添加指定目录到暂存区,包括子目录
git add . # 添加当前目录下的所有文件到暂存区
查看状态
查看当前分支下工作区和暂存区的状态
git status [-s] # -s (可选) 表示简短的输出结果
可以得到如下输出
On branch dev # 当前所处的分支
Your branch is ahead of 'origin/dev' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed: # 已经在暂存区, 等待添加到HEAD中的文件
(use "git reset HEAD <file>..." to unstage)
Changes not staged for commit:# 修改的文件,但是没有添加到暂存区
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
Untracked files: # 未跟踪的文件
(use "git add <file>..." to include in what will be committed)
显示索引文件和当前HEAD提交之间的差异,在工作树和索引文件之间有差异的路径以及工作树中没有被Git跟踪的路径。 第一个是通过运行git commit
来提交的; 第二个和第三个是你可以通过在运行git commit
之前运行git add
来提交的。 更多请阅读:git status命令
git log 查看提交日志
git log
命令用于查看历史提交的信息,包含了提交id(哈希 c)、作者、邮箱、日期时间和提交信息等
$ git log
commit 82484f07fd6b5468036be84b4695a8206e0ccc83
Author: <your_name> <your_email@mail.com>
Date: Tue Feb 21 09:20:39 2023 +0800
🚚 chore: move worksapce
--online
参数只输出提交id(哈希 SHA1值)和提交信息
$ git log --oneline
82484f07 🚚 chore: move worksapce
a88ccec2 🛠 build: add images to project
40位SHA1值过长,实际上在项目中取前7位产生冲突的概率很小,因此 git 使用前7位对提交记录进行表示
82484f07fd6b5468036be84b4695a8206e0ccc83
->82484f07
--graph
可以显示分支结构,不过在终端中还是有些麻烦,无所谓,在 vscode 中插件 Git Graph 会出手--stat
会显示哪些文件进行了修改--author="<author_name>"
将会过滤出指定用户<author_name>
--after '02-10-2023'"
/--before '02-21-2023'"
限定范围日期
🌿 分支操作
git 中的分支,本质上是个指向 commit 对象的可变指针。
HEAD 当前分支引用的指针,指向某一次 commit (默认上一次 commit ),通过 HEAD 可以知道当前工作在哪一个分支上。而该指针存储于 `.git/HEAD` 文件内,打开文件内容为ref: refs/heads/dev
而 ref
指向的则是 .git/refs/heads
,打开该目录,可以看到当前有 dev
和 master
两个文件
命令 git show head
是用来检查 Head 的状态的,该命令将显示 Head 的位置。
git show HEAD
🌿 分支命名
Git 常见分支命名
分支 | 说明 |
---|---|
main /master | 主分支。正式版本,最稳定的 |
dev | 开发分支。功能最新最全的分支,在此分支上修复 bug |
feat-* | 新功能分支。某个功能正在开发阶段,在此分支上写 bug |
release-* | 发布定期要上线的功能 |
bug-* /issue-* | 修复线上代码的 bug |
🌿 查看分支
git branch # 查看本地分支,带 * 表示当前分支
git branch -a # 查看本地和远程分支
🌿 创建和切换分支
查看分支
git branch <branch_name> # 新建分支
git checkout <branch_name> # 切换至指定分支
git checkout -b <branch_name> # 新建并切换至新分支
🌿 向远端推送分支
git push origin <branch_name> # 推送分支到远程
🌿 获取远程代码
从远程获取代码主要有两种方式
git pull
git fetch
(推荐)
git pull
将从远程 (origin
) 拉取最新版本到本地,自动合并(merge
)
git pull origin master
这种方法适用于 vscode 的时候, vscode 会自动弹出冲突位置,解决完成冲突后,提交一次 commit 就可以推送代码
git fetch
从远程拉取最新版本到本地,不会自动合并(merge
) ,
git fetch # 拉取远程全部分支
git fetch origin # 同上一条
git fetch origin main # 拉取远程的 main 分支
查看文件差异
git log -p main..origin/main
该命令执行完后需要执行 git merge
将远程分支合并至本地分支
git merge origin/main
这种方式适合于终端合并代码
每天工作开始之前,没啥事就 pull 自己的分支,然后 fetch 整个项目,开始愉快的一天
🌿 合并分支!!!
实际上合并分支有 git merge
和 git rebase
两种方式
例如将 dev 分支合并至 main 分支
git checkout main # 切换分支
git merge dev # 将 dev 合并进当前分支
这时候合并会产生一个新的提交(76f1e80
) 'Merge branch 'master' of...
git checkout dev # 切换分支
git rebase main # 将 main 变基至当前分支
在 dev 分支上执行 rebase 则会将 main 分支最后的提交作为基点,逐个应用 dev 的每个更改。
可以看出, rebase 的方式像是把当前分支的历史提交“接”到了 main
分支最新提交的后面,因此使用 rebase 则不会像 merge 那也因为合并而产生新的提交
在工作环境中,我们会在自己的分支上进行开发
关于 merge
和 rebase
在知乎上有个讨论 ,而 “rebase 的最大好处并不是消除 merge ,而是避免 merge 的交织”,因此建议如下以避免历史提交中无意义的交织:
- 将主分支 rebase 到自己的分支上
- 将自己分支的修改 merge 到主分支上
另外,尤雨溪曾说过:“多用 rebase”
🌿 分支合并策略
⬅️ 版本回退
撤回
git reflog
能列出你在 Git 上的所有操作记录。你只要找到 HEAD@{index}
前面所对应的操作索引,就可以 git reset
进行退回:
git reflog
git reset HEAD@{index}
使用时需将HEAD@{index}替换为对应索引。
获取指定提交 cherry-pick
https://zhuanlan.zhihu.com/p/58962086
git reset 重置
git reset [commit-hash]~1
这个命令是用来将当前分支的 HEAD 指针移动到指定提交的前一个提交。这里详细解释一下:
git reset
策略主要有如下几种:
- Soft Reset
📥 储藏代码
背景:为什么需要储藏代码呢?
某一天,在feature
分支上快乐地写着新的 BUG,突然,因为不可抗力因素必须切换到main
分支或者其他分支上 checkout 一个 hotfix 分支出来紧急修复之前写过的 BUG。但是当前的代码还没有写完,也没有提交,更不可能删了不要,因此就需要储藏当前分支的代码
执行 git stash
对当前的分支的修改进行储藏,之后
git stash
# 对储藏添加标记
git stash save [stashMessage]
执行后,用 git status
查看分支状态,工作区是干净的。就可以切换分支进行修改。
git stash list
可以查看储藏记录列表
git stash list
储藏记录每一条标识如下
stash@{index}: WIP on [分支名]: [最近一次的commitID] [最近一次的提交信息]
修改完成后再取出储藏的修改,二选一
git stash pop # 取出最近一次储藏的修改到工作区
git stash apply stash@{index} # 取出指定index的储藏的修改到工作区中
git stash drop stash@{index} # 将指定index的储藏从储藏记录列表中删除
🐛 修复 BUG
https://blog.csdn.net/Huang_ZX_259/article/details/122657055
🧽 压缩提交
参考: https://blog.csdn.net/starperfection/article/details/110764759
当前工作区中有 4 次提交
$ git log
commit d00e013a020c63e6b11fbb6483b1875f2c260464 (HEAD -> main)
Author: HenryZhuHR <26506195@qq.com>
Date: Sat Aug 26 05:25:21 2023 +0000
commit 3
commit 3f26195229713952d4134579351cb0a996cc2201
Author: HenryZhuHR <26506195@qq.com>
Date: Sat Aug 26 05:25:14 2023 +0000
commit 2
commit 8a27bc4bab19cbcb0ccb1c2bec92eb9d50de2fc2
Author: HenryZhuHR <26506195@qq.com>
Date: Sat Aug 26 05:25:03 2023 +0000
commit 1
commit f8db341ebd9b71a7ddfb59b65baaddcc6373e74b
Author: HenryZhuHR <26506195@qq.com>
Date: Sat Aug 26 05:24:17 2023 +0000
init
(END)
git rebase -i HEAD~n # 压缩最近 n 次提交
git rebase -i [start_hash] [end_hash] # 压缩的范围
git rebase -i HEAD~3 # 压缩最近 3 次提交
git rebase -i 8a27bc f8db34 # 压缩 8a27bc 到 f8db34 之间的提交
执行后会打开一个编辑器,内容如下, pick
修改为 squash
或者 s
,然后保存退出
pick 8a27bc4 commit 1
pick 3f26195 commit 2
pick d00e013 commit 3
commit 447daa0a3490147f40a7ef4b53a3c141f21b5636 (HEAD)
Author: HenryZhuHR <26506195@qq.com>
Date: Sat Aug 26 05:25:03 2023 +0000
commit test
commit f8db341ebd9b71a7ddfb59b65baaddcc6373e74b
Author: HenryZhuHR <26506195@qq.com>
Date: Sat Aug 26 05:24:17 2023 +0000
init
(END)
👦 子模块
在 git 中,可以通过子模块的方式将一个 git 仓库嵌套到另一个 git 仓库中,这样可以方便的管理多个仓库之间的依赖关系,或者可以避免单个仓库过大。
官方教程:Git 子模块
usage: git submodule [--quiet] [--cached]
or: git submodule [--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
or: git submodule [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: git submodule [--quiet] init [--] [<path>...]
or: git submodule [--quiet] deinit [-f|--force] (--all| [--] <path>...)
or: git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] [--] [<path>...]
or: git submodule [--quiet] set-branch (--default|--branch <branch>) [--] <path>
or: git submodule [--quiet] set-url [--] <path> <newurl>
or: git submodule [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
or: git submodule [--quiet] foreach [--recursive] <command>
or: git submodule [--quiet] sync [--recursive] [--] [<path>...]
or: git submodule [--quiet] absorbgitdirs [--] [<path>...]
添加子模块
通常来说,为仓库添加子模块的步骤如下[6]:
在当前 git 仓库下添加子模块
git submodule add <url> <path>
git submodule add git@github.com:HenryZhuHR/homepage-docs.git docs
<url>
是子模块的地址,<path>
是子模块的路径
获取子模块的仓库
方法一:获取父仓库时,使用 --recursive
参数,可以一并获取子模块的仓库,需要注意 submodule 默认是不在任何分支上的,它指向父项目存储的 submodule commit id。
git clone --recursive <url>
方法二:获取父仓库后,使用 git submodule init
和 git submodule update
命令获取子模块的仓库
git clone <url>
cd <repo>
git submodule init
git submodule update