我有点担心我当前的工作代码不会被搞砸。
所以说我有以下提交
7abd390c269d4abc9f3208d3c1b0a27ef5ae4162 -> Latest commit
5488da9335eb51eed274aa9b4a0f8f205643a8e0 -> 2nd commit
a31e310304404eb4452e6465e0a92dd472c5329a -> 1st commit
我使用
签出了首次提交git checkout a31e310304404eb4452e6465e0a92dd472c5329a
并开始在此处进行更改。现在,我想提交它并将其推送到github。
但是我想避免的事情 Git pull和Git合并
因为此时我已经做了很多更改。
问题:有人可以告诉我是否可以强制推送此提交,如果可以,那么怎么办?
答案 0 :(得分:1)
您可能想做的是为您的最新提交创建一个新的分支名称,例如git checkout -b newname
。然后,您可以将这个新名称用于所有工作,或者在您回到较早的名称并开始工作时,用它来记住您的提交。
我使用检出了第一次提交
git checkout a31e310304404eb4452e6465e0a92dd472c5329a
执行此操作将产生Git称为分离的HEAD
。尽管这听起来很可怕,但实际上并不是致命的,只是意味着HEAD
未附加。
通常,特殊名称HEAD
(用大写字母表示,就像这个 1 一样)附加到一些现有分支名称上。 Git中的分支名称是Git跟踪谁在做什么工作的方式。更具体地说,名称本身存储这些提交的原始哈希ID。因此,像master
或develop
这样的分支名称将存储那个很大的难看的哈希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
中可以找到A
。 A
没有父母,所以我们可以在这一点上停止。
现在,如果我们只有一个分支名称,则非常简单。我们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
附加到该分支名称。这里,第一点并不重要,仅因为master
和develop
都指向了相同提交。
如果您通过其原始哈希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
中找到H
和G
(然后是F
,依此类推)。但是,例如,如果您使用C
或git checkout master
,Git将使提交git checkout develop
或D
为当前提交,而会将分支名称写入{{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
时,它也会同时获得H
和H
,并且如果需要,还将获得H
和G
甚至是{{1 }}(假设这个特定的图形是正确的)。从本质上讲,F
向分支发送 all ,或者将它们发送到 (请参见Think Like (a) Git)分支,除非另一个Git已经存在有他们。然后,您的Git会为其Git设置一些名称,通常使用相同的名称,以记住 last 提交,即分支的方式。
如果您不希望保存两个C
和B
快照及其提交元数据,现在是时候使用A
或类似的方法来重写某些提交了,但这是一个另一个问题的主题(有关此问题,StackOverflow上已经有很多答案了。)
答案 1 :(得分:0)
如果您强行推送您的新提交,您将丢失其他两个在线提交(我假设您拥有一个新的本地提交,没有理由不这样做)。
如果您不想立即合并,请创建一个新的本地分支。但是您将“必须”将在线内容提取出来,然后合并。
拉和合并不会破坏您的本地合并,但是如果您留在同一分支上,则必须合并,因为每个使用git的命名分支只能有一个头提交。