39

For instance, I've started working on a new feature and created a featureX branch. Later, we decided that we no longer needed the feature. What should I do with featureX?

I see value in keeping the branch around in case we change our mind on it again.

My strategy is to push that branch back to the remote and delete it from my local; if it's ever needed again, I can find it there. Odds are it will sit there untouched, for eternity.

But I'd like to know if there are other best practices around this. What do you do?

2

11 Answers 11

41

There are no best practices here. You've outlined a valid strategy that I've used many times, and yes, these branches tend to sit around for eternity. It's only hard drive space. As long as you don't have hundreds of these sitting around, nobody will probably care.

There is one giant downside though. Say 3 years have passed since you pushed featureX. Three years worth of commits have been added to the repository. Code can evolve significantly in that amount of time. Merging the latest changes into featureX to bring it up to date will likely be an ugly merge. The longer a branch sits around the more effort is required to start work on it again.

  1. Consult with your team first to see if anyone even cares. They might not even if you do. If nobody cares, don't feel like you need to keep this branch around.

  2. If the team desires to keep it around, go ahead and push it. The team should review these abandoned feature branches periodically and delete branches that are older than some amount of time decided by the team. This could be a number of months, sprints, releases, quarters, etc.

There is nothing inherently wrong with keeping abandoned branches around, but at some point the changes are so stale it might be better to start over again. From a product perspective, requirements might have changed since the last time the team worked on the feature. In this case, you have the effort to bring the code up to date, plus the effort to change the old "new feature" code to bring it into alignment with the changed requirements.

You need to strike a balance between not losing work and not creating more work to update the abandoned branch. There is no hard line you can apply to every project. You will need to assess how long it's worth keeping these branches around based on how frequently you release, how much change happens over a period of time, and how much test automation you can leverage to ensure new code changes are integrated safely.

It basically amounts to "keep it for a while and periodically delete old branches." This becomes part of your team's housekeeping duties to maintain version control.

11
  • 32
    Yes, it most likely becomes so stale that one should start over. But it's still worth it to be able to look at the old changes of the feature branch to evaluate what approach was taken the last time, what hurdles needed to be overcome, what solutions were found, and maybe what mistakes were made or avoided. It ultimately helps to remind oneself why the branch was abandoned.
    – Bergi
    Jan 4 at 0:28
  • 11
    @Bergi: I think that's the best approach for old branches, and goes well with Alexander's answer of renaming such branches archive/featureX so you can filter them to avoid clutter. The value isn't in even trying to actually merge, it's reading the commit messages and design-notes comments. Maybe sometimes it would be counterproductive to even look at the code changes and start trying to do similar things in a new branch from the current head, if too many internals have changed, but I agree the criterion for keeping a branch shouldn't be just whether you could actually merge it. Jan 4 at 2:05
  • 2
    @Bergi: Still, there is something to be said for making a project smaller. Less code is easier to think about, deleting code while keeping functionality the same can be a good thing. I'm not sure how much this applies to old half-finished attempts at adding features. I'm sure sometimes it's better to just start fresh, without bogging yourself down with the deltas between how the mainline code used to work vs. how it works now, but design notes on corner cases a feature needs to handle are still probably useful. And an archived branch seems like an ok place to keep that. Jan 4 at 2:07
  • 8
    It is not only "disk space". The branch also pollutes branch namespace, which makes finding useful branches a bit harder!
    – Basilevs
    Jan 4 at 10:17
  • 2
    @AlexanderThe1st: that would be something handled by the git repository host, and would work right up to the first merge conflict. That can happen much quicker than you think. Jan 5 at 2:05
27

I like to keep these around in the remote, but I'll first rename it to archive/featureX, so I can hide all such branches most of the time.

Alternatively, you can create a tag archive/featureX pointing to the the branch, then delete the branch - this keeps the list of branches clean, and removes the option of committing to the old branch.

6
  • 1
    This sounds like a good idea. Could someone explain the downvote?
    – Jamie
    Jan 4 at 1:39
  • 1
    @Jamie Perhaps because the question is fundamentally opinion-based. I still think it's a useful idea to consider, regardless of whether people will choose to use it or not.
    – Alexander
    Jan 4 at 4:08
  • I didn't downvote, and I think that it could be useful.but I can see two problems with renaming - specifically that it invalidates any references to "featureX" (so if someone wrote "implemented widget X on featureX" you will have to figure out that you should also look for "archive/featureX"), and that it requires an active effort to abandon the feature. If your system has some mechanism for doing it without renaming it may be preferable - e.g., GitHub makes branches "stale" and somewhat hidden after 3 months of inactivity. Jan 5 at 12:23
  • 2
    A variant on this I like to use is to tag the head of the branch with such a name, then delete the branch. Being a tag makes it clear that it's not something you could continue to work on as a branch, and avoids it showing up in the list of branches at all (rather than having to hide it with filters). Jan 5 at 14:32
  • 1
    @R..GitHubSTOPHELPINGICE: Good point, took the liberty of adding it to the answer.
    – sleske
    Mar 27 at 9:30
