20 — 远程进阶:强制推送的保险绳——force-with-lease

20 --- 远程进阶:强制推送的保险绳------force-with-lease

20 --- 远程进阶:强制推送的保险绳------force-with-lease

文章摘要: 本文深入讲解 Git 远程协作中的高级场景,重点解决推送被拒(non-fast-forward)问题。通过信箱类比解释非快进本质,区分 fetch 与 pull 的差异,介绍 pull --rebase 保持历史干净的方法。核心讲解强制推送的安全实践:对比 --force 的危险性与 --force-with-lease 的保险机制,详解远程跟踪分支原理。涵盖多远程配置、三角工作流、命令分组对照、安全习惯及真实场景应对。最后提供动手实验和常见问题解答,帮助读者建立安全的远程协作工作流。


写在前面:这一章要解决什么

你兴冲冲写完代码,git push 一敲------

css 复制代码
! [rejected]        main -> main (non-fast-forward)

推送被拒了。心里一慌:别人的代码是不是被我覆盖了?还是我落后了?要不要 --force 强推?强推会不会出事?

这一章就是帮你搞明白:推送为什么会失败,失败了该怎么做,以及怎么在必须强推时给自己拴一根保险绳。

学完后,你应该能:

  1. 用大白话解释「非快进」是什么意思
  2. 说清 fetchpull 的区别
  3. 知道什么时候该 git pull --rebase
  4. 理解 --force 为什么危险、--force-with-lease 为什么更安全
  5. 看懂「远程跟踪分支」在干什么
  6. 配置多个远程仓库,走通「三角工作流」

读者设定: 大一同学,已经学过基本远程协作(06 章),想搞懂推送被拒和强制推送那些事。


1. 定位:为什么要单开一章讲远程进阶

1.1 一句话先记住

推送被拒 = 你想往信箱塞信,但信箱里已经有别人的信了。

你出门前看了一眼信箱是空的,结果塞信的时候发现别人已经塞了一封进去。你的信和别人的信冲突了,邮局不帮你硬塞------这就是「非快进」。

1.2 没有这些知识会怎样

场景 不知道怎么办 知道以后
push 被拒 懵了,乱敲命令 知道先拉再推
想强推覆盖 --force 一敲,同事代码没了 --force-with-lease 安全兜底
fetch 和 pull 分不清 每次都 pull,经常出合并提交 按需选择,历史更干净
多人改同一分支 互相覆盖,越推越乱 理解远程跟踪分支,有条有理

1.3 生活里的类比

你已经会的 远程进阶对应
往信箱塞信 push
信箱里已经有别人的信,塞不进去 非快进(non-fast-forward)
先把信箱里的信取出来看看,再塞自己的 fetch + merge / pull
强行把别人的信拿出来换自己的 force push(危险!)
换之前先确认信箱里还是你上次看到的那几封 force-with-lease(有保险绳)

1.4 本章内容目标

目标 你能做到
理解非快进 说清「远程有新提交,你本地没有」
区分命令 fetch 只下载,pull = fetch + 整合
安全强推 --force-with-lease 代替 --force
看懂跟踪分支 解释 origin/main 是什么
多远程协作 配置 upstream,走通三角工作流

2. 本质:推送被拒到底发生了什么

2.1 一张图先建立感觉

图:本地与远程之间的同步------fetch 把远程的提交拉到本地,push 把本地的提交推到远程。

图:分布式版本控制------每个人都有完整副本,通过远程仓库交换改动。

2.2 用白话拆开

  1. 远程仓库 是大家共享的「中心信箱」,所有人的代码最终汇总在那。
  2. 远程跟踪分支 (比如 origin/main)是你本地对远程分支的「缓存快照」------上次 fetch 的时候远程是什么样。
  3. 推送 是把你本地的提交上传到远程。
  4. 非快进 是远程比你本地记录的多了新提交,你的提交不是在远程最新提交「之后」的,所以 Git 拒绝你。

2.3 非快进到底是什么

用信箱类比:

