利用 Topgit 对 Nutch 定制开发进行代码管理(一)

2 篇文章 0 订阅

对开源项目的定制开发,源代码管理是一个人抓狂的问题。随着主代码版本的推进和定制版本开发越来越远,要使用主代码中新版本的特性,势必要将主代码中的新版本合并到自己的定制版本中。这时可能出现大量的冲突,而且冲突的数量会随着时间的推移和两个分支上代码量的增加而显著增多。解决这些冲突要耗费大量的时间和精力,而且可能会弄得一塌糊涂,令人望而生畏。然而这个过程不会结束,每隔一段时间就会由于主代码版本发布新的版本,而要执行一次这样的过程,各种分支可能变得非常难于管理。

除非只针对主代码的某一特定版本进行定制开发,而不在乎主代码将来发布新版本时,是否合并到定制版本中。这样虽然不会带来合并冲突的问题,但会损失主代码新版本中的新特性和 bug 修复,我想很少有人会这样做。

对于 Nutch 的应用,大多数应用都会对其进行各种针对性的定制开发,使其适用于自己的应用场景。因此,对 Nutch 的定制开发同样会面临上述令人恐惧的过程。


对于 Git 管理的开源项目,尤其是在 GitHub 上发布的项目,使用 TopGit 来管理定制开发的源代码是个很好的选择。它能有效管理主代码版本和定制开发版本的依赖关系,通过补丁队列(patch queue)管理开发历程,使得基于开源项目的定制开发过程非常顺畅,将程序员从繁重的代码管理工作中解脱出来,将主要精力和时间应用于项目开发中。


对 Nutch 的定制开发,或者说对任意开源项目的定制开发,TopGit 都能发挥其强大的分支依赖管理功能。TopGit 并不能避免冲突,但会更好地管理定制开发的代码分支和该分支所依赖的基准分支。

 

1. TopGit (TopGit -- A different patch queue manager):
-------------------------------------------------------------------------------------------------------------------------
TopGit 是 Topic Git 的缩写,是 shell 脚本开发的辅助工具,对 Git 功能进行了扩展,用于管理特性分支的开发。从其本身的定义可以看出,TopGit 是一个不同的补丁队列管理工具(TopGit -- A different patch queue manager),以区别于其他的补丁管理工具。


项目本身的描述原文如下:
-------------------------------------------------------------------------------------------------------------------------
DESCRIPTION:

TopGit aims to make handling of large amounts of interdependent topic branches easier. In fact, it is designed especially
for the case where you maintain a queue of third-party patches on top of another (perhaps Git-controlled) project and want
to easily organize, maintain and submit them -- TopGit achieves that by keeping a separate topic branch for each patch and
providing some tools to maintain the branches.


-------------------------------------------------------------------------------------------------------------------------
TopGit 没有所谓的主代码分支(master) 和卖主分支 (Vender Branch)的概念。只有特性分支和基准分支的概念,将定制开发的分支称为特性分支,在 TopGit 的术语中,称为 topic branch,即开发者进行定制开发所在的分支。而基准分支则是特性分支所依赖的分支,并且一个特性分支可以依赖多个分支,而这些依赖分支共同组成了该特性分支的基准分支。TopGit 在一个特性分支上同时维护这两类分支,通过特性分支工作区根目录下的 .topdeps 文件,将二者联系起来,.topdeps 文件是 TopGit 的关键所在。这就是 TopGit 最核心的数据,也是 TopGit 有别于原生 Git 的地方。

TopGit 为特性分支引入了基准分支的概念,并以此管理特性分支间的依赖,让上游版本向特性分支的迁移变得非常简单。

TopGit 的主要特点:

    □ 上游的原始代码位于开发主线 master 分支上,而每一个定制开发都对应于一条 Git 特性分支,通过原生 Git 的
        
        refs/heads/t/feature_name
        
      引用维护。
      
    □ 基准分支是特性分支所依赖的分支,可能由一个或多个分支组成,由特性分支工作区根目录下 .topdeps 文件管理,通过原生 Git

        refs/top-bases/t/feature_name
      
      引用维护。
    
    □ 特性分支之间的依赖关系可以任意设定分支之间的依赖:多重依赖,特性依赖等。
    □ 特性分支和其依赖的分支可以导出为 Quilt 格式的补丁列表。
    □ 因为针对某一需求的定制开发限定在特定的特性分支上,因此可以多人协同参与,和正常的 Git 开发一样。

TopGit 在每个特性分支工作区的根目录引入了 2 个文件,用于记录分支的依赖及关于此分支的说明:

    □ .topdeps 文件:记录了该分支所依赖的分支列表。
        该文件通过 tg create 命令在创建特性分支时自动创建,或者通过 tg depend add 命令来添加新依赖。
    
    □ .topmsg 文件:记录该分支的描述信息。
        该文件通过 tg create 命令在创建特性分支时创建,可以手动编辑。
        
    


2. TopGit 设计原理
-------------------------------------------------------------------------------------------------------------------------
TopGit 的设计围绕着如下三个主要的原则:

● TopGit 是基于 Git 之上的一个管理层,它尽可能地使这个管理层简单、轻便。使用者仍然使用 Git 来维护索引和提交,TopGit 只会  自动执行一些必要的任务,多一点都不做。

● TopGit 时刻保持版本库的历史记录。它从不重写历史,所有的版本库元数据都由 Git 跟踪,保持平稳,从不做任何篡改。在清理历史时,  最好使用一个单一的点,这个点包含在上游项目中(upstream project),在本地,将会看到补丁如何演变,并且很容易恢复到以前的版本。
 
● TopGit 特别设计工作于分布式环境中。可以有多个 TopGit 感知的(TopGit-aware) 仓库实例,并平稳地保持所有的实例都是最新的,以  及在实例之间传递变更。

 
TopGit 的主要应用场景是用于跟踪第三方补丁(patch),而每一个补丁实际上就是一个单独的特性分支(a single topic branch)。

为了灵活地适应更为复杂的场景,比如跟踪多个补丁分支,而这些分支中有些彼此独立,而有些存在依赖关系,TopGit摈弃了传统的 Quilt 补丁序列概念,而是允许以 DAG 图的形式自由地打补丁。就像 Git 历史本身那样,只有一个更高的级别。目前,使用者必须手动指定当前分支所依赖的补丁分支,但将来 TopGit 会隐式地自动执行这类操作。


术语:
    
base :多个依赖的联合或者合并称为这个特性分支(topic branch)的基准分支,即 base 。

 

3. 约定
-------------------------------------------------------------------------------------------------------------------------
使用 TopGit 来管理 TopGit 特性分支时有几个通用的约定,这些约定都不是强制性的,只是使用过程中约定俗成的建议。

对于一个 TopGit 分支,有 3 个典型的用法:

    ① [PATCH]    :常规的 TopGit 分支,表示一个单独的分支,称为补丁分支("patch" TopGit branches)。
    ② [BASE]     :没有依赖的空("empty") TopGit 分支(即有一个空的 .topdeps 文件),表示一个其它常规 TopGit 分支依赖的基准。 称为基准分支("base" TopGit branches)。不要与 refs/top-bases/..refs 混淆。
    ③ [STAGE]    :也是空的("empty") TopGit 分支,用于暂时地将几个其它 TopGit 分支放在一起,可以将它们一起使用或测试。称为 暂存分支("stage" TopGit branches)

一个空的("empty") TopGit 分支本身不会有任何改变,它可以有依赖(例如 "stage" 分支有自己的依赖,而 "base" 分支则没有依赖)。
tg summary 命令的输出显示空分支为一个 "0" 标记。没有废除的常规 "patch" 分支、 "base" 分支和 "stage" 分支可以归为这种类别中。
(被废除的分支通常会在 tg summary 命令输出中忽略,但如果给 tg summary 命令提供显示的参数,也可以显示。但输出的消息行是不正确的,因为一个被废除的分支没有自己的 .topmsg 文件。)


"patch" 分支名称通常以 "t/" 开始,而 "base" 和 "stage" 分支名称通常没有。

"base" 分支通过 tg create 的 --base 选项(或者 --no-deps)创建,会自动生成 "[BASE]" 消息前缀,而不是 "[PATCH]" 消息前缀。

"stage" 分支的创建与常规 patch 分支的创建类似,除了它唯一处理的改变就是添加和删除依赖。其主题(subject) 前缀必须手动修改为
"[STAGE]" 以反映其目的。

由于 "base" 和 "stage" 分支通常在它们的 .topmsg 文件中只有一个 "Subject:" 行,因此非常容易地通过 tg create 命令的--topmsg 选项创建。

"stage" 和 "base" 分支的使用完全是可选的,然而,没有 "stage" 分支的用法,很难一次性一起测试多个互相独立的 patch 分支。
"base" 分支仅仅是一个提供更显示控制的便捷工具,当一个公用的 base 更新时,多个常规分支也获得的更新显示在 tg summary 输出和 tg remote --populate 设置中。

