Skip to content

Git 进阶操作

本章将介绍 Git 的高级功能和技巧,包括 rebase、stash、submodule、hooks 等进阶操作,帮助你更高效地使用 Git。

Git Rebase 详解

什么是 Rebase?

Rebase 是将一系列提交重新应用到另一个基础提交上的操作。它可以创建更清晰的项目历史。

Rebase 前:
A---B---C  (main)
     \
      D---E  (feature)

Rebase 后:
A---B---C---D'---E'  (feature)

基本 Rebase 操作

bash
# 将当前分支 rebase 到 main
git rebase main

# 将 feature 分支 rebase 到 main
git rebase main feature

# 交互式 rebase
git rebase -i HEAD~3

# 继续 rebase(解决冲突后)
git rebase --continue

# 跳过当前提交
git rebase --skip

# 中止 rebase
git rebase --abort

演示 Rebase 操作

bash
# 创建演示环境
mkdir git-rebase-demo
cd git-rebase-demo
git init

# 创建主分支的提交
echo "主分支文件1" > main1.txt
git add main1.txt
git commit -m "主分支提交1"

echo "主分支文件2" > main2.txt
git add main2.txt
git commit -m "主分支提交2"

# 创建功能分支
git checkout -b feature
echo "功能文件1" > feature1.txt
git add feature1.txt
git commit -m "功能提交1"

echo "功能文件2" > feature2.txt
git add feature2.txt
git commit -m "功能提交2"

# 在主分支添加新提交
git checkout main
echo "主分支文件3" > main3.txt
git add main3.txt
git commit -m "主分支提交3"

# 将功能分支 rebase 到最新的主分支
git checkout feature
git rebase main

# 查看历史
git log --oneline --graph

交互式 Rebase

交互式 rebase 允许你修改提交历史:

bash
# 启动交互式 rebase
git rebase -i HEAD~3

在编辑器中可以进行以下操作:

  • pick:保留提交
  • reword:修改提交信息
  • edit:修改提交内容
  • squash:合并到前一个提交
  • fixup:合并到前一个提交但丢弃提交信息
  • drop:删除提交
bash
# 示例:压缩最近3个提交
git rebase -i HEAD~3

# 在编辑器中修改:
pick abc1234 第一个提交
squash def5678 第二个提交
squash ghi9012 第三个提交

Rebase vs Merge

bash
# Merge 保留分支历史
git checkout main
git merge feature

# Rebase 创建线性历史
git checkout feature
git rebase main
git checkout main
git merge feature  # 这将是快进合并

Git Stash 详解

什么是 Stash?

Stash 允许你临时保存工作区和暂存区的更改,以便快速切换分支或拉取更新。

基本 Stash 操作

bash
# 保存当前更改
git stash

# 保存时添加描述
git stash push -m "临时保存登录功能开发"

# 查看 stash 列表
git stash list

# 应用最新的 stash
git stash pop

# 应用特定的 stash
git stash apply stash@{1}

# 删除 stash
git stash drop stash@{0}

# 清空所有 stash
git stash clear

演示 Stash 操作

bash
# 创建一些修改
echo "正在开发的功能" > work_in_progress.txt
echo "修改现有文件" >> existing_file.txt

# 添加到暂存区
git add work_in_progress.txt

# 保存到 stash
git stash push -m "保存正在开发的功能"

# 查看 stash
git stash list

# 切换分支处理紧急问题
git checkout -b hotfix
echo "紧急修复" > hotfix.txt
git add hotfix.txt
git commit -m "紧急修复"

# 回到原分支恢复工作
git checkout main
git stash pop

# 继续开发
git add .
git commit -m "完成功能开发"

高级 Stash 操作

bash
# 只 stash 工作区更改,不包括暂存区
git stash --keep-index

# 包括未跟踪的文件
git stash -u

# 包括被忽略的文件
git stash -a

# 交互式 stash
git stash -p

# 从 stash 创建分支
git stash branch new-feature stash@{0}

# 查看 stash 的内容
git stash show stash@{0}
git stash show -p stash@{0}  # 显示详细差异

Git Submodule

什么是 Submodule?

Submodule 允许你将一个 Git 仓库作为另一个 Git 仓库的子目录。

添加 Submodule

bash
# 添加 submodule
git submodule add https://github.com/user/repo.git path/to/submodule

# 添加特定分支的 submodule
git submodule add -b branch-name https://github.com/user/repo.git path/to/submodule

# 提交 submodule 配置
git add .gitmodules path/to/submodule
git commit -m "添加 submodule"

