无法理解某些git行为

时间:2019-09-20 16:43:43

标签: git gitlab

我无法理解git表现出的某些行为。

我有一个本地分支,例如myBranch,它与gitlab上的远程分支同名。实际上,我首先在本地创建了分支,然后使用以下命令将其推送到远程:

  

git push -u origin myBranch

我知道这也意味着origin / myBranch也与myBranch处于同一提交(例如C1)(我希望我是对的)。

现在我进行一些本地更改,然后使用amend添加它们。

  

git commit --amend --no-edit。

所以我正在修改现有的提交C1,我希望myBranch和origin / myBranch都应指向C1。

但是当我执行git status时,我得到了:

  

在分支myBranch

     

您的分支机构与“ origin / myBranch”分支不同,   并且分别具有1和1个不同的提交。     (使用“ git pull”将远程分支合并到您的分支中)

当myBranch和origin / myBranch都指向C1时,为什么我要得到这个?

3 个答案:

答案 0 :(得分:4)

向本地分支添加提交不会自动更新远程跟踪分支。为了使用本地分支中的任何其他提交更新远程跟踪分支,请使用git push(假设您已经设置了远程跟踪分支,这是在为初始{{指定-u 1}})。

顺便说一句,在generate中,应避免修改已推送到远程存储库的分支上的提交。修改提交时,它将更改提交对象以包括您要修改的所有更改。如果您尝试使用git push推送此分支,则会收到一条错误消息,说明推送被拒绝,因为分支的尖端位于其远程副本的后面。

这样做的原因是,Git使用加密的SHA-1哈希来唯一地标识每个提交(例如,通过执行git push,您可以在提交历史中看到它们),以及某些提交元数据,例如消息和时间戳以及提交的内容也被散列。本质上,当您修改该提交时,您创建了一个具有不同哈希值的新提交,并用它替换了旧提交。因此,您的本地分支指向该提交,并且位于远程后面,因为它不包含旧提交,而远程分支位于您的本地分支后面,因为它不包含新提交。

您需要强制推送(git log)才能将更改推送到远程存储库-也将远程分支指向的提交也替换为新的提交。这样做的问题是,如果有任何其他开发人员将该分支拉到了他们的本地计算机上,那么他们很难同步他们的更改,因为您是从它们下面将分支更改出来的。

对于本地或私有分支机构,修改提交和重新设置基准是可以的,但是通常,您应该避免对其他开发人员可能已下拉到其计算机的任何分支机构执行此操作。

答案 1 :(得分:2)

--amend选项似乎可以更改现有的提交。 情况并非如此。

我认为这里最好的说明是绘制(部分)提交图。请记住,在Git中,每个提交都有一个唯一的哈希ID-丑陋的字母和数字字符串。每个提交都有自己的独立ID。这些东西太笨拙,以至于人类无法正常使用,因此我们倾向于使用名称(例如分支名称)来记住它们:

...--F--G--H   <-- myBranch

进行新的提交时,您首先要对所需分支进行git checkout。这会将特殊名称HEAD附加到该分支名称, 并提取名称所指向的特定提交(在本例中为H)。

然后,您可以进行所需的任何更改(在工作树中),使用git add(将更新的文件复制回索引/临时区域),然后运行git commit(不使用{ {1}})。此时,Git:

  • 将索引/暂存区文件保存为新提交的新快照;
  • 将新提交的作者和提交者设置为您,并将其日期和时间标记设置为“现在”;
  • 将您的日志消息写入新的提交,并且
  • 当前提交(我们使用字母--amend)的原始哈希ID 写入新提交作为其父提交。 / li>

现在我们有了:

H

其中...--F--G--H <-- myBranch (HEAD) \ I 是新提交。最后一步是棘手的​​部分:您的Git现在写入了新提交的哈希ID (它是通过创建提交而创建的,我们在这里将其称为I,但又有点大了丑陋的哈希ID)更改为名称I,因为特殊名称myBranch附加到该名称:

HEAD

如果您现在将此...--F--G--H \ I <-- myBranch (HEAD) 与您正在呼叫的另一个Git git push,则您的Git会记住他们的Git的origin指向提交myBranch

I

但是现在您认为落实...--F--G--H \ I <-- myBranch (HEAD), origin/myBranch 出了点问题。因此,您需要对文件进行一些更改并I,然后运行git add这不会更改提交git commit --amend,它会创建另一个新的提交,我们称之为I,但这一次,而不是设置J的父项到J,Git再次将I的父级设置为J。然后它将H的实际哈希ID(无论是什么)写入J

myBranch

这是您存储库中的内容。位于 J <-- myBranch (HEAD) / ...--F--G--H \ I <-- origin/myBranch 处的另一个存储库,其origin名称指向提交myBranch,即您先前提供的名称。

现在,您必须更正另一个Git存储库的I应该指向何处的想法。您必须向其交付提交myBranch,然后告诉它:忘记提交J,只需将您的I名称提交myBranch这样,您的存储库都将根本无法找到提交J。您的Git将具有以下内容:

I

请注意,提交 J <-- myBranch (HEAD), origin/myBranch / ...--F--G--H \ I [abandoned] 尚未消失。它会保留一段时间(默认情况下至少为30天),以防您决定恢复原状。但是,查找会很困难。我使用I来代表其实际的哈希ID。但是它的实际哈希ID是什么?您将如何猜测其哈希ID可能是什么? (有一种方法,但是将其保存为另一个问题:-))

Andy said in his answer一样,您将需要使用I来强制服务器在记住新的替换提交git push -f的同时忘记提交I。只要可以使用服务器的J名称的每个人都事先同意以这种方式操纵这些名称,就可以了。如果您是唯一使用服务器存储库的人,则必须同意自己的观点,这是可以的。如果其他人正在使用它,请确保他们首先了解您在做什么!

答案 2 :(得分:1)

  

所以我正在修改现有的提交C1,我希望myBranch和origin / myBranch都应指向C1。

您已经在分支C1中编辑了一个提交,git将分支移至了新的提交。没有理由期望git会转移另一个分支,即本地或远程跟踪。

引用origin/myBranch(不完全是一个分支,它是一个引用)停留在旧提交上,因为远程跟踪引用仅在您获取/拉动/推动时移动。远程跟踪引用的初衷是记住与远程存储库进行通信时其上游远程分支在哪里。