暗无天日

=============>DarkSun的个人博客

git branch大扫除

http://www.joachim-breitner.de/blog/751-Spring_cleaning__local_git_branches 上看到的小技巧,觉得很棒,记录一下。

在git项目中经常会包含大量的分支,比如在在我 Linux中国 的选题仓库中就包含了一对各式各样的分支:

git branch
  add-MjAxODA4MDUgV2hlcmUgVmltIENhbWUgRnJvbS5tZAo=
  add-MjAxODA5MDIgTGVhcm5pbmcgQkFTSUMgTGlrZSBJdC1zIDE5ODMubWQK
  add-MjAxODEwMTUgSW50ZXJuZXRzIG9mIEludGVyZXN0IC03LSBJYW4gQ29vcGVyIG9uIFRlc3QgRHJpdmVuIERldmVsb3BtZW50Lm1kCg==
  add-MjAxODExMDMgSW50ZXJuZXRzIG9mIEludGVyZXN0IC04LSBUb2RkIEZlcm5hbmRleiBvbiB0aGUgbWFudWZhY3R1cmluZyBvZiBtb2Rlcm4gc2VtaWNvbmR1Y3RvcnMubWQK
  add-MjAxODExMTEgSW50ZXJuZXRzIG9mIEludGVyZXN0IC05LSBKYXNvbiBTY290dCBvbiB0aGUgQ29weXJpZ2h0IEZ1cnkgUm9hZC5tZAo=
  add-MjAxODExMTUgSW50ZXJuZXRzIG9mIEludGVyZXN0IC0xMC0gTWlrZSBIYXJyaXNvbiBvbiB0aGUgRWlkb3Bob3IubWQK
  add-MjAxODEyMzAgSW50ZXJuZXRzIG9mIEludGVyZXN0IC0xMS0gWWVzdGVyZGF5LXMgQ29tcHV0ZXIgb2YgVG9tb3Jyb3ctIFRoZSBYZXJveCBBbHRvLm1kCg==
  add-MjAxOTA0MDMgQWJzb2x1dGUgVW5pdCAodGVzdCkubWQK
  add-MjAxOTA2MDUgSG93IHRvIG5hdmlnYXRlIHRoZSBLdWJlcm5ldGVzIGxlYXJuaW5nIGN1cnZlLm1kCg==
  add-MjAxOTA2MTAgV2h5IGNvbnRhaW5lcnMgYW5kIEt1YmVybmV0ZXMgaGF2ZSB0aGUgcG90ZW50aWFsIHRvIHJ1biBhbG1vc3QgYW55dGhpbmcubWQK
  add-MjAxOTA2MjggVW5kbyByZWxlYXNlcyBMaXZlIFJlY29yZGVyIDUuMCBmb3IgTGludXggZGVidWdnaW5nLm1kCg==
  add-MjAxOTA3MDUgU3lkbmV5IEhpZ2ggUGVyZm9ybWFuY2UgR28gd29ya3Nob3AubWQK
  add-MjAxOTA3MDkgUmVkIEhhdCwgSUJNLCBhbmQgRmVkb3JhLm1kCg==
  add-MjAxOTA4MzAgSFBFIGludHJvZHVjZXMgVk13YXJlIHNlcnZpY2VzIG9uIEdyZWVuTGFrZS5tZAo=
  add-MjAxOTA5MDIgVXNpbmcgcHJlZGljdGl2ZSBhbmFseXRpY3MgdG8gdHJvdWJsZXNob290IG5ldHdvcmsgaXNzdWVzLSBGYWN0IG9yIGZpY3Rpb24ubWQK
  add-MjAxOTA5MDUgUS1BLSBIUEUtcyBuZXR3b3JraW5nIGNoaWVmIG9wZW5zIHVwIGFib3V0IGludGVsbGlnZW50IGVkZ2UsIENpc2NvIGFuZCBtaWNybyBkYXRhIGNlbnRlcnMubWQK
  add-MjAxOTA5MDYgMi1NaW51dGUgTGludXggVGlwLSBUaGUgZW52IGNvbW1hbmQubWQK
  add-MjAxOTA5MDkgTW92aW5nIHRvIFNELVdBTi0gQ29uc2lkZXIgU2VjdXJpdHkgQ2FyZWZ1bGx5LCBTYXkgRXhwZXJ0cyBhbmQgSVQgTWFuYWdlcnMubWQK
  add-MjAxOTA5MTEgNSBXYXlzIFNELVdBTiBQcm9tb3RlIEJ1c2luZXNzIEFnaWxpdHkubWQK
  add-MjAxOTA5MTEgOCBLZXkgQ29uc2lkZXJhdGlvbnMgV2hlbiBTZWxlY3RpbmcgYSBNYW5hZ2VkIFNELVdBTiBTZXJ2aWNlIFByb3ZpZGVyLm1kCg==
  add-MjAxOTA5MTEgQ2lzY28gYWRkcyBzcGVlZCwgc21hcnRzIHRvIE1EUyBzdG9yYWdlIG5ldHdvcmtpbmcgZmFtaWx5Lm1kCg==
  add-MjAxOTA5MTEgU0QtV0FOLSBEb2VzIFlvdXIgSVQgVGVhbSBIYXZlIFdoYXQgSXQgVGFrZXMubWQK
  add-MjAxOTA5MTIgNSBNdXN0LWhhdmUgU0QtV0FOIFNlY3VyaXR5IENhcGFiaWxpdGllcy5tZAo=
  add-MjAxOTA5MTIgSUJNIHoxNSBtYWluZnJhbWUsIGFtcHMtdXAgY2xvdWQsIHNlY3VyaXR5IGZlYXR1cmVzLm1kCg==
  add-MjAxOTA5MTIgVG9wIDMgTWlzY29uY2VwdGlvbnMgQWJvdXQgU0QtV0FOLm1kCg==
  add-MjAxOTA5MTMgR05PTUUgMy4zNCByZWxlYXNlZCAtIGNvbWluZyBzb29uIGluIEZlZG9yYSAzMS5tZAo=
  add-MjAxOTA5MTMgSm9pbiBPcGVuIEphbSAyMDE5IHRvIGJ1aWxkIG9wZW4gc291cmNlIGluZGllIGdhbWVzLm1kCg==
  add-MjAxOTA5MTMgV2hhdCBwb2xpdGljcyBjYW4gdGVhY2ggdXMgYWJvdXQgb3BlbiBzb3VyY2UubWQK
  add-MjAxOTAyMTYgU29tZSBQcmVzZW50YXRpb24gU2xpZGVzLm1kCg==
  add-MjAxOTAyMjAgSW50ZXJuZXRzIG9mIEludGVyZXN0IC0xMi0gVGVzdGluZyBNaWNoYWVsIEZlYXRoZXJzLSBQYXRpZW5jZS5tZAo=
  add-MjAxOTEwMzEgRmlyZWZveCB0aXBzIGZvciBGZWRvcmEgMzEubWQK
  add-MjAxOTExMTUgRmVkb3JhIHBhc3RlYmluIGFuZCBmcGFzdGUgdXBkYXRlcy5tZAo=
  add-MjAxOTExMTggRmVkb3JhIHNoaXJ0cyBhbmQgc3dlYXRzaGlydHMgZnJvbSBIRUxMT1RVWC5tZAo=
  add-MjAxOTExMjIgU2hhcmluZyBGZWRvcmEubWQK
  add-MjAxOTExMjQgQmF1aCAtIE1hbmFnZSBTbmFwcywgRmxhdHBha3MgYW5kIEFwcEltYWdlcyBmcm9tIE9uZSBJbnRlcmZhY2UubWQK
  add-MjAxOTEyMTIgNyB3YXlzIHRvIHJlbWVtYmVyIExpbnV4IGNvbW1hbmRzLm1kCg==
  add-MjAxOTEyMTMgSG93IHRvIHVzZSB0aGUgTGludXggdW5pcSBjb21tYW5kLm1kCg==
  add-MjAxOTEyMzEgT25lIE9mIFRoZSBSZWFzb25zIFdoeSBMaW51eCA1LjUgQ2FuIEJlIFJ1bm5pbmcgU2xvd2VyLm1kCg==
  add-MjAyMDA1MDcgSG93IHRvIHJlcGVhdCBhIExpbnV4IGNvbW1hbmQgdW50aWwgaXQgc3VjY2VlZHMubWQK
  add-MjAyMDAzMTAgUnVuIEt1YmVybmV0ZXMgb24gYSBSYXNwYmVycnkgUGkgd2l0aCBrM3MubWQK