克隆包含 Submodule 的仓库

bash
# 克隆主仓库
git clone https://github.com/user/main-repo.git

# 初始化并更新 submodule
git submodule init
git submodule update

# 或者一次性完成
git clone --recursive https://github.com/user/main-repo.git

# 或者在克隆后
git submodule update --init --recursive

更新 Submodule

bash
# 进入 submodule 目录更新
cd path/to/submodule
git pull origin main

# 在主仓库中更新 submodule
git submodule update --remote

# 更新特定的 submodule
git submodule update --remote path/to/submodule

# 提交 submodule 更新
git add path/to/submodule
git commit -m "更新 submodule"

演示 Submodule 操作

bash
# 创建主项目
mkdir main-project
cd main-project
git init

# 创建一个子项目(模拟外部库)
mkdir ../library
cd ../library
git init
echo "库函数" > lib.js
git add lib.js
git commit -m "初始库提交"

# 回到主项目,添加 submodule
cd ../main-project
git submodule add ../library lib

# 查看状态
git status
cat .gitmodules

# 提交 submodule 配置
git add .
git commit -m "添加库作为 submodule"

# 在 submodule 中进行更改
cd lib
echo "新功能" >> lib.js
git add lib.js
git commit -m "添加新功能"

# 在主项目中更新 submodule 引用
cd ..
git add lib
git commit -m "更新库到最新版本"

Git Hooks

什么是 Hooks?

Hooks 是在特定 Git 事件发生时自动执行的脚本。

常用的 Hooks

bash
# 客户端 hooks(在 .git/hooks/ 目录)
pre-commit      # 提交前执行
prepare-commit-msg  # 准备提交信息时执行
commit-msg      # 提交信息验证
post-commit     # 提交后执行
pre-push        # 推送前执行

# 服务端 hooks
pre-receive     # 接收推送前执行
update          # 更新引用时执行
post-receive    # 接收推送后执行

创建 Pre-commit Hook

bash
# 创建 pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash

# 检查代码风格
echo "检查代码风格..."

# 检查 JavaScript 文件
js_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$')
if [ -n "$js_files" ]; then
    echo "检查 JavaScript 文件: $js_files"
    # 这里可以添加 ESLint 检查
    # eslint $js_files
fi

# 检查 Python 文件
py_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')
if [ -n "$py_files" ]; then
    echo "检查 Python 文件: $py_files"
    # 这里可以添加 flake8 检查
    # flake8 $py_files
fi

# 检查提交中是否包含调试代码
if git diff --cached | grep -E "(console\.log|debugger|pdb\.set_trace)"; then
    echo "错误: 提交中包含调试代码"
    exit 1
fi

echo "代码检查通过"
exit 0
EOF

# 使脚本可执行
chmod +x .git/hooks/pre-commit

创建 Commit-msg Hook

bash
# 创建 commit-msg hook 验证提交信息格式
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/bash

commit_regex='^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}'

if ! grep -qE "$commit_regex" "$1"; then
    echo "错误的提交信息格式!"
    echo "格式应该是: type(scope): description"
    echo "类型: feat, fix, docs, style, refactor, test, chore"
    echo "示例: feat(auth): 添加用户登录功能"
    exit 1
fi
EOF

chmod +x .git/hooks/commit-msg

演示 Hooks 使用

bash
# 测试 pre-commit hook
echo "console.log('debug');" > debug.js
git add debug.js
git commit -m "添加调试代码"  # 应该被阻止

# 移除调试代码
echo "function hello() { return 'Hello World'; }" > debug.js
git add debug.js
git commit -m "feat: 添加 hello 函数"  # 应该成功

# 测试错误的提交信息格式
git commit --allow-empty -m "错误格式"  # 应该被阻止

# 正确的提交信息格式
git commit --allow-empty -m "docs: 更新 README"  # 应该成功

Git Worktree

什么是 Worktree?

Worktree 允许你同时检出同一个仓库的多个分支到不同的目录。

Worktree 操作

bash
# 创建新的 worktree
git worktree add ../feature-branch feature-branch

# 创建新分支的 worktree
git worktree add -b new-feature ../new-feature

# 列出所有 worktree
git worktree list

# 删除 worktree
git worktree remove ../feature-branch

# 清理无效的 worktree
git worktree prune

演示 Worktree 使用

bash
# 在主目录工作
pwd  # /path/to/main-project

# 创建功能分支的 worktree
git worktree add ../feature-work feature-branch

# 现在可以同时在两个目录工作
cd ../feature-work
# 在这里开发功能分支

