gitmergegit-mergegit-merge-conflict

Git merge does not take all files from source branch


I have two branches, 'master' and 'develop'.

Other branches have been merged into 'master'.

Now, when I merge 'master' into 'develop', some files from 'master' are not carried over into 'develop'.

How can I resolve this?

When i merge master into develop, it gives me conflicts:

enter image description here

once solved, some files that are in master (which are not the ones in the screen), are not brought back to develop.

In master, there are commits that involve creating/modifying files that are not merged into develop.

There are no commits that delete these files

How can resolve this?

Thank you


UPDATE

In June, a branch was merged into Develop, and then immediately reverted.

This branch started from master. Could it be that the files I'm referring to were not brought into Develop because of that revert?

enter image description here

the merge: enter image description here

the revert: enter image description here

And now, when i merge master into develop, that file exists in Master, but the merge do not carry the AccessTokenController.php


Solution

  • I can't say for sure without the full history, git log --graph --decorate --oneline --stat develop master, but here's what is plausible.

    1. develop branches from master
    2. master added AccessTokenController.php
    3. fix-memory-limit branches from master; it includes AccessTokenController.php
    4. fix-memory-limit merges into develop adding AccessTokenController.php
    5. develop reverts the merge deleting AccessTokenController.php
    6. master is merged into develop

    A revert is just a commit, so Git doesn't understand that deleting AccessTokenController.php was due to a revert. All it knows is develop deleted master's AccessTokenController.php so it will not resurrect it. And since AccessTokenController.php hasn't changed there's no conflict.

    This is why complex branching structures are to be avoided: they create histories too complex to comprehend.

    Let's go through it in steps. We'll start after fix-memory-limit has been merged into develop and then reverted. Your repo looks like this.

    R [develop] << deletes AccessTokenController.php
    |
    M 
    |\
    3 \
    |  9 [fix memory limit]
    2  |
    |  8
    1  |  C [master]
    \   \ |
     \   \|
      \   B << adds AccessTokenController.php
       \  |
        \ |
         \|
          A
         ...
    

    M is the merge. R is the reversion. B adds AccessTokenController.php and it was done on master after develop branched.

    M brings AccessTokenController.php into develop, but R deletes it. Note that B was not part of develop's history before the merge, now it is.

    You merge master into develop.

    $ git switch develop
    $ git merge master
    
    M2 [develop]
    |\
    | \
    |  \
    |   \
    |    \
    R     | << rm AccessTokenController.php
    |     |
    M     |
    |\    |
    3 \   |
    |  9  | [fix memory limit]
    2  |  |
    |  8  |
    1  |  C [master]
    \   \ |
     \   \|
      \   B << adds AccessTokenController.php
       \  |
        \ |
         \|
          A
         ...
    

    develop sees it deleted AccessTokenController.php, so it does not allow the merge to resurrect it. AccessTokenController.php has not changed since the merge, so it does not create a conflict.


    If you have a failed merge, instead of reverting its better to undo the merge commit entirely using git reset. git-reset allows you to move branches around as you like.

    Let's go back to just after the first merge.

    M [develop]
    |\
    3 \
    |  9 [fix memory limit]
    2  |
    |  8
    1  |  C [master]
    \   \ |
     \   \|
      \   B
       \  |
        \ |
         \|
          A
         ...
    

    Instead of reverting, undo the merge. Move develop back to commit 3.

    $ git switch develop
    $ git reset --hard 3
    
    M 
    |\
    3 \  [develop]
    |  9 [fix memory limit]
    2  |
    |  8
    1  |  C [master]
    \   \ |
     \   \|
      \   B
       \  |
        \ |
         \|
          A
         ...
    

    Now it's just as if the merge never happened. While the merge commit is still there, nothing references it; it will eventually be garbage collected.


    in develop, after the merge revert, 3 months have passed.

    In this case you can't simply reset. If you do you'll lose all the work in those 3 months. You need to do an interactive rebase to remove the merge and revert commits and then replay all the newer commits on top of commit 3.

    Let's say your repository looks like this.

    100 [develop]
    .
    .
    .
    5
    |
    4
    |
    R
    |
    M 
    |\
    3 \
    |  9 [fix memory limit]
    2  |
    |  8
    1  |  C [master]
    \   \ |
     \   \|
      \   B
       \  |
        \ |
         \|
          A
         ...
    

    We want to delete R and M to get, effectively, this.

    100 [develop]
    .
    .
    .
    5
    |
    4
    |
    3
    |  9 [fix memory limit]
    2  |
    |  8
    1  |  C [master]
    \   \ |
     \   \|
      \   B
       \  |
        \ |
         \|
          A
         ...
    

    develop is no longer associated with fix memory limit.

    Doing git rebase -i -r 3^ will bring up an editor showing all the commits from 100 back to 3. Simply delete the lines for the merge and reversion. Then Git will rewrite commit 4 on top of commit 3 making a new commit with a new commit ID. It'll rewrite commit 5 on top of the new 4 making a new 5, commit 6 on top of the new 5, and so on until it gets to 100.

    However, even though all the commit should have the same content (M and R cancel each other out) they will have different commit IDs. Commit IDs are fundamental to how Git works. This has consequences for anyone else working on develop. You will not be able to simply push your change, you will need to git push --force-with-lease and then others who are working on develop will need to git pull --rebase to update their local repositories. See The Perils Of Rebasing in the Rebasing section of Pro Git.

    Rebasing 3 months worth of work could go sideways. Be sure to check your work before pushing. You can always return develop back to its original state with git reset --hard <the original commit>. Be sure to note down the commit ID of develop before rebasing, or tag it.

    After the rebase, try merging master into develop and see if it goes better this time.