gpt4 book ai didi

Git 推送具有相同文件的新分支,再次上传所有文件

转载 作者:太空狗 更新时间:2023-10-29 12:55:11 28 4
gpt4 key购买 nike

给定以下场景。

  1. 创建新分支
  2. 提交一个 10MB 的文件
  3. Git push(上传 10MB 文件)
  4. 创建一个新分支(孤儿)
  5. 提交相同的 10mb 文件(不做任何更改,相同的对象 sha hash)
  6. Git push 再次上传 10MB 的文件

我的期望是,已经上传的文件不会使用 git push 再次上传。但实际发生的情况是,当创建一个新分支时,所有文件(即使是数千个较小的源文件,而不是一个 10MB 的文件)都会一次又一次地上传。

我的问题:如何让 Git 检测到 10mb 的文件已经上传?你知道在推送提交时让 Git 检测服务器上已经存在的对象的解决方法/修复吗?Git 通过其 sha 检测文件,因此它应该能够检测到提交树中的某些文件已经存在于服务器上。

可能的用例:我有两个完全不同的分支,但是这两个分支共享一些公共(public)文件。当我推送一个分支时,我不想在推送第二个分支时再次上传公共(public)文件。

实际用例:我使用 Python 脚本和一些较小的数据集 (1MB - 10MB) 进行了大量机器学习实验。每次开始实验时,我都会将所有必要的实验文件添加到新的 Git 树中,并在没有分支的情况下在新的提交中使用该树。该提交完全悬而未决,然后使用新的 Git 引用(例如 refs/jobs/my-experiment-name)进行引用。当我现在用几乎相同的文件(因此有两个引用)进行两个实验时,当我推送这些引用时,Git 会再次推送所有对象。我的带宽很低,这确实减慢了我的工作速度。

$ mkdir git-test && cd git-test
$ git init
$ git remote add origin git@gitlab.com:username/projectname.git

# create dummy 10MB file
$ head -c 10000000 /dev/urandom > dummy

$ git add dummy
$ git commit -m 'init'

# first push, uploads everything - makes sense
$ git push origin master
Counting objects: 3, done.
Delta compression using up to 6 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 9.54 MiB | 1.13 MiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)

# create new empty branch, not based from master
$ git checkout --orphan branch2

# add same files again
$ git add dummy
$ git commit -m 'init on branch2'

# this uploads now again the dummy file (10MB), although the server
# has that object alread
$ git push origin branch3
Counting objects: 3, done.
Delta compression using up to 6 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 9.54 MiB | 838.00 KiB/s, done.

在技术方面,我们有:

  1. 不共享同一父级的两个提交(具有完全不同的历史)
  2. 这两个提交具有完全相同的树 sha id(因此引用相同的目标文件)
  3. 推送两次提交会导致将同一棵树中的所有对象传输两次。尽管我希望 Git 检测到第二次提交中的树已经存在,或者该树中的文件对象已经在服务器上。

回答(我不能再回答了,因为有人将此标记为重复)。

不幸的是,解决方案并没有那么简单。

每次 Git 想要同步两个存储库时,它都会构建一个包文件,其中包含所有必要的对象(如文件、提交、树)。当您执行 git push 时,远程将所有现有引用(分支)及其头部提交 SHA 发送到客户端。这是问题所在: pack protocol不是要针对每个对象使用,而是针对每次提交。因此,根据协议(protocol)本身,上面解释的行为是正确的。为了解决这个问题,我构建了一个简单的脚本,每个人都可以使用它来执行基于对象的 git push,而不是提交。

您可以在这里找到它:https://github.com/marcj/git-objects-sync

它的作用:

  1. 进行一次提交(只有一次,您还需要在每个未同步的父提交上执行它)并构建属于该提交(父提交除外)的对象 SHA(文件、树、提交)列表。<
  2. 将这个列表发送到服务器,服务器回复它还没有的对象的 SHA
  3. 客户端基于丢失的对象 SHA 构建一个包文件,并将其连同需要更新到哪个提交的信息一起发送到服务器。
  4. 服务器接收包文件,将其解包并使用给定的提交 SHA 更新 ref。

当然这有一些缺点,但我在链接的 Github 存储库中描述了它们。

使用我上面的脚本,您现在得到以下内容:

marc@osx ~/git-test (branch11*) $ # added new branch11 as explained at the very top
marc@osx ~/git-test (branch11*) $ python git-sync.py refs/heads/branch11
Counting objects: 1, done.
Writing objects: 100% (1/1), 158 bytes | 158.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
marc@osx ~/git-test (branch11*) $ git push origin branch11
Everything up-to-date

如您所见,它只同步一个对象(提交对象),而不会再次同步dummy 文件及其树对象。

最佳答案

Git 仅使用引用交换来开发其 list 并打包对象。如果您推送的分支与远程没有共同的祖先,它将打包并重新上传从该分支可到达的所有对象。

顺序是这样的

  • 您:将您当前的远程引用列表发送到远程
  • 远程:将所有无法从该列表访问的对象和新引用列表发送给客户端
  • 您:将对象添加到本地数据库并更新远程引用
  • 你:发现你的分支上的所有提交都无法从任何远程引用访问(没有共同的祖先,分支上的所有提交都将被发送)
  • 您:根据这些提交的差异构建 list (找到 10mb 文件)
  • 你:打包发送到远程

Remote 将识别其数据库中已有的对象并使用现有对象。

逻辑是不时发送一些不必要的文件比在每次推送时遍历整个历史记录(可能访问和比较数万或数十万个对象)要高效得多。

关于Git 推送具有相同文件的新分支,再次上传所有文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48228425/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com