“git reset”和“git rebase”有什么区别?

81

我一直在尝试使用git(还是个新手),我想知道“reset”和“rebase”的区别。哪一个更强大?

假设我想删除历史记录中加粗的3个提交,哪一个更好用,或者我应该打标签然后用git tag -d <tagname>来删除它?

17a64df 2012-06-21 | Hello使用style.css(HEAD,origin/style,master),
a6792e4 2012-06-21 | 添加css样式表
801e13e 2012-06-21 | 添加README
5854339 2012-06-21 | 添加index.html
0b1dd4c 2012-06-21 | 将hello.html移动到lib目录下
55649c3 2012-06-21 | 添加作者/电子邮件注释
9b2f3ce 2012-06-21 | 添加作者注释
cdb39b0 2012-06-21 | 提交带有文本的p标签(v1.1)
b7b5fce 2012-06-21 | 撤销提交a6faf60631b5fbc6ee79b52a1bdac4c971b69ef8。
a6faf60 2012-06-21 | 恢复“糟糕,我们不想要这个提交”
a006669 2012-06-21 | 糟糕,我们不想要这个提交
262d1f7 2012-06-21 | 添加HTML标题(v1)
b1846e5 2012-06-21 | 添加标准HTML页面标签(v1-beta)
bf1131e 2012-06-21 | 添加HI TAG
02b86d0 2012-06-21 | 第一次提交

4个回答

84
他们是完全不同的。git-reset与引用一起使用,在您的工作目录和索引上工作,而不触及任何提交对象(或其他对象)。另一方面,git-rebase 用于重写先前制作的提交对象。
因此,如果您想要重写历史记录,git-rebase 就是您想要的。请注意,您应该永远不要重写已推送并提供给其他人的历史记录,因为重新基础重写了对象,使它们与旧对象不兼容,这会给其他任何涉及的人带来麻烦。
话虽如此,您想做的是交互式变基。使用git rebase -i 262d1f7调用它,您应该会得到一个类似于以下提示的提示:
pick 262d1f7 Added HTML header (v1)
pick a006669 Oops, we didn't want this commit
pick a6faf60 Revert "Oops, we didn't want this commit"
pick b7b5fce This reverts commit a6faf60631b5fbc6ee79b52a1bdac4c971b69ef8.
pick cdb39b0 Commit p tags with text (v1.1)
pick 9b2f3ce Added an author comment
pick 55649c3 Add an author/email comment
pick 0b1dd4c Moved hello.html to lib
pick 5854339 Added index.html
pick 801e13e Added README
pick a6792e4 Added css stylesheet
pick 17a64df Hello uses style.css (HEAD, origin/style, master),

只需删除您想要删除的提交行,保存并退出编辑器,Git 将重写您的历史记录。如果您已经推送了更改,请不要这样做。通常情况下,历史记录中存在这样的提交是完全可以的。


从rebase中删除行与将单词“pick”更改为“s”以进行压缩是否相同? - Ninjaxor
7
@Ninjaxor 不行,删除行会完全从历史记录中删除提交及其引入的更改。就好像从未进行过提交一样。例如,如果您删除添加了一行的提交,则在重新设置后该行将不会出现在任何后续提交中。另一方面,压缩提交将仍然保留提交引入的更改,同时只是摆脱那个单独的提交本身。 - poke
git reset --hard 会重写之前提交的 commit 对象。 - Irina Rapoport

38

我希望这个通俗易懂的解释是正确的。

git reset和git rebase都会影响你的本地分支。它们将强制使你的本地分支与特定提交保持同步。不同之处在于:

  1. "git reset --hard {commit-id}" 将使用本地历史记录中的提交。
  2. "git rebase origin/{branch-name}" 将使用仓库中最新的提交

附加信息

何时使用reset?

假设你完成了工作并提交到本地。然后你的猫从键盘上走过,不小心提交了猫的工作。使用reset。

何时使用rebase?

假设你重构了一个函数/类的名称,这个改变会影响到许多文件。当你提交并尝试推送到origin时,你意识到你的同事已经做了一些重要的更改并已经将其推送到origin。假设你认为再次重构名称(使用IDE)比处理所有冲突的文件容易,你可以选择rebase,它将删除你的工作并保持你同事的不变。

为什么每个答案/教程都包含如此多的技术免责声明?

因为reset和rebase都可以永久删除本地更改。因此,用户需要知道如何采用策略(例如创建备份分支)来保留他们的工作。


0

虽然 "git reset" 明显不是用来从历史记录中删除 仅有的3个提交 的工具,但在至少一个场景中我发现它非常有用,而且似乎还没有被充分记录,这与所提出的问题仍然相关:

假设你有一个名为 'environment-X' 的分支,用于部署到特定环境,除了一些明确定义的更改外,大多数情况下应该反映 'master' 分支。如果由于不正确的 git 使用或其他原因导致该分支的历史记录混乱,将其重新基于 master 并将其所有非 master 提交压缩为一个可能是一个好选择。

在这种情况下,交互式变基可能很困难,并且会导致许多需要逐个手动修复的冲突。

更简单的操作是:

  1. 找到公共祖先提交: git merge-base master environment-X
  2. 重置到它: git reset $commit(这将使磁盘上的文件保持不变)
  3. 添加并提交修改后的文件 - 作为奖励,这给了您验证是否存在意外更改的机会
  4. 强制推送

0

git reset - 通过将指针移动到之前的提交之一,在本地获取以前的分支状态

git rebase - 通过指定它的最后一个提交来更改分支


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接