在软件开发领域,版本控制系统经历了从本地版本控制(如 RCS)到集中式版本控制(如 SVN),再到分布式版本控制(如 Git)的演进过程。2005 年,Linux 内核开发社区因商业版本控制系统 BitKeeper 停止免费使用,促使 Linus Torvalds 开发了 Git。Git 的设计目标非常明确:快速、简单、完全分布式、高效处理大型项目(如 Linux 内核)、具备强大的分支管理能力。
Git 与其他版本控制系统的主要区别在于其对待数据的方式。大多数系统将信息存储为基于文件的变更列表,而 Git 将其数据视为一系列文件系统的快照。每次提交时,Git 会记录当前所有文件的快照并存储该快照的引用。这种设计使得 Git 在分支切换、历史查看等操作上极其高效。
Git 仓库本质上是一个有向无环图(DAG)数据结构,其中每个节点代表一次提交,边代表父子关系。仓库包含以下关键组成部分:
本地仓库与远程仓库的关系不是主从关系,而是对等的。每个开发者都拥有完整的仓库历史,这使得 Git 在网络不可用时仍能正常工作,这是分布式版本控制的核心优势。
每次提交包含以下元数据:
Git 使用 SHA-1 哈希算法(正在向 SHA-256 过渡)为每个对象生成唯一的 40 字符校验和。这种内容寻址的文件系统确保了数据的完整性 - 任何微小的变更都会导致哈希值完全不同。
Git 的分支本质上只是指向提交对象的可变指针。创建新分支时,Git 只是在 .git/refs/heads 目录下创建一个新文件,文件内容是该分支最新提交的哈希值。这种轻量级设计使得 Git 分支的创建和切换几乎瞬间完成。
与许多人的直觉相反,Git 的默认分支名原是 master,现在社区逐渐转向使用 main。这可以通过以下命令修改默认分支名称:
bash复制git config --global init.defaultBranch main
Git 支持三种基本合并策略:
合并冲突发生时,Git 会在冲突文件中插入标记(<<<<<<<,=======,>>>>>>>),开发者需要手动解决这些冲突。理解合并基础(merge base)的概念对解决复杂冲突至关重要。
远程仓库通过远程引用(remote refs)实现,存储在 .git/refs/remotes 目录下。常见的远程操作命令背后实际执行的是 Git 的传输协议(本地协议、HTTP 协议、SSH 协议或 Git 协议)。
Git 使用"远程跟踪分支"(remote-tracking branches)来记录远程仓库分支的状态。这些分支(如 origin/main)是本地分支,但 Git 会自动移动它们以反映远程仓库的状态。理解这一点对掌握 git fetch 和 git pull 的区别至关重要。
Windows 版 Git 自带了一个精简的 MSYS2 环境,安装时有几个关键选项需要注意:
安装后可以启用性能优化:
bash复制# 启用文件系统缓存
git config --global core.fscache true
# 启用内置文件系统监视器
git config --global core.untrackedCache true
除了 Homebrew,macOS 用户还可以通过 MacPorts 安装:
bash复制sudo port install git
推荐配置 macOS 的密钥链集成:
bash复制git config --global credential.helper osxkeychain
对于大型仓库,可以启用文件系统监视器提升性能:
bash复制git config --global core.trustctime false
git config --global core.preloadIndex true
对于 Linux 服务器环境,建议编译安装最新版 Git 以获得最佳性能:
bash复制# 安装依赖
sudo apt-get install dh-autoreconf libcurl4-gnutls-dev libexpat1-dev \
gettext libz-dev libssl-dev
# 编译安装
wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.42.0.tar.gz
tar -zxf git-2.42.0.tar.gz
cd git-2.42.0
make prefix=/usr all doc info
sudo make prefix=/usr install install-doc install-html install-info
针对大型仓库的优化配置:
bash复制git config --global pack.threads 0 # 自动检测最佳线程数
git config --global pack.deltaCacheSize 1g
git config --global pack.windowMemory 1g
全局配置适用于所有仓库,但可以为特定仓库设置不同的用户信息:
bash复制# 在仓库目录内执行
git config user.name "Work Account"
git config user.email "work@example.com"
可以通过条件配置实现自动切换:
bash复制# ~/.gitconfig
[includeIf "gitdir:~/work/"]
path = .gitconfig-work
[includeIf "gitdir:~/personal/"]
path = .gitconfig-personal
配置 VS Code 作为默认编辑器:
bash复制git config --global core.editor "code --wait --new-window"
配置图形化差异和合并工具(以 VS Code 为例):
bash复制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"
针对混合开发环境的最佳实践:
bash复制# Windows 开发者
git config --global core.autocrlf true
# macOS/Linux 开发者
git config --global core.autocrlf input
# 统一仓库中的换行符
echo "* text=auto" > .gitattributes
git add .gitattributes
git commit -m "Set text=auto in .gitattributes"
创建高效工作流别名:
bash复制git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.graph "log --all --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
高级别名示例(显示被忽略的文件):
bash复制git config --global alias.ignored '!git ls-files -v | grep "^[[:lower:]]"'
执行 git init 时,Git 会创建以下目录结构:
code复制.git/
├── HEAD # 当前分支引用
├── config # 仓库特定配置
├── description # 仓库描述
├── hooks/ # 客户端/服务端钩子脚本
├── info/ # 全局排除模式
├── objects/ # 所有数据对象
│ ├── info/
│ └── pack/
└── refs/ # 分支和标签引用
├── heads/
└── tags/
可以通过 --bare 参数创建裸仓库(没有工作目录):
bash复制git init --bare my-project.git
深度克隆(只获取最近历史):
bash复制git clone --depth 1 https://github.com/user/repo.git
克隆特定分支:
bash复制git clone -b develop --single-branch https://github.com/user/repo.git
克隆时配置稀疏检出(只检出部分文件):
bash复制git clone --filter=blob:none --no-checkout https://github.com/user/repo.git
cd repo
git sparse-checkout init --cone
git sparse-checkout set src/docs
git checkout main
Git 文件有四种主要状态:
使用 git status -vv 可以查看更详细的状态信息,包括未暂存和已暂存的差异。
交互式暂存:
bash复制git add -i
补丁模式暂存(选择部分更改):
bash复制git add -p
只暂存已跟踪文件的更改:
bash复制git add -u
项目级 .gitignore:
bash复制# 忽略所有.class文件
*.class
# 但不忽略重要的Config.class
!Config.class
# 忽略特定目录
/target/
全局忽略配置(~/.gitignore_global):
bash复制git config --global core.excludesfile ~/.gitignore_global
仓库特定忽略(.git/info/exclude) - 不会提交到版本控制
图形化历史:
bash复制git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
按内容搜索:
bash复制git log -S'function_name' # 查找添加/删除特定字符串的提交
git log -G'regex' # 使用正则表达式搜索
按路径过滤:
bash复制git log --follow -- path/to/file # 包含重命名历史
比较两次提交:
bash复制git diff commit1..commit2 # 两点语法
git diff commit1...commit2 # 三点语法(共同祖先对比)
统计变更:
bash复制git diff --stat HEAD~3 HEAD
Git Flow vs GitHub Flow:
重命名分支:
bash复制git branch -m old-name new-name
git push origin --delete old-name
git push origin new-name
设置上游分支:
bash复制git branch -u origin/main
删除已合并的分支:
bash复制git branch --merged | egrep -v "(^\*|main|develop)" | xargs git branch -d
查看远程详情:
bash复制git remote show origin
添加多个远程仓库:
bash复制git remote add upstream https://github.com/original/repo.git
修改远程 URL:
bash复制git remote set-url origin git@github.com:user/repo.git
强制推送的安全替代方案:
bash复制# 不推荐
git push -f
# 推荐
git push --force-with-lease
拉取时变基(避免不必要的合并提交):
bash复制git pull --rebase
# 或设置为默认行为
git config --global pull.rebase true
创建带注释的标签:
bash复制git tag -a v1.4 -m "my version 1.4"
推送标签到远程:
bash复制git push origin v1.4
# 或推送所有标签
git push origin --tags
删除远程标签:
bash复制git tag -d v1.4
git push origin :refs/tags/v1.4
--soft:只移动 HEAD 指针,不修改索引和工作目录
bash复制git reset --soft HEAD~1
适用场景:修改上次提交信息或组合多个提交
--mixed(默认):移动 HEAD 并重置索引,但不修改工作目录
bash复制git reset HEAD~1
适用场景:取消暂存某些文件
--hard:移动 HEAD 并重置索引和工作目录
bash复制git reset --hard HEAD~1
适用场景:完全放弃最近的更改(慎用)
创建新的提交来撤销之前的更改:
bash复制git revert HEAD
还原合并提交:
bash复制git revert -m 1 <merge-commit-hash>
恢复工作目录文件:
bash复制git restore file.txt
恢复暂存区文件:
bash复制git restore --staged file.txt
配置 diff3 风格显示冲突:
bash复制git config --global merge.conflictstyle diff3
这会显示冲突文件的三个版本:
code复制<<<<<<< ours
本地更改
||||||| base
原始内容
=======
远程更改
>>>>>>> theirs
使用 ours/theirs 策略:
bash复制# 保留我们的版本
git merge -Xours branch-to-merge
# 保留他们的版本
git merge -Xtheirs branch-to-merge
中止合并:
bash复制git merge --abort
重置失败的合并:
bash复制git reset --hard HEAD
压缩多个提交:
bash复制git rebase -i HEAD~3
# 将 pick 改为 squash 或 fixup
修改历史提交信息:
bash复制git rebase -i HEAD~3
# 将 pick 改为 edit
git commit --amend
git rebase --continue
从历史中移除大文件:
bash复制git filter-branch --tree-filter 'rm -f large-file.zip' HEAD
重写所有提交中的邮箱地址:
bash复制git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "old@example.com" ];
then
GIT_AUTHOR_EMAIL="new@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
BFG Repo-Cleaner 是比 filter-branch 更快的替代方案:
bash复制# 移除特定文件
java -jar bfg.jar --delete-files large-file.zip my-repo.git
# 移除所有大于50M的文件
java -jar bfg.jar --strip-blobs-bigger-than 50M my-repo.git
添加子模块:
bash复制git submodule add https://github.com/user/lib.git lib
克隆包含子模块的项目:
bash复制git clone --recurse-submodules https://github.com/user/project.git
或后续初始化子模块:
bash复制git submodule init
git submodule update
添加新工作树:
bash复制git worktree add ../feature-branch feature-branch
列出所有工作树:
bash复制git worktree list
移除工作树:
bash复制git worktree remove ../feature-branch
创建特性分支:
bash复制git checkout -b feature-x
开发过程中频繁提交:
bash复制git add .
git commit -m "实现功能X的基础结构"
定期变基保持历史整洁:
bash复制git fetch origin
git rebase origin/main
完成功能后整理提交历史:
bash复制git rebase -i HEAD~5
推送到远程:
bash复制git push -u origin feature-x
使用 git stash 暂存当前工作:
bash复制git stash push -m "正在开发的功能X"
git checkout -b hotfix-y
# 修复问题后
git checkout feature-x
git stash pop
列出所有暂存项:
bash复制git stash list
应用特定暂存:
bash复制git stash apply stash@{1}
创建特性分支:
bash复制git checkout -b feature-a
开发并推送:
bash复制git push -u origin feature-a
在 GitHub/GitLab 创建 Pull Request/Merge Request
团队成员审查后讨论修改
根据反馈进行修改:
bash复制git commit --amend # 修改上次提交
git push -f # 强制推送更新
通过 CI/CD 流水线后合并
配置分支保护规则:
使用 CODEOWNERS 文件指定审查者:
code复制# 在 .github/CODEOWNERS 或根目录
src/core/ @team-core @lead-dev
docs/ @tech-writer
常用命名约定:
配置客户端钩子验证分支名:
bash复制# .git/hooks/pre-commit
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [[ ! "$BRANCH" =~ ^(feature|fix|release|hotfix)/ ]]; then
echo "分支名必须以 feature/, fix/, release/ 或 hotfix/ 开头"
exit 1
fi
配置提交模板:
bash复制git config --global commit.template ~/.gitmessage.txt
模板示例:
code复制# 类型: feat|fix|docs|style|refactor|test|chore
# 模块: (可选)
# --------------------
# 主题行 (50字符以内)
# 详细说明 (72字符换行)
# - 为什么需要这个变更
# - 解决了什么问题
# - 有哪些需要注意的地方
# 相关问题: closes #123, refs #456
Git 钩子与 CI 集成示例:
bash复制# .git/hooks/pre-push
#!/bin/sh
# 运行测试
npm test
if [ $? -ne 0 ]; then
echo "测试失败,请先修复"
exit 1
fi
GitLab CI 示例:
yaml复制stages:
- test
- deploy
test:
stage: test
script:
- npm install
- npm test
deploy:
stage: deploy
script:
- npm run build
- scp -r dist/ user@server:/path
only:
- main
查找历史中的大文件:
bash复制git rev-list --objects --all |
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
awk '/^blob/ {print substr($0,6)}' |
sort --numeric-sort --key=2 |
cut -c 1-12,41- |
$(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
使用 git filter-repo(推荐):
bash复制git filter-repo --strip-blobs-bigger-than 10M
清理 reflog 和垃圾:
bash复制git reflog expire --expire=now --all
git gc --prune=now --aggressive
仅克隆最近历史:
bash复制git clone --depth 1 https://github.com/user/repo.git
按需获取对象:
bash复制git clone --filter=blob:none https://github.com/user/repo.git
更新所有子模块:
bash复制git submodule update --init --recursive
批量操作子模块:
bash复制git submodule foreach 'git checkout main && git pull'
Google 的 repo 工具管理多个 Git 仓库:
bash复制repo init -u https://android.googlesource.com/platform/manifest
repo sync
repo start feature-x --all
启用文件系统监视器(Windows):
bash复制git config --global core.fscache true
git config --global core.untrackedCache true
MacOS 优化:
bash复制git config --global core.trustctime false
git config --global core.preloadindex true
使用 SSH 压缩:
bash复制git config --global core.compression 9
git config --global core.loosecompression 9
git config --global core.deltaBaseCacheLimit 2g
配置包缓存:
bash复制git config --global pack.windowMemory 1g
git config --global pack.packSizeLimit 1g
git config --global pack.threads 0
配置 GPG 签名:
bash复制git config --global user.signingkey <gpg-key-id>
git config --global commit.gpgsign true
验证签名:
bash复制git log --show-signature
服务器端钩子示例(pre-receive):
bash复制#!/bin/sh
# 禁止直接推送到main分支
while read oldrev newrev refname; do
if [ "$refname" = "refs/heads/main" ]; then
echo "错误:不能直接推送到main分支,请使用Pull Request"
exit 1
fi
done
使用不同的 SSH 密钥:
bash复制# ~/.ssh/config
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
对应的仓库 URL:
bash复制git remote set-url origin git@github-work:company/repo.git
安全存储凭证:
bash复制git config --global credential.helper store
# 或使用缓存
git config --global credential.helper 'cache --timeout=3600'
Windows 凭据管理器:
bash复制git config --global credential.helper wincred
| 工具 | 平台 | 主要特点 | 适合场景 |
|---|---|---|---|
| GitHub Desktop | Win/Mac | 简洁易用,深度GitHub集成 | GitHub用户,初学者 |
| GitKraken | Win/Mac/Linux | 强大可视化,Git Flow支持 | 团队协作,复杂项目 |
| SourceTree | Win/Mac | 专业功能,免费 | 中级用户,企业环境 |
| Tower | Win/Mac | 精致UI,强大功能 | 专业开发者 |
| GitAhead | Win/Mac/Linux | 开源,性能优秀 | 技术爱好者 |
增强型 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"
交互式差异查看器(delta):
bash复制# 安装
brew install git-delta
# 配置
git config --global core.pager "delta --dark"
yaml复制name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm install
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- run: npm install
- run: npm run build
- uses: actions/upload-artifact@v3
with:
name: dist
path: dist
使用多项目流水线:
yaml复制trigger_job:
trigger:
project: group/project
strategy: depend
父子流水线:
yaml复制stages:
- test
- deploy
test:
stage: test
script:
- echo "Running tests"
rules:
- changes:
- "src/**/*"
deploy:
stage: deploy
trigger:
include: deploy/.gitlab-ci.yml
rules:
- if: $CI_COMMIT_BRANCH == "main"
安装 Gerrit 代码审查工具:
bash复制docker run -ti -p 8080:8080 -p 29418:29418 gerritcodereview/gerrit
推送变更审查:
bash复制git push origin HEAD:refs/for/main
安装 Arcanist(Phabricator CLI):
bash复制curl -L https://secure.phabricator.com/arcanist/bin/arc/stable/arc -o /usr/local/bin/arc
chmod +x /usr/local/bin/arc
提交差异审查:
bash复制arc diff
查看对象内容:
bash复制# blob对象
git cat-file -p <hash>
# tree对象
git ls-tree <hash>
# commit对象
git cat-file -p <commit-hash>
手动创建 Git 对象:
bash复制echo "test content" | git hash-object -w --stdin
查看引用:
bash复制git show-ref
检查打包文件:
bash复制git verify-pack -v .git/objects/pack/pack-*.idx
安装 Git LFS:
bash复制git lfs install
跟踪大文件:
bash复制git lfs track "*.psd"
git add .gitattributes
安装 Scalar:
bash复制git config --global protocol.version 2
scalar clone https://github.com/user/repo.git
Mercurial (hg) 与 Git 的比较:
Fossil:
官方文档:
在线课程:
贡献 Git:
bash复制git clone https://github.com/git/git
参加 Git Merge 会议
加入 Git 邮件列表
查看 Git 操作耗时:
bash复制GIT_TRACE_PERFORMANCE=1 git status
分析索引状态:
bash复制git update-index --test-untracked-cache
检查仓库完整性:
bash复制git fsck
查看对象统计:
bash复制git count-objects -v
分割大仓库:
bash复制git rev-list --all --objects | \
awk '{print $1}' | \
git cat-file --batch-check | \
grep blob | \
sort -k3nr | \
head -n 10
使用稀疏检出:
bash复制git sparse-checkout init --cone
git sparse-checkout set src/core
使用 SSH 压缩:
bash复制git config --global core.compression 9
git config --global core.loosecompression 9
配置包缓存:
bash复制git config --global pack.windowMemory 1g
git config --global pack.packSizeLimit 1g
运行垃圾回收:
bash复制git gc --auto
git prune
重新打包对象:
bash复制git repack -ad --depth=250 --window=250
设置仓库健康检查:
bash复制git maintenance run --task=gc
git maintenance run --task=commit-graph
自动化维护:
bash复制git maintenance start