当使用 tg tag 命令创建标记以记录一个或多个 TopGit 分支的当前状态时,标记(tag) 的名称通常以 "t/" 开始。

 

 

4. TopGit 安装
-------------------------------------------------------------------------------------------------------------------------
TopGit 的开发还在继续,开发社区非常活跃,随着 Git 新版本的发布,TopGit 不断加入新的特性,目前版本为 topgit-0.19.11,最近一次提交发生在 2018 年4月26日。 TopGit 代码在 GitHub 上维护:

    
    https://github.com/mackyle/topgit

 

4.1 系统要求
-------------------------------------------------------------------------------------------------------------------------
TopGit 由一系列 POSIX 兼容的 shell 脚本组成,因此需要系统内存在 POSIX 兼容的实用工具:sed, awk, cat 等等。

为了使用 git worktree add 命令,要求 Git 版本至少为 Git 2.5.0。当前 Git 的最新发布版本为 Git 2.18.0

TopGit 脚本需要预编译和安装,因此需要确保系统上有 make 工具和 perl 工具。

 

4.2 安装 TopGit
-------------------------------------------------------------------------------------------------------------------------
TopGit 依赖于 Git,因此安装 TopGit 之前,首先应先安装最新版本的 Git。


    NOTE:
    ---------------------------------------------------------------------------------------------------------------------
    本文所有描述基于 Linux 系统环境,其它操作系统大同小异,不再单独描述。测试机器为 Fedora 24 系统,其它系统可能会需要替换 为对应的命令,例如在 CentOS 系统上, dnf 替换为 yum 命令,等等。
    
    
    
    

4.2.1 安装 Git
-------------------------------------------------------------------------------------------------------------------------
Git 源代码也在 GitHub 上维护:

    https://github.com/git/git
    
下载或克隆 Git 源代码,进入代码目录执行:

    [devalone@devalone git]$ make configure
    [devalone@devalone git]$ ./configure --prefix=/usr
    [devalone@devalone git]$ make all doc

    [devalone@devalone git]$ sudo make install install-doc install-html

    [devalone@devalone git]$ git --version
    git version 2.18.0.547.g1d89318c4

 

4.2.2 安装 TopGit
-------------------------------------------------------------------------------------------------------------------------
进入 TopGit 源码维护网址:

    https://github.com/mackyle/topgit
    
下载或克隆 TopGit 源代码,进入代码目录执行:

    [devalone@devalone repo]$ git clone https://github.com/mackyle/topgit.git

    [devalone@devalone topgit]$ make
    [devalone@devalone topgit]$ sudo make prefix=/usr/local install
    
    [devalone@devalone topgit]$ tg --version
    TopGit version 0.19.11-5-g4656
    
    
TopGit 被安装到了 /usr/local/bin 目录内。


将 contrib/tg-completion.bash 文件复制到 /etc/,然后在 /etc/profile 文件中通过 source 命令调用该文件。这是 TopGit 自动
补全脚本。

默认安装没有安装 html 帮助文件,需要单独安装,过程如下:

    [devalone@devalone topgit]$ make html
    [devalone@devalone topgit]$ sudo make prefix=/usr/local install-html
    
    install -d -m 755 "/usr/local/share/topgit"
    install -m 644 topgit.html tg-help.html tg-status.html tg-tg.html tg-annihilate.html tg-base.html tg-checkout.html\
    tg-contains.html tg-create.html tg-delete.html tg-depend.html tg-export.html tg-files.html tg-import.html tg-info.html\
    tg-log.html tg-mail.html tg-migrate-bases.html tg-next.html tg-patch.html tg-prev.html tg-push.html tg-rebase.html\
    tg-remote.html tg-revert.html tg-shell.html tg-summary.html tg-tag.html tg-update.html "/usr/local/share/topgit"

帮助文档被安装到了 /usr/local/share/topgit 目录内。


如果系统内因缺少 rst2html 命令而无法编译,需要执行如下指令安装:

    [devalone@devalone topgit]$ sudo pip install rst2html5
    [devalone@devalone topgit]$ which rst2html5
    /usr/bin/rst2html5
    [devalone@devalone topgit]$ sudo ln -s /usr/bin/rst2html5 /usr/bin/rst2html

rst2html 安装完成后在执行 make html 编译即可通过。

 

 

5. TopGit 的使用
-------------------------------------------------------------------------------------------------------------------------
TopGit 为管理特性分支引入的配置文件和基准分支都是和 Git 兼容的。

□ 在 refs/top-bases/ 名称空间下的引用,用于记录特性分支的基准分支
□ 在特性分支的工作区根目录下引入两个文件: .topdeps 和 .topmsg 文件,用于记录分支的依赖和说明。
□ 引入新的钩子脚本 hooks/pre-commit, 用于在提交时检查分支依赖有没有发生循环等。


5.1 TopGit 命令工具
-------------------------------------------------------------------------------------------------------------------------
TopGit 的命令为 tg, 一般格式如下:

    tg [global options] <subcommand> [<subcommand option/argument>...]

    
说明:在子命令 <subcommand> 后面可跟子命令相关的参数。

    
5.1.1 global options :[-C <dir>]... [-r <remote> | -u] [-c <name>=<val>]... [--[no-]pager]
-------------------------------------------------------------------------------------------------------------------------
可设置如下全局选项:

    -C <dir>        :在进行任何操作前切换到 <dir> 目录
    -r <remote>        :用于指定远程版本库 <remote>, 会覆盖 topgit.remote 的设置而使用 <remote>    的值
    -u                :不使用 topgit.remote 属性的设置
    -c <name=val>    :向 git 传递配置选项,可能由多个
    -w <tgtag>        :使用 tg tag <tgtag> 激活 wayback machine
    --no-pager        :禁用任何 pager (包括 TopGit 和 Git)
    --pager            :启用 pager 使用 (或者使用 -p)
    --top-bases        :显示全部 top-bases 前缀的引用并退出
    --exec-path        :显示子命令脚本文件所在的位置并退出
    --help            :显示简要用法帮助并退出(或者使用 -h)

 

 

5.1.2 tg 子命令
-------------------------------------------------------------------------------------------------------------------------
    


● tg annihilate:
-------------------------------------------------------------------------------------------------------------------------
标记一个 TopGit 控制的分支为失效。

用法:
    tg [...] annihilate [-f] [--no-update] [<name>]

    Make a commit on the current or given TopGit-controlled topic
    branch that makes it equal to its base, including the presence or
    absence of .topmsg and .topdeps.  Annihilated branches are not
    displayed by ``tg summary``, so they effectively get out of your
    way.  However, the branch still exists, and ``tg push`` will
    push it (except if given the ``-a`` option).  This way, you can
    communicate that the branch is no longer wanted.


● tg base:
-------------------------------------------------------------------------------------------------------------------------
显示一个或多个 TopGit 分支的基准提交。即打印出每一个给定的特性分支的基准分支的提交,如果没有给出分支名称,则打印出当前分支
的基准分支提交。

用法:
    tg [...] base [--short[=n] | --no-short] [--] [branch...]

如果给定的分支名称不是一个 TopGit 分支,则打印出错误消息并退出 1。

 

● tg checkout:
-------------------------------------------------------------------------------------------------------------------------
带有名称的 git checkout 命令的快捷方式。

Usage: tg [...] checkout [--iow] [-f] [-b <branch>] (next | prev) [<steps>]
   Or: tg [...] checkout [--iow] [-f] (next | prev) -a
   Or: tg [...] checkout [--iow] [-f] [goto] [--] <pattern> | --series[=<head>]

    Switch to a topic branch.  You can use ``git checkout <branch>``
    to get the same effect, but this command helps you navigate
    the dependency graph, or allows you to match the topic branch
    name using a regular expression, so it can be more convenient.

    The ``--branch`` (or ``-b`` or ``--branch=<name>``) option changes
    the default starting point from ``HEAD`` to the specified branch.

    For the "next" and "previous" commands, the ``<steps>`` value may
    be ``--all`` (or ``-a``) to take "As many steps As possible" or
    "step ALL the way" or "ALL steps at once" (or make something better
    up yourself).

 

● tg contains:
-------------------------------------------------------------------------------------------------------------------------
哪个 TopGit-controlled 分支包含指定的提交。

用法: tg [...] contains [-v] [-r] [--ann] [--no-strict] [--] <committish>

    Search all TopGit-controlled branches (and optionally their remotes)
    to find which TopGit-controlled branch contains the specified commit.

    This is more than just basic branch containment as provided for by the
    ``git branch --contains`` command.  While the shown branch name(s)
    will, indeed, be one (or more) of those output by the
    ``git branch --contains`` command, the result(s) will exclude any
    TopGit-controlled branches from the result(s) that have one (or more)
    of their TopGit dependencies (either direct or indirect) appearing in
    the ``git branch --contains`` output.

 


● tg create:
-------------------------------------------------------------------------------------------------------------------------
用于创建一个新的 TopGit-controlled 特性分支。

