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的默认分支叫mastermain,它只是一个指针。

用“平行宇宙”比喻

  • 主分支是你现实世界的时间线
  • 创建分支等于进入一个平行宇宙,在那里你可以做任何实验
  • 合并分支等于把平行宇宙的成果合并回现实世界
      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

解决冲突的原则

  1. 找到冲突文件(git status会列出)
  2. 手动编辑,删除 <<<<<<<, =======, >>>>>>> 标记
  3. 保留最终想要的代码
  4. git add 标记为解决
  5. 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 clonegit branchfork 三者的区别

这三个概念初学者最容易混淆,我们用表格和场景来解释:

操作 作用范围 是否联网 结果 典型场景
git clone 本地 ← 远程 需要 下载整个仓库到本地 第一次获取代码
git branch 本地 不需要 创建一个新指针(分支) 本地开发新功能
fork GitHub/GitLab平台 需要 复制别人的仓库到自己的账号下 贡献开源项目

详细解释

  1. git clone克隆:把远程仓库完整复制到本地,包括所有历史、分支。你拥有完整的读写权限(本地)。

    # 克隆后,你本地就有了整个项目
    git clone https://github.com/facebook/react.git
    cd react
    git branch -a  # 可以看到所有远程分支
    
  2. git branch分支:在本地创建一个新的开发线,不涉及网络。分支只是指针,创建成本极低。

    # 在当前仓库创建一个新分支
    git branch new-feature
    # 切换到新分支开发
    git checkout new-feature
    
  3. 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 pullgit 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:

  1. 立即改密码!
  2. 使用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

下一步学习建议

  1. 深入Git原理:阅读《Pro Git》(免费在线),理解Git的对象模型
  2. 图形化工具:学习使用Sourcetree、GitKraken等GUI工具辅助理解
  3. CI/CD集成:学习Git与Jenkins、GitHub Actions的配合
  4. 高级技巧:Git hooks自动化、git worktree多任务并行开发

Git就像编程世界的"后悔药"和"保险箱",掌握了它,你就能自信地在代码世界里自由穿梭。记住:Git不会丢失你的数据,除非你强制覆盖。多练习、多实验,每个Git命令都值得你亲手敲一遍。


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