• 【Git 学习笔记】第三章 分支、合并及配置项(下)


    3.4 使用 rerere 合并有冲突的 Git 版本

    如果每天都需要合并分支,或者在一个长期维护的特性分支上需要一直相同的代码冲突,那么可以试试 git rererereuse recorded resolution )。该命令默认不生效,需要手动配置生效:(可设为用户级配置,添加 --global 标记)

    $ git config rerere.enabled true
    

    下面以 jgit 为例,演示该命令的用法:

    # Checkout a branch
    $ git checkout -b rerereExample --track origin/stable-2.2
    # Do some modification: 2.5.1 --> 2.5.2, then check by git diff
    $ git diff 
    diff --git a/pom.xml b/pom.xml
    index 085e00fef..d5aec1777 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -208,7 +208,7 @@
    
             <plugin>
               <artifactId>maven-compiler-plugin</artifactId>
    -          <version>2.5.1</version>
    +          <version>2.5.2</version>
             </plugin>
    
             <plugin>
    # Add a new commit for the modification
    $ git add pom.xml
    $ git commit
    [rerereExample 37ef9f194] Recorded resolution for 'pom.xml'.
     1 file changed, 1 insertion(+), 1 deletion(-)
    # Notice the first output from git
    # Checkout another branch
    $ git checkout -b rerereExample2
    Switched to a new branch 'rerereExample2'
    # rebase to stable-3.2 branch
    $ git rebase origin/stable-3.2
    # resolve conflicted file (pom.xml) as follows:
    

    fig 3-3

    # Notice the code in L233 (from git rerere)
    # Remain 2.5.2 line and continue rebase
    $ git add pom.xml
    $ git rebase --continue
    # Add commit message 'Update maven-compiler-plugin to 2.5.2.' in editor
    

    gitk 验证合并效果:

    fig 3-4 gitk result view

    发散练习:当需要确定某个版本归属哪个分支时,可以轻松使用 git branch 命令的 --contains 参数实现,后跟 commit ID 即可:

    # list some commit ID and select one
    git log -5 --oneline --format='%h %s'
    7384fac94 Update maven-compiler-plugin to 2.5.2.
    f839d383e Prepare post 3.2.0 builds
    699900c30 JGit v3.2.0.201312181205-r
    0ff691cdb Revert "Fix for core.autocrlf=input resulting in modified file..."
    1def0a125 Fix for core.autocrlf=input resulting in modified file and unsmudge
    # select the 3rd one
    $ git branch --contains 699900c30
      master
    * rerereExample2
    # If specific commit ID omitted, HEAD is used:
    $ git branch -a --contains
    * rerereExample2
    

    contains 的值除了 SHA-1 外,还可以是标签(tags)、分支名(branch names):

    # Use tag
    $ git branch -a --contains v2.3.0.201302130906
      master
    * rerereExample2
      remotes/origin/HEAD -> origin/master
      remotes/origin/master
      remotes/origin/next
      remotes/origin/stable-2.3
      remotes/origin/stable-3.0
    # ... (omitted)
    

    3.5 计算分之间的差异

    合并分支前比较分支之间的差异可以得到很多有价值的信息。默认的 git diff 命令会输出所有差异,这往往不全是最需要的信息;更多的情况是想了解某个文件或路径在两个分支间的差异。这就需要用到 --name-only 标记:

    # on master branch
    $ git checkout master
    # Diff origin/stable-3.1 with the origin/stable-3.2 branch
    $ git diff --name-only origin/stable-3.1 origin/stable-3.2 org.eclipse.jgit/src/org/eclipse/jgit/transport/
    org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/ServiceMayNotContinueException.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
    org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
    

    这里出现笔误:指定的路径为 org.eclipse.jgit/src/org/eclipse/jgit/transport/,书中写成了 org.eclipse.jgit/src/org/eclipse/jgit/transport/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetch。且 Git 会提示在版本引用与文件列表之间,用 -- 进行分隔。因此,更正后的保险写法是:

    git diff --name-only origin/stable-3.1 origin/stable-3.2 -- org.eclipse.jgit/src/org/eclipse/jgit/transport/
    

    该类命令的语法结构为:

    git diff [options]

    git diff [options] --

    这对于仅比较指定路径的需求而言,是十分方便的。

    如果还要查看各个变更文件的状态,使用 --name-status 标记;若还想列出只有新增或删除过若干行的文件的列表,则可以使用筛选条件 --diff-filter=DA

    $ git diff --name-status --diff-filter=DA origin/stable-3.1 origin/stable-3.2 
    

    结果如下:

    fig 3-5 git diff result

    倘若交换两个分支的顺序,则得到相反的结果:

    $ git diff --name-status --diff-filter=DA origin/stable-3.2 origin/stable-3.1 
    

    fig 3-6 reversed result

    注意,git diff 中的第一个版本引用为起点(source),第二个为终点(destination)。

    3.6 孤立分支(Orphan branches

    根据 DAG 有向无环图的设计,通常一个 Git 对象都有一个父级引用,比如创建的新分支,其 commit 对象就是其父级对象。有时也会遇到没有父级引用的对象,即孤立分支(orphan branches)。

    孤立分支的一个应用场景,就是合并两个单独的 git 库。使用简单复制粘贴文件的方法无疑会丢失历史提交记录。但利用孤立分支的相关特性,就能实现在一个 git 库中 fetch 到另一个 git 库的数据。

    # clone demo repo
    $ git clone https://github.com/PacktPublishing/Git-Version-Control-Cookbook-Second-Edition.git demo
    $ cd demo
    # checkout an orphan branch
    $ git checkout --orphan fresh-start
    # Check git log
    $ git log
    fatal: your current branch 'fresh-start' does not have any commits yet
    # check ls and git status
    $ ls
    $ git status
    # ... (omitted)
    # unstage files in working directory
    $ git rm --cached README.md a_sub_directory/readme another-file.txt cat-me.txt hello_world.c
    # then remove them
    # on Linux:
    $ rm -rf README.md a_sub_directory another-file.txt cat-me.txt hello_world.c
    # on Windows:
    $ rm -Recurse -Force README.md,a_sub_directory,another-file.txt,cat-me.txt,hello_world.c
    # Check status
    $ git status
    On branch fresh-start
    
    No commits yet
    
    nothing to commit (create/copy files and use "git add" to track)
    

    这样,fresh-start 分支既没有任何文件,也没有任何提交记录。此时可以用 git add remote 关联远程分支,再用 git fetch 获取到本地,这样两个库的提交历史都不受影响。为简单起见,这里只演示从本地新增提交记录,再将孤立分支并入 master 分支:

    # Create a commit on orphan branch
    $ echo "This is from an orphan branch." > orphan.txt
    $ git add orphan.txt
    $ git commit -m "Orphan"
    [fresh-start (root-commit) babe63f] Orphan
     1 file changed, 1 insertion(+)
     create mode 100644 orphan.txt
    # Toggle to master branch
    $ git checkout master
    $ git merge fresh-start
    fatal: refusing to merge unrelated histories
    

    默认情况下,Git 拒绝合并孤立分支。通过 gitk 命令可以看到两个版本树:

    fig 3-7 orphant branch

    这时可以使用 --allow-unrelated-histories 强制并入孤立分支:

    # Force to merge orphan branch with --allow-unrelated-histories
    $ git merge fresh-start --allow-unrelated-histories
    Merge made by the 'ort' strategy.
     orphan.txt | 1 +
     1 file changed, 1 insertion(+)
     create mode 100644 orphan.txt
    

    再次查看 gitk,孤立分支已被并入 master 分支:

    fig 3-8

    孤立分支虽然用得不多,但需要重新组织代码库的时候就可以大显身手了。

    拓展

    孤立分支的另一个场景,是需要将本地代码共享到线上 git 服务器托管。比如本地维护了一段时间的 git 代码库,需要和 GithubGitlab 上新建的远程仓库(通常是空的)进行合并。这就需要孤立分支的相关知识,步骤如下:

    1. 在本地仓库创建一个孤立分支 A
    2. 利用 A 关联并拉取远程仓库内容;
    3. 将本地代码并入 A 分支(使用 --allow-unrelatived-histories
  • 相关阅读:
    金仓数据库KingbaseES数据库管理员指南--17数据库调度概念
    【Python语言速回顾】——爬虫基础知识
    muduo库的高性能日志库(五)——AsyncLogging文件
    28-搭建Keepalived+LVS+Nginx高可用集群负载均衡
    《uni-App打包支付宝小程序并发布的详细教程》
    NCP81239MNTXG 开关降压/升压控制器,USB 功率传递和 Type-C 应用
    mysql 从入门到放弃— 数据库设计
    同元软控新一代复杂装备虚拟试验解决方案与实践
    【Git技巧】第三篇 删除冗余的本地或远程的操作分支
    Linux常见操作命令(1)
  • 原文地址:https://blog.csdn.net/frgod/article/details/140280325