Git从入门到精通:常用命令、工作区原理、分支管理、版本控制模型全解析
Git从入门到精通:常用命令、工作区原理、分支管理、版本控制模型全解析
前言:为什么每个程序员都必须懂Git?
想象一下这个场景:你正在写一个项目,今天写了很多代码,明天又想改回昨天某个功能,但你发现Ctrl+Z已经不够用了。更糟糕的是,你和团队成员同时修改同一个文件,一不小心就把别人的代码覆盖了。这时候,Git就像一台“代码时光机”,帮你记录每一次修改,随时回到过去,还能让多人协作井井有条。
据统计,全球超过90%的软件开发团队使用Git进行版本控制。无论是GitHub、GitLab还是Gitee,底层都是Git。本文将用最通俗的语言、最直观的Demo,带你从零开始掌握Git的核心概念和常用操作。
一、Git的核心概念:什么是版本控制模型?
1.1 版本控制的本质
版本控制就是记录文件随时间变化的历史,并允许你回溯到任意版本。就像玩游戏时的存档点,你可以随时读取任何一个存档。
举个生活例子:写论文时,你可能会这样操作:
论文初稿.docx论文修改1.docx论文最终版.docx论文最终版_导师意见修改.docx
这不就是最原始的版本控制吗?但问题是:
- 文件太多容易混乱
- 不知道每个版本改了啥
- 多人协作时无法合并修改
Git用**快照(Snapshot)**而非差异(Difference)来解决这些问题。
1.2 Git的版本控制模型
Git采用分布式版本控制模型,与传统的集中式(如SVN)有本质区别:
| 特性 | 集中式(SVN) | 分布式(Git) |
|---|---|---|
| 中央服务器 | 必须存在 | 可选 |
| 提交历史 | 存储在服务器 | 每个本地都有完整历史 |
| 网络要求 | 几乎所有操作需联网 | 大部分操作离线可用 |
| 备份风险 | 服务器挂了就完了 | 每个克隆都是完整备份 |
分布式模型的核心优势:每个人本地都是一个完整的仓库,包含所有历史。你可以在没有网络的情况下提交、查看历史、创建分支,等有网时再同步到远程。
1.3 Git的三个主要区域(重点!)
这是Git最基础也最重要的概念,理解了你就算掌握了一半:
工作区 (Working Directory) 暂存区 (Staging Area/Index) 本地仓库 (Local Repository)
│ │ │
│ git add │ git commit │
├─────────────────────────────>├─────────────────────────────>│
│ │ │
│ git checkout -- <file> │ git reset HEAD <file> │
│<─────────────────────────────┤<─────────────────────────────┤
用“快递发货”比喻:
- 工作区:你的工厂正在生产商品(修改文件)
- 暂存区:仓库里的打包区,准备发货的货物(
git add把修改标记为待提交) - 本地仓库:已经发货上路的货物(
git commit把暂存区内容永久保存)
用代码演示:
# 第一步:在工作区新建一个文件
echo "Hello Git" > hello.txt
# 第二步:查看状态 - 文件在工作区,未被跟踪
git status
# 输出:Untracked files: hello.txt
# 第三步:添加到暂存区(打包)
git add hello.txt
# 第四步:再次查看 - 文件在暂存区
git status
# 输出:Changes to be committed: new file: hello.txt
# 第五步:提交到本地仓库(发货)
git commit -m "添加hello.txt文件"
# 第六步:最终状态 - 工作区干净,所有修改已提交
git status
# 输出:nothing to commit, working tree clean
二、Git常用命令大全(带详细Demo)
2.1 配置与初始化
git config - 配置用户信息
# 设置全局用户名和邮箱(重要:每次提交都会记录)
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
# 查看所有配置
git config --list
# 设置别名(偷懒必备)
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status
git config --global alias.ci commit
# 之后可以用 git st 代替 git status
git init - 初始化仓库
# 创建一个新项目目录
mkdir my-project
cd my-project
# 初始化Git仓库(会生成隐藏的.git文件夹)
git init
# 查看.git目录(不要手动修改里面的文件)
ls -a .git
# 输出:branches config description HEAD hooks info objects refs
Demo:从零创建并第一次提交
# 创建项目文件夹
mkdir git-tutorial && cd git-tutorial
git init
# 创建README文件
echo "# My First Git Project" > README.md
# 添加到暂存区并提交
git add README.md
git commit -m "Initial commit"
# 查看提交历史
git log
# 输出:commit xxxxx (HEAD -> master) Author: ... Date: ... Initial commit
2.2 基本快照操作
git add - 添加到暂存区
# 添加单个文件
git add index.html
# 添加多个文件
git add file1.txt file2.txt
# 添加所有变更(注意:不包括未跟踪的文件?其实包括)
git add . # 添加所有文件和修改(包括新文件)
git add -A # 同上,更明确的写法
git add --all # 同上
# 添加某个目录下所有文件
git add src/
# 交互式添加(只添加文件的部分修改)
git add -p
Demo:增删改三种状态的add
# 创建三个文件
touch a.txt b.txt c.txt
git add a.txt b.txt # 只添加两个
# 修改a.txt并删除b.txt
echo "content" >> a.txt
rm b.txt
touch d.txt # 新文件
git status
# 输出:
# Changes to be committed: a.txt, b.txt
# Changes not staged: a.txt (modified), b.txt (deleted)
# Untracked files: d.txt
# 添加所有变更到暂存区
git add -A
git status
# 现在所有变化都在暂存区
git commit - 提交到仓库
# 普通提交
git commit -m "提交信息"
# 跳过暂存区直接提交(只提交已跟踪文件的修改)
git commit -a -m "直接提交所有修改"
# 修改上一次提交(如果写错提交信息或漏了文件)
git commit --amend -m "修正的提交信息"
# 添加漏掉的文件到上一次提交
git add forgotten.txt
git commit --amend --no-edit # 不修改提交信息
Demo:模拟正常开发流程
# 1. 新建功能文件
echo "function hello() { return 'world'; }" > app.js
# 2. 添加到暂存区
git add app.js
# 3. 提交
git commit -m "添加hello函数"
# 4. 修改文件
echo "function goodbye() { return 'bye'; }" >> app.js
# 5. 跳过add直接提交(必须加-a)
git commit -a -m "添加goodbye函数"
# 6. 查看提交历史
git log --oneline
# 输出:
# a1b2c3d (HEAD -> master) 添加goodbye函数
# e4f5g6h 添加hello函数
git status - 查看状态
git status
# 详细输出示例:
# On branch master
# Your branch is up to date with 'origin/master'.
#
# Changes to be committed:
# (use "git restore --staged <file>..." to unstage)
# modified: index.html
# new file: style.css
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# modified: README.md
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
# temp.log
# 短格式输出
git status -s
# 输出:
# M index.html (M在左边表示暂存区有修改)
# A style.css
# M README.md (M在右边表示工作区有修改但未暂存)
# ?? temp.log (新文件未跟踪)
git diff - 查看差异
# 查看工作区和暂存区的差异
git diff
# 查看暂存区和上次提交的差异
git diff --staged
git diff --cached # 等价
# 查看工作区和上次提交的差异
git diff HEAD
# 比较两个提交的差异
git diff commit_id1 commit_id2
# 只查看某个文件的差异
git diff README.md
# 图形化差异(彩色输出)
git diff --color-words
Demo:理解diff的输出
# 创建文件
echo "line 1" > test.txt
git add test.txt
git commit -m "add test.txt"
# 修改文件
echo "line 2" >> test.txt
echo "line 1 modified" > test.txt # 覆盖第一行
# 查看工作区和暂存区的差异
git diff
# 输出:
# -line 1
# +line 1 modified
# +line 2
# 添加到暂存区
git add test.txt
# 再次修改工作区
echo "line 3" >> test.txt
# 对比暂存区和HEAD
git diff --staged
# 输出:显示line 1修改和line 2添加,但没有line 3
# 对比工作区和HEAD
git diff HEAD
# 输出:显示所有修改
2.3 撤销与恢复
git restore - 恢复文件(Git 2.23+推荐)
# 丢弃工作区的修改(小心:不可恢复)
git restore <file>
git restore README.md
# 从暂存区撤回到工作区(相当于反向add)
git restore --staged <file>
# 从指定提交恢复文件
git restore --source=HEAD~2 <file>
git reset - 重置版本(传统方式)
# 软重置:只移动HEAD,保留工作区和暂存区
git reset --soft HEAD~1
# 混合重置(默认):移动HEAD,保留工作区,清空暂存区
git reset --mixed HEAD~1
git reset HEAD~1 # 等价
# 硬重置:移动HEAD,丢弃所有修改(危险!)
git reset --hard HEAD~1
# 重置到指定提交
git reset --hard commit_id
Demo:三种reset的区别
# 准备实验环境
echo "v1" > version.txt
git add version.txt
git commit -m "v1"
echo "v2" >> version.txt
git commit -am "v2"
echo "v3" >> version.txt
git commit -am "v3"
# 当前HEAD在v3
# 1. --soft 重置到v2
git reset --soft HEAD~1
git status
# 输出:Changes to be committed: modified: version.txt
# version.txt内容仍然是v1,v2,v3(工作区没变)
# 但HEAD指向v2,v3的修改在暂存区
# 2. --mixed 重置到v2
git reset --mixed HEAD~1
git status
# 输出:Changes not staged: modified: version.txt
# 暂存区被清空,修改在工作区
# 3. --hard 重置到v2(危险!)
git reset --hard HEAD~1
git status
# 输出:nothing to commit
# version.txt内容变回v1,v2,v3彻底丢失
git revert - 安全撤销(推荐公共分支使用)
# 撤销某个提交(会生成新的提交)
git revert <commit_id>
# 撤销最近的提交
git revert HEAD
# 撤销并自动提交
git revert --no-edit HEAD
reset vs revert 对比:
reset:移动指针,重写历史(适合本地未推送的分支)revert:保留历史,新增一个反向提交(适合公共分支)
三、分支管理:团队协作的核心
3.1 分支的本质
分支就是指向某个提交的移动指针。Git的默认分支叫master或main,它只是一个指针。
用“平行宇宙”比喻:
- 主分支是你现实世界的时间线
- 创建分支等于进入一个平行宇宙,在那里你可以做任何实验
- 合并分支等于把平行宇宙的成果合并回现实世界
master
│
v
A---B---C
\
D---E (feature分支)
│
v
3.2 分支基本操作
git branch - 管理分支
# 列出所有本地分支(*号表示当前分支)
git branch
# 创建新分支
git branch feature-login
# 删除分支(已合并)
git branch -d feature-login
# 强制删除分支(未合并也可以删)
git branch -D feature-login
# 重命名当前分支
git branch -m new-name
# 查看所有分支(包括远程)
git branch -a
# 查看分支的最新提交
git branch -v
git checkout / git switch - 切换分支
# 传统方式(checkout)
git checkout feature-login
# 新版方式(switch,更直观)
git switch feature-login
# 创建并切换到新分支
git checkout -b new-feature
git switch -c new-feature # 新版
# 切换到上一个分支
git checkout -
git merge - 合并分支
# 将指定分支合并到当前分支
git merge feature-login
# 合并时禁止快进(生成一个合并提交)
git merge --no-ff feature-login
# 合并并自动提交
git merge --no-edit feature-login
Demo:完整的分支工作流
# 1. 初始化项目
mkdir branch-demo && cd branch-demo
git init
echo "# Main Project" > README.md
git add README.md
git commit -m "Initial commit"
# 2. 创建并切换到功能分支
git branch feature-1
git switch feature-1 # 或 git checkout feature-1
# 3. 在分支上开发
echo "Feature 1 code" > feature.txt
git add feature.txt
git commit -m "Add feature 1"
# 4. 继续提交
echo "More feature code" >> feature.txt
git commit -am "Update feature 1"
# 5. 切换回master
git switch master
# 6. 在master上修改
echo "Main update" >> README.md
git commit -am "Update README"
# 7. 合并feature-1到master
git merge feature-1
# 输出:Merge made by the 'recursive' strategy.
# 8. 查看分支历史
git log --oneline --graph --all
# 输出:
# * a1b2c3d (HEAD -> master) Merge branch 'feature-1'
# |\
# | * e4f5g6h (feature-1) Update feature 1
# | * i6j7k8l Add feature 1
# * | b9c0d1e Update README
# |/
# * f2g3h4i Initial commit
3.3 合并冲突及解决
什么时候产生冲突? 两个分支修改了同一个文件的同一行,或者一个分支删除了文件另一个分支修改了它。
Demo:制造并解决冲突
# 准备冲突场景
mkdir conflict-demo && cd conflict-demo
git init
echo "Line 1" > conflict.txt
echo "Line 2" >> conflict.txt
git add conflict.txt
git commit -m "Initial conflict.txt"
# 创建并切换到feature分支
git checkout -b feature
# 在feature分支修改第一行
echo "Feature Line 1" > conflict.txt
echo "Line 2" >> conflict.txt
git commit -am "Feature modified line 1"
# 切换回master
git checkout master
# 在master分支修改第一行(不同内容)
echo "Master Line 1" > conflict.txt
echo "Line 2" >> conflict.txt
git commit -am "Master modified line 1"
# 尝试合并(会产生冲突)
git merge feature
# 输出:
# Auto-merging conflict.txt
# CONFLICT (content): Merge conflict in conflict.txt
# Automatic merge failed; fix conflicts and then commit the result.
# 查看冲突文件内容
cat conflict.txt
# 输出:
# <<<<<<< HEAD
# Master Line 1
# =======
# Feature Line 1
# >>>>>>> feature
# Line 2
# 手动解决冲突(编辑文件,保留想要的内容)
cat > conflict.txt << EOF
Master and Feature Combined Line
Line 2
EOF
# 标记冲突已解决
git add conflict.txt
# 完成合并
git commit -m "Resolved conflict between master and feature"
# 查看结果
git log --oneline --graph
解决冲突的原则:
- 找到冲突文件(
git status会列出) - 手动编辑,删除
<<<<<<<,=======,>>>>>>>标记 - 保留最终想要的代码
git add标记为解决git commit完成合并
3.4 分支管理策略
Git Flow 经典模型
master (main) → 生产环境代码,每次提交都打tag
↑
develop → 开发主分支,集成所有功能
↑
feature/* → 功能分支,从develop拉出,完成后合并回develop
release/* → 发布分支,从develop拉出,测试修复后合并到master和develop
hotfix/* → 紧急修复分支,从master拉出,修复后合并到master和develop
GitHub Flow 简化版(适合持续部署)
master (main) → 始终可部署
↑
feature branch → 从master拉出,完成后通过Pull Request合并
Demo:模拟Git Flow工作流
# 1. 初始化主分支和开发分支
git checkout -b develop
echo "Develop branch initialized" > develop.txt
git add develop.txt
git commit -m "Init develop branch"
# 2. 从develop创建功能分支
git checkout -b feature/payment develop
# 3. 在功能分支开发
echo "Payment gateway code" > payment.py
git add payment.py
git commit -m "Add payment processing"
# 4. 切换回develop并合并功能分支
git checkout develop
git merge --no-ff feature/payment -m "Merge feature/payment"
# 5. 创建发布分支
git checkout -b release/1.0.0 develop
# 6. 在发布分支做最后的bug修复
echo "Fix critical bug" > release-fix.txt
git add release-fix.txt
git commit -m "Release bug fix"
# 7. 合并发布分支到master(生产)
git checkout master
git merge --no-ff release/1.0.0 -m "Release version 1.0.0"
git tag -a v1.0.0 -m "Release version 1.0.0"
# 8. 同步发布分支到develop
git checkout develop
git merge --no-ff release/1.0.0 -m "Sync release fixes to develop"
# 9. 删除功能分支和发布分支
git branch -d feature/payment
git branch -d release/1.0.0
四、深入理解Git的三个工作区
4.1 工作区、暂存区、仓库的关系
使用Git时,你的文件处于以下三种状态之一:
- 已修改(modified):修改了但还没保存到数据库
- 已暂存(staged):标记了当前版本要放到下次提交中
- 已提交(committed):安全地保存在本地数据库
对应的区域:
工作区(Working Directory) 暂存区(Index/Staging Area) 本地仓库(Local Repository)
│ │ │
修改文件 git add git commit
(modified) ───────────────> (staged) ─────────────> (committed)
▲ │ │
│ │ git reset HEAD │
└─────────────────────────────────┘ │
│
git checkout -- <file> │
└───────────────────────────────────────────────────────────────┘
4.2 .git目录的秘密
.git文件夹是Git的心脏,理解它能帮助你真正掌握Git。
.git/
├── HEAD # 当前指向的分支引用
├── config # 仓库配置(用户信息、远程仓库等)
├── description # 仓库描述
├── hooks/ # 钩子脚本(提交前后自动执行)
├── index # 暂存区的二进制文件
├── objects/ # 所有数据对象(提交、文件内容)
│ ├── 00/ # 对象按哈希值前两位分目录
│ └── ...
└── refs/ # 引用(分支、标签)
├── heads/ # 本地分支
├── remotes/ # 远程分支
└── tags/ # 标签
实验:探索.git目录
# 创建一个新仓库
mkdir explore-git && cd explore-git
git init
# 第一次提交
echo "Hello" > hello.txt
git add hello.txt
git commit -m "First commit"
# 查看.git/objects目录
find .git/objects -type f
# 输出类似:.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
# 这就是Git保存的Blob对象
# 查看对象类型
git cat-file -t ce013625030ba8dba906f756967f9e9ca394464a
# 输出:blob
# 查看对象内容
git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a
# 输出:Hello
# 查看HEAD指向
cat .git/HEAD
# 输出:ref: refs/heads/master
# 查看当前分支指向的提交
cat .git/refs/heads/master
# 输出:一个40位哈希值
4.3 暂存区的内部机制
暂存区的本质是.git/index文件,它记录了下次要提交的文件快照。
Demo:手动操作暂存区
# 创建多个文件
echo "Content A" > A.txt
echo "Content B" > B.txt
# 只添加A.txt
git add A.txt
# 查看暂存区内容
git ls-files --stage
# 输出:
# 100644 哈希值 0 A.txt
# 现在添加B.txt
git add B.txt
# 修改A.txt(工作区修改但未add)
echo "New content" > A.txt
# 查看暂存区和工作区的差异
git diff --cached # 暂存区 vs HEAD
git diff # 工作区 vs 暂存区
# 从暂存区移除文件(但保留工作区)
git restore --staged B.txt
git ls-files --stage
# B.txt不在暂存区了
五、远程协作:GitHub/GitLab的使用
5.1 远程仓库操作
git remote - 管理远程仓库
# 添加远程仓库
git remote add origin https://github.com/username/repo.git
# 查看远程仓库
git remote -v
# 重命名远程仓库
git remote rename origin upstream
# 删除远程仓库
git remote remove origin
# 修改远程URL
git remote set-url origin https://new-url.git
git clone - 克隆仓库
# 克隆HTTPS地址
git clone https://github.com/username/repo.git
# 克隆SSH地址(需要配置SSH key)
git clone git@github.com:username/repo.git
# 克隆到指定目录
git clone https://github.com/username/repo.git my-folder
# 克隆特定分支
git clone -b develop https://github.com/username/repo.git
# 浅克隆(只下载最新提交,节省时间)
git clone --depth 1 https://github.com/large-project/repo.git
git push - 推送到远程
# 推送当前分支到远程同名分支
git push origin HEAD
# 推送本地master到远程master
git push origin master
# 推送并设置上游跟踪
git push -u origin master # -u 是 --set-upstream 缩写
# 推送所有本地分支
git push --all origin
# 删除远程分支
git push origin --delete feature-branch
# 强制推送(危险!会覆盖远程历史)
git push --force origin master
git pull / git fetch - 拉取更新
# fetch:只下载,不合并
git fetch origin
git fetch origin master
git fetch --all
# pull:下载并合并(等于 fetch + merge)
git pull origin master
# pull rebase模式(更干净的历史)
git pull --rebase origin master
# 只拉取特定分支
git pull origin feature-branch
5.2 git pull vs git fetch 的区别
| 命令 | 操作 | 是否合并 | 工作区变化 |
|---|---|---|---|
git fetch |
下载远程最新提交到本地仓库 | 否 | 无变化 |
git pull |
下载远程最新提交并自动合并 | 是 | 自动合并 |
最佳实践:多人协作时,用git fetch + git diff 查看差异后再决定是否合并。
# 安全的拉取流程
git fetch origin
git diff origin/master # 查看差异
git merge origin/master # 确认无误后再合并
# 或者一步到位(推荐)
git pull --rebase origin master # 保持线性历史
5.3 git clone、git branch、fork 三者的区别
这三个概念初学者最容易混淆,我们用表格和场景来解释:
| 操作 | 作用范围 | 是否联网 | 结果 | 典型场景 |
|---|---|---|---|---|
| git clone | 本地 ← 远程 | 需要 | 下载整个仓库到本地 | 第一次获取代码 |
| git branch | 本地 | 不需要 | 创建一个新指针(分支) | 本地开发新功能 |
| fork | GitHub/GitLab平台 | 需要 | 复制别人的仓库到自己的账号下 | 贡献开源项目 |
详细解释:
-
git clone克隆:把远程仓库完整复制到本地,包括所有历史、分支。你拥有完整的读写权限(本地)。# 克隆后,你本地就有了整个项目 git clone https://github.com/facebook/react.git cd react git branch -a # 可以看到所有远程分支 -
git branch分支:在本地创建一个新的开发线,不涉及网络。分支只是指针,创建成本极低。# 在当前仓库创建一个新分支 git branch new-feature # 切换到新分支开发 git checkout new-feature -
fork复刻:在GitHub平台上的操作。你想修改别人的项目(比如React),但你没有写入权限,那就点击"Fork"按钮,把项目复制一份到你的GitHub账号下,然后你就可以clone你自己的副本并随意修改。完成后通过Pull Request向原项目提交贡献。
实战流程:
1. 在GitHub上fork react仓库 → 你的账号下有了 react(fork)
2. git clone https://github.com/你的用户名/react.git → 下载到本地
3. git branch fix-bug → 在本地创建修复分支
4. 修改代码并 push 到你的远程仓库(origin)
5. 在GitHub上发起Pull Request → 请求原项目合并你的修改
5.4 Pull Request 的工作流程
Pull Request(PR)不是Git命令,而是GitHub/GitLab等平台的协作机制。它让你告诉其他人:“我做了修改,请合并到主分支”。
PR vs git branch 的区别:
git branch:本地操作,创建分支- Pull Request:平台功能,请求合并分支
完整的PR流程:
# 1. 在GitHub上fork目标仓库
# 2. 克隆自己的fork到本地
git clone https://github.com/your-username/project.git
cd project
# 3. 添加上游仓库(原项目)
git remote add upstream https://github.com/original-owner/project.git
# 4. 创建功能分支
git checkout -b new-feature
# 5. 进行修改并提交
echo "new code" > feature.txt
git add feature.txt
git commit -m "Add new feature"
# 6. 推送到自己的远程仓库
git push origin new-feature
# 7. 在GitHub上创建Pull Request(网页操作)
# 8. 保持PR更新(如果原项目有新的提交)
git fetch upstream
git merge upstream/master
git push origin new-feature # PR会自动更新
PR的最佳实践:
- 每个PR只做一件事(单一职责)
- 写清楚PR描述(做了什么、为什么、怎么测试)
- 保持PR小且容易review(不超过400行修改)
- 使用Draft PR表示还在开发中
六、高级技巧与常见问题
6.1 改写历史(谨慎使用)
git rebase - 变基操作
# 将当前分支的提交移到另一个分支上
git rebase master
# 交互式rebase(修改、合并、删除提交)
git rebase -i HEAD~3
# 自动解决冲突后继续
git rebase --continue
# 放弃rebase
git rebase --abort
merge vs rebase:
- merge:保留完整历史,有合并提交
- rebase:线性历史,更干净但会改写提交哈希
# merge方式
git checkout feature
git merge master
# 历史:... A --- B --- C (master)
# \ \
# D --- E --- M (feature)
# rebase方式
git checkout feature
git rebase master
# 历史:... A --- B --- C (master) --- D' --- E' (feature)
6.2 暂存和清理
git stash - 临时保存工作区
# 保存当前修改(包括暂存区)
git stash
# 保存并添加描述
git stash save "正在开发登录功能"
# 查看所有暂存列表
git stash list
# 恢复最近一次暂存(不删除stash)
git stash apply
# 恢复并删除
git stash pop
# 删除特定stash
git stash drop stash@{0}
# 恢复特定stash
git stash apply stash@{1}
# 清空所有stash
git stash clear
Demo:紧急修复bug时使用stash
# 正在开发新功能
echo "new feature code" > feature.js
git add feature.js
# 突然需要修复紧急bug
git stash save "开发中的新功能"
# 切换到主分支修复bug
git checkout master
echo "bug fix" > bugfix.js
git add bugfix.js
git commit -m "Fix critical bug"
# 切回功能分支并恢复开发状态
git checkout feature
git stash pop
# 之前的工作区和暂存区都恢复了
git clean - 清理未跟踪文件
# 查看哪些文件会被删除(干跑)
git clean -n
# 删除未跟踪的文件
git clean -f
# 删除未跟踪的目录
git clean -fd
# 删除被.gitignore忽略的文件
git clean -fx
# 交互式删除
git clean -i
6.3 调试与查找
git log 的高级用法
# 图形化显示分支历史
git log --graph --oneline --all
# 查看每次修改的内容
git log -p
# 查看最近n次提交
git log -2
# 按作者搜索
git log --author="John"
# 按提交信息搜索
git log --grep="fix bug"
# 按时间搜索
git log --since="2024-01-01" --until="2024-12-31"
# 查看某个文件的修改历史
git log --follow README.md
# 格式化输出
git log --pretty=format:"%h - %an, %ar : %s"
git bisect - 二分查找bug
# 开始二分查找
git bisect start
# 标记当前版本有bug
git bisect bad
# 标记某个已知的正常版本
git bisect good v1.0
# Git会自动切换到中间提交,测试后标记
# 如果当前有bug:git bisect bad
# 如果当前正常:git bisect good
# 重复直到找到第一个bug提交
# 结束二分查找
git bisect reset
git blame - 谁改的这行代码?
# 查看文件每一行的作者和提交
git blame index.html
# 显示具体提交信息
git blame -l index.html
# 查看特定行范围
git blame -L 10,20 index.html
6.4 子模块与子树
git submodule - 管理外部依赖
# 添加子模块
git submodule add https://github.com/example/lib.git libs/lib
# 克隆包含子模块的仓库
git clone --recursive https://github.com/main/project.git
# 更新所有子模块
git submodule update --init --recursive
# 子模块内部操作
cd libs/lib
git checkout master
git pull
cd ../..
git add libs/lib
git commit -m "Update submodule"
6.5 常用别名配置(提高效率)
# 配置一系列实用别名
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.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-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.amend "commit --amend --no-edit"
git config --global alias.who "shortlog -s --"
git config --global alias.contrib "shortlog -sn"
# 使用示例
git lg # 漂亮的日志
git unstage file.txt # 撤销add
git last # 最后一次提交
git amend # 快速修正提交
七、实战场景演练
7.1 场景一:新员工入职,第一次配置Git并拉取项目
# 1. 全局配置身份
git config --global user.name "张三"
git config --global user.email "zhangsan@company.com"
# 2. 生成SSH key(免密登录)
ssh-keygen -t rsa -b 4096 -C "zhangsan@company.com"
cat ~/.ssh/id_rsa.pub
# 复制输出到GitHub/GitLab的SSH keys设置中
# 3. 克隆项目
git clone git@github.com:company/backend.git
cd backend
# 4. 查看项目状态
git status
git branch -a # 查看所有分支
# 5. 切换到开发分支
git checkout -b develop origin/develop
# 6. 安装依赖等操作...
npm install
# 7. 开始开发...
7.2 场景二:开发一个新功能
# 确保当前在develop分支且是最新
git checkout develop
git pull origin develop
# 创建功能分支
git checkout -b feature/payment-gateway
# 开发过程中多次提交
echo "function pay() {}" > payment.js
git add payment.js
git commit -m "Add pay function"
echo "function refund() {}" >> payment.js
git commit -am "Add refund function"
# 发现有个小错误,立即修正
echo "fix bug" >> payment.js
git commit -am "Fix typo in refund"
# 合并这三个提交(保持历史干净)
git rebase -i HEAD~3
# 在编辑器中将后两个fixup改为squash或fixup
# 功能完成后推送到远程
git push origin feature/payment-gateway
# 在GitLab/GitHub上创建Merge Request/Pull Request
7.3 场景三:紧急修复生产环境bug
# 1. 立即从master创建hotfix分支
git checkout master
git checkout -b hotfix/login-crash
# 2. 修复bug
vim login.js
git add login.js
git commit -m "Fix login crash when password empty"
# 3. 合并到master并打标签
git checkout master
git merge --no-ff hotfix/login-crash -m "Hotfix: login crash"
git tag -a v1.0.1 -m "Hotfix version 1.0.1"
# 4. 同步到develop
git checkout develop
git merge master
# 5. 删除hotfix分支
git branch -d hotfix/login-crash
# 6. 推送到远程
git push origin master --tags
git push origin develop
7.4 场景四:处理复杂的合并冲突
# 同步远程develop时产生冲突
git pull origin develop
# 输出:CONFLICT in app.js
# 查看冲突文件
git status
# 使用工具解决冲突
git mergetool # 会打开配置的合并工具(如vimdiff, vscode)
# 或手动编辑冲突文件
vim app.js
# 搜索 <<<<<<< 找到冲突区域
# 手动保留需要的代码
# 解决后标记为已解决
git add app.js
# 继续合并
git commit -m "Merge develop and resolve conflicts"
# 如果合并过程中想放弃
git merge --abort
7.5 场景五:误操作恢复
# 误删了未提交的文件(工作区)
rm important.txt
# 恢复
git checkout -- important.txt
# 误删已add但未commit的文件
rm important.txt
git add important.txt # 已添加到暂存区
# 恢复
git restore --staged important.txt
git checkout -- important.txt
# 误commit了错误的文件
git add wrong.txt
git commit -m "Wrong commit"
# 修正:重新提交正确的文件
git add right.txt
git commit --amend -m "Correct commit"
# 误commit后想撤销(但还没push)
git reset HEAD~1 # 保留修改在工作区
# 或完全丢弃修改
git reset --hard HEAD~1
# 已经push了发现错误
git revert HEAD # 生成反向提交
git push origin master
7.6 场景六:开源贡献完整流程
# 1. 在GitHub上fork目标仓库(如facebook/react)
# 2. 克隆自己的fork
git clone https://github.com/your-username/react.git
cd react
# 3. 添加上游仓库
git remote add upstream https://github.com/facebook/react.git
# 4. 同步最新代码
git fetch upstream
git checkout main
git merge upstream/main
# 5. 创建分支
git checkout -b fix-typo-docs
# 6. 修改文档
vim README.md
git add README.md
git commit -m "docs: fix typo in README"
# 7. 推送到自己的fork
git push origin fix-typo-docs
# 8. 在GitHub上创建Pull Request
# 填写PR描述,选择目标分支(facebook/react的main)
# 9. 等待review,如果需要修改
git add .
git commit --amend --no-edit
git push --force origin fix-typo-docs # PR会自动更新
# 10. PR合并后,删除本地和远程分支
git checkout main
git branch -d fix-typo-docs
git push origin --delete fix-typo-docs
# 11. 同步上游的新提交
git pull upstream main
git push origin main
八、常见问题FAQ
Q1: git pull 和 git pull --rebase 有什么区别?
A: git pull = git fetch + git merge,会产生合并提交。git pull --rebase = git fetch + git rebase,历史是线性的。推荐在个人分支上使用--rebase,保持历史干净。
Q2: 如何撤销已经push的commit?
A: 两种方式:
# 方法1:revert(安全,推荐)
git revert <commit-hash>
git push origin master
# 方法2:reset后强制推送(危险,会改写远程历史)
git reset --hard HEAD~1
git push --force origin master # 如果别人已经拉取会出问题
Q3: 不小心把密码提交到Git了怎么办?
A:
- 立即改密码!
- 使用BFG Repo-Cleaner或
git filter-branch彻底删除历史
# 安装BFG后
java -jar bfg.jar --replace-text passwords.txt my-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force
Q4: 如何只clone某个分支?
A:
git clone -b develop --single-branch https://github.com/repo.git
Q5: git merge --no-ff 是什么?
A: 禁止快进合并(no fast-forward)。即使分支可以线性合并,也要创建一个合并提交。保留分支信息,方便追溯历史。
Q6: 如何找到丢失的commit?
A: 使用git reflog,Git记录所有HEAD的移动历史
git reflog
# 找到丢失的commit hash
git checkout <hash>
# 或者重新创建分支
git branch recovered-branch <hash>
Q7: 如何修改历史commit的提交信息?
A:
# 修改最近一次
git commit --amend -m "New message"
# 修改更早的(交互式rebase)
git rebase -i HEAD~3
# 将对应行的pick改为reword,保存后修改信息
九、总结与最佳实践
Git核心思维导图
Git
├── 本地操作
│ ├── 工作区 → git add → 暂存区 → git commit → 本地仓库
│ └── 分支管理:branch, checkout, merge, rebase
├── 远程协作
│ ├── clone/fetch/pull/push
│ ├── fork + Pull Request
│ └── remote管理
├── 历史管理
│ ├── log/diff/blame
│ ├── reset/revert
│ └── rebase -i / reflog
└── 实用技巧
├── stash临时保存
├── bisect找bug
├── submodule子模块
└── alias提高效率
最佳实践清单
提交规范:
- 小而频繁的提交,每个提交只做一件事
- 提交信息格式:
<type>(<scope>): <subject>,如feat(login): add captcha - 不要提交编译产物、日志、本地配置(用
.gitignore)
分支策略:
- master/main分支保持稳定,只接受合并
- 功能分支从develop拉取(Git Flow)或master(GitHub Flow)
- 分支命名:
feature/xxx,bugfix/xxx,hotfix/xxx
合并原则:
- 公共分支用
merge,个人分支用rebase - 推送前先
pull --rebase保持线性 - 不要强制推送(
--force)到公共分支
安全事项:
- 定期推送,不要积累大量本地提交
- 不要提交敏感信息(密码、密钥)
- 使用
.gitignore排除不必要的文件 - 推送前
git status检查要推送的内容
常用命令速查表
| 操作 | 命令 |
|---|---|
| 初始化 | git init |
| 克隆 | git clone <url> |
| 查看状态 | git status |
| 添加文件 | git add <file> |
| 提交 | git commit -m "msg" |
| 推送 | git push origin <branch> |
| 拉取 | git pull origin <branch> |
| 查看分支 | git branch -a |
| 创建分支 | git branch <name> |
| 切换分支 | git checkout <name> |
| 合并分支 | git merge <branch> |
| 查看日志 | git log --oneline --graph |
| 撤销工作区修改 | git restore <file> |
| 撤销暂存区 | git restore --staged <file> |
| 撤销提交(安全) | git revert <commit> |
| 撤销提交(危险) | git reset --hard HEAD~1 |
下一步学习建议
- 深入Git原理:阅读《Pro Git》(免费在线),理解Git的对象模型
- 图形化工具:学习使用Sourcetree、GitKraken等GUI工具辅助理解
- CI/CD集成:学习Git与Jenkins、GitHub Actions的配合
- 高级技巧:Git hooks自动化、git worktree多任务并行开发
Git就像编程世界的"后悔药"和"保险箱",掌握了它,你就能自信地在代码世界里自由穿梭。记住:Git不会丢失你的数据,除非你强制覆盖。多练习、多实验,每个Git命令都值得你亲手敲一遍。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)