You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Git会重新发送远程仓库已存在的对象?

准备仓库

$ git init repo

$ cd repo

$ git config user.name username

$ git config user.email username@mail

$ head --bytes=100000000 < /dev/urandom > file

创建远程master分支

$ git add file

$ git commit --message=initial\ commit
[master (root-commit) 6068e0d] initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file

$ git rev-parse master
6068e0d0071bc76f31065ddb1ddbad0d46c635b8

$ git cat-file -p master
tree f9862a0f570f7a910a01ab1fa743d66407452fdd
author username <username@mail> 1719130146 +0300
committer username <username@mail> 1719130146 +0300

initial commit

$ git cat-file -p f9862a0f570f7a910a01ab1fa743d66407452fdd
100644 blob bbb3bfdf995b0d2eea02b1fed8688e886da134de    file

$ git cat-file -s bbb3bfdf995b0d2eea02b1fed8688e886da134de
100000000

$ time git push git@hostname:username/reponame.git master:master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 20 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 95.40 MiB | 4.57 MiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To hostname:username/reponame.git
 * [new branch]      master -> master

real    0m22.503s
user    0m3.233s
sys 0m0.483s

准备远程dev分支

$ git checkout -b dev

$ git rev-parse dev
6068e0d0071bc76f31065ddb1ddbad0d46c635b8

$ git cat-file -p dev
tree f9862a0f570f7a910a01ab1fa743d66407452fdd
author username <username@mail> 1719130146 +0300
committer username <username@mail> 1719130146 +0300

initial commit

$ time git push git@hostname:username/reponame.git dev:dev
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To hostname:username/reponame.git
 * [new branch]      dev -> dev

real    0m1.671s
user    0m0.035s
sys 0m0.015s

此时完全可以理解为何增量为0,以及推送dev分支耗时极短的原因。

在本地和远程修改初始dev提交

$ git commit --amend --no-edit
[dev b15f0a6] initial commit
 Date: Sun Jun 23 11:09:06 2024 +0300
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file

$ git rev-parse dev
b15f0a6c36463e6e8b28b63473c464fb2fbcc326

$ git cat-file -p dev
tree f9862a0f570f7a910a01ab1fa743d66407452fdd
author username <username@mail> 1719130146 +0300
committer username <username@mail> 1719131177 +0300

initial commit

# it still points to the f9862a0f570f7a910a01ab1fa743d66407452fdd tree as expected

$ time git push --force git@hostname:username/reponame.git dev:dev
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 20 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 95.40 MiB | 4.44 MiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To hostname:username/reponame.git
 + 6068e0d...b15f0a6 dev -> dev (forced update)

real    0m23.326s
user    0m3.202s
sys 0m0.507s

问题解答

虽然远程仓库确实已经拥有该tree和blob对象,但推送耗时久的原因主要有两点:

  1. Git打包传输的机制限制:git commit --amend生成的新提交哈希值和原提交完全不同,Git推送时会默认遍历新提交关联的所有对象(提交本身、tree、blob)并生成pack文件。即使远程已有这些tree和blob,本地打包过程依然会把它们包含进去——Git不会主动去远程校验对象是否存在,而是直接传输整个pack包,远程收到后才会自动忽略已存在的对象,但传输的时间和带宽消耗已经产生。
  2. 强制推送的特殊逻辑:使用--force推送时,Git的历史校验逻辑会简化,不会像普通推送那样对比远程分支历史来筛选需要传输的对象,而是直接发送新分支顶端关联的所有可达对象,这就导致原本不需要传输的大体积blob被重复打包发送。

内容的提问来源于stack exchange,提问作者terrorrussia-keeps-killing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:18:14