发布于2026-07-10 阅读(0)
扫一扫,手机访问
在日常开发中,Git 几乎成了每一位开发者绕不开的基础工具。但说实话,很多人对它的使用一直停留在 add、commit、push 这三板斧上,遇到复杂场景就容易手忙脚乱。其实,Git 的设计远比表面看起来要精巧得多,掌握一些核心命令和进阶技巧,不仅能大幅提升日常效率,还能在紧急情况下从容应对。下面就从最常用的命令开始,逐步深入到内部原理和团队协作实战。
开始一个新项目或者加入一个已有的项目,第一步往往是初始化仓库或者克隆一个远程仓库。这类操作虽然基础,但细节不少。

# 初始化新仓库 git init # 克隆远程仓库 git clone# 克隆到指定目录 git clone # 克隆指定分支 git clone -b
常见场景: 开始新项目或加入已有项目时,这些操作会频繁用到。比如通过 git clone 拉取团队仓库,或者用 git init 在本地新建一个仓库。
配置方面,首次使用 Git 时需要设置用户名和邮箱,此外还可以定制编辑器、配置别名来减少命令输入量。同时,不同项目可能需要不同的用户信息(比如工作项目和个人项目),项目级配置就可以派上用场。
# 配置用户名和邮箱 git config --global user.name "Your Name" git config --global user.email "your.email@example.com" # 配置默认编辑器 git config --global core.editor "code --wait" # VS Code git config --global core.editor "vim" # Vim # 配置命令别名 git config --global alias.st status git config --global alias.co checkout git config --global alias.br branch git config --global alias.ci commit git config --global alias.unstage 'reset HEAD --' git config --global alias.last 'log -1 HEAD' git config --global alias.visual 'log --graph --oneline --all' # 查看所有配置 git config --list # 查看特定配置 git config user.name
常见场景: 首次使用 Git 或更换开发环境时,需要设置全局配置;而项目级配置则适用于需要在不同项目中使用不同身份或处理方式的场景。
日常开发中最频繁的操作就是查看状态、比对差异、添加暂存、提交更改以及撤销修改。看似简单,但每一条命令都有它的使用技巧。
# 查看工作区状态 git status # 简洁版状态 git status -s # 查看未暂存的更改 git diff # 查看已暂存的更改 git diff --staged git diff --cached # 比较工作区与指定提交 git diff# 比较两个提交 git diff # 比较两个分支 git diff branch1..branch2 # 只显示文件名 git diff --name-only # 显示统计信息 git diff --stat
常见场景: 每次提交前检查更改内容,确保不会把调试代码或无关修改带进去。
# 添加指定文件 git add# 添加所有更改 git add . git add -A # 交互式添加 git add -p # 提交更改 git commit -m "commit message" # 添加并提交(仅已跟踪文件) git commit -am "commit message" # 修改最后一次提交 git commit --amend # 修改最后一次提交信息 git commit --amend -m "new message" # 空提交(触发CI/CD) git commit --allow-empty -m "trigger build"
常见场景: 保存工作进度,或者修正提交信息。
# 撤销工作区的修改(危险操作) git checkout --git restore # 新版本推荐 # 取消暂存文件 git reset HEAD git restore --staged # 新版本推荐 # 撤销最后一次提交,保留更改 git reset --soft HEAD~1 # 撤销最后一次提交,不保留更改(危险) git reset --hard HEAD~1 # 撤销指定提交(创建新提交) git revert # 清理未跟踪文件(预览) git clean -n # 清理未跟踪文件 git clean -f # 清理未跟踪文件和目录 git clean -fd
常见场景: 修正错误或者清理工作区,特别注意 reset --hard 会永久丢弃更改,使用前务必确认。
分支是 Git 最强大的特性之一,合理使用分支能让开发并行的同时互不干扰。创建、切换、合并、删除,每一步都有对应的命令。
# 查看所有分支 git branch git branch -a # 包括远程分支 git branch -r # 仅远程分支 # 创建新分支 git branch# 切换分支 git checkout git switch # 新版本推荐 # 创建并切换到新分支 git checkout -b git switch -c # 新版本推荐 # 基于远程分支创建本地分支 git checkout -b origin/ # 基于指定提交创建分支 git checkout -b
常见场景: 开发新功能或修复 Bug 时,通常从主分支创建功能分支。
# 合并指定分支到当前分支 git merge# 不使用快进合并 git merge --no-ff # 合并时squash所有提交 git merge --squash # 取消合并 git merge --abort
常见场景: 功能开发完成后合并回主分支。使用 --no-ff 可以保留分支历史,让后续查看更清晰。
# 删除本地分支 git branch -d# 强制删除本地分支 git branch -D # 删除远程分支 git push origin --delete # 批量删除已合并的分支 git branch --merged | grep -v "\*\|main\|develop" | xargs -n 1 git branch -d # 清理远程已删除的本地追踪分支 git fetch --prune git remote prune origin
常见场景: 保持仓库整洁,定期清理无用分支。
# 重命名当前分支 git branch -m# 重命名指定分支 git branch -m # 删除远程旧分支并推送新分支 git push origin --delete git push origin git push origin -u
常见场景: 分支命名有误或需要规范命名时使用。
远程仓库管理是团队协作的基础,包括添加、修改、删除远程地址,以及拉取、推送代码。
# 查看远程仓库 git remote -v # 添加远程仓库 git remote add origin# 修改远程仓库地址 git remote set-url origin # 重命名远程仓库 git remote rename origin upstream # 删除远程仓库 git remote remove origin # 查看远程仓库详细信息 git remote show origin
常见场景: 管理多个远程仓库,比如 Fork 了别人的项目后需要同时维护上游仓库和自己的远程仓库。
# 拉取远程更新 git fetch origin # 拉取并合并 git pull origin main # 使用rebase拉取 git pull --rebase origin main # 推送到远程 git push origin main # 推送所有分支 git push origin --all # 推送标签 git push origin --tags git push origin# 强制推送(危险,会覆盖远程历史) git push -f origin main git push --force-with-lease origin main # 更安全的强制推送 # 设置上游分支 git push -u origin main
常见场景: 同步本地和远程代码。注意强制推送会覆盖远程历史,团队中应谨慎使用,推荐用 --force-with-lease。
# 设置当前分支跟踪远程分支 git branch --set-upstream-to=origin/git branch -u origin/ # 查看跟踪关系 git branch -vv # 拉取所有远程分支 git fetch --all
常见场景: 团队协作时,本地分支与远程分支的跟踪关系必须正确,才能顺畅地 pull 和 push。
提交历史是代码演进的记录,通过 git log 可以回溯每一次变更,结合各种参数可以精准定位所需信息。
# 查看提交历史 git log # 单行显示 git log --oneline # 显示最近n条 git log -n 5 # 图形化显示分支 git log --graph --oneline --all # 显示每次提交的文件变更 git log --stat # 显示每次提交的详细更改 git log -p # 查看某个文件的历史 git log --# 搜索提交信息 git log --grep="keyword" # 按作者筛选 git log --author="name" # 按时间筛选 git log --since="2024-01-01" git log --until="2024-12-31" git log --since="2 weeks ago" # 查看某两个提交之间的历史 git log .. # 美化输出 git log --pretty=format:"%h - %an, %ar : %s"
常见场景: 代码审查、追踪变更时,配合 --stat 和 -p 可以快速了解每次提交做了什么。
# 查看最新提交 git show # 查看指定提交 git show# 查看某个提交的某个文件 git show : # 查看标签详情 git show
常见场景: 检查特定提交的内容,尤其是怀疑某个提交引入了问题。
# 查看文件每行的修改记录 git blame# 查看指定行范围 git blame -L 10,20 # 查看文件的修改历史 git log -p # 查看文件的重命名历史 git log --follow
常见场景: 追踪 Bug 来源,通过 git blame 找出是谁最后修改了某行代码。
标签通常用来标记版本发布点,比如 v1.0.0。推荐使用附注标签,因为它包含作者、日期和说明信息。
# 创建轻量标签 git tag v1.0.0 # 创建附注标签(推荐) git tag -a v1.0.0 -m "Release version 1.0.0" # 为历史提交打标签 git tag -a v0.9.0-m "Version 0.9.0" # 创建签名标签 git tag -s v1.0.0 -m "Signed release"
常见场景: 标记发布版本,方便后续快速回退或比对。
# 列出所有标签 git tag # 查找特定标签 git tag -l "v1.8.*" # 查看标签详情 git show v1.0.0 # 删除本地标签 git tag -d v1.0.0 # 删除远程标签 git push origin --delete v1.0.0 # 推送标签到远程 git push origin v1.0.0 git push origin --tags # 推送所有标签
常见场景: 版本发布管理,注意标签不会随分支推送,需要单独推送。
当你在一个分支上工作到一半,突然需要切换到其他分支处理紧急任务时,git stash 可以暂存当前工作区的修改,稍后再恢复。
# 保存当前工作进度
git stash
# 保存时添加说明
git stash sa ve "work in progress on feature X"
# 包括未跟踪文件
git stash -u
# 包括忽略的文件
git stash -a
# 查看stash列表
git stash list
# 查看stash内容
git stash show
git stash show -p # 显示详细差异
# 应用最新的stash
git stash apply
# 应用指定的stash
git stash apply stash@{2}
# 应用并删除stash
git stash pop
# 删除stash
git stash drop stash@{0}
# 清空所有stash
git stash clear
# 从stash创建分支
git stash branch
常见场景: 临时切换任务、保存未完成的工作。注意 git stash branch 可以从当前 stash 创建一个新的分支,适合用于恢复后不想覆盖原来分支的情况。
在项目代码量很大的时候,git grep 比普通文件搜索更快,因为它只搜索版本控制内的文件。
# 在工作区搜索 git grep "search-term" # 显示行号 git grep -n "search-term" # 统计匹配数量 git grep -c "search-term" # 在特定提交中搜索 git grep "search-term"# 在所有分支中搜索 git grep "search-term" $(git rev-list --all)
常见场景: 代码审计、查找 API 使用情况时,可以快速定位引用。
如果遇到某个回归 Bug,想精确找到引入它的提交,git bisect 就是一把利器。
# 开始二分查找 git bisect start git bisect bad # 当前版本有bug git bisect good# 已知的好版本 # 标记当前版本 git bisect good # 或 git bisect bad # 自动化bisect git bisect run # 结束bisect git bisect reset
常见场景: 快速定位引入问题的提交,尤其适用于自动化测试可以通过的环境。
.gitignore 文件用于排除不需要版本控制的内容,比如编译产物、依赖包和环境配置文件。
# .gitignore 示例 # 忽略所有 .log 文件 *.log # 忽略 node_modules 目录 node_modules/ # 忽略所有 .env 文件 .env .env.local # 但不忽略 .env.example !.env.example # 忽略所有 .pdf 文件,除了 docs/ 目录下的 *.pdf !docs/*.pdf # 忽略构建产物 dist/ build/ *.min.js
有时文件已经被跟踪后,才想加入 .gitignore,需要先停止跟踪。
# 停止跟踪文件但保留在工作区 git rm --cached# 停止跟踪目录 git rm -r --cached # 更新.gitignore后应用 git rm -r --cached . git add . git commit -m "Update .gitignore"
常见场景: 排除不需要版本控制的文件,或者纠正之前错误添加的文件。
误操作是难免的,Git 提供了一系列恢复手段,其中 git reflog 堪称救命稻草。
# 查看操作历史
git reflog
# 恢复到某个历史状态
git reset --hard HEAD@{2}
# 恢复误删的分支
git checkout -b
# 恢复误删的文件
git checkout --
常见场景: 误操作后的紧急恢复。记住 git reflog 记录了本地的所有操作,即使 git log 看不到的提交,reflog 里也可能存在。
举个实际例子:你正在开发,突然收到线上紧急问题需要修复。
# 快速保存所有更改 git stash # 切换分支处理紧急问题 git checkout hotfix-branch # 处理完后回到原分支 git checkout original-branch git stash pop
常见场景: 紧急处理线上问题时,stash 可以让你保存当前进度。
掌握了单个命令,还需要能组合使用。下面列出几个典型协作场景的命令序列,基本覆盖了日常开发的全流程。
场景1:开始新功能开发
# 1. 更新主分支 git checkout main git pull origin main # 2. 创建功能分支 git checkout -b feature/user-login # 3. 开发并提交 git add . git commit -m "feat: implement user login" # 4. 推送到远程 git push -u origin feature/user-login
场景2:更新功能分支
# 1. 保存当前工作 git stash # 2. 切换到主分支并更新 git checkout main git pull origin main # 3. 回到功能分支 git checkout feature/user-login # 4. 合并主分支的更新 git merge main # 或使用 rebase git rebase main # 5. 恢复工作 git stash pop
场景3:代码审查后合并
# 1. 更新主分支 git checkout main git pull origin main # 2. 合并功能分支(不使用快进) git merge --no-ff feature/user-login # 3. 推送到远程 git push origin main # 4. 删除功能分支 git branch -d feature/user-login git push origin --delete feature/user-login
场景4:修复合并冲突
# 1. 尝试合并 git merge feature-branch # 2. 查看冲突文件 git status # 3. 手动解决冲突或使用工具 git mergetool # 4. 标记为已解决 git add# 5. 完成合并 git commit # 或者放弃合并 git merge --abort
场景5:同步Fork的仓库
# 1. 添加上游仓库 git remote add upstream# 2. 拉取上游更新 git fetch upstream # 3. 合并到本地主分支 git checkout main git merge upstream/main # 4. 推送到自己的远程仓库 git push origin main
| 类别 | 命令 | 说明 |
|---|---|---|
| 初始化 | git init | 初始化仓库 |
git clone | 克隆仓库 | |
| 配置 | git config --global user.name "name" | 设置用户名 |
git config --global user.email "email" | 设置邮箱 | |
| 状态 | git status | 查看状态 |
git diff | 查看未暂存的更改 | |
git diff --staged | 查看已暂存的更改 | |
| 添加提交 | git add | 添加文件 |
git add . | 添加所有更改 | |
git commit -m "msg" | 提交 | |
git commit -am "msg" | 添加并提交 | |
| 分支 | git branch | 列出分支 |
git branch | 创建分支 | |
git checkout | 切换分支 | |
git checkout -b | 创建并切换分支 | |
git merge | 合并分支 | |
git branch -d | 删除分支 | |
| 远程 | git remote -v | 查看远程仓库 |
git fetch | 拉取远程更新 | |
git pull | 拉取并合并 | |
git push | 推送到远程 | |
git push -u origin | 推送并设置上游 | |
| 历史 | git log | 查看历史 |
git log --oneline | 简洁历史 | |
git log --graph | 图形化历史 | |
git show | 查看提交详情 | |
| 撤销 | git checkout -- | 撤销工作区更改 |
git reset HEAD | 取消暂存 | |
git reset --soft HEAD~1 | 撤销提交保留更改 | |
git reset --hard HEAD~1 | 撤销提交丢弃更改 | |
git revert | 反转提交 | |
| 暂存 | git stash | 暂存更改 |
git stash list | 查看暂存列表 | |
git stash pop | 应用并删除暂存 | |
git stash apply | 应用暂存 | |
| 标签 | git tag | 创建标签 |
git tag -a | 创建附注标签 | |
git push --tags | 推送标签 | |
| 其他 | git clean -fd | 清理未跟踪文件 |
git reflog | 查看引用日志 | |
git grep "text" | 搜索代码 |
搞懂 Git 的对象模型和引用机制,是进阶为 Git 高手的必经之路。下面我们来看看 Git 是如何存储数据的。
Git 使用四种主要对象类型存储数据:Blob(文件内容)、Tree(目录结构)、Commit(提交快照)和 Tag(标签)。
Blob 对象 保存文件内容,但不包含文件名。
# 查看文件对应的 blob 对象 git hash-object README.md # 查看 blob 对象内容 git cat-file -p# 查看对象类型 git cat-file -t
Tree 对象 记录目录结构和文件名映射关系。
# 查看树对象
git cat-file -p master^{tree}
# 查看提交的树对象
git ls-tree -r HEAD
Commit 对象 包含指向树对象的指针、父提交、作者、提交信息等。
# 查看提交对象详细信息 git cat-file -p HEAD # 查看提交的父节点 git rev-parse HEAD^ git rev-parse HEAD~2
Tag 对象 附注标签会创建一个标签对象,包含标签名、消息和指向提交的指针。
# 创建附注标签(创建 tag 对象) git tag -a v1.0.0 -m "Release version 1.0.0" # 查看标签对象 git cat-file -p v1.0.0
引用(refs)是 Git 用来指向特定提交的指针,比如分支、标签和 HEAD。reflog 则记录了这些引用在本地仓库中的变化历史,是恢复误操作的关键。
# 查看所有引用
git show-ref
# 查看 HEAD 的历史记录
git reflog
# 恢复误删的提交
git reflog
git reset --hard HEAD@{2} # 回到2步之前的状态
# 恢复误删的分支
git reflog show
git checkout -b HEAD@{n}
常见场景: 恢复误删的分支或提交。只要没有执行垃圾回收,reflog 中的记录通常能帮你找回丢失的提交。
随着仓库使用时间的增长,对象数量会越来越多,Git 会自动将松散对象打包成 pack 文件以减少空间占用。手动执行垃圾回收可以进一步压缩仓库。
# 查看对象数量 git count-objects -v # 手动触发垃圾回收 git gc --aggressive --prune=now # 查看打包文件 git verify-pack -v .git/objects/pack/*.idx | head -20 # 找出最大的文件 git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10
常见场景: 优化仓库大小,清理无用对象。
在实际团队协作中,只有合理的分支策略才能保证代码质量和发布节奏。
Git Flow 是一种经典的分支管理模型,适合大型团队的发布管理,它定义了 main、develop、feature、release、hotfix 等分支类型。
# 初始化 git flow git flow init # 开始新功能开发 git flow feature start user-authentication # 完成功能开发 git flow feature finish user-authentication # 开始发布分支 git flow release start 1.2.0 # 完成发布 git flow release finish 1.2.0 # 紧急修复 git flow hotfix start critical-bug git flow hotfix finish critical-bug
GitHub Flow 更简洁,适合持续部署的敏捷团队。只有一个长期分支 main,功能分支通过 Pull Request 合并。
# 从 main 创建功能分支 git checkout -b feature/new-api main # 定期推送并创建 PR git push -u origin feature/new-api # PR 合并后更新本地 git checkout main git pull origin main git branch -d feature/new-api
在 GitHub 或 GitLab 上可以设置分支保护规则,比如要求代码审查、状态检查通过,禁止强制推送等。
# 本地模拟分支保护 git config branch.main.pushRemote no_push
定期清理已合并的分支也是保持仓库整洁的好习惯。
# 删除已合并的本地分支
git branch --merged main | grep -v "main" | xargs git branch -d
# 删除远程已删除的本地追踪分支
git fetch --prune
# 批量删除远程分支
git branch -r --merged main | grep -v "main" | sed 's/origin\///' | xargs -I {} git push origin --delete {}
当需要将某个分支的部分提交移植到另一个分支,或者重新设置分支的基准点时,可以使用 cherry-pick 和 rebase --onto。
# 将分支 A 的提交移植到分支 B git checkout feature-B git cherry-pick# 批量 cherry-pick git cherry-pick .. # 重新设置分支基点 git checkout feature-branch git rebase --onto main old-base feature-branch # 示例:将 feature 从旧的 develop 分支移到新的 main 分支 git rebase --onto main develop feature
有时候你只想提交文件中的部分修改,而不是全部。Git 提供了交互式暂存能力,可以逐块选择。
# 进入交互式暂存模式 git add -i # 交互式选择文件片段 git add -p# 在编辑器中精确选择行 git add -e
交互式暂存快捷键:
y - 暂存这个块n - 不暂存这个块s - 分割成更小的块e - 手动编辑块q - 退出通过交互式 rebase 可以合并、编辑、删除或重新排序提交,是整理提交历史的利器。
# 交互式重写最近 5 个提交 git rebase -i HEAD~5 # 在编辑器中: # pick = 保留提交 # reword = 修改提交信息 # edit = 修改提交内容 # squash = 合并到上一个提交 # fixup = 合并到上一个提交但丢弃消息 # drop = 删除提交
实战示例:合并多个小提交
# 假设有以下提交历史: # abc123 Fix typo # def456 Add tests # ghi789 Implement feature # jkl012 Fix linting git rebase -i HEAD~4 # 编辑器中修改为: # pick ghi789 Implement feature # squash def456 Add tests # squash abc123 Fix typo # squash jkl012 Fix linting
使用 Conventional Commits 规范可以让提交信息更加统一,也方便自动生成 CHANGELOG。
# 使用 Conventional Commits 规范 git commit -m "feat(auth): add OAuth2 login support" git commit -m "fix(api): resolve null pointer exception" git commit -m "docs(readme): update installation guide" git commit -m "refactor(database): optimize query performance" git commit -m "test(user): add unit tests for user service" git commit -m "chore(deps): update dependencies" # 使用 commitizen 工具 npm install -g commitizen cz-conventional-changelog git cz # 交互式生成规范的提交信息
在某些情况下,你需要修改已经提交的历史,比如移除敏感文件或修正作者信息。注意,重写已推送的公共历史通常是被禁止的。
# 修改特定提交 git rebase -i^ # 将对应行改为 'edit',保存退出 git commit --amend git rebase --continue # 全局替换文件内容 git filter-branch --tree-filter 'rm -f passwords.txt' HEAD # 使用 git-filter-repo(推荐,更快更安全) pip install git-filter-repo git filter-repo --path passwords.txt --invert-paths
# 修改最后一次提交的作者 git commit --amend --author="New Author" # 批量修改历史作者信息 git filter-branch --env-filter ' if [ "$GIT_COMMITTER_EMAIL" = "old@example.com" ]; then export GIT_COMMITTER_NAME="New Name" export GIT_COMMITTER_EMAIL="new@example.com" export GIT_AUTHOR_NAME="New Name" export GIT_AUTHOR_EMAIL="new@example.com" fi ' --tag-name-filter cat -- --all
如果一个大提交包含了多个不相关的改动,可以把它拆分成多个独立提交。
# 重置到要拆分的提交 git rebase -i^ # 标记为 'edit' # 取消暂存所有文件 git reset HEAD^ # 分别添加和提交 git add file1.js git commit -m "Part 1: Add feature A" git add file2.js git commit -m "Part 2: Add feature B" # 继续 rebase git rebase --continue
合并冲突是日常开发中常见的头疼事,Git 提供了多种解决冲突的手段。
配置一个可视化的合并工具可以极大提升解决冲突的效率。
# 配置合并工具(vimdiff/kdiff3/meld/p4merge) git config --global merge.tool vimdiff git config --global mergetool.prompt false # 使用合并工具 git mergetool # VS Code 作为合并工具 git config --global merge.tool vscode git config --global mergetool.vscode.cmd 'code --wait $MERGED'
有时你可能希望直接采用当前分支或合并分支的内容,可以使用 -X theirs 或 -X ours 策略。
# 使用 ours 策略(保留当前分支) git merge -X ours feature-branch # 使用 theirs 策略(采用合并分支) git merge -X theirs feature-branch # 仅针对特定文件使用策略 git checkout --ours path/to/file git checkout --theirs path/to/file git add path/to/file
Rerere 功能可以自动记录并复用你之前解决冲突的方式,适合经常遇到相同冲突的场景。
# 启用 rerere git config --global rerere.enabled true # Git 会自动记录和重用冲突解决方案 # 查看 rerere 缓存 git rerere status git rerere diff # 清除 rerere 缓存 git rerere forget
当需要定位代码中的 Bug 或者追踪某个函数的演变时,Git 的高级搜索功能远胜于普通的文件搜索。
# 在所有文件中搜索 git grep "function getUserData" # 在特定提交中搜索 git grep "TODO"# 显示行号 git grep -n "import React" # 搜索整个单词 git grep -w "user" # 统计匹配数量 git grep -c "console.log" # 在特定类型文件中搜索 git grep "error" -- "*.js" # 正则表达式搜索 git grep -E "class \w+Controller"
通过二分查找,可以在几十个提交中快速找到引入 Bug 的那一个。配合自动化测试脚本,效率更高。
# 开始二分查找 git bisect start # 标记当前版本为坏版本 git bisect bad # 标记已知的好版本 git bisect good v1.2.0 # Git 会自动切换到中间提交,测试后标记 git bisect good # 或 git bisect bad # 自动化测试 git bisect run npm test # 结束 bisect git bisect reset
实战示例:
# 编写自动化测试脚本
cat > test.sh << 'EOF'
#!/bin/bash
npm test
if [ $? -eq 0 ]; then
exit 0 # 测试通过
else
exit 1 # 测试失败
fi
EOF
chmod +x test.sh
git bisect start HEAD v1.0.0
git bisect run ./test.sh
想知道每行代码是谁在什么时候修改的?git blame 可以给出答案。
# 查看文件每行的最后修改者 git blame# 显示邮箱和时间 git blame -e -t # 查看特定行范围 git blame -L 10,20 # 忽略空白更改 git blame -w # 查看删除的代码 git log -S "deleted_function" --source --all # 追踪函数的演变 git log -L :functionName:file.js
当需要在一个仓库中引用另一个外部仓库时,子模块(Submodule)和 Subtree 是两种常见的方式。
子模块允许你将一个外部仓库嵌入到当前仓库中,但保持独立的 Git 历史。
# 添加子模块 git submodule add https://github.com/user/repo.git libs/repo # 克隆包含子模块的仓库 git clone --recurse-submodules# 初始化并更新子模块 git submodule init git submodule update # 更新所有子模块到最新版本 git submodule update --remote # 在子模块中工作 cd libs/repo git checkout main git pull cd ../.. git add libs/repo git commit -m "Update submodule" # 删除子模块 git submodule deinit libs/repo git rm libs/repo rm -rf .git/modules/libs/repo
Subtree 则将外部仓库的内容直接合并到当前仓库中,历史会一起保留。
# 添加远程仓库 git remote add vendor-lib https://github.com/vendor/lib.git # 添加 subtree git subtree add --prefix=vendor/lib vendor-lib main --squash # 更新 subtree git subtree pull --prefix=vendor/lib vendor-lib main --squash # 推送更改回上游 git subtree push --prefix=vendor/lib vendor-lib main
Submodule vs Subtree 对比:
| 特性 | Submodule | Subtree |
|---|---|---|
| 历史记录 | 分离 | 合并 |
| 克隆复杂度 | 需要额外命令 | 简单 |
| 更新操作 | 复杂 | 相对简单 |
| 仓库大小 | 小 | 大 |
| 适用场景 | 清晰的依赖边界 | 需要修改外部代码 |
随着项目不断迭代,仓库体积可能变得非常庞大。定期维护和优化是必要的。
# 查看仓库大小 git count-objects -vH # 查找大文件 git rev-list --objects --all | \ git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \ sed -n 's/^blob //p' | \ sort --numeric-sort --key=2 | \ tail -n 10 # 使用 BFG Repo-Cleaner 删除大文件 ja va -jar bfg.jar --strip-blobs-bigger-than 50M# 或使用 git-filter-repo git filter-repo --strip-blobs-bigger-than 10M # 清理后的垃圾回收 git reflog expire --expire=now --all git gc --prune=now --aggressive
对于大型仓库,尤其是 CI/CD 环境中,浅克隆可以显著减少克隆时间。
# 只克隆最近 1 次提交 git clone --depth 1# 取消浅克隆限制 git fetch --unshallow # 只克隆特定分支 git clone --single-branch --branch main
如果仓库很大但你只需要其中一部分目录,稀疏检出可以让你只检出需要的文件。
# 初始化稀疏检出 git clone --no-checkoutcd git sparse-checkout init --cone # 指定要检出的目录 git sparse-checkout set src/components src/utils # 查看当前配置 git sparse-checkout list # 添加更多目录 git sparse-checkout add docs
高阶的 Git 操作最终要服务于团队协作。代码审查、冲突预防、发布管理都是日常工作中的关键环节。
# 创建审查分支 git checkout -b review/feature-x origin/feature-x # 查看更改摘要 git diff main...feature-x --stat # 逐文件审查 git diff main...feature-x -- path/to/file # 审查特定提交 git show# 批准并合并 git checkout main git merge --no-ff feature-x
养成推送前先 rebase 的习惯,可以大幅减少合并冲突。
# 在推送前先拉取并 rebase
git pull --rebase origin main
# 配置默认使用 rebase
git config --global pull.rebase true
# 使用 pre-push hook 强制检查
cat > .git/hooks/pre-push << 'EOF'
#!/bin/bash
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
git fetch origin $current_branch
if ! git diff origin/$current_branch --quiet; then
echo "Remote has changes. Please pull first."
exit 1
fi
EOF
chmod +x .git/hooks/pre-push
# 创建发布标签 git tag -a v1.2.0 -m "Release version 1.2.0" git push origin v1.2.0 # 生成 CHANGELOG git log v1.1.0..v1.2.0 --oneline > CHANGELOG.md # 使用 conventional-changelog 自动生成 npm install -g conventional-changelog-cli conventional-changelog -p angular -i CHANGELOG.md -s # 创建 GitHub Release gh release create v1.2.0 --generate-notes
Git Hooks 是 Git 的一项强大特性,可以在特定事件(如提交、推送)发生时自动执行脚本,从而保证代码质量和流程规范。
pre-commit - 提交前代码检查
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# 运行代码格式化
npm run format
# 运行 Linter
npm run lint
if [ $? -ne 0 ]; then
echo "Linting failed. Please fix errors before committing."
exit 1
fi
# 运行测试
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Please fix before committing."
exit 1
fi
EOF
chmod +x .git/hooks/pre-commit
commit-msg - 提交信息验证
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/bash
commit_msg=$(cat "$1")
# 验证 Conventional Commits 格式
if ! echo "$commit_msg" | grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+"; then
echo "Error: Commit message must follow Conventional Commits format"
echo "Example: feat(auth): add OAuth2 support"
exit 1
fi
EOF
chmod +x .git/hooks/commit-msg
pre-push - 推送前检查
cat > .git/hooks/pre-push << 'EOF'
#!/bin/bash
# 禁止直接推送到 main
protected_branch='main'
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
if [ "$current_branch" = "$protected_branch" ]; then
echo "Direct push to $protected_branch is not allowed!"
exit 1
fi
# 运行完整测试套件
npm run test:all
if [ $? -ne 0 ]; then
echo "Tests failed. Push aborted."
exit 1
fi
EOF
chmod +x .git/hooks/pre-push
pre-receive - 服务器端接收前检查
cat > hooks/pre-receive << 'EOF'
#!/bin/bash
while read oldrev newrev refname; do
# 禁止删除 main 分支
if [ "$refname" = "refs/heads/main" ] && [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
echo "Error: Deleting main branch is not allowed"
exit 1
fi
# 禁止强制推送
if [ "$oldrev" != "0000000000000000000000000000000000000000" ]; then
if ! git merge-base --is-ancestor "$oldrev" "$newrev"; then
echo "Error: Force push is not allowed"
exit 1
fi
fi
done
EOF
chmod +x hooks/pre-receive
在团队中,手动放置 hooks 脚本难以共享,使用 Husky 这类工具可以方便地在项目中统一管理 Git Hooks。
# 安装 Husky
npm install --sa ve-dev husky
# 初始化 Husky
npx husky init
# 添加 pre-commit hook
npx husky add .husky/pre-commit "npm test"
# 添加 commit-msg hook(配合 commitlint)
npm install --sa ve-dev @commitlint/{config-conventional,cli}
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
最后,通过几个真实场景来演示如何在工作中综合运用这些高级技巧。
问题: 生产环境发现严重 bug,需要立即修复并发布
# 1. 从生产标签创建 hotfix 分支 git checkout -b hotfix/critical-bug v1.2.0 # 2. 修复 bug 并提交 git add . git commit -m "fix: resolve critical null pointer exception" # 3. 创建新版本标签 git tag -a v1.2.1 -m "Hotfix release" # 4. 合并回 main 和 develop git checkout main git merge --no-ff hotfix/critical-bug git push origin main --tags git checkout develop git merge --no-ff hotfix/critical-bug git push origin develop # 5. 删除 hotfix 分支 git branch -d hotfix/critical-bug
问题: 不小心删除了重要代码,已经提交但未推送
# 1. 查看 reflog 找到删除前的提交 git reflog # 2. 恢复文件 git checkout-- path/to/file # 3. 如果已经推送,创建恢复提交 git revert git push origin main
问题: 不小心提交了包含密码的配置文件
# 1. 使用 git-filter-repo(推荐) pip install git-filter-repo git filter-repo --path config/secrets.yml --invert-paths # 2. 强制推送(警告:会重写历史) git push origin --force --all git push origin --force --tags # 3. 通知团队成员重新克隆仓库 # 或者使用 git pull --rebase 更新
问题: 功能分支开发时间长,与 main 分支差异大
# 1. 确保功能分支是最新的 git checkout feature/long-running git fetch origin git rebase origin/main # 2. 解决所有冲突 git mergetool git rebase --continue # 3. 运行完整测试 npm run test:all # 4. 使用 squash merge 保持历史清晰 git checkout main git merge --squash feature/long-running git commit -m "feat: implement complete user management system" # 5. 推送并删除功能分支 git push origin main git branch -d feature/long-running git push origin --delete feature/long-running
问题: 将多个独立仓库合并为一个 monorepo
# 1. 创建 monorepo 结构 mkdir monorepo && cd monorepo git init # 2. 添加第一个项目 git remote add project-a https://github.com/user/project-a.git git fetch project-a git merge -s ours --no-commit --allow-unrelated-histories project-a/main git read-tree --prefix=packages/project-a/ -u project-a/main git commit -m "Merge project-a into monorepo" # 3. 添加第二个项目 git remote add project-b https://github.com/user/project-b.git git fetch project-b git merge -s ours --no-commit --allow-unrelated-histories project-b/main git read-tree --prefix=packages/project-b/ -u project-b/main git commit -m "Merge project-b into monorepo" # 4. 清理远程引用 git remote remove project-a git remote remove project-b
问题: 只构建发生变化的包
# 1. 检测变化的文件
changed_packages=$(git diff --name-only HEAD~1 HEAD | grep "^packages/" | cut -d'/' -f2 | sort -u)
# 2. 只测试变化的包
for pkg in $changed_packages; do
cd packages/$pkg
npm test
cd ../..
done
# 3. 使用 Lerna 或 Nx 进行智能构建
npx lerna run test --since HEAD~1
npx nx affected:test --base=HEAD~1
问题: 针对不同环境维护不同配置
# 1. 使用分支策略 git checkout -b config/production # 修改生产配置 git commit -am "chore: production configuration" git checkout -b config/staging # 修改预发布配置 git commit -am "chore: staging configuration" # 2. 部署时合并配置 git checkout main git merge --no-commit --no-ff config/production # 部署到生产环境 # 3. 使用 git-crypt 加密敏感配置 git-crypt init echo "*.secret.yml filter=git-crypt diff=git-crypt" >> .gitattributes git add .gitattributes git commit -m "chore: enable git-crypt for secrets"
Git 的高阶功能远不止基本的 add、commit、push。掌握这些高级技巧可以:
最佳实践建议:
.gitignore 避免提交无关文件git reflog 恢复误操作--force 推送记住:Git 是一个强大的工具,但强大的功能也意味着需要谨慎使用。在团队环境中,始终遵循约定的工作流程和最佳实践。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8