【问题标题】:How do I best revert to a tag, make further changes, then reinstate all the subsequent changes, in Git?在 Git 中,如何最好地恢复到标记,进行进一步更改,然后恢复所有后续更改?
【发布时间】:2020-03-30 11:29:47
【问题描述】:

查看在处理分支、标签等时在 Git 中恢复或重置的所有方法......我以为我掌握了它,但它变得令人困惑。

那么我需要执行以下操作的最简单的命令序列是什么?

  1. 通过拉取请求将主分支恢复到较早的状态。据推测,我会创建一个功能分支,将其还原,然后通过拉取请求合并它。 (此还原包括还原之前的合并。)
  2. 在功能分支中进行进一步更改。不确定我是否在之前和之后合并到 master 之后,或者仅在之后的步骤是否重要。
  3. 推送然后合并到主服务器(通过拉取请求将其进入主服务器,在 Git 之外)
  4. 然后恢复我在第 1 步中回滚的后续更改,解决所有冲突,并保留第 2-3 步中的更改。

我从git --no-commit revert commit-after-the-tag..HEAD 开始,但现在我担心恢复更改没有简单的方法,因为我的恢复提交将比我想要恢复的更改更新。 (另外,由于要还原的代码包含合并,所以我无法还原。)

所以我想我想恢复到一个标签,进行进一步的更改,然后恢复所有后续恢复的更改。我该怎么做?

【问题讨论】:

  • 还原还原。您的第 1 步还原将添加另一组提交,以恢复这些更改,稍后再还原它们。
  • 但是恢复revert并保留所有“最新”更改也可以吗?
  • 我认为最干净的方法是从您的功能分支创建一个新的功能分支 2(从您想要的提交开始)。完成所有工作,将 feature-branch-2 合并为 master。然后将 feature-branch 合并到 feature-branch-2 (或 rebase,如果未发布,则外界将看不到您的技巧),并再次将其带到 master。
  • Reverting-the-reverts 也应该可以正常工作,如果更改与以后的更改发生冲突(如在此处询问)stackoverflow.com/questions/6084483/…,您应该会看到恢复冲突。但是你真的需要在主仓库中查看这种“混淆”的历史吗?
  • Sbat,这行不通,因为我们仍然会在 master 中进行更新。我需要让 master 回到之前的状态,修复问题,部署,然后恢复更改。还有其他想法吗?

标签: git


【解决方案1】:

这是一种方法——假设您的原始 Git 存储库位于支持拉取请求的系统中,例如 GitHub 或 Azure DevOps,因此您希望查看更改并避免修改历史记录。如果您没有或不想要拉取请求,则可以改用更简单的 git reset --HARD 方法。

现在我不喜欢尝试 git-revert 的行为,因为它让我一一解决冲突,即使早期的更改使冲突变得多余。所以以下是更好的方法。

  1. 使用git patchgit apply 回滚新分支中的更改,注意生成的提交哈希
  2. 使用一个或多个拉取请求(普通开发工作流程)合并回滚和修复(到主)。进行所需的构建、部署和迭代。
  3. 准备就绪后,恢复前面提到的哈希以恢复更改并合并到主服务器中

这种方法会保留所有历史记录,并且不需要更改主拉请求策略。 git reset --hard 稍微简单一些,但需要强制推送,这是我们不想要的。

以下正是执行此操作所需的命令。如果任何命令失败或给出警告,请不要继续;关注它们并解决它们。

回滚

; ensure you are in a good state without other changes to confuse you
git checkout master && git pull

; create the patch that can roll back the code
git diff master [known-good-tag] > /some/path/tempfile.patch

; create the hotfix branch and push it to origin
git checkout -b [HOTFIX-BRANCH-NAME] && git push --set-upstream origin [HOTFIX-BRANCH-NAME]

; apply the patch in the new branch
git apply  /some/path/tempfile.patch

; commit the changes locally
git add -A   && git commit -m "rolling back newest changes"
; Note the resulting commit hash: later you'll have to revert this rollback commit ONLY.  If you forget it, you can find it in the git log history by the commit comment. 

; push the commit to origin
git push

; Make further changes.  Iterate as desired with ordinary workflow.
git add -A   && git commit -m "Fix to xyz because of abc"
git push

将回滚+修复与拉取请求合并。多次合并也可以。

恢复回滚,保留最新修复并恢复最近的开发:

; ensure you are in a good state without other changes to confuse you   
git checkout master && git pull

; After deployment Create a new branch for reverting the rollback
git checkout -b [REINSTATE-BRANCH-NAME]  && git push --set-upstream origin [REINSTATE-BRANCH-NAME]

; revert the rollback commit noted earlier.  Note that `git revert` creates a commit.
git revert --no-edit [hash]


