我想在两台计算机(计算机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是获取+合并,您可以将它们拆分。但是推是什么加上合并?
答案 0 :(得分:1)
(请记住,这里涉及到两个Git。您要呼叫一个 A 。您可以从机器 A 中将其放入sem机 B ,因此在机器 A 上,在那里的存储库中,您使用远程名称B
来谈论“另一个Git”。而在机器 B 上,您使用远程名称A
来谈论“另一个Git”。)
使用git push B master:refs/remotes/A/master
在A
上创建或更新实际的远程跟踪名称。使用git push B +master:refs/remotes/A/master
强制覆盖A
上的远程跟踪名称(或与--force
相同,但不带加号)。
您可以使用git push B master:A/master
在A/master
上建立或更新常规(本地分支,而不是远程跟踪名称)A
。和以前一样,您可以在此refspec中添加--force
或加号以覆盖。
如果您使用remotes/A/master
,而没有refs/
部分,则会创建一个名为remotes/A/master
的 local 分支,我认为这是一个坏主意。在机器A
上,运行git branch
和git 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]
repoA
和repoB
都使用common
作为其遥控器。 repoA
和repoB
都将其更改推入和移出裸仓库common
。 common
未检出。它为repoA
和repoB
提供了一个静态位置来共享他们的工作。
例如,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 --rebase
从common
获取更新,并在此之上重放其自身的更改。
[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
然后可以(强制)将其更改推向普通状态。
然后下一次repoB
从common
中提取时,它将获得更新,并且每个人都同步。
[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