<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Git on TripleZ&#39;s Blog</title>
    <link>https://blog.triplez.cn/tags/git/</link>
    <description>Recent content in Git on TripleZ&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-cn</language>
    <lastBuildDate>Fri, 08 Apr 2022 00:33:11 +0800</lastBuildDate><atom:link href="https://blog.triplez.cn/tags/git/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Mastering Git Cherry-pick</title>
      <link>https://blog.triplez.cn/posts/mastering-git-cherry-pick/</link>
      <pubDate>Fri, 08 Apr 2022 00:33:11 +0800</pubDate>
      
      <guid>https://blog.triplez.cn/posts/mastering-git-cherry-pick/</guid>
      <description>&lt;p&gt;本文希望教你如何成为一个 &lt;code&gt;git cherry-pick&lt;/code&gt; 的 “master”！通过使用 &lt;code&gt;git cherry-pick&lt;/code&gt; 来轻松地维护多个分支版本，再也不会让 multi-version maintaining  成为你心头上的那把令你屡次痛心的剑了！&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>本文希望教你如何成为一个 <code>git cherry-pick</code> 的 “master”！通过使用 <code>git cherry-pick</code> 来轻松地维护多个分支版本，再也不会让 multi-version maintaining  成为你心头上的那把令你屡次痛心的剑了！</p>
<p>本文所有内容都会基于以下（精心构造的）示例，该例子涵盖了大部分工程上容易出现的 Git log pattern（如有其他 corner-case ，欢迎<a href="mailto:me@triplez.cn">联系我</a>，一起努力让该文变得对大家更有帮助）。</p>
<p>贯穿本文的示例场景，这是该示例基于时间序的 Git 提交历史。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1"> 1</a>
</span><span class="lnt" id="hl-0-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-2"> 2</a>
</span><span class="lnt" id="hl-0-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-3"> 3</a>
</span><span class="lnt" id="hl-0-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-4"> 4</a>
</span><span class="lnt" id="hl-0-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-5"> 5</a>
</span><span class="lnt" id="hl-0-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-6"> 6</a>
</span><span class="lnt" id="hl-0-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-7"> 7</a>
</span><span class="lnt" id="hl-0-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-8"> 8</a>
</span><span class="lnt" id="hl-0-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-9"> 9</a>
</span><span class="lnt" id="hl-0-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-10">10</a>
</span><span class="lnt" id="hl-0-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-11">11</a>
</span><span class="lnt" id="hl-0-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-12">12</a>
</span><span class="lnt" id="hl-0-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-13">13</a>
</span><span class="lnt" id="hl-0-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-14">14</a>
</span><span class="lnt" id="hl-0-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-15">15</a>
</span><span class="lnt" id="hl-0-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-16">16</a>
</span><span class="lnt" id="hl-0-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-17">17</a>
</span><span class="lnt" id="hl-0-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-18">18</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order
</span></span><span class="line"><span class="cl">* f2c1619 <span class="o">(</span>HEAD -&gt; red<span class="o">)</span> R6
</span></span><span class="line"><span class="cl">*   e6899ea R5 merge branch <span class="s1">&#39;blue&#39;</span> into <span class="s1">&#39;red&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span><span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>* <span class="se">\ </span>  0979d45 R4 merge branch <span class="s1">&#39;green&#39;</span> into <span class="s1">&#39;red&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span><span class="se">\ \
</span></span></span><span class="line"><span class="cl"><span class="se"></span><span class="p">|</span> <span class="p">|</span> * 186da41 <span class="o">(</span>blue<span class="o">)</span> B3
</span></span><span class="line"><span class="cl"><span class="p">|</span> * <span class="p">|</span> c950910 <span class="o">(</span>green<span class="o">)</span> G3
</span></span><span class="line"><span class="cl">* <span class="p">|</span> <span class="p">|</span> 17e2629 R3
</span></span><span class="line"><span class="cl"><span class="p">|</span> <span class="p">|</span> * 69edfc9 B2
</span></span><span class="line"><span class="cl"><span class="p">|</span> * <span class="p">|</span> 059425a G2
</span></span><span class="line"><span class="cl"><span class="p">|</span> * <span class="p">|</span> 05719c8 G1
</span></span><span class="line"><span class="cl"><span class="p">|</span> <span class="p">|</span> * ebb218d B1
</span></span><span class="line"><span class="cl"><span class="p">|</span> <span class="p">|</span>/
</span></span><span class="line"><span class="cl">* / 8c6595b R2
</span></span><span class="line"><span class="cl"><span class="p">|</span>/
</span></span><span class="line"><span class="cl">* 6581ff8 R1
</span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit
</span></span></code></pre></td></tr></table>
</div>
</div><details>
<summary><mark>快速创建该示例。</mark></summary>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1"> 1</a>
</span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2"> 2</a>
</span><span class="lnt" id="hl-1-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-3"> 3</a>
</span><span class="lnt" id="hl-1-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-4"> 4</a>
</span><span class="lnt" id="hl-1-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-5"> 5</a>
</span><span class="lnt" id="hl-1-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-6"> 6</a>
</span><span class="lnt" id="hl-1-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-7"> 7</a>
</span><span class="lnt" id="hl-1-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-8"> 8</a>
</span><span class="lnt" id="hl-1-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-9"> 9</a>
</span><span class="lnt" id="hl-1-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-10">10</a>
</span><span class="lnt" id="hl-1-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-11">11</a>
</span><span class="lnt" id="hl-1-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-12">12</a>
</span><span class="lnt" id="hl-1-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-13">13</a>
</span><span class="lnt" id="hl-1-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-14">14</a>
</span><span class="lnt" id="hl-1-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-15">15</a>
</span><span class="lnt" id="hl-1-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-16">16</a>
</span><span class="lnt" id="hl-1-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-17">17</a>
</span><span class="lnt" id="hl-1-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-18">18</a>
</span><span class="lnt" id="hl-1-19"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-19">19</a>
</span><span class="lnt" id="hl-1-20"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-20">20</a>
</span><span class="lnt" id="hl-1-21"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-21">21</a>
</span><span class="lnt" id="hl-1-22"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-22">22</a>
</span><span class="lnt" id="hl-1-23"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-23">23</a>
</span><span class="lnt" id="hl-1-24"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-24">24</a>
</span><span class="lnt" id="hl-1-25"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-25">25</a>
</span><span class="lnt" id="hl-1-26"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-26">26</a>
</span><span class="lnt" id="hl-1-27"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-27">27</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir cherry-pick<span class="p">;</span> <span class="nb">cd</span> cherry-pick/
</span></span><span class="line"><span class="cl">git init
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;init&#34;</span> &gt;&gt; init<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;init commit&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git checkout -b red
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;red&#34;</span> &gt;&gt; red<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;R1&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git branch green
</span></span><span class="line"><span class="cl">git branch blue
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;red&#34;</span> &gt;&gt; red<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;R2&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git checkout blue
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;blue&#34;</span> &gt;&gt; blue<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;B1&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git checkout green
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;green&#34;</span> &gt;&gt; green<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;G1&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;green&#34;</span> &gt;&gt; green<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;G2&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git checkout blue
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;blue&#34;</span> &gt;&gt; blue<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;B2&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git checkout red
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;red&#34;</span> &gt;&gt; red<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;R3&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git checkout green
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;green&#34;</span> &gt;&gt; green<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;G3&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git checkout blue
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;blue&#34;</span> &gt;&gt; blue<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;B3&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git checkout red
</span></span><span class="line"><span class="cl">git merge green -m <span class="s2">&#34;R4 merge branch &#39;green&#39; into &#39;red&#39;&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">git merge blue -m <span class="s2">&#34;R5 merge branch &#39;blue&#39; into &#39;red&#39;&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;red&#34;</span> &gt;&gt; red<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;R6&#34;</span><span class="p">;</span> sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">git --no-pager log --oneline --graph --date-order
</span></span></code></pre></td></tr></table>
</div>
</div></details>
<br/>
<p>当前 Git 提交历史示意图如下。</p>
<figure class="align-center ">
    <img loading="lazy" src="/img/mastering-git-cherry-pick/git-cherry-pick-overall.jpg#center"
         alt="提交记录示意图"/> <figcaption>
            提交记录示意图
        </figcaption>
