【问题标题】:Backport changes from renamed file从重命名的文件向后移植更改
【发布时间】:2012-04-04 01:57:38
【问题描述】:

我有两个分支:主干,生产。我在主干中发现了一个问题,修复并提交了它,推送了它。现在它已经过测试,我需要将更改合并到生产分支中作为热修复。我尝试使用樱桃采摘。但是它不起作用,因为在我不想投入生产的一些重构过程中,修复程序中更改的文件在早些时候在主干中被重命名。

我不想合并所有内容,但只接受这个提交。樱桃挑选因“被我们删除”冲突而失败(当然,新文件甚至从未存在于生产分支中)。

将更改带入旧文件的正确方法是什么?

【问题讨论】:

    标签: git cherry-pick


    【解决方案1】:

    这有点棘手。例如,您可以从差异创建补丁并将其应用于旧文件。但是以后为了防止这些问题,我建议先在生产分支上进行修复并在那里进行测试,然后从生产分支合并到主干。

    【讨论】:

    • 是的,我明白这一点,但并不总是能够预测热修复中会包含哪些内容。我正在尝试通过format-patch/apply-patch 进行操作,但它什么也没做(没有错误,也没有更改)。请给出正确使用方法的提示。
    • 查看本文blogs.atlassian.com/2011/10/confluence_git_rename_merge_oh_my中的“在git中重命名处理”部分
    【解决方案2】:

    我会为此使用好的旧补丁:

    git show COMMIT_ID -- old/file/name.txt | patch new/file/name.txt
    

    【讨论】:

    • 是的,它对我有用。但是,找到更好的解决方案会很有趣,尤其是在有多个重命名文件的情况下。
    • 这看起来很有希望,但我不知道如何确定 COMMIT_ID 使用什么以及何时应该调用此命令。 (当我运行git rebase develop 并且它遇到冲突时,我可以在 rebase 期间调用它吗?)
    • COMMIT_ID 可以是任何标识提交的内容,例如提交哈希、HEAD、分支名称等。如果您正在变基,查看历史记录并复制可能最容易提交哈希。
    【解决方案3】:

    我遇到了同样的问题,并试图找到解决方案。

    我通过使用一系列变基来解决。 我没有做过比这些更进一步的测试,所以使用风险自负!

    有兴趣的可以去github看看:

    https://github.com/fraschfn/cherry-pick

    【讨论】:

      【解决方案4】:

      在分支之间重命名目录的情况下,对任意数量的文件进行挑选更改:

      git diff ... | sed -e 's|<old dir>|<new dir>|' | git apply -
      

      【讨论】:

      【解决方案5】:

      如果:

      • 您期望/希望 Git 会检测到主干上文件的移动或重命名,但它没有,并且
      • 您的存储库有合理数量的文件

      ...那么您绝对应该考虑像这样更改您的 git 配置:

      $ git config merge.renameLimit 999999
      

      在合并/cherry-pick 期间,git 可能会在找到合适的重命名匹配之前达到默认文件检查限制(我认为它是 400 或 1000 或类似的东西)。提高此限制可能会导致合并/樱桃选择在搜索您重命名的文件时花费更长的时间,但它可以帮助避免“被我们删除”的合并挑战。

      这应该可以解决问题,但是如果您重命名的文件很小并且分支之间的变化很大,您也可以使用-X rename-threshold 设置,例如使用-X rename-threshold=25% 将其从默认的 50% 降低。

      【讨论】:

      • rename-threshold 上给其他用户的注释——这包括随时间发生的所有变化。我有一个合并,其中原始重命名更改了文件中的两行,但由于它在 之后 被大量修改,git 在不降低 rename-threshold 的情况下仍然没有检测到文件相似性。
      【解决方案6】:

      面对同样的问题,我问了一位同事他会怎么做,他的第一反应是:

      git checkout production
      
      git mv production-filename trunk-filename && git commit -m "Just fooling git"
      git cherry-pick trunk-commit
      git mv trunk-filename production-filename && git commit -m "Undo the damage"
      
      # Now squash the 3 commits
      git rebase -i HEAD~3
      

      对我来说就像一个魅力。

      【讨论】:

      • 这对我有用。我使用了 GUI,但遵循相同的步骤。我花了一分钟才弄清楚这是做什么的,所以我将列出步骤: 1. 重命名目标分支上的文件/目录以匹配源分支并提交。 2. Cherrypick 从源分支到目标分支的变化。 3. 将目标分支上的文件/目录重命名回原来的样子并提交。 4. 将这 3 个提交压缩为一个提交。
      • 即使看起来很有希望,我也无法理解这个答案。如果您有时间,我很乐意在这里回答您:stackoverflow.com/q/63044843/470749 谢谢!
      • 好主意!特别是路径被双方重命名。它有很大帮助。谢谢
      【解决方案7】:

      我制作了一个 shell 脚本,它在猜测文件移动时尝试进行挑选(如果您重命名文件本身,它就不起作用,只有当您将它移动到另一个文件夹时): 但是:目前,如果提交添加新文件或它本身重命名文件,它将失败。

      #!/bin/bash
      #
      # Attemps to guess file moves (rename of folders) when cherry-pick'ing.
      # Gaspard van Koningsveld
      #
      [ "$1" == "" ] && echo "usage: $0 <commit-hash-to-cherry-pick>" && exit 1
      TMP_PATCH_FILE="temp-cherry-pick-patch"
      function abort() {
        echo "Aborting"
        "rm" -f "$TMP_PATCH_FILE"
        exit 1
      }
      function main() {
        echo "Retreiving commit patch..."
        "git" show "$1" > "$TMP_PATCH_FILE" || abort
      
        echo "Matching renamed files..."
        sedcmds=""
        for oldfile in $("grep" -E '(--- a|\+\+\+ b)' "$TMP_PATCH_FILE" | "cut" -c 7- | "sort" | "uniq"); do
          [ -f "$oldfile" ] && continue
          renamefound=0
          oldfilepart="$oldfile"
          while [ $renamefound -eq 0 ]; do
            possiblefiles=$("git" ls-files "**/$oldfilepart")
            if [ "$possiblefiles" != "" ]; then
              if [ $("wc" -l <<< "$possiblefiles") == "1" ]; then
                echo "  $oldfile > $possiblefiles"
                sedcmds="$sedcmds s|/$oldfile|/$possiblefiles|g;"
                break
              else
                echo "  ERROR: More than one rename possibility found for file $oldfile:"
                echo "$possiblefiles"
                abort
              fi
            fi
            prevoldfilepart="$oldfilepart"
            oldfilepart="${oldfilepart#*/}"
            if [ "$prevoldfilepart" == "$oldfilepart" ]; then
              echo "  ERROR: Could not find rename for $oldfile."
              abort
            fi
          done
        done
        echo "Renaming files in patch..."
        "sed" -i "$sedcmds" "$TMP_PATCH_FILE" || abort
        echo "Applying patch as new commit..."
        "sed" -i "s/^commit /From commit /;s/^Author: /From: /" "$TMP_PATCH_FILE" || abort
        "git" am -3 "$TMP_PATCH_FILE"
      
        "rm" -f "$TMP_PATCH_FILE"
      }
      main "$@"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-11-19
        • 1970-01-01
        • 1970-01-01
        • 2012-10-02
        • 2021-02-04
        • 2016-05-23
        • 2013-10-06
        相关资源
        最近更新 更多