【问题标题】:What's the equivalent of hg pull and merge in gitgit中的hg pull和merge相当于什么
【发布时间】:2014-09-09 19:52:03
【问题描述】:

来自 mercurial,我在 git 的术语和方法上有些挣扎。这是目前的情况:

两台服务器共享同一个存储库。

  • 在服务器 1 上,开发人员提交了对 file.php 的更改。
  • 在服务器 2 上,开发人员提交了对 file.php 的更改。相同的更改,但不同的提交。

我需要让这些变更集重新同步

要在 mercurial 中解决此问题,我将从服务器 2 运行以下 3 个命令。任何合并冲突都将在编辑器或融合中得到解决。

hg pull -u ssh://remote-server1//shared-repository
hg merge
hg commit -m "Branch Merge"

你是怎么用 git 做这个的?

我做了什么

使用https://www.mercurial-scm.org/wiki/GitConcepts 作为指导,我尝试了git pull,但是它失败了,因为我没有运行git config。这个存储库正在生产中并被许多人共享,所以我尽量避免设置用户名。

按照https://stackoverflow.com/a/17713604/456645,我运行了这个命令:

git fetch ssh://remote-server1//shared-repository

它会响应

From ssh://remote-server1//shared-repository
 * branch            HEAD       -> FETCH_HEAD

我还没有弄清楚在这种情况下如何正确运行git reset

git reset ssh://remote-server1//shared-repository/master 
# FAILS (fatal: Invalid object name 'ssh'.)

git reset origin/master
# FAILS (fatal: ambiguous argument 'origin/master': unknown revision or path not in the working tree.)

git reset FETCH_HEAD/master
# FAILS (fatal: ambiguous argument 'FETCH_HEAD/master': unknown revision or path not in the working tree.)

进一步看,我很困惑只找到一个分支

git branch -a
* master

为了确保它没有自动合并,我检查了历史记录

git log -n 3 
# Here, I find the changesets that previously existed.  Nothing new

此时,我什至不知道git fetch 会发生什么。我没有看到任何提取的证据。

【问题讨论】:

  • 用“git branch -a”列出所有分支。另外,这里有一个 git/mq 备忘单:github.com/sympy/sympy/wiki/Git-hg-rosetta-stone/…
  • "origin" 在上面的重置示例中是其中一个 git 遥控器的名称(运行 git remote -v 以列出您的遥控器)。您可以使用“git remote add server1 ssh://remote-server1//shared-repositor”将远程添加到 repo 1,然后您可以使用“git pull server1”/“git reset server1/mybranch”等。跨度>
  • 更新了响应。 git branch -a 仍然只显示 * master
  • 也感谢您的后续评论。那工作得很好。 git reset server1/mybranch 是干什么用的?我还没有运行那个命令。
  • @folkol 另外,请随时将此作为答案,我会接受它

标签: git merge mercurial


【解决方案1】:

TL;DR 版本

为每个存储库添加一个远程。我将调用这两个存储库alexbob,但您可能应该选择更合适的名称。请注意,如果您 git clone 其中一个,则该名称将自动命名为 origin(尽管您可以选择其他名称)。下面假设您已经有一个相关的存储库,并且需要添加两者,但如果您已经有origin,您只需添加另一个。

$ git remote add alex ssh://their.domain.name/their/path/to/repo.git
$ git remote add bob ssh://bobs.domain.name/and/his/path.git

之后,您可以轻松地git fetch 来自他们的所有内容(所有分支等):

$ git fetch alex
$ git fetch bob

您现在有了名为alex/masteralex/develop 等的“远程跟踪分支”,只要Alex 有名为masterdevelop 等的分支;在 Bob 拥有 master 的任何地方,你都有 bob/master 等等。

现在如果你想合并东西,你可以让你自己的本地分支对应他们的一个:

$ git checkout -b alex-master alex/master

(alex-master 是一个本地分支;如果您愿意,您可以将其称为 master,但每个本地分支名称必须是唯一的,因此您可能必须先将您现有的 master 移开) 并与bob's 合并:

$ git merge bob/master

(这会将bob/master 合并到您的本地alex-master)。如果您有权限,您可以将结果推送回 Alex 的master

$ git push alex alex-master:master

还有 Bob 的master:

$ git push bob alex-master:master

作为git push 的最后一个参数给出的“refspec”将您的本地分支名称放在冒号的左侧,将它们的本地分支名称放在右侧。

与 Mercurial 不同,没有特殊的分支名称; master 只是常规的,而在hg 中,default 有点神奇。 (好吧,在 git 中 master 有一个特殊的魔力位:它改变了 git merge 拼写默认提交消息的方式。但仅此而已。)


这有很多部分,其中一些主要是为了美观/方便,而其中一些是至关重要的。

首先,现在在 git 中,人们主要通过“远程”来指代存储库。远程部分是一种方便的构造,但在从另一个存储库获取和保留提交方面,这也起到了关键作用。

Mercurial 有一些比遥控器简单得多的东西,但它提供了部分便利:在您的 .hgrc 文件的 [paths] 部分中,您可以在短名称下列出长 URL:

[paths]
short = ssh://something.very.long/with/a/very/long/path/that/is/a/pain/to/type/every/time

或其他。将此与 .git/config 中的 git“远程”进行比较:

[remote "short"]
    url = ssh://something.very.long/with/a/very/long/path/etc