</figure>

<p>Git cherry-pick 的命令的基本原理是根据用户所选择的提交，根据提交中的差异信息（diff）将这些提交移植至用户目标版本中。如将 hotfix 应用至其他 LTS 版本中是该功能的一个典型应用。</p>
<p><code>git cherry-pick</code> 的大致用法为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git cherry-pick <span class="o">[</span>options<span class="o">]</span> &lt;commit&gt;...
</span></span></code></pre></td></tr></table>
</div>
</div><p>此处的 <code>&lt;commit&gt;...</code> 即为用户希望移植的提交（集合），这是本文讨论的要点。</p>
<p><code>&lt;commit&gt;</code> 可以为单一提交（commit），也可以为一个版本区间（revision range）。若为 revision range，则该命令会将该 revision range 中的所有 commit 都解析出来，最终成为一连串的单一 commit <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。<code>cherry-pick</code> 可以同时接受多个 <code>&lt;commit&gt;</code> ，此时表现类似于 <code>git rev-list</code> 中的 <code>--no-walk</code> 行为<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<p>那我们依次来讨论 <code>&lt;commit&gt;...</code> 为单一 commit 以及 revision range 的情况。</p>
<h2 id="single-commit">Single commit</h2>
<h3 id="normal-commit">Normal commit</h3>
<p>回到上文的例子，如果仅需要将 <code>G2</code> 选取出来，我们可以这样操作。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-1">1</a>
</span><span class="lnt" id="hl-3-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-2">2</a>
</span><span class="lnt" id="hl-3-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-3">3</a>
</span><span class="lnt" id="hl-3-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-4">4</a>
</span><span class="lnt" id="hl-3-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-5">5</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 回到 master 新建一条分支用于测试</span>
</span></span><span class="line"><span class="cl">$ git checkout master
</span></span><span class="line"><span class="cl">$ git checkout -b cp-single-normal-commit
</span></span><span class="line"><span class="cl"><span class="c1"># G2 的提交 SHA 值为 059425a</span>
</span></span><span class="line"><span class="cl">$ git cherry-pick 059425a
</span></span></code></pre></td></tr></table>
</div>
</div><p>此时会出现合并冲突（merge conflict），输出如下所示。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-1">1</a>
</span><span class="lnt" id="hl-4-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-2">2</a>
</span><span class="lnt" id="hl-4-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-3">3</a>
</span><span class="lnt" id="hl-4-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-4">4</a>
</span><span class="lnt" id="hl-4-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-5">5</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">CONFLICT (modify/delete): green deleted in HEAD and modified in 059425a (G2). Version 059425a (G2) of green left in tree.
</span></span><span class="line"><span class="cl">error: could not apply 059425a... G2
</span></span><span class="line"><span class="cl">hint: after resolving the conflicts, mark the corrected paths
</span></span><span class="line"><span class="cl">hint: with &#39;git add &lt;paths&gt;&#39; or &#39;git rm &lt;paths&gt;&#39;
</span></span><span class="line"><span class="cl">hint: and commit the result with &#39;git commit&#39;
</span></span></code></pre></td></tr></table>
</div>
</div><p>这段内容告知我们这些信息：<code>green</code> 文件在当前（暂存）版本 <code>HEAD</code> 中并不存在，但在选取的 <code>G2</code> 提交中存在。如果需要该文件，则使用 <code>git add</code> 将其提交至暂存区，若希望保留当前暂存版本的状态，即删除该文件，则使用 <code>git rm</code> 将 <code>green</code> 文件舍弃。</p>
<p>我们希望在选取 G2 之后能够保留 <code>green</code> 文件，故采取如下操作。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-1">1</a>
</span><span class="lnt" id="hl-5-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-2">2</a>
</span><span class="lnt" id="hl-5-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-3">3</a>
</span><span class="lnt" id="hl-5-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-4">4</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 将 green 提交至暂存区</span>
</span></span><span class="line"><span class="cl">$ git add green
</span></span><span class="line"><span class="cl"><span class="c1"># 已修复所有合并冲突，继续进行 cherry-pick</span>
</span></span><span class="line"><span class="cl">$ git cherry-pick --continue
</span></span></code></pre></td></tr></table>
</div>
</div><p>此时 cherry-pick 操作已经完成，如果继续执行 <code>git cherry-pick --continue</code> ，则此时会显示 <code>error: no cherry-pick or revert in progress</code> ，即当前没有进行任何 cherry-pick 任务。</p>
<p>查看一下当前的提交记录，则会发现 <code>G2</code> 已经在我们当前的分支 <code>cp-single-normal-commit</code> 上了。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-1">1</a>
</span><span class="lnt" id="hl-6-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-2">2</a>
</span><span class="lnt" id="hl-6-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-3">3</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order
</span></span><span class="line"><span class="cl">* <span class="m">0457362</span> <span class="o">(</span>HEAD -&gt; cp-single-normal-commit<span class="o">)</span> G2
</span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="merge-commit">Merge commit</h3>
<p>那如果我们想选取一个 merge commit 呢，比如将 <code>R4</code> 选取出来。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-7-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-1">1</a>
</span><span class="lnt" id="hl-7-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-2">2</a>
</span><span class="lnt" id="hl-7-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-3">3</a>
</span><span class="lnt" id="hl-7-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-4">4</a>
</span><span class="lnt" id="hl-7-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-5">5</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 回到 master 新建一条分支用于测试</span>
</span></span><span class="line"><span class="cl">$ git checkout master
</span></span><span class="line"><span class="cl">$ git checkout -b cp-single-merge-commit
</span></span><span class="line"><span class="cl"><span class="c1"># R4 的提交 SHA 值为 0979d45</span>
</span></span><span class="line"><span class="cl">$ git cherry-pick 0979d45
</span></span></code></pre></td></tr></table>
</div>
</div><p>当执行完这条 <code>cherry-pick</code> 命令之后，你会得到以下输出。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-8-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-1">1</a>
</span><span class="lnt" id="hl-8-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-2">2</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">error: commit 0979d45f1b46f72730188c5c01b3f2c7f41b18e6 is a merge but no -m option was given.
</span></span><span class="line"><span class="cl">fatal: cherry-pick failed
</span></span></code></pre></td></tr></table>
</div>
</div><p>默认情况下，<code>cherry-pick</code> 不处理 merge commit 并直接报错。因为在 merge commit 中，会有多个 parent 信息，但此时 Git 并不知道该使用哪个 parent 作为 mainline。在错误信息中，也同时提示了我们，如果要选取 merge commit ，则需要使用 <code>-m</code> （亦为 <code>--mainline</code>）选项来指定哪个 parent 是主线<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p>
<p>通过 <code>git show</code> 命令可以获得 merge commit 的多个 parent，且从 1 开始编号。由于该例中我们需要选取的 mainline parent 是 <code>R3(17e2629)</code> ，因此在 <code>cherry-pick</code> 中选择的是 <code>-m 1</code>。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-9-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-1">1</a>
</span><span class="lnt" id="hl-9-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-2">2</a>
</span><span class="lnt" id="hl-9-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-3">3</a>
</span><span class="lnt" id="hl-9-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-4">4</a>
</span><span class="lnt" id="hl-9-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-5">5</a>
</span><span class="lnt" id="hl-9-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-6">6</a>
</span><span class="lnt" id="hl-9-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-7">7</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager show 0979d45
</span></span><span class="line"><span class="cl">commit 0979d45f1b46f72730188c5c01b3f2c7f41b18e6
</span></span><span class="line"><span class="cl">Merge: 17e2629 c950910
</span></span><span class="line"><span class="cl">Author: Triple-Z &lt;me@triplez.cn&gt;
</span></span><span class="line"><span class="cl">Date:   Thu Mar <span class="m">31</span> 01:29:31 <span class="m">2022</span> +0800
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    R4 merge branch <span class="s1">&#39;green&#39;</span> into <span class="s1">&#39;red&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>让我们再来试一次。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-10-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-10-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git cherry-pick -m <span class="m">1</span> 0979d45
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>cherry-pick</code> 圆满完成！此时再看一下当前的提交记录，则发现在 <code>cp-single-merge-commit</code> 分支上产生了一个新的 <code>R4</code> 提交。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-11-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-11-1">1</a>
</span><span class="lnt" id="hl-11-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-11-2">2</a>
</span><span class="lnt" id="hl-11-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-11-3">3</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order
</span></span><span class="line"><span class="cl">* 987aba7 <span class="o">(</span>HEAD -&gt; cp-single-merge-commit<span class="o">)</span> R4 merge branch <span class="s1">&#39;green&#39;</span> into <span class="s1">&#39;red&#39;</span>
</span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit
</span></span></code></pre></td></tr></table>
</div>
</div><p>现在我们再来讲讲刚刚的 <code>-m 1</code> 发生了什么。如果现在去看 <code>cp-single-merge-commit</code> 这个测试分支上的文件，则会发现有 <code>green</code> ，而没有 <code>red</code> 。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-12-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-12-1">1</a>
</span><span class="lnt" id="hl-12-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-12-2">2</a>
</span><span class="lnt" id="hl-12-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-12-3">3</a>
</span><span class="lnt" id="hl-12-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-12-4">4</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ls -lh
</span></span><span class="line"><span class="cl">total <span class="m">16</span>
</span></span><span class="line"><span class="cl">-rw-r--r--  <span class="m">1</span> triplez  staff    18B  <span class="m">4</span>  <span class="m">7</span> 19:06 green
</span></span><span class="line"><span class="cl">-rw-r--r--  <span class="m">1</span> triplez  staff     5B  <span class="m">3</span> <span class="m">31</span> 01:29 init
</span></span></code></pre></td></tr></table>
</div>
</div><p>这是因为我们在选取 merge commit 时，使用的是 mainline 1 ，即 <code>red</code> 分支。因此 <code>cherry-pick</code> 事实是以 <code>red</code> 为基础，寻找 mainline 2 <code>green</code> 分支与 <code>red</code> 的差异，选取的就是 <code>green</code> 分支上所做的修改了。</p>
<h2 id="revision-range">Revision range</h2>
<p>Git 中可用多种方法来表示 revision （版本，或修订快照）<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>，这里我们主要讨论 revision range（版本区间）<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>。</p>
<p>对于 revision range，有以下六种表示法：</p>
<ol>
<li>
<p><code>^&lt;rev&gt;</code> ：（脱字符-表示法）表示<strong>排除</strong> <code>&lt;rev&gt;</code> 以及它<u>所有可到达</u>的父辈 commit。</p>
</li>
<li>
<p><code>&lt;r1&gt;..&lt;r2&gt;</code>（两点-范围表示法）：等同于 <code>^r1 r2</code> ，即 <strong>包含</strong> <code>&lt;r2&gt;</code> 以及其可到达的父辈 commit ，并<strong>排除</strong> <code>&lt;r1&gt;</code> 以及其可到达的父辈 commit。</p>
<blockquote>
<p>如果需要包括 <code>&lt;r1&gt;</code>，可使用这种写法：<code>&lt;r1&gt;^..&lt;r2&gt;</code> 。</p>
</blockquote>
</li>
<li>
<p><code>&lt;r1&gt;...&lt;r2&gt;</code> （三点-对称差分表示法）：<strong>包含</strong>所有 <code>&lt;r1&gt;</code> <u>或</u> <code>&lt;r2&gt;</code> 及其可到达的父辈 commit，并<strong>排除</strong> <code>&lt;r1&gt;</code> <u>和</u> <code>&lt;r2&gt;</code> 两者可到达的<u>共同</u>父辈 commit。</p>
</li>
<li>
<p><code>&lt;rev&gt;^@</code> ：<strong>包含</strong> <code>&lt;rev&gt;</code> 的所有父辈，但<strong>排除</strong> <code>&lt;rev&gt;</code> 本身。</p>
</li>
<li>
<p><code>&lt;rev&gt;^!</code> ：<strong>包含</strong> <code>&lt;rev&gt;</code> 本身，但<strong>排除</strong> <code>&lt;rev&gt;</code> 所有父辈。即表示单个 <code>&lt;rev&gt;</code> commit。</p>
<blockquote>
<p>注意： <code>&lt;rev&gt;</code> （表示 <code>&lt;rev&gt;</code> 及其所有父辈）在 revision range 的语境中不同于 <code>&lt;rev&gt;^!</code> 。仅有指定 <code>--no-walk</code> 参数时，两者才可以认为是相同的（都仅表示 <code>&lt;rev&gt;</code> 本身）。</p>
</blockquote>
</li>
<li>
<p><code>&lt;rev&gt;^-[&lt;n&gt;]</code> ：<strong>包含</strong> <code>&lt;rev&gt;</code> 及其所有父辈，但<strong>排除</strong> <code>&lt;rev&gt;</code> 的第 <code>&lt;n&gt;</code> 个 parent 及其可到达的所有父辈。 <code>&lt;n&gt;</code> 的缺省值为 1。</p>
</li>
</ol>
<p>看起来很复杂，我们来用文中的场景来举两个范围表示法的例子。</p>
<p>首先，考虑 <code>&lt;r1&gt;</code> 和 <code>&lt;r2&gt;</code> 都在同一分支上的情况，如 <code>G1 (05719c8)</code> 和 <code>G3 (c950910)</code>。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-13-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-1">1</a>
</span><span class="lnt" id="hl-13-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-2">2</a>
</span><span class="lnt" id="hl-13-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-3">3</a>
</span><span class="lnt" id="hl-13-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-4">4</a>
</span><span class="lnt" id="hl-13-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-5">5</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 回到 master 新建一条分支用于测试</span>
</span></span><span class="line"><span class="cl">$ git checkout master
</span></span><span class="line"><span class="cl">$ git checkout -b cp-range-same-branch
</span></span><span class="line"><span class="cl"><span class="c1"># G1 的提交 SHA 值为 05719c8，G3 的 SHA 值为 c950910</span>
</span></span><span class="line"><span class="cl">$ git cherry-pick 05719c8^..c950910
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>05719c8(G1)^..c950910(G3)</code> 的含义应当是：</p>
<ul>
<li><strong>包含</strong> <code>G3</code> 及其所有父辈。</li>
<li>并<strong>排除</strong> <code>G1</code> 的所有父辈（不排除 <code>G1</code>）。</li>
</ul>
<p>因此结果应当是选出从 <code>G1</code> 到 <code>G3</code> 的所有提交，示意图如下，黄色为被包含的节点，灰色则代表被排除的节点。</p>
<figure class="align-center ">
    <img loading="lazy" src="/img/mastering-git-cherry-pick/git-cherry-pick-g1-g3.jpg#center"
         alt="git cheery-pick G1^..G3"/> <figcaption>
            git cheery-pick G1^..G3
        </figcaption>
