4

Here is the scenario: Someone has a git repository (on github if that matters) which I've forked. In my branch, I've created lots of branches with changes that the upstream has pulled. Or in some cases applied the diff of my branch instead of properly pulling the branch.

What I want to find is a list of all my branches that has been fully merged into upstreams master branch. That way I can then safely delete those branches because git branch and other tools list 30+ branches and it looks cluttered.

I thought I explained properly why this question is different from the linked one: Or in some cases applied the diff of my branch instead of properly pulling the branch e.g. git branch --mergedis not a full answer, but oh well...

Björn Lindqvist
  • 19,221
  • 20
  • 87
  • 122
  • 1
    Voting to reopen as detecting merged branches with an upstream repo (multiple remotes) is a subtly but significantly different problem - blindly applying advice for detecting already-merged branches will delete e.g. origin/master if it's unmodified. – AD7six Feb 25 '14 at 09:57

2 Answers2

5

Depending on how often you wish to do that, you may want to do it manually (explained first) or script it (explained afterwards).

In the below it is assumed there is an "upstream" remote pointing at the repòsitory you forked e.g.:

$ git remote -v
origin git@github.com:you/repo.git (fetch)
origin git@github.com:you/repo.git (push)
upstream git://github.com/original/repo.git (fetch)
upstream git://github.com/original/repo.git (push)

List all local branches that are merged

To get a list of branches that have already been merged with the upstream master:

$ git fetch upstream
$ git branch --merged upstream/master
my-feature
my-other-feature
master

You can then delete them:

$ git branch -d my-feature
$ git branch -d my-other-feature

If changes were applied manually, they may not be in the upstream history, and so the local branch with the equivalent code changes may not be listed. Branches where this is the case should be detected manually.

Delete local references to remote branches that don't exist

If you have local branches that represent a remote branch which has already been deleted (the branch was for a pull request, and after merging the pull request you deleted the branch via github's gui) there is a standard git command for cleaning up these branch references:

$ git remote prune origin

Delete remote branches which have been merged

To get a list of remote branches in your fork which have been merged, is similar to the first command - but use the -r switch (remote branches):

$ git branch -r --merged upstream/master
origin/my-feature
origin/my-other-feature
upstream/someone-elses-branch

To delete your own remote branches that have been merged:

$ git push origin :my-feature
$ git push --delete origin my-other-feature # equivalent, just different syntax

Script solution

Be careful using a scripted solution as it's quite easy to accidentally delete a branch you didn't intend to. That said, remember that branches are just references/names - deleting a branch (by accident) doesn't mean the contents are lost they can easily be recovered (just check git reflog for example).

The below script deletes local and remote branches which begin with "feature" - this is one way to ensure your own up-to-date master branch, for example, doesn't get deleted by accident.

#!/bin/bash
# E.g. save as ~/bin/git-cleanup, make executable and have ~/bin in your path
# call as `git cleanup`

echo -n "Fetching latest ... "
git fetch origin
git fetch upstream
echo "done"

echo "Deleting merged local feature branches ... "
branches=`git branch --no-color --merged upstream/master`
for branch in $branches;
do
    if [[ "$branch" =~ ^feature ]]; then
        git branch -d $branch;
    fi
done
echo "done"

echo "Deleting local refs to remote branches that don't exist ... "
git remote prune origin
git remote prune upstream
echo "done"

echo "Deleting merged remote feature and hotfix branches ... "
branches=`git branch -r --no-color --merged upstream/master`
for branch in $branches;
do
    if [[ "$branch" =~ ^(origin)\/(feature.*$) ]]; then
        remote=${BASH_REMATCH[1]};
        branch=${BASH_REMATCH[2]};
        git push $remote :$branch;
    fi
done

echo "done"

echo ""
echo "all finished"
AD7six
  • 63,116
  • 12
  • 91
  • 123
  • 1
    Certainly more detailed than my answer ;) +1 If "accidents" can happen when deleting a branch, `git reflog` can come in handy indeed. – VonC Feb 24 '14 at 10:06
  • Unfortunately most of my branches are of the "Branches where this is the case should be detected manually." kind. :( I really need a way to automate that. – Björn Lindqvist Feb 24 '14 at 13:02
  • Then there's something quite wrong with your git work flow =). – AD7six Feb 24 '14 at 13:57
  • I know, thanks for the script though. I just think it should be possible to extend it to work for wrongly applied branches too, possibly by comparing diffs. – Björn Lindqvist Feb 24 '14 at 23:17
  • Btw, I don't think upstream is doing anything wrong at all, it's git that is stupid and does not create a proper merge if you don't use `git merge --no-ff`. Fast forwards aren't listed in `git branch --merged`. – Björn Lindqvist Feb 24 '14 at 23:25
  • I didn't say the upstream was doing anything wrong. Whether it's a ff merge or not git will correctly identify merged branches. If a branch contains a single commit, that commit eas cherry picked but the branch not merged, git will correctly identify the branch as merged. _Only_ if the changes were applied manually, incompletely or "differently" (rebasing) will git not identify already-changed changes. – AD7six Feb 25 '14 at 07:52
  • Ok I honestly don't know why the "merged" branches aren't listed correctly. But they just aren't. Perhaps `git merge --squash` was used, which seem to be one scenario in which "forgets" that the branch was merged. – Björn Lindqvist Feb 25 '14 at 09:44
  • Indeed it would - Finding merged branches is basically "is the head (commit hash) of this branch in the history of upstream/master" (whilst ignoring merge commits). If commits are being squashed the simple and immediate answer is: no - It's correct and accurate to say that your branches are not in upstream/master, as they don't share common history in that case. If that's what's been happening you'll need to e.g. checkout your branch, merge in upstream/master, resolve conflicts (and this is why it can't be automated) diff to upstream/master and check for no differences. Good luck =). – AD7six Feb 25 '14 at 09:50
1

If you have updated your fork with the latest commits from the upstream repo (the one you have forked), you can then follow "How can I delete all git branches which are already merged?".

git branch --merged | grep -v "\*" | xargs -n 1 git branch -d 

But that will only list merged branches (through pull requests for instance), not cherry-picking (when only the diff of one branch is applied to another).

For the second category, you might have to diff those local branches and see if they still have differences that aren't part of the main branches yet.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250