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

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

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

bash 复制代码
$ git config rerere.enabled true

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

bash 复制代码
# 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:
bash 复制代码
# 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 验证合并效果:

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

bash 复制代码
# 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):

bash 复制代码
# 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 标记:

bash 复制代码
# 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 会提示在版本引用与文件列表之间,用 -- 进行分隔。因此,更正后的保险写法是:

bash 复制代码
git diff --name-only origin/stable-3.1 origin/stable-3.2 -- org.eclipse.jgit/src/org/eclipse/jgit/transport/

该类命令的语法结构为:

git diff [options] <srcCommit> <desCommit> <path>

git diff [options] <srcCommit> <desCommit> -- <path>

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

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

bash 复制代码
$ git diff --name-status --diff-filter=DA origin/stable-3.1 origin/stable-3.2 

结果如下:

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

bash 复制代码
$ git diff --name-status --diff-filter=DA origin/stable-3.2 origin/stable-3.1 

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

3.6 孤立分支(Orphan branches

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

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

bash 复制代码
# 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 分支:

bash 复制代码
# 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 命令可以看到两个版本树:

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

bash 复制代码
# 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 分支:

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

拓展

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

  1. 在本地仓库创建一个孤立分支 A
  2. 利用 A 关联并拉取远程仓库内容;
  3. 将本地代码并入 A 分支(使用 --allow-unrelatived-histories
相关推荐
范范08256 分钟前
Scala基础入门:从零开始学习Scala编程
开发语言·学习·scala
╰★忝若冇凊★丶1 小时前
Linux笔记
linux·笔记
IT ·南栀1 小时前
Day18_1--Vue基础学习入门
前端·vue.js·学习
焦点康师傅2 小时前
SpringMabatis学习笔记1
笔记·学习
小狮子安度因2 小时前
Linux源码阅读笔记13-进程通信组件中
linux·笔记
Take^that2 小时前
IT课程学习搭子
开发语言·学习·云原生·golang
不要小瞧我啊八嘎o.0?2 小时前
Matlab数据处理学习笔记
笔记·学习
hsg772 小时前
基于arcpro3.0.2的北斗网格生成简介
笔记
进击的小白菜2 小时前
Sprong Boot学习|使用 guava-retrying 实现重试
spring boot·学习·guava