</figure>

<p>让我们再看看当前的提交记录。Bingo！ <code>G1</code> ，<code>G2</code> ，<code>G3</code> 这三个提交已经被选取出来了。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-14-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-1">1</a>
</span><span class="lnt" id="hl-14-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-2">2</a>
</span><span class="lnt" id="hl-14-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-3">3</a>
</span><span class="lnt" id="hl-14-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-4">4</a>
</span><span class="lnt" id="hl-14-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-5">5</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order
</span></span><span class="line"><span class="cl">* 32eac39 <span class="o">(</span>HEAD -&gt; cp-range-same-branch<span class="o">)</span> G3
</span></span><span class="line"><span class="cl">* d3b1130 G2
</span></span><span class="line"><span class="cl">* c82c4c7 G1
</span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit
</span></span></code></pre></td></tr></table>
</div>
</div><p>那 <code>&lt;r1&gt;</code> 和 <code>&lt;r2&gt;</code> 在不同分支上，是什么情况呢？</p>
<p>我们以 <code>G1 (05719c8)</code> 和 <code>B2 (69edfc9)</code> 作为用例。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-15-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-1">1</a>
</span><span class="lnt" id="hl-15-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-2">2</a>
</span><span class="lnt" id="hl-15-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-3">3</a>
</span><span class="lnt" id="hl-15-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-4">4</a>
</span><span class="lnt" id="hl-15-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-5">5</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 回到 master 新建一条分支用于测试</span>
</span></span><span class="line"><span class="cl">$ git checkout master
</span></span><span class="line"><span class="cl">$ git checkout -b cp-range-diff-branch
</span></span><span class="line"><span class="cl"><span class="c1"># G1 的提交 SHA 值为 05719c8，B2 的 SHA 值为 69edfc9</span>
</span></span><span class="line"><span class="cl">$ git cherry-pick 05719c8^..69edfc9
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>05719c8(G1)^..69edfc9(B2)</code> 的含义应当是：</p>
<ul>
<li><strong>包含</strong> <code>B2</code> 及其所有父辈。</li>
<li>并<strong>排除</strong> <code>G1</code> 的所有父辈（不排除 <code>G1</code>）。</li>
</ul>
<p>由于 <code>B2</code> 及其所有父辈中，并不包括 <code>G1</code>。因此我们可以将 <code>G1^..B2</code> 理解为包含 <code>B2</code> 及其所有父辈，且排除 <code>B2</code> 和 <code>G1</code> 的共同父辈后的结果。自然就只剩下 <code>B1</code> 和 <code>B2</code> 两个 commit 了。示意图如下，黄色为被包含的节点，灰色则代表被排除的节点。</p>
<figure class="align-center ">
    <img loading="lazy" src="/img/mastering-git-cherry-pick/git-cherry-pick-g1-b2.jpg#center"
         alt="git cherry-pick G1^..B2"/> <figcaption>
            git cherry-pick G1^..B2
        </figcaption>
