The part of your question about deleting all non-master branches is already answered. However:
I am later doing a cherry-pick that fails because it reaches a commit that was a merge, so it has two parents. What do I need to do so that it only has one parent (from the master branch)?
You can't do anything about this. No existing commit can ever be changed: not by you, nor by Git. If you're deleting branch names with the idea that this might also change existing commits, it won't: all it will do is make it impossible to find some existing commits.
Once a commit is un-find-able (unreachable, in technical terms), git gc
will (eventually) delete it entirely. But a merge commit, with its two parents, makes both parents find-able: finding the merge—by starting at some branch name and working backwards through history—inherently finds both parents, and then the parents' parents, and so on.
For (much) more about this, see Think Like (a) Git.
What you probably want to do, rather than trying to modify this existing commit, is one of these two things:
Don't cherry-pick the merge at all. Cherry-pick, instead, all of the commits behind the merge (on both legs / sides of the merge).
Or, do cherry-pick the merge, but use git cherry-pick -m number
to specify which parent will produce the diffs for the cherry-pick.
You can think of git cherry-pick
as meaning: Take the snapshot (commit) I name, and turn it into changes (diffs against its parent), then apply those same diffs to my current commit. That's just an approximation though.
You might have a series of commits that can be drawn like this:
o--o--A--B--o--...--o <-- branch1
/
...--o--*
\
o--o--o--C <-- branch2 (HEAD)
Here, your current commit is commit C
(C stands in for its actual hash) and you're on branch branch1
. If you git cherry-pick B
, Git compares A
vs B
and merges whatever happened there with the difference between A
and C
. Cherry-pick is therefore actually a merge, not a diff-and-apply; this is what leads to weird merge conflicts sometimes.
In any case, this obviously only works for non-merge commits, because a merge has two parents:
o--...--A
/ \
...--* M--...
\ /
o--...--B
If you ask to cherry-pick M
, should Git compare M
to A
, or to B
? Which of A
and B
gets compared to your current commit?
The cherry-pick command's -m
switch allows you to choose. If you choose A
, Git will merge the A
-to-M
changes with the A
-to-HEAD
changes. The effect is to take all the changes that the merge introduced because of commit B
! So this means you no longer need to cherry-pick some commits behind B
. You got all of "their" (B's) changes relative to the merge base commit *
—or rather, all that whoever did the merge into A
thought were appropriate to keep.
Note that you still might want commits behind A
, including commit *
itself and things earlier. Or you may not, depending on what, exactly, you're doing. Git mostly provides tools rather than solutions; it's up to you to make use of these tools, however you like.