问题
Lets assume you have the following structure in git
A <-- refs/heads/somebranch
|
B
| \
C D <-- refs/tags/TAG1
| |
E F
| | \
G H I <-- refs/heads/branch1
|
J <-- refs/heads/master
Now I want to find all refs that contain commit B
in their history.
So it would be nice if I could do
$ git refs --contains B
refs/tags/TAG1
refs/heads/branch1
refs/heads/master
I took a look at the git decumentation and found git branch -a --contains <commit_id>
which lists all branches that contain a commit_id
.
$ git branch -a --contains 4af9822
master
remotes/origin/someBranch
...
and I found the command git tag --contains 9338f2d
$ git tag --contains 9338f2d
someTag
anotherTag
...
Of course I can do something like this,
$ git branch -a --contains 4af9822 && git tag --contains 9338f2d
but is there a command that prints all refs at once?
回答1:
To add to torek's answer, git 2.7 (Q4 2015) will offer a more complete version of git for-each-ref, which now support the --contains
git for-each-ref --contains <SHA1>
See commit 4a71109, commit ee2bd06, commit f266c91, commit 9d306b5, commit 7c32834, commit 35257aa, commit 5afcb90, commit d325406, commit 6841104, commit b2172fd, commit b2172fd, ..., commit b2172fd (07 Jul 2015), and commit af83baf (09 Jul 2015) by Karthik Nayak (KarthikNayak).
(Merged by Junio C Hamano -- gitster -- in commit 9958dd8, 05 Oct 2015)
Some features from "
git tag -l
" and "git branch -l
" have been made available to "git for-each-ref
" so that eventually the unified implementation can be shared across all three, in a follow-up series or two.
* kn/for-each-tag-branch:
for-each-ref: add '--contains' option
ref-filter: implement '--contains' option
parse-options.h: add macros for '--contains' option
parse-option: rename parse_opt_with_commit()
for-each-ref: add '--merged' and '--no-merged' options
ref-filter: implement '--merged' and '--no-merged' options
ref-filter: add parse_opt_merge_filter()
for-each-ref: add '--points-at' option
ref-filter: implement '--points-at' option
Note that starting Git 2.13 (Q2 2017), git for-each-ref --no-contains <SHA1>
is finally supported!
See commit 7505769, commit 783b829, commit ac3f5a3, commit 1e0c3b6, commit 6a33814, commit c485b24, commit eab98ee, commit bf74804 (24 Mar 2017), commit 7ac04f1, commit 682b29f, commit 4612edc, commit b643827 (23 Mar 2017), and commit 17d6c74, commit 8881d35, commit b084060, commit 0488792 (21 Mar 2017) by Ævar Arnfjörð Bjarmason (avar).
(Merged by Junio C Hamano -- gitster -- in commit d1d3d46, 11 Apr 2017)
回答2:
There is nothing built in [Edit, Oct 2015: there is now, see VonC's new answer], but using git branch -a --contains
and git tag --contains
should get you all the references you would normally find "interesting".
There is a (non-built-in) way to find all such references. Whenever you ask about "all references", you should look at git for-each-ref. It allows you to iterate over all references, or some subset of all:
$ git for-each-ref
996b0fdbb4ff63bfd880b3901f054139c95611cf commit refs/heads/master
740c281d21ef5b27f6f1b942a4f2fc20f51e8c7e commit refs/remotes/origin/maint
996b0fdbb4ff63bfd880b3901f054139c95611cf commit refs/remotes/origin/master
7327a17171fc87d5f8f5c790eb1ba1d0e031482d commit refs/remotes/origin/next
[... snip]
efe35e936c6c32a7630086a84b2c3b3471ea534f tag refs/tags/v2.0.1
b4463ead04f1801104502ea087dbb6bdd21b4ef1 tag refs/tags/v2.0.2
3c81e95201ece182e799709c91b15a3501919d26 tag refs/tags/v2.0.3
(in this case, I have run git for-each-refs
on the repository for git itself, with no additional arguments, so it produces the default output).
All that you have to do now is to run the --contains
detector on the raw reference. While there is no plumbing command that has this as a verb, --contains
is easily expressed as a test in git merge-base, using --is-ancestor
. As the git branch documentation notes, --contains
simply tests whether the branch-tip—the SHA-1 shown above on the left of a refs/heads/
or refs/remotes/
reference—is a descendent of the specified commit. "Is a descendent of" is really the same test as "is an ancestor of", with the arguments swapped:
$ git branch --contains 996b0fd^
* master
$ git rev-parse 996b0fd^
6da748a7cebe3911448fabf9426f81c9df9ec54f
Since master
is 996b0fd...
, --contains
matches for both 996b0fd
and its first parent 6da748a...
, so we can tell if we have our git merge-base --is-ancestor
arguments the right way around:
$ git merge-base --is-ancestor 6da748a 996b0fd && echo ok
ok
Note that 996b0fd
is considered an ancestor of itself:
$ git merge-base --is-ancestor 996b0fd 996b0fd && echo ok
ok
So all you need to do is string together a git for-each-ref
and a shell command or loop that runs git merge-base --is-ancestor
.
来源:https://stackoverflow.com/questions/24993772/how-to-find-all-refs-that-contain-a-commit-in-their-history-in-git