在签出先前的提交并在其中进行更改后如何推送提交

时间:2018-10-27 18:50:06

标签: git

我有点担心我当前的工作代码不会被搞砸。

所以说我有以下提交

7abd390c269d4abc9f3208d3c1b0a27ef5ae4162 -> Latest commit 

5488da9335eb51eed274aa9b4a0f8f205643a8e0 -> 2nd commit

a31e310304404eb4452e6465e0a92dd472c5329a -> 1st commit

我使用

签出了首次提交
git checkout a31e310304404eb4452e6465e0a92dd472c5329a

并开始在此处进行更改。现在,我想提交它并将其推送到github。

但是我想避免的事情 Git pull和Git合并

因为此时我已经做了很多更改。

问题:有人可以告诉我是否可以强制推送此提交,如果可以,那么怎么办?

2 个答案:

答案 0 :(得分:1)

TL; DR

您可能想做的是为您的最新提交创建一个新的分支名称,例如git checkout -b newname。然后,您可以将这个新名称用于所有工作,或者在您回到较早的名称并开始工作时,用它来记住您的提交。

  

我使用检出了第一次提交

git checkout a31e310304404eb4452e6465e0a92dd472c5329a

执行此操作将产生Git称为分离的HEAD 。尽管这听起来很可怕,但实际上并不是致命的,只是意味着HEAD未附加。

通常,特殊名称HEAD(用大写字母表示,就像这个 1 一样)附加到一些现有分支名称上。 Git中的分支名称是Git跟踪谁在做什么工作的方式。更具体地说,名称本身存储这些提交的原始哈希ID。因此,像masterdevelop这样的分支名称将存储那个很大的难看的哈希ID,即a31e310304404eb4452e6465e0a92dd472c5329a。这使您(人类)可以为这个看似随机的事物分配一个有意义的名称。

您没有做正常的事情,这意味着您确实需要知道自己在做什么。否则,您将无法跟踪自己的提交,事情会很痛苦。


1 在Windows和MacOS上,可以小写使用head,但这是系统的意外/怪癖。最好坚持使用全大写形式,因为小写形式在Linux上不起作用。如果您不喜欢在所有大写字母中都输入HEAD,可以改用@来表示相同的意思。


提交和分支名称

让我们更多地讨论Git通常如何与分支名称一起使用,因为这部分有些棘手,但这是成功使用Git的关键之一。首先要了解的是,Git本身并不太在乎这些名称:它们主要供人类使用。 Git最在乎的是哈希ID,即那些难看的字母和数字大字符串。那些唯一地标识每个提交,并且提交是Git根本存在的原因。所以它们就是Git关心的。

让我们看一下一个很小的,几乎是新的存储库,其中只有三个提交。这三个提交将各自具有自己唯一的哈希ID,但是为了避免让我们烦恼,我将对它们使用单​​个大写字母。我将称第一次进行的提交A,第二次提交B和第三次提交C

每个提交都存储源树的完整快照,以及一些元数据,这只是git log可以打印 about 的东西的一个花哨的词提交:谁创建了它,何时创建,以及他们输入了什么日志消息。元数据还包括提交的 parent 提交的原始哈希ID,这是当 提交人时,所代替的提交。

换句话说,每个提交指向指向其先前的提交。因此,我们来画一下:

A  <-B  <-C

第一次提交A no 父级,因为它不能。但是提交B记住A是创建B时的提交,因此B指向A。同样,提交C为我们记住B是其父项,因此C指向B

这时我们和Git所需要的是一种找到提交C的丑陋哈希ID的方法。这就是分支名称的来源:分支名称master可以容纳C的丑陋的唯一哈希ID,如下所示:

A--B--C   <-- master

我们说分支名称master 指向链中 last 提交的。我们使用分支名称来获取哈希ID,这使我们(通过Git)可以提交C。提交内部有B的哈希,因此从C中可以找到B,从B中可以找到AA没有父母,所以我们可以在这一点上停止。

现在,如果我们只有一个分支名称,则非常简单。我们git checkout master,这使我们提交了C。然后,我们进行一些更改,git add更改后的文件,然后运行git commit。这将打包新快照,添加我们自己的名称和当前时间,添加我们的日志消息,保存C的哈希ID,并进行新的提交,该提交现在将获得一个新的,唯一的,丑陋的哈希ID ,现在关键步骤发生了: Git将新的哈希ID写入名称master

A--B--C--D   <-- master

但是如果我们有两个分支名称怎么办?让我们现在创建一个新的分支名称:

A--B--C--D   <-- master, develop

现在,两个名称都存储 same 哈希ID,即提交D的哈希ID。现在,Git需要知道要更新哪个分支名称。为此,Git将HEAD附加到其中一个,无论我们给git checkout提供哪个。例如:

git checkout master

A--B--C--D   <-- master (HEAD), develop

但是:

git checkout develop

A--B--C--D   <-- master, develop (HEAD)

