Awesome Git (Notes)¶
这个文档是我自己的 Git 查漏补缺笔记:偏“日常高频 + 容易混淆的点”。 风格:先给心智模型,再给最常用命令,最后给坑点/排错。
Best Practice for Git¶
git config --global core.editor "nvim"
git config --global user.name "hotchilipowder"
git config --global user.email "h12345jack@gmail.com"
# 我倾向保持线性历史(但要理解 rebase 的代价)
git config --global pull.rebase true
# 多账号/避免明文:优先 cache(再按需调 timeout)
git config --global credential.helper cache
说明: 1. 设置默认编辑器 2. 设置默认用户名和邮箱(影响 commit 的 author/committer) 3. 设置 pull 的默认整合策略(rebase/merge/ff-only) 4. 设置凭证管理器(避免每次输入密码/Token)
Git 的三层心智模型(最重要)¶
很多命令之所以难记,是因为你没把它们映射到 Git 的三层:
HEAD:当前分支指向的提交(历史/指针)
Index / Staging:暂存区(git add 后那份)
Worktree:工作区(你编辑器里看到的文件)
我记忆方式:
restore:只动文件内容(Index/Worktree),一般不动 HEAD
reset:动 HEAD 指针(并可选择是否同步 Index/Worktree)
checkout:老命令,既能切分支又能还原文件,语义太多(所以后来拆出 switch/restore)
rm:把“删除文件”也变成一次变更(放进 Index 等你 commit)
四个命令的关系:restore / reset / checkout / rm¶
Git Restore¶
git restore 是“恢复文件内容”的新命令,用来替代一部分 git checkout -- <file> 的用法。
不带
--staged:默认从 Index 恢复到 Worktree带
--staged:默认从 HEAD 恢复到 Index--source <commit>:指定从某个 commit/branch/tag 当作“恢复源”
# 1) 丢弃工作区改动(未 add):Worktree <- Index
git restore .
git restore path/to/file
# 2) 取消暂存(已经 add 了想撤回):Index <- HEAD
git restore --staged .
git restore --staged path/to/file
# 3) 同时撤销暂存 + 工作区(把文件回到 HEAD):Index+Worktree <- HEAD
git restore --staged --worktree path/to/file
# 常见:只恢复某个 commit 的版本
git restore --source <commit> --staged --worktree path/to/file
# 4) 交互式恢复(hunk级别)
git restore -p path/to/file
备注
restore 不会移动 HEAD,所以不会“撤销提交历史”,它只是在改你当前这份文件状态。
Git Reset¶
reset 的核心是“把 HEAD 指针挪到某个 commit”,然后选择是否同步 Index/Worktree。
# soft:只移动 HEAD(保留 Index/Worktree)
git reset --soft HEAD~1
# mixed(默认):移动 HEAD + 重置 Index(保留 Worktree)
git reset HEAD~1
git reset --mixed HEAD~1
# hard:移动 HEAD + 重置 Index + 重置 Worktree(最危险)
git reset --hard HEAD~1
警告
reset --hard 是“仓库级大杀器”,优先用 restore 精准恢复单文件/单目录,除非你确定要彻底丢弃。
Git Checkout / Switch¶
checkout 是老命令:
- git checkout <branch> 切分支
- git checkout -- <file> 还原文件
后来 Git 拆成两个更清晰的命令:
- git switch:只切分支
- git restore:只还原文件
git switch main
git switch -c feat/xxx
git switch -
Git RM¶
rm 是“产生删除变更”,不是撤销工具。
# 删除并暂存(commit 后历史里就删了)
git rm path/to/file
# 只从 git 索引里移除(保留本地文件)
git rm --cached path/to/file
常见“等价/不等价”对照表(防止混淆)¶
你看到的命令 |
直觉想法 |
更准确的解释 |
|---|---|---|
|
“是不是等于 reset?” |
不等价。restore 主要是 Worktree <- Index(不动 HEAD) |
|
“是不是等于 reset?” |
这更像“取消暂存”:Index <- HEAD(不动 HEAD) |
|
“是不是等于 reset --hard?” |
对这个文件效果很像“回到 HEAD”,但不移动 HEAD,不影响其它文件 |
|
“彻底回滚” |
可能移动 HEAD(如果指定了目标),并同步 Index+Worktree,通常是全仓库级别 |
Pull / Rebase / Merge / Fast-forward(你最困惑的点)¶
git pull 的本质¶
git pull = git fetch + “把远端整合进来”。
整合方式常见三种:fast-forward / merge / rebase。
Fast-forward(快进)是什么?¶
如果本地分支没有额外提交,只是落后远端,那么 Git 只需要把分支指针往前挪一下。 这叫 fast-forward(不会产生 merge commit)。
我理解成:历史是一条线,指针快进。
A---B---C (origin/main)
A---B (main)
pull 后(fast-forward):
A---B---C (origin/main, main)
Merge(合并)是什么?¶
如果本地也有提交、远端也有提交,历史分叉了:
D---E (main)
/
A--B--C (origin/main)
merge 会创建一个新的 merge commit,把两条线汇合:
D---E
/ \
A--B--C---M (main)
好处:保留真实分叉/汇合 坏处:log 会多一个 M(你会觉得不友好)
Rebase(变基)是什么?¶
rebase 的核心是:把你本地那串提交挪到远端最新提交的后面重放(commit id 会变)。
D---E (main)
/
A--B--C (origin/main)
rebase 后(线性):
A--B--C--D'--E' (main) A--B--C (origin/main)
好处:历史线性好读 坏处:D/E 变成 D'/E'(重写历史)
pull.rebase / pull.ff 我应该怎么选?¶
我自己的倾向(个人分支日常开发):
想要线性:
git config --global pull.rebase true想要绝不产生 merge commit(不能快进就报错):
git config --global pull.ff only
# 默认 pull 走 rebase(线性)
git config --global pull.rebase true
# 或者:只允许 fast-forward(不能快进就失败,逼你手动处理分叉)
git config --global pull.ff only
注意
如果一个分支已经被别人基于它开发/已经共享,慎用 rebase(会让别人需要处理“历史被重写”的问题)。
多账号快速工作流(我现在推荐的“可解释版本”)¶
你目前的方案是对的:clone 时指定 credential.username + 进入 repo 配 local 身份。
核心目的: - commit 的 author 不串(user.name/user.email) - push/pull 的认证不串(credential.username + helper)
步骤¶
# 1) clone 时临时禁用 helper + 指定用户名(避免被旧凭证污染)
git clone -c credential.helper= -c credential.username=xxx https://github.com/xxx/demo.git
# 2) 进入仓库后:把“身份”写死在本仓库 config
cd demo
git config --local user.name "xxx"
git config --local user.email "xxx@example.com"
git config --local credential.username "xxx"
# 3) 关键:让这个仓库(或全局)记住 token,不要每次输入
# cache 默认 900 秒(15分钟),我一般调长
git config --local credential.helper "cache --timeout=604800" # 1周
# 或全局(看你是否所有 repo 都能接受同一个策略)
# git config --global credential.helper "cache --timeout=604800"
解释:我到底要设置到什么程度?¶
user.name/user.email:只影响 commit 记录(不解决免密码)credential.username:告诉 Git 默认用哪个用户名(减少重复输入)credential.helper cache --timeout=...:决定“多久内不再输入 token”
备注
cache 是内存缓存:重启/daemon 退出就没了。安全性更好,但不是永久保存。
常见坑:MacOS 的 osxkeychain 多账号不好用怎么办?¶
我自己的经验是:多账号时 keychain 容易“串号/撞记录”,体验不稳定。 我一般用两个替代路线:
路线A(少折腾):HTTPS + cache(把 timeout 调长)
路线B(最稳):SSH 多 key(每个账号一把 key + ssh config 区分 host)
# 全局 cache + 8小时
git config --global credential.helper "cache --timeout=28800"
# 对某个 repo 单独设置 1周(更常用)
git config --local credential.helper "cache --timeout=604800"
# 想立刻清空缓存(排错时很有用)
git credential-cache exit
思路: - 账号1:Host github-personal -> 使用 key1 - 账号2:Host github-work -> 使用 key2 然后每个 repo 的 remote 用不同 host,从根上隔离身份。
排错:到底哪个 credential.helper 在生效?¶
git config --show-origin --get credential.helper
清理(按优先级逐级排查):
git config --local --unset credential.helper
git config --global --unset credential.helper
git config --system --unset credential.helper
Common git skill¶
Git Restore(再强调一次:适合“精准撤文件”)¶
# 丢弃工作区改动(未 add)
git restore .
# 取消暂存(staged -> unstaged)
git restore --staged .
# 单文件回到 HEAD(相当于“这个文件彻底不要了”)
git restore --staged --worktree path/to/file
# 从某个 commit 恢复文件
git restore --source <commit> -- path/to/file
Change history user.name and user.email¶
这个需求常见:多设备没设置 user.name/email,导致 history 里出现奇怪作者。 (注意:改历史会影响 commit id,做之前先备份/确认)
#!/bin/sh
git filter-branch --env-filter '
OLD_EMAIL="you@example.com"
CORRECT_NAME="hotchilipowder"
CORRECT_EMAIL="h12345jack@gmail.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
备注
最好的办法还是:一开始就把 user.name/user.email 配好,避免后面“洗历史”。
Delete all history¶
这个需求也常见:有些历史不想让人看到(太蠢了)。 (注意:这会让历史断掉,等于开新仓库)
git checkout --orphan latest_branch
git add .
git commit -m "Update"
git branch -D main
git branch -m main
git push -f origin main
Github Action¶
首先,github action 已经成为了软件开发领域不可获取的部分。
关于 Github Action 文档学习,
首先,需要创建 .github/workflow/xxx.yml目录文件。
下面是我在用的一些 Github Action
My config¶
mkdocs.yml
name: GitHub Pages
on:
push:
branches:
- main
jobs:
publish:
name: Deploy Site
runs-on: ubuntu-24.04
environment: github-pages
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Setup uv
uses: astral-sh/setup-uv@v6
- name: Install
shell: bash
run: |
uv pip install --system -r requirements.txt
sphinx-build -b html docs build
- name: Make snippets to rst
shell: bash
working-directory: snippets
run: |
python3 snippets.py
- name: Deploy Website
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build
本项目使用的github,其主要包括以下功能:
安装依赖+构建文档
Make snippsts to rst
push html to github page
Self-hosted Action¶
最近,得知了Github Action可以Self-hosted了。基于这个特性,将会非常好的使用Github Action去替换Jenkin。
具体的步骤主要是按照要求进行安装即可。