20

If you feel that you still want to reference the unneeded featureX in the future, but also don't want to keep its branch around, you could merge it into the main branch with the --strategy ours option, i.e., not resulting in any change in the main branch.

From the git-merge documentation:

ours

This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches. […]

It is also mentioned in the examples:

Merge branch obsolete into the current branch, using ours merge strategy:

$ git merge -s ours obsolete

Compared to keeping the branch in a remote but not locally, or moving it into some "archive" namespace, this gives you the opportunity to document when and why you decided that you don't need the feature.

4
  • 2
    I like this more than keeping the old branch on the remote, because it guarantees the commit won't be lost. Any branch on the remote can be deleted by anyone, causing the commit to be lost.
    – JoelFan
    Jan 4 at 11:20
  • 2
    This is an interesting option that I hadn't known about. It basically keeps the history of changes without actually applying any of them to the main branch? One could tag it I suppose to make them easier to find. Jan 4 at 20:16
  • @JoelFan "Any branch on the remote can be deleted by anyone" depends on what's on the other side of the git commands and/or permissions. Azure Devops by default (or just how my org has it set up) only allows remote branches to be deleted by their creators and administrators. So one additional drawback of people leaving these abandoned branches around is that when those people leave the organization, only administrators can clean up the branches they left behind.
    – stannius
    Jan 6 at 15:21
  • Furthermore, the preponderance of branches begets more branches. With dozens or hundreds of branches, it's easier to miss when you have merged/pulled a set of changes into master but forgot to delete the branch afterwards. Then, as per my above comment, once you leave the org, an administrator has to spend time figuring out whether your abandoned branches are incomplete or shelved work with some value, or branches you simply forgot to delete.
    – stannius
    Jan 6 at 15:28
17

Even if you do change your mind, is any of the work going to be relevant and useful?

Unless you invest the time in keeping the branch up-to-date with the upstream branch, the branch will become out-of-sync and it will be harder and harder to update without the risk of a conflict. And even if there's no conflict in the sense of your version control system not being able to merge changes successfully, there could be a logical conflict that results in errors and failing tests, so someone would need to evaluate and understand these errors in order to make the changes useful.

On top of this, your context will be changing. Dependencies will be updated or perhaps even change. What your team considers good practices will also change. Any code written may not meet your team's current standards. Introducing the changes as they were written in the past may be introducing technical debt into the code base, reducing overall maintainability.

If you do decide to keep the feature branch on the remote, I would put a time limit on it. After a certain date, even if you decide to revisit the feature, it would be better to start again rather than trying to understand and finish it, along with any changes.

3
  • 5
    I think I would be more interested in the diffs on these branches down the road. Depending on the complexity of the feature, it might be worth reviewing what was tried last time.
    – Jamie
    Jan 4 at 1:42
  • 1
    @Jamie I've seen people try that. Looking at what was tried last time usually doesn't help when a lot about your system changes. But it really does depend on the time and frequency of change. It's more valuable for longer in more stable systems.
    – Thomas Owens
    Jan 4 at 3:42
  • 1
    I had couple of instances where that left over code was useful. Branch as a whole was not useful (it was severely out of date), but I could harvest some general concepts or smaller code chunks from it. But it is true that this was very rare and I would not want to pollute branch list of the main repo for this. Jan 4 at 16:31
11

Technically, just keeping the branch would be fine, but it disregards the social aspect of coding: Over time, it will be harder to find active branches (although using a common prefix for those branches might help a bit with that).

A branch implies that additions can be made, whereas your abandoned attempts are dead ends. Git has a concept for fixed immutable code revisions: tags.

Therefore, I prefer to tag an abandoned branch (with a prefix like archive/), and then delete it from my local repository as well as the origin repo:

$ git tag -m "never worked reliably" archive/frobnicator feature/frobnicator-experiment
$ git push origin archive/frobnicator
$ git push origin --delete feature/frobnicator-experiment
$ git branch -D feature/frobnicator-experiment

With the tag, the changes won't be lost, and they can easily be cherry-picked or even revived into an active branch at any later time.

The only caveat is that if you use tags for releases, clients need to be smart enough to ignore the archive tags so that any dependency update check does not inadvertently mistake an abandoned branch for a new release.

6

The first thing I'd ask is: does feature X still have tangible value? Or has feature Y taken over what would have been in feature X? If feature Y ostensibly subsumes what would have been in feature X, then I'd be inclined to delete it.

Branches themselves don't consume much space so there isn't really a storage concern here. That being said, long lived branches do tend to be frowned upon. After time, there is a brain drain and merging the branch in at a later date could become a bit of a nightmare.

If this keeps happening, I'd perhaps push back and ask why you're spending time developing stuff that isn't being delivered. These days, it is usually the most pressing features that bubble up the priority list and are developed first. If you've just become a feature factory of sorts, then that might point to a wider organisational problem.

