1

I am doing the scary work of deleting local branches. I fetch the latest from our integration branch:

git fetch origin dev

and then I want to compare origin/dev with feature branches locally. But I do not want to check out origin/dev. I believe the goal is to ensure the git log for origin/dev contains the tip (latest commit) of each feature branch.

The following command is not very effective on my case:

git branch --contains <commit-id>
  1. it is unclear how to use a branch other than the current one.
  2. I don't have the commit-id of the comparison branch, but I do have the branch name.

So I am looking for an alternative command:

git branch origin/dev --contains-branch <some-feature-branch>

is there a command that can do this?

  • Would a command which prints all non-merged branches help in your case? I don't have access to my other machine at the moment, but I could look up the command in my alias later and post an answer. It works for my use-case, YMMV – knittl May 08 '20 at 05:41
  • `git branch --no-merged upstream` is what I have. `git branch` is *not* a plumbing command, so it shouldn't be used in scripts (but it works for my use case and I only use it in a non-destrcutive manner) – knittl May 08 '20 at 06:10

2 Answers2

0

What git branch --contains does is:

  1. Resolve HEAD to a commit hash. If you're on branch br, this resolves the name br to a commit hash. (Let some variable h represent this hash.)
  2. For all branch names:

    • Resolve the name to a commit hash. Let's call this b for the branch-hash.
    • Test ancestor-ness: is b an ancestor of h?
    • If b is an ancestor of h, print this branch's name.

The command is now complete: it has listed the names of branches whose branch-tip commit is contained in this branch.

Your problem is very similar, but simpler. You want to resolve the name origin/dev to some hash h. Then, for some subset of your branch names—the feature names you care about—you want to:

  • Resolve the name to a hash ID b.
  • Test ancestor-ness: is b an ancestor of h? (In other words: s this branch's tip commit contained within origin/dev by being an ancestor of the tip commit of origin/dev?)
  • If so, print the name.

There is a command that implements this "is-ancestor" test, but it's a bit surprising: it's git merge-base. The command:

git merge-base $b $h

reports success (exits zero) if $b is an ancestor of $h, and reports failure (exits nonzero) if not. The "is ancestor" test it implements is the same one that git branch --contains uses so this directly provides the answer you want.

The remaining problem is to find the desired branch name(s). The git for-each-ref command is literally designed to do just that.

Hence:

hname=origin/dev
h=$(git rev-parse $hname) || die "cannot get commit hash ID for $hname"
git for-each-ref --format='%(refname:short) %(objectname)' refs/heads/feature |
    while read name b; do
        if git merge-base --is-ancestor $h $b; then
            echo $name is contained within $hname
        fi
    done

(note: untested). The refs/heads/feature here represents branches named feature/*; if you have a different pattern you need to match, use that different pattern, or use refs/heads itself and put something in the loop to skip "uninteresting" branch names, or whatever it takes.

See the documentation for git merge-base and git for-each-ref (and git rev-parse, for that matter) for details.

torek
  • 448,244
  • 59
  • 642
  • 775
0

To list the branch tips that are reachable from origin/dev, you can use

git branch --merged origin/dev

Note that this does list origin/dev if it is a local branch (which I assume is not in your case).

If you want to script around git branch, be warned that it is a "porcelain" command, i.e., its output format is not cast in stone and may change in a future version of Git.

j6t
  • 9,150
  • 1
  • 15
  • 35