2

I have a development branch called Atomics (more correctly, atomics). Its finished being developed. It tested OK, so it was merged into Master. I'm now ready to completely delete it since its no longer needed.

I really thought this question was clear and answered well: Delete a Git branch both locally and remotely. Alas, it did not work...

Here's what I did:

# Accepted answer, succeeded
$ git push origin --delete atomics

# Shit, it did not delete the local branch
$ git branch -a
...
* master
atomics

# Another answer. Whoops, causes an error
git push --delete origin atomics
error: unable to delete 'atomics': remote ref does not exist

# Try yet another answer:
git fetch --all --prune

# Check again
$ git branch -a
...
* master
atomics

According to Torek's answer:

$ git branch -d atomics
error: The branch 'atomics' is not fully merged.

# See below on the prelude that may have caused this
# (yet another Git black-hole)

# Yep, did not work
$ git fetch --prune origin
$ git branch -a
* master
atomics
...

In case it matters, this is Apple's Git:

$ git --version
git version 1.8.5.2 (Apple Git-48)

At this point, I have two questions. First, how do we delete local and remote branches that actually works? This may seem redundant given the cited question, but the question does not appear to be answered even though an answer was accepted.

Second, how do I recover from this special Git state where things are partially deleted?


Here's the prelude. The --squash is to ensure the log on Master is accurate.

$ git checkout master
$ git merge --squash atomics
# These files already existed in Master, but a 'git push' after the merge
# did nothing. Also, 'git status' showed they were modified.
$ git add a.h b.h a.cpp b.cpp
$ git commit a.h b.h a.cpp b.cpp -m "Mergae Atomics into Master"
$ git push

EDIT: I accepted Torek's answer because the man worked too damn hard not to get something out of it. Unfortunately, the procedure did not work for this question; and it did not work on the following experiment, when another dev branch was deleted. The tool is broken, and there's no amount of Q&A that can fix it.

Community
  • 1
  • 1