css 复制代码
你上次看信箱:信 A(你的本地记录 origin/main 指向 A)

现在信箱里:信 A → 信 B(别人在你不知道的时候推了 B)

你要塞:信 A → 信 C(你的本地 main 指向 C,C 的前驱是 A)

问题:信 C 的前驱是 A,但信箱里 A 后面已经有了 B。
      你的信 C 没有包含 B 的内容,如果硬塞,B 就丢了。

Git 拒绝了,这就是「非快进」(non-fast-forward)。它是在保护远程上别人的代码不被你的推送覆盖。

2.4 fetch vs pull:关键区别

命令 干什么 动了哪些指针
git fetch 只下载远程的提交和对象 只更新 origin/main 等「远程跟踪分支」
git pull fetch + 自动合并 更新远程跟踪分支 + 合并到你的本地分支

白话: fetch 是「先看看信箱里有啥新信」,pull 是「看完了还自动归档到你的文件夹里」。

大多数时候你想先 fetch 看看情况,再决定怎么整合。pull 一步到位,但可能在你没准备好的时候制造合并提交。

2.5 几条硬规矩

规矩 白话
远程跟踪分支你改不了 origin/main 是 fetch 更新的,不能手动提交到上面
push 前先 fetch 看看远程有没有新东西
永远不要对共享分支用 --force 别人的提交会被你覆盖
--force-with-lease 只用于自己的功能分支 它是保险绳,不是通行证
push 被拒是 Git 在保护你 别慌,先拉再推

2.6 新手最常踩的坑

表现 办法
push 被拒就 --force 别人的提交没了 fetch + merge/rebase,实在要强推用 --force-with-lease
分不清 fetch 和 pull pull 出一堆合并提交 想先看看就用 fetch,想一步到位再用 pull
不知道 origin/main 是什么 以为是远程上的分支,其实是本地缓存的快照 记住:origin/main 是本地对远程的「记忆」
改了已推送的提交又推 被拒,因为历史变了 变基后用 --force-with-lease
多人推同一分支 互相覆盖 各开功能分支,走 PR/MR 流程

3. 建议学习顺序

text 复制代码
理解非快进 → fetch 和 pull 的区别 → pull --rebase → 远程跟踪分支 → force-with-lease → 多远程 → 三角工作流

4. 动手准备

bash 复制代码
git --version
text 复制代码
git version 2.43.0

准备一个可以随便折腾的目录。演示身份:Ada Example ada@example.com

下面我们在本地模拟一个「远程」,这样不用真连网络也能练。

bash 复制代码
# 1. 建一个「假远程」仓库
mkdir -p /tmp/fake-remote.git
cd /tmp/fake-remote.git
git init --bare -b main

# 2. 建你的工作仓库
mkdir -p /tmp/my-work
cd /tmp/my-work
git init -b main
git config user.name "Ada Example"
git config user.email "ada@example.com"

# 3. 加个远程指向假远程
git remote add origin /tmp/fake-remote.git

# 4. 提交一个初始版本
printf 'v1\n' > file.txt
git add file.txt
git commit -m "init: 第一个版本"

# 5. 推上去
git push -u origin main
text 复制代码
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 221 bytes | 221.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To /tmp/fake-remote.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

白话翻译: Git 把 3 个对象推到了远程,远程上有了 main 分支,本地 main 也设置跟踪 origin/main 了。


5. 跟着做:一次完整远程进阶实验

5.1 制造非快进:模拟别人先推了代码

在另一个目录模拟「别人的提交」:

bash 复制代码
# 建一个「别人的」工作区
mkdir -p /tmp/other-work
cd /tmp/other-work
git clone /tmp/fake-remote.git .
git config user.name "Bob Example"
git config user.email "bob@example.com"
printf 'v2 from Bob\n' > file.txt
git add file.txt
git commit -m "feat: Bob 的修改"
git push
text 复制代码
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 240 bytes | 240.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To /tmp/fake-remote.git
   a1b2c3d..e4f5g6h  main -> main

