What is Knowledge of Merging Strategies in the Context of Git?

Question
What are different merging strategies in the context of Git?

Possible Answers
"...branches are completely useless unless you merge them…" - Linus Torvalds

A branch is a set of files associated with a code base usually in a collection known as a repository. Atlassian says "[b]ranching is a feature available in most modern version control systems...", and a merge is "[a] process that unifies the work done in two branches..." (as quoted from here.)

A build, release, or DevOps engineer or software developer will use code versioning tools. Merging, to reconcile different branches, can be managed by technical configurations of code versioning systems as well as enterprise procedures that may govern teammates usage of such systems.

There are multiple types of merging that happen in version control systems behind-the-scenes. Depending on how they are coded, they can use line-based, text-based, static semantic, and syntactic merging.* This article does not focus on the development of your own code version control system; this article focuses on being a user of such a software product and the strategies for branching and merging.

The develop branch (like a feature branch) collects and receives changes to the code base. At some point, this branch will be merged with the main branch.

There are different merge policies that a given code versioning system can be configured to support. For example, Azure DevOps Repos, Atlassian BitBucket, Git, etc. have built-in configurations to handle conflict reconciliation or prevent conflicts from happening.

Squash merges eliminate the history of the changes to files, and give you just the result of the changes. Microsoft's website says "[a] simple way to think about this is that squash merge gives you just the file changes, and a regular merge gives you the file changes and the commit history."

Some development teams are discouraged from a procedural perspective from doing squash merges, but your CVS may allow them to happen. Squash merging is strongly discouraged in part because of the benefits of doing diffs (examining the differences in code that was checked in if a bug was discovered). (This previous sentence was derived from a Medium.com posting.)

A merge of branches preserves the history of changes in the most recently modified branch whereas rebase undoes recent changes to make one branch identical to the source branch. (The source of this is https://betterprogramming.pub/differences-between-git-merge-and-rebase-and-why-you-should-care-ae41d96237b6.)

Rebase involves removing zero or more changes in a codebase's branch (often on a client) to make it like a repository's source branch version (often remotely in the central repository), whereas merge involves receiving and reconciling two different branches. see this StackOverflow posting.

It is hard to discretely list all possible merging strategies. In the context of using Git, there are numerous options to merging code. There are built-in options that software repositories have (e.g., for BitBucket) called "merge strategies" that are different from human techniques and policies to merge code. In theory the term "strategy" could refer to either.

Merge commits have two or more parent commits (according to this external site).

Squash commits have a non-merge commit on the target branch (according to Atlassian's website).

Rebase involves having a remote branch's changes supercede any conflicts with the local branch you are working on. (Your local repo's files can be overwritten if you rebase.)

Fast-forward merges involve a merge commit if the source branch is older than the target branch otherwise the target branch gets updated with the changes on the source branch. (The source of this is this external site.)

To learn about recursive, resolve and octopus merge strategies, see this posting. To learn more about Git merging strategies in general, see these postings:

The influential book Continuous Delivery recommends adopting a consistent merge strategy (on page 408), but this is achievable with enabling/using rebasing, fast-forward merges, squash commits, and merge commits regularly.

Sometimes changes are merged to a Git repo that were not from a branch in the repo. The term "promiscuous integration" refers to pulling from different forks of the "same" repository (according to page 81 of Continuous Delivery).

Branching strategies are closely related to merging strategies. You may want to see this posting. Trunk-based development deals with few to no branches; when you use it you would often commit code to one branch and not merge code.


* To learn more about the behind-the-scenes merging strategies, read the following:

To learn about them you could read pages 367 through 369 of Elements of Programming Interviews in python.

Text-based merging does not take into account syntax, semantics or inconsequential changes; thus failures are reported as "conflicts" are detected when there are no significant changes. (This previous sentence was paraphrased from page 368 of Elements of Programming Interviews in python.)

To learn more about line-based merging, see this article.

To learn more about semantic merging, see this PDF or page 22 of this PDF.

To learn more about syntactic merging, see page 20 of of this PDF.

Generally, there are ways of preventing conflicts, reconciling conflicts asynchronously, or addressing conflicts in someone's absence. Certain practices that support this are the following: 1) not committing binary files (as the resulting merge will be unusable or the code versioning system will use too much space). 2) Making meaningful commit messages (such as a link to a Jira or ServiceNow ticket). 3) Setting up email notification when a branch has been updated. Some individuals will want to know if there is activity on a certain branch.

For more recommendations on how to govern the code versioning system, see this.

Leave a comment

Your email address will not be published. Required fields are marked *