我是 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
。
答案 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对象。这就是提交存储文件的方式:它有一个blob
,tree
有tree
个,可能还有blob
个,tree
■在签出提交时给出文件对象ID和工作树中使用的名称。
然而,专注于此的关键线是blob
行。该行给出了另一个提交的ID。
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个提交,我们可能会给它们所有的单字母名称HEAD
到master
(比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
。事实上,E
到side
的提交目前在两个分支上。
如果您现在删除名称 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
指向master
,H
指向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
另一方面,如果您使用强制推送(使用H
或master
),您的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
名称的命令是master
和H
(当master
指向git branch -f
时,即:{ {1}}调整当前分支,这是git reset
分的一个分支。
1 有一个相当奇特的协议交换,通过它你的Git和他们的Git可以找出你没有提交的提交,以及其他哪些对象,如果有的话,需要得到你的一切。但这些细节超出了本文的范围。
2 如果分支未更改为指向它们,您可能想知道这些提交会发生什么。接收推送的存储库通常是&#34;裸&#34;存储库,默认情况下没有reflogs,这些未使用的对象应该立即丢弃(通常是)。没有reflogs,如果你强行推动并且&#34;输掉&#34;提交,提交也通常会立即丢弃,所以要小心!丢失的提交通常来自某些 Git存储库某处,如果该存储库仍然完好无损,那些提交仍在其中 - 但您可能需要搜索找到那个存储库。