白话翻译: Bob 的提交已经推到远程了,远程 main 现在指向 Bob 的提交。

回到你的工作区,在不知情的情况下也做提交并推送:

bash 复制代码
cd /tmp/my-work
printf 'v2 from Ada\n' > file.txt
git add file.txt
git commit -m "feat: Ada 的修改"
git push
text 复制代码
To /tmp/fake-remote.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '/tmp/fake-remote.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.

白话翻译: 推送被拒了!因为你本地的 main 落后于远程了------远程有 Bob 的提交,你本地没有。Git 让你先整合远程的改动。

这就是非快进。你遇到了。

5.2 先 fetch 看看远程有什么

bash 复制代码
git fetch
text 复制代码
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From /tmp/fake-remote
   a1b2c3d..e4f5g6h  main       -> origin/main

白话翻译: fetch 把远程的提交下载到了本地,更新了 origin/main,但没有动你的工作区和 main 分支。

看看现在的状态:

bash 复制代码
git log --oneline --graph --all
text 复制代码
* e4f5g6h (origin/main) feat: Bob 的修改
| * 7a8b9c0 (HEAD -> main) feat: Ada 的修改
|/
* a1b2c3d init: 第一个版本

怎么读: 从共同起点 a1b2c3d 分叉了------远程 origin/main 指向 Bob 的提交,你本地 main 指向你的提交。

5.3 用 pull 整合(merge 方式)

bash 复制代码
git pull
text 复制代码
Merge made by the 'ort' strategy.
 file.txt | 1 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

白话翻译: git pull = git fetch + git merge origin/main。它自动把远程的改动合并进来,产生了一个合并提交。

看图:

bash 复制代码
git log --oneline --graph --all
text 复制代码
*   1a2b3c4 (HEAD -> main) Merge remote-tracking branch 'origin/main'
|\
| * e4f5g6h (origin/main) feat: Bob 的修改
* | 7a8b9c0 feat: Ada 的修改
|/
* a1b2c3d init: 第一个版本

注意: 出了合并提交。如果你的团队偏好干净历史,可以考虑用 --rebase

这次因为两边改了同一个文件同一行,pull 时会出现冲突。解决方式和之前分支合并一模一样------编辑文件、删标记、add、提交。

冲突解决后推送:

bash 复制代码
git push
text 复制代码
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 350 bytes | 350.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To /tmp/fake-remote.git
   e4f5g6h..1a2b3c4  main -> main

白话翻译: 推上去了。现在远程也有你的提交了。

5.4 用 pull --rebase 代替 pull

如果你想避免合并提交,让历史保持一条直线,用 --rebase

先把场景重置------假设你又遇到非快进,这次用 rebase 方式:

bash 复制代码
git fetch
git pull --rebase
text 复制代码
Rebasing 1 commit onto e4f5g6h
Successfully rebasing and applied 1 commit.

白话翻译: Git 把你的提交「搬」到了远程最新提交的后面,而不是合一个合并提交。历史是一条直线。

bash 复制代码
git log --oneline --graph --all
text 复制代码
* 5d6e7f8 (HEAD -> main) feat: Ada 的修改
* e4f5g6h (origin/main) feat: Bob 的修改
* a1b2c3d init: 第一个版本

对比:

方式 历史形状 命令
git pull 出现分叉 ` ` 和合并提交 git pull
git pull --rebase 一条直线 git pull --rebase

很多团队约定用 --rebase,这样 git log 更干净。你可以设置默认行为:

bash 复制代码
# 让 pull 默认用 rebase
git config pull.rebase true
text 复制代码
(无输出,设置已保存。)

白话翻译: 以后 git pull 就自动用 rebase 模式了,不用每次手动加 --rebase

5.5 远程跟踪分支是什么

每次 git fetch 后,Git 会在本地记录「远程每个分支当前指向哪」。这些记录就是「远程跟踪分支」。

bash 复制代码
git branch -r
text 复制代码
  origin/main

白话翻译: -r 列出所有远程跟踪分支。origin/main 就是你本地对「origin 上的 main 分支」的最新记忆。

