355
votes

I have some old branches in my git repository that are no longer under active development. I would like to archive the branches so that they don't show up by default when running git branch -l -r. I don't want to delete them, because I want to keep the history. How can I do this?

I know that it's possible to create a ref outside of refs/heads. For example, refs/archive/old_branch. Are there any consequences of doing that?

13
git-rm does not delete resources from the repository, it only removes them from the index kernel.org/pub/software/scm/git/docs/git-rm.html You can easily restore these resources using git checkout [rev] fileDana the Sane
Not that I know of. I use Attic/<branchname> lightweight tags to archive branches, though.Jakub Narębski
tags are the quick and safe and sane choice.kch

13 Answers

469
votes

I believe the proper way to do this is to tag the branch. If you delete the branch after you have tagged it then you've effectively kept the branch around but it won't clutter your branch list.

If you need to go back to the branch just check out the tag. It will effectively restore the branch from the tag.

To archive and delete the branch:

git tag archive/<branchname> <branchname>
git branch -d <branchname>

To restore the branch some time later:

git checkout -b <branchname> archive/<branchname>

The history of the branch will be preserved exactly as it was when you tagged it.

151
votes

Jeremy's answer is correct in principle, but IMHO the commands he specifies are not quite right.

Here's how to archive a branch to a tag without having to checkout the branch (and, therefore, without having to checkout to another branch before you can delete that branch):

> git tag archive/<branchname> <branchname>
> git branch -D <branchname>

And here's how to restore a branch:

> git checkout -b <branchname> archive/<branchname>
35
votes

Yes, you can create a ref with some non-standard prefix using git update-ref. e.g.

  • Archive the branch: git update-ref refs/archive/old-topic topic && git branch -D topic
  • Restore the branch (if needed): git branch topic refs/archive/old-topic

Refs with non-standard prefix (here refs/archive) won't show up on usual git branch, git log nor git tag. Still, you can list them with git for-each-ref.

I'm using following aliases:

[alias]
    add-archive = "!git update-ref refs/archive/$(date '+%Y%m%d-%s')"
    list-archive = for-each-ref --sort=-authordate --format='%(refname) %(objectname:short) %(contents:subject)' refs/archive/
    rem = !git add-archive
    lsrem = !git list-archive

