如何归档git分支?

我的git存储库中有一些旧的分支,它们不再处于活跃的开发状态。我想要存档分支,以便它们在运行git branch -l -r时默认不显示。我不想删除它们,因为我想保留历史。我该怎么做呢?

我知道可以在refs/heads之外创建一个ref。例如,refs/archive/old_branch。这样做有什么后果吗?

113235 次浏览

您可以将分支归档到另一个存储库中。虽然没那么优雅,但我觉得这是个可行的选择。

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

我认为正确的方法是标记分支。如果你在标记了分支之后删除了它,那么你就有效地保留了这个分支,但它不会使你的分支列表变得混乱。

如果您需要返回分支,只需检查标签。它将有效地从标记中恢复分支。

归档并删除分支:

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

在一段时间后恢复分支:

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

分支的历史记录将被保留,就像您标记它时一样。

杰里米的回答原则上是正确的,但恕我直言,他指定的命令不太正确。

下面是如何在不签出分支的情况下将一个分支归档到一个标签中(因此,在删除该分支之前,不必签出到另一个分支):

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

下面是如何恢复一个分支:

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

我使用以下别名隐藏归档的分支:

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

因此,git br显示正在开发的分支,git bra显示包括“存档”在内的所有分支。

您可以使用为您存档分支的脚本

archbranch

它为您创建一个带有前缀archive/的标记,然后删除分支。在使用它之前,请确保您了解它的功能=)


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

如果希望运行脚本而不向其写入位置,则将其添加到路径中

那你可以顺便来看看

$ archbranch [branchname] [defaultbranch]

[defaultbranch]是归档完成后它将去的分支。有一些问题与颜色编码,但除此之外,它的工作。我已经在项目中使用它很长时间了。

我不会归档分支。换句话说,分支将自己存档。你想要的是确保考古学家的相关信息可以通过可靠的方法找到。可靠之处在于它们有助于日常开发,并且不会在完成工作的过程中增加额外的步骤。也就是说,我不相信人们在使用完分支之后还会记得添加标签。

这里有两个简单的步骤,将极大地帮助考古而且的发展。

  1. 使用简单的命名约定将每个任务分支与问题跟踪器中的关联问题链接起来
  2. 总是使用git merge --no-ff来合并任务分支;你需要合并提交和历史记录气泡,即使只是一次提交。

就是这样。为什么?因为作为代码考古学家,我很少一开始就想知道在一个分支上做了什么工作。更经常这就是为什么这九大地狱里的代码是这样写的!我需要改变代码,但它有一些奇怪的功能,我需要把它们弄清楚,以避免破坏一些重要的东西。

下一步是git blame,以找到相关的提交,然后希望日志消息是解释性的。如果我需要深入挖掘,我将找出工作是否在一个分支中完成,并从整体上阅读该分支(以及问题跟踪器中的注释)。

让我们说git blame指向提交XYZ。我打开一个Git历史浏览器(gitk, GitX, git log --decorate --graph,等等…),找到提交XYZ,然后看到…

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

这是我的树枝!我知道QQ, UU, XYZ, JJ和MM都是同一个分支的一部分,我应该查看他们的日志消息以了解详细信息。我知道GG将是一个合并提交,并有分支的名称,希望与跟踪器中的问题相关联。

如果,出于某种原因,我想要找到一个旧的分支,我可以运行git log并在合并提交中搜索分支名称。即使在非常大的存储库上,它也足够快。

这就是我所说的分支自己归档的意思。

给每个分支加标签会给完成工作增加不必要的工作(这是一个应该被无情地简化的关键过程),用数百个偶尔有用的标签混淆标签列表(不是说性能,而是人类的可读性),甚至对考古学都没有多大用处。

下面是它的别名:

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

这样加起来:

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

请记住,已经有git archive命令,所以不能使用archive作为别名。

你还可以定义alias来查看“存档”分支的列表:

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

关于添加别名

我的方法是用“trash_”前缀重命名所有我不关心的分支,然后使用:

git branch | grep -v trash

(带壳键绑定)

为了保持活跃分支的颜色,我们需要:

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

是的,你可以使用git update-ref创建一个带有非标准前缀的ref。如。

  • 存档分支:git update-ref refs/archive/old-topic topic && git branch -D topic
  • 恢复分支(如果需要):git branch topic refs/archive/old-topic

带有非标准前缀的引用(这里是refs/archive)不会出现在通常的git branchgit loggit tag上。不过,你可以用git for-each-ref列出它们。

我使用以下别名:

[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

此外,你可能想要像push = +refs/archive/*:refs/archive/*一样配置遥控器来自动推送存档的分支(或者只指定像git push origin refs/archive/*:refs/archive/*这样的推送来实现一次性推送)。

另一种方法是在删除分支之前在某个地方写下SHA1,但它有局限性。没有任何引用的提交将是3个月后GC(或几周后没有reflog),更不用说手动git gc --prune了。由refs指向的提交对GC是安全的。

编辑:通过@ap: git-attic找到了相同思想的perl实现

编辑^ 2:发现一篇博文中Gitster自己使用相同的技术。他将其命名为git hold

扩展Steve的回答来反映遥控器上的变化,我做到了

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

要从远程恢复,请参见这个问题

我有时会这样归档分支:

  1. 生成补丁文件,例如format-patch <branchName> <firstHash>^..<lastHash>(使用git log <branchName>获取firstthash和lastHash。
  2. 将生成的补丁文件移动到文件服务器的目录下。
  3. 删除分支,例如git branch -D <branchName>

当你需要再次使用分支时,“应用”补丁;然而,根据目标分支的状态,应用补丁文件(参见git am)可能具有挑战性。从好的方面来看,这种方法的好处是允许分支的提交被垃圾收集,并节省了repo中的空间。

< p > 步骤0。检查以确保工作树是干净的,以免 失去任何工作:

git status
< p > 步骤1。从本地存储库的根签出分支和 然后将其标记为要存档:

git checkout <branch_name> && git tag archive/<branch_name>
< p > 步骤2。上传标签到远程,并确保在另一个分支之前 继续,例如main:

git push origin --tags && git checkout main

步骤3。从本地和远程存储库中删除分支:

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

,你应该用分支的名称替换<branch_name> origin和远程存储库的名称 如果不是origin.

评论:

    步骤1之前和之后,你可能想要运行git tag来注意 添加标签。< / p >
  • 步骤3之前和之后你可能想看 https://github.com/<github-username>/<github-repository-name>/branches 和/或运行git branch -a来注意分支是如何被删除的

  • 恢复分支:

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

紧随其后的是

git push --set-upstream origin <branch_name>
< p >引用:
https://gist.github.com/zkiraly/c378a1a43d8be9c9a8f9
https://dev.to/clsource/archiving-git-branches-3k70 < / p >

在Git中,分支只是一个指向某个提交的指针。因此,删除一个分支只会删除一个指针,即使分支从未合并,也不会删除相关的代码。因此,“存档”;本地分支就像记住指针名称一样简单(提交哈希)。你可以在./git/refs/heads目录中找到这些信息。或者,您可以简单地将分支到提交的映射复制到如下所示的文本文件中。

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

删除分支之后,可以使用以下命令在本地恢复它。

git branch <branch_name> <commit_hash>

要归档超过n个月或n年的分支,运行这个bash脚本。

#!/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

谢谢杰里米炖的肉。