【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
相关推荐
时光追逐者23 分钟前
MongoDB从入门到实战之MongoDB快速入门(附带学习路线图)
数据库·学习·mongodb
一弓虽28 分钟前
SpringBoot 学习
java·spring boot·后端·学习
晓数2 小时前
【硬核干货】JetBrains AI Assistant 干货笔记
人工智能·笔记·jetbrains·ai assistant
我的golang之路果然有问题2 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
genggeng不会代码2 小时前
用于协同显著目标检测的小组协作学习 2021 GCoNet(总结)
学习
lwewan2 小时前
26考研——存储系统(3)
c语言·笔记·考研
jstart千语2 小时前
【Git】连接github时的疑难杂症(DNS解析失败)
git·github
搞机小能手2 小时前
六个能够白嫖学习资料的网站
笔记·学习·分类
工具罗某人3 小时前
TortoiseGit使用图解
git
Zhuai-行淮3 小时前
vscode和git 踩坑
ide·git·vscode