<div class="sect1"> <h2 id="_基本操作">基本操作</h2> <div class="sectionbody"> <div class="sect2"> <h3 id="_工作流">工作流</h3> <img data-lightbox="Git Handbook" data-title="Git Handbook" loading="eager" border="0" class="image" src="/media/posts/images/workflow.png" alt="workflow"/> <div class="paragraph"> <p>常见的 Workflow 有三种情况:</p> </div> <div class="sect3"> <h4 id="_单分支">单分支</h4> <div class="paragraph"> <p>一般见于个人使用场景, 所有工作内容均在主分支进行和完成。</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">C----C----C----C----C---- (main)</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_github_flow">Github Flow</h4> <div class="paragraph"> <p>一般见于小型团队使用场景, 每项工作内容均在新建的针对性的短期分支 (如 <code>feture</code>、<code>bug-fix</code> 等) 进行, 并最终合并到主分支。</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh"> C---C---C C (feature/bugfix) / \ / \ C---------------C------C---- (main)</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_git_flow">Git Flow</h4> <div class="paragraph"> <p>一般见于各类团队使用场景, 分支类型根据生命周期分为长期分支和短期分支:</p> </div> <div class="ulist"> <ul> <li> <p>长期分支包括:</p> <div class="ulist"> <ul> <li> <p>主分支 (<code>main</code>/<code>master</code>), 仅包含删节的提交记录和节点</p> </li> <li> <p>开发分支 (<code>dev</code>/<code>devlop</code>/<code>stage</code>), 由主分支 <code>fork</code> 产生, 将包含项目的完整提交记录和节点, 最终合并入主分支</p> </li> </ul> </div> </li> <li> <p>短期分支包括:</p> <div class="ulist"> <ul> <li> <p>新增功能分支 (<code>feature-<strong>*</strong></code>), 由开发分支 <code>fork</code> 产生, 最终合并入开发分支</p> </li> <li> <p>缺陷修复分支 (<code>bugfix-<strong><strong></strong></code>/<code>hotfix-<strong></strong></strong></code>), 由开发分支 <code>fork</code> 产生, 最终合并入主分支与开发分支</p> </li> <li> <p>版本发布分支 (<code>release-<strong>*</strong></code>), 由开发分支 <code>fork</code> 产生, 可以进行缺陷修复、文档生成等发布相关变动, 完成后合并入主分支, 并合并回开发分支</p> </li> <li> <p>本地临时分支</p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>长期分支并不用于日常维护, 仅当临时的短期分支中的变更完成后, 才会依次合并入长期分支。</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-shell" data-lang="shell"> C-----C (feature/bugfix) / \ / \ C (release) / \ / \ C--------------C \ (dev/develop/stage) / \ C---------------------------C---- (main/master)</code></pre> </div> </div> <img data-lightbox="Git Handbook" data-title="Git Handbook" loading="eager" border="0" class="image" src="/media/posts/images/feature-branches.svg" alt="feature-branches.svg"/> <img data-lightbox="Git Handbook" data-title="Git Handbook" loading="eager" border="0" class="image" src="/media/posts/images/hotfix-branches.svg" alt="hotfix-branches.svg"/> <img data-lightbox="Git Handbook" data-title="Git Handbook" loading="eager" border="0" class="image" src="/media/posts/images/release-branches.svg" alt="release-branches.svg"/> </div> <div class="sect3"> <h4 id="_其它">其它</h4> <div class="paragraph"> <p>当然, 实践中还有很多其它方案, 例如 <a href="https://docs.gitlab.com/ee/topics/gitlab_flow.html">Gitlab Flow</a> 等, 此处不一一列举。</p> </div> <div class="paragraph"> <p>如果使用 <code>git-flow</code> 工具, 可以参考 <a href="https://danielkummer.github.io/git-flow-cheatsheet/">git-flow-cheatsheet</a>。</p> </div> </div> </div> <div class="sect2"> <h3 id="_如何初始化当前目录为_git_仓库">如何初始化当前目录为 Git 仓库</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git init</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何添加远程仓库">如何添加远程仓库</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add remote https:/domain.com/repo.git</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何查看远程仓库信息">如何查看远程仓库信息</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git remote -v</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git remote show origin</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何克隆仓库">如何克隆仓库</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git clone https://domain.com/repo.git</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git clone https://domain.com/repo.git path/to/folder/</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何查看_git_仓库配置信息">如何查看 Git 仓库配置信息</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config -l</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何查看全局_git_配置信息">如何查看全局 Git 配置信息</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config --global -l</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何配置_git_仓库姓名和邮箱">如何配置 Git 仓库姓名和邮箱</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config user.name "Ezra"</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config user.email "admin@picsew.cn"</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何全局配置姓名和邮箱">如何全局配置姓名和邮箱</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config --global user.name "Ezra"</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config --global user.email "admin@picsew.cn"</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何记住账号密码等认证信息">如何记住账号密码等认证信息</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config credential.helper cache</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config credential.helper store</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何更改默认编辑器">如何更改默认编辑器</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config --global core.editor "nano"</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何全局配置记住认证信息">如何全局配置记住认证信息</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config --globan credential.helper cache</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config --global credential.helper store</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何取消某条_git_仓库配置">如何取消某条 Git 仓库配置</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config unset config-label</code></pre> </div> </div> <div class="paragraph"> <p>例如取消记住认证信息:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config unset credential.helper</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何取消某条_git_全局配置">如何取消某条 Git 全局配置</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config --global unset config-label</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何暂存文件">如何暂存文件</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add path/to/file.ext</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add .</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add --all</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add *.txt</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何在暂存文件时进行对比选择">如何在暂存文件时进行对比选择</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add -p</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何取消暂存文件">如何取消暂存文件</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git reset path/to/file.ext</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git reset *</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何撤销修改">如何撤销修改</h3>
</div> <div class="sect2"> <h3 id="_如何撤销未暂存文件的修改">如何撤销未暂存文件的修改</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git checkout path/to/file.ext</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何撤销已暂存文件的修改">如何撤销已暂存文件的修改</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git reset --hard path/to/file.ext</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git reset --hard HEAD</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何查看_git_仓库状态">如何查看 Git 仓库状态</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git status</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何对比文件修改信息">如何对比文件修改信息</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git diff</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git diff path/to/file.ext</code></pre> </div> </div> <div class="literalblock"> <div class="content"> <pre>git diff --staged</pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何提交修改">如何提交修改</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git commit</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git commit -m "commit message here"</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何设置提交信息模板">如何设置提交信息模板</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config commit.template /absolute/path/to/template/file</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config commit.template /relative/path/from/repository/root</code></pre> </div> </div> <div class="sect3"> <h4 id="_如何撰写一条合适的提交记录">如何撰写一条合适的提交记录</h4> <div class="paragraph"> <p>一条合适的提交记录应该包含:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-shell" data-lang="shell">type: subject
body (optional)
footer (optional)</code></pre> </div> </div> <div class="sect4"> <h5 id="_type类型">Type/类型</h5> <div class="ulist"> <ul> <li> <p><code>feat</code> - 一个新增功能</p> </li> <li> <p><code>fix</code> - 一个缺陷修复</p> </li> <li> <p><code>docs</code> - 文档变动</p> </li> <li> <p><code>style</code> - 样式调整</p> </li> <li> <p><code>refactor</code> - 非缺陷修复和新增功能的代码变动</p> </li> <li> <p><code>test</code> - 测试相关变动</p> </li> <li> <p><code>chore</code> - 更新编译构建、项目配置等</p> </li> </ul> </div> </div> <div class="sect4"> <h5 id="_subject主题">Subject/主题</h5> <div class="paragraph"> <p>简要的描述修改内容, 一般在 50 个字符以内较好。</p> </div> </div> <div class="sect4"> <h5 id="_body详情">Body/详情</h5> <div class="paragraph"> <p>这一部分用于解释你进行的修改和修改的原因等内容。当然, 并非所有的提交都需要这一部分进行说明。</p> </div> <div class="paragraph"> <p>在 <code>Body</code> 与 <code>Subject</code> 之间必须要留有一行空行。同时, <code>Body</code> 部分每一行应限制在 72 个字符以内。</p> </div> </div> <div class="sect4"> <h5 id="_footer脚注">Footer/脚注</h5> <div class="paragraph"> <p>这一部分也是可选的, 一般在你使用了缺陷追踪工具时会在这里添加和引用缺陷 ID 等信息。</p> </div> </div> <div class="sect4"> <h5 id="_示例">示例</h5> <div class="listingblock"> <div class="content"> <pre class="highlight"><code>fix: crash when click on the home button
Every time when user click on the home button, the app crash. This bug no longer exists.
Resolves: #123 See also: #456, #789</code></pre> </div> </div> </div> </div> </div> <div class="sect2"> <h3 id="_如何查看提交记录">如何查看提交记录</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log --oneline</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log --graph</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何查看所有分支的提交记录">如何查看所有分支的提交记录</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log --all</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何查看远程仓库的提交记录">如何查看远程仓库的提交记录</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log origin/main</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何修正最近一次的提交信息">如何修正最近一次的提交信息</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git commit --amend</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何修正最近一次的提交信息并添加新文件">如何修正最近一次的提交信息并添加新文件</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git commit --amend -a</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何回滚提交">如何回滚提交</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git revert HEAD</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git revert commit-id-here</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何在_git_中重命名文件">如何在 Git 中重命名文件</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git mv oldname.ext newname.ext</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何在_git_中删除文件">如何在 Git 中删除文件</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rm path/to/file/or/folder</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rm --cached path/to/file/or/folder</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何推送到远程仓库">如何推送到远程仓库</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何强制推送">如何强制推送</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push -f</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何从远程仓库拉取并自动与本地合并">如何从远程仓库拉取并自动与本地合并</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git pull</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何配置偏离分支合并策略">如何配置偏离分支合并策略</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config pull.rebase false</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git config pull.ff only</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何从远程仓库拉取但不自动与本地合并">如何从远程仓库拉取但不自动与本地合并</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git fetch</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何查看分支列表">如何查看分支列表</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git branch</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git branch -r</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何创建新本地分支">如何创建新本地分支</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git branch branch-name-here</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何重命名分支">如何重命名分支</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git branch -m old-branch-name new-branch-name</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何切换分支">如何切换分支</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git checkout branch-name-here</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何创建并切换到新本地分支">如何创建并切换到新本地分支</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git checkout -b branch-name-here</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何将本地新分支推送到远程仓库">如何将本地新分支推送到远程仓库</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push -u origin branch-name-here</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何删除分支">如何删除分支</h3>
</div> <div class="sect2"> <h3 id="_如何删除本地分支">如何删除本地分支</h3> <div class="literalblock"> <div class="content"> <pre>git branch -d branch-name-here</pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何删除远程分支">如何删除远程分支</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push --delete origin branch-name-here</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何合并其它分支到当前分支">如何合并其它分支到当前分支</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git merge branch-name-here</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何终止正在进行的合并">如何终止正在进行的合并</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git merge --abort</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何新建标签">如何新建标签</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git tag 1.0.0</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何将标签推送到远程仓库">如何将标签推送到远程仓库</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push --tags</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何删除标签">如何删除标签</h3>
</div> <div class="sect2"> <h3 id="_如何删除本地标签">如何删除本地标签</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git tag -d 1.0.0</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何删除远程仓库的标签">如何删除远程仓库的标签</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push --delete tag 1.0.0</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何对提交记录进行变基_遴选压平编辑等操作">如何对提交记录进行变基: 遴选、压平、编辑等操作</h3> <div class="paragraph"> <p>选择要操作的提交记录的前一条记录进行变基:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rebase -i previous-commit-id
p, pick = use commit
r, reword = use commit, but edit the commit message
e, edit = use commit, but stop for amending
s, squash = use commit, but meld into previous commit
f, fixup = like "squash", but discard this commit’s log message
x, exec = run command (the rest of the line) using shell
d, drop = remove commit</code></pre>
</div> </div> <div class="quoteblock"> <blockquote> <div class="paragraph"> <p>此操作会将指定 <code>commit-id</code> 后 (不包括该 <code>commit-id</code> 所对应的提交记录) 所有提交记录提供给用户进行遴选、压平、编辑等操作。</p> </div> </blockquote> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> <div class="paragraph"> <p>请注意, 除非个人使用, 否则不要用于操作公开和远程的分支、提交记录。</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>例如, 我们当前有如下提交记录:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log --oneline
612f2f7 This commit should not be squashed a3fb05d This commit should be squashed d84b05d This commit should be updated 36d15de Rebase from here 17692d1 Did some more stuff e647334 Another Commit 2e30df6 Initial commit</code></pre> </div> </div> <div class="paragraph"> <p>此时假设我们希望的需求是:</p> </div> <div class="ulist"> <ul> <li> <p>保留最新的 <code>612f2f7 This commit should not be squashed</code> 记录</p> </li> <li> <p>但将 <code>a3fb05d This commit should be squashed</code> 压平后合并入 <code>d84b05d This commit should be updated</code></p> </li> <li> <p>并更新为新的日志信息</p> </li> </ul> </div> <div class="paragraph"> <p>那么我们应当选取其前一条记录, 也即 <code>36d15de Rebase from here</code>, 进行变基操作:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rebase -i 36d15de</code></pre> </div> </div> <div class="paragraph"> <p>在出现的编辑器中, 提交记录的顺序与 <code>git log</code> 中的顺序是相反的。</p> </div> <div class="paragraph"> <p>此时根据我们的需求, 我们应当:</p> </div> <div class="ulist"> <ul> <li> <p>保持最新的 <code>612f2f7 This commit should not be squashed</code> 记录为 <code>pick</code> 状态</p> </li> <li> <p>修改要被合并的 <code>a3fb05d This commit should be squashed</code> 为 <code>squash</code>/<code>s</code> 状态</p> </li> <li> <p>修改其合并入的 <code>d84b05d This commit should be updated</code> 为 <code>edit</code>/<code>e</code> 状态</p> <div class="ulist"> <ul> <li> <p>(如果不需要更新此处的日志信息, 则修改状态为 <code>pick</code>/<code>p</code> 即可)</p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>然后保存。</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">edit d84b05d This commit should be updated squash a3fb05d This commit should be squashed pick 612f2f7 This commit should not be squashed</code></pre> </div> </div> <div class="paragraph"> <p>此处由于我们标记了 <code>edit</code> 状态, 保存后会要求执行 <code>git commit --amend</code> 命令编辑日志信息, 我们将根据需求修改日志信息, 例如 <code>A updated commit message</code>, 并保存。</p> </div> <div class="paragraph"> <p>再次查看日志, 会发现记录 <code>a3fb05d This commit should be squashed</code> 已经被压平合并到其前一条记录中, 且该记录的日志信息也已经变更:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log --oneline
612f2f7 This commit should not be squashed d84b05d A updated commit message ac60234 Yet another commit 36d15de Rebase from here 17692d1 Did some more stuff e647334 Another Commit 2e30df6 Initial commit</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何对分支进行变基">如何对分支进行变基</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rebase base-branch-name-here</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何终止变基操作">如何终止变基操作</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rebase --abort</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何继续变基操作">如何继续变基操作</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rebase --continue</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何区分合并与变基">如何区分合并与变基</h3> <img data-lightbox="Git Handbook" data-title="Git Handbook" loading="eager" border="0" class="image" src="/media/posts/images/merging.svg" alt="merging"/> <img data-lightbox="Git Handbook" data-title="Git Handbook" loading="eager" border="0" class="image" src="/media/posts/images/rebasing.svg" alt="rebasing"/> </div> <div class="sect2"> <h3 id="_如何进行打包发布">如何进行打包发布</h3> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git archive --format=tar --prefix=proj-1.2.3/ HEAD</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_如何同时操作多个_git_仓库">如何同时操作多个 Git 仓库</h3> <div class="paragraph"> <p>例如使所有子目录下的 Git 仓库统一执行拉取操作:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">find . -type d -exec git --git-dir={}/.git --work-tree=$PWD/{} pull origin master \;</code></pre> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="example-flow">操作实例</h2> <div class="sectionbody"> <div class="sect2"> <h3 id="_一般流程">一般流程</h3> <div class="sect3"> <h4 id="_准备工作">准备工作</h4> <div class="paragraph"> <p>接手新项目后, 首先要在本地安装 <code>git</code> 工具、客户端等工具, 例如:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">sudo apt install git sudo yum install git sudo dnf install git sudo pacman -Sy git</code></pre> </div> </div> <div class="paragraph"> <p>克隆项目仓库到本地:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git clone https://domain.com/path/to/repo.git</code></pre> </div> </div> <div class="paragraph"> <p>然后进入克隆好的本地仓库:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">cd repo</code></pre> </div> </div> <div class="sect4"> <h5 id="_初始化">初始化</h5> <div class="paragraph"> <p>如果是全新构建的项目, 先创建开发分支 <code>stage</code> 并推送到服务器:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git checkout -b stage</code></pre> </div> </div> <div class="paragraph"> <p>此时我们将处于 <code>stage</code> 分支中。</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push -u origin stage</code></pre> </div> </div> </div> </div> <div class="sect3"> <h4 id="_分支切换">分支切换</h4> <div class="paragraph"> <p>进入新克隆的项目时我们默认处于主分支中, 一般为 <code>master</code> 或 <code>main</code> 分支。</p> </div> <div class="paragraph"> <p>根据团队要求的不同, 我们可能需要进行分支切换和创建。通常来说, 我们会切换到 <code>dev</code>/<code>devlop</code>/<code>stage</code> 分支, 并基于该分支创建新的短期工作分支:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git checkout stage</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git checkout -b feature-0206</code></pre> </div> </div> <div class="paragraph"> <p>此时可以在 <code>feature-0206</code> 分支开始工作了。</p> </div> </div> <div class="sect3"> <h4 id="_提交变动">提交变动</h4> <div class="paragraph"> <p>待工作告一段落时, 首先需要对修改的文件进行添加暂存, 最简单的是全部暂存:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add --all</code></pre> </div> </div> <div class="paragraph"> <p>如果需要暂存特定文件:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add path/to/file.txt</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add path/*.txt</code></pre> </div> </div> <div class="paragraph"> <p>如果修改的内容较多, 可以进行对比:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git diff path/to/file.txt</code></pre> </div> </div> <div class="paragraph"> <p>并选择其中部分进行暂存:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git add -p path/to/file.txt</code></pre> </div> </div> <div class="paragraph"> <p>如果意外暂存了错误的文件, 可以对其状态进行重置:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git reset path/to/file.txt</code></pre> </div> </div> <div class="paragraph"> <p>如果不需要暂存某文件, 可以通过增加 <code>.gitignore</code> 文件进行忽略, 或者:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rm path/to/file.txt</code></pre> </div> </div> <div class="paragraph"> <p>接下来, 需要本地提交这些暂存:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git commit -m "some commit message here"</code></pre> </div> </div> <div class="paragraph"> <p>通过反复暂存和提交后, 所有修改均已在本地提交, 此时应检查日志:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log</code></pre> </div> </div> <div class="paragraph"> <p>你将看到类似下面的结果:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">commit 655acf96e613aa38a25c62a969cee56d2ccf9cdd (HEAD -> master) Author: Ezra <meniny@qq.com> Date: Tue Feb 6 14:16:47 2022 +0800
API doc improvements
commit e4b480e2446570d2aa32543c930bd37121ae9dc9 Author: Ezra <meniny@qq.com> Date: Tue Feb 6 14:16:17 2022 +0800
change api document
commit 85d81525fc07db28de1304b4743f8bea1a300db8 Author: Ezra <meniny@qq.com> Date: Tue Feb 6 13:51:18 2022 +0800
API
commit ccf8ad5c9758c9d07212ae7cf11aa2bc5ffc4c8d Author: Ezra <meniny@qq.com> Date: Tue Feb 6 13:50:38 2022 +0800
dev files
commit 71d35b059bfd1951e9a2310eadda12c4f26a3d61 Author: Ezra <meniny@qq.com> Date: Tue Feb 6 13:50:07 2022 +0800
README
commit 18031d7cf833118200f9aae247680bdb91a10f87 Author: Ezra <meniny@qq.com> Date: Tue Feb 6 13:47:57 2022 +0800
init</code></pre> </div> </div> <div class="sect4"> <h5 id="_遴选压平">遴选压平</h5> <div class="paragraph"> <p>在此示例中, 最近的三条提交记录均为对 <code>API.md</code> 文件的同一类型的修改, 因此我们希望将如下三条记录合并为一条:</p> </div> <div class="ulist"> <ul> <li> <p><code>655acf96e613aa38a25c62a969cee56d2ccf9cdd API doc improvements</code></p> </li> <li> <p><code>e4b480e2446570d2aa32543c930bd37121ae9dc9 change api document</code></p> </li> <li> <p><code>85d81525fc07db28de1304b4743f8bea1a300db8 API</code></p> </li> </ul> </div> <div class="paragraph"> <p>这时我们需要找到前一条记录, 假设为 <code>ccf8ad5c9758c9d07212ae7cf11aa2bc5ffc4c8d dev files</code>, 执行变基操作:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git rebase -i ccf8ad5c9758c9d07212ae7cf11aa2bc5ffc4c8d</code></pre> </div> </div> <div class="paragraph"> <p>执行上述指令后会出现编辑器:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh"> 1 pick 85d8152 API 2 pick e4b480e change api document 3 pick 655acf9 API doc improvements 4 5 # Rebase ccf8ad5..655acf9 onto ccf8ad5 (3 commands) 6 # 7 # Commands: 8 # p, pick <commit> = use commit 9 # r, reword <commit> = use commit, but edit the commit message 10 # e, edit <commit> = use commit, but stop for amending 11 # s, squash <commit> = use commit, but meld into previous commit 12 # f, fixup <commit> = like "squash", but discard this commit's log message 13 # x, exec <command> = run command (the rest of the line) using shell 14 # b, break = stop here (continue rebase later with 'git rebase --continue') 15 # d, drop <commit> = remove commit 16 # l, label <label> = label current HEAD with a name 17 # t, reset <label> = reset HEAD to a label 18 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] 19 # . create a merge commit using the original merge commit's 20 # . message (or the oneline, if no original merge commit was 21 # . specified). Use -c <commit> to reword the commit message. 22 # 23 # These lines can be re-ordered; they are executed from top to bottom. 24 # 25 # If you remove a line here THAT COMMIT WILL BE LOST. 26 # 27 # However, if you remove everything, the rebase will be aborted. 28 #</code></pre> </div> </div> <div class="paragraph"> <p>由于我们要进行三条记录的合并, 因此将编辑器中第二行和第三行的 <code>pick</code> 改为 <code>squash</code> 或 <code>s</code> 后保存:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh"> 1 pick 85d8152 API 2 s e4b480e change api document 3 s 655acf9 API doc improvements</code></pre> </div> </div> <div class="paragraph"> <p>这一操作意味着, 我们将遴选第一条记录, 然后将第二条和第三条记录压平, 以便合并为同一条。</p> </div> <div class="paragraph"> <p>保存后, <code>git</code> 还会弹出新的编辑器, 要求我们对提交信息进行编辑:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh"> 1 # This is a combination of 3 commits. 2 # This is the 1st commit message: 3 4 API 5 6 # This is the commit message #2: 7 8 change api document 9 10 # This is the commit message #3: 11 12 API doc improvements 13 14 # Please enter the commit message for your changes. Lines starting 15 # with '#' will be ignored, and an empty message aborts the commit. 16 # 17 # Date: Tue Feb 8 13:51:18 2022 +0800 18 # 19 # interactive rebase in progress; onto ccf8ad5 20 # Last commands done (3 commands done): 21 # squash e4b480e change api document 22 # squash 655acf9 API doc improvements 23 # No commands remaining. 24 # You are currently rebasing branch 'master' on 'ccf8ad5'. 25 # 26 # Changes to be committed: 27 # new file: API.md 28 #</code></pre> </div> </div> <div class="paragraph"> <p>此处我们根据实际情况对提交记录的信息进行编辑并保存, 当然, 也可以直接保存:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">[detached HEAD 7a713af] API doc improvements Date: Tue Feb 8 13:51:18 2022 +0800 1 file changed, 7 insertions(+) create mode 100644 API.md Successfully rebased and updated refs/heads/master.</code></pre> </div> </div> <div class="paragraph"> <p>看到上面的信息时, 我们的变基操作已经成功, 接下来, 再次查看日志进行确认:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git log</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">commit 7a713af4d153bd5cb1a74f83161e496c5d4442a6 (HEAD -> master) Author: Ezra <meniny@qq.com> Date: Tue Feb 8 13:51:18 2022 +0800
API doc improvements
commit ccf8ad5c9758c9d07212ae7cf11aa2bc5ffc4c8d Author: Ezra <meniny@qq.com> Date: Tue Feb 8 13:50:38 2022 +0800
dev files
commit 71d35b059bfd1951e9a2310eadda12c4f26a3d61 Author: Ezra <meniny@qq.com> Date: Tue Feb 8 13:50:07 2022 +0800
README
commit 18031d7cf833118200f9aae247680bdb91a10f87 Author: Ezra <meniny@qq.com> Date: Tue Feb 8 13:47:57 2022 +0800
init</code></pre> </div> </div> <div class="paragraph"> <p>可以看到, 我们已经将最近的三条记录合并为同一条。</p> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> <div class="paragraph"> <p>注意: 此压平变基操作仅适用于还未推送到服务器的本地提交记录, 如果你的操作涉及到远程服务器和其他项目成员, 可能会造成非常严重的不良后果!</p> </div> </td> </tr> </table> </div> </div> </div> <div class="sect3"> <h4 id="_拉取变更">拉取变更</h4> <div class="paragraph"> <p>现在, 我们可以拉取远程仓库的变更到本地:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git pull</code></pre> </div> </div> <div class="paragraph"> <p>依照 <code>git</code> 提示, 解决出现的冲突 (如果有)。</p> </div> </div> <div class="sect3"> <h4 id="_合并推送">合并推送</h4> <div class="paragraph"> <p>接下来, 你可能需要根据实际请进行以下某一项操作:</p> </div> <div class="ulist"> <ul> <li> <p>合并到 <code>dev</code>/<code>devlop</code>/<code>stage</code> 分支后推送到远程分支</p> </li> <li> <p>直接推送修改到远程服务器</p> </li> </ul> </div> <div class="paragraph"> <p>第一种情况下, 首先需要切换到 <code>dev</code>/<code>devlop</code>/<code>stage</code> 分支, 然后进行合并:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git checkout stage</code></pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git merge feature-0206</code></pre> </div> </div> <div class="paragraph"> <p>你需要根据提示解决冲突 (如果有), 之后便可以进行推送了:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push</code></pre> </div> </div> <div class="paragraph"> <p>第二种情况下, 直接推送当前的 <code>feature-0206</code> 分支到远程仓库:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push -u origin feature-0206</code></pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> <div class="paragraph"> <p>当然, 你也可以选择使用 <code>git rebase branch_name</code> 的方式进行分之合并, 以使记录保持线性, 但由于 <code>dev</code>/<code>devlop</code>/<code>stage</code> 分支通常都由多人同时使用, 因此并不建议使用这种方式合并。</p> </div> </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_下一次">下一次</h4> <div class="paragraph"> <p>在下次一次开始工作时, 切记先进行拉取操作后再根据情况进行新建分支、切换分支等工作。</p> </div> </div> </div> <div class="sect2"> <h3 id="_强制推送">强制推送</h3> <div class="paragraph"> <p>在某些特殊情况下 (例如你在使用属于你个人的私有仓库), 如果你真的需要对已经推送到服务器的提交记录进行遴选、压平和变基操作, 你可以强制推送你的变更到服务器:</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-sh" data-lang="sh">git push -f</code></pre> </div> </div> <div class="paragraph"> <p>当然, 很多仓库服务器的默认设置是不允许强制推送的, 你提前需要在管理端开启。</p> </div> </div> </div> </div>