Git help - branches with no commits?
-
Is there a way/command to find all branches (on the remote) that have no commits in them? Or I guess no commits unique to them.
Our Stash server is choking on like 500 branches, many of which have never been touched. (Created for a bug; turned-out the bug didn't need a code change. Created for a feature, feature got canned before any work was done. Etc.)
I'd like to grab a list of branches it's safe to delete.
Ideally, and I don't know if Stash allows this, but to actually delete them from the remote without having to click all of them in the web UI individually.
-
Is there a way/command to find all branches (on the remote) that have no commits in them?
I'm not sure it's even physically possible to have a branch without commits. But I might be wrong.Or I guess no commits unique to them.
Define "unique". Change history isn't stored in branches, but in commits themselves (to be exact, in the parent relations of commits). A branch can be thought of as a mutable pointer to commit, with some bonus functionality around it.First, let's say what it means to be unique. I think the best definition is that a branch is unique if it's not equal to any other branch. And what does "equal" mean? Well, here's the fun part - since branches are technically just pointers to commits, there isn't an immediately obvious answer. I can think of at least three equality relations that make sense:
-
Branches A and B are equal if they point to the same commit. Easiest, but might not really be the solution of your problem - for example, if I make branch A off a branch B, and later commit something to branch B, is branch A unique or not?
-
Branches A and B are equal if they point to the same commit, or if A points to ancestor commit of B' head, or if B points to ancestor commit of A's head. In other words - if branches never diverged, they're equal. Seems to be exact fit for the problem, but there's one caveat - if A and B merge, they become equal (merge commits have two parents - even though only one of branches changes during a commit, one head becomes a child of the other). This might give you quite a few of false positives - but depending on what you need exactly, they might not be false at all.
-
Same as 2, but when looking for ancestry of merge commits, you only take the first parent into consideration (yes, parents are ordered - when you merge, the first parent is the former head of current branch, and the second parent is the head of the branch you merge from). This solves the problem mentioned above - but only for regular merges; fast-forward merges are still problematic, since if they happen, the branches being merged haven't really diverged, so you cannot even check for it. Also, and I must not that I might be entirely wrong here, if you merge branch A into branch B, and then fast-forward-merge A to the merged B, the order of parents (from A's perspective) might reverse, which will give you a false positive - but depending on your branching and merging strategy, you might not encounter this problem at all (I think that if you never fast-forward-merge on your master/trunk/whatever, you should be safe).
To sum it up - if you don't need to keep branches that have been merged into master/trunk/whatever, then option 2 should do. However, if you have to keep them for whatever reason (you shouldn't, but that discussion is for another topic (ha, ha)), you're pretty much fucked, and most likely have to go through all branches by hand.
-
-
I'm not sure it's even physically possible to have a branch without commits.
Ok, keep in mind that I am not a pedantic dickweed. I think you know what I meant: the branch doesn't have any commits unique to that branch. (Obviously, it has the full set of commits of its parent branch to the point where it was created.)
To sum it up - if you don't need to keep branches that have been merged into master/trunk/whatever, then option 2 should do.
I don't; Stash removes those when you merge.
However, if you have to keep them for whatever reason (you shouldn't, but that discussion is for another topic (ha, ha)), you're pretty much fucked, and most likely have to go through all branches by hand.
? I don't want to keep branches that have been merged. You lost track of the question.
I want to get rid of branches that were created, but then never had any commits made to them.
-
I want to get rid of branches that were created, but then never had any commits made to them.
Assuming those branches were based on master and assuming you don't care about the difference between those branches and fully merged branches:
git branch --merged master
I just checked: Branches with no commits are treated like merged branches.
-
What is that command instructing Git to do? Merely list the branches? Or check them out?
I just want a list.
EDIT: looks like I need to add --list to it?
EDIT EDIT: this:
git branch --list --merged master
Gave me this:
fatal: malformed object name master
-
It's only listing them.
If you want to include remote branches in that list which you never checked out (as I assume you do):
git fetch # make sure you get the latest list of remote branches git branch --merged -a # -a is "include remote branches"
EDIT: looks like I need to add --list to it?
On my system, it worked without that.
-
git branch --merged -a # -a is "include remote branches"
That just gave me:
fatal: malformed object name -a
-
> git branch --list --merged master
Gave me this:
fatal: malformed object name master
Huh? Okay, which git version are you using? I'm using:
git --version
git version 2.5.0
-
Ok, keep in mind that I am not a pedantic dickweed.
Ok, I'll keep in mind you don't know shit about how Git works.I think you know what I meant: the branch doesn't have any commits unique to that branch.
I spent whole five paragraphs explaining that even though I know that you mean branches without unique commits, it's still not enough to tell what you actually need, since that statement is very ambiguous in itself.I don't; Stash removes those when you merge.
OK, then go with option 2.? I don't want to keep branches that have been merged.
Yes I know, you just said that. "Just" is a perfect word here, actually - because you only clarified that in this post, which didn't exist when I was writing my previous reply, and OP says nothing at all about merges.I want to get rid of branches that were created, but then never had any commits made to them.
On its own, this sentence totally contradicts what you just said about not wanting already merged branches. Only if you put it in context of merged branches being already deleted, or have some shoulder aliens handy, you can decode the actual meaning of this sentence.
-
1.9.5.github.0
But I'm using Github's version of the Git command line app.
-
Wait a second, I think I have 1.9.5 installed on my Surface. I'll check there.
-
If you just want to call me an idiot, don't post in a General Help thread. You can use any of the other categories for that.
-
1.9.5.github.0
Get a newer Git then. Usually, multiple Git versions can be installed side-by-side, as long as you keep them in separate environments. Also, with a bit of luck, you can operate on your repo using newer version and then go back to older version without any problems - without even having to clone again.
-
-
Ok, I just noticed I have 2.6.2 installed on my Surface. But I googled a bit, and it seems the the order of arguments is important in earlier git versions. Try the following:
git branch -a --merged master
Also explains the second error message you got: Git thought you wanted it to list the branches merged into commit "-a", which doesn't exist.
-
Get a newer Git then.
You're not helping - potentially ruining his setup is not the first option.@Gaska said:
with a bit of luck
What could possibly go wrong?Seriously, stay within the boundaries of the problem or call it quits.
-
If you just want to call me an idiot
Where's fun in that? Walls of text are so much better.don't post in a General Help thread.
I posted in a thread where the post I'm replying to is located, and 100% of my reply is w.r.t. that post. And that post is a reply to a post I've previously written. I like to keep a single discussion in a single topic, thank you very much.
-
-
I 'd you above.
-
I tried:
git branch -a --list --merged master
Still got:
fatal: malformed object name master
I can't find any additional information about that error message, natch.
-
Ok now we're cooking:
git branch -a --list --merged > test.txt
test.txt is literally 55k. Ouch.
... and... it's wrong. It apparently contains a list of ALL branches, including the ones we obviously can't delete, like "Development" and "Release/v4.5". Sigh. Looks like it just listed all branches, past and future, as far as I can tell.
-
I just found a system on which I have Git 1.7 installed. I can now reproduce that
git branch -a --list --merged master
doesn't work. Can you try without
--list
? Git seems to be confused by the combination--list
and--merged
.
-
I'm afraid if I remove the --list it'll do something other than merely giving me a list of them, which is what I want.
Well, whatever, I can blow away the repo and start over if it doesn't work I suppose.
EDIT: removing --list produced a test.txt that is, as far as I can tell, identical.
I suppose it's possible that it's correct-- I noticed it listed most of the "release/vX.X" branches but did not list "release/v6.1" which has commits that are not in development. Hm.
-
You're not helping - potentially ruining his setup is not the first option.
I assumed @blakeyrat is proficient enough with computers to know what separate environments mean. That, or he doesn't, but applied some common sense and extreme caution and concluded that a virtual machine is separate enough. Also, in case anything goes wrong, he can always make a new clone.What could possibly go wrong?
The most likely issue is new Git simply refusing to work with old repo, or old Git refusing to work on repo tampered with new Git. In both cases, new clone fixes issue. And the probability of any other problem is trumped by probability of getting fatally stabbed with knife on the street.
-
I'm afraid if I remove the --list it'll do something other than merely giving me a list of them, which is what I want.
The worst thing that can happen is that it creates a new branch.
git branch
seems to have two modes:- List branches (happens when you use any special options)
- Create a branch (happens when you don't use one of the options and supply a name instead)
I suppose it's possible that it's correct-- I noticed it listed most of the "release/vX.X" branches but did not list "release/v6.1" which has commits that are not in development. Hm.
Hm, ok, so it works but doesn't give you the information you need. Damn.
-
I'm spot-checking some of the output with our Stash server. Let me get a sample size of 10 or so and I'll report back.
EDIT: nope, it's a big ol' list of branches that, as far as Stash is concerned, literally don't even exist. WTF.
EDIT EDIT: Ok I tested a shitload of them. The vast majority do not exist, according to both Stash and SourceTree. I tested about 12 of 'em and of those 11 did not exist.
One did exist, and did indeed have no commits.
So. I have no idea what to do with this. I guess if I could get a list of actual branches from SourceTree and correlate it with this list, maybe I can narrow it down?
-
EDIT: nope, it's a big ol' list of branches that, as far as Stash is concerned, literally don't even exist. WTF.
Have you done a
git fetch
recently? if not, do so now so you have up-to-date information on the remote branches on your local machine.Also, you need to filter the output: All the branches in the output without a prefix are your local branches, the branches you're interested in are the ones starting with
remotes/origin/
.
-
Have you done a git fetch recently?
Yes, back when you told me to.
Also, you need to filter the output: All the branches in the output without a prefix are your local branches, the branches you're interested in are the ones starting with remotes/origin/.
I've already deleted my local branches before starting on this quest. I can confirm only one is listed, "development", which is the one I didn't delete.
-
Ok, last attempt: If
git branch -a --merged | grep "remotes/origin" > test.txt
does not work (make sure you use Git Bash, I'm pretty sure CMD doesn't have grep) then Stash is doing something really weird there.
-
if you are only interested in remote branches change the
-a
to-r
-
EDIT: nope, it's a big ol' list of branches that, as far as Stash is concerned, literally don't even exist. WTF.
More likely, they indeed do exist in repo, but Stash just hides them in its UI. I'm pretty sure you could check them out and push changes just fine. Don't do that, though - it might completely break Stash (and/or other tooling).
-
No, that won't work, I just tested that. Git will list all remote branches if you use -r, not just the merged ones. Seems like -r wins over --merged.
-
Just eyeballing the output, I can already tell you that will return the same list. But why not.
Yup. Identical. It's just missing "development" on the top line.
The thing is, I think it is showing me what I need, it's showing me all branches that are 100% merged into development (our version of master), the problem is that list isn't useful for what I want to do because it's not just OPEN branches, but also the ones from long-lost history.
What I need to do is compare this list of 427 branches to a list of currently open branches to trim it down, but that's getting to be more effort than just having a "neater" Stash screen is worth.
-
What do you mean by an "open" branch?
-
The thing is, I think it is showing me what I need, it's showing me all branches that are 100% merged into development (our version of master), the problem is that list isn't useful for what I want to do because it's not just OPEN branches, but also the ones from long-lost history.
Ah, so you want to sort/filter them by date? Should be doable somehow, but that'll make it a bit more complicated.
-
What do you mean by an "open" branch?
Well to answer your question backwards:
If I try to delete branch AA-32636 in Stash, I can't because it's not listed at all in Stash. That's the case for something like 90% of these.
So I need to get a list of branches I can actually delete from Stash based on this list.
The interesting thing is that now that I look again and open all the expandos, I think SourceTree is also grabbing this big-ass long list of 427+ branches instead of just the 150 or so Stash lists. So something funky's going on here.
-
Ah, so you want to sort/filter them by date? Should be doable somehow, but that'll make it a bit more complicated.
Maybe? I need to sort/filter them by whatever criteria Stash uses to sort/filter them, but I don't know what that criteria is.
-
-
So something funky's going on here.
Probably an optimization in Stash where it forgets about old branches.
What you could do is check whether you have the permission to delete remote branches from the command-line, then we could script something with the commands we already have and you could also delete those branches Stash has forgotten about.
Can you take 1 branch you definitely want to delete and check whether the following succeeds?
git push origin --delete <branch>
-
That's great, but it requires going through one-by-one and that ain't gonna happen.
Also it's not sufficient to see if it's merged into "development" (our version of "master"), it also can be merged into any of the "release/vX.X" branches.
... maybe this is what I need to do:
Clean-up outdated references:
$ git remote prune origin
Maybe most of that big ol' list are "outdated" references? I don't know.
EDIT: I ran git fetch -p as that article recommends for removing stale references (which I guess are the same as outdated references? It changed terminology on me without a clutch), and now it's just spewing hundreds of branch names, haha.
EDIT EDIT: AHA! That was it. Now when I run the last command, test.txt only contains about 50 branches. That's more reasonable. Only about 30 of which aren't "release/vX.X" branches...
Of course now the list seems suspiciously TOO FEW branches. Hm.
-
Maybe most of that big ol' list are "outdated" references? I don't know.
Ah, yeah, that could explain the different branch lists on your local system and in Stash as well. I'd try
git remote prune origin
, then.
-
That's great, but it requires going through one-by-one and that ain't gonna happen.
There's always xargs...
-
AHA! That was it.
So mystery solved, then?
TIL that git fetch doesn't delete old references to branches that don't exist anymore. I always assumed it did.
-
So mystery solved, then?
Nope.
I'm spot-checking the list, and at least 3 of the entries have commits. So I think the git branch command never worked right in the first place, but its failure was obscured by the hundreds of junk branches in the output.
Ok interesting. The list seems to contain the branches I'm wanting to delete and branches that have open pull requests associated with them for some goddamned reason. Sigh.
-
at least 3 of the entries have commits
Commits that were not merged into master? Because if those where merged into master, then this is expected behavior.
-
Commits that were not merged into master?
None of our commits are merged into master; we don't have a "master". I'm sure you're implying something other than a literal branch named "master", but if so it's sailing right over my head.
-
I'm sure you're implying something other than a literal branch named "master", but if so it's sailing right over my head.
Ah, yeah, I mean merged into the branch you were on when you executed
git branch -a --merged
-
None of our commits are merged into master; we don't have a "master". I'm sure you're implying something other than a literal branch named "master"
"master" is a literal branch name in the command:
git branch --list --merged master
Which might explain
fatal: malformed object name master
and the lack of useful results. What do you use for your main production branch, ala trunk in svn?
-
Ah, yeah, I mean merged into the branch you were on when you executed git branch -a --merged
Oh, this is literally the first time you've mentioned that it's important what branch I'm currently on. Haha.
The problem is we have maybe a dozen branches that are all "master" branches, and I can't be on them all simultaneously. (Hopefully not TOO many but at least a few have hotfixes in them that are not also in "development".) So... not sure if this strategy works for me at all.
-
Oh, this is literally the first time you've mentioned that it's important what branch I'm currently on. Haha.
Ah, ok, short explanation:
git branch -a --merged
Lists the branches (remote and local) merged into the current branch.
git branch -a --merged <branch-name>
Lists the branches merged into <branch-name>.
I was a bit confused by the error message you got, which is why I suggested the first form later, but if you literally don't have a master branch, then it all makes sense now. I take everything back: Use the second version all the time.