Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

What is the general process for merging two git branches, reviewing edits on each branch?

+9
−0

Suppose you have a largish git repository with many files of different types (both text and images) distributed among a number of nested directories. Parallel development has occurred on two branches, each editing, creating, and removing files at various points within the directory tree.

Now you want to merge the two branches, reviewing each of the edits to ensure that the correct information is preserved to maintain the desired content and functionality from each of the two branches.

What is the process for doing this?

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

1 comment thread

Merge or git merge? (3 comments)

2 answers

You are accessing this answer with a direct link, so it's being shown above all other answers regardless of its score. You can return to the normal view.

+9
−0

There are two major sections to this answer: the Git part and the conflict resolution part. It wasn't clear at first which one was intended by the question, but both are important for a full answer.

Git workflow

Workflow summary

  1. Make a temporary branch in case things go badly.
  2. Start the merge.
  3. Resolve conflicts.
  4. Iterate bug fixes until it works.
  5. Consolidate the merge.
  6. Merge back the changes from the temporary branch to your primary.

For a simple merge, you can get by with 2, 3, and 6. But if this is as big a merge as you imply, the extra steps can be really helpful.

Workflow details

  1. Make an integration branch from your "primary" (typically main or master) branch.

    git switch -c integration
    
  2. Merge your other branch into integration.

    git merge other-branch
    

    This will probably result in merge conflicts if what you describe is true. If it doesn't result in merge conflicts, the merge will finish right here and you can skip to Step 2.3.

    1. Deal with the merge conflicts in an appropriate merge tool.

      git mergetool
      
      • This is probably the most complicated part of the process. See the dedicated section below.
      • You may have to consult with the submitters of text and images on both branches to determine which files to keep.
      • You may even have to write new code to rectify changes from both sides.
      • You may make mistakes. That's okay! You can fix those.
    2. Commit the merge.

      git commit
      
    3. Test the code yourself.

    4. Push integration to be evaluated by your CI/CD and your testers.

      git push
      
  3. Fix any mistakes on integration.

    Use new commits for these for now. It's lots easier to comprehend your fixes when they're separate from the huge merge.

  4. Repeat Steps 2.3 to 3 until satisfied.

  5. Squash all the fixup commits on integration.

    This is not strictly necessary, but anyone using Git ReReRe will appreciate that you consolidate any extra conflict resolution directly into the merge commit. You can even share rerere solutions across a team.

    • The easiest way is to use a GUI Git client to combine the fixups with your merge commit.

    • In the absence of a GUI, this is done with an interactive rebase, which can be a little intimidating with the --rebase-merges option:

      1. git rebase -i --rebase-merges 3fe8aa^
        

        …where 3fe8aa is the merge commit's SHA. Note the ^ to get the commit before it.

      2. Your text editor will then present a list of commits in the format below.

        label onto
        
        # Branch other-branch
        reset 57a270 # Merge branch 'other-branch' into integration
        pick a27157 So many commits from...
        pick f00ba2 ...the other branch here
        label other-branch
        
        reset onto
        merge -C 3fe8aa other-branch # Merge branch 'other-branch' onto integration
        pick ed170r Fix the build
        pick de1e7e Really fix the build this time
        pick c0ffee Fix the backend
        pick dec0de Fix the UI
        
      3. Change all the pick lines in the bottom section to say fixup[1] instead of pick to fold them into the merge commit. There are instructions at the bottom of the file in a big comment, in case you forget.

  6. Test again, just to be sure.

  7. Merge integration into your primary branch.

    git switch main
    git merge integration
    

Merge conflict resolution

Congratulations! You've found one of the hardest parts of SCM. Git's pretty good at slotting in new lines or taking things out when a merge occurs. But sometimes the additions or subtractions occur in the same location in a file, and Git needs human intervention.

Types of conflict

  • Two branches changed the same part (or parts) of a file.
  • One branch edited a file, but the other branch moved or deleted that file.
  • Two branches independently added a file to the same place.

This is not quite a comprehensive list, but it covers most cases. To see all the possibilities, see the "unmerged" lines in the git status --short documentation's XY chart.

Conflict markers

In text files with conflicts, Git will add conflict markers to designate the pre-existing conditions. These are usually paired <<<<<<< and >>>>>>> separated by a =======. There is optionally a ||||||| section showing the last shared content of that section. These are labeled with the source branch or commit.

You'll see these if you open a text file in your editor that contains conflicts before that file has been resolved. Most of the time, you're using a tool that reconstructs the full file from each side of the merge and you don't see the markers, but it's worth knowing what they are in case you encounter them.

Sometimes with simple conflicts, you can identify what ought to be in a conflicted section and fix it directly with a text editor in a pinch.

Merge tools

You may have noticed that instructions on how to actually deal with conflicts is pretty sparse in Git's documentation. That's because it pawns off conflict resolution onto "mergetools," and which one is right for you or right for a certain situation is a matter of preference.