到目前为止,它们完全相同,只是拼写/语法不同。但是有一点:

    fetch = +refs/heads/*:refs/remotes/short/*

(事实上,这通常出现在url 之前,但这两者的顺序无关紧要)。我们稍后再讨论这个问题。


接下来,hg pull 的最直接等价物确实是git fetch。然而,hg pull-ugit fetch 最肯定 有,因为由于 Mercurial 分支和 git 之间的差异,这两个操作的内部结构最终完全不同分支。

Mercurial 分支是一种“真实的东西”,而 git 分支则更为短暂。更具体地说/准确地说,一个 git 分支更像是一个 Mercurial 书签,而不是一个 Mercurial 分支。但是,有一个概念(或者“自负”可能是一个更好的词)在这里根本无法正确翻译:具体来说,添加到 Mercurial 存储库的提交按顺序编号并保留在存储库中,即使没有命名它们:你只是风有一个匿名的头。例如,您可以有一个提交图(显示为 hg log --graph 等),如下所示:

o
|  o
o  |
|  o
o /
|/
o

不需要任何外部名称指向这两个头;他们只是匿名的头(在一个分支内,因为根据定义,hg 中的所有提交都在某个分支内)。

相比之下,在 git 中,没有外部名称的提交有资格被垃圾收集(hg 可能称之为“剥离”,尽管细节有所不同)。所以所有 hg 所说的“头”必须有某种名称。这是遥控器的fetch = 行生效的地方。

“但是等等,”你可能会问,“我可以做到git fetch ssh://...,而且它没有命名遥控器!”这就是HEAD -> FETCH_HEAD 位的用武之地。

如果您没有“远程”,您仍然可以从另一个存储库中引入一些分支(甚至多个分支)。然而,如此获得的任何分支机构的负责人都需要一个名字。这个“名字”被填充到名为FETCH_HEAD 的伪分支中。细节变得复杂,但如果你只引入一个分支,就像在这种情况下,更容易解释和思考:本质上,git 引入了一个命名分支,并且在你的本地存储库中,将其命名为 @ 987654372@.

请注意,随后的git fetch 通常会覆盖FETCH_HEAD,此时您带来的所有提交都符合垃圾回收条件。因此,一旦你提交了,你需要另一个步骤。


现在,一旦您有一些提交并想要保留它们,有不同的方法可以做到这一点。最喜欢hg pull的就是使用“远程”机制。在这里,fetch = 行解决了这个问题。当您从远程git fetch 时,远程具有指向所有分支头的分支名称(根据定义,因为 git 需要 this)。这些分支名称是 refs/heads/<em>name</em> 形式的引用。

fetch = 行告诉 git 如何重命名分支,以便它们在本地存储库中具有新的、唯一的本地名称。例如,通过将refs/heads/master 重命名为refs/remotes/short/master,您将获得short/master 作为远程称为“分支master”的新本地名称。这保留了提交,我们很好(尽管我们仍然需要合并)。

或者,我们可以只获取一个有趣的分支并将其命名为FETCH_HEAD,然后立即合并。当我们这样做时,我们选择一些本地分支(例如,master),如果需要检查它,然后运行git merge FETCH_HEAD

一旦合并完成,我们的本地分支(在此示例中为master)可能通过合并提交指向我们通过git fetch 带来的提交,因此它们不再容易受到垃圾收集:它们在分支上,因此可以通过该分支名称命名。我们现在可以随时安全地覆盖FETCH_HEAD

最后一种方法——将东西带过来,但将其命名为FETCH_HEAD,然后将本地分支与FETCH_HEAD 合并——是“git pull”命令的作用。从这个意义上说,它只是git fetch,后跟git merge。我个人尽量避免使用pull 脚本,因为它有很多额外的魔力来处理各种极端情况,在过去,这些情况在一些未考虑的极端情况下会出错;一般来说,我更喜欢获取,查看发生了什么,然后选择是否以及如何合并或变基我自己的代码。


最终没有唯一正确的方法,但如果你要多次这样做,我会添加一个 git 远程,并使用“远程分支”概念(fetch = 将“他们的”分支重命名为您自己本地存储的“远程跟踪分支”)。

【讨论】:

  • 这让我大开眼界!我真的很欣赏与 Mercurial 术语的比较。此外,更微妙的澄清线索(“如果需要,请查看”)对理解全貌有很大帮助。
【解决方案2】:

编辑:通常,git remote 是一个“裸”存储库,这意味着您不能在服务器上拥有签出的工作副本。即使服务器仓库是一个裸仓库,这也可以工作。

Clone 本地计算机的存储库之一:

$ git clone ssh://remote-server1//shared-repository

cd 到那个文件夹:

$ cd shared-repository

Add 第二个是 remote,名称为 server2:

$ git remote add server2 ssh://remote-server2//shared-repository

Fetch 以及服务器 2 上存储库中的所有内容(与 nq pull 相同):

$ git fetch server2

Merge server2 的 master 分支进入你的本地 master:

$ git merge server2 master

Push 将结果返回到服务器 2:

$ git push server2 master

Push 将结果返回到服务器 1(源是您克隆的远程服务器):

$ git push origin master

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 2012-04-17
    • 2012-06-24
    • 2016-12-11
    • 2018-03-05
    相关资源
    最近更新 更多