git merge

Combine changes from different branches, integrating feature work into main or incorporating updates from teammates.

You've finished working on a feature branch, your pull request has been approved, and now you need to integrate those changes into main. Or perhaps you're on a feature branch and want to pull in the latest updates from main to stay current. git merge combines the histories of two branches, creating a new commit that ties them together.

The basic flow is to check out the branch you want to merge into, then run git merge branch-name to merge the other branch in. If you're on main and run git merge feature-login, Git brings all commits from feature-login into main. If the branches haven't diverged—meaning feature-login is just ahead of main with new commits—Git performs a "fast-forward" merge by simply moving the main branch pointer forward. No merge commit is created because there's nothing to merge; main just catches up to feature-login.

When both branches have new commits, Git creates a merge commit that combines both histories. This commit has two parents, representing where the branches split and where they came back together. The merge commit appears in your history as a marker of when the integration happened, and it includes a default message like "Merge branch 'feature-login' into main." You can customize this message or keep the default.

Handling Merge Conflicts

Conflicts happen when both branches modified the same lines in the same files. Git can't automatically decide which changes to keep, so it stops the merge and asks you to resolve the conflicts manually. You'll see files marked with conflict markers showing both versions, and Git lists the conflicted files. You edit each one, choosing which changes to keep, remove the markers, then stage the resolved files with git add and complete the merge with git commit.

If you run into conflicts you're not ready to handle, git merge --abort cancels the merge and returns your branch to its state before you started. This is useful when you realize you need to understand the conflicting changes better or coordinate with the person who wrote the other code.

Merge Strategies and Options

By default, Git creates merge commits when histories have diverged, but git merge --no-ff forces a merge commit even for fast-forward merges. This preserves a record of the branch existing, which some teams prefer for tracking when features were integrated. The opposite is git merge --ff-only, which fails if a fast-forward isn't possible, preventing unexpected merge commits.

The --squash option combines all commits from the merged branch into a single commit on your current branch. Running git merge --squash feature-login brings all the changes from feature-login, but instead of creating a merge commit, it stages everything for you to commit manually. This creates a cleaner history with one commit per feature instead of dozens of small commits, though you lose the detailed commit history from the feature branch.

Pull Request Integration

In pull request workflows, merging usually happens through the platform's interface (GitHub, GitLab, etc.) rather than manually. When you click "Merge pull request," the platform runs git merge on the server, integrating your branch into the target branch. Different platforms offer different merge strategies: regular merge commits, squash merges that combine commits, or rebase-and-merge that replays commits without a merge commit.

Even though the PR platform handles the final merge, you often merge locally during development. If main has advanced while you were working on your feature branch, you might merge main into your feature branch to stay up to date. This ensures your PR will merge cleanly when approved and lets you test that your feature works with the latest code.

Three-Way Merges and Ancestry

Git's merge algorithm is a "three-way merge" that looks at three points: the common ancestor of both branches, the tip of the current branch, and the tip of the branch being merged. By comparing all three, Git can intelligently determine what changes were made by each branch and how to combine them. This is more sophisticated than a simple two-way diff and allows Git to automatically resolve many potential conflicts.

When merging fails with complex conflicts, you might want more context about what happened. Running git log --graph --oneline --all after a failed merge shows the branch structure and helps you understand how the branches diverged. Some developers use visual merge tools configured with git mergetool to resolve conflicts with a graphical interface instead of editing conflict markers manually.

After a successful merge, the merged branch still exists. If you merged feature-login into main, feature-login remains as a branch pointing to its last commit. You typically delete feature branches after merging with git branch -d feature-login, which keeps your branch list clean. GitHub and other platforms often do this automatically when you merge pull requests.

Understanding merge means understanding that Git is combining two parallel timelines into one. Sometimes that combination is trivial (fast-forward), sometimes Git can do it automatically (three-way merge with no conflicts), and sometimes you need to make decisions (resolving conflicts). The key is knowing which type of merge you want and being prepared to handle conflicts when they arise.