Skip to content

第三阶段:远程协作 & 第四阶段:进阶操作


第三阶段:远程协作

一、远程仓库操作

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 only

1.4 git fetch — 仅拉取不合并

从远程获取最新数据,但不会修改工作区。

bash
# 拉取所有远程分支的最新数据
git fetch

# 拉取指定远程
git fetch origin

# 拉取并清理本地已过期的远程跟踪分支
git fetch --prune

# 拉取所有远程仓库
git fetch --all

1.5 git pull vs git fetch + git merge

git pull = git fetch + git merge(一步到位)
操作git fetch + git mergegit pull
控制力高,可以先看变化再决定是否合并低,自动合并
安全性更安全,可先检查可能产生意外合并
便捷性需两步一步完成
适用场景谨慎操作、检查远程变化快速同步、简单场景

推荐工作流:

bash
# 先看看远程有什么变化
git fetch origin

# 查看远程和本地的差异
git log HEAD..origin/main --oneline

# 确认没问题后再合并
git merge origin/main

1.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    # 同步你的 Fork

2.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 push

2.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 resetgit 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 def5678

reflog 的保存期限: 默认保留 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 --abort

4.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/login

4.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.0

5.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.0

5.3 语义化版本(Semantic Versioning)

版本号格式:MAJOR.MINOR.PATCH

  v2.1.3
  │ │ │
  │ │ └── PATCH:修复 Bug(向后兼容)
  │ └──── MINOR:新增功能(向后兼容)
  └────── MAJOR:破坏性变更(不向后兼容)

版本号递增规则:

变更类型版本递增示例
Bug 修复PATCH +11.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 push

Demo 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 only

Q2: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-pickmerge 有什么区别?

A:

特性cherry-pickmerge
粒度选取单个/几个提交合并整个分支
历史关系不建立分支关联建立合并关系
提交 hash创建新的提交(新 hash)保留原提交或创建合并提交
典型场景移植某个修复到其他分支功能分支合并回主干

Q9:远程仓库 HTTPS 和 SSH 该选哪个?

A:

协议认证方式适用场景
HTTPS用户名+密码/Token简单、防火墙友好、临时使用
SSHSSH Key(密钥对)免密、安全、长期使用推荐

配置 SSH Key:

bash
# 生成密钥对
ssh-keygen -t ed25519 -C "your@email.com"

# 查看公钥(添加到 GitHub/GitLab 设置中)
cat ~/.ssh/id_ed25519.pub

# 测试连接
ssh -T git@github.com

Q10:git fetch --prunegit 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 pullgit fetch + merge 的区别
  • [ ] 能完成一次 Fork + PR 的完整流程
  • [ ] 能处理推送被拒绝的情况
  • [ ] 能使用 git branch -vv 查看上游跟踪关系

历史管理:

  • [ ] 能区分 git reset 的三种模式及各自适用场景
  • [ ] 能区分 git resetgit revert 的使用场景
  • [ ] 能使用 git reflog 找回误删的提交

变基与整理:

  • [ ] 能使用 git rebase 保持线性历史
  • [ ] 能使用 git rebase -i 合并多个零碎提交
  • [ ] 能使用 git cherry-pick 移植特定提交
  • [ ] 能使用 git stash 管理临时工作

标签管理:

  • [ ] 能创建附注标签并推送到远程
  • [ ] 能说出语义化版本的递增规则