Reputation: 12083
Is it possible to "close" a branch instead of merging it, or "merge" it without adding any of the changes to the branch I'm merging to.
Basically, a long time ago I made a branch to do something, then forgot about it. Recently, I wanted to add that feature back to the master, but the code had changes so much, and so many files had been deleted from the master that were still in this branch, that merging it would have just added a bunch of garbage to the master. So I added the stuff manually to my master branch, going line by line. Is there anyway to end the branch I now no longer need, or is it doomed to hang out in limbo forever?
Upvotes: 1
Views: 173
Reputation: 489233
Unlike many (most?) version control systems, in git, branches are not actually part of the repository.1 Branches are just labels—they're only a little bit special2 in that they are "automatically moving labels". This means there is nothing to "close", unlike (say) Mercurial, where commits carry their branch, so that branches really are part of the repository, and there's a "closed" bit for each branch.
To achieve what you want, we'll first have to define what it is you want.
So what, exactly, do you want? Let's go back to the question description:
... a long time ago I made a branch to do something, then forgot about it. Recently, I wanted to add that feature back [but it made more sense to copy in the feature-pieces than to use
git merge
].
So let's draw the commit graph, or a simplified version of it (it's funny how many git things start with "well, let's draw the graph"). Here's what you had before you started the "add that feature back" sequence:
...-o-o-...-o-o-o-o <-- master
\
F1-F2-F3 <-- long-ago-feature
The three F
commits are the old feature. You've now copied ("cherry-picked", except that per description, you did it by hand) these three into master
. Let's draw them in using lowercase, since it's too hard to do color, and I'll also assume they're just all one commit now, not that it matters very much:
...-o-o-...-o-o-o-o-f123 <-- master
\
F1-F2-F3 <-- long-ago-feature
Now you want to "close" the long-ago-feature
branch. But what does this mean?
Let's have a little side-bar here. In git, a branch-name does two things for you: it lets you refer to a specific commit, such as F3
, by name. (And then F3
is what refers to F2
, and so on.) But the branch-name also lets you "check out" the branch, rather than just "checking out" the specific commit; this git checkout foo
action puts you "on branch foo", after which git status
will say on branch foo
. This is where that special feature comes in. If you are on branch foo, and you use git commit
to make a new commit, git makes the new commit and then changes branch label foo
to point to the new commit. The new commit points back to the old branch-tip. That's really what makes branches "branches". Everything else they do, you can do with any old reference name; but only branch references automatically "move forward" like this.
Note that even with a branch name, git lets you do git checkout --detach foo
, which checks out the commit but takes you "off branch", into "detached HEAD" state. In this case, adding a new commit does not update any branch, as there is no branch to update. (Instead, it updates the special HEAD
ref directly. These added commits have no other label, so unless you add one later, they're abandoned—see description below—as soon as you get on some other branch.)
You can simply delete it, as raina77ow proposed. What that does is simply remove the label:
...-o-o-...-o-o-o-o-f123 <-- master
\
F1-F2-F3 [abandoned - no label]
The three old commits are abandoned, and will eventually (in about a month) be garbage-collected, after which they will be really gone. But that's not quite what you want:
I'd still like to "keep" [the branch] for future reference. Just not as [a branch, or at least an "open" branch, whatever that really means].
In this case, you could just leave it there, but then it looks exactly like any other branch—obviously also not what you want, or you would not be asking about closing a branch.
You could simply rename it. For instance, you could have ordinary branches that you've named dead
or closed
, to tell yourself you're just saving them for reference, not for being added-to:
git branch -m long-ago-feature dead/long-ago-feature
That simply renames the label. It's still a branch, it just has a name starting with dead/
, which you can use as a reminder.
Perhaps, though, your objection to keeping it as a branch is that branches are "special", because the labels move if you add more commits. You want to make sure you do not add more commits. In this case, you can change the type of label. For instance, make it a tag:
git tag dead/long-ago-feature long-ago-feature
git branch -D long-ago-feature
Now commit F3
is pointed-to by the tag dead/long-ago-feature
, which is a tag, rather than a branch. If you check it out by tag name, you'll be in "detached HEAD" mode, rather than on a branch.
You might not like that either: perhaps being a tag makes it clutter up the tag name space, and too easy to push to a remote (git push remote --tags
pushes all your tags). In that case, you could use a reference that's not in any of the three standard "branch and tag" name-spaces, nor the same as any current or future3 name-space:
git update-ref refs/deadheads/long-ago-feature long-ago-feature
git branch -D long-ago-feature
Now commit F3
is labeled, so it won't go away, but the label is not a branch, not a tag, and not even a remote branch. To name commit F3
by name you will have to spell out the long form of the reference (see the gitrevisions documentation).
1Well, this is something of a matter of definition, really. But branches are not recorded as repository objects. A branch is simply a special form of reference, with the two other standard special-but-general forms being tags and "remote branches". In general, a reference is a name starting with refs/
; branches are the ones that start with refs/heads/
. Tags start with refs/tags/
and remote-branches start with refs/remotes/
. There's also refs/stash
for the (single) stash reference, and a name-space starting with refs/notes/
for git notes
. In any case, none of these are repository objects. They're just names, in what looks like a directory-structured name-space, stored either in directories in .git/refs/
, or a flat file in .git/packed-refs
.
2In fact, "automatically moving" makes them quite a lot special, and might be the entire motivation for "closing". Or maybe not. Let's find out! :-)
3As Niels Bohr and/or Yogi Berra said, it's difficult to make predictions, especially about the future. Any name-space you pick could get used someday, but at least deadheads
is suggestive. (Although maybe of followers of Jerry Garcia.)
Upvotes: 5
Reputation: 106443
Just remove it - locally:
git branch -D branch_name
(it's -D
, because 'soft form' - -d
- won't allow you to remove the non-merged branch)
... and remotely:
git push origin :branch_name
Upvotes: 3