Git 5 篇

初始化仓库

git init

git init 默认创建的分支名是 master。GitHub 等平台现在推荐用 main,有三种方式:

方式一:一劳永逸(推荐)

git config --global init.defaultBranch main

设置后,所有 git init 创建的仓库默认分支就是 main。只需执行一次,永久生效。

方式二:创建时指定(Git 2.28+)

git init -b main

只对当前仓库生效,不影响全局配置。

方式三:创建后改名

git init
git branch -M main

兼容所有 Git 版本。但注意改名只是本地生效,首次 push 前还没远程分支,所以直接 push 即可。

验证

git branch    # 查看当前分支,前面带 * 的就是

创建 SSH 公钥

ssh-keygen -t ed25519 -C "your_email@example.com"

查看公钥:

cat ~/.ssh/id_ed25519.pub

将公钥内容添加到 GitHub/GitLab 的 SSH Keys 设置中。

配置用户信息

git config --global user.name "Your Name"
git config --global user.email "email@example.com"

添加远程仓库

git remote add origin <url>

<url> 是 GitHub 仓库地址。

克隆远程仓库

git clone <url>

直接下载远程仓库到本地,无需手动 init 和 remote。

.gitignore 忽略文件

# 在项目根目录创建 .gitignore 文件

常见忽略规则:

# 依赖
node_modules/
vendor/

# 构建产物
dist/
build/

# 环境变量
.env
.env.local

# 系统文件
.DS_Store
Thumbs.db

# IDE
.vscode/
.idea/

# 日志
*.log

快速开始

# 场景 A:从零开始
git init -b main                    # 以 main 为主分支初始化
git add .
git commit -m "first commit"
git remote add origin <url>
git push -u origin main

# 场景 B:克隆已有项目
git clone <url>
cd <project>

文件操作

git add <file>                         # 添加指定文件到暂存区
git add .                              # 添加所有修改
git add -p                             # 交互式选择添加(逐块确认,推荐)
git add *.js                           # 通配符添加
git add src/                           # 添加整个目录
git rm <file>                          # 删除文件并暂存
git mv <old> <new>                     # 重命名文件
git restore <file>                     # 丢弃工作区修改(回到最近一次 add/commit 的状态)
git restore --staged <file>            # 取消暂存(修改回到工作区)

提交管理

git status                             # 查看工作区状态
git status -s                          # 简洁状态
git diff                               # 查看未暂存的修改内容
git diff --staged                      # 查看已暂存但未提交的修改
git commit -m "提交说明"                # 提交暂存区
git commit -am "提交说明"               # add + commit(仅已跟踪文件)
git commit --amend -m "新的信息"         # 修改最近一次提交的信息
git commit --amend --no-edit           # 追加修改到最近一次提交,不改信息

提交信息规范(Conventional Commits)

<type>: <简短描述>

feat: 添加用户登录
fix: 修复登录超时
refactor: 提取 token 刷新逻辑
docs: 更新 README
chore: 升级依赖

远程同步

git remote -v                          # 查看远程仓库列表
git remote add origin <url>            # 添加远程仓库
git remote set-url origin <url>        # 修改远程仓库地址
git remote rm origin                   # 删除远程仓库关联

git fetch origin                       # 下载远程更新,不合并
git fetch --prune                      # 同时清理远程已删除的分支
git pull origin main                   # 拉取并合并(= fetch + merge)
git pull --rebase origin main          # 拉取并变基(= fetch + rebase,推荐)

git push origin <branch>               # 推送到远程
git push -u origin <branch>            # 首次推送并设置上游(之后直接 git push)
git push --force-with-lease            # 安全强制推送(不会覆盖别人新推的提交)
git push origin --delete <branch>      # 删除远程分支

临时保存(stash)

git stash                              # 暂存工作进度
git stash save "描述"                  # 带描述暂存
git stash list                         # 查看 stash 列表
git stash pop                          # 恢复最近一次并删除
git stash apply                        # 恢复但不删除
git stash drop                         # 删除最近一次
git stash pop stash@{1}                # 恢复指定的 stash

典型场景:开发到一半需要切分支修 bug,但又不想提交半成品代码。

分支策略

大厂常用的两种模型:Trunk-Based Development(主流)和 Git Flow(传统)。

Trunk-Based Development(推荐)

Google、Facebook 等公司的主流选择。核心思想:所有人都往 main 提交,分支存活不超过 1 天

main ───●────●────●────●────●────●──▶
         │    │         │
         │    │         └── 新功能 C(分支存活 4 小时)
         │    └── 新功能 B(分支存活 8 小时)
         └── 新功能 A(分支存活 6 小时)

