git reset的实际用法–soft?
我一直在使用git一个多月。 事实上,我昨天才第一次使用了复位function,但软复位对我来说依然没有什么意义。
我知道我可以使用软重置编辑提交而不改变索引或工作目录,因为我会用git commit --amend
。
这两个命令是否真的一样( reset --soft
vs commit --amend
)? 任何理由使用这一个或另一个实际条件? 更重要的是,除了修改一个提交之外,是否还有其他的用途来reset --soft
?
git reset
是关于移动HEAD
。
问题:工作树和索引怎么样?
当使用 – --soft
, 移动HEAD
,只有头 。
这不同于commit --amend
:
- 它不会创build一个新的提交。
- 它实际上可以将HEAD移动到任何提交(因为
commit --amend
只是关于不移动HEAD,而允许重做当前的提交)
刚刚find这个结合的例子:
- 一个经典的合并
- 一个子树合并
全部合为一体(章鱼,因为有两个以上的分支合并)提交合并。
Tomas“被仓鼠”Carnecky在他的“Subtree Octopus合并”一文中解释道 :
- 如果要将一个项目合并到另一个项目的子目录中,并且随后保持该子项目处于最新状态,则可以使用子树合并策略。 这是git子模块的替代scheme。
- 章鱼合并策略可以用来合并三个或更多分支。 正常的战略可以合并只有两个分支,如果你尝试合并更多的,git自动回落到章鱼战略。
问题是你只能select一种策略。 但是我想把两者结合起来,以便得到一个清晰的历史logging,在这个历史logging中,整个存储库都被primefaces更新为新版本。
我有一个超级项目,我们称之为
projectA
,然后是一个子项目projectB
,它被合并到projectA
的子目录中。
(这是子树合并部分)
我也维护一些本地提交。
ProjectA
定期更新,projectB
每隔几天或几周都有一个新版本,通常取决于projectA
的特定版本。当我决定更新这两个项目时,我不会简单地从
projectA
和projectB
拉出来, 因为这会为整个项目的primefaces更新创build两个提交 。
相反, 我创build了一个合并提交,它结合了projectA
,projectB
和我的本地提交 。
这里棘手的部分是这是一个章鱼合并(三头), 但projectB
需要与子树策略合并 。 所以这就是我所做的:
# Merge projectA with the default strategy: git merge projectA/master # Merge projectB with the subtree strategy: git merge -s subtree projectB/master
在这里,作者使用了reset --hard
,然后read-tree
来恢复前两次合并对工作树和索引所做的操作,但这正是reset --soft
可以提供帮助的地方:
如何重做这两个合并 ,这是工作,即我的工作树和索引是好的,但不必logging这两个提交?
# Move the HEAD, and just the HEAD, two commits back! git reset --soft HEAD@{2}
现在,我们可以恢复Tomas的解决scheme:
# Pretend that we just did an octopus merge with three heads: echo $(git rev-parse projectA/master) > .git/MERGE_HEAD echo $(git rev-parse projectB/master) >> .git/MERGE_HEAD # And finally do the commit: git commit
所以,每一次:
- 你对最终结果感到满意(在工作树和索引方面)
- 你不满意带你到达那里的所有提交:
git reset --soft
是答案。
用例 – 结合一系列本地提交
“哎呀,这三个承诺可能只是一个。”
所以,撤销最后3(或其他)提交(不影响索引或工作目录)。 然后将所有更改作为一个提交。
例如
> git add -A; git commit -m "Start here." > git add -A; git commit -m "One" > git add -A; git commit -m "Two" > git add -A' git commit -m "Three" > git log --oneline --graph -4 --decorate > * da883dc (HEAD, master) Three > * 92d3eb7 Two > * c6e82d3 One > * e1e8042 Start here. > git reset --soft HEAD~3 > git log --oneline --graph -1 --decorate > * e1e8042 Start here.
现在所有的变化都被保存下来,并准备作为一个承诺。
简短的回答你的问题
这两个命令是否真的一样( reset --soft
vs commit --amend
)?
- 没有。
任何理由使用这一个或另一个实际条件?
-
commit --amend
从最后一次提交中添加/ rm文件或更改其消息。 -
reset --soft <commit>
将几个连续的提交合并成一个新的。
更重要的是,除了修改一个提交之外,是否还有其他的用途来reset --soft
?
- 查看其他答案:)
我用它来修改不止是最后一次提交。
假设我在提交A时犯了一个错误,然后提交了B.现在我只能修改B.所以我做了git reset --soft HEAD^^
,我纠正并重新提交A,然后重新提交B.
当然,对于大型提交来说并不是很方便,但是不pipe怎样,你都不应该做大提交;-)
您可以使用git reset --soft
来更改您希望作为父索引的版本,以适应您在索引和工作树中git reset --soft
的更改。 这是有用的情况是罕见的。 有时你可能会认为在工作树中所做的更改应该属于不同的分支。 或者你可以使用这个方法将多个提交合并为一个(类似于squash / fold)。
VonC看到这个答案是一个实际的例子: 在Git中压缩前两个提交?
使用' git reset --soft <sha1>
'的一个很好的理由是将HEAD
移动到裸露的回购站中。
如果您尝试使用--mixed
或--hard
选项,则会尝试修改并使用不存在的树和/或索引,因此会出现错误。
注意:您将需要直接从裸回购做到这一点。
再次注意:您将需要确保您想要在裸回购重置的分支是活动的分支。 如果没有,请按照VonC关于如何在直接访问回购时更新裸回购中的活动分支的回答 。
一种可能的用法是当你想在另一台机器上继续工作时。 它会这样工作:
-
签出一个存储类似名称的新分支,
git checkout -b <branchname>_stash
-
把你的储藏分支拉起来,
git push -u origin <branchname>_stash
-
切换到其他机器。
-
拉下你的藏匿处和现有的分支,
git checkout <branchname>_stash; git checkout <branchname>
-
你现在应该在你现有的分支上。 合并来自存储分支的更改,
git merge <branchname>_stash
-
在合并之前,将现有分支软件重置为1,
git reset --soft HEAD^
-
删除你的存储分支,
git branch -d <branchname>_stash
-
同时删除你的存储分支从原点,
git push origin :<branchname>_stash
-
继续处理您的更改,就好像您正常保存了一样。
我想,在未来,GitHub和co。 应该以更less的步骤提供这种“远程存储”function。
另一个潜在的用途是作为替代存储(有些人不喜欢,见https://codingkilledthecat.wordpress.com/2012/04/27/git-stash-pop-considered-harmful/ )。
例如,如果我在分支上工作,需要在主人身上紧急解决问题,我可以这样做:
git commit -am "In progress."
然后结帐主,并修复。 当我完成后,我回到我的分支去做
git reset --soft HEAD~1
继续工作,我离开了。
一个实际的用法是,如果你已经承诺你的本地回购(即git commit -m),那么你可以通过做git reset –soft HEAD〜1
另外对于你的知识,如果你已经上演你的变化(即使用git add),那么你可以通过做git reset –mixed HEAD
最后, git reset –hard把所有东西都抹掉 ,包括你本地的变化。 头后的数字告诉你从顶端开始有多less提交。
SourceTree是一个git GUI,它有一个非常方便的界面,用于分级你想要的位。 它没有任何类似的修改正确的修订。
所以在这种情况下, git reset --soft HEAD~1
比commit --amend
更有用。 我可以撤销提交,将所有更改恢复到暂存区域,然后使用SourceTree继续调整暂存的位。
实际上,在我看来, commit --amend
是更为多余的两个命令,但是git是git,并且不会回避类似的命令,它们做的事情稍有不同。