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.


  • Banned

    @blakeyrat said:

    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.

    @blakeyrat said:

    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:

    1. 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?

    2. 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.

    3. 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.



  • @Gaska said:

    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.)

    @Gaska said:

    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.

    @Gaska said:

    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.


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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


  • Winner of the 2016 Presidential Election

    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"
    

    @blakeyrat said:

    EDIT: looks like I need to add --list to it?

    On my system, it worked without that.



  • @asdf said:

    git branch --merged -a # -a is "include remote branches"

    That just gave me:

    fatal: malformed object name -a


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    > 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


  • Banned

    @blakeyrat said:

    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.

    @blakeyrat said:

    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.

    @blakeyrat said:

    I don't; Stash removes those when you merge.

    OK, then go with option 2.

    @blakeyrat said:

    ? 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.

    @blakeyrat said:

    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.


  • Winner of the 2016 Presidential Election

    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.


  • Banned

    @blakeyrat said:

    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.


  • Winner of the 2016 Presidential Election

    @Gaska said:

    Get a newer Git then.

    I think Visual Studio wants 1.9.5 for some reason.


  • Winner of the 2016 Presidential Election

    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.


  • Fake News

    @Gaska said:

    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.


  • Banned

    @blakeyrat said:

    If you just want to call me an idiot

    Where's fun in that? Walls of text are so much better.

    @blakeyrat said:

    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.



  • @asdf said:

    git branch --merged -a

    try:

    git branch -a --merged

  • Winner of the 2016 Presidential Election

    I :hanzo:'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.


  • Winner of the 2016 Presidential Election

    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.


  • Banned

    @JBert said:

    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.

    @JBert said:

    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.


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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)

    @blakeyrat said:

    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?


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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/.



  • @asdf said:

    Have you done a git fetch recently?

    Yes, back when you told me to.

    @asdf said:

    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.


  • Winner of the 2016 Presidential Election

    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


  • Banned

    @blakeyrat said:

    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).


  • Winner of the 2016 Presidential Election

    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?


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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.



  • @Pharylon said:

    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.



  • @asdf said:

    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.




  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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.


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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.


  • Banned

    @blakeyrat said:

    That's great, but it requires going through one-by-one and that ain't gonna happen.

    There's always xargs...


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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.



  • @asdf said:

    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.


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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.



  • @asdf said:

    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.


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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


  • I survived the hour long Uno hand

    @blakeyrat said:

    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:

    @blakeyrat said:

    git branch --list --merged master

    Which might explain

    @blakeyrat said:

    fatal: malformed object name master

    and the lack of useful results. What do you use for your main production branch, ala trunk in svn?



  • @asdf said:

    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.


  • Winner of the 2016 Presidential Election

    @blakeyrat said:

    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.


Log in to reply