Git 在远程创建孤儿分支并推送

今天有一个需求。本地有一个代码库,我希望推送到自己的远程代码库的master(A/proj.git)作为存储;同时还要发布到另一个人的远程代码库,作为一个全新的、不带他的提交历史的分支(B/proj2.git),因为二者文件完全不相关。不过,B/proj2的feat分支现在并不存在。首先想到的方案就是,在B上创建一个孤儿(orphan)分支 feat,然后本地master直接 git push B feat 不就好了?嘛,被 Git 的各种机制坑了。

PS. 2016年8月到今年1月的文章,以后会放上来……


我进行了这样的操作:

1
2
git remote add origin A/proj.git
git push -u origin

好,到这里都没问题。我已经在远程(BitBucket)上创建了 proj 这个项目,推送没问题。然后到了开orphan分支的事情了。然而 BitBucket 不支持创建孤儿分支(GitHub 也不支持),只支持从master创建新分支。那只好手工上了。

1
2
git remote add r B/proj2.git
git push r +feat

我觉得,既然在本地创建了远程B,我的master分支应该不用跟踪proj2就能推送(而且我也不希望master跟踪proj2)才对。于是收到了报错:

1
2
error: src refspec feat does not match any.
error: failed to push some refs to 'B/proj2.git'

我对 Git 分支的表示有略微了解,大概就是某个 refs/... 下的东西。于是略微谷歌一下,找到了一篇 StackOverflow 上的回答。照葫芦画瓢:

1
git push r r:refs/heads/feat

然而又收到了一样的错误。哎,先将目标定为创建一个 B/proj2 的孤儿分支吧。遵循指导(12),在新的目录下:

1
2
3
git clone B/proj2.git
git checkout --orphan feat
git rm -rf .

好了,到这里我觉得可以直接发布分支了,于是尝试:

1
2
# 此时我是在刚clone下来的目录内
git push origin feat

然而无法推送。想了想,是因为没有提交吧!添加了一个空文件后提交,推送成功。此时 B/proj2 上出现了一个孤儿分支feat,带有一个提交。

但真正文件的推送还没解决。考虑到网上许多人都是checkout了本地分支后再操作的,好吧,我放弃直接在master上操作了,于是创建了一个新分支branch2:

1
2
3
git checkout -b branch2
# 别忘了之前的远程r还在
git pull r feat

继续报错:

1
2
* branch            feat -> FETCH_HEAD
fatal: refusing to merge unrelated histories

历史冲突,那用本地孤儿分支又如何呢?

1
2
3
4
5
6
7
git checkout master
# 由于有未完成的合并,必须强制删除(-D)
git branch -D branch2
# 呃,这里为什么突然换成了feat作为名字,我不知道;如果严格按照“实验”来做,这里应该先试branch2
git checkout --orphan feat
git rm -rf .
git pull r feat

这次拉取倒是没问题:

1
2
From B/proj2
* branch feat -> FETCH_HEAD

然而我惊恐地发现,由于是孤儿分支而且进行了 git rm -rf,目录下空空如也。那赶快合并吧:

1
git merge master

又是喜闻乐见的报错:

1
fatal: refusing to merge unrelated histories

究其原因,是因为我之前为了在远程创建这个分支而提交的一个空文件导致了历史冲突。那……直接checkout呢?

1
2
3
4
5
6
git checkout master
git branch -D feat
# 这次就不是孤儿分支了,是带有master历史的分支
git checkout -b feat
# 这里再次设置跟踪
git push -u r feat

这一次,出现了不同的东西:

1
2
3
4
5
6
7
To B/proj2.git
! [rejected] feat -> feat (non-fast-forward)
error: failed to push some refs to 'B/proj2.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

好的,希望很大!看到这个提示,就表明推送是因为修改时间冲突(我先在本地提交,再去创建远程孤儿分支下的空文件)。既然如此,强制推送就行了:

1
git push r +feat

现在就可以了。BitBucket 上显示,新分支feat有相对于B/proj2的master两个超前提交(我在本地的修改历史)和若干个落后提交(master已有的历史)。这是正确的。

不过好像有一个副作用,在推送之前我有个文件是有改动未提交的,经过这次推送后这个改动消失了,不知是不是某个操作隐含意义的问题。


总结:正确的创建和推送流程——推送到 B/proj2.git 的 feat 分支

在临时目录:

1
2
3
4
5
6
7
8
9
git clone B/proj2.git
git branch --orphan feat
git rm -rf .
# 这个文件名就随便了,会被覆盖的
touch orphan
git add orphan
# 这一条消息,以后也会被覆盖掉
git commit -m "Initial commit"
git push origin feat

在工作目录:

1
2
3
4
git remote add remote1 B/proj2.git
git checkout master
git checkout -b feat
git push remote1 +feat

话说在各个命令间,“feat”是固定的吗?本地的分支和远程分支要同名吗?不确定,不过根据 PR 的两个相关分支间可以名字不同,猜测这里也可以不同。但为了不造成混乱,最好取一样的名字吧。

分享到 评论