* master
  revert-c291cmNlcy90ZWNoLzIwMjAwNDA3IEhvdyB0byB1c2UgcHllbnYgdG8gcnVuIG11bHRpcGxlIHZlcnNpb25zIG9mIFB5dGhvbiBvbiBhIE1hYy5tZAo=

这些分支大致会处于下面几种状态:

  1. master分支, 指向上游的master分支
  2. 已经推送到remote仓库的开发分支.
  3. 已经发起pull request但未处理的开发分支.
  4. 已经发起pull request,且merge到master的开发分支
  5. 已经发起pull request, 且sqush或rebase到master的开发分支
  6. 已经发起pull request, 但被拒绝,但remote branch还在的开发分支
  7. 已经发起pull request, 但被拒绝,且remote branch已被删除的开发分支
  8. 尚未推送到remote的本地开发分支

实际上,这些分支还可以进一步合并为三类:

  1. master分支
  2. 推送到remote仓库的开发分支(remote branch或pull request)
  3. 未推送到remote仓库的分支

一个比较简单的方法就是使用 git branch --merged 列出所有已合并入master的本地分支然后删掉除当前分支(以* 开头的分支):

git branch --merged | egrep -v "(^\*|master)" | xargs git branch -d

不过这种方法只能删除已经merge到master的分支,其他状态的分支无法被删除,比如在我 Linux中国 的选题仓库运行 git branch --merge 的结果如下

git branch --merged |egrep -v "(^\*|master)"
add-MjAxOTA2MDUgSG93IHRvIG5hdmlnYXRlIHRoZSBLdWJlcm5ldGVzIGxlYXJuaW5nIGN1cnZlLm1kCg==
add-MjAxOTA2MTAgV2h5IGNvbnRhaW5lcnMgYW5kIEt1YmVybmV0ZXMgaGF2ZSB0aGUgcG90ZW50aWFsIHRvIHJ1biBhbG1vc3QgYW55dGhpbmcubWQK

可以看到,删除的效果不佳啊。

下面是重点来了

事实上,github 上的pull request(不管是否关闭) 都对应了一个branch,通过下面操作可以将这些branch拉取下来:

  1. 修改 .git/config[remote "origin"] 中添加下面内容(假设你的远程repo为origin):

    [remote "origin"]
      url = …
      fetch = +refs/heads/*:refs/remotes/origin/*
      fetch = +refs/pull/*/head:refs/remotes/origin/pr/*
    
  2. 运行 git fetch 拉去remote上的分支。

你会看到大量类似 remotes/origin/pr/XXXX 的分支

下面的事情就简单了,遍历所有remote上的分支,然后删除已经合并到该分支上的本地开发队列就行了:

for r in $(git branch -r --format='%(refname)'); do git branch --merged $r; done | sort -u | grep -v '^\*' | xargs -r git branch -D

当然,你也很容易发现,若远程仓库pull request特别多,那么这个循环操作也会比较慢。