用法:
    tg [... -r remote] create [-q] [-m <msg> | -F <file>] [--topmsg <msg> | --topmsg-file <file>] [--no-edit]\
    [--no-commit | --no-update] [--base] [<name> [<dep>...|-r [<rname>]] ]   


其中:
    <name>        :是新想特性分支名称,必须提供。一般约定俗成,<name> 以 t/ 为前缀开头表名此分支是一个 TopGit 特性分支。
    <dep>...    :是可选的一个或多个依赖分支名称。如果不听依赖分支名称,则使用当前分支为唯一依赖。
    -r [<rname>]:将远程分支作为依赖分支。
    
默认情况下,tg create 会在一个新的 .topmsg 文件上打开文本编辑器,然后自动使用合适的默认提交消息提交.topmsg 和.topdeps 文件


使用 -m (or --message) or -F (or --file) 选项修改提交消息。
使用 --no-ccmmit (or -n) 选项禁止自动提交。
使用 --no-edit 禁止在新的 .topmsg 文件上运行编辑器(不会禁止自动提交,除非给定 --no-commit)。或者为 .topmsg 文件通过
    --topmsg or --topmsg-file 选项提供显示的值。不管哪一种情况, .topmsg 的内容会被自动格式化为具有 Subject: 行的形式。


● tg delete:
-------------------------------------------------------------------------------------------------------------------------
干净地删除一个 TopGit 特性分支及其基准分支。

用法:
    tg [...] delete [-f] <name>

默认只删除没有改动的分支,即标记为 "0" 的分支,除非是有 -f 参数进行强制删除。

 

● tg depend:
-------------------------------------------------------------------------------------------------------------------------
向一个 TopGit-controlled 分支添加一个新的依赖。本意是修改 TopGit 特性分支的依赖,应该有多个子命令,但目前只支持 add 命令。

用法:
    tg [...] depend add [--no-update | --no-commit] <name>...

    <name>    :要添加的依赖分支名称,可以添加多个分支。
    
    该命令将 <name> 参数指定的分支加入到 .topdeps 文件中,并将 <name> 分支向该特性分支及该特性分支的基准分支进行合并操作。

 

● tg export:
-------------------------------------------------------------------------------------------------------------------------
导出 TopGit 分支补丁到文件或者一个分支中,便于向上游贡献。可以导出为 Quilt 格式的补丁列表,或者顺序提交到另外的分支中去。

用法:  tg [...] export [--collapse] [--force] [-s <mode>] <newbranch>
   Or: tg [...] export --linearize [--force] [-s <mode>] <newbranch>
   Or: tg [...] export --quilt [--force] [-a | --all | -b <branch>...]
                [--binary] [--flatten] [--numbered] [--strip[=N]] <directory>

这个命令有三种导出方法:
    □ 将所有 TopGit 特性分支压缩为一个,提交到一个新的特性分支:
        
        tg [...] export [--collapse] [--force] [-s <mode>] <newbranch>
    
    □ 将所有的 TopGit 特性分支安装线性顺序提交到一个新的分支中:
    
        tg [...] export --linearize [--force] [-s <mode>] <newbranch>
        
    □ 将指定的 TopGit 分支(一个或多个)及其依赖分支转换为 Quilt 格式的补丁,保存到指定的目录中:
    
        tg [...] export --quilt [--force] [-a | --all | -b <branch>...]
                [--binary] [--flatten] [--numbered] [--strip[=N]] <directory>

        其中:
            -a | --all 导出所有分支
             -b <branch>... :列出要导出的分支列表
            

● tg files:
-------------------------------------------------------------------------------------------------------------------------
显示由一个 TopGit 分支改变的文件。

用法:
    tg [...] files [-i | -w] [<name>]
    
Options:
    -i            list files based on index instead of branch
    -w            list files based on working tree instead of branch


● tg help:
-------------------------------------------------------------------------------------------------------------------------
显示 TopGit 帮助选项。

TopGit 集成了丰富的帮助工具,为用户提供各种形式的帮助信息:

    [devalone@devalone ~]$ tg help
    TopGit version 0.19.11-5-g4656 - A different patch queue manager
    Usage: tg [-C <dir>] [-r <remote> | -u] [-c <name>=<val>] [--[no-]pager|-p] [-w [:]<tgtag>] (annihilate|base|\
    checkout|contains|create|delete|depend|export|files|import|info|log|mail|next|patch|prev|push|rebase|remote|\
    revert|shell|st[atus]|summary|tag|update) ...
       Or: tg help [-w] [<command>]
    Use "tg help tg" for overview of TopGit

    
help 命令后跟子命令,可以获得该子命令的详细帮助信息。

    [devalone@devalone ~]$ tg help tg
    =========================================
    TopGit -- A different patch queue manager
    =========================================


    DESCRIPTION
    -----------

    TopGit aims to make handling of large amounts of interdependent topic
    ...

由 lynx 工具显示的 web 形式帮助信息:
    
    [devalone@devalone ~]$ tg help -w tg

 

● tg import:
-------------------------------------------------------------------------------------------------------------------------
导入提交到一个不同的 TopGit 分支。即将分支的提交转换为 TopGit 特性分支,指定范围的每个提交都转换为一个特性分支,各个特性
分支之间线性依赖。

用法:
    tg [...] import [-d <base-branch>] ([-p <prefix>] <range>... | -s <name> <commit>)

如果不使用 -d 参数,特性分支则以当前分支为依赖。特性分支名称自动生成,使用约定的 t/ 作为前缀,也可以通过 -p 参数指定其他
前缀。可以通过 -s 参数设定特性分支的名称。

 

● tg info:
-------------------------------------------------------------------------------------------------------------------------
显示当前或指定特性分支的状态信息。

用法:
    tg [...] info [-i | -w] [--heads | --leaves | --series[=<head>]] [<name>]
Or: tg [...] info [-i | -w] [--deps | --dependencies | --dependents] [<name>]


    <name> :可选的分支名称,如果没有指定,则显示当前特性分支的信息。
    

● tg log:
-------------------------------------------------------------------------------------------------------------------------
显示特定 TopGit 特性分支的 git log,并忽略合并引入的提交。

用法:
    tg [...] log [--compact] [<name>] [--] [<git-log-option>...]
    
tg log 命令实际上是对原生 git log 命令的封装。这个命令通过 --no-merges 和 --first-parent 参数调用 git log, 虽然屏蔽了大量
因和依赖分支合并而引入的依赖分支的提交日志,但同时也屏蔽了合并到该特性分支的其它贡献者的提交。

 

● tg mail:
-------------------------------------------------------------------------------------------------------------------------
使用 tg patch 的输出执行  git send-email 命令的快捷方式。即将当前分支或指定特性分支的补丁以邮件的形式发送出去。

用法:
    tg [...] mail [-s <send-email-args>] [-r <reference-msgid>] [-i | -w] [<name>]

其中:
    -s    :选项用于向 git send-email 命令传递参数(选项参数需要用双引号括起来)。
    -r    :选项引用回复邮件的 ID 以便正确地生成 in-reply-to 邮件头。
    
邮件中的目的地址从补丁文件头中的 To, Cc 和 Bcc 等字段获取。

注意:此命令可能会发送多封邮件,可以通过如下设置在调用 git send-email 命令发送邮件时进行确认:

    git config sendemail.confirm always
    


● tg migrate-bases:
-------------------------------------------------------------------------------------------------------------------------
将 top-bases 传输到新的位置。

用法: tg [...] migrate-bases (--dry-run | --force) [--no-remotes | --remotes-only]

    Transition top-bases from old location to new location.

 

● tg next:
-------------------------------------------------------------------------------------------------------------------------
显示直接依赖于一个 TopGit 分支的所有分支,即显示当前或指定特性分支被其它哪些特性分支所依赖。

用法: tg [...] next [-v] [-i | -w] [-a | -n <steps>] [<name>]

        Output tne "next" branch(es) in the patch series containing the current
        or named branch.  The "next" branch(es) being one step away by default.

        Options:
          -i            show dependencies based on index instead of branch
          -w            show dependencies based on working tree instead of branch
          -n <steps>    take ``<steps>`` "next" steps (default 1)
          --all         take as many "next" steps as possible (aka ``-a``)
          --verbose     show containing series name(s) (aka ``-v``)

 

● tg patch:
-------------------------------------------------------------------------------------------------------------------------
为一个 TopGit 分支生成一个补丁文件。即通过比较特性分支及其基准分支的差异,显示该特性分支的补丁。

用法:
    tg [...] patch [-q] [-i | -w] [--binary] [<name>] [--] [<git-diff-tree-option>...]

其中:
    -i    :显示暂存区(即索引中)和基准分支的差异。
    -w    :显示工作区和基准分支的差异。
    


● tg prev:
-------------------------------------------------------------------------------------------------------------------------
显示一个分支的非失效 TopGit 依赖,即显示当前或指定特性分支所依赖的分支名。

