<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rebase on kenji.blog</title><link>http://kenji.blog/zh-tw/tags/rebase/</link><description>Recent content in Rebase on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-tw</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-tw/tags/rebase/index.xml" rel="self" type="application/rss+xml"/><item><title>【Git 指令】rebase 與 merge 的差異與實務上的正確使用時機</title><link>http://kenji.blog/zh-tw/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/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 指令】rebase 與 merge 的差異與實務上的正確使用時機" />&lt;h1 id="1-前言為什麼要用-merge-還是-rebase是個永遠的課題">1. 前言：為什麼「要用 merge 還是 rebase」是個永遠的課題
&lt;/h1>&lt;p>Git 是現代軟體開發中不可或缺的版本控制系統。當多位開發者同時修改程式碼庫時，Git 強大的分支模型便能發揮其威力。然而，在團隊開發中，「該使用 &lt;code>merge&lt;/code> 還是 &lt;code>rebase&lt;/code>」的爭論，是從初學者到老手都經常煩惱的議題之一。&lt;/p>
&lt;p>本文將從 Git 的內部結構：DAG（有向無環圖）以及提交（Commit）雜湊（Hash）的數學性質出發，深入探討 &lt;code>git merge&lt;/code> 與 &lt;code>git rebase&lt;/code> 的機制差異。接著，將配合具體的工作流程，徹底解說在實務上該如何正確區分使用它們。這不僅僅是介紹指令，透過理解 Git 在背後進行了哪些計算，你將不再對衝突（Conflict）感到恐懼，並能建立出乾淨且可追蹤的歷史紀錄。&lt;/p>
&lt;hr>
&lt;h1 id="2-git-的內部結構提交雜湊與物件模型">2. Git 的內部結構：提交雜湊與物件模型
&lt;/h1>&lt;p>為了理解 Git 是如何整合歷史紀錄的，我們必須先了解 Git 是如何儲存資料的。Git 並不只是儲存檔案修改的差異（Patch），而是儲存特定時間點整個檔案系統的快照（Snapshot）。&lt;/p>
&lt;h2 id="21-提交雜湊的密碼學性質">2.1 提交雜湊的密碼學性質
&lt;/h2>&lt;p>Git 的每一個提交，都是透過 SHA-1（Secure Hash Algorithm 1）雜湊函數，根據其內容計算出一組 40 個字元的十六進位數字來唯一識別。提交物件（Commit Object）由以下元素組成：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>指向 Tree 物件的指標&lt;/strong>：該時間點的目錄結構與檔案（Blob）的快照&lt;/li>
&lt;li>&lt;strong>指向父提交的指標&lt;/strong>：一個或多個父提交的雜湊值（首次提交沒有父提交，而合併提交則有兩個以上的父提交）&lt;/li>
&lt;li>&lt;strong>作者資訊（Author）&lt;/strong>：撰寫程式碼的人與時間&lt;/li>
&lt;li>&lt;strong>提交者資訊（Committer）&lt;/strong>：建立與套用提交的人與時間&lt;/li>
&lt;li>&lt;strong>提交訊息（Commit Message）&lt;/strong>：說明修改意圖的文字&lt;/li>
&lt;/ol>
&lt;p>用數學來表示，對於提交物件 $C$，其雜湊值 $H(C)$ 定義如下：&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>這裡的 $\parallel$ 代表資料的串接。由於雜湊函數的特性，即使只改變了提交訊息中的一個字元，或是父提交不同，都會產生完全不同的雜湊值。也就是說，&lt;strong>提交是不可變的（Immutable）&lt;/strong>。後文將提到 &lt;code>rebase&lt;/code> 常被說成是「改寫歷史紀錄」，實際上它是「建立內容相似但雜湊值不同的新提交」。&lt;/p>
&lt;p>雜湊空間的大小為 $2^{160}$，利用生日攻擊（Birthday Paradox）的理論，可以近似計算出發生碰撞（Collision，即不同提交擁有相同雜湊值）的機率 $P$（$n$ 為提交數量）：&lt;/p>
$$
P(\text{collision}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>這個機率極低，在實務上 Git 的提交雜湊幾乎不可能發生碰撞。&lt;/p>
&lt;hr>
&lt;h1 id="3-圖論與-daggit-歷史的數學模型">3. 圖論與 DAG：Git 歷史的數學模型
&lt;/h1>&lt;p>Git 的提交歷史，在圖論中被模型化為「有向無環圖（Directed Acyclic Graph, DAG）」。&lt;/p>
&lt;h2 id="31-什麼是-dag有向無環圖">3.1 什麼是 DAG（有向無環圖）
&lt;/h2>&lt;p>在圖 $G = (V, E)$ 中，$V$ 是提交的集合（頂點），$E$ 是表示提交之間父子關係的有向邊集合。在 Git 中，邊的方向是「從子提交指向父提交」。這是因為新的提交會保存指向過去提交的指標。&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;Commit A (初始)&amp;#34;]
B[&amp;#34;Commit B&amp;#34;]
C[&amp;#34;Commit C (主分支)&amp;#34;]
D[&amp;#34;Commit D (功能分支)&amp;#34;]
E[&amp;#34;Commit E (合併)&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>DAG 最大的特徵是「不存在循環（Cycle）」。因此，追溯提交歷史的演算法不會陷入無限迴圈，並且能確實到達終點（初始提交）。&lt;/p>
&lt;h2 id="32-拓撲排序與歷史順序">3.2 拓撲排序與歷史順序
&lt;/h2>&lt;p>當我們使用 &lt;code>git log&lt;/code> 等指令顯示歷史紀錄時，DAG 會透過拓撲排序（Topological Sort）演算法被排序為一維的列表。對於 DAG 中任意的有向邊 $u \to v$（$u$ 為 $v$ 的子節點），在列表中 $u$ 會被排在 $v$ 的前面。&lt;/p>
&lt;hr>
&lt;h1 id="4-git-merge-的機制與種類">4. git merge 的機制與種類
&lt;/h1>&lt;p>整合分支變更最基本的指令就是 &lt;code>git merge&lt;/code>。然而，根據目前的狀態，Git 會自動選擇不同的合併策略。&lt;/p>
&lt;h2 id="41-快轉合併fast-forward---ff">4.1 快轉合併（Fast-Forward, &amp;ndash;ff）
&lt;/h2>&lt;p>如果整合目標（例如：&lt;code>main&lt;/code>）分支是整合來源（例如：&lt;code>feature&lt;/code>）分支的直接祖先，Git 就會執行「快轉（Fast-Forward）」合併。這是一個不會建立新提交，只是單純將分支指標向前推進的操作。&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>快轉合併能讓歷史紀錄保持一直線，但缺點是會失去「哪些提交是屬於同一個功能開發（feature）」的上下文資訊。&lt;/p>
&lt;h2 id="42-非快轉合併non-fast-forward---no-ff">4.2 非快轉合併（Non-Fast-Forward, &amp;ndash;no-ff）
&lt;/h2>&lt;p>如果明確指定 &lt;code>git merge --no-ff&lt;/code>，即使在可以快轉合併的情況下，也必定會建立一個新的「合併提交（Merge 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
commit id: &amp;#34;Main 工作 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>這種方法的優點是，功能分支的存在與歷史會明確地保留在 DAG 上。當發生問題時，只要執行 &lt;code>git revert -m 1 &amp;lt;合併提交的雜湊&amp;gt;&lt;/code>，就可以安全地一次將整個功能取消（Revert）。&lt;/p>
&lt;h2 id="43-三方合併3-way-merge演算法">4.3 三方合併（3-Way Merge）演算法
&lt;/h2>&lt;p>當整合目標與整合來源的分支各自擁有獨立的提交時，Git 會執行三方合併。此時，Git 會探索 DAG，並找出兩個分支的「共同祖先（Lowest Common Ancestor, LCA）」。&lt;/p>
&lt;p>找出 LCA 的演算法時間複雜度 $T_{\text{LCA}}$，相對於頂點數 $|V|$ 與邊數 $|E|$，可以在線性時間內執行完成：&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>Git 會比較「LCA 的狀態」、「目前分支的狀態」與「對方分支的狀態」這三者，若變更沒有衝突，就會自動產生合併提交。&lt;/p>
&lt;hr>
&lt;h1 id="5-git-rebase-的機制與歷史的重建">5. git rebase 的機制與歷史的重建
&lt;/h1>&lt;p>相較於 &lt;code>git merge&lt;/code> 是將歷史「整合」，&lt;code>git rebase&lt;/code> 則是將歷史「重建（移花接木）」。&lt;/p>
&lt;h2 id="51-rebase-背後的運作方式">5.1 Rebase 背後的運作方式
&lt;/h2>&lt;p>將 &lt;code>feature&lt;/code> 分支變基到 &lt;code>main&lt;/code> 分支（&lt;code>git rebase main&lt;/code>）時，內部的運作方式如下：&lt;/p>
&lt;ol>
&lt;li>找出 &lt;code>feature&lt;/code> 分支與 &lt;code>main&lt;/code> 分支的共同祖先（LCA）。&lt;/li>
&lt;li>將從 LCA 到 &lt;code>feature&lt;/code> 分支最前端的提交差異，儲存到暫存區域。&lt;/li>
&lt;li>將 &lt;code>feature&lt;/code> 分支的指標移動到 &lt;code>main&lt;/code> 分支的最前端。&lt;/li>
&lt;li>將儲存的差異，依序一個個套用（Cherry-Pick）到新的基底（&lt;code>main&lt;/code> 的最前端）上，並產生新的提交。&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 (主分支)&amp;#34;]
B --&amp;gt; D[&amp;#34;Commit D (舊功能分支)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;Commit D&amp;#39; (新功能分支)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>這裡重要的是，透過 rebase 產生的提交 $D'$，&lt;strong>因為父提交與原本的提交 $D$ 不同，所以會有完全不同的雜湊值&lt;/strong>（請參考前面提到的雜湊函數定義 $H(C)$）。&lt;/p>
&lt;h2 id="52-互動式變基interactive-rebase">5.2 互動式變基（Interactive Rebase）
&lt;/h2>&lt;p>使用 &lt;code>git rebase -i&lt;/code>（或 &lt;code>--interactive&lt;/code>），可以隨心所欲地操作提交歷史。這是用來整理本地端歷史紀錄最強大的工具。&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code>：直接採用該提交&lt;/li>
&lt;li>&lt;code>reword&lt;/code>：只修改提交訊息&lt;/li>
&lt;li>&lt;code>edit&lt;/code>：暫停以修改提交內容&lt;/li>
&lt;li>&lt;code>squash&lt;/code>：將此提交與前一個提交融合，並合併兩者的訊息&lt;/li>
&lt;li>&lt;code>fixup&lt;/code>：與 &lt;code>squash&lt;/code> 相同，但會捨棄此提交的訊息&lt;/li>
&lt;li>&lt;code>drop&lt;/code>：完全刪除該提交&lt;/li>
&lt;/ul>
&lt;p>從數學的角度來看，如果一個分支上有 $N$ 個提交，透過 rebase 改變順序所能產生的線性歷史排列組合（排列）$P$ 如下：&lt;/p>
$$
P = N!
$$&lt;p>Git 給予了開發者 $N!$ 種自由，使其能夠保持歷史紀錄在合乎邏輯且優美的狀態。&lt;/p>
&lt;hr>
&lt;h1 id="6-rebase-的黃金法則the-golden-rule-of-rebase">6. Rebase 的黃金法則（The Golden Rule of Rebase）
&lt;/h1>&lt;p>&lt;code>rebase&lt;/code> 雖然非常強大，但存在一個絕對的規則。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>「絕對不要對已經公開的歷史紀錄進行 rebase」&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-為什麼不能對公開的歷史紀錄進行-rebase">6.1 為什麼不能對公開的歷史紀錄進行 rebase？
&lt;/h2>&lt;p>Git 是分散式的。你推送到 &lt;code>origin/main&lt;/code> 的提交，也會被複製（Clone）到其他開發者的本地端儲存庫中。如果你對已經推送的提交進行 rebase 來改寫歷史，並且使用 &lt;code>git push --force&lt;/code> 強制覆蓋，會發生什麼事呢？&lt;/p>
&lt;p>其他開發者本地端的 DAG 會與遠端的 DAG 產生根本性的分歧。當其他開發者執行 &lt;code>git pull&lt;/code> 時，Git 會試圖強制合併擁有不同歷史的提交群，導致產生大量衝突或重複的提交（內容相同但雜湊不同的提交），讓儲存庫陷入混亂狀態。&lt;/p>
&lt;p>Rebase 的鐵則就是**只能對「尚未與任何人共享的本地端分支」**執行。&lt;/p>
&lt;hr>
&lt;h1 id="7-解決衝突conflict與-git-rebase---continue">7. 解決衝突（Conflict）與 git rebase &amp;ndash;continue
&lt;/h1>&lt;p>當多個人修改了同一個檔案的同一個地方時，就會發生衝突。&lt;code>merge&lt;/code> 與 &lt;code>rebase&lt;/code> 在解決衝突的過程上有所不同。&lt;/p>
&lt;h2 id="71-merge-中的衝突解決">7.1 Merge 中的衝突解決
&lt;/h2>&lt;p>在使用 &lt;code>git merge&lt;/code> 的情況下，衝突解決&lt;strong>只會發生一次&lt;/strong>。在建立最終的合併提交之前，會一次性修正所有衝突的地方。&lt;/p>
&lt;h2 id="72-rebase-中的衝突解決">7.2 Rebase 中的衝突解決
&lt;/h2>&lt;p>在使用 &lt;code>git rebase&lt;/code> 的情況下，由於是將提交一個一個重新套用，因此&lt;strong>每個提交都有可能發生衝突&lt;/strong>。&lt;/p>
&lt;p>在 rebase 過程中若發生衝突，Git 會暫停處理。解決的流程如下：&lt;/p>
&lt;ol>
&lt;li>開啟編輯器或 IDE（例如 VS Code），手動修正衝突標記（&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>將修正好的檔案加入索引：
&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;修正的檔案&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>不建立提交，直接恢復 rebase 處理：
&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>如果是想要取消 rebase 本身並恢復到原本的狀態，則執行以下指令：&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>（※如果不需要解決衝突，想直接略過該提交，則使用 &lt;code>git rebase --skip&lt;/code>）&lt;/p>
&lt;hr>
&lt;h1 id="8-實務上的正確使用時機工作流程實踐">8. 實務上的正確使用時機（工作流程實踐）
&lt;/h1>&lt;p>那麼，在實際的開發現場中，應該如何區分使用 &lt;code>merge&lt;/code> 和 &lt;code>rebase&lt;/code> 呢？這裡介紹最標準且安全的做法。&lt;/p>
&lt;h2 id="81-情境-1整理本地端的工作歷史使用-rebase">8.1 【情境 1】整理本地端的工作歷史（使用 Rebase）
&lt;/h2>&lt;p>假設在功能分支開發中，累積了許多細碎的提交（例如「修正拼寫錯誤」、「暫存」等）。在發出發布請求（Pull Request, PR）之前，為了將這些提交整理成有意義的單位，可以使用互動式變基。&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"># 在 feature 分支的狀態下執行&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"># （編輯器會開啟，使用 squash 或 fixup 來讓歷史變得乾淨）&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>這樣一來，就能建立出讓審查者更容易理解意圖、優美的提交歷史。&lt;/p>
&lt;h2 id="82-情境-2跟上最新的-main-分支使用-rebase">8.2 【情境 2】跟上最新的 main 分支（使用 Rebase）
&lt;/h2>&lt;p>當開發時間拉長，&lt;code>main&lt;/code> 分支不斷被合併了其他人的變更，自己的 &lt;code>feature&lt;/code> 分支就會變舊。在這種情況下，將 &lt;code>feature&lt;/code> 分支 rebase 到最新的 &lt;code>main&lt;/code> 上以保持同步。&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"># 取得 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"># 將 feature 分支重新接到最新的 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>這樣可以讓歷史保持一直線，並防止後續合併時發生衝突。同時也能避免產生多餘的合併提交（&amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;）。&lt;/p>
&lt;h2 id="83-情境-3整合完成的功能使用-merge">8.3 【情境 3】整合完成的功能（使用 Merge）
&lt;/h2>&lt;p>當 &lt;code>feature&lt;/code> 分支上的開發完成，終於要整合到 &lt;code>main&lt;/code> 分支的階段時。這裡要使用 &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong>（這等同於在 GitHub 等的 Pull Request 中選擇「Create a merge commit」）。&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: 實作使用者登入功能&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>如此一來，&lt;code>main&lt;/code> 分支的 DAG 上就會留下「在這裡合併了一個功能」的歷史節點（合併提交）。日後回顧歷史時，就能以功能為單位輕鬆追蹤程式碼。&lt;/p>
&lt;hr>
&lt;h1 id="9-結論總結">9. 結論（總結）
&lt;/h1>&lt;p>在 Git 的操作中，「全部都用 Merge 解決」或是「全部都用 Rebase 弄成一直線」這類極端的做法，各自都有其優缺點。&lt;/p>
&lt;p>實務上的最佳實踐是**「本地端的私人歷史用 rebase 優美地整理，公開的整合歷史用 merge &amp;ndash;no-ff 保留上下文」**這種混合式的方法。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>本地端（個人的工作空間）&lt;/strong>：使用 &lt;code>rebase&lt;/code> 排除無謂的提交，並跟上最新的主線，維持一直線的歷史。&lt;/li>
&lt;li>&lt;strong>全域（共享的工作空間）&lt;/strong>：使用 &lt;code>merge --no-ff&lt;/code> 將功能分支的存在作為合併提交記錄在 DAG 上，讓 revert 或追蹤變得更容易。&lt;/li>
&lt;/ul>
&lt;p>透過理解 DAG 的結構與雜湊函數機制的數學及架構背景，Git 指令就不再只是單純的死記硬背，而是昇華為「有目的的歷史紀錄設計」。請遵守 rebase 的黃金法則，並根據情況選擇最合適的指令，建立出對整個團隊來說易讀且易於維護的乾淨提交歷史吧。&lt;/p></description></item></channel></rss>