注意:远程跟踪分支是 只读的 ------你不能直接提交到 origin/main 上,它只在 git fetch 时被动更新。

bash 复制代码
git branch -vv
text 复制代码
* main 5d6e7f8 [origin/main] feat: Ada 的修改

白话翻译: -vv 显示本地分支和它跟踪的远程分支的对应关系。你的 main 跟踪 origin/main,目前是同步的。

5.6 --force 为什么危险

假设你做了一个变基(rebase),改变了已经推送过的提交的历史:

bash 复制代码
printf 'rewritten\n' > file.txt
git add file.txt
git commit --amend -m "feat: 重写提交信息"

现在推送:

bash 复制代码
git push
text 复制代码
To /tmp/fake-remote.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '/tmp/fake-remote.git'

被拒了------因为你改写了历史,远程那边的提交和你本下的提交不一样了。

如果你用 --force

bash 复制代码
git push --force
text 复制代码
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To /tmp/fake-remote.git
 + e4f5g6h...9h0i1j2 main -> main (forced update)

成功了------但看看那个 + 号。 它表示远程的提交被你覆盖了。如果 Bob 在这段时间又推了新提交,那些提交就被你抹掉了,找都找不回来(除非 Bob 本地还有)。

白话翻译: --force 就是不管信箱里有什么,全扔掉换成你的。如果别人在信箱里放了信,那些信就没了。

5.7 --force-with-lease:保险绳

--force-with-lease 的逻辑是:

我知道我要强推,但 前提是远程还是我看到的那样子。如果远程有我不知道的新提交,就拒绝。

继续上面的场景,先 fetch 一下确认状态:

bash 复制代码
git fetch
git push --force-with-lease
text 复制代码
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To /tmp/fake-remote.git
 + e4f5g6h...9h0i1j2 main -> main (forced update)

推成功了。因为远程确实是你 fetch 时的状态,没有别人的新提交。

现在来模拟危险情况------Bob 又推了新提交:

bash 复制代码
cd /tmp/other-work
printf 'v3 from Bob\n' > file.txt
git add file.txt
git commit -m "feat: Bob 又推了"
git pull --rebase
git push

回到你的仓库,不 fetch 直接强推:

bash 复制代码
cd /tmp/my-work
git push --force-with-lease
text 复制代码
To /tmp/fake-remote.git
 ! [rejected]        main -> main (stale info)
error: failed to push some refs to '/tmp/fake-remote.git'

被拒了------这正是你想要的!

白话翻译: Git 发现远程已经变了(有 Bob 的新提交),但你本地的「记忆」(origin/main)还是旧的,所以拒绝推送。这就是保险绳------它挡住了你覆盖别人代码的操作。

正确的做法:先 fetch,处理完再推。

5.8 远程管理:查看、添加、删除

bash 复制代码
# 查看所有远程
git remote -v
text 复制代码
origin  /tmp/fake-remote.git (fetch)
origin  /tmp/fake-remote.git (push)

白话翻译: -v 显示远程的名字和地址。每个远程有 fetch 和 push 两个地址(大多数时候是一样的)。

bash 复制代码
# 重命名远程
git remote rename origin upstream

# 再改回来
git remote rename upstream origin
text 复制代码
(无输出,重命名成功。)
bash 复制代码
# 删除远程
git remote remove origin
text 复制代码
(无输出,删除成功。本地不再关联这个远程了。)
bash 复制代码
# 加回来
git remote add origin /tmp/fake-remote.git
text 复制代码
(无输出,添加成功。)

5.9 多远程与三角工作流