Also, you may want to configure remotes like push = +refs/archive/*:refs/archive/* to push archived branches automatically (or just specify on push like git push origin refs/archive/*:refs/archive/* for one-shot ).

Another way is to writing down SHA1 somewhere before deleting branch, but it has limitations. Commits without any ref will be GC'd after 3 months (or a couple of weeks without reflog), let alone manual git gc --prune. Commits pointed by refs are safe from GC.

Edit: Found a perl implementation of the same idea by @ap: git-attic

Edit^2: Found a blog post where Gitster himself using the same technique. He named it git hold.

28
votes

Extending Steve's answer to reflect the changes on the remote, I did

 git tag archive/<branchname> <branchname>
 git branch -D <branchname>
 git branch -d -r origin/<branchname>
 git push --tags
 git push origin :<branchname>

To restore from the remote, see this question.

19
votes

You could archive the branches in another repository. Not quite as elegant, but I'd say it's a viable alternative.

git push git://yourthing.com/myproject-archive-branches.git yourbranch
git branch -d yourbranch
13
votes

Here is an alias for that:

arc    = "! f() { git tag archive/$1 $1 && git branch -D $1;}; f"

Add it like this:

git config --global alias.arc '! f() { git tag archive/$1 $1 && git branch -D $1;}; f'

Bear in mind there is git archive command already so you cannot use archive as an alias name.

Also you can define alias to view the list of the 'archived' branches:

arcl   = "! f() { git tag | grep '^archive/';}; f"

about adding aliases

7
votes

I am using following aliases to hide archived branches:

[alias]
    br = branch --no-merge master # show only branches not merged into master
    bra = branch                  # show all branches

So git br to show actively developed branches and git bra to show all branches including "archived" ones.

5
votes

I would not archive branches. Put another way, branches archive themselves. What you want is to ensure the information relevant to archeologists can be found by reliable means. Reliable in that they aid daily development and doesn't add an extra step to the process of getting work done. That is, I don't believe people will remember to add a tag once they're done with a branch.

Here's two simple steps that will greatly help archeology and development.

  1. Link each task branch with an associated issue in the issue tracker using a simple naming convention.
  2. Always use git merge --no-ff to merge task branches; you want that merge commit and history bubble, even for just one commit.

That's it. Why? Because as a code archeologist, rarely do I start with wanting to know what work was done on a branch. Far more often it's why in all the screaming nine hells is the code written this way?! I need to change code, but it has some odd features, and I need to puzzle them out to avoid breaking something important.

The next step is git blame to find the associated commits and then hope the log message is explanatory. If I need to dig deeper, I'll find out if the work was done in a branch and read the branch as a whole (along with its commentary in the issue tracker).

Let's say git blame points at commit XYZ. I open up a Git history browser (gitk, GitX, git log --decorate --graph, etc...), find commit XYZ and see...

AA - BB - CC - DD - EE - FF - GG - II ...
     \                       /
      QQ - UU - XYZ - JJ - MM

There's my branch! I know QQ, UU, XYZ, JJ and MM are all part of the same branch and I should look at their log messages for details. I know GG will be a merge commit and have the name of the branch which hopefully is associated with an issue in the tracker.

If, for some reason, I want to find an old branch I can run git log and search for the branch name in the merge commit. It is fast enough even on very large repositories.

That is what I mean when I say that branches archives themselves.

Tagging every branch adds unnecessary work to getting things done (a critical process which should be ruthlessly streamlined), gums up the tag list (not speaking of performance, but human readability) with hundreds of tags that are only very occasionally useful, and isn't even very useful for archeology.

3
votes

Step 0. Check to make sure that the working tree is clean so as to not lose any work:

git status

Step 1. From the root of the local repository, check out the branch and then tag it to be archived:

git checkout <branch_name> && git tag archive/<branch_name>

Step 2. Upload tags to remote and be sure to be in another branch before continuing, for example main:

git push origin --tags && git checkout main

Step 3. Delete the branch from the local and remote repositories:

git branch -D <branch_name> && git push origin -d <branch_name>

where you should replace <branch_name> with the name of the branch to archive, and origin with the name of the remote repository if other than origin.

Comments:

  • Before and after step 1 you may want to run git tag to notice the added tag.

  • Before and after step 3 you may want to watch https://github.com/<github-username>/<github-repository-name>/branches and/or run git branch -a to notice how the branch was deleted.

  • To restore a branch:

git checkout -b <branch_name> archive/<branch_name>

followed by

git push --set-upstream origin <branch_name>

References:
https://gist.github.com/zkiraly/c378a1a43d8be9c9a8f9
https://dev.to/clsource/archiving-git-branches-3k70

2
votes

My approach is to rename all branches I don't care about with a "trash_" prefix, then use:

git branch | grep -v trash

(with a shell key binding)

To retain the coloring of the active branch, one would need:

git branch --color=always | grep --color=never --invert-match trash
1
votes

I sometimes archive branches as follows:

  1. Generate patch files, e.g., format-patch <branchName> <firstHash>^..<lastHash> (get firstHash and lastHash using git log <branchName>.
  2. Move the generated patch files to directory on a file server.
  3. Delete the branch, e.g., git branch -D <branchName>

"Apply" the patch when you need to use the branch again; however, applying the patch files (see git am) can be challenging depending on the state of the target branch. On the plus side, this approach has the benefit of allowing the branch's commits to be garbage-collected and saving space in your repo.

1
votes

You can use a script that will archive the branch for you

archbranch

It creates a tag for you with the prefix archive/ and then deletes the branch. Make sure you understand what it does before you use it =)


Usage - $/your/location/of/script/archbranch [branchname] [defaultbranch]

If you want to run the script without writing the location to it add it to your path

Then you can call it by

$ archbranch [branchname] [defaultbranch]

The [defaultbranch] is the branch that it will go to when the archiving is done. There are some issues with the color coding but other than that it works. I've been using it in projects for a long time.

1
votes

In Git a branch is just a pointer to some commit. So removing a branch will only remove a pointer, it won't remove the associated code even if the branch was never merged. So that "archiving" a local branch is as simple as remembering the pointer name (commit hash). You can find this information in ./git/refs/heads directory. Alternatively you can simply copy the branch-to-commit map into a text file like follows.

git branch -v > /path/to/backup/branches.txt

Once a branch has been removed you can restore it locally with the following command.

git branch <branch_name> <commit_hash>