Resolve merge conflicts

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

Visual Studio 2019 | Visual Studio 2022

When you merge or rebase, you're telling Git to integrate changes made on one branch with changes made on another. Often, Git completes a merge or rebase automatically without your assistance. However, if Git discovers that a change made on one branch conflicts with a change made on another, it prompts you to resolve the conflict. A merge conflict can occur when the merged branches edit the same file line differently, or when one branch modifies a file and another branch deletes it. The process for resolving merge conflicts is applicable to both Git merge and rebase.

You can resolve merge conflicts in Visual Studio, or by using the command line and any text editor.

For an overview of the Git workflow, see Azure Repos Git tutorial.

This article provides procedures for the following tasks:

  • Understand merge conflicts
  • Resolve merge conflicts

Understand merge conflicts

Git merge or rebase integrates commits from a source branch into your current local branch (target branch). Git merge performs either a fast-forward or a no-fast-forward merge. The no-fast-forward merge is also known as a three-way merge or true merge. Git rebase is another type of merge. These merge types are shown in the following diagram.

Diagram showing the before and after commits when using Git merge and Git rebase.

For Git merge, if the tip of the target branch exists within the source branch, the default merge type will be a fast-forward merge. Otherwise, the default merge type will be a no-fast-forward merge.

A fast-forward merge can never have a merge conflict because Git won't apply a fast-forward merge if the tip of the target branch has diverged from the source branch. By default, Git uses a fast-forward merge whenever possible. For example, Git will apply a fast-forward merge on a local branch that you only update by pulling from its remote counterpart branch.

A no-fast-forward merge generates a new target branch "merge commit" that integrates source branch changes with target branch changes. The applicable changes are those made after the last commit that's common to both branches. In the preceding diagram, commit C is the last common commit in both branches. If any source branch change conflicts with any target branch change, then Git will prompt you to resolve the merge conflict. The merge commit (L) contains the integrated source branch and target branch changes. The source and target branch tips (K and E) are the parents of the merge commit. In your branch's commit history, a merge commit is a useful marker for a merge operation, and clearly shows which branches were merged.

Git rebase resequences the commit history of the target branch so that it contains all source branch commits, followed by all target branch commits since the last common commit. In the preceding diagram, commit C is the last common commit in both branches. Another way to view it is that a rebase replays the changes in your target branch on top of the source branch history. If any source branch change conflicts with any target branch change, then Git will prompt you to resolve the merge conflict. As with the fast-forward merge, a rebase doesn't create a merge commit. Notably, a rebase changes the sequence of the existing target branch commits, which isn't the case for the other merge strategies. In the preceding diagram, commit K' contains the same changes as K, but has a new commit ID because it links back to commit E instead of C.

Git merge and rebase only modify the target branch—the source branch remains unchanged. When you encounter one or more merge conflicts, you must resolve them to complete the merge or rebase. Or, you can cancel the merge/rebase operation and return the target branch to its prior state.

For more information on merge options and strategies, see the Git reference manual and Git merge strategies.

When to resolve merge conflicts

Git merge and Git rebase are extensively used in the Git workflow. When working on a local feature or bugfix branch, it's common practice to:

  1. Keep your local main branch current with its remote counterpart by periodically pulling to fetch and merge remote commits.
  2. Integrate local main branch updates into your local feature branch using a rebase or merge.
  3. Back up your work on the local feature branch by pushing it to the corresponding remote branch.
  4. On feature completion, create a pull request to merge your remote feature branch into the remote main branch.

By frequently integrating remote changes into your local repo, you can stay aware of recent work by others and promptly resolve any merge conflicts that arise.

Resolve merge conflicts

The process for resolving merge conflicts is applicable to both Git merge and Git rebase. Although the following steps describe how to resolve merge conflicts during a merge, you can similarly resolve merge conflicts during a rebase.

Tip

If the source branch is a remote-tracking branch, ensure that branch is up-to-date by running a Git fetch before a merge. Or, run the Git pull command, which combines a Git fetch with a Git merge.

Visual Studio 2022 provides a Git version control experience by using the Git menu, Git Changes, and through context menus in Solution Explorer. Visual Studio 2019 version 16.8 also offers the Team Explorer Git user interface. For more information, see the Visual Studio 2019 - Team Explorer tab.

  1. In the Branches pane of the Git Repository window, checkout the target branch. Then right-click the source branch and choose Merge <source-branch> into <target-branch>.

    Screenshot of the Merge option in the branch context menu in the Git Repository window of Visual Studio.

  2. Visual Studio will notify you if Git halted the merge due to conflicts. In that event, you can either resolve the conflicts, or cancel the merge and return to the pre-merge state. The Unmerged Changes section of the Git Changes window lists the files with merge conflicts. For a file with merge conflicts in its content, double-click the file to open it in the merge editor.

    Screenshot of the files with merge conflicts in the Git Changes window of Visual Studio.

  3. In the merge editor, the Incoming pane shows the source branch file version, the Current pane shows the target branch file version, and the Result pane shows the resultant merge file. To apply specific source or target branch changes, select the checkbox next to the conflicting line(s) that you want to keep. You can also directly edit the merge file in the Result pane. Choose Accept Merge after you've resolved all merge conflicts in the current file. Repeat this step for each file with content conflicts.

    Screenshot of the merge editor in Visual Studio.

  4. For a file that was edited in one branch and deleted in the other, right-click the file and select which branch action you want.

    Screenshot of the context menu for a conflicting file in the Git Changes window of Visual Studio.

  5. In the Git Changes window, enter a commit message and choose Commit Staged to complete the merge—after you've resolved all merge conflicts for all files.

    Screenshot of the commit message and Commit Staged button in the Git Changes window of Visual Studio.

Next steps