我提交并将一些文件推送到远程。然后发现自己有什么地方不对,想恢复推送做一些编辑。我隐藏、恢复并想重新应用存储,但应用后,我的工作目录仍然缺少文件。请帮忙。这是历史。
$ git commit -m "Model package"
[dev ec5e61d] Model package
40 files changed, 1306 insertions(+), 110 deletions(-)
$ git push
$ git log
commit ec5e61d2064a351f59f99480f1bf95927abcd419
Author: Me
Date: Mon Feb 6
Model package
$ git revert ec5e61d2064a351f59f99480f1bf95927abcd419
error: Your local changes to the following files would be overwritten by merge:
model/R/train.R
Please, commit your changes or stash them before you can merge.
Aborting
$ git stash
Saved working directory and index state WIP on dev: ec5e61d Model package
HEAD is now at ec5e61d Model package
$ git revert ec5e61d2064a351f59f99480f1bf95927abcd419
[dev 062f107] Revert "Model package"
40 files changed, 135 insertions(+), 1331 deletions(-)
$ git stash apply
CONFLICT (modify/delete): model/R/train.R deleted in Updated upstream and modified in Stashed changes. Version Stashed changes of model/R/train.R left in tree.
$ git stash apply
model/R/train.R: needs merge
unable to refresh index
$ git commit model/R/train.R
[dev ed41d20] Resolve merge conflict
1 file changed, 138 insertions(+)
create mode 100644 model/R/train.R
$ git stash apply
On branch dev
Your branch is ahead of 'origin/dev' by 2 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: scripts/gm.r
Run Code Online (Sandbox Code Playgroud)
但是我的文件没有从藏匿处返回!
$ git stash list
stash@{0}: WIP on dev: ec5e61d Model package
$ git stash show
model/R/train.R | 79 ++++++++++++++++++++++----------------------
scripts/gm.r | 87 ++++++++++++++++++++++++++++++++-----------------
2 files changed, 97 insertions(+), 69 deletions(-)
Run Code Online (Sandbox Code Playgroud)
你在这里混合了很多东西。特别是,您使用了git stash 和 git revert,同时还进行了各种提交。我们需要把它们分开。
git stash作用虽然git stash会变得很复杂,1它的正常使用还是相当简单的。您创建了一个 stash,它是各种提交。这具有“清理”工作树和索引的副作用,a la git reset --hard HEAD(实际上里面有一个文字git reset --hard HEAD命令git stash)。然后你切换到其他一些提交,git stash applystash,如果一切顺利,git stash dropstash。
该apply步骤将您所做的特殊存储提交转换为补丁,然后像git apply -3这样应用它,必要时进行三向合并。因此,如果你想用普通来模拟简化的过程,git commit你可以这样做——你不需要这个git reset --hard部分,因为你为新提交创建了一个真正的分支:
git checkout -b tempbranch
git add -A
git commit -m 'stash my work-in-progress'
git checkout <something else>
git show tempbranch | git apply -3
Run Code Online (Sandbox Code Playgroud)
要删除提交(例如删除存储),您只需删除临时分支:
git branch -D tempbranch
Run Code Online (Sandbox Code Playgroud)
尽管如此,无论您如何保存和应用存储,提取存储可能会导致执行三向合并。因此,它可能会发生合并冲突。这就是你遇到的。
1主要并发症是:git stash节约,分别,两者目前的指数和当前工作树,如两次提交(无分支)。因此,这很像运行git commit两次。但是,当您使用git stash apply,它结合了这两个提交到工作树一个变化,在默认情况下。
如果您在进行 stash 之前没有进行任何准备工作,那么这种复杂性就变得无关紧要了。或者,如果您暂存了所有内容,然后没有更改工作树,这也会使这种复杂性变得无关紧要。在任何情况下,如果您在没有 的情况下应用存储--index,如果需要,应用步骤将有利于工作树更改而不是索引更改。
另一个复杂的问题是,如果您将-uor-a选项添加到您的git stash save步骤中,Git 会保存第三次提交。这些三提交隐藏更难应用,应谨慎使用。
在我们继续讨论合并冲突之前,让我们简要地看一下 Git 提交中的内容,以及补丁(或差异)是什么。
一个承诺是一个快照。这是您告诉 Git 要跟踪的所有文件,它们以您运行git commit. 更具体地说,它是当时索引中的内容。索引,也称为暂存区,是您告诉 Git 在下一次提交中保存什么的地方,git add用于更新索引中的项目。
提交不是补丁。这不是变化:它是快照。但是,每2 个提交都有一个父级(或“上一个”)提交,并且 Git 可以轻松地将提交与其父级进行比较。这会将提交变成补丁:
因此,提交可以成为补丁。补丁只是简单的指令:添加这些东西,删除这些其他东西。您(或 Git)可以将补丁应用到其他一些提交,然后根据结果进行新的提交。如果应用程序删除 C 并添加 D,并且您进行新提交,则新提交的补丁是“删除 C 并添加 D”——这也是旧提交的补丁。
将提交转换为补丁,然后在其他地方再次播放,重复更改。
2至少有一次提交——你做过的第一个提交——是特别的,因为它没有以前的提交,即没有父提交。此外,合并提交很特别,因为它们有两个(甚至更多)父级。但是我们在这里最感兴趣的是普通的单亲提交。
因为git stashmake commits,所以 stash commits 也有父级。实际上,这就是 Git 将 stash 转换为补丁的方式,与 Git 将普通提交转换为补丁的方式非常相似。
git cherry-pick和git revert(注意:你只是git revert直接使用,但我在这里包括两者,因为它们非常相似。)
当我们将提交变成补丁时,如上所述,然后将其应用到另一个分支,3这是一个git cherry-pick操作。我们在说“我们上次在那边做的改变是个好主意:让我们再次做同样的改变,在这里。” 我们可以对相同的文件进行相同的更改——这总是有效的——或者对仅仅足够相似的文件进行相同的更改,以便我们实际上可以进行相同的更改。
“应用补丁”和“制作樱桃挑选”之间的本质区别在于“制作樱桃挑选”需要源代码提交,而不仅仅是补丁。默认情况下,git cherry-pick还会自动进行新的提交——因此这也可以重用原始提交的提交消息。
一旦您了解了补丁及其git cherry-pick工作原理,git revert它的作用就变得非常明显:它只是反向应用补丁。你将 Git 指向某个特定的提交,并告诉它撤消该提交所做的任何事情。Git 将该提交转换为补丁;补丁说“删除C并添加D”;然后 Git 删除 D 并添加 C。与 一样git cherry-pick,git revert默认情况下根据结果进行新的提交。
3从技术上讲,我们只需要将它应用到另一个提交。但这涉及到我们所说的“分支”是什么意思的问题:请参阅我们所说的“分支”究竟是什么意思?
合并背后的想法很简单。我们——不管“我们”是谁——做了一些改变,而他们——不管“他们”是谁——做了一些改变——但我们都从同一个快照开始。我们要求 Git 将我们的更改和他们的更改结合起来。例如,假设你们都以, , 和开头,并且您更改了文件而他们没有,而他们更改了文件而您没有。这真的很容易组合:将您修改的和他们修改的. 在F1F2F3F1F2F1F2F3,您更改了文件顶部附近的一行,而他们更改了靠近底部的一行。这有点难以结合,但仍然不是问题:Git 只需要两个更改。
但是,有些更改根本无法合并。这些变化是合并冲突。
合并冲突有多种形式。最常见的情况发生在“你”(在你的提交中)和“他们”(在他们的提交中)修改同一文件中的同一行时。当我们接受我们的提交并将其/它们转换为补丁时会发生这种情况——“删除 C 并添加 D”——但他们的更改表示保留C 并添加 E。Git 不知道要使用哪个更改:我们应该保留 C 并添加 D 和 E,还是删除 C 并仅保留 E,或者什么?
但是,这不是您得到的:您遇到了修改/删除冲突。当您说在 file 中train.R,我们应该删除 C 并添加 D时,就会发生修改/删除冲突-他们说“扔掉整个train.R文件”。
在所有合并冲突的情况下,Git 都会举手示意并暂时放弃进行合并。它将冲突文件写入您的工作树,声明合并冲突,然后停止。现在是你的工作完成的合并,想出了在正确的快照,然后git add和git commit结果。
当然,合并冲突可能会在您执行操作时发生git merge(此时很清楚哪个是“我们的”,哪个是“他们的”)。4 但它们也可能发生在git apply -3(-3意思是“使用三路合并”)git cherry-pick、 和 期间git revert。如果补丁没有完全应用,Git 可以找出要使用的公共基础,然后找出更改的两个部分:您正在应用的补丁中的内容,以及从公共基础到您正确提交的更改现在,您正在尝试使用git cherry-pickor进行修补git revert。
4 “我们的”版本在所有情况下都是HEAD版本。在 rebase 期间也是如此,但 rebase 是通过重复一系列的cherry-picks 来工作的,此时,HEAD是您正在构建的新替换分支。请参阅CommaToast 的评论:“由于头部是心灵的所在地,它是身份的来源,它是自我的来源,因此将 HEAD 指向的任何东西视为‘我的’更有意义。 ..”。
git commit我在上面提到过,您通常将git add文件从工作树复制到索引/暂存区。这也会在合并冲突期间将文件标记为已解决,这就是为什么您的工作是首先编辑文件,然后对其进行使用。一旦您修复并-ed 所有文件,您就可以进行合并。这完成了合并,或樱桃选择或恢复或重新设置步骤或您正在做的任何有冲突的事情。git addgit addgit commit
当你git commit像这样运行时,没有特定的命名文件,Git 提交索引(暂存区)内容。结果,新HEAD提交与索引匹配。我们稍后会用到这个事实。
git commit <path>但是,如果您运行,这会自动暂存命名文件(就像您运行了一样git add file),然后提交 - 好吧,有些东西。这部分有点棘手。默认行为就像您运行git commit --only <path>.
使用--only,Git不会接受任何其他已经暂存的文件。也就是说,如果你已经git add编五个文件,没有命名README.txt,然后运行git commit README.txt,Git的使得新提交使用工作树版本的README.txt,但HEAD其他五个文件的版本。换句话说,它只更改命名文件,保留所有其他文件的先前版本。
使用--include,Git<file>也会暂存命名的,并提交整个结果。也就是说,这就像git add <file>在运行之前做的一样git commit。这比--only行为更容易解释,但当然,如果你想要这个,你可以只git add <file>.
但是,在任何一种情况下,一旦进行了新的提交,<file>就已经上演了,就好像您已经git add对其进行了-ed 一样。所以在我们的--only例子中,有五个文件add-ed,索引现在更新了所有六个文件。当然,有一个新的HEAD提交,README.txt现在匹配新的HEAD提交,所以只有五个不同的阶段。
您显示的第一个命令是:
$ git commit -m "Model package"
[dev ec5e61d] Model package
40 files changed, 1306 insertions(+), 110 deletions(-)
Run Code Online (Sandbox Code Playgroud)
我们没有看到您git add的任何s,但是您必须添加了 40 个文件,以便 Git 说“40 个文件已更改”。但你也必须不能有git add-ed的东西,我们将看到一个时刻。
接下来,您显示:
$ git push
Run Code Online (Sandbox Code Playgroud)
没有输出。目前尚不清楚这推送了什么(如果有的话)(这取决于您的push.default配置,以及当前分支是否具有上游设置,等等)。然而,无论你推送了什么,你自己本地的仓库内容都没有改变,后来的证据表明推送成功了,将提交推ec5e61d送到origin.
现在您显示两个带有一些输出的命令:
$ git log
commit ec5e61d2064a351f59f99480f1bf95927abcd419
Author: Me
Date: Mon Feb 6
Model package
Run Code Online (Sandbox Code Playgroud)
这是您所做的提交:ec5e61d, on branch dev,如git commit输出所示。
$ git revert ec5e61d2064a351f59f99480f1bf95927abcd419
error: Your local changes to the following files would be overwritten by merge:
model/R/train.R
Please, commit your changes or stash them before you can merge.
Aborting
Run Code Online (Sandbox Code Playgroud)
这很有趣,因为它表明您的工作树中有更改,model/R/train.R即使您git commit最近运行,也进行了ec5e61d提交。
这意味着无论是在指数的model/R/train.R不匹配什么是现在的工作树model/R/train.R。从中我们无法准确判断索引中的内容,只是它与工作树不匹配。
如果git revert说Aborting,这意味着它在所有什么也没做:它发现你的工作树是不是“干净”,即不匹配的HEAD承诺ec5e61d。默认情况下git revert要求工作树与HEAD提交匹配。
接下来,你运行:
$ git stash
Saved working directory and index state WIP on dev: ec5e61d Model package
HEAD is now at ec5e61d Model package
Run Code Online (Sandbox Code Playgroud)
该git stash命令作出了承诺,说真的,两次提交,一个用于索引,一个用于工作树; 但是索引与HEAD提交相同,所以我们真正需要关心的是工作树提交——然后“清理”工作树以匹配HEAD提交。
换句话说,stash提交——或者更确切地说,我们关心的两个提交之一——具有model/R/train.R. 它还具有已修改(和跟踪)但尚未提交的任何其他文件的工作树版本,以及仍然与提交HEAD或ec5e61d提交匹配的所有剩余文件的工作树版本。(与往常一样,每次提交都包含每个文件。这就是“作为快照”的含义。稍后我们将能够更多地了解隐藏的工作树提交和ec5e61d提交之间的不同之处。)
一旦存储git stash了所有这些,它就会用于git reset --hard HEAD丢弃索引和工作树更改。(它们被安全地保存在 stash 中,因此在索引和工作树中不再需要。)所以现在您model/R/train.R的工作树中的文件与HEAD.
现在你重新运行你的git revert:
$ git revert ec5e61d2064a351f59f99480f1bf95927abcd419
[dev 062f107] Revert "Model package"
40 files changed, 135 insertions(+), 1331 deletions(-)
Run Code Online (Sandbox Code Playgroud)
而这一次,由于您正在恢复刚刚提交的更改,因此“反向修补”很容易成功(撤消您刚刚所做的事情很简单)。Git062f107在 branch 上进行了一个新的提交 , ,dev所以最后两个提交是:
... <- ec5e61d <- 062f107 <-- dev
Run Code Online (Sandbox Code Playgroud)
存储本身附加到提交ec5e61d(每个存储直接附加到HEAD您进行存储时的提交)。
现在您尝试应用存储,但由于合并冲突而失败:
$ git stash apply
CONFLICT (modify/delete): model/R/train.R deleted in Updated upstream
and modified in Stashed changes. Version Stashed changes of
model/R/train.R left in tree.
Run Code Online (Sandbox Code Playgroud)
这特别有趣,因为这意味着它model/R/train.R必须已被revert commit删除062f107,这意味着它必须已添加到 commit 中ec5e61d。同时,存储提交在转换为补丁时会显示“将此更改为model/R/train.R.”。
由于 Git 不知道如何将“完全删除此文件”与“进行此更改”结合起来,因此它在工作树中保留了整个隐藏的工作树版本model/R/train.R。现在你的工作是弄清楚工作树中应该有什么,以及git add那个。
同时,Git 应该作为补丁应用的任何其他更改,Git确实作为补丁应用于所有其他文件。这些更改被暂存以进行提交,即,这些文件的索引已经更新。
接下来,你运行:
$ git stash apply
model/R/train.R: needs merge
unable to refresh index
Run Code Online (Sandbox Code Playgroud)
这会尝试再次应用存储,但不能,所以(幸运的是)它什么都不做。这使未完成的合并未完成。
然后你跑了:
$ git commit model/R/train.R
[dev ed41d20] Resolve merge conflict
1 file changed, 138 insertions(+)
create mode 100644 model/R/train.R
Run Code Online (Sandbox Code Playgroud)
请注意,这具有git commit <file>形式。这git add小号model/R/train.R从工作树,然后跳过其他添加的文件,使新HEAD提交ed41d20的结果。所以这个新提交的所有文件都与旧的HEAD 062f107 相同,除了 model/R/train.R:
... <- ec5e61d <- 062f107 <- ed41d20 <-- dev
Run Code Online (Sandbox Code Playgroud)
现在你又跑git stash apply了:
$ git stash apply
On branch dev
Your branch is ahead of 'origin/dev' by 2 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: scripts/gm.r
Run Code Online (Sandbox Code Playgroud)
就像上次一样,这会将存储提交变成补丁并尝试应用该补丁。补丁没有正确应用,所以 Git 尝试了三路合并:找到基础版本,找到你的 HEAD ( ed41d20) 版本(“我们的”)中的内容,比较那些,然后将该差异与补丁结合起来。结果证明该差异与补丁完全相同,即补丁已经应用。所以你会得到最后的输出git status,它显示了一个修改过的文件,以及两个提交——当然,这些必须是062f107和ed41d20——你已经有了,origin还没有。
最后,你展示这些:
$ git stash list
stash@{0}: WIP on dev: ec5e61d Model package
$ git stash show
model/R/train.R | 79 ++++++++++++++++++++++----------------------
scripts/gm.r | 87 ++++++++++++++++++++++++++++++++-----------------
2 files changed, 97 insertions(+), 69 deletions(-)
Run Code Online (Sandbox Code Playgroud)
这完成了我们对存储中内容的了解:它就像提交一样,ec5e61d除了两个文件model/R/train.R和scripts/gm.r. 在git diff从提交指令转换ec5e61d到工作树犯藏匿5发言权删除69条原线,并插入97条更换线路,在这两个文件。
这就是git stash apply试图这样做,只是它不能删除任何从一个完全删除的行model/R/train.R,所以它只是把整个保存工作树stash@{0}文件的版本model/R/train.R在工作树。
5之前,我们再次证明了存储中的索引提交与ec5e61d提交完全匹配。因此,我们可以忽略索引提交,而只关注工作树提交。
此时您应该做什么取决于您想要什么结果。我不能给你正确的答案,但我可以给你一些重要的问题:
ec5e61d?(即,其他人是否有权访问您推送提交的上游存储库ec5e61d?)Commitec5e61d存在于您的存储库和您将其推送回git push命令的存储库中。在另一个存储库中,名称dev可能指向ec5e61d. model/R/train.R与其父提交相比,它具有文件作为新文件,以及对其他 39 个文件的附加更改或新创建。
提交062f107仅存在于您的存储库中。这是对 commit 的完全退出,ec5e61d因此,它可能没有用,除非您需要确保完全退出ec5e61d任何其他存储库。
提交ed41d20也仅存在于您的存储库中。它的无论是在副本父的ec5e61d不同之处在于它具有的藏匿版本model/R/train.R添加。
您当前的工作树大部分匹配,ed41d20除了它已scripts/gm.r修改,在补丁ec5e61d和存储提交之间的任何更改,在工作树中也以相同的方式更改。并且,由于ed41d20大部分匹配 的父级ec5e61d,因此 的父级ec5e61d和工作树之间的差异等于您scripts/gm.r在存储中更改的任何内容,以及 的隐藏版本model/R/train.R。
你还有藏匿处。如果那有用,请保留它,直到它不再有用为止。一旦您确定它不再有用,请使用git stash drop删除它(此步骤极难撤消,因此请确保您已完成)。
您可能希望完全丢弃最近的两次提交(还原和仅添加一个文件版本的提交model/R/train.R)。在这种情况下,您可以git reset --hard origin/dev放弃两次提交,并将您的工作树放回提交时的状态ec5e61d。
如果您是上游存储库的唯一用户(这是一个非常大的“if”),并且提交ec5e61d本身并不是真正有用的,您可能也想放弃该提交——但要小心,不仅仅是因为非常很大的“if”部分,但也因为那个特定的提交变得更难恢复——虽然不像 stash 那样难。只要您还有存储,还原和git stash apply结果就很容易重复:您只需运行相同的git命令。
| 归档时间: |
|
| 查看次数: |
5358 次 |
| 最近记录: |