Git推送而没有合并或反向获取

时间:2019-03-16 19:02:12

标签: git

我想在两台计算机(计算机A和计算机B)之间同步一些配置文件。计算机A在计算机B上具有ssh登录名,但不是这样。

当我同时通过两种方式建立ssh连接时,通常会执行以下操作:运行fetch,然后从任一仓库中合并远程/上游/主服务器。这样,我可以让事情有所分歧,只有在有时间的时候才需要合并。

当我只有一个单向连接时,可以像往常一样通过在A中运行从B到A进行更改:git fetch B,然后git merge remotes / B / master。为了将更改从A更改为B,我尝试从A运行:git pushB。但是我收到错误消息error: refusing to update checked out branch: refs/heads/master。这是因为我试图推送到已签出的分支,从而更新HEAD(据我所知)。但是我不想更新HEAD,我只想更改B中作为远程服务器/ A /主服务器可用的A。因此,我想提供一个从A到B的推送不尝试更新B的头部或主节点的推送选项。这与获取相同,但是相反。

另一种放置方式:pull是获取+合并,您可以将它们拆分。但是推是什么加上合并?

3 个答案:

答案 0 :(得分:1)

TL; DR

(请记住,这里涉及到两个Git。您要呼叫一个 A 。您可以从机器 A 中将其放入sem机 B ,因此在机器 A 上,在那里的存储库中,您使用远程名称B来谈论“另一个Git”。而在机器 B 上,您使用远程名称A来谈论“另一个Git”。)

使用git push B master:refs/remotes/A/masterA上创建或更新实际的远程跟踪名称。使用git push B +master:refs/remotes/A/master强制覆盖A上的远程跟踪名称(或与--force相同,但不带加号)。

您可以使用git push B master:A/masterA/master上建立或更新常规(本地分支,而不是远程跟踪名称)A。和以前一样,您可以在此refspec中添加--force或加号以覆盖。

如果您使用remotes/A/master,而没有refs/部分,则会创建一个名为remotes/A/master local 分支,我认为这是一个坏主意。在机器A上,运行git branchgit branch -r来查看分支名称和远程跟踪名称之间的区别;或使用git for-each-ref将其全部以全名形式转储出去。

首先,请注意,git push 不会合并git fetch对此也没有。 拒绝更新消息与合并无关!您在这里的诊断至少是部分正确的(取决于您所说的分歧的意思-您不是从什么 say说出):

  

...失败,因为它会使工作树和索引分叉

实际上,索引和工作树将保持不变,这就是问题所在。

您要做的只是git push到某个名称,该名称​​不是当前签出的分支名称。

运行git push时,通常通过https(端口443)或ssh(端口22)将您的Git定向到另一个IP地址。另一个Git是一个具有自己分支的存储库。如果它是--bare存储库,则它没有 no 工作树,并且其中没有人可以从事任何工作,因此它通常会接受推送。如果不是,则它有一个工作树,该工作树由其索引索引,并且有人可能正在其中工作。

考虑什么是提交 。每个提交都有一个唯一的哈希ID,该ID实际上是提交的真实名称。每个提交都存储所有文件的快照,每个提交都存储该快照的一些元数据关于:创建快照的人(姓名和电子邮件地址),时间(时间戳),和为什么(日志消息)。它还存储其直接父提交或所有父提交(对于合并提交)的哈希ID(真实名称)。

分支名称(位于Git存储库中)用于在提交链中找到 last 提交:

... <-F <-G <-H   <--branch

分支名称存储提交H的哈希ID。 H本身存储其前身G的哈希ID,该哈希ID存储另一个ID F,依此类推,直到历史。

运行git push时,您的Git会调用其他Git。然后,您的Git为他们的Git提供一些提交哈希ID。它们要么具有此提交功能,要么通过加密校验和的魔术在整个所有 Git中具有通用的哈希ID,或者没有。如果他们没有提交,则需要它(及其所有文件),也许还需要其父提交(以及所有那个提交的文件),也可能需要父提交的父提交,依此类推。本质上,他们需要您拥有的任何不需要的提交。因此,您的Git为他们提供了提交,直到他们的Git说:“好吧,我也有一个以及所有的较早版本,您现在可以停止。”

然后,您的Git发送这些提交(及其文件)。在这里,您可以看到计数,压缩和写入对象消息。最终,您的Git已将所需的一切发送给他们。如果他们已经有了提交,则此阶段根本不发送任何数据,但是无论如何,最后他们都将提交。

现在,您的Git完成了git push的最后一部分:礼貌地(不带--force的情况下推送)或命令(git push --force),要求其他Git设置一些它的分支名称可以记住您刚刚发送给他们的提交,或者如果他们已经有了它们就不发送。

假设您向他们发送了父项为I的新提交H

...--F--G--H   <-- branch
            \
             I

他们现在有I,但是现在您问他们:设置您的名字branch来记住提交I而不是H,好吗?

那不是 好,因为有人已经branch签出了!据他们所有的Git知道,有人正在忙于编辑文件并摆弄索引和/或工作树,并计划在一纳秒或两秒钟内使他们自己的提交J

...--F--G--H--J   <-- branch
            \
             I

这将使I全部消失,或者:

...--F--G--H--I--J   <-- branch

由于git push 进行任何合并,因此将使提交J有效地 undo 提交I。 / p>