jww
  • 97,681
  • 90
  • 411
  • 885
  • You could try reading the `-D`ocumentation... – jthill Jun 15 '16 at 03:04
  • @jthill - or, the tool could be fixed :o But to answer your question, yes, I spent copious amounts of time in the man pages. And then I still have to follow up on Stack Overflow, which wastes more time. So much time is spent that some consider it a waste of time. – jww Jun 15 '16 at 03:05
  • We [stopped using Git development branches](https://groups.google.com/d/msg/cryptopp-users/WvyI6Z63z1I/8xM9cAqRBAAJ). All of these problems are now solved. I cast the first close vote using the reason ***Problem can no longer be reproduced***. – jww Jun 18 '16 at 01:55

3 Answers3

7

TL;DR: use (at least) two commands, in any order. Delete local (and or remote-tracking, which is also local). Delete remote.

In your output, atomics is a local branch. To delete it, make sure you're not on it (you aren't, it would have been prefixed with *, that's just a general first step) and then use git branch -d to delete it.

[Edit to account for the edited-in remark about what happened with git branch -d: If Git complains that the local branch is not fully merged, Git is trying to tell you that there may be some commit(s) you will lose access to, that are not saved anywhere else. If that's OK—if you really do mean to ditch them, or if you mean to keep them but are quite sure they are safely saved somewhere—you can tell Git to force the delete, using git branch -D or git branch --delete --force. This instructs Git to ignore the commits that may be abandoned. End edit]

The push --delete stuff is to tell some other Git, at some remote site, to delete his local branch. What happens on some other site does not affect your local stuff in general. There are a few exceptions—in particular git fetch and git push can make local changes—but by default neither one affects any local branch at all, regardless of what it succeeds or fails at getting some other Git to do.

You may also need git fetch --prune origin to delete your remote-tracking branch (which, despite the name, is local to your repository). You can instead use git branch -r -d origin/atomics (locally delete remote-tracking branch origin/atomics).

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thanks Torek. All of the commands I executed were on Master, not Atomics. – jww Jun 07 '16 at 02:56
  • Thanks again, Torek. I've merge a [dev branch into master](http://github.com/weidai11/cryptopp/commit/c1f025343a1cfd4d2acb7e1eeb83eea935a7a4cf), so I'm ready to try another experiment. Would you please clearly state what commands you are referring to in these sentences: *"use (at least) two commands, in any order. Delete local (and or remote-tracking, which is also local). Delete remote...."* For the purposes of this discussion, I'm on *Master* and I want to delete *Unwanted* ***everywhere***. – jww Jun 14 '16 at 23:24
  • I am a bit confused by your uppercase notation here, and quite lost now that the original question has been edited several times. The *current* version of the question shows that there is no branch `atomics` on the remote named `origin`, though, so at this point the only thing to do is manipulate it locally. `git branch -d atomics` will delete the local branch `atomics` *if* this is totally safe, and `git branch -D atomics` will delete it regardless of safety. – torek Jun 14 '16 at 23:54
  • If you think uppercase is confusing, then you should try Git and ***completely*** deleting a branch ***everywhere***. I've literally wasted hours on this simple concept. To get back to your question, I want this branch to go away ***everywhere***: [Alignas branch](http://github.com/weidai11/cryptopp/tree/alignas). it was a dev branch, and its no longer needed because its been merged into Master. What are the two commands that will accomplish it? – jww Jun 15 '16 at 00:44
  • Alas, I *also* find the github web interface terribly confusing. So, I cloned the repository from GitHub (`git clone git://github.com/weidai11/cryptopp`), which lets me work a lot more directly with it. Let's ditch this particular answer-so-far, which is about a different repository and/or branch name. (I'm putting in a second answer, this will take a bit.) – torek Jun 15 '16 at 00:52
  • OK, sounds good. Thanks. For what its worth, ***completely*** deleting `alignas` dev branch now is the same question as ***completely*** deleting `atomics` dev branch then. And it will be the same problem as ***completely*** deleting the `solaris` dev branch in the future. I am amazed this conceptually simple operation is so difficult. Its not like its a 1-off corner-case. Its the common case! – jww Jun 15 '16 at 00:58
  • Git is very ... cautious? anal? ... about not ever deleting anything, and with things like a bad commit that gets copied to a bunch of repositories on a bunch of developers' systems, the bad commits keep re-infecting the central repository. At a previous job I put in some hooks with explicit commit IDs that we explicitly rejected, to avoid this problem. It's a general issue for all DVCSes; the same happens with Mercurial. – torek Jun 15 '16 at 01:32
2

This is a separate answer, partly so that I can get some formatting in, and also because we seem to be talking about a different repository and/or branch at this point.

Copying from GitHub, so I can see what's actually there

I start by cloning git://github.com/weidai11/cryptopp:

$ cd tmp
$ git clone git://github.com/weidai11/cryptopp
Cloning into 'cryptopp'...
remote: Counting objects: 7756, done.
remote: Compressing objects: 100% (75/75), done.
remote: Total 7756 (delta 33), reused 0 (delta 0), pack-reused 7681
Receiving objects: 100% (7756/7756), 7.48 MiB | 1.87 MiB/s, done.
Resolving deltas: 100% (5480/5480), done.
Checking connectivity... done.
$ cd cryptopp/

Now I can view the branches as seen on GitHub. In my own repository, I have only a local master (just now created by git clone).

$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/alignas
  remotes/origin/arm-neon
  remotes/origin/det-sig
  remotes/origin/master
  remotes/origin/solaris

I see that there is a branch named alignas. Let's find the commit to which it points:

$ git rev-parse origin/alignas
d0760a44eab5e6da04690d4a0d80fc306abd0844

Are there any other names that point to this commit? If the branch is fully merged into some other branch, there will be, if not there will not:

$ git branch -r --contains d0760a44eab5e6da04690d4a0d80fc306abd0844
  origin/alignas

So there is at least one commit on origin/alignas that is not on any other branch. A brief look at it shows (edited a bit):

$ git show --name-status origin/alignas
commit d0760a44eab5e6da04690d4a0d80fc306abd0844
Author: [redacted]
Date:   [redacted]

    Fix CRYPTOPP_ALIGN_DATA placement

M       rijndael.cpp

If I were to delete this branch from GitHub now—not that I could, I have no write permission on GitHub for this repository and git:// offers no write permission—that commit would be dropped from GitHub, perhaps with some others. (Further inspection shows that commits before this point are mostly merges of things that are on other branches; we'd lose the merge commits, but the things being merged-in are protected by those other branches.)

Deleting from GitHub (I can't, so, hypothetical only)

Should you wish to delete this branch from GitHub right now, even though there is at least one commit that this would lose, you1 could do this:

git push --delete origin alignas

That would leave you with remote-tracking branch origin/alignas in your own repository (assuming you have it now), but git fetch origin --prune or git remote prune origin would subsequently delete origin/alignas from your own repository:

git fetch origin --prune

or:

git remote origin prune

The git fetch origin --prune step should do the job, but in at least some versions of Git, this was broken for a while.


1Assuming you have write permission and are using https:// or ssh://, of course. Neither is true for me, so I cannot.

The local branch, in your repository

You might also have local branch alignas in your repository. I do not have it in mine yet, but before you delete alignas from GitHub, while I still have origin/alignas in my repository, I can now do this:

$ git checkout alignas
Branch alignas set up to track remote branch alignas from origin.
Switched to a new branch 'alignas'

and now I do have that branch:

$ git branch -l
* alignas
  master

It's my current branch. Let's say I want to delete it from my repository copy (while leaving origin/alignas alone):

$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

(this step is necessary because Git won't let me delete the branch I'm standing on)

$ git branch -d alignas
warning: deleting branch 'alignas' that has been merged to
         'refs/remotes/origin/alignas', but not yet merged to HEAD.
Deleted branch alignas (was d0760a4).

My version of git is 2.8.1. Apple git 1.8.x will probably give you an error about the branch not being properly merged.

Let me create local alignas again, then delete it again, more forcefully this time:

$ git checkout alignas
Branch alignas set up to track remote branch alignas from origin.
Switched to a new branch 'alignas'

This is the same as before: my Git used remote-tracking branch origin/alignas to create local branch alignas, and then put me on that branch.

$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git branch -D alignas
Deleted branch alignas (was d0760a4).

Again, I have to get off it, then I can delete it. Once I have deleted it locally, I can't accidentally push it (see below).

Everyone else

Anyone else who has a copy of this repository may also have an alignas branch. Suppose Fred has a copy, for instance, and he does not delete his alignas. Suppose further that he has write permission for this repository on GitHub.

Any time after you have deleted alignas from GitHub, Fred could re-create it. All he has to do is git push origin alignas or git push origin --all, and it comes back on GitHub.

To stop that, you must stop Fred. :-)

torek
  • 448,244
  • 59
  • 642
  • 775
  • *"If I were to delete this branch from GitHub now... that commit would be dropped from GitHub"* - I've tried to parse this, but I don't know what it means. As long as the source code has the change, then I can live with no log message/history as a compromise. If that's not the case, then what went wrong with `git checkout master; git merge alignas`? I know this is wandering way off topic, but I am trying to get my head around the disconnects, and how merging a dev branch into master does not meet expectations. – jww Jun 15 '16 at 01:37
  • Thanks again Torek (I had to remove it from the first comment due to length). To summarize, these are the three commands in the order they should be executed from master: `git push --delete origin alignas; git fetch origin --prune; git branch -d alignas`. Is that correct? I'm going to execute them, and then check the results. If the results are expected (i.e., its been deleted ***everywhere***), then I'm going to Accept. If it does not work, then I'm giving up and tell people "*don't use branch x"*. I'll also stop using dev branches because they don't seem to work in the bigger scheme. – jww Jun 15 '16 at 01:45
  • Also, there's no need to redact. I stand behind my mistakes, too. I believe in total transparency, and don't cover up anything. – jww Jun 15 '16 at 01:46
  • Finally, if you are wondering about the frequent merges, it's because [Git kept breaking its index files](http://stackoverflow.com/q/36820084), which left the entire repo unusable. I found frequent merges meant I could continue to use the repo; otherwise I would have to `cp ; cd ..; rm -rf ; git clone ... ; cd ; cp /* .` Its an incredibly inefficient way to do things. – jww Jun 15 '16 at 02:02
  • (back from food) Regarding the first comment: commit `d0760a44eab5e6da04690d4a0d80fc306abd0844` (with the different version of `rijndael.cpp` in it) has only the one external reference to it, namely the branch name (`alignas` on GitHub, `origin/alignas` in my copy of the repo). Git's commits form a directed graph. We start with all external references (branch and tag names) and do a GC, a la Lisp: mark those as referenced. When a referenced commit references another *parent* (or two or more for merges) commit, we mark those as well, and recurse. After the mark pass, we have a sweep (cont'd): – torek Jun 15 '16 at 02:23
  • loop through every commit in the repository and consider unmarked ones eligible to be discarded. Git's other objects undergo the same process, in fact, and all unmarked objects can be GC-ed. This is the *only* way anything ever goes away from a repo, through this GC process. So deleting an external ref, like a branch name, *potentially* exposes objects to the Grim Reaper. Hence Git's stubbornness about deleting refs. – torek Jun 15 '16 at 02:25
  • As for: "... what went wrong with `git checkout master; git merge alignas`: I don't know. I'm not sure *anything* went wrong, perhaps you simply have not pushed the resulting merge commit back to GitHub yet. If the merge succeeded in your repo, your `master` branch points to the merge commit, which points back to two parent commits, one of which is `d0760a44eab5e6da04690d4a0d80fc306abd0844`, where `alignas` points on GitHub. Once you push this successfully, GitHub's `master` will point to your new commit, which will ref `d0760a4...` and preserve it, reinstating it if GitHub already GC-ed it. – torek Jun 15 '16 at 02:29
  • Here we go again: ***`git push --delete origin alignas; git fetch origin --prune; git branch -d alignas`***. It results in ***`error: The branch 'alignas' is not fully merged.`*** I would give my left testicle and right big toe for Git to actually work in practice. Thanks for all your efforts and troubles. – jww Jun 15 '16 at 02:49
  • I accepted the answer. I think you've worked too hard not get something out of it. If you can point out other unaccepted answers, I will accept them too. Its not fair to you to *not* accept when the tool is broken. – jww Jun 15 '16 at 02:56
  • For what it's worth, that last bit suggests that when you did `git checkout master; git merge alignas`, the merge did not work, or is not yet committed (`git merge` normally auto-commits which runs your editor on the merge message, maybe you haven't exited the editor yet?). – torek Jun 15 '16 at 02:58
  • This is too funny... The [solaris branch was merged into master](http://github.com/weidai11/cryptopp/commit/b1df5736a7191eb14adb178cfff2c8ea3daf638e). I then ran ***`git push --delete origin solaris && git fetch origin --prune; git branch -d solaris`***. It resulted in ***`error: The branch 'solaris' is not fully merged.`*** I don't care what Git Fan Boi's say - this damn tool is broken. – jww Jun 15 '16 at 09:09
  • Just a side note: if you're using squash merges, remember that they're not actually merges. They don't get recorded in the commit graph and hence a subsequent `git branch -d` thinks (correctly) that they're not merged (because a squash "merge" *isn't* a merge). Use `-D` or `-d -f` to force-delete if you really want all the commits gone. – torek Jun 15 '16 at 10:02
1

The following solution helped me: rewrite the command, but with the -D flag git branch -D your_branch

Eldar
  • 458
  • 3
  • 13