主题
第三阶段:远程协作 & 第四阶段:进阶操作
第三阶段:远程协作
一、远程仓库操作
1.1 git remote — 管理远程仓库
远程仓库(Remote)是托管在网络上的项目副本(如 GitHub、GitLab、Gitee)。origin 是克隆时自动创建的默认远程名称。
bash
# 查看远程仓库列表
git remote
# 查看远程仓库详细信息(含 URL)
git remote -v
# origin https://github.com/user/repo.git (fetch)
# origin https://github.com/user/repo.git (push)
# 添加远程仓库
git remote add origin https://github.com/user/repo.git
# 添加第二个远程仓库(如公司内部 GitLab)
git remote add gitlab https://gitlab.company.com/user/repo.git
# 修改远程仓库 URL
git remote set-url origin https://github.com/user/new-repo.git
# 重命名远程仓库
git remote rename origin upstream
# 删除远程仓库关联
git remote remove gitlab
# 查看某个远程仓库的详细信息
git remote show origin远程仓库的本质:
本地 远程(GitHub)
┌──────────────┐ ┌──────────────┐
│ 工作区 │ │ │
│ 暂存区 │ push / pull │ origin │
│ 本地仓库 │ ◄════════════════► │ (远程仓库) │
│ │ │ │
│ origin/main │ ← 远程跟踪分支 │ main │
│ (只读快照) │ (上次同步状态) │ │
└──────────────┘ └──────────────┘远程跟踪分支 origin/main 是远程 main 在本地的只读镜像,只有 fetch/pull 时才会更新。
1.2 git push — 推送到远程
将本地提交推送到远程仓库。
bash
# 推送当前分支到远程同名分支
git push
# 首次推送,设置上游跟踪关系
git push -u origin main
# -u 等同于 --set-upstream
# 推送指定分支
git push origin feature/login
# 推送所有分支
git push --all origin
# 推送标签
git push origin v1.0.0
git push --tags # 推送所有标签
# 删除远程分支
git push origin --delete feature/old-branch
# 强制推送(危险!会覆盖远程历史)
git push --force
# 安全的强制推送(只有远程没被别人更新时才成功)
git push --force-with-lease--force vs --force-with-lease:
| 命令 | 行为 | 安全性 |
|---|---|---|
--force | 无条件覆盖远程 | 危险,可能丢失他人提交 |
--force-with-lease | 远程未被更新时才覆盖 | 相对安全,有保护机制 |
原则: 除非你非常清楚在做什么,否则永远不要使用
--force。个人分支可以用--force-with-lease。
1.3 git pull — 拉取并合并
从远程拉取最新代码并自动合并到当前分支。
bash
# 拉取并合并(默认行为)
git pull
# 等价于:
git fetch origin
git merge origin/main
# 拉取时使用 rebase(避免产生合并提交)
git pull --rebase
# 等价于:
git fetch origin
git rebase origin/main
# 拉取指定远程和分支
git pull origin develop推荐配置默认 pull 行为:
bash
# 全局配置:pull 时默认使用 rebase
git config --global pull.rebase true
# 或设为仅在快速合并时自动合并,否则报错
git config --global pull.ff only1.4 git fetch — 仅拉取不合并
从远程获取最新数据,但不会修改工作区。
bash
# 拉取所有远程分支的最新数据
git fetch
# 拉取指定远程
git fetch origin
# 拉取并清理本地已过期的远程跟踪分支
git fetch --prune
# 拉取所有远程仓库
git fetch --all1.5 git pull vs git fetch + git merge
git pull = git fetch + git merge(一步到位)| 操作 | git fetch + git merge | git pull |
|---|---|---|
| 控制力 | 高,可以先看变化再决定是否合并 | 低,自动合并 |
| 安全性 | 更安全,可先检查 | 可能产生意外合并 |
| 便捷性 | 需两步 | 一步完成 |
| 适用场景 | 谨慎操作、检查远程变化 | 快速同步、简单场景 |
推荐工作流:
bash
# 先看看远程有什么变化
git fetch origin
# 查看远程和本地的差异
git log HEAD..origin/main --oneline
# 确认没问题后再合并
git merge origin/main1.6 上游分支(Upstream)的设置与跟踪
上游分支是本地分支关联的远程分支,设置后 git push/git pull 不需要每次指定远程和分支名。
bash
# 推送时设置上游(最常用)
git push -u origin feature/login
# 之后在该分支上直接 git push / git pull 即可
# 手动设置上游
git branch --set-upstream-to=origin/main main
# 查看所有分支的跟踪关系
git branch -vv
# * main abc1234 [origin/main] 最近的提交信息
# feature/login def5678 [origin/feature/login: ahead 2] 提交信息
# ahead 2 表示本地比远程多 2 个提交
# behind 3 表示远程比本地多 3 个提交
# 取消上游关联
git branch --unset-upstream二、协作实践
2.1 Fork & Pull Request 工作流
这是开源项目最常用的协作模式。
原始仓库(upstream) 你的 Fork(origin) 本地仓库
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ user-a/repo │ ── Fork ─→│ your/repo │ ─ clone ─→│ 本地 │
│ │ │ │ │ │
│ main │ │ main │ │ main │
│ │◄── PR ─────│ feature │◄── push ──│ feature │
└──────────────┘ └──────────────┘ └──────────────┘完整流程:
bash
# ① 在 GitHub 上 Fork 原始仓库
# ② 克隆你的 Fork
git clone https://github.com/your-name/repo.git
cd repo
# ③ 添加原始仓库为 upstream
git remote add upstream https://github.com/original-owner/repo.git
git remote -v
# origin https://github.com/your-name/repo.git (fetch/push)
# upstream https://github.com/original-owner/repo.git (fetch/push)
# ④ 创建功能分支
git switch -c feature/improve-docs
# ⑤ 开发和提交
# ...编辑文件...
git add . && git commit -m "docs: 改进安装文档"
# ⑥ 推送到你的 Fork
git push -u origin feature/improve-docs
# ⑦ 在 GitHub 上创建 Pull Request(from your fork → upstream)
# ⑧ 同步上游最新代码(在 PR 被合并后,或开发过程中)
git fetch upstream
git switch main
git merge upstream/main
git push origin main # 同步你的 Fork2.2 Code Review 流程
Pull Request(PR)/ Merge Request(MR)是代码审查的核心机制。
提交 PR 的规范:
markdown
## PR 标题
feat: 添加用户登录功能
## 描述
- 实现了基于 JWT 的用户认证
- 添加了登录/注册 API
- 单元测试覆盖率 > 85%
## 变更类型
- [x] 新功能(feat)
- [ ] Bug 修复(fix)
- [ ] 文档更新(docs)
## 测试
- [x] 单元测试通过
- [x] 本地手动测试通过
## 截图(UI 相关变更)
无Review 要点:
| 维度 | 关注点 |
|---|---|
| 正确性 | 逻辑是否正确,边界条件是否处理 |
| 可读性 | 命名是否清晰,代码是否好理解 |
| 安全性 | 有无注入、越权、敏感信息泄露 |
| 性能 | 有无 N+1 查询、不必要的循环 |
| 测试 | 关键路径是否有测试覆盖 |
| 规范 | 是否符合团队编码规范 |
2.3 处理远程冲突
当你推送时发现远程有新提交(别人先推了),需要先拉取解决冲突。
bash
# 推送被拒绝
git push
# ! [rejected] main -> main (non-fast-forward)
# hint: Updates were rejected because the remote contains work that you do not have locally.
# 方式一:fetch + merge
git fetch origin
git merge origin/main
# 如果有冲突,手动解决后:
git add .
git commit -m "merge: 解决与远程的冲突"
git push
# 方式二:pull --rebase(推荐,历史更干净)
git pull --rebase
# 如果有冲突,解决后:
git add .
git rebase --continue
git push2.4 多人协作的注意事项
| 原则 | 说明 |
|---|---|
| 先 pull 再 push | 推送前先同步远程最新代码 |
| 不要在公共分支上 force push | 会覆盖他人工作 |
| 及时同步 | 每天至少同步一次主干 |
| 小步提交 | 频繁提交小变更比一次大提交好 |
| 分支保护 | main 分支设置保护规则,禁止直接推送 |
| 沟通先行 | 改公共文件前先和相关人员沟通 |
分支保护规则(GitHub 为例):
- 禁止直接推送到
main - 合并前必须通过 CI 检查
- 合并前必须有至少 1 人审批
- 要求分支保持最新(合并前先同步 main)
第四阶段:进阶操作
三、历史管理与回退
3.1 git reset — 重置提交
git reset 将 HEAD 移动到指定提交,根据参数不同,对暂存区和工作区的影响不同。
git reset 的三种模式
提交历史:C1 ← C2 ← C3 (HEAD)
执行:git reset C1
工作区 暂存区 提交历史
--soft 不变 不变 回到 C1(C2、C3 的改动保留在暂存区)
--mixed(默认) 不变 清空 回到 C1(C2、C3 的改动保留在工作区)
--hard 清空 清空 回到 C1(C2、C3 的改动全部丢失)bash
# 回退到上一个提交,保留修改在暂存区(适合重新组织提交)
git reset --soft HEAD~1
# 回退到上一个提交,保留修改在工作区(默认行为)
git reset HEAD~1
git reset --mixed HEAD~1
# 回退到上一个提交,丢弃所有修改(危险!不可逆)
git reset --hard HEAD~1
# 回退到指定提交
git reset --soft abc1234
# 回退多个提交
git reset --soft HEAD~3各模式适用场景:
| 模式 | 适用场景 |
|---|---|
--soft | 想把最近几次提交合并成一个提交 |
--mixed | 想重新选择哪些文件加入暂存区 |
--hard | 彻底放弃最近的修改,回到某个干净状态 |
⚠️ 警告:
git reset会改写历史。如果提交已推送到远程,不要使用 reset,请用git revert。
3.2 git revert — 安全回退
revert 创建一个新提交来"撤销"某次提交的改动,不会改写历史,对已推送的提交是安全的。
bash
# 撤销最近一次提交
git revert HEAD
# 撤销指定提交
git revert abc1234
# 撤销一个合并提交(-m 1 保留主分支内容)
git revert -m 1 <merge-commit-hash>
# 撤销多个连续提交
git revert HEAD~3..HEAD
# 撤销但不自动提交(方便一次 revert 多个后再统一提交)
git revert --no-commit abc1234
git revert --no-commit def5678
git commit -m "revert: 撤销 abc1234 和 def5678"git reset vs git revert 对比:
原始历史:C1 ← C2 ← C3
git reset --hard C1 的结果:
C1 (C2、C3 消失,历史被改写)
git revert C3 的结果:
C1 ← C2 ← C3 ← C3' (新增撤销提交,历史完整保留)| 特性 | git reset | git revert |
|---|---|---|
| 改写历史 | 是 | 否 |
| 已推送的提交 | 危险 | 安全 |
| 操作方式 | 移动 HEAD 指针 | 创建新的反向提交 |
| 适用场景 | 本地未推送的提交 | 已推送或公共分支 |
3.3 撤销工作区修改
bash
# ——— 新版命令(Git 2.23+,推荐) ———
# 撤销工作区的修改(恢复到暂存区或最新提交的版本)
git restore file.txt
# 撤销所有工作区修改
git restore .
# 从暂存区撤回(unstage),但保留工作区修改
git restore --staged file.txt
# 恢复到指定提交的版本
git restore --source=HEAD~2 file.txt
# ——— 旧版命令 ———
# 撤销工作区修改
git checkout -- file.txt
# 从暂存区撤回
git reset HEAD file.txt撤销操作速查图:
已提交 ← git reset/revert ← 已暂存 ← git restore --staged ← 已修改 ← git restore ← 未修改3.4 git reflog — 找回丢失的提交
reflog 记录了 HEAD 的每次移动,是你的"后悔药"。
bash
# 查看 HEAD 的移动历史
git reflog
# 输出示例:
# abc1234 HEAD@{0}: reset: moving to HEAD~1
# def5678 HEAD@{1}: commit: feat: 添加新功能
# ghi9012 HEAD@{2}: commit: fix: 修复 Bug
# ...
# 恢复到某个状态
git reset --hard def5678
# 基于某个 reflog 创建分支(用于检查恢复内容)
git branch recover-branch def5678reflog 的保存期限: 默认保留 90 天。超过后会被垃圾回收(git gc)清理。
安全网: 只要在 90 天内,几乎任何 Git 操作都可以通过
reflog找回。
四、变基与整理
4.1 git rebase — 变基操作
将当前分支的提交"重新播放"到目标分支的最新提交之上,生成线性历史。
bash
# 将当前分支变基到 main
git rebase main变基的过程:
变基前:
main
↓
A ← B ← E
↖
C ← D ← feature (HEAD)
git rebase main 的过程:
1. 找到共同祖先 B
2. 提取 feature 的提交(C、D)
3. 将它们依次重新应用到 main 的最新提交(E)之后
变基后:
main
↓
A ← B ← E ← C' ← D' ← feature (HEAD)
注意:C' 和 D' 是新的提交(hash 不同),内容与 C、D 相同bash
# 变基过程中遇到冲突
git rebase main
# CONFLICT ...
# 解决冲突后继续
git add .
git rebase --continue
# 跳过当前提交(放弃这个提交的改动)
git rebase --skip
# 放弃整个变基,恢复原状
git rebase --abort4.2 git rebase -i — 交互式变基
交互式变基可以修改、合并、删除、重排提交历史,是整理提交的利器。
bash
# 对最近 3 个提交进行交互式变基
git rebase -i HEAD~3执行后会打开编辑器,显示提交列表:
pick abc1234 feat: 添加用户模型
pick def5678 fix: 修复拼写错误
pick ghi9012 feat: 添加用户 API
# 可用的命令:
# p, pick = 保留该提交
# r, reword = 保留但修改提交信息
# e, edit = 保留但暂停让你修改内容
# s, squash = 合并到前一个提交(保留提交信息)
# f, fixup = 合并到前一个提交(丢弃提交信息)
# d, drop = 删除该提交常见场景:
bash
# 场景 1:合并多个提交为一个
# 将 def5678 和 ghi9012 合并到 abc1234
pick abc1234 feat: 添加用户模型
squash def5678 fix: 修复拼写错误
squash ghi9012 feat: 添加用户 API
# 保存后会让你编辑合并后的提交信息
# 场景 2:修改某次提交的提交信息
pick abc1234 feat: 添加用户模型
reword def5678 fix: 修复拼写错误 ← 改为 reword
pick ghi9012 feat: 添加用户 API
# 保存后会让你重新编辑 def5678 的提交信息
# 场景 3:删除某次提交
pick abc1234 feat: 添加用户模型
drop def5678 fix: 修复拼写错误 ← 改为 drop
pick ghi9012 feat: 添加用户 API
# 场景 4:调整提交顺序(直接调换行的顺序)
pick ghi9012 feat: 添加用户 API
pick abc1234 feat: 添加用户模型
pick def5678 fix: 修复拼写错误4.3 git rebase vs git merge 的选择
| 场景 | 推荐 | 原因 |
|---|---|---|
| 同步主干到个人功能分支 | rebase | 保持线性历史 |
| 功能分支合并回主干 | merge --no-ff | 保留分支语义 |
| 已推送的公共分支 | merge | 不改写共享历史 |
| 整理本地提交后推送 | rebase -i | 干净的提交历史 |
推荐工作流(rebase + merge):
bash
# 在功能分支上开发时,用 rebase 同步主干
git switch feature/login
git rebase main
# 功能完成后,用 merge --no-ff 合并回主干
git switch main
git merge --no-ff feature/login4.4 git cherry-pick — 选择性合并提交
将指定的提交"复制"到当前分支,不需要合并整个分支。
bash
# 选择单个提交
git cherry-pick abc1234
# 选择多个提交
git cherry-pick abc1234 def5678
# 选择一个范围(不含起点)
git cherry-pick A..B
# 选择一个范围(含起点)
git cherry-pick A^..B
# cherry-pick 但不自动提交
git cherry-pick --no-commit abc1234
# 遇到冲突时
git cherry-pick abc1234
# 解决冲突后
git add .
git cherry-pick --continue
# 或放弃
git cherry-pick --abort适用场景:
- 只需要另一个分支的某几个提交,不想合并整个分支
- 把某个 Bug 修复从一个版本分支移植到另一个版本分支
- 从已废弃的分支中拯救有价值的提交
4.5 git stash — 暂存未完成的工作
临时保存工作区和暂存区的修改,让工作目录变干净。
bash
# 暂存当前修改
git stash
# 暂存并添加说明(推荐)
git stash push -m "正在开发登录功能,临时切分支修 Bug"
# 暂存包括未跟踪的新文件
git stash -u
# 或
git stash --include-untracked
# 查看暂存列表
git stash list
# stash@{0}: On feature/login: 正在开发登录功能
# stash@{1}: WIP on main: abc1234 some commit
# 恢复最近一次暂存(并从列表中删除)
git stash pop
# 恢复最近一次暂存(保留在列表中)
git stash apply
# 恢复指定的暂存
git stash apply stash@{1}
# 查看某次暂存的内容
git stash show stash@{0}
git stash show -p stash@{0} # 显示详细 diff
# 删除某次暂存
git stash drop stash@{0}
# 清空所有暂存
git stash clear
# 基于暂存内容创建新分支
git stash branch new-branch stash@{0}stash 的典型使用场景:
bash
# 场景:正在开发功能,突然要切分支修 Bug
git stash push -m "feature/login 开发中"
git switch main
git switch -c hotfix/urgent-bug
# ...修复 Bug...
git add . && git commit -m "fix: 修复紧急 Bug"
git switch main && git merge --no-ff hotfix/urgent-bug
git switch feature/login
git stash pop # 恢复之前的工作五、标签管理
5.1 git tag — 创建标签
标签(Tag)是对某个提交的永久标记,通常用于标记发布版本。
两种标签类型:
| 类型 | 创建方式 | 内容 | 适用场景 |
|---|---|---|---|
| 轻量标签 | git tag v1.0 | 只是一个指针 | 临时标记 |
| 附注标签 | git tag -a v1.0 | 包含作者、日期、说明 | 正式发布 |
bash
# 创建轻量标签
git tag v1.0.0
# 创建附注标签(推荐用于发布)
git tag -a v1.0.0 -m "正式发布 1.0.0 版本"
# 给历史提交打标签
git tag -a v0.9.0 abc1234 -m "补打 0.9.0 标签"
# 查看所有标签
git tag
# 按模式筛选
git tag -l "v1.*"
# 查看标签详细信息
git show v1.0.0
# 删除本地标签
git tag -d v1.0.05.2 标签的推送与删除
bash
# 推送单个标签
git push origin v1.0.0
# 推送所有本地标签
git push origin --tags
# 删除远程标签
git push origin --delete v1.0.0
# 或
git push origin :refs/tags/v1.0.0
# 删除本地 + 远程标签
git tag -d v1.0.0
git push origin --delete v1.0.05.3 语义化版本(Semantic Versioning)
版本号格式:MAJOR.MINOR.PATCH
v2.1.3
│ │ │
│ │ └── PATCH:修复 Bug(向后兼容)
│ └──── MINOR:新增功能(向后兼容)
└────── MAJOR:破坏性变更(不向后兼容)版本号递增规则:
| 变更类型 | 版本递增 | 示例 |
|---|---|---|
| Bug 修复 | PATCH +1 | 1.0.0 → 1.0.1 |
| 新增功能(兼容) | MINOR +1,PATCH 归零 | 1.0.1 → 1.1.0 |
| 破坏性变更 | MAJOR +1,MINOR/PATCH 归零 | 1.1.0 → 2.0.0 |
预发布版本:
v1.0.0-alpha.1 # 内部测试版
v1.0.0-beta.1 # 公开测试版
v1.0.0-rc.1 # 发布候选版(Release Candidate)
v1.0.0 # 正式版用标签标记发布的完整流程:
bash
# 确保在 main 分支且代码最新
git switch main
git pull origin main
# 打标签
git tag -a v1.2.0 -m "Release v1.2.0: 新增搜索功能和性能优化"
# 推送标签
git push origin v1.2.0
# 在 GitHub 上基于标签创建 Release(可附带 changelog 和构建产物)六、实践 Demo
Demo 1:远程仓库的基本操作
bash
# ① 创建本地项目
mkdir remote-demo && cd remote-demo
git init
echo "# Remote Demo" > README.md
git add . && git commit -m "feat: 初始化项目"
# ② 模拟远程仓库(用本地裸仓库代替 GitHub)
cd ..
git init --bare remote-repo.git
# ③ 添加远程并推送
cd remote-demo
git remote add origin ../remote-repo.git
git push -u origin main
# ④ 模拟另一个开发者克隆
cd ..
git clone remote-repo.git developer-b
cd developer-b
# ⑤ 开发者 B 提交并推送
echo "New feature by B" > feature-b.txt
git add . && git commit -m "feat: B 添加新功能"
git push
# ⑥ 开发者 A 拉取 B 的修改
cd ../remote-demo
git pull
cat feature-b.txt # 能看到 B 的文件Demo 2:模拟远程冲突并解决
bash
# 基于 Demo 1 继续
# ① 开发者 A 修改 README
cd ../remote-demo
echo "Modified by A" >> README.md
git add . && git commit -m "docs: A 修改 README"
# ② 开发者 B 也修改 README 并先推送
cd ../developer-b
echo "Modified by B" >> README.md
git add . && git commit -m "docs: B 修改 README"
git push
# ③ 开发者 A 推送失败
cd ../remote-demo
git push
# 被拒绝!远程有新提交
# ④ 使用 pull --rebase 解决
git pull --rebase
# 如果有冲突,解决后:
# git add .
# git rebase --continue
# ⑤ 推送成功
git pushDemo 3:git reset 三种模式对比
bash
mkdir reset-demo && cd reset-demo
git init
# 准备三个提交
echo "v1" > file.txt && git add . && git commit -m "commit 1"
echo "v2" > file.txt && git add . && git commit -m "commit 2"
echo "v3" > file.txt && git add . && git commit -m "commit 3"
git log --oneline
# abc commit 3
# def commit 2
# ghi commit 1
# ——— 测试 --soft ———
git reset --soft HEAD~1
git status # file.txt 在暂存区,内容为 v3
git log --oneline # 只剩 commit 1 和 commit 2
# 恢复
git commit -m "commit 3 restored"
# ——— 测试 --mixed ———
git reset --mixed HEAD~1
git status # file.txt 已修改(未暂存),内容为 v3
git log --oneline # 只剩 commit 1 和 commit 2
# 恢复
git add . && git commit -m "commit 3 restored"
# ——— 测试 --hard ———
git reset --hard HEAD~1
git status # 干净,没有任何修改
cat file.txt # 内容回到 v2
git log --oneline # 只剩 commit 1 和 commit 2
# 通过 reflog 恢复
git reflog
git reset --hard HEAD@{1}Demo 4:交互式变基整理提交
bash
mkdir rebase-demo && cd rebase-demo
git init
# 准备一些零碎的提交
echo "feature" > feature.txt && git add . && git commit -m "feat: 开始新功能"
echo "typo fix" >> feature.txt && git add . && git commit -m "fix: 修复拼写"
echo "more work" >> feature.txt && git add . && git commit -m "wip: 继续开发"
echo "done" >> feature.txt && git add . && git commit -m "feat: 完成功能"
git log --oneline
# 4 个零碎提交
# 交互式变基,合并这些提交
git rebase -i HEAD~4
# 在编辑器中设置:
# pick xxx feat: 开始新功能
# fixup xxx fix: 修复拼写
# fixup xxx wip: 继续开发
# fixup xxx feat: 完成功能
# 保存退出
git log --oneline
# 变成 1 个干净的提交Demo 5:git stash 实战
bash
mkdir stash-demo && cd stash-demo
git init
echo "base" > app.js
git add . && git commit -m "feat: 初始化"
# ① 正在开发新功能
echo "new feature code..." >> app.js
echo "config" > config.json
# ② 突然需要修紧急 Bug
git stash push -u -m "feature/search 开发中"
# ③ 工作区变干净了
git status # clean
cat app.js # 只有 base
# ④ 修复 Bug
git switch -c hotfix/urgent
echo "// bug fixed" >> app.js
git add . && git commit -m "fix: 修复紧急问题"
# ⑤ 回来继续开发
git switch main
git merge --no-ff hotfix/urgent
git branch -d hotfix/urgent
# ⑥ 恢复之前的工作
git stash list
git stash pop
cat app.js # 恢复了开发中的内容
ls config.json # 未跟踪的新文件也回来了Demo 6:标签管理实战
bash
mkdir tag-demo && cd tag-demo
git init
echo "v1" > app.js && git add . && git commit -m "feat: v1.0.0 发布"
git tag -a v1.0.0 -m "Release v1.0.0: 初始版本"
echo "v1.1" > app.js && git add . && git commit -m "feat: 添加搜索功能"
git tag -a v1.1.0 -m "Release v1.1.0: 新增搜索功能"
echo "v1.1.1" > app.js && git add . && git commit -m "fix: 修复搜索 Bug"
git tag -a v1.1.1 -m "Release v1.1.1: 修复搜索 Bug"
# 查看所有标签
git tag
# 查看标签详细信息
git show v1.0.0
# 查看标签和提交的关系
git log --oneline --decorate
# 切换到某个标签查看历史版本代码
git checkout v1.0.0
cat app.js # v1
git switch - # 切回来七、常见问题 Q&A
Q1:git pull 时出现 "divergent branches" 警告怎么办?
A: Git 2.27+ 要求你明确选择 pull 策略:
bash
# 方式一:配置默认使用 merge(传统行为)
git config --global pull.rebase false
# 方式二:配置默认使用 rebase(推荐)
git config --global pull.rebase true
# 方式三:只允许快速合并,否则报错
git config --global pull.ff onlyQ2:push 被拒绝,提示 "non-fast-forward" 是什么意思?
A: 远程分支上有你本地没有的提交。需要先拉取合并再推送:
bash
# 推荐
git pull --rebase
git push
# 或
git fetch origin
git merge origin/main
git push⚠️ 不要用
--force,除非你确定要覆盖远程。
Q3:git reset --hard 后代码消失了怎么恢复?
A: 用 reflog 找回:
bash
# 查看操作历史
git reflog
# 找到 reset 之前的提交 hash
git reset --hard <之前的hash>只要在 90 天内,reflog 里都有记录。
Q4:git rebase 过程中冲突太多不想继续了怎么办?
A:
bash
# 放弃 rebase,恢复到执行 rebase 之前的状态
git rebase --abort完全不会有任何副作用,分支恢复到 rebase 前的状态。
Q5:git stash 恢复时有冲突怎么办?
A: git stash pop 冲突时,stash 不会被自动删除:
bash
# pop 失败后,手动解决冲突
# 编辑冲突文件...
git add .
# 手动删除已应用的 stash
git stash drop stash@{0}Q6:如何只推送某个标签而不推送所有标签?
A:
bash
# 只推单个标签
git push origin v1.2.0
# 不要用 --tags(会推送所有本地标签)Q7:git revert 合并提交时为什么需要 -m 参数?
A: 合并提交有两个父提交,Git 不知道你想保留哪一边的内容:
bash
# -m 1 表示保留第一个父提交(通常是主分支)
git revert -m 1 <merge-commit>
# -m 2 表示保留第二个父提交(通常是被合并的分支)
git revert -m 2 <merge-commit>查看合并提交的父提交:
bash
git log --oneline --graph
# 或
git cat-file -p <merge-commit>Q8:cherry-pick 和 merge 有什么区别?
A:
| 特性 | cherry-pick | merge |
|---|---|---|
| 粒度 | 选取单个/几个提交 | 合并整个分支 |
| 历史关系 | 不建立分支关联 | 建立合并关系 |
| 提交 hash | 创建新的提交(新 hash) | 保留原提交或创建合并提交 |
| 典型场景 | 移植某个修复到其他分支 | 功能分支合并回主干 |
Q9:远程仓库 HTTPS 和 SSH 该选哪个?
A:
| 协议 | 认证方式 | 适用场景 |
|---|---|---|
| HTTPS | 用户名+密码/Token | 简单、防火墙友好、临时使用 |
| SSH | SSH Key(密钥对) | 免密、安全、长期使用推荐 |
配置 SSH Key:
bash
# 生成密钥对
ssh-keygen -t ed25519 -C "your@email.com"
# 查看公钥(添加到 GitHub/GitLab 设置中)
cat ~/.ssh/id_ed25519.pub
# 测试连接
ssh -T git@github.comQ10:git fetch --prune 和 git remote prune origin 有什么区别?
A: 功能类似,都是清理本地已过期的远程跟踪分支:
bash
# fetch --prune:拉取最新数据 + 清理
git fetch --prune
# remote prune:只清理,不拉取
git remote prune origin
# 配置每次 fetch 自动清理
git config --global fetch.prune true八、操作速查表
┌──────────────────────────────────────────────────────────────────┐
│ 远程协作 & 进阶操作速查 │
├───────────────────────────────┬──────────────────────────────────┤
│ 远程操作 │ │
├───────────────────────────────┼──────────────────────────────────┤
│ git remote -v │ 查看远程仓库 │
│ git remote add <name> <url> │ 添加远程 │
│ git push -u origin <branch> │ 推送并设置上游 │
│ git pull --rebase │ 拉取(rebase 方式) │
│ git fetch │ 仅拉取不合并 │
│ git push --force-with-lease │ 安全的强制推送 │
├───────────────────────────────┼──────────────────────────────────┤
│ 历史管理 │ │
├───────────────────────────────┼──────────────────────────────────┤
│ git reset --soft HEAD~1 │ 回退提交,改动留在暂存区 │
│ git reset --hard HEAD~1 │ 回退提交,丢弃所有改动 │
│ git revert <commit> │ 安全地撤销某次提交 │
│ git reflog │ 查看所有操作记录 │
│ git restore <file> │ 撤销工作区修改 │
│ git restore --staged <file> │ 从暂存区撤回 │
├───────────────────────────────┼──────────────────────────────────┤
│ 变基与整理 │ │
├───────────────────────────────┼──────────────────────────────────┤
│ git rebase main │ 变基到 main │
│ git rebase -i HEAD~N │ 交互式变基 │
│ git rebase --abort │ 放弃变基 │
│ git cherry-pick <commit> │ 选择性合并提交 │
│ git stash push -m "msg" │ 暂存修改 │
│ git stash pop │ 恢复暂存 │
├───────────────────────────────┼──────────────────────────────────┤
│ 标签管理 │ │
├───────────────────────────────┼──────────────────────────────────┤
│ git tag -a v1.0 -m "msg" │ 创建附注标签 │
│ git push origin v1.0 │ 推送标签 │
│ git push origin --delete v1.0 │ 删除远程标签 │
└───────────────────────────────┴──────────────────────────────────┘九、学习检查清单
完成以下任务,确认你已掌握这两个阶段的内容:
远程协作:
- [ ] 能使用
git remote管理多个远程仓库 - [ ] 能说出
git pull和git fetch + merge的区别 - [ ] 能完成一次 Fork + PR 的完整流程
- [ ] 能处理推送被拒绝的情况
- [ ] 能使用
git branch -vv查看上游跟踪关系
历史管理:
- [ ] 能区分
git reset的三种模式及各自适用场景 - [ ] 能区分
git reset和git revert的使用场景 - [ ] 能使用
git reflog找回误删的提交
变基与整理:
- [ ] 能使用
git rebase保持线性历史 - [ ] 能使用
git rebase -i合并多个零碎提交 - [ ] 能使用
git cherry-pick移植特定提交 - [ ] 能使用
git stash管理临时工作
标签管理:
- [ ] 能创建附注标签并推送到远程
- [ ] 能说出语义化版本的递增规则