5

We use a Scavenge and Discard strategy.

When developing a new feature, we sometimes have to do some refactoring, implement new helper classes, create a new an UI component, or any other useful things that, while were built for the now abandoned feature, can be quite useful to the main codebase.

Those are scavenged out from the now abandoned feature and pushed back into the main codebase. Then the abandoned branch is left alone in the repo for up to two releases of our software. If after two releases nobody reverted the decision to give up on that feature, it is them removed from the repo (hotfixes don't count as releases for this process).

Sometimes great ideas or the best refactorings come from features that won't be implemented at all. It is a shame to discard those things just because the feature itself was abandoned. By scavenging what we can before discarding the branch, we can keep the good work around, and the devs that worked on the branch don't feel that bad for "wasting their time" on features that won't see live.

1
  • 4
    I was going to suggest cherry-picking the useful changes and content if no one else did. Kudos. Jan 4 at 17:13
3

If it’s in remote and not automatically checked out then it’s safe there and in nobody’s way.

There comes a time where nobody knows what that branch is about, and where nobody would dare merging a current version into it. So when you decide that it is absolutely no use to anyone then you would think it could be removed. However, if you spent months working on it, and in two years time you get a new manager who is wondering what you did all this time, it’s better if you can show that you actually worked on something.

3

Your instinct is good. Do keep abandoned work. If it’s just code and such textual changes—the thing that Git is good at—you should never have to worry about the overhead that it will cause (like more refs to traverse when garbage collecting). I would keep it local since it’s my own stuff and I don’t want to worry about what others might do to our shared remote. If I do want to optimize the repository someday I’ll make an “archive” clone (never done so though even thought it’s been some years).

Keeping the graveyard neat

I have a lot of abandoned or situational work sitting around.[1] You might eventually run into the issue of remembering what <branch name> is about. But if you use an issue tracker it might not be a problem: use the practice of including the ticket key in your branch name. You might of course have the branch which did get merged too though (I keep them as well (as tags)). You can differentiate them by calling your branches something like failed-refactor-<issue key>.

But the project you are working on might not have an issue tracker. That’s the case for one of the projects I dip my toes in. And there I also have work from months ago that I might get back to and try to get merged. So for that I have my own issue tracker (git-bug(1), a distributed issue tracker which integrates with the Git repository). Something like a text file would also work.

Leaving a note on your abandoned branch

I also like to document what I was doing last on a branch. I do that by committing an empty commit on top:

git commit --allow-empty

This is empty as in there are no changes to the tree—there’s just a message. There I can explain what a hopeless endeavor it was. Maybe. Or maybe it’s some work that I can pick up later.

Keeping the abandoned branches up to date

You won’t be able to merge a branch that you have abandoned for a quarter of a year. But that’s fine. Like you say: a lot of these things will probably never be looked at again. And if you do go back to it it should be to get a gist of what you did so you can reimplement it. For example, I had an experiment where I added some assertion handling to our Java code base: lightweight run-time toggling of error-or-log-or-assert (fail) which goes beyond the built-in JVM thing (-ea (enable assertions). But my boss didn’t like it. Still though I keep it around so that I have documented the patterns that I tried out.

Notes

  1. Situational are things like a quick and dirty change to the code in order to facilitate testing or other ephemeral work
2

How I solve this problem (since I hate to throw away code) is to use:

git format-patch -1 {commit}

to generate a patch file. (You can use -2 or higher numbers if you want more than 1 commit to be converted to a patch... this will create several patch files).

Then I commit the patch file to my own personal "misc" repo, which contains random useful things like notes, patch files and data samples. This doesn't affect anyone else and also reduces the risk it will be lost by someone deleting my remote branch on the code repo.

I can "apply" this patch file at any time with:

git am --reject < {patch-file}

The am command applies the patch and the --reject allows it to create "reject" files in case the patch can't be applied cleanly. This allows the command to partially merge what it can instead of completely failing, and the "reject" files can then be used to help you manually make the changes that failed to merge.

The result of the git am is to recreate the commit that produced the patch file. If you want to apply several patch files, you must be careful to apply them in the original order.

(I got this idea from contributing to the git project, because that project does not use pull requests, because Linus Torvalds hates them. Instead, it uses patch files sent to an email listserv, which can then be reviewed by fellow contributors)

1
  • This seems like an extra step for no clear gain. If you store them as patches (like a directory per branch) then you’ve reinvented a ref namespace somewhere in the working tree itself. But the upside is that git(1) has less refs to traverse when doing bookkeeping and things like that. Jan 15 at 21:08
1

I usually create a patch that contains all individual commits, chuck that patch into archive folder on disk (that is then part of my general backup system) and delete the branch. That way those branches do not pollute branch list of the git repo, but I can still access them whenever I want.

Not the answer you're looking for? Browse other questions tagged or ask your own question.