【问题标题】:Practical uses of git reset --soft?git reset --soft 的实际用途?
【发布时间】:2011-07-09 09:07:16
【问题描述】:

我已经使用 git 一个多月了。的确,我昨天才第一次使用reset,但是软重置对我来说仍然没有多大意义。

我知道我可以使用软重置来编辑提交,而无需更改索引或工作目录,就像使用 git commit --amend 一样。

这两个命令真的一样吗(reset --soft vs commit --amend)?有任何理由在实际中使用其中一种吗?更重要的是,reset --soft 除了修改提交还有其他用途吗?

【问题讨论】:

    标签: git


    【解决方案1】:

    我用它来修改的不仅仅是 last 提交。

    假设我在提交 A 中犯了一个错误,然后提交了 B。现在我只能修改 B。 所以我做git reset --soft HEAD^^,我更正并重新提交A,然后重新提交B。

    当然,对于大提交来说不是很方便……但你通常不应该做大提交。

    【讨论】:

    • git commit --fixup HEAD^^ git rebase --autosquash HEAD~X 也很好用。
    • git rebase --interactive HEAD^^ 您选择编辑提交 A 和 B。这样可以保留 A 和 B 的提交消息,如果需要,您仍然可以修改它们。
    • 重置为 A 后如何重新提交 B?
    • 另一种可能更少工作的方法:检查你的 git 日志,获取 B 的提交哈希,然后 git reset A,进行并添加更改,git commit --amendgit cherry-pick <B-commit-hash>
    【解决方案2】:

    一个实际用途是,如果您已经提交到本地 repo(即 git commit -m ),那么您可以通过执行 git reset --soft HEAD~1

    据您所知,如果您已经暂存了您的更改(即使用 git add 。)那么您可以通过执行 git reset --mixed HEAD 来反转暂存,或者我通常也只是用过git reset

    最后,git reset --hard 会清除所有内容,包括您的本地更改。头后的 ~ 告诉你从顶部到多少次提交。

    【讨论】:

    • git reset --soft HEAD ~1 给了我fatal: Cannot do soft reset with paths. 我认为我们需要删除 HEAD 之后的空格,所以它将是 git reset --soft HEAD~1
    【解决方案3】:

    git reset 是关于移动HEADand generally the branch ref
    问题:工作树和索引呢?
    当与--soft 一起使用时,移动HEAD,最常更新分支引用,并且仅更新HEAD
    这与commit --amend 不同:

    • 它不会创建新的提交。
    • 它实际上可以将 HEAD 移动到任何提交(因为commit --amend 只是关于移动 HEAD,同时允许重做当前提交)

    刚找到这个组合的例子:

    • 经典合并
    • 子树合并

    合而为一(章鱼,因为合并了两个以上的分支)提交合并。

    Tomas "wereHamster" Carnecky 在他的"Subtree Octopus merge" article 中解释:

    • 如果您想将一个项目合并到另一个项目的子目录中,然后使子项目保持最新,则可以使用子树合并策略。它是 git 子模块的替代品。
    • 章鱼合并策略可用于合并三个或更多分支。正常策略只能合并两个分支,如果您尝试合并更多分支,git 会自动回退到章鱼策略。

    问题是您只能选择一种策略。但我想将两者结合起来,以获得一个干净的历史记录,其中整个存储库自动更新到新版本。

    我有一个超级项目,我们称之为projectA,还有一个子项目projectB,我合并到projectA 的子目录中。

    (这是子树合并部分)

    我还在维护一些本地提交。
    ProjectA 会定期更新,projectB 每隔几天或几周就会有一个新版本,并且通常取决于 projectA 的特定版本。

    当我决定更新这两个项目时,我不会简单地从projectAprojectB 中提取,因为这会为应该是整个项目的原子更新创建两个提交。
    相反,我创建了一个合并提交,它结合了 projectAprojectB 和我的本地提交
    这里棘手的部分是这是一个章鱼合并(三个头),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 可以提供帮助的地方:
    我如何重做这两个合并,它们已经工作了,即我的工作树和索引都很好,但不必记录这两个提交?

    # Move the HEAD, and just the HEAD, two commits back!
    git reset --soft HEAD@{2}
    

    现在,我们可以恢复 Tomas 的解决方案:

    # 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 就是答案。

    【讨论】:

    • 自我注意:使用git reset --soft进行挤压的简单示例:stackoverflow.com/questions/6869705/…
    • 如果您提交到错误的分支,它也很有用。所有更改都会返回到暂存区并在您结帐正确的分支时与您一起移动。
    【解决方案4】:

    另一个用例是当您想在拉取请求中用您的分支替换另一个分支时,例如,假设您有一个软件在开发中具有功能 A、B、C。

    您正在使用下一个版本进行开发,并且您:

    • 移除功能 B

    • 新增功能 D

    在此过程中,为功能 B 开发刚刚添加的修补程序。

    您可以将开发合并到下一个,但这有时会很混乱,但您也可以使用git reset --soft origin/develop 并使用您的更改创建一个提交,分支可以合并而不会发生冲突并保留您的更改。

    事实证明git reset --soft 是一个方便的命令。我个人经常使用它来压缩没有“已完成工作”的提交,比如“WIP”,所以当我打开拉取请求时,我所有的提交都是可以理解的。

    【讨论】:

      【解决方案5】:

      虽然我真的很喜欢这个帖子中的答案,但我使用 git reset --soft 来表示稍微不同但非常实用的场景。

      我使用 IDE 进行开发,它有一个很好的 diff 工具来显示我上次提交后的更改(分阶段和非分阶段)。现在,我的大部分任务都涉及多次提交。例如,假设我进行 5 次提交以完成特定任务。在 1-5 的每次增量提交期间,我使用 IDE 中的 diff 工具来查看我上次提交的更改。我发现这是在提交之前检查我的更改的一种非常有用的方法。

      但是在我的任务结束时,当我想一起查看我的所有更改(从第一次提交之前),在发出拉取请求之前进行自我代码审查时,我只会看到我之前提交的更改(在提交 4 之后)并且不会从我当前任务的所有提交中更改。

      所以我使用 git reset --soft HEAD~4 返回 4 次提交。这让我可以一起看到所有的变化。当我对自己的更改充满信心时,我可以执行git reset HEAD@{1} 并自信地将其推送到远程。

      【讨论】:

      • ... 然后重复执行 git add --patchgit commit 以构建如果您一直知道自己在做什么,您将构建的提交系列。你的第一次提交就像你桌上的笔记或备忘录的初稿,它们是用来组织你的想法,而不是为了发表。
      • 嘿,我不太明白你的建议。
      • 只是,您可以使用git add -pgit commit 构建一个用于发布的系列,而不是使用git reset @{1} 来恢复您的初稿系列。
      • 是的,正确的。另一种方法是(我通常遵循的方法)基于上游/主服务器并将提交压缩为一个。
      【解决方案6】:

      用例 - 组合一系列本地提交

      “糟糕。这三个提交可能只是一个。”

      所以,撤消最后 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 除了修改提交还有其他用途吗?

      • 查看其他答案:)

      【讨论】:

      • 查看其他答案“除了修改提交之外,reset --soft 是否还有其他用途 - 否”
      • 作为对上一条评论的回应——部分同意,但我想说修改 single 提交过于具体。例如,一旦编写了一个特性,您可能希望压缩所有内容并开辟对审阅者更有用的新提交。我想说软重置(包括普通的git reset)在你(a)想要重写历史时很好,(b)不关心旧的提交(这样你就可以避免交互式变基的麻烦),并且(c) 有多个提交更改(否则,commit --amend 更简单)。
      【解决方案7】:

      另一种潜在用途是作为存储的替代方法(有些人不喜欢,参见例如https://codingkilledthecat.wordpress.com/2012/04/27/git-stash-pop-considered-harmful/)。

      例如,如果我在一个分支上工作,并且需要紧急修复 master 上的某些问题,我可以这样做:

      git commit -am "In progress."
      

      然后结帐大师并进行修复。完成后,我回到我的分支并做

      git reset --soft HEAD~1
      

      从我离开的地方继续工作。

      【讨论】:

      • 更新(现在我对 git 的理解更好):--soft 在这里实际上是不必要的,除非您真的关心立即进行更改。我现在只在执行此操作时使用git reset HEAD~。如果我在需要切换分支时进行了一些更改,并希望保持这种状态,那么我执行git commit -m "staged changes",然后执行git commit -am "unstaged changes",然后执行git reset HEAD~,然后执行git reset --soft HEAD~,以完全恢复工作状态。虽然,老实说,现在我知道git-worktree 后我做这两件事的次数要少得多:)
      【解决方案8】:

      SourceTree 是一个 git GUI,它有一个非常方便的界面,可用于暂存您想要的位。它没有任何类似的东西可以修改适当的修订。

      所以git reset --soft HEAD~1 在这种情况下比commit --amend 有用得多。我可以撤消提交,将所有更改返回到暂存区域,然后使用 SourceTree 继续调整暂存位。

      真的,在我看来commit --amend 是两者中更多余的命令,但 git 是 git,并且不会回避执行略有不同的类似命令。

      【讨论】:

        【解决方案9】:

        当您想在另一台机器上继续工作时,一种可能的用法是。它会像这样工作:

        1. 用类似 stash 的名称签出一个新分支,

          git checkout -b <branchname>_stash
          
        2. 向上推你的 stash 分支,

          git push -u origin <branchname>_stash
          
        3. 切换到您的另一台机器。

        4. 拉下你的 stash 和现有的分支,

          git checkout <branchname>_stash; git checkout <branchname>
          
        5. 您现在应该在现有的分支上。合并来自 stash 分支的更改,

          git merge <branchname>_stash
          
        6. 在合并之前将现有分支软重置为 1,

          git reset --soft HEAD^
          
        7. 删除你的 stash 分支,

          git branch -d <branchname>_stash
          
        8. 同时从原点移除你的 stash 分支,

          git push origin :<branchname>_stash
          
        9. 继续处理您的更改,就像您已正常隐藏它们一样。

        我认为,在未来,GitHub 和 co。应该以更少的步骤提供这种“远程存储”功能。

        【讨论】:

        • 我想指出,你的第一台机器上的第一次 stash 和 pop 是完全没有必要的,你可以直接从一个脏的工作副本创建一个新分支,提交,然后将更改推送到遥控器。
        【解决方案10】:

        使用“git reset --soft &lt;sha1&gt;”的一个重要原因是将HEAD 移动到一个裸仓库中。

        如果您尝试使用 --mixed--hard 选项,您将收到错误消息,因为您尝试修改和工作的树和/或索引不存在。

        注意:您需要直接从裸仓库执行此操作。

        再次注意:您需要确保要在裸仓库中重置的分支是活动分支。如果没有,请关注 VonC 的 answer,了解如何在您可以直接访问仓库时更新裸仓库中的活动分支。

        【讨论】:

        • 如您之前的回答 (stackoverflow.com/questions/4624881/…) 中所述,当您可以直接访问裸仓库(您在此处提到)时,情况确实如此。 +1 虽然。
        • @VonC 是的,绝对正确,感谢您添加注释!我一直忘记补充这一点,因为我假设重置是直接从 repo 完成的。另外,我假设该人想要在裸仓库中重置的分支是他们的活动分支。如果该分支不是他们的活动分支,则需要根据您的回答 (stackoverflow.com/questions/3301956/…) 更新有关如何更新裸仓库的活动分支的信息。我也会用活动的分支信息更新答案。再次感谢!!!
        【解决方案11】:

        您可以使用git reset --soft 来更改您希望拥有的版本作为您在索引和工作树中所做更改的父版本。这很有用的情况很少见。有时您可能会决定您在工作树中所做的更改应该属于不同的分支。或者您可以将其用作将多个提交合并为一个的简单方法(类似于 squash/fold)。

        有关实际示例,请参阅 VonC 的此答案: Squash the first two commits in Git?

        【讨论】:

        • 这是一个我以前没有发现的好问题。但我不确定我能否看到如何使用 reset soft 将更改放到另一个分支上。所以我有结帐分支,然后我使用git reset --soft anotherBranch 然后在那里提交?但是你并没有真正改变 ckeckout 分支,所以你会提交到 Branch 还是 anotherBranch
        • 执行此操作时,不要使用 git checkout 很重要,因为这会改变您的树。将 git reset --soft 视为一种仅操作 HEAD 指向的修订版的方式。
        猜你喜欢
        • 1970-01-01
        • 2019-03-11
        • 2020-02-16
        • 2020-06-22
        • 1970-01-01
        • 2021-10-22
        • 1970-01-01
        • 2013-07-19
        • 1970-01-01
        相关资源
        最近更新 更多