Usage: tg [...] prev [-v] [-i | -w] [-a | -n <steps>] [<name>]

        Output the "previous" branch(es) in the patch series containing the
        current or named branch.  The "previous" branch(es) being one step
        away by default.

        Options:
          -i            show dependencies based on index instead of branch
          -w            show dependencies based on working tree instead of branch
          -n <steps>    take ``<steps>`` "previous" steps (default 1)
          --all         take as many "previous" steps as possible (aka ``-a``)
          --verbose     show containing series name(s) (aka ``-v``)

 

● tg push:
-------------------------------------------------------------------------------------------------------------------------
在 TopGit 分支和依赖上执行 git push。即将 TopGit 特性分支及对应的基准分支推送到远程版本库。

用法:
    tg [...] push [--dry-run] [--force] [--no-deps] [--tgish-only] [-r <pushRemote>] [-a | --all | <branch>...]

其中:
    <branch>...    :用于指定要推送给远程服务器的分支列表,可以有多个。如果省略则推送当前分支。
    -a | --all    :将所有的 TopGit 特性分支推送到远程版本库。
    --dry-run    :选项用于测试推送的执行效果,而不是真正执行。
    --no-deps    :不推送依赖分支,默认是推送依赖分支。
    --tgish-only:只推送 TopGit 特性分支,默认推送指定的所有分支。
    -r <pushRemote>:可选项。指定远程名称,如果没有指定,则默认使用 topgit.pushRemote 配置信息,如果该配置信息没有设置,
                    则使用 topgit.remote 配置。即为之前通过 tg remote 设置的 'origin' 值。查看 .git/config 配置文件可获得
                    具体配置:
                    
                    [devalone@devalone nutch]$ cat .git/config
                    [core]
                            repositoryformatversion = 0
                            filemode = true
                            bare = false
                            logallrefupdates = true
                    [remote "upstream"]
                            url = https://github.com/apache/nutch.git
                            fetch = +refs/heads/*:refs/remotes/upstream/*
                    [branch "master"]
                            remote = upstream
                            merge = refs/heads/master
                    [branch "nutch-2.x"]
                            remote = upstream
                            merge = refs/heads/2.x
                    [remote "origin"]
                            url = repo.sansovo.org:scm/vnutch
                            fetch = +refs/heads/*:refs/remotes/origin/*
                            fetch = +refs/top-bases/*:refs/remotes/origin/top-bases/*
                    [merge "ours"]
                            name = \"always keep ours\" merge driver
                            driver = touch %A
                    [topgit]
                            remote = origin

    
                    其中最后一项:
                    [topgit]
                            remote = origin
                            
                    即是这个选项的默认值。

● tg rebase:
-------------------------------------------------------------------------------------------------------------------------
如果冲突解决,自动继续执行 git rebase。

用法: tg [...] rebase (-m | --merge) [<git-rebase-arg>...]

    Provides a ``git rebase`` rerere auto continue function.  It may be
    used as a drop-in replacement front-end for ``git rebase -m`` that
    automatically continues the rebase when ``git rerere`` information is
    sufficient to resolve all conflicts.

    You have enabled ``git rerere`` haven't you?

    If the ``-m`` or ``--merge`` option is not present then ``tg rebase``
    will complain and not do anything.

    When ``git rerere`` is enabled, previously resolved conflicts are
    remembered and can be automatically staged (see ``rerere.autoUpdate``).

    However, even with auto staging, ``git rebase`` still stops and
    requires an explicit ``git rebase --continue`` to keep going.

    In the case where ``git rebase -m`` is being used to flatten history
    (such as after a ``tg export --collapse`` prior to a
    ``git format-patch``), there's a good chance all conflicts have already
    been resolved during normal merge maintenance operations so there's no
    reason ``git rebase`` could not automatically continue, but there's no
    option to make it do so.

    The ``tg rebase`` command provides a ``git rebase --auto-continue``
    function.

 

● tg remote:
-------------------------------------------------------------------------------------------------------------------------
将 remote 注册为由 TopGit 所控制,即为抓取/推送(fetching/pushing) TopGit 分支设置远程分支。This will create the namespace
for the remote branch bases and teach git fetch to operate on them. However, from TopGit 0.8 onwards you need to use
tg push, or git push --mirror, for pushing TopGit-controlled branches.


用法:
    tg [...] remote [--populate] [<remote>]

其中 <remote> 为远程版本库名称,默认为 origin. 执行 tg remote 命令会自动在版本库的配置文件中增加 refs/top-bases下引用同步
表达式。

如果使用 --populate 选项,除了会设置默认的 TopGit 远程版本库外,还会自动执行 git fetch 命令,然后在本地建立 TopGit 特性分
支和对应的基准分支。

当执行 tg 命令时,如果不用 -r remote 全局选项,则默认使用 origin 远程版本库。

如果版本库设置了多个远程版本库,要针对每一个远程版本库执行 tg remote <remote> 操作。但只能有一个远程版本库作为默认的特性
分支源,即通过 --populate 选项将其设置为默认的远程版本库。

 


● tg revert:
-------------------------------------------------------------------------------------------------------------------------
反转引用到存储在 tg tag 中的一个状态。

用法: tg [...] revert (-f | -i | -n) [-q] [--tgish-only] [--no-deps] [--no-stash] [--exclude <ref>...] \
    (<tagname> | --stash) [<ref>...]   Or: tg [...] revert [-l] [--no-short] [--hash] [--tgish-only] [(--deps | --rdeps)]\
    [--exclude <ref>...] (<tagname> | --stash) [(--[topgit-]heads] | <ref>...)]

    Provides the ability to revert one or more TopGit branches and their
    dependencies to a previous state contained within a tag created using
    the ``tg tag`` command.  In addition to the actual revert mode
    operation a list mode operation is also provided to examine a tag's ref
    contents.

 


● tg shell:
-------------------------------------------------------------------------------------------------------------------------
扩展的 wayback machine 模式。

用法:
    tg [...] -w [:]<tgtag> shell [--directory=<dirpath>] [-q] [--] [<arg>...]

    Enter extended `wayback machine`_ mode.

 

● tg status:
-------------------------------------------------------------------------------------------------------------------------
显示当前的 TopGit 状态。

用法: tg st[atus] [-v] [--exit-code]

    Our sophisticated status facility.  Similar to Git's status command
    but shows any in-progress update that's awaiting a merge resolution
    or any other on-going TopGit activity (such as a branch creation).

    With a single ``--verbose`` (or ``-v``) option include a short status
    display for any dirty (but not untracked) files.  This also causes all
    non file status lines to be prefixed with "## ".

    With two (or more) ``--verbose`` (or ``-v``) options, additionally
    show full symbolic ref names and unabbreviated hash values.

    With the ``--exit-code`` option the exit code will be non-zero if any
    TopGit or Git operation is currently in progress or the working
    tree is unclean.

 


● tg summary:
-------------------------------------------------------------------------------------------------------------------------
显示有关 TopGit 管理的特性分支列表和各个分支的状态信息。

用法:
    tg [...] summary [-t | --list | --heads[-only] | --sort | --deps[-only] | --rdeps | --graphviz] [-i | -w]\
    [--tgish-only] [--with[out]-(deps|related)] [--exclude branch]... [--all | branch...]


    -t | -l | --list : 显示特性分支列表
                    [devalone@devalone libgit2]$ tg summary -t
                    t/feature1
                    t/feature2
                    t/feature3
    
    --deps            : 显示 TopGit 特性分支,并显示特性分支的依赖
                    [devalone@devalone libgit2]$ tg summary --deps
                    t/feature1 master
                    t/feature2 master
                    t/feature3 t/feature1
                    t/feature3 t/feature2

    --sort            :按分支依赖的顺序显示分支列表,除了显示 TopGit 分支外,还会显示依赖的非 TopGit 分支。
    
                    [devalone@devalone libgit2]$ tg summary --sort
                    t/feature3
                    t/feature2
                    t/feature1
                    master

    --graphviz        :生成 GraphViz 格式文件,显示特性分支关系图。
    
                    [devalone@devalone libgit2]$ tg summary --graphviz | dot -T png -o topgit.png
                    
                    
                    在当前目录下生成 topgit.png 图片文件,可以使用任何一种图片查看器查看该文件上图形表示的依赖关系。
                    
    不带任何参数执行 tg summary 显示分支列表及状态,这是最常用的 TopGit 命令之一。
    
        [devalone@devalone libgit2]$ tg summary
                 t/feature1                     [PATCH] t/feature1
         0       t/feature2                     [PATCH] t/feature2
        >0       t/feature3                     [PATCH] t/feature3

    其中:
        □ 标识 ">"    :t/feature3 分支之前的大于号,用于标记当前所处的特性分支。
        □ "0"        : t/feature2 之前的数字 0 含义是该分支中没有变化。
        □ 标记 "D"    : 表明该分支处于过时状态( out-of-date )。可能是一个或多个依赖的分支包含了新的提交,尚未合并到此特性
                      分支。可以使用 tg info 命令查看到底是由于哪个依赖分支的改动导致该特性分支处于过时状态。
        □ 标记 "B"    : 表明该分支处于 Break 状态,即可能由于冲突未解决或其它原因导致该特性分支的基准分支(base) 相对该特性
                      分支的头 (head) 不匹配。例如 refs/top-bases/ 下的特性分支基准分支更新了,但是特性分支为完成更新。
        □ 标记 "!"    : 表明该特性分支所依赖的分支不存在。
        □ 标记 "l"    : 表明该特性分支只存在于本地,不存在于远程跟踪服务器。
        □ 标记 "r"    : 表明该特性分支既存在于本地,又存在于远程跟踪服务器,并且两者匹配。
        □ 标记 "L"    : 表明该特性分支,本地比远程跟踪服务器的新。
        □ 标记 "R"    : 表明该特性分支,远程跟踪服务器的比本地的新。
        □ 如果没有出现 "l/r/L/R" : 表明该版本库尚未设置远程版本库。
        □ 一般带有 "r" 是最常见的,也是最正常的。
        
    通过 tg remote 命令来设置远程版本库。
    

● tg tag:
-------------------------------------------------------------------------------------------------------------------------
创建记录当前 TopGit 分支状态的标记。

用法:
       tg [...] tag [-s | -u <key-id>] [-f] [-q] [--no-edit] [-m <msg> | -F <file>] [--tree <treeish>]\
       (<tagname> | --refs) [<branch>...]
   Or: tg [...] tag (-g | --reflog) [--reflog-message | --commit-message] [--no-type] [-n <number> | -number] [<tagname>]
   Or: tg [...] tag (--clear | --delete) <tagname>
   Or: tg [...] tag --drop <tagname>@{n}


    Creates a TopGit annotated/signed tag or lists the reflog of one.

    A TopGit annotated tag records the current state of one or more TopGit
    branches and their dependencies and may be used to revert to the tagged
    state at any point in the future.

    When reflogs are enabled (the default in a non-bare repository) and
    combined with the ``--force`` option a single tag name may be used as a
    sort of TopGit branch state stash.  The special branch name ``--all``
    may be used to tag the state of all current TopGit branches to
    facilitate this function and has the side-effect of suppressing the
    out-of-date check allowing out-of-date branches to be included.

 

● tg update:
-------------------------------------------------------------------------------------------------------------------------
使用相关联的依赖更新 TopGit 分支。使用各自依赖的分支或远程分支,更新当前的、指定的、或者所有的特性分支。即从依赖的分支获取
最新的提交合并到当前或指定的分支。同时在 refs/top-bases/ 命名空间下的特性分支的基准分支也会更新。

这个命令分两步执行:首先,将依赖中的改变合并到基准分支,然后基准分支合并到特性分支中。如果发生冲突,会在命令输出中指导如何
进行下一步操作。

用法:tg [...] update [--[no-]stash] [--skip-missing] ([<name>...] | -a [<pattern>...])
   Or: tg [...] update --base [-F <file> | -m <msg>] [--[no-]edit] [-f] <base-branch> <ref>
   Or: tg [...] update --continue | -skip | --stop | --abort


其中 <name>    :参数是可选的,用于指定要更新的特性分支。

   
 

5.2 使用 TopGit 工具管理特性分支
-------------------------------------------------------------------------------------------------------------------------
为了不给 Nutch 代码带来任何不必要的提交,随便在 GitHub 上找一个代码库作为实验,本文使用 libgit2 项目:

    [devalone@devalone git-test]$ git clone https://github.com/libgit2/libgit2.git
    正克隆到 'libgit2'...
    remote: Counting objects: 83033, done.
    remote: Compressing objects: 100% (23601/23601), done.
    remote: Total 83033 (delta 57777), reused 83003 (delta 57748), pack-reused 0
    接收对象中: 100% (83033/83033), 37.81 MiB | 236.00 KiB/s, 完成.
    处理 delta 中: 100% (57777/57777), 完成.

    [devalone@devalone git-test]$ cd libgit2/
    [devalone@devalone libgit2]$ ll
    总用量 156
    -rw-rw-r--.  1 devalone devalone   267 8月   4 16:46 api.docurium
    -rw-rw-r--.  1 devalone devalone  1288 8月   4 16:46 appveyor.yml
    -rw-rw-r--.  1 devalone devalone  1274 8月   4 16:46 AUTHORS
    drwxrwxr-x.  2 devalone devalone  4096 8月   4 16:46 ci
    drwxrwxr-x.  3 devalone devalone  4096 8月   4 16:46 cmake
    -rw-rw-r--.  1 devalone devalone 11144 8月   4 16:46 CMakeLists.txt
    -rw-rw-r--.  1 devalone devalone 51023 8月   4 16:46 COPYING
    drwxrwxr-x.  6 devalone devalone  4096 8月   4 16:46 deps
    drwxrwxr-x.  2 devalone devalone  4096 8月   4 16:46 docs
    drwxrwxr-x.  4 devalone devalone  4096 8月   4 16:46 examples
    drwxrwxr-x.  3 devalone devalone  4096 8月   4 16:46 fuzzers
    -rw-rw-r--.  1 devalone devalone  3105 8月   4 16:46 git.git-authors
    drwxrwxr-x.  3 devalone devalone  4096 8月   4 16:46 include
    -rw-rw-r--.  1 devalone devalone   764 8月   4 16:46 libgit2_clar.supp
    -rw-rw-r--.  1 devalone devalone   297 8月   4 16:46 libgit2.pc.in
    -rw-rw-r--.  1 devalone devalone   282 8月   4 16:46 package.json
    -rw-rw-r--.  1 devalone devalone 14570 8月   4 16:46 README.md
    drwxrwxr-x.  2 devalone devalone  4096 8月   4 16:46 script
    drwxrwxr-x.  8 devalone devalone 12288 8月   4 16:46 src
    drwxrwxr-x. 51 devalone devalone  4096 8月   4 16:46 tests
    [devalone@devalone libgit2]$ git status
    位于分支 master
    您的分支与上游分支 'origin/master' 一致。

    无文件要提交,干净的工作区
    [devalone@devalone libgit2]$ ll .git
    总用量 896
    drwxrwxr-x. 2 devalone devalone   4096 8月   4 16:43 branches
    -rw-rw-r--. 1 devalone devalone    263 8月   4 16:46 config
    -rw-rw-r--. 1 devalone devalone     73 8月   4 16:43 description
    -rw-rw-r--. 1 devalone devalone     23 8月   4 16:46 HEAD
    drwxrwxr-x. 2 devalone devalone   4096 8月   4 16:43 hooks
    -rw-rw-r--. 1 devalone devalone 853225 8月   4 16:54 index
    drwxrwxr-x. 2 devalone devalone   4096 8月   4 16:43 info
    drwxrwxr-x. 3 devalone devalone   4096 8月   4 16:46 logs
    drwxrwxr-x. 4 devalone devalone   4096 8月   4 16:43 objects
    -rw-rw-r--. 1 devalone devalone  21390 8月   4 16:46 packed-refs
    drwxrwxr-x. 5 devalone devalone   4096 8月   4 16:46 refs
    [devalone@devalone libgit2]$ ll .git/refs
    总用量 12
    drwxrwxr-x. 2 devalone devalone 4096 8月   4 16:46 heads
    drwxrwxr-x. 3 devalone devalone 4096 8月   4 16:46 remotes
    drwxrwxr-x. 2 devalone devalone 4096 8月   4 16:43 tags

● 利用 tg create 在当前 master 分支上创建特性分支 t/feature1 :
-------------------------------------------------------------------------------------------------------------------------
    [devalone@devalone libgit2]$ tg create t/feature1
    tg: automatically marking dependency on master
    tg: creating t/feature1 base from master...
    切换到一个新分支 't/feature1'
    [t/feature1 0bf9e20d7] tg create t/feature1
     2 files changed, 5 insertions(+)
     create mode 100644 .topdeps
     create mode 100644 .topmsg
    tg: Topic branch t/feature1 created.
    
特性分支 t/feature1 已成功创建,并切换到新创建的 t/feature1 分支上。

查看 git 引用:
    [devalone@devalone libgit2]$ ll .git/refs
    总用量 16
    drwxrwxr-x. 3 devalone devalone 4096 8月   4 16:57 heads
    drwxrwxr-x. 3 devalone devalone 4096 8月   4 16:46 remotes
    drwxrwxr-x. 2 devalone devalone 4096 8月   4 16:43 tags
    drwxrwxr-x. 3 devalone devalone 4096 8月   4 16:57 top-bases
    [devalone@devalone libgit2]$ ll .git/refs/top-bases/
    总用量 4
    drwxrwxr-x. 2 devalone devalone 4096 8月   4 16:57 t
    [devalone@devalone libgit2]$ ll .git/refs/top-bases/t/
    总用量 4
    -rw-rw-r--. 1 devalone devalone 41 8月   4 16:57 feature1


.git/refs/ 下新创建了 top-bases 目录,其下包含新创建的 t/feature1 引用:

    [devalone@devalone libgit2]$ cat .git/refs/top-bases/t/feature1
    64138b70e10b9812af8f944e83747aa51da9a920

    [devalone@devalone libgit2]$ git branch
      master
    * t/feature1
    [devalone@devalone libgit2]$ git status
    位于分支 t/feature1
    无文件要提交,干净的工作区

当前正处于 t/feature1 分支上。

查看新创建的 .topmsg 和 .topdeps 文件内容:

    [devalone@devalone libgit2]$ cat .topmsg
    From: devalone <devalone@sohu.com>
    Subject: [PATCH] t/feature1

    feature1
    [devalone@devalone libgit2]$ cat .topdeps
    master


● 创建另一个特性分支 t/feature2, 并且也依赖于 master
-------------------------------------------------------------------------------------------------------------------------
注意,要在命令中提供 master 作为 dep 参数,以设定依赖分支。因为当前处于 t/feature1 特性分支上。如果不指定,tg create 命令
会自动使用当前分支 t/feature1 作为新特性分支的依赖。

    [devalone@devalone libgit2]$ tg create t/feature2 master
    tg: creating t/feature2 base from master...
    切换到一个新分支 't/feature2'
    [t/feature2 fea9549b5] tg create t/feature2
     2 files changed, 5 insertions(+)
     create mode 100644 .topdeps
     create mode 100644 .topmsg
    tg: Topic branch t/feature2 created.
    [devalone@devalone libgit2]$ git branch
      master
      t/feature1
    * t/feature2
    [devalone@devalone libgit2]$ git status
    位于分支 t/feature2
    无文件要提交,干净的工作区

 

● 创建特性分支 t/feature3, 依赖于 t/feature1 和 t/feature2
-------------------------------------------------------------------------------------------------------------------------
    [devalone@devalone libgit2]$ tg create t/feature3 t/feature1 t/feature2
    tg: creating t/feature3 base from t/feature1...
    切换到一个新分支 't/feature3'
    [t/feature3 8a9f1e9e2] tg create t/feature3
     2 files changed, 6 insertions(+)
     create mode 100644 .topdeps
     create mode 100644 .topmsg
    tg: Topic branch t/feature3 created.
    tg: Running tg update to merge in dependencies.
    tg: Updating t/feature3 base with t/feature2 changes...
    Merge made by the 'trivial aggressive' strategy.
    tg: Updating t/feature3 against new base...
    Merge made by the 'trivial aggressive' strategy.


查看状态和新建文件内容:
    [devalone@devalone libgit2]$ git branch
      master
      t/feature1
      t/feature2
    * t/feature3
    [devalone@devalone libgit2]$ cat .topdeps
    t/feature1
    t/feature2
    [devalone@devalone libgit2]$ cat .topmsg
    From: devalone <devalone@sohu.com>
    Subject: [PATCH] t/feature3

    t/feature3, depends on t/feature1 and t/feature2.

    [devalone@devalone libgit2]$ git status
    位于分支 t/feature3
    无文件要提交,干净的工作区

查看特性分支引用:
    [devalone@devalone libgit2]$ ll .git/refs/heads/t/
    总用量 12
    -rw-rw-r--. 1 devalone devalone 41 8月   4 17:41 feature1
    -rw-rw-r--. 1 devalone devalone 41 8月   4 17:22 feature2
    -rw-rw-r--. 1 devalone devalone 41 8月   4 18:03 feature3

查看特性分支基准分支引用:
    [devalone@devalone libgit2]$ ll .git/refs/top-bases/t
    总用量 12
    -rw-rw-r--. 1 devalone devalone 41 8月   4 16:57 feature1
    -rw-rw-r--. 1 devalone devalone 41 8月   4 17:22 feature2
    -rw-rw-r--. 1 devalone devalone 41 8月   4 18:03 feature3

 

● 显示特性分支信息
-------------------------------------------------------------------------------------------------------------------------
    [devalone@devalone libgit2]$ tg info
    Topic Branch: t/feature3 (2/1 commits)
    Subject: [PATCH] t/feature3
    Base: 310b63ba6
    Depends: t/feature1
             t/feature2
    Up-to-date.
    [devalone@devalone libgit2]$ tg info t/feature1
    Topic Branch: t/feature1 (1/1 commit)
    Subject: [PATCH] t/feature1
    Base: 64138b70e
    Depends: master
    Up-to-date.
    [devalone@devalone libgit2]$ tg info t/feature2
    Topic Branch: t/feature2 (1/1 commit)
    Subject: [PATCH] t/feature2
    Base: 64138b70e
    Depends: master
    Up-to-date.

 

● 更新依赖分支版本(增加新提交)
-------------------------------------------------------------------------------------------------------------------------
切换到 t/feature1 特性分支,并做一些修改并提交:

    [devalone@devalone libgit2]$ git branch
      master
      t/feature1
      t/feature2
    * t/feature3
    [devalone@devalone libgit2]$ git checkout t/feature1
    切换到分支 't/feature1'
    [devalone@devalone libgit2]$ echo "This is a test for deps" > aaa.txt
    [devalone@devalone libgit2]$ git status
    位于分支 t/feature1
    未跟踪的文件:
      (使用 "git add <文件>..." 以包含要提交的内容)

            aaa.txt

    提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
    [devalone@devalone libgit2]$ git add aaa.txt
    [devalone@devalone libgit2]$ git commit -m "add aaa.txt in t/feature1 branch."
    [t/feature1 c4e10fbdb] add aaa.txt in t/feature1 branch.
     1 file changed, 1 insertion(+)
     create mode 100644 aaa.txt


查看 t/feature3 的状态信息:

    [devalone@devalone libgit2]$ tg info t/feature3
    Topic Branch: t/feature3 (2/1 commits)
    Subject: [PATCH] t/feature3
    Base: 310b63ba6
    Depends: t/feature1
             t/feature2
    Needs update from:
            t/feature1 (1/1 commit)

状态信息显示,t/feature3 不再是 Up-to-date 状态,因其依赖的 t/feature1 分支包含了新的提交,需要从 t/feature1 获取更新。

使用 tg update 命令对 t/feature3 执行依赖更新操作:

    [devalone@devalone libgit2]$ tg update t/feature3
    tg: Updating t/feature3 base with t/feature1 changes...
    Merge made by the 'trivial aggressive' strategy.
     1 file changed, 1 insertion(+)
    tg: Updating t/feature3 against new base...
    Merge made by the 'trivial aggressive' strategy.
     1 file changed, 1 insertion(+)

从输出信息可以看出,执行两次分支合并操作,一次针对 refs/top-bases/t/feature3 引用指向的特性分支的基准分支,另外一次针对的
是 refs/heads/t/feature3 的特性分支。
 
切换到 t/feature3 :
    [devalone@devalone libgit2]$ git branch
      master
    * t/feature1
      t/feature2
      t/feature3
    [devalone@devalone libgit2]$ git checkout t/feature3
    切换到分支 't/feature3'

查看新提交的内容:
    [devalone@devalone libgit2]$ ll
    总用量 160
    -rw-rw-r--.  1 devalone devalone    24 8月   4 17:40 aaa.txt
    -rw-rw-r--.  1 devalone devalone   267 8月   4 16:46 api.docurium
    -rw-rw-r--.  1 devalone devalone  1288 8月   4 16:46 appveyor.yml
    -rw-rw-r--.  1 devalone devalone  1274 8月   4 16:46 AUTHORS
    drwxrwxr-x.  2 devalone devalone  4096 8月   4 16:46 ci
    drwxrwxr-x.  3 devalone devalone  4096 8月   4 16:46 cmake
    -rw-rw-r--.  1 devalone devalone 11144 8月   4 16:46 CMakeLists.txt
    -rw-rw-r--.  1 devalone devalone 51023 8月   4 16:46 COPYING
    drwxrwxr-x.  6 devalone devalone  4096 8月   4 16:46 deps
    drwxrwxr-x.  2 devalone devalone  4096 8月   4 16:46 docs
    drwxrwxr-x.  4 devalone devalone  4096 8月   4 16:46 examples
    drwxrwxr-x.  3 devalone devalone  4096 8月   4 16:46 fuzzers
    -rw-rw-r--.  1 devalone devalone  3105 8月   4 16:46 git.git-authors
    drwxrwxr-x.  3 devalone devalone  4096 8月   4 16:46 include
    -rw-rw-r--.  1 devalone devalone   764 8月   4 16:46 libgit2_clar.supp
    -rw-rw-r--.  1 devalone devalone   297 8月   4 16:46 libgit2.pc.in
    -rw-rw-r--.  1 devalone devalone   282 8月   4 16:46 package.json
    -rw-rw-r--.  1 devalone devalone 14570 8月   4 16:46 README.md
    drwxrwxr-x.  2 devalone devalone  4096 8月   4 16:46 script
    drwxrwxr-x.  8 devalone devalone 12288 8月   4 16:46 src
    drwxrwxr-x. 51 devalone devalone  4096 8月   4 16:46 tests
    [devalone@devalone libgit2]$ cat aaa.txt
    This is a test for deps
    [devalone@devalone libgit2]$


OK, t/feature1 分支上更新的内容,完全更新到 t/feature3 分支上。这种跟踪版本变化和更新的能力,正是我们所需要的。

 

● 解决更新冲突
-------------------------------------------------------------------------------------------------------------------------
使用 TopGit 不会避免冲突,它的优势在于对特性分支强大的管理功能。

执行 tg update 命令因为涉及分支合并,因此并非每次都会成功。故意创造一个冲突,在 t/feature1 和 t/feature3 分支上,同时对
aaa.txt 文件作出修改,看看 TopGit 如何表现:

    [devalone@devalone libgit2]$ git branch
      master
      t/feature1
      t/feature2
    * t/feature3
    [devalone@devalone libgit2]$ git checkout t/feature1
    切换到分支 't/feature1'

    [devalone@devalone libgit2]$ cat aaa.txt
    This is a test for deps
    test topgit confict in t/feature1 branch.

    [devalone@devalone libgit2]$ git add aaa.txt
    [devalone@devalone libgit2]$ git commit -m "add a confict in t/feature1"
    [t/feature1 e1b7b6466] add a confict in t/feature1
     1 file changed, 1 insertion(+)

    
    切换到 t/feature3 分支修改 aaa.txt 文件:
    [devalone@devalone libgit2]$ git checkout t/feature3
    切换到分支 't/feature3'

    [devalone@devalone libgit2]$ echo "AAAAA test topgit confict in t/feature3 branch." >> aaa.txt
    [devalone@devalone libgit2]$ cat aaa.txt
    This is a test for deps
    AAAAA test topgit confict in t/feature3 branch.

    [devalone@devalone libgit2]$ git commit -m "add a confict in t/featur3 branch"
    [t/feature3 cab5639ea] add a confict in t/featur3 branch
     1 file changed, 1 insertion(+)

    [devalone@devalone libgit2]$ git status
    位于分支 t/feature3
    无文件要提交,干净的工作区

git status 命令在这里看不出任何变化。

使用 tg info 命令查看 t/featur3 分支的状态信息:

    [devalone@devalone libgit2]$ tg info
    Topic Branch: t/feature3 (4/2 commits)
    Subject: [PATCH] t/feature3
    Base: b65bb0666
    Depends: t/feature1
             t/feature2
    Needs update from:
            t/feature1 (1/1 commit)


通过 tg info 命令发现需要从 t/feature1 分支进行更新,因为 t/feature1 是 t/feature3 的依赖分支,并且有了新的提交。

执行更新操作:

    [devalone@devalone libgit2]$ tg update
    tg: Updating t/feature3 base with t/feature1 changes...
    Merge made by the 'trivial aggressive' strategy.
     1 file changed, 1 insertion(+)
    tg: Updating t/feature3 against new base...
    自动合并 aaa.txt
    冲突(内容):合并冲突于 aaa.txt
    Automatic merge failed; fix conflicts and then commit the result.
    tg: Please commit merge resolution and call `tg update --continue`
    tg: (use `tg status` to see more options)

    
从输出可以看出,使用 t/feature1 更新 t/featur3 的基准分支 (base) 的合并操作成功了。而用更新过的基准分支来合并更新t/featur3
分支操作时,冲突出现了,定位在 aaa.txt 文件上,提示自动合并失败。

提示下一步:
    ① 解决冲突后调用  tg update --continue 以完成更新操作
    ② 通过 tg status 命令查看更多选项
    

通过 tg info 命令查看一下当前分支的状态信息:
    
    [devalone@devalone libgit2]$ tg info
    Topic Branch: t/feature3 (4/2 commits)
    Subject: [PATCH] t/feature3
    Base: 29a2acb86
    * Base is newer than head! Please run `tg update`.
    Depends: t/feature1
             t/feature2
    Up-to-date.

从上面 tg info 命令的输出可以看出当前分支的状态时 Up-to-date, 但是输出中包含一个提示:分支的基要比头新,请执行 tg update
命令(Base is newer than head! Please run `tg update`.)

使用 tg summary 命令查看:

    [devalone@devalone libgit2]$ tg summary
             t/feature1                     [PATCH] t/feature1
     0       t/feature2                     [PATCH] t/feature2
    >     B  t/feature3                     [PATCH] t/feature3

发现 t/feature3 处于 B (Break) 状态。

执行 tg status 命令看看更多选项:

    [devalone@devalone libgit2]$ tg status
    HEAD -> t/feature3 [cab5639ea]
    tg update in progress; currently updating branch 't/feature3'
    You are currently updating as a result of:
      tg update t/feature3
      (use "tg update --continue" to continue)
      (use "tg update --skip" to skip this branch and continue)
      (use "tg update --stop" to stop and retain changes so far)
      (use "tg update --abort" to restore pre-update state)
    git merge in progress
    fix conflicts and then "git commit" the result

总之,是要解决冲突,然后更新继续才算完成更新操作。通过手动编辑 aaa.txt 文件,或者冲突解决工具解决冲突,之后再次执行:

    tg update --continue
    
以完成操作。

    [devalone@devalone libgit2]$ vi aaa.txt
    [devalone@devalone libgit2]$ git status
    位于分支 t/feature3
    您有尚未合并的路径。
      (解决冲突并运行 "git commit")
      (使用 "git merge --abort" 终止合并)

    未合并的路径:
      (使用 "git add <文件>..." 标记解决方案)

            双方修改:   aaa.txt

    修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
    [devalone@devalone libgit2]$ git add aaa.txt
    [devalone@devalone libgit2]$ git commit -m"confict resovled."
    [t/feature3 bba40bd04] confict resovled.
    [devalone@devalone libgit2]$ git status
    位于分支 t/feature3
    无文件要提交,干净的工作区
    [devalone@devalone libgit2]$ tg update --continue
    tg: The base is up-to-date.
    tg: The t/feature3 head is up-to-date wrt. the base.

再次查看状态信息:
    [devalone@devalone libgit2]$ tg info
    Topic Branch: t/feature3 (5/2 commits)
    Subject: [PATCH] t/feature3
    Base: 29a2acb86
    Depends: t/feature1
             t/feature2
    Up-to-date.

好了,冲突解决,t/featur3 的状态更新为 Up-to-date,这就是我们所要的此次更新的最终结果。

 

● 设置远程版本库
-------------------------------------------------------------------------------------------------------------------------
目前 TopGit 特性分支还只限于本地,并未与远程版本库关联:

    [devalone@devalone libgit2]$ tg summary
             t/feature1                     [PATCH] t/feature1
     0       t/feature2                     [PATCH] t/feature2
    >0       t/feature3                     [PATCH] t/feature3

没有 "l/r/L/R" 任何标记出现。

查看当前版本库设置:
    [devalone@devalone libgit2]$ cat .git/config
    [core]
            repositoryformatversion = 0
            filemode = true
            bare = false
            logallrefupdates = true
    [remote "origin"]
            url = https://github.com/libgit2/libgit2.git
            fetch = +refs/heads/*:refs/remotes/origin/*
    [branch "master"]
            remote = origin
            merge = refs/heads/master
    [merge "ours"]
            name = \"always keep ours\" merge driver
            driver = touch %A

远程版本库 "origin" 已设置为原始的 https://github.com/libgit2/libgit2.git

因此,要创建一个开发环境内的远程版本库,用于保存我们的定制开发成果。如下所示:

    [devalone@devalone libgit2]$ git init --bare /home/devalone/test/git-test/repos/tglibgit2.git
    已初始化空的 Git 仓库于 /home/devalone/test/git-test/repos/tglibgit2.git/

将此新建的版本库设置为一个远程版本库,注意,名称 "origin" 已被占用,因此不能习惯性地使用这个名称,而要使用一个没有被现有
配置占用的远程仓库名称,如 "tglibgit2" :

    [devalone@devalone libgit2]$ git remote add tglibgit2  /home/devalone/test/git-test/repos/tglibgit2.git

看下配置变化:
    [devalone@devalone libgit2]$ cat .git/config
    [core]
            repositoryformatversion = 0
            filemode = true
            bare = false
            logallrefupdates = true
    [remote "origin"]
            url = https://github.com/libgit2/libgit2.git
            fetch = +refs/heads/*:refs/remotes/origin/*
    [branch "master"]
            remote = origin
            merge = refs/heads/master
    [merge "ours"]
            name = \"always keep ours\" merge driver
            driver = touch %A
    [remote "tglibgit2"]
            url = /home/devalone/test/git-test/repos/tglibgit2.git
            fetch = +refs/heads/*:refs/remotes/tglibgit2/*


"tglibgit2" 被添加为新的 [remote "tglibgit2"] 配置中,并设置了 url 和 fetch 属性。

切换到 master 分支,将当前版本库的 master 分支推送到刚刚创建的远程版本库 "tglibgit2" 中:
    [devalone@devalone libgit2]$ git branch
      master
      t/feature1
      t/feature2
    * t/feature3
    [devalone@devalone libgit2]$ git checkout master
    切换到分支 'master'
    您的分支与上游分支 'origin/master' 一致。

    [devalone@devalone libgit2]$ git push tglibgit2 master
    枚举对象: 73767, 完成.
    对象计数中: 100% (73767/73767), 完成.
    Delta compression using up to 4 threads.
    压缩对象中: 100% (21399/21399), 完成.
    写入对象中: 100% (73767/73767), 23.99 MiB | 39.94 MiB/s, 完成.
    Total 73767 (delta 50731), reused 73764 (delta 50728)
    remote: 处理 delta 中: 100% (50731/50731), 完成.
    To /home/devalone/test/git-test/repos/tglibgit2.git
     * [new branch]          master -> master


设置 TopGit 远程分支:
    [devalone@devalone libgit2]$ tg remote --populate tglibgit2
    tg: Remote tglibgit2 can now follow TopGit topic branches.
    tg: Populating local topic branches from remote 'tglibgit2'...
    tg: The remote 'tglibgit2' is now the default source of topic branches.
    
远程分支 'tglibgit2' 成为目前默认的特性分支源(the default source of topic branches),这很重要。

查看配置文件变化:
    [devalone@devalone libgit2]$ cat .git/config
    [core]
            repositoryformatversion = 0
            filemode = true
            bare = false
            logallrefupdates = true
    [remote "origin"]
            url = https://github.com/libgit2/libgit2.git
            fetch = +refs/heads/*:refs/remotes/origin/*
    [branch "master"]
            remote = origin
            merge = refs/heads/master
    [merge "ours"]
            name = \"always keep ours\" merge driver
            driver = touch %A
    [remote "tglibgit2"]
            url = /home/devalone/test/git-test/repos/tglibgit2.git
            fetch = +refs/heads/*:refs/remotes/tglibgit2/*
            fetch = +refs/top-bases/*:refs/remotes/tglibgit2/top-bases/*
    [topgit]
            remote = tglibgit2

查看 tg summary 变化:
    [devalone@devalone libgit2]$ tg summary
      l      t/feature1                     [PATCH] t/feature1
     0l      t/feature2                     [PATCH] t/feature2
     0l      t/feature3                     [PATCH] t/feature3

看到了 3 个特性分支都有 "l" 标记,即本地提交比远程版本库 "tglibgit2" 新。

执行 tg push 命令将特性分支 t/featur2 及其基准分支推送到远程版本库:

    [devalone@devalone libgit2]$ tg push t/feature2
    Enumerating objects: 4, done.
    Counting objects: 100% (4/4), done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (3/3), done.
    Writing objects: 100% (3/3), 360 bytes | 360.00 KiB/s, done.
    Total 3 (delta 1), reused 0 (delta 0)
    To /home/devalone/test/git-test/repos/tglibgit2.git
     * [new branch]          t/feature2 -> t/feature2
     * [new branch]          refs/top-bases/t/feature2 -> refs/top-bases/t/feature2

特性分支 t/featur2 及其基准分支已推送到远程版本库:

再次查看状态信息:
    [devalone@devalone libgit2]$ tg summary
      l      t/feature1                     [PATCH] t/feature1
     0r      t/feature2                     [PATCH] t/feature2
    >0l      t/feature3                     [PATCH] t/feature3

t/feature2 标记变为 "r",表明特性分支 t/feature2 既存在于远程版本库,也存在于本地版本库,并且两者匹配,即远程和本地同步。

运行 tg push --all | -a 命令,将所有的 TopGit 分支推送到远程版本库:

    [devalone@devalone libgit2]$ tg push --all
    Enumerating objects: 31, done.
    Counting objects: 100% (31/31), done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (25/25), done.
    Writing objects: 100% (27/27), 2.70 KiB | 2.70 MiB/s, done.
    Total 27 (delta 11), reused 0 (delta 0)
    To /home/devalone/test/git-test/repos/tglibgit2.git
     * [new branch]          t/feature1 -> t/feature1
     * [new branch]          t/feature3 -> t/feature3
     * [new branch]          refs/top-bases/t/feature1 -> refs/top-bases/t/feature1
     * [new branch]          refs/top-bases/t/feature3 -> refs/top-bases/t/feature3


再次查看状态信息:
    [devalone@devalone libgit2]$ tg summary
      r      t/feature1                     [PATCH] t/feature1
     0r      t/feature2                     [PATCH] t/feature2
    >0r      t/feature3                     [PATCH] t/feature3

所有的特性分支都标记为 "r",说明 3 个特性分支的远程和本地库处于同步状态,这正是我们所要的状态。

 

5.3 TopGit 使用中的注意事项
-------------------------------------------------------------------------------------------------------------------------

 

● 经常运行 tg remote --populate 获取他人创建的特性分支
-------------------------------------------------------------------------------------------------------------------------
运行命令 git fetch 或命令 git pull 和远程版本库同步,只能将他人创建的 TopGit 特性分支在本地以远程分支的方式保存,即存储在

    refs/remotes/origin/t/<branch-name>
    
而不能自动地在本地建立分支。

如果确认版本库是使用 TopGit 维护的话,应该在和远程版本库同步的时候执行 tg remote --populate origin 命令。这条命令会做两件
事情:

    □ 自动调用 git fetch origin 获取远程 origin 版本库的新的提交和引用
    □ 检查 refs/remote/origin/top-bases/ 下的所有引用,如果有新的,在本地 refs/top-bases/ 下尚不存在,说明其他人创建了新的
      特性分支, TopGit 会据此自动在本地创建新的特性分支。

 

● 适时地调整特性分支的依赖关系
-------------------------------------------------------------------------------------------------------------------------
例如之前用于演示是版本库,各个特性分支的依赖文件内容如下:

□ 分支 t/feature1 的 .topdeps 文件:

    [devalone@devalone libgit2]$ cat .topdeps
    master

□ 分支 t/feature2 的 .topdeps 文件:

    [devalone@devalone libgit2]$ cat .topdeps
    master

□ 分支 t/feature3 的 .topdeps 文件:

    [devalone@devalone libgit2]$ cat .topdeps
    t/feature1
    t/feature2

如果 t/featur3 的 .topdeps 文件是这样的,就可能存在问题:
    
    master
    t/feature1
    t/feature2

问题在于 t/featur3 依赖的其它分支已经依赖了 master 分支,虽然不会造成致命影响,但在特定情况下这种重复依赖会造成麻烦。例如master 分支更新后,可能由于代码重构的比较厉害,在特性分支迁移时会造成冲突,如在 t/feature1 分支中执行 tg update 会遇到冲突当辛苦完成冲突解决并提交后,在 t/featur3 中执行 tg update 会因为先依赖的 master 分支,所以先在 master 分支上对 t/featur3 分支进行合并,这样就肯定会遇到和 t/feature1 相同的冲突,还要再重复解决一次。

如果在 .topdeps 文件中删除对 master 分支的重复依赖,就不会出现上面的重复解决冲突的问题。

同样的道理,如果 t/featur3 的 .topdeps 文件写成这样,效果也将不同:

    t/feature2
    t/feature1

依赖的顺序不同会造成合并的顺序也不同,同样也会产生重复的冲突解决。因此当发现重复的冲突时,可以取消合并操作,修改特性分支的 .topdeps 文件,调整文件内容(删除重复分支,调整分支顺序) 并提交,然后再执行 tg update 继续合并操作。

 

● TopGit 特性分支的里程碑和分支管理
-------------------------------------------------------------------------------------------------------------------------
TopGit 本身是管理特性分支的程序,TopGit 某个时刻的开发状态是 TopGit 管理下的所有分支(包括基准分支)整体的状态。

能够用里程碑来标记 TopGit 管理的版本库的开发状态吗?

使用里程碑来管理是可能的,Git 里程碑只能针对一个分支做标记而不能标记所有分支。但 TopGit 提供了自己的 tg tag 命令,可以对一个或多个 TopGit 特性分支同时创建里程碑,以记录某一时刻 TopGit 分支的状态。

输入:
    tg help tag
    
查看创建里程碑用法说明。

另一种方法时使用克隆。克隆不但用于标记 TopGit 版本库的开发状态,也可以用于 TopGit 版本库的分支管理。例如一旦上游出现新的版本,就从当前版本库建立一个克隆,原来的版本库用于维护原有上游版本的定制开发,新的克隆版本库针对新的上游版本进行迁移,用于新的上游版本的特性开发。

 

续: 利用 Topgit 对 Nutch 定制开发进行代码管理(二)

 

系列目录:

利用 Topgit 对 Nutch 定制开发进行代码管理(一)

利用 Topgit 对 Nutch 定制开发进行代码管理(二)

 

-------------------------------------------------------------------------------------------------------------------------
参考:

    https://github.com/mackyle/topgit
    https://wiki.apache.org/nutch/RunNutchInEclipse

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值