</figure>

<p>让我们再看看当前的提交记录，确实是只选择了 <code>B1</code> 和 <code>B2</code> 两个提交。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-16-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-16-1">1</a>
</span><span class="lnt" id="hl-16-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-16-2">2</a>
</span><span class="lnt" id="hl-16-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-16-3">3</a>
</span><span class="lnt" id="hl-16-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-16-4">4</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order
</span></span><span class="line"><span class="cl">* e63f214 <span class="o">(</span>HEAD -&gt; cp-range-diff-branch<span class="o">)</span> B2
</span></span><span class="line"><span class="cl">* aed6717 B1
</span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="rerere">Rerere</h2>
<p>Rerere 是“重用已记录的冲突解决方案（reuse recorded resolution）”，它是一种简化冲突解决的方法<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>。</p>
<p>如果你经常进行大量的 merge, rebase 或 cherry-pick，或在维护一个长期不同于主干的分支<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>，那么非常建议开启 <code>rerere</code> 功能。</p>
<p>开启 <code>rerere</code> 非常简单，仅需要进行一次全局配置即可。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-17-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-17-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git config --global rerere.enabled <span class="nb">true</span>
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p>在本地仓库中直接创建 <code>.git/rr-cache</code> 文件夹，也可以为该仓库开启 <code>rerere</code>。</p>
</blockquote>
<h2 id="whats-next">What&rsquo;s next</h2>
<p>在笔者撰写该文的过程中，也看到了 Microsoft 的 Raymond Chen 写的 <a href="https://devblogs.microsoft.com/oldnewthing/20180323-01/?p=98325">Stop cherry-picking, start merging 系列文章</a>，他在其中提及了许多工程实践中 cherry-pick 可能导致的 pitfall。接下来的时间里，笔者将会逐一阅读该系列文章，并根据文中案例去分析 cherry-pick 是否能够在常用软件开发工作流给我们带来足够的收益，以及，是否应该 stop cherry-picking, start merging。</p>
<p>只有在深入了解工具后，我们才能更好地运用工具，真正实现效率提升。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>git-cherry-pick &lt;commit&gt;&hellip; <br/><a href="https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt-ltcommitgt82308203">https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt-ltcommitgt82308203</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>git-rev-list &ndash;no-walk<br/><a href="https://git-scm.com/docs/git-rev-list#Documentation/git-rev-list.txt---no-walksortedunsorted">https://git-scm.com/docs/git-rev-list#Documentation/git-rev-list.txt---no-walksortedunsorted</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>git-cherry-pick -m, &ndash;mainline<br/><a href="https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt--mltparent-numbergt">https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt--mltparent-numbergt</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>gitrevisions: Specifying Revisions<br/><a href="https://git-scm.com/docs/gitrevisions/#_specifying_revisions">https://git-scm.com/docs/gitrevisions/#_specifying_revisions</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>gitrevisions: Specifying Ranges<br/><a href="https://git-scm.com/docs/gitrevisions/#_specifying_ranges">https://git-scm.com/docs/gitrevisions/#_specifying_ranges</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Pro Git (zh): Git 工具 - Rerere<br/><a href="https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-Rerere#ef_rerere">https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-Rerere#ef_rerere</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>Pro Git (en): Git Tools - Rerere<br/><a href="https://git-scm.com/book/en/v2/Git-Tools-Rerere">https://git-scm.com/book/en/v2/Git-Tools-Rerere</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>Pro Git (zh): 分布式 Git - 维护项目 - Rerere<br/><a href="https://git-scm.com/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E7%BB%B4%E6%8A%A4%E9%A1%B9%E7%9B%AE#_rerere">https://git-scm.com/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E7%BB%B4%E6%8A%A4%E9%A1%B9%E7%9B%AE#_rerere</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content:encoded>
    </item>
    
    <item>
      <title>添加、删除、更新 Git submodule</title>
      <link>https://blog.triplez.cn/posts/add-remove-update-git-submodule/</link>
      <pubDate>Fri, 05 Mar 2021 23:32:21 +0800</pubDate>
      
      <guid>https://blog.triplez.cn/posts/add-remove-update-git-submodule/</guid>
      <description>Git submodule 的简单操作</description>
      <content:encoded><![CDATA[<h2 id="添加-git-子模块">添加 Git 子模块</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git submodule add https://github.com/account/your_target_module your/target_module_dir
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="删除-git-子模块">删除 Git 子模块</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1">1</a>
</span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2">2</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git rm your/target_module_dir
</span></span><span class="line"><span class="cl">$ rm -rf .git/modules/your/target_module_dir
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="更新-git-子模块">更新 Git 子模块</h2>
<p>进入 Git 子模块目录，对改动进行提交以及推送到远端子模块仓库。</p>
<p>重新退回使用 Git 子模块的 Git 仓库中，对子模块更新进行提交即可。</p>
<p>若子模块更新，其他项目可使用如下命令进行子模块的更新（根据 <code>.gitmodules</code> ）。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git submodule update
</span></span></code></pre></td></tr></table>
</div>
</div><p>同步远端子模块则为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git submodule update --remote
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="references">References</h2>
<ol>
<li>How do I remove a submodule?
<a href="https://stackoverflow.com/a/21211232">https://stackoverflow.com/a/21211232</a></li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>Git Initialization</title>
      <link>https://blog.triplez.cn/posts/git-initialization/</link>
      <pubDate>Sun, 01 Sep 2019 10:37:13 +0000</pubDate>
      
      <guid>https://blog.triplez.cn/posts/git-initialization/</guid>
      <description>Git init operations</description>
      <content:encoded><![CDATA[<ul>
<li>Change <code>Git</code> default settings;</li>
<li>Use <code>GPG</code> key to sign the <code>Git</code> commits.</li>
</ul>
<h2 id="step-by-step">Step by step</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1"> 1</a>
</span><span class="lnt" id="hl-0-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-2"> 2</a>
</span><span class="lnt" id="hl-0-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-3"> 3</a>
</span><span class="lnt" id="hl-0-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-4"> 4</a>
</span><span class="lnt" id="hl-0-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-5"> 5</a>
</span><span class="lnt" id="hl-0-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-6"> 6</a>
</span><span class="lnt" id="hl-0-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-7"> 7</a>
</span><span class="lnt" id="hl-0-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-8"> 8</a>
</span><span class="lnt" id="hl-0-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-9"> 9</a>
</span><span class="lnt" id="hl-0-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-10">10</a>
</span><span class="lnt" id="hl-0-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-11">11</a>
</span><span class="lnt" id="hl-0-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-12">12</a>
</span><span class="lnt" id="hl-0-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-13">13</a>
</span><span class="lnt" id="hl-0-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-14">14</a>
</span><span class="lnt" id="hl-0-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-15">15</a>
</span><span class="lnt" id="hl-0-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-16">16</a>
</span><span class="lnt" id="hl-0-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-17">17</a>
</span><span class="lnt" id="hl-0-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-18">18</a>
</span><span class="lnt" id="hl-0-19"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-19">19</a>
</span><span class="lnt" id="hl-0-20"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-20">20</a>
</span><span class="lnt" id="hl-0-21"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-21">21</a>
</span><span class="lnt" id="hl-0-22"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-22">22</a>
</span><span class="lnt" id="hl-0-23"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-23">23</a>
</span><span class="lnt" id="hl-0-24"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-24">24</a>
</span><span class="lnt" id="hl-0-25"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-25">25</a>
</span><span class="lnt" id="hl-0-26"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-26">26</a>
</span><span class="lnt" id="hl-0-27"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-27">27</a>
</span><span class="lnt" id="hl-0-28"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-28">28</a>
</span><span class="lnt" id="hl-0-29"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-29">29</a>
</span><span class="lnt" id="hl-0-30"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-30">30</a>
</span><span class="lnt" id="hl-0-31"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-31">31</a>
</span><span class="lnt" id="hl-0-32"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-32">32</a>
</span><span class="lnt" id="hl-0-33"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-33">33</a>
</span><span class="lnt" id="hl-0-34"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-34">34</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git config --global core.editor <span class="s2">&#34;vim&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># config your identity</span>
</span></span><span class="line"><span class="cl">git config --global user.email <span class="s2">&#34;YOUR_EMAIL&#34;</span>
</span></span><span class="line"><span class="cl">git config --global user.name <span class="s2">&#34;YOUR_NAME&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># enable gpg signed commit</span>
</span></span><span class="line"><span class="cl">git config --global commit.gpgsign <span class="nb">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># generate gpg key</span>
</span></span><span class="line"><span class="cl"><span class="c1">## git &gt;= 2.1.17</span>
</span></span><span class="line"><span class="cl">gpg --full-generate-key
</span></span><span class="line"><span class="cl"><span class="c1"># or gpg --gen-key</span>
</span></span><span class="line"><span class="cl"><span class="c1">## git &lt; 2.1.17</span>
</span></span><span class="line"><span class="cl">gpg --default-new-key-algo rsa4096 --gen-key
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># check your key id</span>
</span></span><span class="line"><span class="cl">$ gpg --list-secret-keys --keyid-format LONG
</span></span><span class="line"><span class="cl">gpg: checking the trustdb
</span></span><span class="line"><span class="cl">gpg: marginals needed: <span class="m">3</span>  completes needed: <span class="m">1</span>  trust model: pgp
</span></span><span class="line"><span class="cl">gpg: depth: <span class="m">0</span>  valid:   <span class="m">1</span>  signed:   <span class="m">0</span>  trust: 0-, 0q, 0n, 0m, 0f, 1u
</span></span><span class="line"><span class="cl">/home/triplez/.gnupg/pubring.kbx
</span></span><span class="line"><span class="cl">--------------------------------
</span></span><span class="line"><span class="cl">sec   rsa4096/96B4168C3D454E11 2018-11-28 <span class="o">[</span>SC<span class="o">]</span>
</span></span><span class="line"><span class="cl">      6B0B48B063F8F592CE9006DE96B4168C3D454E11
</span></span><span class="line"><span class="cl">uid                 <span class="o">[</span>ultimate<span class="o">]</span> Zhenzhen Zhao <span class="o">(</span>my gpg<span class="o">)</span> &lt;me@triplez.cn&gt;
</span></span><span class="line"><span class="cl">ssb   rsa4096/435B8EB04CBF8E8D 2018-11-28 <span class="o">[</span>E<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">gpg --armor --export 96B4168C3D454E11
</span></span><span class="line"><span class="cl"><span class="c1"># copy your GPG key, beginning with -----BEGIN PGP PUBLIC KEY BLOCK----- and ending with -----END PGP PUBLIC KEY BLOCK-----, then add it to the GitHub</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># set user to sign with this gpg key</span>
</span></span><span class="line"><span class="cl">git config --global user.signingkey 96B4168C3D454E11
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="references">References</h2>
<ul>
<li><a href="https://help.github.com/articles/generating-a-new-gpg-key/">Generating a new GPG key</a></li>
<li><a href="https://stackoverflow.com/questions/52808365/git-error-gpg-failed-to-sign-the-data-on-linux">Git error: gpg failed to sign the data on Linux</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>让你的 Git Commit 带上 GPG 签名</title>
      <link>https://blog.triplez.cn/posts/let-git-commit-brings-with-your-gpg-signature/</link>
      <pubDate>Fri, 09 Feb 2018 16:42:22 +0000</pubDate>
      
      <guid>https://blog.triplez.cn/posts/let-git-commit-brings-with-your-gpg-signature/</guid>
      <description>&lt;p&gt;让你的 git commits 更安全。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>让你的 git commits 更安全。</p>
<h2 id="生成-gpg-key">生成 GPG Key</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ gpg --gen-key
</span></span></code></pre></td></tr></table>
</div>
</div><p>生成时需要的信息：</p>
<ul>
<li>加密算法：选择 <code>RSA</code>；</li>
<li>密钥长度：<code>4096</code>；</li>
<li>有效时间：<code>1y</code>；</li>
<li>名字：<code>TripleZ</code> <del>其实随意</del>；</li>
<li>邮箱：<code>me@triplez.cn</code> 必须是 <code>GitHub</code> 认证过的邮箱；</li>
<li>注释：<code>Generate GPG for git commits.</code> <del>随意即可</del>。</li>
</ul>
<h2 id="查看-gpg-key">查看 GPG Key</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1">1</a>
</span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2">2</a>
</span><span class="lnt" id="hl-1-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-3">3</a>
</span><span class="lnt" id="hl-1-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-4">4</a>
</span><span class="lnt" id="hl-1-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-5">5</a>
</span><span class="lnt" id="hl-1-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-6">6</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ gpg --list-keys
</span></span><span class="line"><span class="cl">/home/triplez/.gnupg/pubring.gpg
</span></span><span class="line"><span class="cl">--------------------------------
</span></span><span class="line"><span class="cl">pub   4096R/2******4 2018-02-09 <span class="o">[</span>expires: 2019-02-09<span class="o">]</span>
</span></span><span class="line"><span class="cl">uid                  TripleZ <span class="o">(</span>Gen GPG <span class="k">for</span> git commits<span class="o">)</span> &lt;me@triplez.cn&gt;
</span></span><span class="line"><span class="cl">sub   4096R/0******E 2018-02-09 <span class="o">[</span>expires: 2019-02-09<span class="o">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="导出-gpg-公钥">导出 GPG 公钥</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ gpg --armor --export 2******4
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p>哈希值要根据实际情况而定。</p>
</blockquote>
<h2 id="配置-git">配置 Git</h2>
<p>设置 <code>Git</code> 默认使用刚才生成的 <code>GPG Key</code> ：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git config --global user.signingkey 2******4
</span></span></code></pre></td></tr></table>
</div>
</div><p>提交 <code>commit</code> 并使用 <code>GPG Key</code> 签名：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git commit -S
</span></span></code></pre></td></tr></table>
</div>
</div><p>设置 <code>Git</code> 默认开启 <code>GPG Key</code> 签名：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-1">1</a>
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git config --global commit.gpgsign <span class="nb">true</span>
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p>设置默认开启签名之后就不需要在 <code>commit</code> 时再加 <code>-S</code> 参数啦！</p>
</blockquote>
<p>再试试提交代码到 <code>GitHub</code> 上，是不是在 <code>commit</code> 上多了一个 <code>Verified</code> 按钮 🎉</p>]]></content:encoded>
    </item>
    
  </channel>
</rss>
