除了最后一个提交之外,为什

时间:2017-01-05 11:12:22

标签: git github

我是 git 的新手。 5天前,我已通过此命令在本地文件夹(名为 myproject 上初始化了git:

$ git init

此外,我已通过此命令将所有文件夹/文件(存在于 myproject 文件夹中)添加到舞台:

$ git add .

然后通过此命令提交它:

$ git commit -m "my first commit"

之后,我为它设置了一个遥控器:

$ git remote add origin https://...

然后通过此命令将所有内容推送到github上的存储库中:

$ git push -u origin master

从5天前到昨天,我已经做了7次更改。

注意:所以我昨天有8次提交。

今天,我试图获得一些关于git命令的新体验,现在令人惊讶的是我只面对一个提交(只是最后一个存在,所有其他以前的提交都没有了) 。老实说,我不知道我做了什么。但我怀疑这个命令:

$ git push -f origin {token}:master

它会删除除最后一个提交之外的所有其他提交吗?如果没有,你知道我做了什么吗? (我想知道,因为我想避免再次这样做)

编辑:我不确定这是否重要,但当我使用命令(以获得经验)时,我也创建了1分支checkout

1 个答案:

答案 0 :(得分:1)

  

git push -f删除[some]提交...?

可能,是的。这取决于你推动的是什么。

  

今天,我试图获得一些关于git命令的新体验,现在令人惊讶的是我只面对一次提交(只存在最后一次提交,所有其他先前的提交都已消失)。

首先,让我们注意Git非常努力永远不要放弃任何提交,所以你可能没问题(只要你没有从{{1}中删除文件无论如何目录本身。)

其次,让我们快速了解一下提交的工作原理。

在Git中,提交本身就是一个相当小的对象。以下是Git自身的Git存储库提交示例(但.git替换为@):

这是一个完整的提交,即ID为$ git rev-parse HEAD e05806da9ec4aff8adfed142ab2a2b3b02e33c8c $ git cat-file -p HEAD | sed 's/@/ /' tree 14b1822ef3c838411b7b204e03b7272b8ddd63d3 parent af09003b2897db76cefdb08ab363ed68f2bb295b author Junio C Hamano <gitster pobox.com> 1482859071 -0800 committer Junio C Hamano <gitster pobox.com> 1482859071 -0800 Fourth batch for 2.12 Signed-off-by: Junio C Hamano <gitster pobox.com> 的提交。但是,这个提交并不完全孤立。请注意e05806d...tree行。 parent行给出了另一个Git对象的ID,我不会完全引用它,但会显示顶部,给出一点味道:

tree

正如您所看到的,此$ git cat-file -p HEAD: | expand | head -5 100644 blob 320e33c327c6f597bcfd255b13876f21b0b2d8aa .gitattributes 100644 blob 6722f78f9ab7e9647a3358a52e627f1c9e83f685 .gitignore 100644 blob 9cc33e925de8adc562daf9b176135aba7b5e4d9b .mailmap 100644 blob 3843967a692d1642e43f536d5e2652b566ca554d .travis.yml 100644 blob 536e55524db72bd2acf175208aef4f3dfc148d42 COPYING 依次引用tree s,它们是保存文件内容的Git对象。这就是提交存储文件的方式:它有一个blobtreetree个,可能还有blob个,tree ■在签出提交时给出文件对象ID和工作树中使用的名称。

然而,专注于此的关键线是blob行。该行给出了另一个提交的ID。

Git向后工作

Git使用这些parent ID,它们都指向向后(从较新的提交到较旧的提交),以构建Git称之为&#34;提交图形&#34;,或者 D 竖立 A 循环 G raph或 DAG 。我们可以自己绘制这个DAG,用代表提交的轮parent替换每个大丑陋的SHA-1哈希ID:

o

这是一个非常简单,完全线性的存储库图,其中只有三个提交。名称o <- o <- o <-- master - 分支名称 - &#34;指向&#34;最新的提交。也就是说,它有一个丑陋的SHA-1哈希ID。 (名称master,顺便说一句,&#34;指向&#34;大部分时间都是主人。如果你检查HEAD,你会看到该文件由一行读取:{{ 1}}。)

Git使用.git/HEAD的内容(由ref: refs/heads/master指示)来查找最近提交的哈希ID。然后,它使用该提交的master行来查找上一个(第二个)提交,并使用该HEAD行来查找先前(第一个)提交。第一个提交根本没有parent行,因为之前没有提交,所以进程就在那里停止了,Git可以向我们显示所有三个提交。

同样,它只是从parent开始 - 它说&#34;看主人&#34; - 然后转到parent,它会提供最新 {{ 1}} - 提交ID,然后每个提供给你(和Git)一个早期的ID。

你的八次提交

总共有8个提交,我们可能会给它们所有的单字母名称HEADmaster(比40个字符的非明显哈希更容易使用),并绘制他们是这样的:

master

这些都在您的存储库中,虽然我没有在此时间内绘制内部箭头,A指向H,指向A--B--C--D--E--F--G--H <-- master ,依此类推到H

但是,如果您创建了多个分支,则会得到需要绘制多行的内容。例如,假设您在G上通过F提交了A,但随后运行A以创建名为E的新分支:

master

请注意,此时git checkout -b side指向提交side,就像A--B--C--D--E <-- master, HEAD->side 一样。但是side指向E而不是master,现在您在HEAD而不是side上进行最后三次提交;而这种情况发生了:

master

现在side指向master,而A--B--C--D--E <-- master \ F--G--H <-- HEAD->side 仍然指向side。如果您H并运行master,Git将首先阅读E(现在将指向git checkout master而不是git log),然后阅读提交HEAD,然后master返回side,您只会看到五个提交,而不是全部八个。

如果您运行E,您将看到所有八次提交。这是因为D不是来自A,而是来自git log side,它指向git log。请注意,HEAD仍然指向side,因此记录从H顺利过渡到F。事实上,Eside的提交目前在两个分支上。

如果您现在删除名称 master,则会发生以下情况:

A

通过E提交side似乎完全消失了!但这是关键所在:他们还在那里。它们受到Git称为 reflog 的保护。每个分支名称都有一个reflog,A--B--C--D--E <-- HEAD->master \ F--G--H [abandoned] 有一个。删除分支时删除分支F的那个,因此它不再保护提交;但H的那个仍然存在,并继续保护提交(默认情况下为30天,之后reflog条目本身就会过期)。

因此,要查找提交HEAD,您可以查看reflog。运行side(或仅HEAD)以查看H的reflog。你怎么知道哪个提交是什么,当他们拥有的是大丑陋的哈希ID?好吧,你可以git reflog HEAD任何有希望的ID或其reflog名称,看看你是否能找到它。它往往有点困难,特别是如果reflog中充满了各种无用的剩余物(通常就是这种情况)。

您还可以使用git reflog,这会为您提供更长的输出,这可能会有所帮助。或者,您甚至可以使用HEAD将它们全部视为补丁。

git show怎么样?

我提到git log -g HEAD可能会删除一些提交。让我们回到那些图表绘图,特别是git log -g -p HEAD上所有八个提交的绘图:

git push

您的存储库中的内容。但是在其他地方还有另一个独立的Git存储库,特别是在你称之为git push的机器上。 那个 Git存储库有一些提交,一些分支名称指向每个分支的提示提交。正如您的master指向提交A--B--C--D--E--F--G--H <-- master 一样,他们的主要指向一些提交。

如果您运行origin 而不 master - H是&#34;强制&#34;标志,也可拼写为git push,以便你-f,然后你的Git会将你的提交发送给他们的Git, 1 ,然后礼貌地问他们,如果他们愿意将-f设置为指向提交--force,就像你的那样。

如果他们git push origin master指向master左侧的某个位置(即H中的任何提交),那么他们就可以安全地master {1}}指向新的H提交,因为A-B-C-...-G指向masterH指向H,依此类推到G。这意味着他们不会失去&#34;任何提交。 (从G现在指向F的任何地方的动作都称为快进操作。)但如果他们还有其他一些你缺的提交,那么他们的A指向别的东西 - 即使它有点不同 - 他们会拒绝你的礼貌请求,因为这会失去工作。例如,假设代替master - 通过 - H,他们有:

master

您将他们添加到其存储库的A链交给他们:

H

然后让他们将A--B--C--D--E--I 设置为F-G-H;他们会说:&#34;不,那不是一个快进的&#34;。他们将A--B--C--D--E--I \ F--G--H 指向他们的提交master 2

另一方面,如果您使用强制推送(使用Hmaster),您的Git会向他们发送请求而不是礼貌请求:&#34;设置你的I就是我所说的!&#34;他们 没有服从,但他们通常会这样做。如果这意味着失去提交,那么,那就失去了承诺。

你表示:

-f

没有解释--force的含义。 master中的$ git push -f origin {token}:master 字符串称为 refspec ,冒号左侧的内容是refspec的源部分,右边的东西 - token,在这种情况下 - 目的地。如果你在这里放一个哈希ID,你的Git会查找哈希ID,确保它引用你的存储库中的某个提交,然后向他们的Git发送一个请求或命令(通常依赖于force标志)他们将他们的目的地名称设置为您指定为来源的ID。

如果这是一个命令,并且您使用了force,并且源ID导致它们丢失了提交,那么它们可能会丢失这些提交。

但请注意,这不会使失去提交。 您的 token:master仍应指向您的提交git push。移动您的 master名称的命令是masterH(当master指向git branch -f时,即:{ {1}}调整当前分支,这是git reset分的一个分支。

1 有一个相当奇特的协议交换,通过它你的Git和他们的Git可以找出你没有提交的提交,以及其他哪些对象,如果有的话,需要得到你的一切。但这些细节超出了本文的范围。

2 如果分支未更改为指向它们,您可能想知道这些提交会发生什么。接收推送的存储库通常是&#34;裸&#34;存储库,默认情况下没有reflogs,这些未使用的对象应该立即丢弃(通常是)。没有reflogs,如果你强行推动并且&#34;输掉&#34;提交,提交通常会立即丢弃,所以要小心!丢失的提交通常来自某些 Git存储库某处,如果该存储库仍然完好无损,那些提交仍在其中 - 但您可能需要搜索找到那个存储库。