Git
Git 是一个分布式版本控制系统,非常流行,可以称得上是目前的行业标准。
Git 保存的代码通常被叫做仓库。仓库可以是 本地仓库,存放在本机上;也可以是 远程仓库,存放在服务器上。
有很多的服务商可以托管 Git 仓库,比如 GitHub、GitLab 等。这些服务大多都是免费的,让租不起服务器的开发者也能够与别人进行协作,推动了开源社区的发展。
代码修改后需要提交,然后变更会被记录到仓库中。
每个提交都会产生一个唯一的哈希值,你可以用这个哈希值来指定具体的单次提交。也可以用 HEAD 表示最近的提交,用 HEAD~n 来表示最近提交的前 n 个提交
提交时必须有提交信息,这些信息是给人阅读的。提交信息应该遵循一定的规范。Conventional Commits 是一个社区里常用的提交范式,如果不知道怎么写提交的话可以参考一下。
暂存使得我们可以在未正式提交前就备份代码,并且能够精确控制提交的内容。
暂存区(有时也叫索引)用于存放准备提交的变更。在提交修改后,暂存区会被清空。
分支是 Git 中的重要功能,正是分支让 Git 成为了 分布式 的版本控制系统
远程仓库存储公共的分支,而每个协作者只在自己本地私有的分支上修改。这使得协作者只需关注并同步的特定公共分支,而不需要关注其它分支,大大降低了沟通成本
在实现新功能之前最好本地新建一个分支,推送到远程仓库时也应该推送到指定的分支。项目通常会有一个主分支,主分支的代码应保证始终可用。远程仓库中可以启用主分支保护以避免被推送更新,然后由高权限的人员定期将别的分支合并到主分支中
钩子指一段自动触发的脚本。Git 提供了钩子机制,可以在其执行某个操作前/后自动运行一段脚本。
Git 中有很多种钩子,可以修改它们来实现想要的功能。更多介绍请阅读 官方文档
pre-commit提交前钩子,会在git commit前自动运行post-commit提交后钩子,会在git commit后自动运行commit-msg提交消息钩子,由git commit/git merge调用。这个钩子可以直接编辑消息文件来规范提交消息,还可以在检查消息文件后拒绝提交post-checkout检出后钩子,会在git checkout/git switch后自动运行pre-rebase变基前钩子,会在git rebase前自动运行post-merge合并后钩子,会在git merge/git pull后自动运行pre-push推送前钩子,会在git push前自动运行
对于常用的提交前钩子,社区维护了一个框架,可以在 prek 章节中了解更多。
# Windowsscoop install git# Ubuntuapt install git# Archpacman -S git# macOSbrew install gitGit 有非常多的子命令,详细的使用说明可以参考 官方文档
对于本地使用,基本的工作流是这样的
graph
初始化 --> 修改 --> 暂存 --> 提交
# 初始化本地仓库git init# 暂存所有修改git add --all# 提交修改git commit -m "Message"若要远程协作,则工作流会更复杂一点
graph
克隆 --> 新建分支 --> 拉取 --> 修改 --> 暂存 --> 提交 --> 推送
# 将远程仓库克隆到本地git clone <url># 创建并切换到新分支git branch <name>git switch <name># 从远程仓库指定分支中拉取修改到本地仓库的对应分支git pull <repository> <branch># 将本地仓库指定分支的修改推送到远程仓库的对应分支git push <repository> <branch>Git 使用配置文件,默认位置为 ~/.gitconfig。可以直接修改文件,也可以使用 CLI 进行配置
一些常用的配置如下
# 邮箱和姓名git config --global user.name xxxgit config --global user.email xxx@example.com# 关闭非 ASCII 字符转义git config --global core.quotepath false# 修改默认编辑器git config --global core.editor 'code --wait'分支可以创建、切换、删除
# 创建分支git branch <name># 切换分支git switch <name># 删除分支git branch --delete <name># 列出所有分支git branch --all分支合并时如果发生冲突通常需要手动解决
# 将指定分支合并到当前分支git merge <name>暂存区和工作目录
Section titled “暂存区和工作目录”工作目录中的修改可以保存到暂存区里,也可以移除出暂存区
# 查看工作目录和暂存区的状态git status# 将修改保存到暂存区git add <file> # 单个文件git add . # 整个目录# 将修改移除出暂存区git restore --staged <file>git restore --staged :/暂存区可作为一个临时的备份,可以查看未暂存或已暂存的更改。也可以随时撤销未暂存的更改,恢复成已暂存的状态
# 显示未暂存的更改git diff <file> # 单个文件git diff # 整个暂存区# 显示已暂存的更改git diff --staged <file> # 单个文件git diff --staged # 整个暂存区# 撤销对单个文件的未暂存更改git restore path/to/file# 撤销所有的未暂存更改git restore :/Git 的提交历史记录了所有的变更。可以查看、比较这些变更
# 查看完整的提交记录git log# 查看提交的详细信息git show # 最新提交git show HEAD~3 # 前三个提交# 比较两次提交之间的差异git diff <commit-hash> <commit-hash>Git 的提交历史可以回溯和修改
# 回溯到之前的某个提交git reset HEAD~1 # 前一个提交git reset <commit-hash> # 指定的提交# 交互式地修改某个提交之后的提交历史git rebase -i HEAD~5 # 前五个提交git rebase -i <commit-hash> # 指定的提交git rebase -i --root # 初始提交可以把当前提交作为补丁添加到之前的某个提交上
git commit --fixup=<commit-hash>git rebase -i --autosquash <commit-hash>^rebase 编辑器打开时,Git 会自动把 fixup! 提交移动到目标提交之后,并标记为 fixup。确认没问题后直接保存退出即可
配置差异工具
Section titled “配置差异工具”VSCode 有更易阅读 diff 视图。由于 VSCode 提供了命令行工具 code,因此可以修改 ~/.gitconfig 让 git 能够使用 VSCode 的 diff
[diff] tool = vscode[difftool "vscode"] cmd = code --wait --diff \"$LOCAL\" \"$REMOTE\"[difftool] prompt = false之后就可以使用 git difftool 打开 VSCode 的预览视图来查看差异。而 git difftool 的用法和 git diff 完全相同
git difftool <commit-hash> <commit-hash>git 可以给远程仓库设置别名。运行 git clone 后远程仓库会拥有默认的别名 origin。可以添加别的远程仓库以及对应的别名,也可以修改已有仓库的别名
# 查看所有远程仓库的地址和别名git remote -v# 添加远程仓库git remote add <name> <url># 修改远程仓库的别名git remote rename <old-name> <new-name>在 GitHub 等平台协作时,通常习惯把自己 fork 的仓库设为 origin,而原仓库设为 upstream。如果此前已经 clone 了原仓库,那么可以修改原仓库的别名,然后添加自己的仓库
# 把原来的 origin 重命名为 upstreamgit remote rename origin upstream# 把自己的仓库设为 origingit remote add origin <url>推送和拉取的时候,可以直接使用别名,而不必填写完整的 url
# 把 origin 仓库 main 分支的修改拉取到本地仓库git pull origin main# 把本地仓库 main 分支的修改推送到 origin 仓库git push origin main可以修改仓库中的 git 配置,让 git pull 和 git push 使用默认的仓库和分支
# 默认为 origin 仓库的 main 分支和本地仓库的 main 分支git branch --set-upstream-to=origin/main main远程跟踪分支
Section titled “远程跟踪分支”git pull 会把远程仓库中的修改合并到本地仓库的对应分支中。但有些时候我们只想知道远程仓库的状态,不想修改本地分支,这时就需要远程跟踪分支:把远程仓库的状态缓存到本地仓库中
# 更新对应远程仓库的跟踪分支git fetch <repository># 列出所有跟踪分支git branch --remotes然后我们可以手动把修改合并到本地分支中
# 把 upstream 的 main 分支合并到本地的 main 分支git switch maingit merge upstream/main# 或者使用 rebasegit rebase upstream/main远程跟踪分支毕竟只是缓存,而缓存就总会有失效的时候——比如本地仓库中还有对应分支的跟踪,但这个分支已经在远程仓库中删除了。此时可以用多种方式清理跟踪分支
# 更新后自动清理失效的跟踪分支git fetch --prune <repository># 只清理失效的跟踪分支,不更新跟踪分支git remote prune <repository>也可以修改 git 配置,让 git fetch 会默认清理失效的跟踪分支
git config --global fetch.prune true