特点:

  • 分支短命(≤ 1 天),减少合并冲突
  • 未完成功能用 feature flag 隐藏,不影响线上
  • 高频集成,CI/CD 自动化测试和部署
  • Code Review 通过 PR/MR 完成

适用场景: CI/CD 成熟、测试覆盖率高、迭代快的团队。

Git Flow(传统方案)

适合有固定发布周期的项目(客户端软件、SDK)。

main     ──●──────────────●──────────────●──▶
            │              │              │
develop  ──●──●──●──●──●──●──●──●──●──●──●──▶
            │  │      │
feature/A ──┘  │      │
               │      │
release/1.0 ───┘  ●──●──┘
                      │
hotfix/xxx ───────────●──┘
分支 来源 合入 说明
main 生产环境代码
develop 开发主线
feature/* develop develop 新功能
release/* develop main + develop 发布前冻结
hotfix/* main main + develop 线上紧急修复

适用场景: 固定版本号、长发布周期。


分支命名规范

前缀 用途 示例
feature/ 新功能 feature/user-auth
fix/ 修 bug fix/login-error
refactor/ 重构 refactor/api-layer
docs/ 文档 docs/api-guide
chore/ 杂项 chore/update-deps
hotfix/ 紧急修复 hotfix/crash-on-start

分支命令

git branch                              # 查看本地分支
git branch -r                           # 查看远程分支
git branch -a                           # 查看所有分支
git branch <name>                       # 创建分支(不切换)
git switch <name>                       # 切换分支(推荐,Git 2.23+)
git switch -c <name>                    # 创建并切换
git checkout -b <name>                  # 创建并切换(旧写法)
git branch -m <old> <new>               # 重命名分支
git branch -d <name>                    # 删除已合并分支
git branch -D <name>                    # 强制删除
git push origin --delete <name>         # 删除远程分支

合并策略

# merge:保留分支拓扑
git merge feature/login

# squash merge:整个分支压缩成一条提交(推荐用于 PR 合入)
git merge --squash feature/login
git commit -m "feat: 添加登录功能"

# rebase:把当前分支搬到目标分支顶部,历史线性
git rebase main
方式 历史形态 场景
merge 保留分支图 多人协作分支
squash merge 一条直线 PR 合入 main(大厂推荐)
rebase 线性历史 同步上游、整理本地提交

大厂惯例: PR 合入 main 用 squash merge,日常同步用 rebase。


标准开发流程(Feature Branch + PR)

1. 拉取最新

git checkout main
git pull origin main

2. 创建特性分支

git switch -c feature/add-login

3. 开发 + 提交

git status
git diff
git add .
git commit -m "feat: 添加登录功能"
git push -u origin feature/add-login

4. 同步上游(main 有新提交时)

# rebase 方式(推荐)
git checkout main
git pull origin main
git checkout feature/add-login
git rebase main

# 或者 merge 方式
git checkout feature/add-login
git merge main

推送 rebase 后的分支用 git push --force-with-lease,不会覆盖别人的提交。

5. 提交 Pull Request

推送到远程后,在 GitHub/GitLab 网页上创建 PR,填写:

  • 标题:一句话描述
  • 描述:背景、改动、测试方式、截图
  • Reviewer:指定审核人

6. Code Review 后修改

Reviewer 提意见后,在同一分支上继续改

git add .
git commit -m "fix: 根据 review 意见调整"
git push origin feature/add-login

不要另开新分支。PR 会自动更新到最新提交。

7. 合入后清理

git checkout main
git pull origin main
git branch -d feature/add-login
git push origin --delete feature/add-login   # 或直接在 PR 页面点删除

冲突解决

多人改了同一文件时,合并产生冲突:

git merge main
# CONFLICT: Merge conflict in src/app.js

Git 在冲突文件中标记:

<<<<<<< HEAD
你的修改
=======
别人的修改
>>>>>>> main

解决步骤:

# 1. 手动编辑文件,删除标记,保留正确内容
# 2. 标记已解决
git add src/app.js
# 3. 继续
git merge --continue          # merge 场景
git rebase --continue         # rebase 场景

# 想放弃?
git merge --abort             # 或 git rebase --abort

同步上游变更

# 方法一:rebase(推荐)
git fetch origin
git rebase origin/main

# 方法二:merge
git pull origin main

常用组合

# 对比两个分支
git diff main..feature/xxx
git diff main..feature/xxx --name-only     # 只看文件名

# 查看还没 push 的提交
git log origin/main..HEAD --oneline

# 把最近 3 次提交压缩成一条
git rebase -i HEAD~3

# 把某次提交应用到当前分支
git cherry-pick <hash>

撤销与回滚

撤销操作分为四个层级,逐级递进:工作区 → 暂存区 → 本地提交 → 远程提交

层级一:撤销工作区修改(还没 add)

git restore <file>                   # 丢弃单个文件的修改
git restore .                        # 丢弃所有文件的修改

本质是从暂存区(或最新提交)恢复文件。不可逆

层级二:取消暂存(已经 add,还没 commit)

git restore --staged <file>          # 取消单个文件的暂存,修改回到工作区
git restore --staged .               # 取消所有暂存

修改还在工作区,只是从暂存区拿出来了。

层级三:撤销本地提交(已经 commit,还没 push)

reset — 回退到指定提交

工作区 ← 暂存区 ← 本地仓库 ← 远程
                     ↑
                   这里

三种模式:

git reset --soft <commit>     修改保留在暂存区(可以直接重新 commit)
git reset --mixed <commit>    修改回到工作区(需要重新 add)
git reset --hard <commit>     修改全部丢弃(不可恢复)

<commit> 的写法:

写法 含义
HEAD~1 回退 1 次提交
HEAD~3 回退 3 次提交
abc1234 回退到指定 commit hash
origin/main 回退到远程 main 的最新状态

示例

当前提交历史:

A --- B --- C --- D (HEAD)

回退到 B(撤销 C 和 D):

# 方式一:相对引用
git reset --soft HEAD~2     # 撤销 C、D,修改在暂存区
git reset --mixed HEAD~2    # 撤销 C、D,修改在工作区
git reset --hard HEAD~2     # 撤销 C、D,修改彻底丢弃

# 方式二:用 commit hash
git log --oneline           # 先查到目标 commit 的 hash
git reset --hard abc1234    # 直接跳到 abc1234

# 方式三:回退到远程版本
git fetch origin
git reset --hard origin/main

三种模式效果对比

假设工作区有文件 f(v2),暂存区有 f(v2),最新提交是 f(v2)

git reset --soft HEAD~1   →  f 保留在暂存区(v2),工作区不变
                             适合:把多个 commit 合并成一个

git reset --mixed HEAD~1  →  f 不在暂存区,在工作区(v2)
                             适合:重新整理后再 commit(默认行为)

git reset --hard HEAD~1   →  f 全部丢弃,回到 HEAD~1 的状态
                             适合:确认要彻底放弃最近的修改

commit --amend

git commit --amend -m "新的提交信息"       # 修改提交信息
git commit --amend --no-edit             # 追加内容,不改信息

只适用于还没 push 的提交。本质是把新修改"塞进"上一次提交。

层级四:撤销远程提交(已经 push)

已 push 的提交不能用 reset(会破坏别人拉取的历史),必须用 revert

git revert abc1234                                          # 撤销指定提交
git revert abc1234..def5678 --no-commit                     # 撤销多个,确认后手动 commit
git revert HEAD~2..HEAD                                     # 撤销最近 3 次

revert 不删除原提交,而是创建一次新的"反向操作"提交。历史完整、安全、可追溯。


清除工作区

清除未跟踪文件

git clean -n                    # 预览:列出会被删除的文件(安全)
git clean -f                    # 删除未跟踪文件
git clean -fd                   # 删除未跟踪文件和目录
git clean -fx                   # 连 .gitignore 里的也删

一步回到干净状态

git reset --hard HEAD                  # 丢弃工作区 + 暂存区所有修改
git reset --hard HEAD && git clean -fd # 连未跟踪文件一起清

切换到指定版本(只读,不回退)

和 reset 不同,切换只是让工作区临时变成历史版本的样子,不移动分支指针,不改历史。

git checkout <commit-hash>       # 进入 "detached HEAD" 状态
# 看看代码、运行测试...
git switch main                  # 看完回来

detached HEAD 图解

正常状态:                          detached HEAD:
HEAD → main → D --- C --- B --- A       HEAD → B --- A
                                                  ↑
                                          main → D --- C

HEAD 直接指向 commit 而非分支。此时做的提交,切回分支后就"找不到"了。

想基于旧版本改代码?

git checkout -b fix-from-old <commit-hash>    # 从旧版本拉新分支

只看不切

git show <hash>:<file>                        # 查看历史中某个文件的内容
git show HEAD~3:src/app.js

找回误删的提交(reflog)

git reflog                           # 查看 HEAD 移动记录

即使 reset --hard 或删了分支,只要提交过,reflog 里就能找到 hash,然后恢复:

# 从 reflog 找到要恢复的 hash
git checkout -b recovered-branch abc1234

默认保留 90 天。只要 commit 过,就不会真正丢失。


场景速查

我想... 命令
丢弃某个文件的修改 git restore <file>
取消暂存 git restore --staged <file>
撤销 commit 但保留修改 git reset --soft HEAD~1
撤销 commit,回到工作区 git reset --mixed HEAD~1
彻底丢弃最近一次 commit git reset --hard HEAD~1
回退到远程最新状态 git reset --hard origin/main
回退到指定 commit git log --onelinegit reset --hard <hash>
撤销已 push 的提交 git revert <hash> 然后 git push
改最后一次提交信息 git commit --amend -m "新的"
删除未跟踪文件 git clean -f
完全回到干净状态 git reset --hard HEAD && git clean -fd
临时看历史版本代码 git checkout <hash>
找回误删的提交 git refloggit checkout -b <name> <hash>

reset vs revert 决策

提交已经 push 到共享分支?(main / develop / 协作分支)
  ├── 是 → 用 git revert(安全)
  └── 否 → 用 git reset(干净)

没把握时先用 revert,永远不会出错。

速查表

命令 说明
git init 初始化新仓库
git clone <url> 克隆远程仓库
git status 查看工作区状态
git diff 查看未暂存的修改
git diff --staged 查看已暂存的修改
git add <file> 添加指定文件到暂存区
git add . 添加所有修改到暂存区
git add -p 交互式选择添加
git commit -m "msg" 提交暂存区文件
git commit --amend 修改最近一次提交
git log --oneline --graph --all 图形化查看提交历史
git blame <file> 查看文件每行的修改者
git branch 列出本地分支
git branch -r 列出远程分支
git switch <branch> 切换分支
git switch -c <branch> 创建并切换分支
git branch -d <branch> 删除已合并分支
git merge <branch> 合并指定分支到当前分支
git rebase <branch> 将当前分支变基到目标分支
git cherry-pick <hash> 将指定提交应用到当前分支
git stash 暂存工作进度
git stash pop 恢复最近暂存
git restore <file> 丢弃工作区修改
git restore --staged <file> 取消暂存
git revert <hash> 安全撤销某次提交
git reset --soft HEAD~1 撤销提交,保留修改
git reset --hard HEAD~1 彻底回退一次提交
git remote -v 查看远程仓库
git remote add origin <url> 添加远程仓库
git fetch origin 拉取远程更新(不合并)
git pull origin <branch> 拉取并合并远程分支
git push origin <branch> 推送分支到远程
git push -u origin <branch> 推送并设置上游
git push origin --delete <branch> 删除远程分支
git tag v1.0.0 创建标签
git push origin v1.0.0 推送标签到远程

工作流一:日常开发(Feature Branch)

# 1. 每天开始:拉取最新
git checkout main
git pull origin main

# 2. 创建特性分支
git switch -c feature/my-feature

# 3. 开发 + 提交
git add .
git commit -m "feat: 添加某某功能"
git push -u origin feature/my-feature

# 4. 期间同步 main 的新变更(如果有)
git fetch origin
git rebase origin/main

# 5. 推送并提 PR → Code Review → 修改 → 合入

# 6. 合入后清理
git checkout main
git pull origin main
git branch -d feature/my-feature

工作流二:紧急修复(Hotfix)

# 1. 从 main 拉最新
git checkout main
git pull origin main

# 2. 创建修复分支
git switch -c hotfix/critical-bug

# 3. 修复 + 提交 + 推送
git add .
git commit -m "fix: 修复线上紧急问题"
git push -u origin hotfix/critical-bug

# 4. 提 PR → 加急 Review → 合入 main

# 5. 确认线上修复后清理
git checkout main
git pull origin main
git branch -d hotfix/critical-bug

工作流三:临时切换任务

# 开发到一半需要切去修 bug
git stash save "WIP: 正在开发用户模块"

# 切去修 bug...
git checkout main
git switch -c hotfix/xxx
# ... 修复、提交、提 PR、合入 ...

# 回到原任务
git checkout feature/my-feature
git stash pop

常用组合

# 看最近一周谁改了什么
git log --oneline --since="1 week ago" --author="你的名字"

# 撤销最近一次 commit(保留修改)
git reset --soft HEAD~1

# 把当前分支的提交整理成一条
git rebase -i HEAD~3       # 交互式整理最近 3 次提交

# 对比两个分支的差异
git diff main..feature/xxx

# 查看哪些文件在 main 上改了但我还没同步
git fetch origin
git diff HEAD..origin/main --name-only