Backport changes from renamed file
Asked Answered
R

7

27

I have two branches: trunk, production. I have found a problem in trunk, made fix and committed it, pushed it. Now it was tested and I need do merge changes into the production branch as a hot-fix. I try to use the cherry-pick. However it doesn't work because a changed file(s) in the fix was renamed in the trunk earlier during some refactoring which I don't want bring into production.

I don't want merge everything, but take only this commit. The cherry pick fails with "deleted by us" conflict (of course, the new file never even existed in the production branch).

What is the correct way to bring the changes into the old file?

Rompers answered 19/3, 2012 at 15:7 Comment(0)
S
14

I'd use good old patch for this:

git show COMMIT_ID -- old/file/name.txt | patch new/file/name.txt
Shericesheridan answered 19/3, 2012 at 16:2 Comment(3)
Yes, it works for me. However it would be interesting to find a better solution, particularly in case if there are several renamed files.Rompers
This looks promising but I can't figure out how to determine what to use for COMMIT_ID and when I'm supposed to call this command. (Can I call it during a rebase when I run git rebase develop and it hits a conflict?)Tarriance
COMMIT_ID can be anything identifying a commit, such as a commit hash, HEAD, a branch name, etc. If you're rebasing, it's probably easiest to look at the history and copy the commit hash.Shericesheridan
P
28

If:

  • You expected/hoped that Git would detect the move or rename of the file on trunk, but it didn't, and
  • Your repository has a reasonable number of files

... then you should definitely consider changing your git config like this:

$ git config merge.renameLimit 999999

It is possible that during a merge/cherry-pick, git is hitting the default file check-limit (I think it's 400 or 1000 or something like that) before it is able to locate the suitable rename match. Upping this limit may cause merge/cherry-pick to take longer while it searches for your renamed file, but it can help avoid "deleted by us" merge-challenges.

That should do the trick, but if your renamed file was small and the changes between branches is significant, you might also play with the -X rename-threshold setting, e.g. lowering it from the default 50% with -X rename-threshold=25%.

Puissant answered 16/2, 2016 at 5:48 Comment(1)
A note on rename-threshold for other users -- this includes all changes over time. I had a merge where the original rename changed two lines in the file, but since it had been heavily modified after that point, git still did not detect the file similarity without lowering rename-threshold.Nimrod
S
14

I'd use good old patch for this:

git show COMMIT_ID -- old/file/name.txt | patch new/file/name.txt
Shericesheridan answered 19/3, 2012 at 16:2 Comment(3)
Yes, it works for me. However it would be interesting to find a better solution, particularly in case if there are several renamed files.Rompers
This looks promising but I can't figure out how to determine what to use for COMMIT_ID and when I'm supposed to call this command. (Can I call it during a rebase when I run git rebase develop and it hits a conflict?)Tarriance
COMMIT_ID can be anything identifying a commit, such as a commit hash, HEAD, a branch name, etc. If you're rebasing, it's probably easiest to look at the history and copy the commit hash.Shericesheridan
B
11

Faced with the same problem, I asked a colleague what he would do, and his instant response was:

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

Worked like a charm for me.

Barbaresi answered 24/2, 2017 at 12:15 Comment(4)
This worked from me. I used a GUI, but followed the same steps. It took me a minute to figure out what this is doing, so I'll list out the steps: 1. Rename files/directories on the destination branch to match the source branch and commit. 2. Cherrypick the change from the source branch to the destination branch. 3. Rename files/directories on the destination branch back how they were originally and commit. 4. Squash those 3 commits into one commit.Hardener
I have not been able to make any sense out of this answer even though it looks promising. If you have a moment, I'd love for you to answer here: https://mcmap.net/q/22346/-how-to-recover-from-git-quot-deleted-by-us-quot-conflict-during-rebase-after-file-had-been-renamed-but-git-treated-as-a-deletion/470749 Thanks!Tarriance
excellent idea! Especially the path is rename by both sides. It helps a lot. ThanksArcadia
I didn't have to commit after the mv instructions, just stage was enough.Dejesus
C
3

To cherry pick changes to any number of files, in case of a directory rename between branches:

git diff ... | sed -e 's|<old dir>|<new dir>|' | git apply -
Critta answered 5/2, 2015 at 13:55 Comment(1)
I have not been able to make any sense out of this answer even though it looks promising. If you have a moment, I'd love for you to answer here: https://mcmap.net/q/22346/-how-to-recover-from-git-quot-deleted-by-us-quot-conflict-during-rebase-after-file-had-been-renamed-but-git-treated-as-a-deletion/470749 Thanks!Tarriance
M
1

I made a shell script that tries to do a cherry-pick while guessing file moves (it doesn't work if you renamed the file itself, only if you moved it to another folder): However: currently it will fail if the commit adds new files or if it itself rename files.

#!/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 "$@"
Mash answered 18/5, 2017 at 2:29 Comment(0)
P
0

This is kind of tricky. For example, you could create a patch from a diff and apply it to the old file. But in the future to prevent these problems, I'd recommend to do fixes on the production branch and test it there first, then merge from production into trunk.

Penthea answered 19/3, 2012 at 15:33 Comment(3)
Yes, I understand that, however it is not always possible to predict what will go into hot-fix. I'm trying to do it via format-patch/apply-patch, but it does nothing (no errors, no changes too). Please give a hint of proper way to use it.Rompers
Check out the section "Rename handling in git" in this post blogs.atlassian.com/2011/10/confluence_git_rename_merge_oh_myPenthea
Moved to atlassian.com/blog/developer/2011/10/… - sadly, with formatting messed up.Lizzielizzy
C
0

I experience the same problem and have tried to find a solution.

I solved by using a sequence of rebases. I've done no further tests than these so use at own risk!

If your're interested have a look at it on github:

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

Corin answered 2/8, 2012 at 11:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.