在开源贡献和课程协作中,常见的工作流是「三角工作流」:

  1. fork 了老师的仓库到自己的账号(叫 origin
  2. 老师的仓库叫 upstream
  3. 你从 upstream 拉更新,往 origin 推代码
  4. origin 发 PR 给 upstream
bash 复制代码
# 添加你自己的 fork 为 origin
git remote add origin https://github.com/你的用户名/课件作业.git

# 添加老师的仓库为 upstream
git remote add upstream https://github.com/老师用户名/课件作业.git

# 确认一下
git remote -v
text 复制代码
origin    https://github.com/你的用户名/课件作业.git (fetch)
origin    https://github.com/你的用户名/课件作业.git (push)
upstream  https://github.com/老师用户名/课件作业.git (fetch)
upstream  https://github.com/老师用户名/课件作业.git (push)

白话翻译: 现在你有两个远程。origin 是你自己的,有推送权限;upstream 是老师的,只能拉不能推。

bash 复制代码
# 从 upstream 拉最新内容
git fetch upstream

# 把 upstream/main 合并到你的本地
git merge upstream/main

# 推到你的 origin
git push origin main
text 复制代码
(正常推送输出,略。)

这就是三角工作流的日常:从 upstream 取,往 origin 推。

如果你想省点打字,可以设置上游分支:

bash 复制代码
# 让本地 main 跟踪 origin/main(而不是 upstream/main)
git branch --set-upstream-to=origin/main main
text 复制代码
branch 'main' set up to track 'origin/main'.

6. 命令按「用途」分组

6.1 拉取与整合

命令 干什么
git fetch 下载远程更新,不动本地分支
git fetch --all 下载所有远程的更新
git pull fetch + merge
git pull --rebase fetch + rebase(推荐)
git config pull.rebase true 让 pull 默认用 rebase

6.2 推送

命令 干什么
git push 正常推送
git push -u origin main 推送并设置上游跟踪
git push --force 强制推送(危险,会覆盖别人的提交)
git push --force-with-lease 安全强推(远程变了就拒绝)

6.3 远程管理

命令 干什么
git remote -v 列出所有远程及其地址
git remote add 名字 地址 添加远程
git remote remove 名字 删除远程
git remote rename 旧名 新名 重命名远程

6.4 查看跟踪关系

命令 干什么
git branch -r 列出远程跟踪分支
git branch -vv 列出本地分支及其跟踪关系
git branch --set-upstream-to=origin/main main 设置上游跟踪

7. 对照表:降低记忆负担

7.1 push 被拒时怎么办

情况 你想怎么做 命令
远程有新提交,我落后了 先整合再推 git pull --rebasegit push
我改写了历史(自己的分支) 安全强推 git push --force-with-lease
我改写了历史(共享分支) 别推 revert 代替,或和团队沟通
推上去就行,不关心历史 不推荐但可以 git push --force(极度危险)

7.2 fetch vs pull vs pull --rebase

命令 下了代码? 合并了? 历史形状
git fetch 不变
git pull 是(merge) 多一个合并提交
git pull --rebase 是(rebase) 一条直线

7.3 --force vs --force-with-lease

命令 会覆盖别人的提交吗? 安全吗?
git push --force 会,不检查 危险
git push --force-with-lease 远程有新提交就拒绝 有保险绳

7.4 远程跟踪分支 vs 本地分支

概念 例子 谁更新它 你能直接提交到上面吗
远程跟踪分支 origin/main git fetch 不能
本地分支 main 你自己

8. 安全习惯

8.1 建议这样做

习惯 原因
push 前先 git fetch 看看远程有没有新东西
设置 pull.rebase true 避免无意义的合并提交
--force-with-lease 代替 --force 多一层保护
--force-with-lease 只在自己的功能分支用 共享分支不要强推
强推前先 fetch 确保 origin/main 是最新的 保险绳才有效
push 后用 git log 确认 确保推了你想推的

8.2 绝对不要做的事

操作 后果
对共享分支 --force 别人的提交丢失
fetch--force-with-lease 保险绳可能因为本地记忆过期而误判
推送包含密钥的提交 密钥泄露,即使删了也在历史里

8.3 推荐工作流(团队协作)

text 复制代码
1. 开始干活前:git fetch
2. 整合远程:git pull --rebase
3. 开分支改代码、提交
4. 推之前再 fetch 一次:git fetch
5. 正常推送:git push
6. 如果改写了历史(自己的功能分支):git push --force-with-lease

9. 真实场景

9.1 课程作业:和同学共用一个仓库

  • 各自开功能分支,不直接推 main
  • 提 PR 让助教或组长审
  • 审过了再合并
  • 如果 push 被拒,git pull --rebase 再推

9.2 开源贡献:三角工作流

  1. fork 项目到自己的账号
  2. git remote add upstream 原仓库地址
  3. git fetch upstreamgit checkout -b fix/拼写错误 upstream/main
  4. 改好提交,git push -u origin fix/拼写错误
  5. 在 GitHub 上从你的 fork 发 PR 到 upstream

9.3 推送被拒但急着想推

bash 复制代码
# 第一步:别慌,先看看远程有什么
git fetch
git log --oneline origin/main..HEAD
text 复制代码
7a8b9c0 feat: 我的提交

白话翻译: 这条命令显示「你本地有但远程没有」的提交。确认都是你的提交后:

bash 复制代码
git pull --rebase
git push

9.4 变基后要强推

你在自己的功能分支上做了 git rebase,推送被拒了:

bash 复制代码
git fetch
git push --force-with-lease origin feature/my-feature

只对你的功能分支用 --force-with-lease,不要对 main 用。

9.5 误推了敏感信息

bash 复制代码
# 立刻删掉文件并提交
git rm 敏感文件.txt
git commit -m "chore: 删除误推的敏感文件"

# 但它还在历史里!需要彻底重写才行,那是 16 章的内容
# 紧急情况下:让所有人别拉,重写历史后 --force-with-lease 推上去

10. 稍微多懂一点点(可选)

  1. 远程跟踪分支存在哪: .git/refs/remotes/origin/main,存的就是一个提交哈希,和本地分支(.git/refs/heads/main)一样的格式,只不过由 fetch 更新。
  2. --force-with-lease 的原理: 它在推的时候把本地的 origin/main 哈希和远程实际最新的哈希做比较,不一样就拒绝。所以你必须在 --force-with-lease 之前 fetch,保证本地记忆是最新的。
  3. 多个远程的推送策略: git remote set-url --push origin 禁止推送的地址 可以让你 git push 时不会误推到 upstream。
  4. git push --force-if-includes Git 2.30 新增的参数,比 --force-with-lease 更严格------要求你本地已经整合了远程的提交才能强推。两个参数可以一起用。
  5. reflog 保命: 即使被强推覆盖了,远程管理员可能还能从 reflog 找回提交(但前提是还没被垃圾回收)。

11. 小实验(请一定动手)

实验甲:制造并解决非快进

  1. 用本章第 4 节的方法建一个假远程和两个工作区。
  2. A 提交并推送,B 也提交并推送(被拒)。
  3. B 用 fetch 看远程状态,再用 pull --rebase 整合。
  4. B 推送成功。

实验乙:force-with-lease 拦截

  1. A 推送一个提交。
  2. B 也推送(被拒)。
  3. 不 fetch ,B 直接 git push --force-with-lease------也会被拒!
  4. B fetch 后发现远程有 A 的新提交,整合后再推。
  5. 验证:如果在 fetch 之后 A 又推了新提交,你的 --force-with-lease 仍然会拦截。

实验丙:三角工作流

  1. 一个仓库当作 upstream,一个仓库当作你的 fork(origin)。
  2. 从 upstream 拉代码,改完推到 origin。
  3. 从 origin 发 PR(可以只用命令模拟,不用真上 GitHub)。

通过标准: 能独立处理一次推送被拒,知道什么时候用 --force-with-lease,说得出它比 --force 安全在哪里。


12. 常见问题

问 1:推送被拒是不是我做错了?

不是,是远程有你不知道的新提交。先 fetch 再整合就行。

问 2:fetch 和 pull 到底用哪个?

想先看看远程有什么用 fetch,想一步到位用 pull --rebase。建议设 pull.rebase true 后放心用 pull

问 3:--force-with-lease 一定能保护我吗?

在你 fetch 之后、别人又推了新的提交这种时间差内,--force-with-lease 可能拦不住 (因为你的本地记忆还是旧的)。但它比 --force 安全得多,绝大多数情况够用了。

问 4:能不能对 main--force-with-lease

技术上能,但 不应该 。共享分支不应该强推。如果必须回退,用 git revert 更安全。

问 5:pull 出了合并提交怎么办?

下次用 git pull --rebase。设了 pull.rebase true 就不会出了。已有的合并提交如果很烦,可以在下次 rebase 时被压扁。

问 6:origin/mainmain 有什么区别?

origin/main 是你本地对远程 main 的「记忆」(fetch 时更新),main 是你自己本地的分支。两者不一定指向同一次提交。

问 7:删了 remote 会删代码吗?

只删关联关系(名字和地址),不删本地分支和提交,也不删远程仓库。

问 8:三角工作流和普通推送有什么区别?

普通推送你往 origin 推,origin 就是主仓库。三角工作流你往 origin(你的 fork)推,然后通过 PR 合并到 upstream(主仓库)。你在 upstream 上没有推送权限。


13. 总结、学习路线与思维升华

13.1 这一章请记住的

记住什么
非快进 远程有你没有的提交,推送被拒是 Git 在保护你
fetch vs pull fetch 只下载,pull = fetch + 整合
pull --rebase 避免合并提交,保持历史一条线
--force 危险,直接覆盖别人的提交
--force-with-lease 安全强推,远程变了就拒绝
远程跟踪分支 origin/main 是本地对远程的快照,由 fetch 更新
多远程 origin + upstream,三角工作流
下一章 额外命令速查(21 章)

13.2 在整个系列中的位置

text 复制代码
01 认识 Git
02 三区模型
03 基本工作流
04 安全地撤销
05 分支与合并
06 远程协作
......
19 暂存与清理
20 远程进阶          ← 当前
21 额外命令速查
22 冲突策略
23 安全实践
......

13.3 思维升华

保险绳不是鼓励你冒险,而是当你必须强推时多一道确认。 推送被拒是 Git 在说「远程有新东西你还没看」,不是在跟你作对。 先 fetch 看清楚,再决定怎么整合。能不强推就别强推,必须强推就用 --force-with-lease。 信箱类比:塞信前先看看信箱里还是不是你记得的样子------这就是 force-with-lease 的全部意义。

13.4 参考资料

命令输出样例验证环境:Git 2.43.0 ;演示作者信息为虚构:Ada Example <ada@example.com>

13.5 本章检查清单

  • 能说清「非快进」是什么,为什么推送会被拒
  • 能区分 fetchpull
  • 会用 git pull --rebase 保持历史干净
  • 知道 --force-with-lease--force 安全在哪里
  • 能解释 origin/main 是什么
  • 会添加和删除远程仓库
  • 能说出三角工作流的三个步骤
  • 推送被拒时知道该怎么处理

按回车前先预判状态------这一章的保险绳,是你对远程仓库的清醒认识。

相关推荐
Cooper251 小时前
别急着改 Git 历史:rebase、cherry-pick、bisect 与 reflog 的安全操作顺序
git
刚入门的大一新生2 小时前
Linux-版本控制器Git
git
扶苏10022 小时前
同一个 Git 项目整出两份,切分支互不影响?两种方案实测
大数据·git·elasticsearch
ZJU_统一阿萨姆3 小时前
【Git】Github 开源许可证详解
git·开源·github
Geek-Chow1 天前
Git 标签与发布流程:轻量标签、附注标签与签名标签
git·tags
wear工程师1 天前
Git 冲突怎么解决:别只删标记,先看懂暂存区的 1、2、3 阶段
git
村雨遥1 天前
覆盖提交不等于删除,这才是彻底清除 Git 历史的正确姿势
git·github
淮北4941 天前
ubuntu22 默认输入法调整频率
运维·服务器·git·ubuntu·elasticsearch
momo_aa1 天前
小白下载git步骤
git