无论哪种方式,我们都从提交D开始。区别在于,当我们进行新的提交E时,哪个分支 name 会更改:

A--B--C--D   <-- master
          \
           E   <-- develop (HEAD)

这里要记住的事情是:每当我们添加一个新的提交时,Git都会使新的提交以先前的当前提交作为其父提交。然后,Git将 new 提交的ID写入附有HEAD分支名称

请注意,git checkout步骤执行了以下两项操作:

  • 它选择分支名称指向的提交;和
  • 它将HEAD附加到该分支名称。

这里,第一点并不重要,仅因为masterdevelop都指向了相同提交。

一个分离的HEAD几乎相同,但是不涉及分支名称

如果您通过其原始哈希ID git checkout提交,则Git仍会选择该提交为当前提交,但是这次,它附加{{1} }为分支名称。例如,假设您选择了提交HEAD的哈希ID。没有指向C的分支名称,因此Git必须这样做:

C

我们在这里所做的只是将 E <-- develop / D <-- master / A--B--C <-- HEAD D向上摆动,以便我们可以在指向直接提交E的过程中绘制HEAD。 Git在这里所做的是将原始哈希ID写入名称C中,而不是在HEAD中使用分支名称。 (如果需要,您可以查看文件HEAD:它包含这样的原始哈希ID,或者,如果附加到分支,则包含文本.git/HEAD。)

现在,我们可以照常进行新的提交,方法是编辑文件,根据需要运行ref: refs/heads/branch,然后运行git add。 Git照常收集我们的提交消息,并照常进行新的提交。新提交的父提交是当前提交(提交git commit),其哈希ID位于C文件中。然后,Git不会将新提交的哈希ID写入HEAD文件中的分支名称,而是将新提交的ID直接写入HEAD

HEAD

如果您继续进行两次提交,则该过程将简单地重复:

          E   <-- develop
         /
        D   <-- master
       /
A--B--C--F   <-- HEAD

请注意,Git现在可以轻松地找到提交 E <-- develop / D <-- master / A--B--C--F--G--H <-- HEAD ,因为它的哈希ID位于H文件中。 Git可以从HEAD中找到HG(然后是F,依此类推)。但是,例如,如果您使用Cgit checkout master,Git将使提交git checkout developD为当前提交,而会将分支名称写入{{1 }}文件,丢失了E的哈希ID

如果将哈希ID保存在某个地方,则仍可以使用它一段时间。 2 但是,变得很难找到,丢失在几乎相同的提交中,主要由它们看似随机的哈希ID来区分,这变得很难找到。最好的办法是给它起一个名字。


2 默认情况下使用HEAD reflog 至少持续30天。 reflog包含H在过去30到90天内或更长时间内命名的所有哈希ID,具体取决于我将不在此处讨论的许多细节,因为这已经太长了。 :-)


为提交哈希提供名称

那么,您现在可能想要做的就是分配一个名称来记住哈希ID HEAD。这就是分支名称和标记名称的用途。如果您现在立即创建一个 new 分支名称,则默认情况下,它将指向当前提交

HEAD

请注意,这没有 H附加到名称git branch xyzzy E <-- develop / D <-- master / A--B--C--F--G--H <-- HEAD, xyzzy 。创建名称后,您可以HEAD告诉Git从现在xyzzy指向的提交git checkout xyzzy切换到H指向的提交HEAD。该开关不执行任何操作,但是现在H的第二部分适用,Git将xyzzy附加到名称git checkout上:

HEAD

您可以使用xyzzy一次完成这两项操作:创建 E <-- develop / D <-- master / A--B--C--F--G--H <-- xyzzy (HEAD) ,指向当前提交,同时将git checkout -b xyzzy附加到新名称{{ 1}}。

完成此操作后,现在将为提交使用名称。现在可以使用xyzzy将提交发送到另一个Git存储库,告诉其他Git存储库将 its HEAD设置为指向提交xyzzy(无论如何) git push的实际哈希ID是)。

当其他Git存储库获得xyzzy时,它也会同时获得HH,并且如果需要,还将获得HG甚至是{{1 }}(假设这个特定的图形是正确的)。从本质上讲,F向分支发送 all ,或者将它们发送到 (请参见Think Like (a) Git)分支,除非另一个Git已经存在有他们。然后,您的Git会为其Git设置一些名称,通常使用相同的名称,以记住 last 提交,即分支的方式。

如果您不希望保存两个CB快照及其提交元数据,现在是时候使用A或类似的方法来重写某些提交了,但这是一个另一个问题的主题(有关此问题,StackOverflow上已经有很多答案了。)

答案 1 :(得分:0)

如果您强行推送您的新提交,您将丢失其他两个在线提交(我假设您拥有一个新的本地提交,没有理由不这样做)。

如果您不想立即合并,请创建一个新的本地分支。但是您将“必须”将在线内容提取出来,然后合并。

拉和合并不会破坏您的本地合并,但是如果您留在同一分支上,则必须合并,因为每个使用git的命名分支只能有一个头提交。