{"id":625,"date":"2023-01-13T16:16:00","date_gmt":"2023-01-13T16:16:00","guid":{"rendered":"https:\/\/www.aviator.co\/blog\/?p=625"},"modified":"2025-09-25T11:51:11","modified_gmt":"2025-09-25T11:51:11","slug":"rebase-vs-merge-pros-and-cons","status":"publish","type":"post","link":"https:\/\/www.aviator.co\/blog\/rebase-vs-merge-pros-and-cons\/","title":{"rendered":"Rebase vs. Merge: Pros and cons"},"content":{"rendered":"\n<p>Managing repository history is one of the critical parts of keeping your code base scalable. Many approaches might be helpful in one case and not work in another. Here we will discuss how git merge and git rebase differ from each other.<\/p>\n\n\n\n<p>Let&#8217;s have a look at how both commands work.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/docs.aviator.co\/mergequeue\/quick-setup\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"125\" src=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1024x125.png\" alt=\"\" class=\"wp-image-1787\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1024x125.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-300x37.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-768x94.png 768w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1536x188.png 1536w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-2048x250.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Git rebase<\/strong><\/h2>\n\n\n\n<p>As the <a href=\"https:\/\/git-scm.com\/docs\/git-rebase\" target=\"_blank\" rel=\"noopener\" title=\"\">git official docs<\/a> state: <strong><em>\u201cReapply commits on top of another base tip,\u201d <\/em><\/strong>this reapply will place all commits on the current branch on top of the target branch. Let&#8217;s take an example here:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh5.googleusercontent.com\/e5n2EFBjtME530S5EQKue9ScL8tgpUr073j0md82I8D0Z3y22MAfolbvA9Fvy2RQBXj88ajkp8pmegc-NJ0mdYILVbYZUYYNYKbvYDdDvEbRJLmDd8PGw3FQlPgeSewN2txsME3tDk_-cUCA2imvC9NK4QEP6zPQ0G1OCd2QLh6mkXjNp3M_3VMAWbuy4A\" alt=\"\"\/><\/figure>\n\n\n\n<p>Assume the following history exists and the current branch is a <code>feature<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>           A---B---C feature\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\n\n&nbsp;&nbsp;&nbsp;&nbsp;D---E---F---G main<\/code><\/pre>\n\n\n\n<p>From this point on, <code>feature<\/code> as the result of the following commands:<\/p>\n\n\n\n<p><code>git rebase main<\/code><\/p>\n\n\n\n<p>would be:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>                   A'--B'--C' feature\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\n\n&nbsp;&nbsp;&nbsp;&nbsp;D---E---F---G main<\/code><\/pre>\n\n\n\n<p>Now all the commits done on the feature branch are re-aligned on top of the new state of the main branch. During this process, all commits on the <code>feature<\/code> branch are rewritten and have new commit IDs. If there is any conflict in between the rebasing process, git will stop the process and ask you to resolve conflicts before proceeding.<\/p>\n\n\n\n<p>Here are the <a href=\"https:\/\/git-scm.com\/docs\/git-rebase#_options\" target=\"_blank\" rel=\"noopener\" title=\"\">options<\/a> available with the <code>git rebase<\/code> command.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Git merge<\/strong><\/h2>\n\n\n\n<p>According to the official docs: \u201cJoin two or more development histories together,\u201d it is a non-destructive operation that adds a forked stream on top of the target stream without rewriting commits.&nbsp;<\/p>\n\n\n\n<p>Here is what it will look like in our example:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh4.googleusercontent.com\/NmSbylwOBPlsEkHEJE4GEJZTFwY198aAg0YaCST80EM6KBesyxiya43tqZGlZyKz5uPCUY-e87s8TFZWyabtJkJ3VKY9GixUnajAdzStHjwqVhGvGdpSiz3aU-4sCStKbvhmMwzRMPXWZUS0tctlih66Deuyj192HQpEK_jy1V5fWNo9g35a7LSJP3jRxQ\" alt=\"\"\/><\/figure>\n\n\n\n<p>Assuming the <code>feature<\/code>&nbsp; branch is forked from the <code>main<\/code> branch, with the <code>main<\/code>&nbsp;having commits applied after the fork was created, the state of the repository will look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>           A---B---C feature\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\n\n&nbsp;&nbsp;&nbsp;&nbsp;D---E---F---G main<\/code><\/pre>\n\n\n\n<p>From this point on, the <code>feature<\/code>:<\/p>\n\n\n\n<p><code>git merge main<\/code><\/p>\n\n\n\n<p>Would result in the following output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>          A---B---C feature\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/ &nbsp; &nbsp; &nbsp; &nbsp; \\\n\n&nbsp;&nbsp;&nbsp;&nbsp;D---E---F---G---H main<\/code><\/pre>\n\n\n\n<p>Here, <code>H<\/code> is the new commit on top of the <code>feature<\/code> branch that has the full stream coming from <code>main<\/code>. <\/p>\n\n\n\n<p>A new commit will be created on the <code>feature<\/code> branch that might feel unnecessary. But, on the flip side, the commit history is intact and commits are not re-written. Git will ensure the <code>merge<\/code> is safe and will prompt in case of any conflicts just like the <code>rebase<\/code> command.<\/p>\n\n\n\n<p>This is what our feature branch will<a href=\"https:\/\/github.com\/zsajjad\/rebase-vs-merge\/commits\/feature\"> look like now<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"416\" src=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/01\/Screenshot-2023-01-13-at-10.44.16-AM-1024x416.png\" alt=\"\" class=\"wp-image-972\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/01\/Screenshot-2023-01-13-at-10.44.16-AM-1024x416.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/01\/Screenshot-2023-01-13-at-10.44.16-AM-300x122.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/01\/Screenshot-2023-01-13-at-10.44.16-AM-768x312.png 768w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/01\/Screenshot-2023-01-13-at-10.44.16-AM-1536x624.png 1536w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/01\/Screenshot-2023-01-13-at-10.44.16-AM-2048x832.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Differences<\/strong><\/h2>\n\n\n\n<p>Both commands as seen may offer similar results but differ in how they achieve them. These differences can have a significant impact on how your commit history looks in the long run. <\/p>\n\n\n\n<p>We have identified a few of the most important differences that can help you better understand both; Remember we aren\u2019t establishing any conclusions just yet, we will discuss pros, cons, and use cases later in the post.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Commit logs<\/strong><\/h2>\n\n\n\n<p>For <code>merge<\/code> it keeps the commit history intact with the merge commit showing on top, while <code>rebase<\/code> rewrites, it means the timestamp of actual commits is lost and new commit IDs are created.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Commit history<\/strong><\/h2>\n\n\n\n<p>On the other hand, when the <code>feature<\/code> branch is merged in <code>main<\/code>, it may create a single commit so the implementation details go away after <code>merge<\/code>. Rebase eliminates the extra merge commits and makes commit history linear with all commits of <code>feature<\/code> lined up together.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conflict resolution<\/strong><\/h2>\n\n\n\n<p>Both commands handle conflicts differently &#8211; <code>merge<\/code>&nbsp;being more focused on bringing the stream on top of the other will show conflicts at once, while <code>rebase<\/code> processes one commit at a time. Hence, it presents conflicts on each commit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Benefits<\/strong><\/h2>\n\n\n\n<p>Both commands in the discussion are super useful in their own capacity. Let&#8217;s dive into what each command offers as compared to each other.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Merge<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Simple &amp; easier to handle, it can be done via most git management UIs<\/li>\n\n\n\n<li>Timestamps of commits are maintained which might be necessary in some regulatory cases.<\/li>\n\n\n\n<li>Maintains all activity done on the branch as it occurred.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Rebase<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Streamlines a commit history for a feature allowing other devs to see how <code>feature<\/code> was implemented<\/li>\n\n\n\n<li>Avoids extra merge commits that to keep commit logs clean<\/li>\n\n\n\n<li>Possibility to truncate useless commits to make sure only significant changes are logged in git logs.&nbsp;<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Pitfalls<\/strong><\/h2>\n\n\n\n<p>Just like benefits, there are certain pitfalls associated with both commands, we have listed a few of them to help you avoid mistakes that can be costly in the future.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Merge<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u26a0\ufe0f Merging public branch to feature will make it ugly, commit history is an important tool for debugging. Having a linear commit history will help diagnose future issues.<\/li>\n\n\n\n<li>\u26a0\ufe0f Resolving conflicts with <code>merge<\/code> can be tricky as it brings all commits from the source at once.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Rebase<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li> \u26a0\ufe0f Never use <code>rebase<\/code>&nbsp; on a public branch, it rewrites the commit history and requires force push which should be prohibited on public branches.<\/li>\n\n\n\n<li>\u26a0\ufe0f You might be a fan of rebase due to its simplicity. But, keeping your entire history may be essential to meet regulatory and compliance requirements.<\/li>\n\n\n\n<li> \u26a0\ufe0f The learning curve for rebase can be steep for teams, but the fruits it bears are worth all the effort that goes into it.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Use cases<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Feature Branch Sync With Main [ Rebase \u2705 |  Merge \u26a0\ufe0f]<\/strong><\/h3>\n\n\n\n<p>This use case is the best fit for <code>rebase<\/code> command, as we have seen in the examples above, having a feature branch forked out of <code>main<\/code> has to be in sync with <code>main<\/code> before we merge it. <\/p>\n\n\n\n<p>In such cases <code>rebase<\/code> works best as it will bring all feature-based commits together on top of <code>main<\/code> resulting in a linear history of how <code>feature<\/code> was developed. With <code>merge<\/code>, it produces an extra commit on the <code>feature<\/code> branch and might be a little tough on resolving conflicts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Updating a public branch [ Rebase \u274c |  Merge \u2705 ]<\/strong><\/h3>\n\n\n\n<p>When your features are ready to be shipped into public branches (e.g develop, main, staging, etc.) it&#8217;s always the <code>merge<\/code> command that one should use. The <strong><em>golden rule <\/em><\/strong>for rebase says \u201c<strong><em>Never rebase public branches<\/em><\/strong>\u201d. <\/p>\n\n\n\n<p>Since <code>rebase<\/code> rewrites commit history it requires force push to sync the remote branch with a new commit history. It is highly recommended to keep your public branches protected and stop force push on these branches. For public branches, <code>rebase<\/code> is not appropriate.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Squash Intermediate Commits &#8211; Cleanup [ Rebase \u2705 |  Merge \u274c ]<\/strong><\/h3>\n\n\n\n<p>During fast-paced development, we tend to create many intermediary commits that must be removed before we push our code to remote. In such cases, only <code>rebase<\/code> with interactive mode is a solution that allows us to drop, squash, and reword commits in our stream.&nbsp;<\/p>\n\n\n\n<p><strong>Conclusion<\/strong><\/p>\n\n\n\n<p>In most scenarios, we use both of these super helpful commands in the same repositories. <\/p>\n\n\n\n<p>For most of the use cases each time a feature branch is ready to be shipped in the public branch, it is rebased on top of public branch and then merged (squashed or rebased) in the public branch. An in-depth understanding of both commands will allow you to leverage all the best they offer to make your commit history more clean and readable.<\/p>\n\n\n\n<p>I am always looking for more use cases to discuss, feel free to comment below. I&#8217;d love to have a healthy discussion around it! <img decoding=\"async\" width=\"31\" height=\"31\" src=\"https:\/\/lh3.googleusercontent.com\/dr-bHAAFrJ6B4gXuQQXNfV7KZRPFoPoY5WipsW0PSHzMpcw4VZD8QRt5u892KjrKwHdlr5D0oQ5I7QBZ2FEIvZEPC1eWrcSVyqDU6eJm7xS3PzQAshSGeziSNOutjId6hCIXcSOUacKwrlNTnbFWMMCDb4UsbH8bQ85ZDMYeAX2dfzaK18IjJCwcgmhJgg\" alt=\"thought balloon\">&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/www.aviator.co\/\" target=\"_blank\" rel=\"noreferrer noopener\">Aviator<\/a>: Automate your cumbersome merge processes<\/h2>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.aviator.co\/\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"727\" src=\"https:\/\/blog.aviator.co\/wp-content\/uploads\/2022\/08\/blog-cta-1024x727.png\" alt=\"\" class=\"wp-image-57\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2022\/08\/blog-cta-1024x727.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2022\/08\/blog-cta-300x213.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2022\/08\/blog-cta-768x545.png 768w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2022\/08\/blog-cta-1536x1090.png 1536w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2022\/08\/blog-cta-2048x1454.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Aviator automates tedious developer workflows by managing git Pull Requests (PRs) and continuous integration test (CI) runs to help your team avoid broken builds, streamline cumbersome merge processes, manage cross-PR dependencies, and handle flaky tests while maintaining their security compliance.<\/p>\n\n\n\n<p>There are 4 key components to Aviator:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>MergeQueue<\/strong>&nbsp;\u2013 an automated queue that manages the merging workflow for your GitHub repository to help protect important branches from broken builds. The Aviator bot uses GitHub Labels to identify Pull Requests (PRs) that are ready to be merged, validates CI checks, processes semantic conflicts, and merges the PRs automatically.<\/li>\n\n\n\n<li><strong>ChangeSets<\/strong>&nbsp;\u2013 workflows to synchronize validating and merging multiple PRs within the same repository or multiple repositories. Useful when your team often sees groups of related PRs that need to be merged together, or otherwise treated as a single broader unit of change.<\/li>\n\n\n\n<li><strong>FlakyBot<\/strong>&nbsp;\u2013 a tool to automatically detect, take action on, and process results from flaky tests in your CI infrastructure.<\/li>\n\n\n\n<li><strong>Stacked PRs CLI<\/strong>\u00a0\u2013 a command line tool that helps developers manage cross-PR dependencies. This tool also automates syncing and merging of stacked PRs. Useful when your team wants to promote a culture of smaller, incremental PRs instead of large changes, or when your workflows involve keeping multiple, dependent PRs in sync.<\/li>\n<\/ol>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A simple explanation of how merge and rebase work along with some use cases to help you choose the best approach.<\/p>\n","protected":false},"author":10,"featured_media":983,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[302],"tags":[29,28,27],"class_list":["post-625","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tooling-integrations"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":6}},"acf":[],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/01\/Screenshot-2023-01-13-at-11.13.11-AM.png","post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/posts\/625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/comments?post=625"}],"version-history":[{"count":23,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/posts\/625\/revisions"}],"predecessor-version":[{"id":3906,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/posts\/625\/revisions\/3906"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/media\/983"}],"wp:attachment":[{"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/media?parent=625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/categories?post=625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/tags?post=625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}