These are sometimes terminal-based programs like vimdiff,[2] or GUI programs like Kdiff3, Meld, Kaleidoscope, WinMerge, Sourcetree, Sublime Merge, and on and on. Git's documentation has a list of already-configured mergetools, though you may or may not have any particular one installed.

Git's documentation isn't going to comprehensively cover how these work or how to use them (and neither can I), but in general they have some common components. You run git mergetool, and the one configured in Git config will spawn. To pick a different one, use git mergetool --tool=winmerge or whatever. You could conceivably use one for text conflicts and another for binary conflicts.

Merge tool layout

(Optionally) List of files to be resolved

If you're working your way through a whole set of files, there may be a way to select the next one for handling.

File contents on each branch

On one side, you're going to see the code from one branch. On the other side, you're going to see the code from the other branch. The sides ("ours" and "theirs") change depending on whether you're doing a merge or a rebase, so look for the descriptive labels to know which side contains which branch.

For each set of conflicted lines, there will be a way to select the lines from the left or the right (maybe both or neither as well), to add to the output.

A mergetool may or may not be able to handle and/or render non-text files.

(Optionally) The merge base

You may have a mergetool that also shows the original file from before it diverged, for context, in case that is helpful.

The editor pane

This is where you can actually edit the outcome of the merge. If the left/right selection is insufficient for what the file should actually look like, you can tweak it directly.

Merge tool use

  • You're going to go through each text file and decide each conflicted section.

    If the other file was moved, your mergetool may be smart enough to ask you about conflicts against the file in its moved location. Or it may not.

  • For images and binary files, you're basically going to choose which whole file is appropriate.

As you "handle" each file in the mergetool, it will report back to Git what the content should be for that file. Each file has to be handled in its entirety. The mergetool reporting has a pass/fail component, but you can "fail" it and come back to (re)fix it after working on some others.

For any files that can't be handled by the mergetool(s), you need to get the files into the shape you desire and git add them to the staging area of the merge commit.

Tips

  • Don't accidentally create empty files. Sometimes a mergetool will incorrectly recreate a "deleted" file with no content. Make sure those are really deleted.

  • Whitespace differences, particularly line endings, can make merging especially difficult. It can wildly expand the "different" lines. There are options when doing a git merge that can alter treatment of whitespace, which may collapse the divergent sections to their appropriate locations.

  • Git ReReRe ("reuse recorded resolutions") is disabled by default. You may want to turn it on to record pre-image and outcome for merge resolutions. Then if it encounters those exact situations again, the conflict can be resolved automatically in the same way.

  • You also have some control over the conflict marker sections. I personally use zdiff3 as my merge.conflictstyle.


  1. You can also use squash instead of pick to include their commit messages into the merge commit. You will then have a chance to modify the merge's commit message if you want to. ↩︎

  2. I swear I will learn vimdiff someday. ↩︎

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

3 comment threads

Step 5 "Squash all the fixup commits on integration." is optional (2 comments)
I think an explanation is needed of what it means to "Deal with the merge conflicts in an appropriate... (6 comments)
Extra intermediate branch is worth it (1 comment)
+3
−0

For completeness, I'll add some more obscure ways:

If your goal is to simply move some changes from one branch to another, you can also use git rebase and git cherry-pick. These give you more control over what exactly you are moving. For example, git rebase has an interactive mode where you can pick and choose exactly which commits you want and also tweak them (message, squash).

I would say Michael's answer (with git merge) is the normal and better way to do it. It makes git take care of a lot of the tedious steps by automatically resolving trivial merges without bothering you for each one. Usually, it's easier to keep your branches from diverging too much, make minimal/atomic commits, and follow other git best practices and then just trust the algorithm of git merge.

But if you really do want the nitty gritty, perhaps something like rebase or cherry pick is more suited for your usage.

One notable caveat: When you use git merge, git will record the merge (by creating a special "merge commit"), so that when visualize the history with various tools the branches will actually come together. If you use things like rebase or cherry-pick, this creates new ordinary commits and the link is somewhat broken. It will not be clear just from looking at the branch diagram that the branch has been partly combined into another, you will have to rely on commit messages and descriptions to indicate that, which are not always well understood by tools.

These days, many Git hosts like Github have taken to implement "pull request" workflows where you can merge via the interface and choose to squash or rebase. The git host will produce the illusion that the history is not broken in contradiction of my previous paragraph. But what's actually happening is that the Web UI and the host's internal database (eg. Postgres) is acting as a layer on top of git that provides extra features like understanding squash/rebases. When you clone the repo and try to look at it without the git host, you will notice that none of the pull request information is working properly, and if you simply push it to a new host, you will discover that the PRs must be migrated separately from the git data.

This "breakage" for rebase/squash commits is arguably a limitation of git, which the utilities (Git hosts) have attempted to solve by adding their own bandaids on top. It was never part of Git as it was originally designed (no central server, and people just email patch to each other). There are other VCS that try to track such things as well, I think Fossil is one. But now we are going on quite a tangent, and if you want to learn more I think you should ask separate questions for it.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

1 comment thread

git rebase --onto (3 comments)

Sign up to answer this question »