git merge: apply changes to code that moved to a different file

后端 未结 4 860
忘了有多久
忘了有多久 2020-12-04 05:49

I am attempting a pretty beefy git merge maneuver right now. One problem that I am coming across is that I made some changes to some code in my branch, but my colleague mov

相关标签:
4条回答
  • 2020-12-04 05:52

    I had a similar issue, and I resolved it by rebasing my work to match the target file organization.

    Say that you modified original.txt on your branch (the local branch), but on the master branch, original.txt has been copied to another one, say copy.txt. This copy has been done in a commit that we name commit CP.

    You want to apply all your local changes, commits A and B below, that were made on original.txt, to the new file copy.txt.

     ---- X -----CP------ (master)
           \ 
            `--A---B--- (local)
     
    

    Create a throwaway branch move at the starting point of your changes with git branch move X. That is to say, put the move branch at commit X, the one before the commits you want to merge; most likely, this is the commit from which you branched out to implement your changes. As user @digory doo wrote below, you can do git merge-base master local to find X.

     ---- X (move)-----CP----- (master)
           \ 
            `--A---B--- (local)
     
    

    On this branch, issue the following renaming command:

    git mv original.txt copy.txt
    

    This renames the file. Note that copy.txt did not yet exist in your tree at this point.
    Commit your change (we name this commit MV).

            ,--MV (move)
           /
     ---- X -----CP----- (master)
           \ 
            `--A---B--- (local)
     
    

    You can now rebase your work on top of move:

    git rebase move local
    

    This should work without problem, and your changes are applied to copy.txt in your local branch.

            ,--MV (move)---A'---B'--- (local)
           /
     ---- X -----CP----- (master)
     
    

    Now, you do not necessarily want or need to have commit MV in your main branch's history, because the move operation may lead to a conflict with the copy operation at commit CP in the main branch.

    You only have to rebase your work again, discarding the move operation, as follows:

    git rebase move local --onto CP
    

    ... where CP is the commit where copy.txt was introduced in the other branch. This rebases all the changes on copy.txt on top of the CP commit. Now, your local branch is exactly as if you always modified copy.txt and not original.txt, and you can continue merging with others.

                    ,--A''---B''-- (local)
                   /
     -----X-------CP----- (master)
     
    

    It is important that the changes are applied on CP or otherwise copy.txt would not exist and the changes would be applied back on original.txt.

    Hope this is clear. This answer comes late, but this may be useful to someone else.

    0 讨论(0)
  • 2020-12-04 06:02

    You can always use git diff (or git format-patch) to generate the patch, then go manually edit the filenames in the patch, and apply it with git apply (or git am).

    Short of this, the only way it's going to work automatically is if git's rename detection can figure out that the old and new files are the same thing - which it sounds like they aren't really in your case, just a chunk of them. It's true that git uses blobs, not files, but a blob is just the contents of an entire file, without the filename and metadata attached. So if you have a chunk of code moved between two files, they aren't really the same blob - the rest of the blob's content is different, just the chunk in common.

    0 讨论(0)
  • 2020-12-04 06:09

    My quick solution to this problem (in my case it was not a single file, but a whole directory structure) was:

    • move the file(s) in "my_branch" to the location where they are in "his_branch" (git rm / git add)
    • git commit -m "moved to original location for merging"
    • git merge his_branch (no conflicts this time!)
    • move the file(s) to my desired location (git rm / git add)
    • git commit -m "moved back to final location after merge"

    You will have two additional commits in the history.

    But since git tracks the moving of the files, git blame, git log etc will still work on these files, since the commits that moved the files did not change them. So I don't see any drawback to this method and it's very straightforward to understand.

    0 讨论(0)
  • 2020-12-04 06:10

    Here is a merge solution of encountering a merge conflict with rename and edit and resolving it with mergetool recognizing the correct 3 merge source files.

    • After a merge fails because of 'deleted file' that you realize was renamed and edited:

      1. You abort the merge.
      2. Commit renamed files on your branch.
      3. And merge again.

    Walk-through:

    Create a file.txt:

    $ git init
    Initialized empty Git repository in /tmp/git-rename-and-modify-test/.git/
    
    $ echo "A file." > file.txt
    $ git add file.txt
    $ git commit -am "file.txt added."
    [master (root-commit) 401b10d] file.txt added.
     1 file changed, 1 insertion(+)
     create mode 100644 file.txt
    

    Create a branch where you will edit later:

    $ git branch branch-with-edits
    Branch branch-with-edits set up to track local branch master.
    

    Create the rename and edit on master:

    $ git mv file.txt renamed-and-edited.txt
    $ echo "edits on master" >> renamed-and-edited.txt 
    $ git commit -am "file.txt + edits -> renamed-and-edited.txt."
    [master def790f] file.txt + edits -> renamed-and-edited.txt.
     2 files changed, 2 insertions(+), 1 deletion(-)
     delete mode 100644 file.txt
     create mode 100644 renamed-and-edited.txt
    

    Swap to branch, and edit there too:

    $ git checkout branch-with-edits 
    Switched to branch 'branch-with-edits'
    Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
      (use "git pull" to update your local branch)
    $ 
    $ echo "edits on branch" >> file.txt 
    $ git commit -am "file.txt edited on branch."
    [branch-with-edits 2c4760e] file.txt edited on branch.
     1 file changed, 1 insertion(+)
    

    Attempt to merge master:

    $ git merge master
    CONFLICT (modify/delete): file.txt deleted in master and modified in HEAD. Version HEAD of file.txt left in tree.
    Automatic merge failed; fix conflicts and then commit the result.
    

    Notice the conflict is hard to resolve - and that files were renamed. Abort, mimic the rename:

    $ git merge --abort
    $ git mv file.txt renamed-and-edited.txt
    $ git commit -am "Preparing for merge; Human noticed renames files were edited."
    [branch-with-edits ca506da] Preparing for merge; Human noticed renames files were edited.
     1 file changed, 0 insertions(+), 0 deletions(-)
     rename file.txt => renamed-and-edited.txt (100%)
    

    Try merge again:

    $ git merge master
    Auto-merging renamed-and-edited.txt
    CONFLICT (add/add): Merge conflict in renamed-and-edited.txt
    Recorded preimage for 'renamed-and-edited.txt'
    Automatic merge failed; fix conflicts and then commit the result.
    

    Great! Merge results in a 'normal' conflict that can be resolved with mergetool:

    $ git mergetool
    Merging:
    renamed-and-edited.txt
    
    Normal merge conflict for 'renamed-and-edited.txt':
      {local}: created file
      {remote}: created file
    $ git commit 
    Recorded resolution for 'renamed-and-edited.txt'.
    [branch-with-edits 2264483] Merge branch 'master' into branch-with-edits
    
    0 讨论(0)
提交回复
热议问题