; Resolve conflicts if necessary. I like to use VS Code because it has easy conflict resolution tools.
code <FILE_WITH_CONFLICT>
git add <FILE_WITH_CONFLICT>

; finish the revert with the resolved conflicts. Iterate if needed.
git revert --continue

; push the completed revert and request another pull request to merge to master.  Then you are back to ordinary workflow.
git push

顺便说一下,考虑一下您是否真的需要执行上述操作。很多情况下有很多更简单的解决方案,比如:

  • 您是否只需要构建和/或部署早期版本而不进行更改?大多数构建系统都允许您构建更早的提交,或部署已构建的工件。
  • 您能否修复 master 和 deploy 的当前状态,包括所有最近的更改?根据需要使用功能切换或其他禁用功能。

【讨论】:

    【解决方案2】:

    我假设您有以下历史记录(时间从左到右流动):

    --o--o--T--a--b--c--d    <- feature-branch
                 /
     ...--x--x--x            <- commits merged into feature-branch
    

    请注意,这是一个“功能”分支这一事实是无关紧要的。

    T 是您想要返回并构建其他更改的标记提交,以便您获得以下历史记录:

    --o--o--T--E--F--G--a'--b'--c'--d'
                           /
               ...--x--x--x
    

    您可以使用以下命令序列来执行此操作。从标记提交处的新临时分支开始:

    git checkout -b tmp T
    

    这会将您的工作(但不是您的分支)倒回到标记的提交。现在你进行提交:

    # edit
    git commit   # E
    # edit
    git commit   # F
    # edit
    git commit   # G
    

    你现在有了这段历史:

              E--F--G        <- tmp
             /
    --o--o--T--a--b--c--d    <- feature-branch
                 /
     ...--x--x--x
    

    最后,您使用git rebase 的“rebase-merges”模式得出最终历史记录:

    git checkout feature-branch
    git rebase -i -r tmp
    

    您不再需要临时分支,您可以将其移除:

    git branch -d tmp
    

    编辑:根据 cmets 中的讨论,我建议您这样做:

    重命名分支tmp:

    git branch -m tmp fixups
    

    尽一切可能获取当前master,您必须恢复到旧状态。致电该分支master-reverted。那么:

    git rm -r .
    git checkout T -- .
    git commit -m "reverted back to tag T"
    

    git rm . 只是确保没有留下任何自 T 以来添加的文件;如果没有添加文件,则可以跳过该步骤。)

    现在将修正合并到分支中:

    git merge fixups
    

    现在发出拉取请求以拉取您的分支master-reverted 并进行部署。

    然后还原还原,但在新分支上执行此操作:

    git checkout -b master-new
    git revert HEAD~
    

    现在发出拉取请求以获取 master-new。这是最后的历史:

     ...--x--x--x          
                 \
    --o--o--T--a--b--c--d          <- feature-branch
             \           \   
              E   ...--m--m       R' <- master-new
               \           \     /
                \            R--M  <- master-reverted
                 \             /
                  F-----------G   <- fixups (was tmp)
    

    --m--m 是上游主分支。 R是master的revert,R'是master的reverted。

    【讨论】:

    • 这看起来很有希望;我可以要求一些调整吗?在 E、F、G 之后,我想推送并执行拉取请求,以便我可以从 master 部署。我该怎么做?我相信我不能,因为 master 仍然有变化 a' 到 d'。
    • 如果你没有推送rebase的分支,那么master就不会有a'到d'。但你听起来好像它有一个直通d。那么你不应该改变它们。我的建议是将 E-F-G 合并到 master 中,例如通过拉取请求。 (只需用一个不同的、有意义的名称来调用通往 G 的分支,而不是 tmp。)
    • 但是如果我将 E-F-G 合并到 master 中,它也会有 a-b-c-d。我需要从 master 部署而不更改 a-b-c-d。还有什么想法吗? (我已经将问题调整得更精确一些。)这让我觉得这是一个非常常见的 git 场景。我很惊讶没有一种通用的方法可以做到这一点。
    • 所以您想丢弃您当前master 的部分历史记录并用新的历史记录替换它?然后您必须将master 重置为a 到d 的合并之前,并使用具有a' 到d' 的新历史来重建它。使用普通的 Git 命令是一项相当简单的任务,但我不知道你将如何使用“拉取请求”(这是一个存在于普通 Git 之外的概念)来做到这一点。
    • 我的扩展建议几乎与您在自己的答案中所做的完全一样。由于某种原因,我直到现在才看到。哦,好吧……如果我有的话,我会省住呼吸的。竖起大拇指将不胜感激。
    猜你喜欢
    • 2015-09-20
    • 2019-04-03
    • 1970-01-01
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-22
    相关资源
    最近更新 更多