<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Ruby China 社区 Git 节点</title>
    <link>https://ruby-china.org/</link>
    <description>Ruby China 社区 Git 节点最新发帖。</description>
    <item>
      <title>GitLab CE 为什么需要如此高的配置</title>
      <description>&lt;p&gt;背景是一个朋友出来创业，我经常去找点乐子顺带无偿帮忙。&lt;br&gt;
之前代码托管在 gitee 上，最近赚钱了，想转移到自部署的代码库。我对 gitlab 的理解还停留在几年前部署的 v9, 当时也就 2g 内存的 vps 可以轻松跑起来。所以这次一开始是用 &lt;code&gt;2c2g 的 lightsail&lt;/code&gt;, 没想到启动即崩溃。&lt;br&gt;
参考网上的功能瘦身方法后起步内存降低到 2.6g, 可以在 &lt;code&gt;ec2 2c4g&lt;/code&gt; 的实例上跑起来，一周后还吃满干爆了。 &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;最终选择是转移到本地服务器部署，云上稳定运行需要 4c16g 朋友实在是肉疼.
好奇 gitlab 为什么会有如此高的占用.
这么一想 github、gittee 的服务成本也太高了...&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>RaySong</author>
      <pubDate>Thu, 09 Nov 2023 10:33:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/43463</link>
      <guid>https://ruby-china.org/topics/43463</guid>
    </item>
    <item>
      <title>弃用！Github 上用了 Git.io 缩址服务的都注意了</title>
      <description>&lt;p&gt;GitHub 是面向开源及私有软件项目的托管平台，因为只支持 Git 作为唯一的版本库格式进行托管，故名 GitHub。对程序员来说，GitHub 可以说是开源精神之所系。在 GitHub 任何职业程序员和编程爱好者都可以分享源代码、探寻自己感兴趣的项目进行交流学习。&lt;/p&gt;

&lt;p&gt;而近日，GitHub 发布了一则《弃用 Git.io》的公告。公告表示 GitHub 将于 2022 年 4 月 29 日起关闭缩址服务，到时 Git.io 上的所有链接将停止重定向。因此 GitHub 呼吁开发者们立即更新替换使用 git.io URL 服务的现有链接。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/upyun/c58a12a1-2c17-4718-be07-55711ddd9a8c.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="Git.io"&gt;Git.io&lt;/h2&gt;
&lt;p&gt;2011 年 GitHub 创建 URL 短网址服务，能够随机或自定义一个短链接并重定向到 github.com 和 github.io 等 GitHub 域。服务一经推出就饱受欢迎，很多程序员都将自己开发在 GitHub 的脚本链接更换成了自定义短网址。&lt;/p&gt;

&lt;p&gt;可是尽管短网址服务大受好评，GitHub 官方却一直没有将它作为正式产品发布，也就是说短网址一直都是实验产品。并且早在今年 1 月，GitHub 就宣布 git.io 进入了只读模式，让弃用进入了倒计时。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/upyun/09296a5f-c850-4423-a3cd-a9ebef88d468.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这当然是有原因的，GitHub 官方在公告中也提到：“由于使用当前 git.io 基础设施重定向的链接的安全性，我们决定加快（弃用）这一时间线，并决定最终会在 4 月 29 日从 git.io 中删除所有现有的链接重定向。”短网址虽然在分享、查阅，以及一些限制字符的场景中为使用者带来了巨大的便利，但是短网址完全覆盖原网址的特性具有很大的安全隐患，恶意软件链接能够轻易替代正确地址，进而造成隐私、资产等用户权利的损失。不仅仅是 GitHub，短网址的这个安全问题其实诟病已久，例如 Google 就在 2019 年 3 月 30 日停止了其网址的缩短服务功能 Google URL Shortener。&lt;/p&gt;

&lt;p&gt;回到最初的通知，各位程序员如果您正在使用 GitHub 的 git.io 短网址服务，请尽快替换这些链接哦~&lt;/p&gt;
&lt;h3 id="推荐阅读"&gt;推荐阅读&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.upyun.com/tech/article/708/AI%20%E8%A7%86%E9%A2%91%E4%BA%91%20VS%20%E7%AA%84%E5%B8%A6%E9%AB%98%E6%B8%85%EF%BC%8C%E8%B0%81%E6%98%AF%E8%A7%86%E9%A2%91%E6%97%B6%E4%BB%A3%E7%9A%84%E5%AE%A0%E5%84%BF.html" rel="nofollow" target="_blank" title=""&gt;AI 视频云 VS 窄带高清，谁是视频时代的宠儿&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.upyun.com/tech/article/709/%E6%B8%B8%E6%88%8F%E5%87%BA%E6%B5%B7%E6%B5%AA%E6%BD%AE%E4%B8%8B%EF%BC%8C%E8%BF%99%E4%BA%9B%E6%8A%80%E6%9C%AF%E9%9A%BE%E7%82%B9%E8%AF%A5%E5%A6%82%E4%BD%95%E6%94%BB%E5%85%8B.html" rel="nofollow" target="_blank" title=""&gt;游戏出海浪潮下，这些技术难点该如何攻克&lt;/a&gt;&lt;/p&gt;</description>
      <author>upyun</author>
      <pubDate>Wed, 11 May 2022 10:01:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/42384</link>
      <guid>https://ruby-china.org/topics/42384</guid>
    </item>
    <item>
      <title>改变世界的一次代码提交</title>
      <description>&lt;p&gt;TL;DR 本文较长，如果对 Git 内部实现不感兴趣可以快速跳过中间两个章节。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;吾诗已成。无论大神的震怒，还是山崩地裂，都不能把它化为无形！&lt;/p&gt;

&lt;p&gt;—— 奥维德《变形记》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;Linux 作为最大也是最成功的开源项目，吸引了全球程序员的贡献，到目前为止，共有两万多名开发者给 Linux Kernel 提交过代码。令人惊讶的是，在项目的前十年 (1991 ~ 2002) 中，Linus 作为项目管理员并没有借助任何配置管理工具，而是以手工方式通过 patch 来合并大家提交的代码。倒不是说 Linus 喜欢手工处理，而是因为他对于软件配置管理工具 (SCM) 非常挑剔，无论是商用的 clearcase 还是开源的 cvs、svn 等都不能入他的法眼。在他看来，一个能够满足 Linux 内核项目开发使用的版本控制系统需要满足几个条件：1) 快 2) 支持多分支场景（几千个分支并行开发场景）3) 分布式 4) 能够支持大型项目。直到 2002 年，Linus 终于找到了一款基本满足他要求的工具——BitKeeper, 而 BitKeeper 是商业工具，他们愿意给 Linux 社区免费使用，但是需要保证遵守不得进行反编译等条款。BitKeeper 提供的默认接口显然不能满足社区用户的全部需要，一位社区开发者反编译 BitKeeper 并利用了未公开接口，这让 BitKeeper 公司撤回了免费使用的 License。不得已，Linus 利用假期十天时间，实现一款 DVCS —— Git，并推送给社区开发者们使用。&lt;/p&gt;
&lt;h2 id="设计"&gt;设计&lt;/h2&gt;
&lt;p&gt;Git 已经成为全球软件开发者的标配，关于 Git 的介绍和用法不需多说，我今天想要谈谈 Git 的内部实现。不过在看本文之前，我先给大家提一个问题：如果是你来设计 git（或者重新设计 git），你打算怎么设计？第一个版本发布准备实现哪些功能？看完本文，再对照自己的想法做个比较。欢迎留言讨论。&lt;/p&gt;