# 回到主目录
cd ../main-project
# 在这里处理主分支的工作

# 查看所有 worktree
git worktree list

Git Bisect

什么是 Bisect?

Bisect 使用二分查找法帮助你找到引入 bug 的提交。

Bisect 操作

bash
# 开始 bisect
git bisect start

# 标记当前提交为坏的
git bisect bad

# 标记已知好的提交
git bisect good commit_hash

# Git 会自动切换到中间的提交
# 测试后标记结果
git bisect good  # 或 git bisect bad

# 继续直到找到问题提交
# 结束 bisect
git bisect reset

自动化 Bisect

bash
# 使用脚本自动化测试
git bisect start HEAD v1.0
git bisect run ./test_script.sh

# test_script.sh 示例
#!/bin/bash
make test
exit $?  # 返回 0 表示好,非 0 表示坏

Git Filter-branch 和 Filter-repo

重写历史

bash
# 从历史中移除文件(使用 filter-branch)
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 filter-repo --mailmap mailmap.txt

# mailmap.txt 格式:
# New Name <new@email.com> Old Name <old@email.com>

高级配置和别名

有用的 Git 别名

bash
# 设置高级别名
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

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 '!gitk'

# 查找别名
git config --global alias.find '!git ls-files | xargs grep -l'

# 统计别名
git config --global alias.count 'shortlog -sn'

# 清理别名
git config --global alias.cleanup '!git branch --merged | grep -v "\\*\\|main\\|master" | xargs -n 1 git branch -d'

高级配置

bash
# 设置默认编辑器
git config --global core.editor "code --wait"

# 设置差异工具
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'

# 设置合并工具
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# 自动纠正拼写错误
git config --global help.autocorrect 1

# 设置推送策略
git config --global push.default simple

# 启用 rerere(重用记录的冲突解决)
git config --global rerere.enabled true

# 设置长路径支持(Windows)
git config --global core.longpaths true

性能优化

大型仓库优化

bash
# 浅克隆
git clone --depth 1 <url>

# 部分克隆
git clone --filter=blob:none <url>

# 稀疏检出
git config core.sparseCheckout true
echo "src/" > .git/info/sparse-checkout
git read-tree -m -u HEAD

# 垃圾回收
git gc --aggressive --prune=now

# 重新打包
git repack -ad

# 清理未跟踪的文件
git clean -fd

网络优化

bash
# 设置代理
git config --global http.proxy http://proxy.company.com:8080

# 增加缓冲区大小
git config --global http.postBuffer 524288000

# 启用并行传输
git config --global http.maxRequestBuffer 100M
git config --global http.threads 5

故障排除和调试

Git 调试技巧

bash
# 启用详细输出
GIT_TRACE=1 git status
GIT_TRACE_PACKET=1 git push
GIT_TRACE_PERFORMANCE=1 git log

# 检查仓库完整性
git fsck --full

# 查看对象信息
git cat-file -t commit_hash  # 查看对象类型
git cat-file -p commit_hash  # 查看对象内容

# 查看引用
git show-ref

# 查看配置
git config --list --show-origin

常见问题解决

bash
# 问题1: 推送被拒绝
git pull --rebase origin main
git push origin main

# 问题2: 合并冲突
git status
git mergetool
git commit

# 问题3: 分离头指针状态
git checkout -b new-branch
git checkout main
git merge new-branch

# 问题4: 文件权限问题
git config core.filemode false

# 问题5: 换行符问题
git config core.autocrlf true  # Windows
git config core.autocrlf input # macOS/Linux

总结

Git 进阶操作的核心要点:

重要工具

bash
rebase    # 重写历史,创建线性提交
stash     # 临时保存更改
submodule # 管理子项目
hooks     # 自动化工作流
worktree  # 多分支并行工作
bisect    # 二分查找问题

最佳实践

  • ✅ 使用 rebase 保持清晰的历史
  • ✅ 利用 stash 灵活切换工作
  • ✅ 通过 hooks 自动化检查
  • ✅ 合理使用 submodule 管理依赖
  • ✅ 配置有用的别名提高效率

性能优化

  • 🚀 浅克隆大型仓库
  • 🚀 使用稀疏检出
  • 🚀 定期垃圾回收
  • 🚀 优化网络配置

掌握这些进阶操作后,你就能够:

  • 🔧 高效管理复杂的项目历史
  • ⚡ 自动化常见的工作流程
  • 🎯 快速定位和解决问题
  • 📈 优化 Git 的使用性能

在下一章中,我们将学习 Git 的最佳实践。

本站内容仅供学习和研究使用。