因此,他们的Git(实际上是您的Git,您在执行此操作时只是站在互联网的另一端)说不,您不能以为名{ {1}}记住提交branch,我的用户在工作时正忙着记住提交J

但是您可以要求他们设置H,这是目前不存在的名称。对于他们的Git,这是一个 new 名称:

merge-me-into-branch

因此,您所需要做的就是运行:

...--F--G--H   <-- branch
            \
             I   <-- merge-me-into-branch

在“其” Git中创建 new 名称git push <remote-or-URL> branch:merge-me-into-branch ,以记住新的提交merge-me-into-branch

答案 1 :(得分:1)

tl; dr 您需要第三个裸仓库来与repoA和repoB(以及repoC和repoD ...)进行协调。任何Git服务都会为您提供一个。或者您可以使用计算机B,因为您可以登录到它。或者,您可以将公用存储库保留在Dropbox though that's not as simple as it sounds上。


您在上游游泳遇到两个问题。首先,正如您所发现的,您不能将其推送到已签出的分支。如果可以的话,会导致很多混乱。签出的分支意味着有人可能正在积极地工作或使用它。当某人进行结帐时,HEAD将从他们的下方移开,其工作副本将不同步。

尝试使它们同步,由于工作副本突然更改,它们将给另一个惊喜。例如,假设您对B进行了一些编辑,然后尝试从A推到B。Git应该做什么?

  

pull是提取+合并,您可以将它们拆分。但是推是什么加上合并?

push不会合并,只会快进。 push基本上是fetch的反向,它发送丢失的对象。但是随后它将快进。

假设您在repoA上做了一些工作并进行了推送。

repoA  A - B - C - D - E [master]

repoB  A - B - C [master]

repoA将发送D和E。然后repoB将master向前滑动到E。不需要合并。

如果您同时从事repoA和repoB怎么办?

repoA  A - B - C - D - E [master]

repoB  A - B - C - F - G [master]

当repoA尝试推送Git时,会注意到分支已“分开”。他们的共同祖先C不在repoB / master的领导下。这需要合并。合并通常需要人为干预,但是repoB方面没有人。 Git不允许这样做,因为它会使事情变得更加混乱。


相反,将您的更改重新设置在B之上。然后推送。这使您可以完成将两个分支缝合在一起的工作,并且由于我们已经重新建立基础而不是合并,所以这将是一个简单的快速前进。

同样,从我们不同的存储库开始。

repoA  A - B - C - D - E [master]

repoB  A - B - C - F - G [master]

如果repoA运行git pull --rebase(我建议设置pull.rebse = preserve,因此git pull默认情况下会重新设置基准并保留分支,与常规的合并请求相比,它的历史记录要干净得多),然后它将获取F和G。

                 F - G [repoB/master]
                /
repoA  A - B - C - D - E [master]

repoB  A - B - C - F - G [master]

然后假装D和E一直写在G之上。

                       D1 - E1 [master]
                      /
                 F - G [repoB/master]
                /
repoA  A - B - C 

repoB  A - B - C - F - G [master]

现在,当repoA(强制)推向repoB时,这是一个简单的快速前进。


这是我为任何项目推荐的工作流程。但是,这不能解决repoB被检出的问题。为了解决这个问题,您需要一个第三个裸仓库,它们都可以推送和拉入。

                 D - E [master]
                /
repoA  A - B - C [common/master]

repoB  A - B - C [common/master]
                \
                 F - G [master]

common A - B - C [master]

repoArepoB都使用common作为其遥控器。 repoArepoB都将其更改推入和移出裸仓库commoncommon未检出。它为repoArepoB提供了一个静态位置来共享他们的工作。

例如,repoA可以推到通用。

                         [common/master]
repoA  A - B - C - D - E [master]

repoB  A - B - C [common/master]
                \
                 F - G [master]

common A - B - C - D - E [master]

然后repoB可以git pull --rebasecommon获取更新,并在此之上重放其自身的更改。

                         [common/master]
repoA  A - B - C - D - E [master]

                                   [common/master]
repoB  A - B - C - D - E - F1 - G1 [master]

common A - B - C - D - E - F1 - G1 [master]

repoB然后可以(强制)将其更改推向普通状态。

然后下一次repoBcommon中提取时,它将获得更新,并且每个人都同步。

                                   [common/master]
repoA  A - B - C - D - E - F1 - G1 [master]

                                   [common/master]
repoB  A - B - C - D - E - F1 - G1 [master]

common A - B - C - D - E - F1 - G1 [master]

common可以位于任意数量的服务上,这些服务将为您提供Git存储库。或者它可以在计算机B上,因为您可以登录。

答案 2 :(得分:1)

最后我将简要说明如何为将来的访客设置它。假设我们有一个仓库A而没有仓库B。

在B上创建仓库:

B> git init path

从A推入

A> git remote add B B:path
A> git push B master:refs/remotes/A/master

在B上设置跟踪母版分支:

B> git remote add A nowhere
B> git checkout -b master A/master

使A主分支轨道B:

A> git fetch B
A> git branch -u B/master master

如果我们不希望自动快进,为什么要设置跟踪分支?这样git status会提供有关存储库如何发散的信息。

现在,要从A更新B:

A> git push B master:A/master

从A更新A:

 A> git fetch B