&lt;p&gt;学习 Git 的内部实现，最好的办法是看 Linus 最初的代码提交，checkout 出 git 项目的第一次提交节点（方法参见博客：&lt;a href="/git-paging" title=""&gt;《阅读开源代码小技巧》&lt;/a&gt;），可以看到代码库中只有几个文件：一个 README，一个构建脚本 Makefile，剩下几个 C 源文件。这次 commit 的备注写的也非常特别：Initial revision of "git", the information manager from hell.&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;commit e83c5163316f89bfbde7d9ab23ca2e25604af290
Author: Linus Torvalds &amp;lt;torvalds@ppc970.osdl.org&amp;gt;
Date:   Thu Apr 7 15:13:13 2005 &lt;span class="nt"&gt;-0700&lt;/span&gt;

    Initial revision of &lt;span class="s2"&gt;"git"&lt;/span&gt;, the information manager from hell

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 README 中，Linus 详细描述了 Git 的设计思路。看似复杂的 Git 工作，在 Linus 的设计里，只有两种对象抽象：1) 对象数据库 ("object database")；2) 当前目录缓存 ("current directory cache")。&lt;/p&gt;

&lt;p&gt;Git 的本质就是一系列的文件对象集合，代码文件是对象、文件目录树是对象、commit 也是对象。这些文件对象的名称即内容的 SHA1 值，SHA1 哈希算法的值为 40 位。Linus 将前二位作为文件夹、后 38 位作为文件名。大家可以在 .git 目录里的 objects 里看到有很多两位字母/数字名称的目录，里面存储了很多 38 位 hash 值名称的文件，这就是 Git 的所有信息。Linus 在设计对象的数据结构时按照 &amp;lt;标签 ascii 码表示&amp;gt;(blob/tree/commit) + &amp;lt;空格&amp;gt; + &amp;lt;长度 ascii 码表示&amp;gt; + &amp;lt;\0&amp;gt; + &amp;lt;二进制数据内容&amp;gt; 来定义，大家可以用 xxd 命令看下 objects 目录里的对象文件 (需 zlib 解压)，比如一个 tree 对象文件内容如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000000: 7472 6565 2033 3700 3130 3036 3434 2068  tree 37.100644 h
00000010: 656c 6c6f 2e74 7874 0027 0c61 1ee7 2c56  ello.txt.'.a..,V
00000020: 7bc1 b2ab ec4c bc34 5bab 9f15 ba         {....L.4[....
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对象有三种：BLOB、TREE、CHANGESET。&lt;/p&gt;

&lt;p&gt;BLOB: 即二进制对象，这就是 Git 存储的文件，Git 不像某些 VCS（如 SVN）那样存储变更 delta 信息，而是存储文件在每一个版本的完全信息。比如先提交了一份 hello.c 进入了 Git 库，会生成一个 BLOB 文件完整记录 hello.c 的内容；对 hello.c 修改后，再提交 commit，会再生成一个新的 BLOB 文件记录修改后的 hello.c 全部内容。Linus 在设计时，BLOB 中仅记录文件的内容，而不包含文件名、文件属性等元数据信息，这些信息被记录在第二种对象 TREE 里。&lt;/p&gt;

&lt;p&gt;TREE: 目录树对象。在 Linus 的设计里 TREE 对象就是一个时间切片中的目录树信息抽象，包含了文件名、文件属性及 BLOB 对象的 SHA1 值信息，但没有历史信息。这样的设计好处是可以快速比较两个历史记录的 TREE 对象，不能读取内容，而根据 SHA1 值显示一致和差异的文件。另外，由于 TREE 上记录文件名及属性信息，对于修改文件属性或修改文件名、移动目录而不修改文件内容的情况，可以复用 BLOB 对象，节省存储资源。而 Git 在后来的开发演进中又优化了 TREE 的设计，变成了某一时间点文件夹信息的抽象，TREE 包含其子目录的 TREE 的对象信息（SHA1）。这样，对于目录结构很复杂或层级较深的 Git 库 可以节约存储资源。历史信息被记录在第三种对象 CHANGESET 里。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/git-object-model-tree.png?w=1280" title="" alt="Git simple objects model"&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图片摘自：&lt;em&gt;Pro Git, 10.2 Git Internals - Git Objects&lt;/em&gt; [^1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CHANGESET: 即 Commit 对象。一个 CHANGESET 对象中记录了该次提交的 TREE 对象信息（SHA1），以及提交者 (committer)、提交备注 (commit message) 等信息。跟其他 SCM（软件配置管理）工具所不同的是，Git 的 CHANGESET 对象不记录文件重命名和属性修改操作，也不会记录文件修改的 Delta 信息等，CHANGESET 中会记录父节点 CHANGESET 对象的 SHA1 值，通过比较本节点和父节点的 TREE 信息来获取差异。Linus 在设计 CHANGESET 父节点时允许一个节点最多有 16 个父节点，虽然超过两个父节点的合并是很奇怪的事情，但实际上，Git 是支持超过两个分支的多头合并的。&lt;/p&gt;

&lt;p&gt;Linus 在三种对象的设计解释后着重阐述了可信 (TRUST)：虽然 Git 在设计上没有涉及可信的范畴，但 Git 作为配置管理工具是可以做到可信的。原因是所有的对象都以 SHA1 编码（Google 实现 SHA1 碰撞攻击是后话，且 Git 社区也准备使用更高可靠性的 SHA256 编码来代替），而签入对象的过程可信靠签名工具保证，如 GPG 工具等。&lt;/p&gt;

&lt;p&gt;理解了 Git 的三种基本对象，那么对于 Linus 对于 Git 初始设计的“对象数据库”和“当前目录缓存”这两层抽象就很好理解了。加上原本的工作目录，Git 有三层抽象，如下图示：一个是当前工作区 (Working Directory)，也就是我们查看/编写代码的地方，一个是 Git 仓库 (Repository),即 Linus 说的对象数据库，我们在 Git 仓看到的 .git 文件夹中存储的内容，Linus 在第一版设计时命名为 .dircache，在这两个存储抽象中还有一层中间的缓存区（Staging Area），即 .git/index 里存储的信息，我们在执行 git add 命令时，便是将当前修改加入到了缓存区。&lt;/p&gt;

&lt;p&gt;Linus 解释了“当前目录缓存”的设计，该缓存就是一个二进制文件，内容结构很像 TREE 对象，与 TREE 对象不同的是 index 不会再包含嵌套 index 对象，即当前修改目录树内容都在一个 index 文件里。这样设计有两个好处：1. 能够快速的复原缓存的完整内容，即使不小心把当前工作区的文件删除了，也可以从缓存中恢复所有文件；2. 能够快速找出缓存中和当前工作区内容不一致的文件。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://rmt.dogedoge.com/fetch/hutusi/storage/blog/git-working-tree-staging-area-git-repo.png?w=1280" title="" alt="Git Working tree, staging area, and Git directory."&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图片摘自：&lt;em&gt;Things About Git and Github You Need to Know as Developer&lt;/em&gt; [^2]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="实现"&gt;实现&lt;/h2&gt;
&lt;p&gt;Linus 在 Git 的第一次代码提交里便完成了 Git 的最基础功能，并可以编译使用。代码极为简洁，加上 Makefile 一共只有 848 行。感兴趣的同事可以通过上一段所述方法 checkout Git 最早的 commit 上手编译玩玩，只要有 Linux 环境即可。因为依赖库版本的问题，需要对原始 Makefile 脚本做些小修改。Git 第一个版本依赖 openssl 和 zlib 两个库，需要手工安装这两个开发库。在 ubuntu 上执行： &lt;code&gt;sudo apt install libssl-dev libz-dev&lt;/code&gt; ；然后修改 makefile 在 &lt;code&gt;LIBS= -lssl&lt;/code&gt; 行 中的 &lt;code&gt;-lssl&lt;/code&gt; 改成 &lt;code&gt;-lcrypto&lt;/code&gt; 并增加 &lt;code&gt;-lz&lt;/code&gt; ；最后执行 make，忽略编译告警，会发现编出了 7 个可执行程序文件：init-db, update-cache, write-tree, commit-tree, cat-file, show-diff 和 read-tree.&lt;/p&gt;

&lt;p&gt;下面分别简要介绍下这些可执行程序的实现：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;init-db: 初始化一个 git 本地仓库，这也就是我们现在每次初始化建立 git 库式敲击的 &lt;code&gt;git init&lt;/code&gt; 命令。只不过一开始 Linus 建立的 仓库及 cache 文件夹名称叫 &lt;code&gt;.dircache&lt;/code&gt;, 而不是我们现在所熟知的 &lt;code&gt;.git&lt;/code&gt; 文件夹。&lt;/li&gt;
&lt;li&gt;update-cache: 输入文件路径，将该文件（或多个文件）加入缓冲区中。具体实现是：校验路径合法性，然后将文件计算 SHA1 值，将文件内容加上 blob 头信息进行 zlib 压缩后写入到对象数据库 (.dircache/objects) 中；最后将文件路径、文件属性及 blob sha1 值更新到 .dircache/index 缓存文件中。&lt;/li&gt;
&lt;li&gt;write-tree: 将缓存的目录树信息生成 TREE 对象，并写入对象数据库中。TREE 对象的数据结构为： 'tree ' + 长度 + \0 + 文件树列表。文件树列表中按照 文件属性 + 文件名 + \0 + SHA1 值结构存储。写入对象成功后，返回该 TREE 对象的 SHA1 值。&lt;/li&gt;
&lt;li&gt;commit-tree: 将 TREE 对象信息生成 commit 节点对象并提交到版本历史中。具体实现是输入要提交的 TREE 对象 SHA1 值，并选择输入父 commit 节点（最多 16 个），commit 对象信息中包含 TREE、父节点、committer 及作者的 name、email 及日期信息，最后写入新的 commit 节点对象文件，并返回 commit 节点的 SHA1 值。&lt;/li&gt;
&lt;li&gt;cat-file: 由于所有的对象文件都经过 zlib 压缩，因此想要查看文件内容的话需要使用这个工具来解压生成临时文件，以便查看对象文件的内容。&lt;/li&gt;
&lt;li&gt;show-diff: 快速比较当前缓存与当前工作区的差异，因为文件的属性信息（包括修改时间、长度等）也保存在缓存的数据结构中，因此可以快速比较文件是否有修改，并展示差异部分。&lt;/li&gt;
&lt;li&gt;read-tree: 根据输入的 TREE 对象 SHA1 值输出打印 TREE 的内容信息。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这就是第一个可用版本的 Git 的全部七个子程序，可能用过 Git 的同事会说：这怎么跟我常用的 Git 命令不一样呢？Git add, git commit 呢？是的，在最初的 Git 设计中是没有我们这些平常所使用的 git 命令的。在 Git 的设计中，有两种命令：分别是底层命令 (Plumbing commands) 和高层命令 (Porcelain commands)。一开始，Linus 就设计了这些给开源社区黑客使用的符合 Unix KISS 原则的命令，因为黑客们本身就是动手高手，水管坏了就撸起袖子去修理，因此这些命令被称为 plumbing commands. 后来接手 Git 的 Junio Hamano 觉得这些命令对于普通的用户可不太友好，因此在此之上，封装了更易于使用、接口更精美的高层命令，也就是我们今天每天使用的 git add, git commit 之类。Git add 就是封装了 update-cache 命令，而 git commit 就是封装了 write-tree, commit-tree 命令。关于底层命令的更详细介绍，大家有兴趣的话可以看 &lt;em&gt;Pro Git&lt;/em&gt; 中的 Git Internals 章节。&lt;/p&gt;

&lt;p&gt;具体的代码实现在这里就不再细述，Linus 的代码风格极为简洁，能一行完成的绝不写两行。另外，对于 Linux API 的使用自然无人出其右，我印象最深的是有好多处使用 mmap 建立文件与内存的映射，省去了内存申请、文件读写等操作，提升了工具性能。正如一位同事说的：Linus 的代码除了不满足编程规范，其他好像真挑不出什么毛病。顺便说一句，Linus 的缩进风格是 Tab 键。&lt;/p&gt;
&lt;h2 id="启示"&gt;启示&lt;/h2&gt;
&lt;p&gt;Linus 在提交了第一个 git commit 后，并向社区发布了 git 工具。当时，社区中有位叫 Junio Hamano 的开发者觉得这个工具很有意思，便下载了代码，结果发现一共才 1244 行代码，这更令他惊奇也引发了极大的兴趣。Junio 在邮件列表与 Linus 交流并帮助增加了 merge 等功能，而后持续打磨 git，最后 Junio 完全接手了 Git 的维护工作，Linus 则回去继续维护 Linux Kernel 项目。&lt;/p&gt;

&lt;p&gt;如果选历史上最伟大的一次 Git 代码提交，那一定是这 Git 工具项目本身的第一次代码提交。这次代码提交无疑是开创性的，如果说 Linux 项目促成了开源软件的成功并改写了软件行业的格局，那么 Git 则是改变了全世界开发者的工作方式和写作方式。在 Git 诞生后两年，旧金山的一个小酒馆里坐着三位年轻的程序员，决定要用 Git 做点什么，几个月后，GitHub 上线。&lt;/p&gt;

&lt;p&gt;回到文中开头提到的问题，如果我来设计 Git 的话，估计还是会从已有工具经验（如 SVN 使用）上来延伸设计，甚至在我最早接触 Git 时候曾肤浅的认为 Git 就是 SVN + 分布式。正是了解了 Git 的内部原理乃至阅读了 Git 的初始代码后才感叹其设计的精妙，Git 的初始设计和实现大概能给（开源）软件产品如下启发：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;解决痛点问题&lt;/strong&gt;：Git 的缘起便是 Linus 本人及 Linux 社区的诉求，而这些诉求推而广之是项目协作开发（特别是跨地域项目）的共性诉求。Linus 解决了他本人遇到的痛点问题，顺便达成了一项伟大的成就。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;极简设计&lt;/strong&gt;：Linus 在设计 Git 工具时并没有受传统 SCM 工具的束缚，考虑文件差异、版本对比等，而是抽象了几种基本对象就把 git 的设计思路给理清楚了。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MVP (minimum viable product, 最小可用产品)&lt;/strong&gt;：这个概念大家都懂，但实际操作起来却不容易。一个 MVP 的配置管理工具需要哪些功能？一般来说会想到代码提交、历史追溯、版本比较、分支合并等。但 Linus 却将它拆解开来，快速实现了底层的基本功能，简单到只有开源社区黑客才能用。但这就够了，黑客们因此发现了它的价值，继续给它添砖加瓦。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;快速发布，快速迭代&lt;/strong&gt;：这也是源于 Linux Kernel 的开发经验；Linus 在实现了 Git MVP 后，便在 Linux 社区邮件列表中公布，并征求意见，迭代完善。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;找到合适接班人&lt;/strong&gt;：《大教堂与集市》中也有类似的观点，它说的是：“如果你对一个项目失去了兴趣，你最后的职责就是把它交给一个称职的继承者。”不过 Linus 将 Git 交给 Junio 并不是因为失去了兴趣，而是因为他发现在 Git 基础架构建立好之后，Junio 比他更擅长于实现更丰富、对普通用户界面更友好的功能，因此他就放心的将 Git 交给了 Junio. 为开源项目找到更合适的接班人，这既需要魄力也需要智慧。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr&gt;

&lt;p&gt;[^1]: &lt;em&gt;Pro Git, 10.2 Git Internals - Git Objects&lt;/em&gt;: &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects" rel="nofollow" target="_blank"&gt;https://git-scm.com/book/en/v2/Git-Internals-Git-Objects&lt;/a&gt;
[^2]: &lt;em&gt;Things About Git and Github You Need to Know as Developer&lt;/em&gt;: &lt;a href="https://medium.com/swlh/things-about-git-and-github-you-need-to-know-as-developer-907baa0bed79" rel="nofollow" target="_blank"&gt;https://medium.com/swlh/things-about-git-and-github-you-need-to-know-as-developer-907baa0bed79&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="原文链接"&gt;原文链接&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://hutusi.com/the-greatest-git-commit" rel="nofollow" target="_blank"&gt;https://hutusi.com/the-greatest-git-commit&lt;/a&gt;&lt;/p&gt;</description>
      <author>hutusi</author>
      <pubDate>Sun, 27 Sep 2020 19:50:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/40446</link>
      <guid>https://ruby-china.org/topics/40446</guid>
    </item>
    <item>
      <title>如何提高 Github 的 pull/push 速度</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;本文假设读者有能力并且已经使用酸酸乳来科学上网，就不再赘述如何科学上网这个话题。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;很多公司会把代码托管在 Github 的私有仓库上，因为众所周知的原因，在中国大陆 git clone/pull/push 代码时非常慢。终端卡在那里，半天没有任何反应，不知道在干什么；好不容易连上 git 服务器，下载也是龟速。&lt;/p&gt;

&lt;p&gt;如果一天提交 15 次代码，就要浪费半个小时的时间。如何提高 git pull/push 的速度呢？&lt;/p&gt;
&lt;h2 id="HTTP 形式的仓库地址"&gt;HTTP 形式的仓库地址&lt;/h2&gt;
&lt;p&gt;对于开源的项目仓库，我们通常大部分并不需要 push 权限，只会 git clone/pull。很多人会选择使用 HTTP 方式来访问仓库地址。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/950ab57d-cd00-4cb6-8421-f7dbb2c6cc80.gif!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;对于 HTTP 形式的仓库地址，可以在终端里输入以下两行命令，为 Git 设置全局的 HTTP 代理，从而提高 git clone/pull 的速度。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global http.proxy "http://127.0.0.1:8080"
git config --global https.proxy "http://127.0.0.1:8080"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行完后 ~/.gitconfig 会多出几行。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[http]
    proxy = http://127.0.0.1:8080
[https]
    proxy = http://127.0.0.1:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以后再 pull 代码就会走代理。&lt;/p&gt;

&lt;p&gt;但是这种方法对于 git@github.com:my_name/repo.git 这种仓库地址并没有效果，因为它走的是 SSH。&lt;/p&gt;
&lt;h2 id="SSH 形式的仓库地址"&gt;SSH 形式的仓库地址&lt;/h2&gt;
&lt;p&gt;公司的私有仓库，大部分开发者会上传公钥到 Github, 使用 SSH 形式的仓库地址，避免 git push 时一次次输入密码。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/c3415215-1a55-41a1-a2f9-2747146feef8.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;对于这种仓库，我们可以在 ~/.ssh/config 设置代理，提高 git pull/push 的速度。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host github.com
    User git
    HostName github.com
    ProxyCommand nc -v -x 127.0.0.1:1080 %h %p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我选了一个比较大的开源项目的仓库做测试，以下是对比图：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/2e6733da-564f-44c7-84d7-1fe6fa178123.jpg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/chenxuhua/issues-blog/issues/3#issuecomment-625405802" rel="nofollow" target="_blank" title=""&gt;Github 中国加速访问&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/chuyik/02d0d37a49edc162546441092efae6a1" rel="nofollow" target="_blank" title=""&gt;macOS 给 Git(Github) 设置代理（HTTP/SSH）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;原文地址：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mednoter.com/slowness-of-github.html" rel="nofollow" target="_blank"&gt;https://mednoter.com/slowness-of-github.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>xiaoronglv</author>
      <pubDate>Tue, 15 Sep 2020 18:15:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/40408</link>
      <guid>https://ruby-china.org/topics/40408</guid>
    </item>
    <item>
      <title>官方 Gitflow Workflow 不适用于所有项目</title>
      <description>&lt;p&gt;最近公司团队在讨论采用什么样的 Git 分支工作方式，在仔细读了 Atlassian 的官方推荐文章之后，有一些小心得分享下：
&lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow" rel="nofollow" target="_blank"&gt;https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="读前准备"&gt;读前准备&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;这里用 'git-flow' 指代：官方 'Gitflow Workflow'，参考上面链接&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="git-flow 的应用场景"&gt;git-flow 的应用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;适用于 "larger projects"&lt;/li&gt;
&lt;li&gt;适用于需要定期 release 的项目&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="核心：中的核心 feature branch"&gt;核心：中的核心 feature branch&lt;/h2&gt;
&lt;p&gt;这是任何的 Git Flow 都会包含的，基础中的基础&lt;/p&gt;

&lt;p&gt;请参见： &lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow" rel="nofollow" target="_blank"&gt;https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="特色：develop 分支"&gt;特色：develop 分支&lt;/h2&gt;
&lt;p&gt;这个分支是个关键，我在思考为什么要存在这个分支？我是否可以不要这个分支？&lt;/p&gt;

&lt;p&gt;--1-- 为什么要存在？&lt;br&gt;
下面这张图足以说明：
&lt;img src="https://l.ruby-china.com/photo/2020/8e6b3477-1935-4a16-97d7-f2d4446c4249.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;其存在的意义就是让 master 分支只记录发布历史，而所有的开发分支的集成都发生在 develop 分支。
从图上看很直观，master 的发布历史确实很简洁，而且每次 release 都有一个 tag。&lt;/p&gt;

&lt;p&gt;--2-- 我是否可以不要 develop 分支&lt;br&gt;
我的观点是看情况：&lt;br&gt;
从技术层面，我们可以不要，后果就是 master 将成为集成分支，发布历史乱糟糟
而想要一个简洁的发布历史，那么你就需要 develop 分支&lt;/p&gt;

&lt;p&gt;看起来是个很容易的决定&lt;/p&gt;
&lt;h4 id="但是："&gt;但是：&lt;/h4&gt;
&lt;p&gt;注意再看看上面的应用场景，其中一个是 &lt;code&gt;适用于需要定期release的项目&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;那如果不是定期 release 的项目呢？&lt;/p&gt;

&lt;p&gt;假设现在有个项目是经常有 change request，而且一些是要即时发布，一些是要稍后发布。
------------ 这里暂且把这个项目叫做 &lt;strong&gt;奇怪的项目&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;那么这两种请求还能同时在 develop 分支工作吗？明显不行了，因为你需要分开部署上线。&lt;/p&gt;

&lt;p&gt;对于即时发布的，你需要在完成后第一时间通过 Pull Request 的形式 merge 回 master&lt;/p&gt;

&lt;p&gt;对于稍后发布的，你就需要放在另外一个地方存起来等待发布&lt;/p&gt;

&lt;p&gt;对于这样的项目，develop 分支，我认为不是最佳的实践&lt;/p&gt;
&lt;h2 id="release 分支"&gt;release 分支&lt;/h2&gt;
&lt;p&gt;release 是从 develop checkout 的，用来做发布用。它存在的意义在于可以解脱 develop 分支，承担了发布的任务，而 develop 分支可以继续开发。
这对于定期发布的项目来讲，确实，堪称完美。&lt;/p&gt;

&lt;p&gt;但是对于“奇怪的项目”，release 还能得心应手吗？其实不然，在奇怪的项目中，对于需要即时发布的需求，一天可能会有几个 release，每个 release 存在的周期及其短，这个时候对于 release 分支的维护成本会变得昂贵，而效用变得低下。&lt;/p&gt;

&lt;p&gt;那对于非即时发布的需求呢？&lt;br&gt;
这个时候个人觉得，release 分支可以用来存储非即时发布的任务，但是有一点较为关键，要做好 master 到 release 的同步，避免时间太久的 merge conflict 产生&lt;/p&gt;

&lt;p&gt;先写这么多吧，跑步去了……
随意讨论&lt;/p&gt;</description>
      <author>hiveer</author>
      <pubDate>Fri, 06 Mar 2020 21:15:59 +0800</pubDate>
      <link>https://ruby-china.org/topics/39570</link>
      <guid>https://ruby-china.org/topics/39570</guid>
    </item>
    <item>
      <title>使用 GitHub Actions 自动删除 merged branch</title>
      <description>&lt;p&gt;以前是 merge 了 branch 以后，自己删除，试着用 github 的新功能 github actions 进行自动化。&lt;/p&gt;

&lt;p&gt;方法这里介绍的很详细&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.github.com/en/articles/creating-a-workflow-with-github-actions#creating-a-workflow-using-the-file-editor" rel="nofollow" target="_blank"&gt;https://help.github.com/en/articles/creating-a-workflow-with-github-actions#creating-a-workflow-using-the-file-editor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这次添加下面的文件然后 push 到 github 就行了，完成以后 actions 页面可以进行查看。&lt;/p&gt;

&lt;p&gt;文件内容&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .github/main.workflow

workflow "on pull request merge, delete the branch" {
  on = "pull_request"
  resolves = ["branch cleanup"]
}

action "branch cleanup" {
  uses = "jessfraz/branch-cleanup-action@master"
  secrets = ["GITHUB_TOKEN"]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后 merge 一个 branch 以后，就会自动删除它。&lt;/p&gt;

&lt;p&gt;不知道大家怎么玩的 github actions&lt;/p&gt;</description>
      <author>heroyct</author>
      <pubDate>Wed, 10 Jul 2019 16:07:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/38824</link>
      <guid>https://ruby-china.org/topics/38824</guid>
    </item>
    <item>
      <title>GitHub 可以免费创建私有仓库了</title>
      <description>&lt;p&gt;&lt;a href="https://blog.github.com/2019-01-07-new-year-new-github/" rel="nofollow" target="_blank"&gt;https://blog.github.com/2019-01-07-new-year-new-github/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;github 个人可以免费创建私有仓库了，仓库最多可邀请 3 个人协作&lt;/p&gt;</description>
      <author>liuminhan</author>
      <pubDate>Tue, 08 Jan 2019 09:30:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/37980</link>
      <guid>https://ruby-china.org/topics/37980</guid>
    </item>
    <item>
      <title>维护测试环境分支和线上环境两个分支管理不同的 dotenv 的最佳实践？</title>
      <description>&lt;p&gt;现在有个项目是这样的：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;开发分支，对应着测试环境，.env.local 对应着测试环境的环境变量&lt;/li&gt;
&lt;li&gt;线上分支，代码全部和开发分支一样，.env.local 对应着线上分支的环境变量&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;每次功能更新，都先在开发分支上提交，测试环境测试无误后，rebase 到线上分支。&lt;/p&gt;

&lt;p&gt;但是由于两个分支总有一个提交不一样 (初次创建分支的时候修改 .env.local 的那个提交)，导致 rebase 后每次 push 都需要 force-with-lease。&lt;/p&gt;

&lt;p&gt;不知道大家怎么处理这种情景？谢谢！&lt;/p&gt;</description>
      <author>lukertty</author>
      <pubDate>Wed, 14 Nov 2018 11:12:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/37758</link>
      <guid>https://ruby-china.org/topics/37758</guid>
    </item>
    <item>
      <title>新分支依赖于未合并的分支</title>
      <description>&lt;p&gt;有个问题，我在 A 分支下建立了一个 topic 模型，现在我需要新建一个 B 分支，这个分支下的功能是基于 topic 模型的，问题是，在 A 分支还没有合并的情况下，我再从主分支新开 B 分支时，是没有 topic 模型的，这时该怎么办？&lt;/p&gt;</description>
      <author>dccmmtop</author>
      <pubDate>Mon, 21 May 2018 11:49:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/36810</link>
      <guid>https://ruby-china.org/topics/36810</guid>
    </item>
    <item>
      <title>Git 多个分支如何正确的共享代码，并且保持分支独有的代码?</title>
      <description>&lt;p&gt;我们的应用场景是这样的：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;公司内网版本基于 master 开发，同时给客户提供定制功能，比如分支名称为 master_B&lt;/li&gt;
&lt;li&gt; 由于产品还在开发过程中，所以大部分功能两个分支都是需要的&lt;/li&gt;
&lt;li&gt; 两个分支又有部分代码不能共享，比如有一些功能只有内部需要使用，另一部分代码只有客户需要&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;现状：
现在刚从 SVN 迁移到 Git，目前主要基于 master 开发，本地开发时使用基于特性 master 的特性分支，开发完成之后 pull 一下远程 master 分支代码，并且提交到 Gitlab，提 Merge Request。如果两个都需要就提两个 MR, 但是这样会把 master 的代码带入到 master_B 上去，并且 master_B 独有的代码也经常可能冲突。&lt;/p&gt;

&lt;p&gt;这种场景应该如何正确的使用 Git?&lt;/p&gt;</description>
      <author>uzor</author>
      <pubDate>Tue, 26 Sep 2017 17:41:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/34260</link>
      <guid>https://ruby-china.org/topics/34260</guid>
    </item>
    <item>
      <title>如何实现类似 GitHub 的那种对 repo 中同一个文件不同分支、不同 commit 的访问</title>
      <description>&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/vuefe/vuejs.org/2.0-cn/README.md" rel="nofollow" target="_blank"&gt;https://raw.githubusercontent.com/vuefe/vuejs.org/2.0-cn/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/vuefe/vuejs.org/5e4a743e662d103f3cd0cbc8f359275e9559dad7/README.md" rel="nofollow" target="_blank"&gt;https://raw.githubusercontent.com/vuefe/vuejs.org/5e4a743e662d103f3cd0cbc8f359275e9559dad7/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;类似于上面的两个 url，都是 README.md 这个文件，既可以让用户访问到 2.0-cn 分支时的这个文件，又可以 访问到 5e4a743e662d103f3cd0cbc8f359275e9559dad7 这个 commit 时的这个文件。&lt;/p&gt;

&lt;p&gt;如果我在我的服务器上放一个 git repo 怎么能实现上面的这种访问，直接通过静态文件的方式访问能实现吗？谢谢！&lt;/p&gt;</description>
      <author>gaolinjie</author>
      <pubDate>Sat, 23 Sep 2017 23:50:37 +0800</pubDate>
      <link>https://ruby-china.org/topics/34237</link>
      <guid>https://ruby-china.org/topics/34237</guid>
    </item>
    <item>
      <title>為你自己學 Git</title>
      <description>&lt;p&gt;各位大牛好：&lt;/p&gt;

&lt;p&gt;繼上回貼了一帖「&lt;a href="https://ruby-china.org/topics/33043" title=""&gt;為你自己學 Ruby on Rails&lt;/a&gt;」之後，最近因為忙著弄些線上課程，所以又順手再寫了一份「為你自己學 Git」。&lt;/p&gt;

&lt;p&gt;網址：&lt;a href="http://gitbook.tw/" rel="nofollow" target="_blank"&gt;http://gitbook.tw/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;這是我自己在&lt;a href="https://5xruby.tw" rel="nofollow" target="_blank" title=""&gt;五倍紅寶石&lt;/a&gt;公司開設的線上培訓課程所用到的補充教材，即然都寫了，而且我自認為我會的東西都是網路上學來的，本著 Open Source 不藏私的精神，也放上來給大家參考。若發現內容有誤或有任何問題，歡迎直接來信或是在該章節頁面下方留言 :)&lt;/p&gt;

&lt;p&gt;希望對想學習 Git 的人有幫助。&lt;/p&gt;</description>
      <author>eddie</author>
      <pubDate>Tue, 19 Sep 2017 08:17:06 +0800</pubDate>
      <link>https://ruby-china.org/topics/34171</link>
      <guid>https://ruby-china.org/topics/34171</guid>
    </item>
    <item>
      <title>交互式删除 git 本地分支的小工具</title>
      <description>&lt;p&gt;通过 &lt;code&gt;gem install git-dd&lt;/code&gt; 安装，然后命令行里执行 &lt;code&gt;git dd&lt;/code&gt; ：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/15ae22f2-ce99-4dc2-8b82-ceca9cd4249a.gif!large" title="" alt="git-dd"&gt;&lt;/p&gt;

&lt;p&gt;项目地址：&lt;a href="https://github.com/Sanster/git-dd" rel="nofollow" target="_blank"&gt;https://github.com/Sanster/git-dd&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;window 下在 git-bash 里无法显示，应该是 &lt;a href="https://github.com/piotrmurach/tty-prompt" rel="nofollow" target="_blank" title=""&gt;tty-prompt&lt;/a&gt; 的问题，提了个 issue &lt;a href="https://github.com/piotrmurach/tty-prompt/issues/57" rel="nofollow" target="_blank" title=""&gt;#57&lt;/a&gt;&lt;/p&gt;</description>
      <author>Sanster</author>
      <pubDate>Sat, 22 Jul 2017 09:44:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/33583</link>
      <guid>https://ruby-china.org/topics/33583</guid>
    </item>
    <item>
      <title>如何保持 Git Fork 持续更新</title>
      <description>&lt;p&gt;原文：&lt;a href="https://robots.thoughtbot.com/keeping-a-github-fork-updated" rel="nofollow" target="_blank" title=""&gt;Keeping A GitHub Fork Updated&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="摘要："&gt;摘要：&lt;/h2&gt;&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git clone git@github.com:croaky/dotfiles.git
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd  &lt;/span&gt;dotfiles
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git remote add upstream git@github.com:thoughtbot/dotfiles.git 
&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git fetch upstream
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git rebase upstream/master
&lt;span class="go"&gt;
//假如对upstream有commit的权限的话

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; upstream upstream/master
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>rocLv</author>
      <pubDate>Fri, 21 Jul 2017 09:57:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/33570</link>
      <guid>https://ruby-china.org/topics/33570</guid>
    </item>
    <item>
      <title>如何 programmatic 的方式用 [账号 + 密码] 的方式将代码 push 到 git 仓库 (而不是在弹出框输入账号密码)？</title>
      <description>&lt;p&gt;比如有一个 git 仓库 &lt;a href="https://github.com/test/test" rel="nofollow" target="_blank"&gt;https://github.com/test/test&lt;/a&gt;，我需要用程序自动的 clone 它，add remote 然后再 push 到另一个 repository，并且一定是一个 http 的 url 而不是 ssh 的。&lt;/p&gt;

&lt;p&gt;其中 &lt;code&gt;clone&lt;/code&gt; &lt;code&gt;add remote&lt;/code&gt; shell 命令都是可以的，但是在 &lt;code&gt;git push&lt;/code&gt; 的时候需要密码，求问这个可以通过在 push 的时候主动添加用户名密码的参数解决吗？或者有别的办法？&lt;/p&gt;</description>
      <author>aisensiy</author>
      <pubDate>Thu, 18 May 2017 13:13:03 +0800</pubDate>
      <link>https://ruby-china.org/topics/33015</link>
      <guid>https://ruby-china.org/topics/33015</guid>
    </item>
    <item>
      <title>.gitignore 文件配置层级跟踪问题</title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/11f1d26d31b8b598b6d0e4658a3924dd.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="跟踪"&gt;跟踪&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;跟踪public/upload/cmi/mdm_repair_exes&lt;/code&gt;而不跟踪 public/upload/下面的其他文件夹&lt;/p&gt;
&lt;h3 id="场景"&gt;场景&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;项目脚本中不写入创建文件夹语句,而是将图片生成在已存在的文件夹内&lt;/code&gt;&lt;/p&gt;</description>
      <author>sunshineboy</author>
      <pubDate>Fri, 31 Mar 2017 09:41:59 +0800</pubDate>
      <link>https://ruby-china.org/topics/32670</link>
      <guid>https://ruby-china.org/topics/32670</guid>
    </item>
    <item>
      <title>[译] 关于 Git 你需要知道的一些事情</title>
      <description>&lt;p&gt;本文译者：&lt;a href="https://blog.coding.net/author/wzw" rel="nofollow" target="_blank"&gt;https://blog.coding.net/author/wzw&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="分支和合并"&gt;分支和合并&lt;/h3&gt;
&lt;p&gt;Git 跟其他版本控制系统最大的优势就在于其高级的分支模型。&lt;/p&gt;

&lt;p&gt;Git 允许而且 &lt;strong&gt;鼓励&lt;/strong&gt; 你在本地使用多个完全独立的分支。这些分支的创建，合并和删除几乎都可以在几秒内完成。&lt;/p&gt;

&lt;p&gt;这意味着你可以轻松的做如下操作：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;无痛的上下文切换&lt;/strong&gt; 创建分支试验一个想法，提交几次，切回你原来分支的状态，应用一个改动 patch，切回你原来正在试验的状态，将刚才应用的 patch 合并过来。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;基于角色的代码支线&lt;/strong&gt; 你可能会有一个分支仅仅包含那些只存在于生产环境上的代码，另外有一个独立的分支用以合并测试环境代码，还有若干个更小一些的分支用于日常开发工作&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;基于特性的工作流&lt;/strong&gt; 为每一个新的特性创建新的分支，你可以方便平滑的在这些分支之间无缝切换，当这些特性的改动完成的时候，你可以将其合并入主分支，并把特性分支删掉。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;任意试验&lt;/strong&gt; 创建一个分支专门用来试验，当觉得试验不理想的时候，直接删除掉即可，放弃掉之前的试验内容。这时候不会有任何其他人察觉到这个试验（甚至在这期间你还可以推送其他不相关的分支）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://dn-coding-net-production-pp.qbox.me/fbc5861e-200d-40dd-9e61-203777544578.png" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;尤其是当你推送至远程仓库的时候，你不必推送所有分支，你可以选择只推送少数你愿意分享的分支，当然如果你愿意，也可以推送所有分支。这一点倾向于让开发者在试验很多新的想法的时候免除发布自己的未成熟的试验计划的顾虑。&lt;/p&gt;

&lt;p&gt;当然，也有一些其他的系统可以部分实现上述的功能和优势，只是具体的执行会变的困难和容易出错。Git 让这些工作变得难以置信的简单，它在开发者学习其使用的同时就改变了开发者的工作模式。&lt;/p&gt;
&lt;h3 id="轻量和快速"&gt;轻量和快速&lt;/h3&gt;
&lt;p&gt;Git 很快。Git 基本上所有的操作都在本地执行，这对于那些必须跟服务器通信的集中式系统是一个巨大的速度优势。&lt;/p&gt;

&lt;p&gt;Git 一开始是为了管理 Linux Kernel 的源代码设计的，这意味着他从第一天诞生就拥有了处理大型仓库的高效优势。Git 使用 C 语言编写，减轻了使用更高级别编程语言的 Runtime 带来的性能损耗。Git 最开始的两个重要的设计目标就是性能和速度。&lt;/p&gt;
&lt;h4 id="压力测试"&gt;压力测试&lt;/h4&gt;
&lt;p&gt;让我们看一下与 SVN（一个通用的集中式存储版本控制系统，跟 CVS 和 Perforce 很像）相比下的常规操作的性能测试指标。这里指标是值越小，速度越快。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-coding-net-production-pp.qbox.me/0f8ef23f-e8ac-44c4-85b4-7d1385a1d57d.png" title="" alt="图片"&gt; &lt;/p&gt;

&lt;p&gt;为了测试，我们在亚马逊的 AWS 的同样的可用区上新建了两个 Large 类型的计算服务器实例。每一个计算实例上都安装 Git 和 SVN。我们把 Ruby 的源代码仓库拷贝到了 Git 和 SVN 的计算服务器示例上，两者都执行通用的操作。&lt;/p&gt;

&lt;p&gt;在有些情况下，两者的命令和实际效果并不能完全对应起来。在这里，我们在常用的操作中选择相似效果的匹配情况。例如，对于“提交”的测试，在 Git 中我们也是计算 Push 的时间的。然而在大多数情况下，你可能实际上并不会在提交后马上就推送到服务器上，这在 SVN 上是不可分割的操作。&lt;/p&gt;

&lt;p&gt;下面表格中所有的时间单位都是秒。&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th style="text-align:center;"&gt;操作&lt;/th&gt;
&lt;th style="text-align:center;"&gt;描述&lt;/th&gt;
&lt;th style="text-align:center;"&gt;Git&lt;/th&gt;
&lt;th style="text-align:center;"&gt;SVN&lt;/th&gt;
&lt;th style="text-align:center;"&gt;性能倍数&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;提交文件 (A)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Add, commit and push 113 modified files (2164+, 2259-)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0.64&lt;/td&gt;
&lt;td style="text-align:center;"&gt;2.60&lt;/td&gt;
&lt;td style="text-align:center;"&gt;4x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;提交图片 (B)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Add, commit and push 1000 1k images&lt;/td&gt;
&lt;td style="text-align:center;"&gt;1.53&lt;/td&gt;
&lt;td style="text-align:center;"&gt;24.70&lt;/td&gt;
&lt;td style="text-align:center;"&gt;16x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;对比当前变动&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Diff 187 changed files (1664+, 4859-) against last commit&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0.25&lt;/td&gt;
&lt;td style="text-align:center;"&gt;1.09&lt;/td&gt;
&lt;td style="text-align:center;"&gt;4x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;对比最近的变动&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Diff against 4 commits back (269 changed/3609+,6898-)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0.25&lt;/td&gt;
&lt;td style="text-align:center;"&gt;3.99&lt;/td&gt;
&lt;td style="text-align:center;"&gt;16x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;对比标签&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Diff two tags against each other (v1.9.1.0/v1.9.3.0 )&lt;/td&gt;
&lt;td style="text-align:center;"&gt;1.17&lt;/td&gt;
&lt;td style="text-align:center;"&gt;83.57&lt;/td&gt;
&lt;td style="text-align:center;"&gt;71x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;提交历史 (50)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Log of the last 50 commits (19k of output)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0.38&lt;/td&gt;
&lt;td style="text-align:center;"&gt;31x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;提交历史 (全部)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Log of all commits (26,056 commits - 9.4M of output)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0.52&lt;/td&gt;
&lt;td style="text-align:center;"&gt;169.20&lt;/td&gt;
&lt;td style="text-align:center;"&gt;325x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;提交历史 (文件)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Log of the history of a single file (array.c - 483 revs)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0.60&lt;/td&gt;
&lt;td style="text-align:center;"&gt;82.84&lt;/td&gt;
&lt;td style="text-align:center;"&gt;138x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;更新&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Pull of Commit A scenario (113 files changed, 2164+, 2259-)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;0.90&lt;/td&gt;
&lt;td style="text-align:center;"&gt;2.82&lt;/td&gt;
&lt;td style="text-align:center;"&gt;3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;Blame&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Line annotation of a single file (array.c)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;1.91&lt;/td&gt;
&lt;td style="text-align:center;"&gt;3.04&lt;/td&gt;
&lt;td style="text-align:center;"&gt;1x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;你需要注意的是，这已经是 SVN 最好的运行场景了 -- 一个没有任何负载的服务器，客户端和服务器之间的网络带宽达到 80MB/s。上文中的所有指标在受网络波动，或者在一个更差的网络环境下 SVN 的表现都更差，然而 Git 这边几乎所有的指标都不受影响。&lt;/p&gt;

&lt;p&gt;很明显，在这些最常用的版本控制工具的操作中，甚至是在 SVN 的理想使用环境下，&lt;strong&gt;Git 在很多方面都大幅领先&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;一个 Git 比 SVN 慢的地方是初始化 clone 仓库。在这种情况下，Git 是在下载整个仓库历史而不是仅仅是最新版本的代码。上文中的表格所示，仅仅执行一次的操作影响并不是很大。&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th style="text-align:center;"&gt;操作&lt;/th&gt;
&lt;th style="text-align:center;"&gt;描述&lt;/th&gt;
&lt;th style="text-align:center;"&gt;Git (Shallow Clone)&lt;/th&gt;
&lt;th style="text-align:center;"&gt;Git&lt;/th&gt;
&lt;th style="text-align:center;"&gt;SVN&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;Clone&lt;/td&gt;
&lt;td style="text-align:center;"&gt;Git Clone 以及 shallow clone(浅 clone)  vs SVN checkout&lt;/td&gt;
&lt;td style="text-align:center;"&gt;21.0&lt;/td&gt;
&lt;td style="text-align:center;"&gt;107.5&lt;/td&gt;
&lt;td style="text-align:center;"&gt;14.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;大小 (M)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;客户端在 clone/checkout 后的文件大小 (以 M 为单位)&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;181.0&lt;/td&gt;
&lt;td style="text-align:center;"&gt;132.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;另外一个有趣的点是，Git 和 SVN 在 Clone 或者 Checkout 到本地后的文件大小几乎差别不大，要知道对于 Git 来说，本地可是包含了整个项目历史。这也展示了 Git 在文件压缩和存储上的超高效率。&lt;/p&gt;
&lt;h3 id="分布式"&gt;分布式&lt;/h3&gt;
&lt;p&gt;Git 最棒的特性之一就是分布式。这意味着，你要 clone 整个仓库而不是仅仅 checkout 分支的最新头部版本。&lt;/p&gt;
&lt;h4 id="多个备份"&gt;多个备份&lt;/h4&gt;
&lt;p&gt;在日常的使用场景中 Git 往往有多个备份。这意味着就算在使用一个中央存储式的工作流，每一个用户都在本地有一个服务器上的完整备份。这里的任意一个版本都可以在服务器端数据损坏或者丢失的时候推送回服务器以挽救损失。事实上，只要你的仓库不是只有一个 copy，Git 就不会存在单点问题。&lt;/p&gt;
&lt;h4 id="任意工作流"&gt;任意工作流&lt;/h4&gt;
&lt;p&gt;因为 Git 拥有分布式特性和极好的分支系统，你可以在此基础上轻松实现大量的工作流模型。&lt;/p&gt;
&lt;h5 id="Subversion（SVN） 风格工作流"&gt;Subversion（SVN）风格工作流&lt;/h5&gt;
&lt;p&gt;集中式存储的工作流非常常见，特别是对于那些从传统的集中式代码版本管理系统转过来使用 Git 的人。Git 一样可以提供这种工作形式：每次 Push 必须要更新到远程仓库的最新版本。所以说大家还是像以前一样使用集中式存储的工作流往同一个服务器上 Push 代码依然没问题。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-coding-net-production-pp.qbox.me/be01baf7-97c3-4df7-a1a2-adb212a8c118.png" title="" alt="图片"&gt; &lt;/p&gt;
&lt;h4 id="整合管理者工作流"&gt;整合管理者工作流&lt;/h4&gt;
&lt;p&gt;另外一个常见的 Git 工作流是整合工作流。主要的仓库有一个单一的开发者维护（维护者）。其他若干开发者从这个仓库 clone，然后推送到他们自己的完全独立的仓库里面，最后请求维护者从主要仓库 Pull 那些他们在各自的仓库里面的改动。这种形式往往在 GitHub 上以开源的形式进行协作。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-coding-net-production-pp.qbox.me/9e404cea-2d16-4fdc-9b99-fa92e0ea961f.png" title="" alt="图片"&gt; &lt;/p&gt;
&lt;h4 id="维护者和负责人工作流"&gt;维护者和负责人工作流&lt;/h4&gt;
&lt;p&gt;对于一些更为复杂的项目来讲，像 Linux 内核这样的开发工作流也是很有效的。在这个模型中，负责人（lieutenants）负责整个项目的一些特定的子系统，他们合并所有跟那个子系统关联的变动。另外一个维护者（dictator，字面理解：独裁者）只能从他管辖的负责人这里获取变更，并将这些变更推送到主要仓库。然后所有人都从这个仓库获取更新。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-coding-net-production-pp.qbox.me/bdae03db-8454-4bb3-a309-d0b22d447b66.png" title="" alt="图片"&gt; &lt;/p&gt;
&lt;h3 id="数据校验"&gt;数据校验&lt;/h3&gt;
&lt;p&gt;Git 的数据模型确保了项目内的每一个字节，每一个 bit 的一致性。提交的每一个文件都会使用校验和计算摘要，检出的时候也使用这个摘要值。没有任何可能会出现从仓库中获取的内容跟你存储的内容有任何差异。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-coding-net-production-pp.qbox.me/3660fd1f-188f-482e-a637-a14accc0d250.png" title="" alt="图片"&gt; &lt;/p&gt;

&lt;p&gt;在不改变 ID（校验和）的情况下也不可能出现改变任何文件，日期，提交说明或者任何其他在 Git 仓库中的数据。这就意味着，如果你有一个 commit ID，你不但可以确定这个版本的代码跟他提交的时候是一模一样的，而且这个版本之前的历史也没有发生任何改变。&lt;/p&gt;

&lt;p&gt;大多数中央存储的版本控制系统默认不提供这样的校验整合。&lt;/p&gt;
&lt;h3 id="暂存区域"&gt;暂存区域&lt;/h3&gt;
&lt;p&gt;不像其他系统，Git 有一个概念叫做“暂存区域”或者“index”。这是一个在提交执行之前的临时的区域可以用来格式化和审阅改动内容的。&lt;/p&gt;

&lt;p&gt;一个 Git 优于其他系统的功能是我们可以快速的暂存一些改动的文件，在工作目录中只提交部分改动的文件，或者文件改动的部分内容，以及在提交的时候在命令行里列出改动的文件列表。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-coding-net-production-pp.qbox.me/252ffa65-f609-4ea7-acba-f159c2c912c3.png" title="" alt="图片"&gt; &lt;/p&gt;

&lt;p&gt;暂存区域允许你仅仅暂存部分的文件改动，在你意识到你忘了提交其中一个文件之前，对文件进行两个逻辑上不相关的修改的日子已经一去不复返了。现在你可以仅仅暂存你当前提交需要改动的文件，其他的改动在下次提交再暂存。这个特性可以扩展到对文件进行的任何更改。&lt;/p&gt;

&lt;p&gt;当然，Git 也允许你忽略掉暂存区域这个过程，你可以轻松的在 commit 命令后面添加 '-a' 选项来直接将所有改动提交。Git 会自动帮你先暂存到暂存区域，再执行提交。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-coding-net-production-pp.qbox.me/7fb2eea0-11f9-4d8b-bde8-83d5222bca05.png" title="" alt="图片"&gt; &lt;/p&gt;
&lt;h3 id="免费和开源"&gt;免费和开源&lt;/h3&gt;
&lt;p&gt;Git 是一个使用 GNU GPL2.0 协议的开源软件。Git 选择 GPLv2 来确保你可以自由的分享和改造自由软件，而且能确保使用它的任何用户都是自由免费的。&lt;/p&gt;

&lt;p&gt;然而，我们确实也保留了“Git”和 &lt;a href="https://git-scm.com/downloads/logos" rel="nofollow" target="_blank" title=""&gt;logos&lt;/a&gt; 避免争议。欲知详情请看我们的&lt;a href="https://git-scm.com/trademark" rel="nofollow" target="_blank" title=""&gt;商标&lt;/a&gt;政策。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;译者注&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;本文译自 &lt;a href="https://git-scm.com/about" rel="nofollow" target="_blank" title=""&gt;Git 官方网站的关于说明&lt;/a&gt;&lt;/p&gt;</description>
      <author>coding</author>
      <pubDate>Thu, 09 Feb 2017 13:20:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/32280</link>
      <guid>https://ruby-china.org/topics/32280</guid>
    </item>
    <item>
      <title>本地执行 git branch --remote 和 gitlab 上的分支列表不一致</title>
      <description>&lt;p&gt;执行 &lt;code&gt;git branch --remote&lt;/code&gt; 能看到一些 'origin/xxx' 分支，但是在 gitlab 上就看不到，这算是 gitlab 的问题 还是 git 的问题？
还是说本地有 remote branches 的缓存？&lt;/p&gt;</description>
      <author/>
      <pubDate>Mon, 05 Dec 2016 18:15:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/31804</link>
      <guid>https://ruby-china.org/topics/31804</guid>
    </item>
    <item>
      <title>GitHub 上不上去了，　好像是被攻击了．．．．．</title>
      <description>&lt;p&gt;大家什么情况．．．&lt;/p&gt;</description>
      <author>lilijreey</author>
      <pubDate>Tue, 04 Oct 2016 14:19:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/31234</link>
      <guid>https://ruby-china.org/topics/31234</guid>
    </item>
    <item>
      <title>GitLab 8.11.7 中文版，有感兴趣的么？</title>
      <description>&lt;h2 id="说明"&gt;说明&lt;/h2&gt;
&lt;p&gt;Gitlab 应该是目前 Git 自托管服务最好的选择，国内应该也有很多团队选择 Gitlab。对 Gitlab 有所了解的朋友应该知道，Gitlab 多语言这块考虑不多，语言与程序都混在一起的，可以说是硬编码。也许在 Gitlab 看来，他们的目标用户都是码农，码农应该天生就能阅读英文，所以没有必要做多语言的考虑。我个人也觉得汉化不是一个刚性需求，所以。。。有需要或感兴趣的同学就用吧。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;注意：请先将你的 gitlab 升级到 8.11.7&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="系统截图"&gt;系统截图&lt;/h2&gt;
&lt;p&gt;老规矩废话少说，上图：&lt;/p&gt;

&lt;p&gt;1、首页 (项目列表页) 
&lt;img src="https://dn-phphub.qbox.me/uploads/images/201609/23/2700/O773NdJGAB.png" title="" alt="file"&gt;&lt;/p&gt;

&lt;p&gt;说明：gitlab 从 8.9 之后就把 sidebar 的 icon 去掉了，感觉有些别扭，我给加上了。&lt;/p&gt;

&lt;p&gt;2、管理后台&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-phphub.qbox.me/uploads/images/201609/23/2700/JRV1Cnvng6.png" title="" alt="file"&gt;&lt;/p&gt;

&lt;p&gt;3、合并请求&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-phphub.qbox.me/uploads/images/201609/23/2700/HofvWPlMJh.png" title="" alt="file"&gt;&lt;/p&gt;

&lt;p&gt;4、群组&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-phphub.qbox.me/uploads/images/201609/23/2700/kMq82HW3C1.png" title="" alt="file"&gt;&lt;/p&gt;
&lt;h2 id="安装手册"&gt;安装手册&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git clone https://github.com/phecho/gitlabhq&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd gitlabhq&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;diff -uNr /opt/gitlab/embedded/service/gitlab-rails/app app/ &amp;gt; ../zh_CN.diff&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;patch -d /opt/gitlab/embedded/service/gitlab-rails/app/ -p1 &amp;lt; ../zh_CN.diff&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gitlab-ctl restart&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;大功告成！&lt;/p&gt;
&lt;h2 id="Docker"&gt;Docker&lt;/h2&gt;
&lt;p&gt;进入 docker 目录&lt;/p&gt;

&lt;p&gt;&lt;img src="https://dn-phphub.qbox.me/uploads/images/201609/23/2700/pA4t3lRb95.png" title="" alt="file"&gt;&lt;/p&gt;
&lt;h2 id="仓库地址"&gt;仓库地址&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/phecho/gitlabhq" rel="nofollow" target="_blank"&gt;https://github.com/phecho/gitlabhq&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;个人能力有限，错误在所难免，欢迎指正，并邀请感兴趣的同学一起来完善。&lt;/p&gt;</description>
      <author>phecho</author>
      <pubDate>Fri, 23 Sep 2016 09:11:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/31142</link>
      <guid>https://ruby-china.org/topics/31142</guid>
    </item>
  </channel>
</rss>
