<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Merge on kenji.blog</title><link>http://kenji.blog/en/tags/merge/</link><description>Recent content in Merge on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/en/tags/merge/index.xml" rel="self" type="application/rss+xml"/><item><title>[Git Commands] The Difference Between rebase and merge, and How to Use Them Properly in Practice</title><link>http://kenji.blog/en/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/en/p/git-rebase-vs-merge-practical-guide/</guid><description>&lt;img src="http://kenji.blog/p/git-rebase-vs-merge-practical-guide/img/eyecatch.jpg" alt="Featured image of post [Git Commands] The Difference Between rebase and merge, and How to Use Them Properly in Practice" />&lt;h1 id="1-introduction-why-merge-vs-rebase-is-an-eternal-question">1. Introduction: Why &amp;ldquo;merge vs. rebase&amp;rdquo; is an Eternal Question
&lt;/h1>&lt;p>Git is an essential version control system in modern software development. When multiple developers make changes to a codebase simultaneously, Git&amp;rsquo;s powerful branching model proves its worth. However, in team development, the debate over &amp;ldquo;whether to use &lt;code>merge&lt;/code> or &lt;code>rebase&lt;/code>&amp;rdquo; is a topic that constantly troubles developers, from beginners to experts.&lt;/p>
&lt;p>In this article, we will delve deeply into the mechanical differences between &lt;code>git merge&lt;/code> and &lt;code>git rebase&lt;/code> by unraveling Git&amp;rsquo;s internal structure, such as the DAG (Directed Acyclic Graph) and the mathematical properties of commit hashes. Then, we will thoroughly explain how to use them properly in practice, incorporating specific workflows. By understanding not just the commands but also the calculations Git performs behind the scenes, you will eliminate the fear of conflicts and become able to build a clean, traceable history.&lt;/p>
&lt;hr>
&lt;h1 id="2-gits-internal-structure-commit-hashes-and-the-object-model">2. Git&amp;rsquo;s Internal Structure: Commit Hashes and the Object Model
&lt;/h1>&lt;p>To understand how Git integrates history, you first need to know how Git stores data. Git does not merely save the diffs (patches) of file changes; it saves a snapshot of the entire file system at a given point in time.&lt;/p>
&lt;h2 id="21-cryptographic-properties-of-commit-hashes">2.1 Cryptographic Properties of Commit Hashes
&lt;/h2>&lt;p>Each commit in Git is uniquely identified by a 40-character hexadecimal number generated by the SHA-1 (Secure Hash Algorithm 1) hash function, which is calculated based on its contents. A commit object consists of the following elements:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Pointer to a Tree object&lt;/strong>: A snapshot of the directory structure and files (Blobs) at that time&lt;/li>
&lt;li>&lt;strong>Pointers to parent commits&lt;/strong>: The hash values of one or more parent commits (the initial commit has no parents, while a merge commit has two or more parents)&lt;/li>
&lt;li>&lt;strong>Author information&lt;/strong>: The person who wrote the code and the date/time&lt;/li>
&lt;li>&lt;strong>Committer information&lt;/strong>: The person who created/applied the commit and the date/time&lt;/li>
&lt;li>&lt;strong>Commit message&lt;/strong>: Text explaining the intent of the changes&lt;/li>
&lt;/ol>
&lt;p>Expressed mathematically, the hash value $H(C)$ for a commit object $C$ is defined as follows:&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>Here, $\parallel$ represents the concatenation of data. Due to the characteristics of hash functions, even changing a single character in the commit message or having a different parent commit generates an entirely different hash value. In other words, &lt;strong>commits are immutable&lt;/strong>. The reason why &lt;code>rebase&lt;/code> (discussed later) is described as &amp;ldquo;rewriting history&amp;rdquo; is that it actually &amp;ldquo;creates new commits with similar content but different hash values.&amp;rdquo;&lt;/p>
&lt;p>The size of the hash space is $2^{160}$, and the probability $P$ of a collision (different commits having the same hash value) can be approximated using the theory of the Birthday Paradox as follows ($n$ is the number of commits):&lt;/p>
$$
P(\text{collision}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>This probability is extremely low, and practically speaking, it is almost impossible for Git commit hashes to collide.&lt;/p>
&lt;hr>
&lt;h1 id="3-graph-theory-and-dag-the-mathematical-model-of-git-history">3. Graph Theory and DAG: The Mathematical Model of Git History
&lt;/h1>&lt;p>Git&amp;rsquo;s commit history is modeled as a &amp;ldquo;Directed Acyclic Graph (DAG)&amp;rdquo; in graph theory.&lt;/p>
&lt;h2 id="31-what-is-a-dag-directed-acyclic-graph">3.1 What is a DAG (Directed Acyclic Graph)?
&lt;/h2>&lt;p>In a graph $G = (V, E)$, $V$ is a set of commits (vertices), and $E$ is a set of directed edges indicating the parent-child relationship between commits. In Git, the direction of the edges points from the &amp;ldquo;child commit to the parent commit.&amp;rdquo; This is because new commits hold a pointer to past commits.&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;Commit A (Initial)&amp;#34;]
B[&amp;#34;Commit B&amp;#34;]
C[&amp;#34;Commit C (Main)&amp;#34;]
D[&amp;#34;Commit D (Feature)&amp;#34;]
E[&amp;#34;Commit E (Merge)&amp;#34;]
B --&amp;gt; A
C --&amp;gt; B
D --&amp;gt; B
E --&amp;gt; C
E --&amp;gt; D
&lt;/pre>
&lt;p>The most significant feature of a DAG is that &amp;ldquo;there are no cycles.&amp;rdquo; This ensures that the algorithm tracing the commit history never falls into an infinite loop and can reliably reach the end (the initial commit).&lt;/p>
&lt;h2 id="32-topological-sort-and-the-order-of-history">3.2 Topological Sort and the Order of History
&lt;/h2>&lt;p>When displaying history with commands like &lt;code>git log&lt;/code>, the DAG is ordered as a one-dimensional list using a Topological Sort algorithm. For any directed edge $u \to v$ in the DAG ($u$ is a child of $v$), it rearranges them so that $u$ comes before $v$ in the list.&lt;/p>
&lt;hr>
&lt;h1 id="4-the-mechanisms-and-types-of-git-merge">4. The Mechanisms and Types of git merge
&lt;/h1>&lt;p>The most basic command to integrate changes from a branch is &lt;code>git merge&lt;/code>. However, Git automatically selects different merge strategies depending on the current state.&lt;/p>
&lt;h2 id="41-fast-forward-merge---ff">4.1 Fast-Forward Merge (&amp;ndash;ff)
&lt;/h2>&lt;p>If the branch you are merging into (e.g., &lt;code>main&lt;/code>) is a direct ancestor of the branch you are merging from (e.g., &lt;code>feature&lt;/code>), Git performs a &amp;ldquo;Fast-Forward&amp;rdquo; merge. This is an operation that simply moves the branch pointer forward without creating a new commit.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
merge feature
&lt;/pre>
&lt;p>A Fast-Forward merge keeps the history straight, but it has the disadvantage of losing the context of &amp;ldquo;which group of commits were grouped together as a single feature development (feature).&amp;rdquo;&lt;/p>
&lt;h2 id="42-non-fast-forward-merge---no-ff">4.2 Non-Fast-Forward Merge (&amp;ndash;no-ff)
&lt;/h2>&lt;p>If you explicitly specify &lt;code>git merge --no-ff&lt;/code>, it will always create a new &amp;ldquo;merge commit,&amp;rdquo; even in a situation where a Fast-Forward is possible. A merge commit is a special commit that has two parents.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
commit id: &amp;#34;Main Work 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>The advantage of this method is that the existence and history of the feature branch clearly remain on the DAG. If an issue occurs, you can safely undo (revert) the entire feature at once by running &lt;code>git revert -m 1 &amp;lt;merge commit hash&amp;gt;&lt;/code>.&lt;/p>
&lt;h2 id="43-3-way-merge-algorithm">4.3 3-Way Merge Algorithm
&lt;/h2>&lt;p>When both the target and source branches have their own unique commits, Git performs a 3-way merge. At this time, Git traverses the DAG and finds the &amp;ldquo;Lowest Common Ancestor (LCA)&amp;rdquo; of the two branches.&lt;/p>
&lt;p>The time complexity $T_{\text{LCA}}$ of the algorithm to find the LCA can be executed in linear time relative to the number of vertices $|V|$ and edges $|E|$:&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>Git compares three states: the &amp;ldquo;state of the LCA,&amp;rdquo; the &amp;ldquo;state of the current branch,&amp;rdquo; and the &amp;ldquo;state of the other branch.&amp;rdquo; If the changes do not conflict, it automatically generates a merge commit.&lt;/p>
&lt;hr>
&lt;h1 id="5-the-mechanism-of-git-rebase-and-reconstructing-history">5. The Mechanism of git rebase and Reconstructing History
&lt;/h1>&lt;p>While &lt;code>git merge&lt;/code> &amp;ldquo;integrates&amp;rdquo; history, &lt;code>git rebase&lt;/code> &amp;ldquo;reconstructs (re-attaches)&amp;rdquo; history.&lt;/p>
&lt;h2 id="51-the-movements-behind-rebase">5.1 The Movements Behind Rebase
&lt;/h2>&lt;p>The internal operation when rebasing the &lt;code>feature&lt;/code> branch onto the &lt;code>main&lt;/code> branch (&lt;code>git rebase main&lt;/code>) is as follows:&lt;/p>
&lt;ol>
&lt;li>Find the lowest common ancestor (LCA) of the &lt;code>feature&lt;/code> branch and the &lt;code>main&lt;/code> branch.&lt;/li>
&lt;li>Save the diffs of the commits from the LCA to the tip of the &lt;code>feature&lt;/code> branch in a temporary area.&lt;/li>
&lt;li>Move the pointer of the &lt;code>feature&lt;/code> branch to the tip of the &lt;code>main&lt;/code> branch.&lt;/li>
&lt;li>Apply the saved diffs one by one sequentially onto the new base (the tip of &lt;code>main&lt;/code>) (Cherry-Pick) to generate new commits.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Commit A&amp;#34;] --&amp;gt; B[&amp;#34;Commit B&amp;#34;]
B --&amp;gt; C[&amp;#34;Commit C (Main)&amp;#34;]
B --&amp;gt; D[&amp;#34;Commit D (Old Feature)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;Commit D&amp;#39; (New Feature)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>What&amp;rsquo;s important here is that the commit $D'$ generated by the rebase &lt;strong>has a completely different hash value because its parent commit is different&lt;/strong> from the original commit $D$ (refer to the definition of the hash function $H(C)$ mentioned above).&lt;/p>
&lt;h2 id="52-interactive-rebase">5.2 Interactive Rebase
&lt;/h2>&lt;p>Using &lt;code>git rebase -i&lt;/code> (or &lt;code>--interactive&lt;/code>), you can manipulate the commit history as you wish. This is the most powerful tool for organizing local history.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code>: Use the commit as is&lt;/li>
&lt;li>&lt;code>reword&lt;/code>: Modify only the commit message&lt;/li>
&lt;li>&lt;code>edit&lt;/code>: Pause to amend the contents of the commit&lt;/li>
&lt;li>&lt;code>squash&lt;/code>: Meld this commit into the previous commit, combining their messages&lt;/li>
&lt;li>&lt;code>fixup&lt;/code>: Same as &lt;code>squash&lt;/code>, but discards the message of this commit&lt;/li>
&lt;li>&lt;code>drop&lt;/code>: Completely remove the commit&lt;/li>
&lt;/ul>
&lt;p>Mathematically speaking, if a branch has $N$ commits, the permutations (variations of linear history) $P$ that can be generated by reordering during a rebase are as follows:&lt;/p>
$$
P = N!
$$&lt;p>Git gives developers the freedom of $N!$ ways, allowing them to keep the history in a logical and beautiful state.&lt;/p>
&lt;hr>
&lt;h1 id="6-the-golden-rule-of-rebase">6. The Golden Rule of Rebase
&lt;/h1>&lt;p>&lt;code>rebase&lt;/code> is incredibly powerful, but there is one absolute rule.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;ldquo;Never rebase public history&amp;rdquo;&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-why-should-you-not-rebase-public-history">6.1 Why should you not rebase public history?
&lt;/h2>&lt;p>Git is distributed. The commits you pushed to &lt;code>origin/main&lt;/code> are also cloned to the local repositories of other developers. If you rewrite the history by rebasing commits that have already been pushed, and force-overwrite them with &lt;code>git push --force&lt;/code>, what will happen?&lt;/p>
&lt;p>The DAGs on other developers&amp;rsquo; local machines and the remote DAG will fundamentally diverge. When other developers run &lt;code>git pull&lt;/code>, Git will forcefully try to merge the groups of commits with different histories, causing a massive amount of conflicts and duplicate commits (commits with the same changes but different hashes), and the repository will fall into a state of panic.&lt;/p>
&lt;p>The ironclad rule is to perform rebases &lt;strong>&amp;ldquo;only on local branches that you haven&amp;rsquo;t shared with anyone yet.&amp;rdquo;&lt;/strong>&lt;/p>
&lt;hr>
&lt;h1 id="7-resolving-conflicts-and-git-rebase---continue">7. Resolving Conflicts and git rebase &amp;ndash;continue
&lt;/h1>&lt;p>When multiple people change the same part of the same file, a conflict occurs. The process of resolving conflicts differs between &lt;code>merge&lt;/code> and &lt;code>rebase&lt;/code>.&lt;/p>
&lt;h2 id="71-conflict-resolution-in-merge">7.1 Conflict Resolution in Merge
&lt;/h2>&lt;p>In the case of &lt;code>git merge&lt;/code>, conflict resolution happens &lt;strong>only once&lt;/strong>. You fix all the conflicting parts at once right before creating the final merge commit.&lt;/p>
&lt;h2 id="72-conflict-resolution-in-rebase">7.2 Conflict Resolution in Rebase
&lt;/h2>&lt;p>In the case of &lt;code>git rebase&lt;/code>, because commits are re-applied one by one, &lt;strong>conflicts can occur at each commit&lt;/strong>.&lt;/p>
&lt;p>When a conflict occurs during a rebase, Git pauses the process. The flow of resolution is as follows:&lt;/p>
&lt;ol>
&lt;li>Open your editor or IDE (like VS Code) and manually fix the conflict markers (&lt;code>&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code>, &lt;code>======&lt;/code>, &lt;code>&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code>).&lt;/li>
&lt;li>Add the modified files to the index:
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git add &amp;lt;modified_file&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>Do not create a commit, just resume the rebase process:
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --continue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>If you want to cancel the rebase itself and return to the original state, run the following command:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --abort
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>(* If you don&amp;rsquo;t need to resolve the conflict and want to skip that entire commit, use &lt;code>git rebase --skip&lt;/code>)&lt;/p>
&lt;hr>
&lt;h1 id="8-proper-usage-in-practice-workflow-practice">8. Proper Usage in Practice (Workflow Practice)
&lt;/h1>&lt;p>So, how should you properly use &lt;code>merge&lt;/code> and &lt;code>rebase&lt;/code> in an actual development environment? Here we introduce the most standard and safe approach.&lt;/p>
&lt;h2 id="81-scenario-1-organizing-local-working-history-using-rebase">8.1 [Scenario 1] Organizing Local Working History (Using Rebase)
&lt;/h2>&lt;p>Suppose you are developing on a feature branch, and many minor commits (&amp;ldquo;typo fix&amp;rdquo;, &amp;ldquo;temporary save&amp;rdquo;, etc.) have piled up. Before submitting a Pull Request (PR), you use interactive rebase to organize these into meaningful units.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Execute while on the feature branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase -i HEAD~5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># (The editor opens, and you clean up the history using squash and fixup)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>By doing this, you can create a beautiful commit history whose intent is easy for reviewers to understand.&lt;/p>
&lt;h2 id="82-scenario-2-catching-up-with-the-latest-main-branch-using-rebase">8.2 [Scenario 2] Catching Up with the Latest main Branch (Using Rebase)
&lt;/h2>&lt;p>If development drags on and other people&amp;rsquo;s changes keep getting merged into the &lt;code>main&lt;/code> branch, your &lt;code>feature&lt;/code> branch will become outdated. In this case, you rebase your &lt;code>feature&lt;/code> branch onto the latest &lt;code>main&lt;/code> to catch up.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Fetch the latest information from main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git fetch origin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Rebase the feature branch onto the latest main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase origin/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This keeps the history linear and prevents conflicts during subsequent merges. It also prevents the generation of unnecessary merge commits (&amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;).&lt;/p>
&lt;h2 id="83-scenario-3-integrating-completed-features-using-merge">8.3 [Scenario 3] Integrating Completed Features (Using Merge)
&lt;/h2>&lt;p>Development on the &lt;code>feature&lt;/code> branch is complete, and it is finally the phase to integrate it into the &lt;code>main&lt;/code> branch. Here, we use &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong> (This is the same as choosing &amp;ldquo;Create a merge commit&amp;rdquo; in a Pull Request on GitHub, etc.).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git merge --no-ff feature -m &lt;span class="s2">&amp;#34;Merge feature: Implement user login functionality&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>By doing this, a node in history (a merge commit) saying &amp;ldquo;a single feature was merged here&amp;rdquo; is left on the DAG of the &lt;code>main&lt;/code> branch. When looking back at the history later, it becomes easier to trace the code by feature units.&lt;/p>
&lt;hr>
&lt;h1 id="9-conclusion-summary">9. Conclusion (Summary)
&lt;/h1>&lt;p>In Git operations, extreme approaches like &amp;ldquo;doing everything with Merge&amp;rdquo; or &amp;ldquo;making everything linear with Rebase&amp;rdquo; each have their pros and cons.&lt;/p>
&lt;p>The best practice in real-world environments is a hybrid approach: &lt;strong>&amp;ldquo;Cleanly organize local private history with rebase, and preserve context in public integration history with merge &amp;ndash;no-ff.&amp;rdquo;&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Local (Personal workspace)&lt;/strong>: Use &lt;code>rebase&lt;/code> to eliminate useless commits, catch up with the latest mainline, and maintain a linear history.&lt;/li>
&lt;li>&lt;strong>Global (Shared workspace)&lt;/strong>: Use &lt;code>merge --no-ff&lt;/code> to record the existence of a feature branch as a merge commit on the DAG, making reverts and tracking easier.&lt;/li>
&lt;/ul>
&lt;p>By understanding the mathematical and architectural backgrounds, such as the structure of the DAG and how hash functions work, Git commands are elevated from mere memorization to &amp;ldquo;designing history with intent.&amp;rdquo; While adhering to the golden rule of rebase, let&amp;rsquo;s select the optimal commands depending on the situation and build a clean commit history that is easy to read and maintain for the entire team.&lt;/p></description></item></channel></rss>