To merge or merge in — is the action of merging two branches in Git, combining changes from one branch into another. In modern development, merge is the standard way to integrate a feature branch into the project’s main branch. According to GitHub Octoverse 2024, over 15 million merges are performed daily. Merge is a key mechanism of collaborative work, allowing the efforts of multiple developers to be combined into a single product.
Key Takeaways
Merge in Git is the operation of combining two or more development histories into one. When a developer merges a branch, Git automatically finds the common ancestor (base commit) and creates a new merge commit that includes changes from both branches. Three-way merge is the standard algorithm that compares three states: the common ancestor, the first branch, and the second branch.
The merge process begins with the git merge command. Git determines the point where the branches diverged and sequentially applies changes from the source branch onto the target branch. If the changes do not conflict, Git performs a fast-forward or creates a merge commit depending on the settings. Fast-forward is a scenario where the target branch simply moves forward to the source branch’s commits.
# Switch to target branch and merge
git checkout main
git merge feature/payment-module
# Merge with explicit no-fast-forward
git merge --no-ff feature/payment-module
# Abort merge if conflicts are too complex
git merge --abort
The --no-ff flag (no fast-forward) forces the creation of a merge commit even when a fast-forward is possible. This preserves information that the changes were made in a separate branch. Many teams prefer this approach to maintain explicit branching history.
Git has three main branch merging strategies, each suitable for a specific scenario. The choice of strategy depends on the team’s culture and requirements for the cleanliness of the project history.
| Strategy | Result | When to Use |
|---|---|---|
| Standard merge | merge commit + full history | teams that value complete history |
| Squash merge | one commit, history compressed | feature branches with many small commits |
| Rebase merge | linear history, no merge commit | personal feature branches, before creating a PR |
Standard merge creates a merge commit with two parents. The full history is preserved, but the branching graph becomes more complex. Squash merge combines all commits of a feature branch into one and applies it on top of the target branch — the history becomes linear and clean, but information about intermediate stages is lost.
Rebase, although not a full-fledged merge, achieves the same result — changes from one branch are moved on top of another. The difference is that history is rewritten: commits of the feature branch are recreated on top of the latest commit of the target branch. This gives a perfectly linear history but requires a force push when uploading.
A merge conflict occurs when the same lines of a file are changed in two branches. Git cannot automatically determine which version to keep and requires developer intervention. Conflicts are displayed in files using special markers: <<<<<<<, =======, >>>>>>>.
The conflict resolution process involves several steps. First, the developer opens the conflicting file and manually selects the needed changes. It is important not just to choose one version but to understand the logic of both changes and make a correct decision. After editing the file, the conflict markers are removed, and the changes are added to the staging area via git add.
# View list of conflicted files
git status
# Start mergetool (e.g., VS Code, IntelliJ)
git mergetool
# After resolving all conflicts
git add .
git merge --continue
# Or abort the merge entirely
git merge --abort
Using visual merge tools significantly speeds up conflict resolution. VS Code, IntelliJ IDEA, and GitKraken provide interfaces with three panels: current branch, incoming branch, and result. The git mergetool command automatically opens the configured editor for each conflicting file.
The best way to avoid complex conflicts is regular synchronization of the feature branch with the main branch. If a developer merges main into their branch once a day, conflicts will be small and easily resolvable. Accumulating changes over a week guarantees complex conflicts with a high risk of errors.
Rebase and merge are two ways of combining changes, and the choice between them often sparks debates in teams. Rebase moves commits from one branch on top of another, rewriting history. Merge creates a new merge commit, preserving branching history. Each approach has its advantages and limitations.
Rebase is appropriate when a developer is working on their local feature branch and wants a clean linear history before creating a Pull Request. After rebase, all commits are arranged sequentially without unnecessary merge commits. However, rebase requires force push and is not applicable to branches where multiple people work simultaneously.
The golden rule of Git: do not use rebase on commits that have already been pushed to the shared repository. This ensures that the history in the shared branch remains unchanged and other developers will not encounter duplicate or lost commits. For integrating a feature branch into the main branch, use merge via Pull Request.
A proper merge process is the foundation of stable development. In modern teamwork, merging is done not through the console but via Pull Request on GitHub or Merge Request in GitLab. A PR goes through code review, automated CI checks, and only then is merged into the main branch.
The first practice — merge only after all checks pass. The CI pipeline should build the project, run tests, and verify code quality. If at least one check fails, the merge is blocked. Modern platforms (GitHub, GitLab) have built-in protection: branch protection rules automatically block merging if CI fails.
The second practice — never merge broken code. Before merging, the developer must ensure their changes do not break the build or regress existing functionality. Automated tests and code review serve this purpose.
The third practice — clean up feature branches after merging. A branch that has been merged should be deleted. This prevents confusion and clutter in the repository. GitHub automatically offers to delete a branch after a PR is merged, and repository settings can be configured for automatic deletion.
Frequently Asked Questions
A merge is the combination of two Git branches into one. Changes from one branch are transferred to another through a three-way merge. The result is recorded in a new merge commit that has two parent commits. Merge commit preserves information about which branches were merged.
Merge creates a new merge commit, preserving branching history. Rebase rewrites history by moving commits on top of another branch without creating a merge commit. Rebase gives a linear history but requires force push. Merge is safer for shared branches, rebase is better for personal ones.
Open the conflicting file, find the <<<<<<<, =======, and >>>>>>> markers, choose the needed changes and remove the markers. Add the file via git add and complete the merge via git merge --continue. Use git mergetool for visual resolution in VS Code or IntelliJ IDEA.
Pull Request (or Merge Request) is mandatory when merging a feature branch into the project’s main branch. A PR goes through code review by colleagues and automated CI checks. This is the standard of modern development. Direct push to the main branch is prohibited in most projects.
Squash merge combines all commits of a feature branch into one before merging. This gives a clean main branch history without intermediate draft commits. Use squash merge when a feature branch contains many utility commits (wip, fixes) and you don’t need to preserve all intermediate steps in the history.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also