git - Git 推送到远程而不拉取并防止远程文件被删除
问题描述
我在不同的节点上有几个本地 Git 存储库。他们都将自己的文件推送到同一个远程的同一个分支,这些文件从一个节点到另一个节点完全不同。没有节点需要访问其他节点的文件。为了节省带宽,我不想git pull
在节点推送文件之前对它们进行操作。有什么方法git push --force
可以将文件从节点推送到远程的单个分支而不删除其他节点提交的文件?
解决方案
TL;博士
为每个节点分配一个保留给该节点的私有名称。我将在下面举例说明如何使它与人类演员一起工作。您必须为机器演员想出自己的系统。然后你必须想出你自己的系统来使用结果提交——这不像选择一个提交那么简单;你需要一个真正的部署引擎。
长
简短的回答是 no 和 yes,或者mu。这里的基本问题是git push
不推送文件; git push
发送提交。这些提交由其唯一的哈希 ID 标识。例如,如果想向您发送a123456
我可以运行的提交:
git push <string-that-leads-my-Git-to-your-Git> a123456:<some-name>
请记住,每个提交都列出了它自己的父提交哈希 ID,这很快就会变得很重要。这a123456
必须有一些父级——一些其他的哈希 ID。但是现在让我们看看这个特定的git push
命令。
当我选择运行这个git push
命令时,我必须放在冒号左侧的东西,在这个git push
命令中,是我要发送给你的提交的提交哈希 ID的说明符。(在这种情况下,我选择了一个我刚刚编造的缩写哈希 ID。)在同一个命令中,我必须放在冒号右侧的东西是我想要让你的 Git 使用的名称保存此提交。
我们稍后会回到这个;现在我们需要看看 Git 提交是如何工作的。
提交如何工作
Git 真正工作的方式非常简单。每个提交都有自己的唯一哈希 ID。您已经看到了这些哈希 ID,因为git log
它们显示了它们。但它们又大又丑,没有人能记住它们。这就是我们让 Git 为我们记住它们的原因。 除了最后一个(分支的最新或提示提交)之外的所有哈希 ID都会在其他提交中被记住。
例如,我们可能会从一个只有三个提交的小型存储库开始:
A <-B <-C
我们可以使用单个大写字母来绘制 Git 的工作原理,而不是写出它们实际的、明显随机的哈希 ID。提交C
是最新的提交,所以它里面有提交的实际哈希 ID B
(不管它可能是什么)。所以我们说 commitC
指向commit B
。
同时,commitB
包含 commit 的实际哈希 ID A
:B
指向A
. A
是一个特例:这是第一次提交;它不能指向之前的提交,因为我们之前没有提交A
。所以A
无处可去,这让 Git 停止了。
分支名称如何工作
但是,不知何故,我们必须找到commit C
。Git 只能通过它们的哈希 ID找到提交——至少是直接的。所以我们必须将提交的哈希 ID 保存在C
某处。我们可以把它写在一张纸或白板上,然后输入,但这太糟糕了。相反,Git 允许我们将哈希 ID 存储在name中。
您可以在此处使用的名称有多种形式,但我们最常使用的是分支名称。分支名称有一个特殊属性:我们可以使用它来获取该分支。当我们这样做时,Git 会将特殊名称附加到该分支名称。分支名称本身仅包含实际提交的原始哈希 ID(在本例中为提交 C),因此分支名称指向该提交,就像每个提交指向其父级一样:git checkout branch
HEAD
A <-B <-C <-- master (HEAD)
现在假设我们要创建一个新分支dev
。我们将创建它,指向提交C
——一个提交的两个名称;这很好,它只是意味着所有三个提交现在都在两个分支上。我们也将 Git 附加HEAD
到dev
:
A--B--C <-- dev (HEAD), master
现在我们将进行新的提交。因为我们已经C
签出,新提交的父级将是C
. Git 将从我们决定快照的任何源代码(我们必须使用 复制到 Git 的索引中)以及我们告诉 Git使用的任何元数据以及它自己发现的任何内容(我们的名字,电子邮件地址、日志消息、当前时间和 commit 的哈希 ID ):git add
C
A--B--C
\
D
请注意如何通过元数据中的父提交哈希 IDD
指向。C
所有数据和元数据都进入加密哈希算法,因此写出提交的行为会为新提交生成新D
的哈希 ID 。现在到了神奇的一步:Git 简单地用新的哈希 ID覆盖分支名称 -附加的分支名称,以便当前分支名称指向新的提交:D
HEAD
A--B--C <-- master
\
D <-- dev (HEAD)
其他名称没有改变。当前的分支名称 ,dev
现在指向新的提交,这样就dev
记住了 hash D
。 HEAD
也没有改变 - 它仍然附加到dev
.
git push
工作原理
当我让我的 Git 调用你的 Git 时,我告诉了我的 Git:
- 我要发送哪个提交,以及
- 我希望我的 Git 要求您的 Git 使用什么名称,以记住该提交。
我的 Git 询问你的 Git:嘿,你有 commita123456
吗? (假设a123456
是我D
在上面那个简单的四提交存储库中的提交。)因为我刚刚完成,所以你没有它。所以我的 Git 会询问你的 Git 是否有C
,因为我的 Git 必须发送所有导致 D
的提交,而不仅仅是D
它自己。你要么有,要么C
没有。如果你不这样做,我的 Git 也会询问B
,依此类推。但您可能已经拥有C
. 可能,您 master
对共享提交的观点C
。
所以我的 Git 打包只是 commit D
,然后发送。然后我的 Git 要求您的 Git 设置您的分支名称之一。
如果我选择让你的 Git 设置你的dev
,而你没有,你的dev
Git 将创建你的dev
,现在你有我做的同样的事情。
同时,假设 ErnieA-B-C
在 Ernie 的 Git 中有相同的提交,但是 Ernie 做了一个新的dev
和一个新的提交E
。厄尼现在有:
A--B--C <-- master
\
E <-- dev
Ernie 没有,D
因为 Ernie 的 Git 没有与我的 Git 对话,反之亦然。就在我向您发送 myD
并要求您创建您的 dev
之后,Ernie 让他的Git 向您发送他的 E
并要求您将您dev
的指向点设置为 commit E
。
你的 Git 现在有:
E <-- polite request to set your `dev` here
/
A--B--C <-- master
\
D <-- dev
如果你的 Git 服从了礼貌的要求,你的 Git 会将你的dev
to 指向E
,从而忘记如何找到D
. 所以你的 Git 只是说不,我不能那样做。
如果 Ernie 改变他git push
的使用--force
,Ernie 将他的礼貌请求改为命令:设置dev
指向E
,我是认真的! 您的 Git 仍然可以拒绝该命令,但默认情况下您的 git 将服从该命令,并丢失D
. 这就是你在尝试使用这种技术时所看到的。
请注意,无论我如何运行git push
,我的 Git 都会要求您的 Git 设置一些名称。我在上面展示的方法——我在冒号左边选择一个哈希 ID,在右边选择一个名称——不是我们通常的拼写方式git push
。我们通常写:
git push origin dev
但这意味着git push origin dev:dev
。_ 左边的东西dev
, 是我的 commit 名字D
。右边的东西dev
,,只是实际的名字dev
。Git 只是让我们在没有冒号的情况下表达这一点。
问题是名称冲突,因此请避免名称冲突
不管如何将提交发送到您的中央收集点选择命名它们的分支,您需要在中央收集点发生的事情都很简单。在上面,我们有两个演员:一个是我,在名字下dev
,发送我的提交D
,厄尼,在名字下dev
,发送他的提交E
。这失败了,因为我D
和他E
都回到了 shared C
,但是我们试图使用一个名字来记住两个不兼容的提交。
但是,如果你给我一个私人名字来使用呢?为简单起见,假设我的名字是 Dave(它不是)并且你让我推动我dev
的incoming/dave
而不是仅仅dev
. 我会跑:
git push origin dev:incoming/dave
同时,您将让 Ernie 运行:
git push origin dev:incoming/ernie
现在在您的Git 存储库中,您将拥有以下内容:
E <-- incoming/ernie
/
A--B--C <-- master
\
D <-- incoming/dave
假设现在我做了一个新的提交F
,而 Ernie 做了一个新的提交G
,我们每个人都在处理我们的dev
. 请记住,提交哈希 ID 是通用的,因此我F
的和 Ernie 的G
定义是不同的大而丑陋的哈希 ID。G
厄尼会和父母一起推他E
。他会G
用他的名字找到他的 Git,dev
但要求你设置你的名字incoming/ernie
。你最终会得到这个:
E--G <-- incoming/ernie
/
A--B--C <-- master
\
D <-- incoming/dave
您incoming/ernie
可以安全地更新,因为它必须G
返回E
,因为它必须返回。
我还没有发送我的F
,但是当我发送时,这F
将指向D
并且我会让你更新你的incoming/dave
. 没关系,因为incoming/dave
将指向F
哪个导致返回D
:不会丢失任何提交。
显然,有人——<em>你——将不得不做一些事情来整合这些独立的开发线。但这不是戴夫和厄尼的问题,那是你的问题。
推荐阅读
- tensorflow - keras.initializers.ones() 是风格吗?
- javascript - 在 Web 应用程序中单击按钮后刷新下拉列表
- scala - 带有替代方法的重载方法值 json: (jsonRDD: org.apache.spark.rdd.RDD[String]) 在 IntelliJ 中使用 Spark
- excel - 单元格包含公式不为空
- node.js - 如何像日历一样使用 Node.js 存储日程安排
- node.js - NodeJS - 确定目标网站是否使用 HTTP/2
- python - 在仅提取数据框的多列时在日期上应用行逻辑
- c# - unity Scriptable 对象在 bulid 中不起作用
- java - 我想使用 jdbcUserDetailsManager 类及其方法使用 Spring Security 进行注册和登录功能
- sql-server-2008 - 将数据从 ADLS 迁移到 SQL 时如何在目标 SQL 数据库表中创建时间戳列(管道内)