How can I archive git branches?

Viewed 108825

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?

14 Answers

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

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.

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>

To archive branches older than n months or years, run this bash script.

#!/bin/bash

# Average days in a month - 30.436875
# Hours in a day 24
# Minutes in an hour 60
# Seconds in a minute 60
months_in_year=12
days_in_month=30.436875
hours_in_day=24
minutes_in_hour=60
seconds_in_minute=60

# Input is x months or x years
# $1 an integer
# $2 a time metric

# Name of the script, follows through simlinks
script_name="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

if [ $# -le 1 ]; then
    echo "Usage: ./${script_name} [1-9] [month(s)/year(s)]"
    exit 1
fi

time_period=$1
time_metric=$2

if [[ ${time_metric} =~ "month" ]]; then minimum_branch_age_in_seconds=$( echo "scale=4; $time_period * $days_in_month * $hours_in_day * $minutes_in_hour * $seconds_in_minute" | bc); fi
if [[ ${time_metric} =~ "year" ]]; then minimum_branch_age_in_seconds=$( echo "scale=4; $time_period * $months_in_year * $days_in_month * $hours_in_day * $minutes_in_hour * $seconds_in_minute" | bc); fi

echo "minimum_branch_age: $1 $2"
echo "minimum_branch_age_in_seconds: ${minimum_branch_age_in_seconds%.*}"

git for-each-ref refs/remotes | while read commit type ref;do
    current=$(date +%s)
    headcd=$(git log -1 --pretty=%cd --date=format:%s ${commit})
    age_in_seconds=$((current-headcd))
    if [[ ${age_in_seconds} -ge ${minimum_branch_age_in_seconds%.*} ]];then
        branch=$(echo $ref | sed 's=refs/remotes/origin/==g')
        age_in_months=$( echo "scale=4; $age_in_seconds / $days_in_month / $hours_in_day / $minutes_in_hour / $seconds_in_minute" | bc)
        echo "archiving $branch - age in seconds - $age_in_seconds - age in months - $age_in_months "
        git tag archive/${branch} ${branch}
        git push -d origin ${branch}
        git branch -D ${branch}
        echo "Unarchive with: git checkout -b ${branch} archive/${branch}"
    fi
done

Thanks to Jeremy for the meat to this stew.

Related