Use git rev-list to exclude a branch, but keep common ancestors with the included branches

耗尽温柔 提交于 2019-12-23 10:24:15

问题


Say I have the following git repo:

I want to specify a git rev-list of all branches (--all) but excluding a given branch (say, feature-D), but I want to show common ancestors of feature-D and the other branches i.e. commit Initial and 1.

If I try git rev-list --all ^feature-D commits Initial and 1 are excluded:

Of course I can list all branches except for feature-D explicitly (git rev-list feature-A feature-B feature-C) to get what I want:

But is there a way to specify a rev-list that obtains the prior result, only by reference to feature-D?

(Note that this question was inspired by this answer: https://stackoverflow.com/a/20978568/430128)


回答1:


This is a bit indirect but seems to work. We start with:

git rev-list --no-walk --all ^feature-D

which produces a list of all SHA-1s directly pointed to by any ref (including tags, branches, stashes, etc; use --branches rather than --all if you want only branches) except those under feature-D:

$ git log --oneline --decorate --graph --all
* 74e0d3e (feature-D) 7
* 0448a13 6
| * ab3a532 (feature-C) 5
|/  
| * 50477c7 (feature-B) 4
| * 28717e5 3
|/  
* 2ac5cef (HEAD, master, feature-A) 2
* c6a10b4 1
* 76c511f Initial
$ git rev-list --no-walk --all ^feature-D
ab3a5320e792e945b896634d667df5ace4a8b871
50477c7b28b5479587e45fe97292e71a3c0851c5
28717e5628ad111e8b68323dc485fd190a780446

Now we feed this to git rev-list again to get the history-walking:

$ git rev-list --no-walk --all ^feature-D | git rev-list --stdin
ab3a5320e792e945b896634d667df5ace4a8b871
50477c7b28b5479587e45fe97292e71a3c0851c5
28717e5628ad111e8b68323dc485fd190a780446
2ac5cefb971c7f5c5be33a77a6db71127982eaf5
c6a10b4568136d65b31b7e262ef7745db4962460
76c511fcf38076e0ea98db73a4923de6fc806b4a

Pipe that to git log --oneline --decorate --graph --stdin to verify that those are the right commits, if eyeballing it is not sufficiently clear:

* ab3a532 (feature-C) 5
| * 50477c7 (feature-B) 4
| * 28717e5 3
|/  
* 2ac5cef (HEAD, master, feature-A) 2
* c6a10b4 1
* 76c511f Initial

(and one side note: if using git log --oneline --decorate --graph to view your middle version, i.e., git rev-list --all ^feature-D, you need to add --boundary to see commit 2; gitk probably adds --boundary to make it show up, not that I tested this).

There's a defect here: if feature-C and feature-B branches did not exist you would not see commits 2 through Initial. I'm not sure, but I think the only real fix for this is to use something much more complex.


Edit: ok, not that much more complex. Start with git for-each-ref to print branches. Pipe through grep -v ^feature-D$ to eliminate feature-D. Now you have all the branches you want as arguments:

$ git log --oneline --graph --decorate $(git for-each-ref \
>  --format '%(refname:short)' refs/heads/ | grep -v '^feature-D$')
* ab3a532 (feature-C) 5
| * 50477c7 (feature-B) 4
| * 28717e5 3
|/  
* 2ac5cef (master, feature-A) 2
* c6a10b4 1
* 76c511f Initial



回答2:


Simple solution

Instead of listing all branches explicitly (either manually or with a complicated program), we can use a regex to include --all refs and --exclude a specific one.

According to the man page, the --all flag will

Pretend as if all the refs in refs/ are listed on the command line as <commit>.

Ideally, we would pretend that all of the refs in refs/ except for feature-D are listed on the command line as <commit>.

So to exclude feature-D we can use --exclude=refs/heads/feature-D --all.

Note: The flag order is important.


A note for gitk users:

(Probably relevant since the OP image includes a gitk screenshot.)

I originally came across this question while trying to ignore a branch in gitk and I noticed a small gitk bug that my answer above will trigger. If gitk is started with the above flags, then everything works fine. However, if after starting gitk the the view is modified from within the GUI menu, the flag ordering seems to get changed and cause the branch to reappear. (This will happen if the OK button is pressed even if no actual changes are made.) I was unable to figure out how to fix this while still using the --all flag, but I have a workaround.

Instead of using --all, we can use --branches=* --tags=* --remotes=* stash.

So to exclude feature-D, we can modify this to say --exclude=refs/heads/feature-D --branches=* --tags=* --remotes=* stash.

We can then start gitk using gitk --exclude=refs/heads/feature-D --branches=* --tags=* --remotes=* stash and editing the view through the GUI no longer causes this bug.



来源:https://stackoverflow.com/questions/20979339/use-git-rev-list-to-exclude-a-branch-but-keep-common-ancestors-with-the-include

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!