我不确定是否会问这个问题,因为关于github的场景太多。
所以我的情况如下:
问题: 我该如何进行git reset --hard操作,以便本地目录中的所有文件都与主BUT中的先前发行版相同,从而保留了我的工作?例如创建的新文件。
我尝试将git reset重置为上一发行版SHA,但是它将文件与最新发行版一起保留在本地工作目录中。
我尝试了git reset --hard,它删除了所有内容以及我自己的工作。
希望这种解释很清楚,对您有所帮助。
谢谢。
更新
对我的情况有更清晰的理解: 我将带有v2标签的回购主复制到我的PC。 我创建一个新的测试分支。 我添加新文件并提交,然后推送到测试分支。 现在,我的高级开发人员告诉我,带有v2标签的母版有问题,请我不要使用。 所以我的测试分支需要回退到v1标签。
如何做到这一点而不删除我提交的文件,而是删除由主v2标签创建的文件?
答案 0 :(得分:3)
您想要git rebase
,必须用力推动。
我使用[
git clone <url>
]将带有v2标签的仓库主库克隆到我的电脑上。我创建了一个新的测试分支。
让我们假设您使用以下方法:
git checkout -b test v2
其中v2
是有问题的标签。在Git中,分支名称(例如master
)和标签名称(例如v2
)之间是有区别的。差异实际上很小,分为两个密切相关的部分。
分支名称标识一个特定的提交。但是提交随时间而变化。该名称标识了我们想说的“最新”提交或“最新”提交,即“在分支上”。实际上,在您运行git checkout <branch-name>
并开始进行新的提交之后,它会自动为您自动更改。
标记名称标识一个特定的提交。它永远不会标识任何其他提交-它应该永远保持那个提交的名称。 (可以移动标签-任何人都可以这样做-但为了保持理智,请不要这样做。)而且,在git checkout <tag-name>
之后,您处于一种有点奇怪的状态,Git称之为分离式HEAD 。
所有这些的原因是,Git实际上根本不是关于分支的,而是有关 commits 的全部。提交是Git中的基本单位。每个提交都有一个丑陋的哈希ID,这对于该特定提交是唯一的。这些哈希ID实际上是提交的“真实名称”。因为哈希ID实际上是提交的 contents 的加密校验和,所以任何提交都不能更改,不能更改。如果您能以某种方式更改内容,那将更改哈希ID,这意味着您将进行新的提交。
这种密码校验和也是为什么提交ID似乎很荒谬,对人类几乎没有用。它们对人类无用的事实是为什么我们需要名称的原因,以及为什么我们拥有分支和标签名称的原因。
每个提交存储一堆东西,例如进行提交的人的姓名和电子邮件地址(以及时间戳),用于显示git log
的日志消息以及完整的内容每个源文件的快照。每个提交存储的内容之一是其 parent 提交的原始哈希ID。这些形成了一个向后看的链:
... <-F <-G <-H
(字母代表实际的哈希ID)。
给出提交H
的哈希ID,Git可以找到实际的提交本身。该提交包含提交G
的哈希ID,因此Git可以找到G
。找到G
之后,Git将获得F
的哈希ID,以便它可以找到F
,依此类推。因此,Git从最新版本开始一直往后退,一直往后退。
由于提交中的任何内容都无法更改,因此我们可以将其绘制为线性序列:
...--F--G--H
只要我们记得向后走 很容易,而向后走 却不容易。
进行新的提交只是一个问题:
选择一个分支名称,该名称还将选择其 last 提交:
...--F--G--H <-- branch-name (HEAD)
选择分支将特殊名称HEAD
附加到名称上,因此HEAD
现在同时是branch-name
和提交H
的同义词。
做一些工作,并使用git add
将文件复制回Git的 index (也称为 staging区域,有时也称为缓存,具体取决于谁在执行此调用)。
请注意,如果您在现有文件上使用git add
,则会将它们复制到索引中已位于索引上方的文件之上,以替换某些旧版本。如果在 new 文件上使用git add
,它们已被复制到索引中,但它们只是新文件,它们不会覆盖以前的任何索引副本。
运行git commit
。这会将索引副本冻结为已提交的版本。它还会从您的日志消息中收集信息,并将您作为此新提交的作者和提交者。此新提交的父是当前提交H
:
...--F--G--H <-- branch-name (HEAD)
\
I
并且,既然新提交已存在(因此已经获得了自己的新唯一哈希ID),Git只需将其哈希ID写入分支名称中,因此branch-name
现在指向提交{{1 }}:
I
因此,如果您做了...--F--G--H
\
I <-- branch-name (HEAD)
,则存储库中将有类似的内容。请注意,名称git checkout -b test v2
标识了其他(不同的,可能是更早的)提交,因此让我们将它们全部绘制:
v1
请注意,...--F <-- tag: v1
\
G--H <-- tag: v2, test (HEAD)
进行了设置,以便新的 git checkout -b name commit-ID
指向所选的提交 name
,然后使其通过在该提交中填充索引和工作树来实现当前功能,并在一个命令中将特殊名称commit-ID
附加到新的 HEAD
上。
我添加新文件并提交
确定,因此您在工作树中(工作所在的位置)创建了一个新文件,并运行name
。这会将git add newfile
复制到您的索引中(只有一个索引与此工作树一起),然后您运行newfile
进行新的提交git commit
:
I
然后推送到测试分支。
因此,您现在运行了:
...--F <-- tag: v1
\
G--H <-- tag: v2
\
I <-- test (HEAD)
这会将提交git push -u origin test
本身发送到您的Git记住的URL下,以I
的名称发送到另一个Git。请注意,那里有一个完整的单独的Git存储库!它具有自己的分支和标签,尽管因为标签永远不会移动或永远不会移动,所以您的标签及其标签都应就哪个提交哈希ID origin
和{{1}达成一致}指向。
发送了提交v1
后,您的Git然后要求其Git设置自己的分支名称v2
或在这种情况下 create 指向提交{{ 1}}。大概I
的Git都很好,所以做到了。
现在,我的高级开发人员告诉我,带有v2标签的master出现了问题,请我不要使用。因此,我的测试分支需要回退以使用v1标签进行掌握。
任何现有提交的任何部分都不能更改。这意味着提交test
停留在原处。如果您再进行几次提交,则它们全部卡住了:
I
您需要的是一系列新的提交,它们类似于 origin
,但在某些方面有所不同。特别是,您希望将新文件添加到I
中,如标签...--F <-- tag: v1
\
G--H <-- tag: v2
\
I--J--K <-- test (HEAD)
所指向。然后,Git应该提交该快照,重新使用来自提交I-J-K
的提交消息,使用新的且不同的哈希ID进行闪亮的新提交,我们将其称为F
,以表明它是副本之v1
:
I
已成功将I'
复制到I
,现在您希望Git以相同的方式将 I' <-- [somehow remembered as in-progress]
/
...--F <-- tag: v1
\
G--H <-- tag: v2
\
I--J--K <-- test
复制到I
,然后将I'
复制到{ {1}}:
J
最后,您希望Git将J'
的标签剥离提交K
,然后粘贴到提交K'
上,然后回到分支 I'-J'-K' <-- [somehow remembered as in-progress]
/
...--F <-- tag: v1
\
G--H <-- tag: v2
\
I--J--K <-- test
像往常一样,只不过现在意味着提交test
:
K
一旦所有事情都发生了,您就需要向K'
处的另一个Git发送新的提交test
并告诉K'
的Git移动他们的 I'-J'-K' <-- test (HEAD)
/
...--F <-- tag: v1
\
G--H <-- tag: v2
\
I--J--K [abandoned]
也指向I'-J'-K'
。
origin
为您做第一部分首先,您应该运行:
origin
如果一切看起来不错,那么您只需要:
test
此命令告诉Git:复制一些提交。要复制的提交是指从我当前的分支“上”(在技术上可以从其访问)的提交,减去名称“ K'
”也“上”的所有提交。放置它们的位置是在由名称git rebase
标识的提交之后。
git checkout test
git status # and make sure everything is committed!
的名称提交git rebase --onto v1 v2
,名称名称的提交v2
,依此类推。因此,这些提交不会被复制。v1
,名称提交v2
,名称H
和名称G
,名称test
,因此{{1} }是要复制的内容。K
告诉Git在哪里放置副本:在提交J
后,由I
命名。在复制过程结束时,Git将标签H
(当前分支)从提交I-J-K
中移出,并使其指向副本--onto v1
。
F
现在需要v1
由于您已经向test
发送了提交K
,因此他们具有:
K'
作为其提交集以及其分支和标签名称。不过,当然,如果从那以后您发送了git push
和--force
,则它们的I
指向提交origin
。现在,我们可以假定它们指向...--F <-- tag: v1
\
G--H <-- tag: v2
\
I <-- test
,并且它们不没有J
和K
,因为如果它们确实具有它们和它们的{{ 1}}指向他们的test
请注意,散列ID(以及底层提交本身)在每个 Git存储库中都是相同。这就是为什么哈希ID是内容的加密校验和的原因!只要您和他们具有相同的内容,您就拥有相同的提交,因此它具有相同的哈希ID。如果您具有不同的哈希ID,那么您将具有不同的内容和不同的提交。我们需要知道的是:您有这个哈希ID吗?
如果您没有滥用标记名称,那么所有Git存储库中的标记名称也都相同:相同的名称标识相同的提交。但是分支名称故意不同,因为Git的目的是继续添加 new 提交。
因此,既然您已经运行K
并放弃了 三个旧的提交I
,现在您将再次运行J
。这将向他们发送K
(您的新提交,您有他们没有的提交,您的Git和他们的Git可以仅通过哈希ID来告知它们),然后要求他们移动他们的从现在其存储库中指向的任何地方进行测试– test
或K
。您是在要求他们将其移至指向git rebase
的位置。
他们会说否。他们之所以拒绝的原因是,他们的Git会看到将他们的{em> {em} I-J-K
从git push origin test
或I'-J'-K'
(无论设置为现在)移动到{ {1}}将放弃提交I
(以及K
和K K'
起源test
测试I
K'` ,您需要这样做:
K
告诉他们:移动您的K'
指向I
,即使它放弃了一些提交!
(他们仍然可以说“不”,但是通常,如果您要告诉他们强行是您的您的分支,那么他们应该允许。)