我该如何存档Git分支?

416

我在我的git存储库中有一些旧分支,不再处于活跃开发状态。我想将这些分支存档,以便在运行git branch -l -r时默认情况下不显示它们。我不想删除它们,因为我想保留历史记录。有什么方法可以做到这一点吗?

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


git-rm 不会从代码库中删除资源,它只会从索引中移除。您可以使用 git checkout [rev] file 命令轻松恢复这些资源。http://www.kernel.org/pub/software/scm/git/docs/git-rm.html - Dana the Sane
16个回答

555

我认为正确的做法是给分支打上标签。如果你在打完标签后删除了分支,那么实际上你已经保留了这个分支,但它不会在分支列表中出现。

如果需要返回到该分支,只需检出标签即可。它将从标签中有效地恢复该分支。

要存档和删除分支:

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

稍后恢复该分支的方法:

git checkout -b <branchname> archive/<branchname>
分支的历史记录将被保留,与您打标签时一模一样。

9
在这种情况下不使用对象标签是否有任何原因?能够看到谁归档了分支以及何时归档可能会很有趣。 - Grégory Joseph
11
这是一个所谓的“带注释标签”。使用它确实很有意义,我想说。 - onnodb
31
小提示:如果您以这种方式将其存档,那么您可能想使用branch -D而不是branch -d,因为它很可能没有完全合并。 - Arkadiy Kukarkin
8
我认为更通用且更有意义的做法是:git tag [-a -m "某些描述"] archive/<你喜欢的任意名称> <分支名或提交ID>。我在一个分支上工作时这样做,但后来决定采取另一种方法。我继续在同一个分支上工作,但进行了重大更改。我所做的工作现在以一个良好的名称和描述存储在那个归档标签中。 - eigil
14
2个重要的命令缺失: git push --tags (推送新创建的归档标签到服务器)。 git push origin :<branchname>(从服务器中删除 <branchname> 分支)。 - jerrymouse
显示剩余12条评论

183

Jeremy的回答在原则上是正确的,但在我看来他指定的命令不太对。

以下是如何将一个分支存档到一个标签而无需检出该分支(因此,也无需先检出另一个分支,然后才能删除该分支):

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

以下是如何恢复一个分支的步骤:

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

git push origin archive/<branchname> 推送标签。 - Nearoo

51

我根据远程更改扩展了Steve的答案

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

要从远程还原,请参见此问题


2
对于 Git v1.7.0+,最后一行也可以是 git push origin --delete <branchname>。来源:https://git-scm.com/book/en/v2/Git-Internals-The-Refspec - jslatane
2
这个答案如果加上一些命令解释会更好。乍一看,它们似乎删除了远程分支两次。我怀疑这不是真的,但我需要研究每个命令才能确定。 - isherwood

42

是的,你可以使用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)的ref不会出现在通常的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。由引用指向的提交是安全的。

编辑:发现了一个相同想法的perl实现,作者是@apgit-attic

编辑^2:发现一个博客文章,Git的创始人也使用了同样的技巧。他将其命名为git hold


6
很好,与这个帖子中的其他人不同,你实际上回答了这个问题。 - tzrlk
1
最后但并非最不重要的是,我不确定这些别名命令应该传递什么参数。我喜欢这种方法,但希望有更多的细节! - Devin Rhode
1
感谢您的评论。增加更多别名以操作存档(git grep-archivegit fetch-archivegit push-archive?)听起来是个好主意。我会更新答案并附上一些简介。rem代表记住我,但无论如何您都可以将其重命名为自己喜欢的名称。这只是一个快捷方式 :) - snipsnipsnip
1
这个Perl实现(git-attic)具有“list”,“save”,“rm”,“fetch”和“push”。 - Devin Rhode
1
我认为秒对于时间戳来说并不是非常有用/重要的,因此让引用名称感觉杂乱无章。但它确实确保了一些唯一性。这是我在这里写的一个版本:https://gist.github.com/devinrhode2/6e370ddfe824b1f49515f56e6314a434 - Devin Rhode
显示剩余5条评论

20
你可以将分支存档到另一个代码库中。虽然不够优雅,但我认为这是可行的替代方案。
git push git://yourthing.com/myproject-archive-branches.git yourbranch
git branch -d yourbranch

5
你可以创建git-bundle代替单独的代码库。 - Jakub Narębski
分支真的很棒。@JakubNarębski 什么是 git-bundle - Devin Rhode
1
@DevinGRhode:git bundle是一种创建文件的方式,该文件包含项目历史的选定部分,可用于获取或克隆。据我所知,它最初是为了“sneakernet”传输而设计的。 - Jakub Narębski

15

这是它的别名:

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用作别名名称。
此外,您可以定义别名来查看“归档”的分支列表:
arcl   = "! f() { git tag | grep '^archive/';}; f"

关于添加别名的内容


5
使用更新版本的Git(建议在此处查看),此别名提供自动完成功能:!git tag archive/$1 $1 && git branch -D - Lack
2
对于那些好奇的人,“git archive” 是用来创建.tar归档文件的,根据我浏览文档页面的5秒钟。 - Devin Rhode

7

我正在使用以下别名来隐藏存档分支:

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

所以使用git br来显示活跃开发的分支,而使用git bra则会显示所有分支,包括已经"归档"的分支。

6
一个分支是否已经被合并到主分支与其存档状态无关。例如,在我的开发团队中,我们有一些专门用于测试的分支。我们想把这些分支保留在存档中,但绝对不想将它们合并到主分支中。 - Bart

6
我不会对分支进行归档。换句话说,分支会自行归档。你需要做的是确保考古学家相关的信息可以通过可靠的方式找到。这种方式可靠是因为它有助于日常开发,并且不会增加完成工作的步骤。也就是说,我不相信人们会在完成分支后记得添加标签。
以下是两个简单的步骤,将极大地帮助考古学和开发:
  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并在合并提交中搜索分支名称。即使在非常大的存储库中,速度也足够快。
这就是我所说的分支自己归档的意思。
给每个分支打标签会增加不必要的工作量(这是一个关键的过程,应该无情地简化),用数百个只偶尔有用的标签堵塞了标签列表(不仅仅是性能问题,而且是人类可读性问题),甚至对考古学也没有多大用处。

3
那么关于混乱的问题呢?也许如果有一种方法可以在10立方码的土地下隐藏旧的树枝。 - bvj
4
这很有用,但不适用于未合并的分支。有时会在一个分支上进行实验,并且您希望保留内容,以防一些内容以后变得有用。 - Neil Mayhew
2
@bvj 我认为这个答案是在建议您应该始终删除已合并的分支,因为您总是可以通过合并提交返回到它们。我同意这种做法。 - Neil Mayhew
@NeilMayhew 是的,我自己也有大约10个未合并的分支。每个分支都与一个开放的任务相关联,这样我就可以记住我正在做什么。我要么对它们进行处理,要么它们变得过时不再相关,我会将它们删除。我曾经参与过一个项目,里面充斥着“我以后可能需要”的分支,以至于我们几乎看不清楚我们正在做什么。这实际上是一些开发人员不想为自己的代码整理干净的借口。适当的宽限是可以的,但不要让情况失控。 - Schwern
@Schwern 我同意。我也参与过这样的项目。我认为将分支转换为标签是消除混乱的好方法,因为标签列表总是会增长,而分支列表不应该增长(因为它代表正在进行的工作量)。对标签使用命名空间使列表更易于管理,但绝对需要抵制囤积癖。开发人员应该在自己的机器上保留提交,除非有很大的机会其他人最终会使用它们。 - Neil Mayhew
显示剩余6条评论

4
我有时会按以下方式归档分支:
  1. 生成补丁文件,例如:format-patch <branchName> <firstHash>^..<lastHash>(使用git log <branchName>获取firstHash和lastHash)。
  2. 将生成的补丁文件移动到文件服务器上的目录中。
  3. 删除分支,例如:git branch -D <branchName>
当您需要再次使用该分支时,“应用”补丁;但是,根据目标分支的状态,应用补丁文件(请参见git am)可能具有挑战性。好处是,这种方法使得分支的提交可以进行垃圾回收,并节省存储库中的空间。

我必须耐心地滚动到底部才能找到对我最有用的答案,并很高兴地给它一个早该给的赞! :) - davmos
根据目标分支的状态,可能会有一定难度。嗯,补丁的提交消息应该说明它是基于哪个提交(firstHash的父提交)的。 - Guildenstern

4

步骤0。检查工作树是否干净,以免丢失任何工作:

git status

步骤1。从本地仓库的根目录开始,检查要归档的分支,然后对其进行标记:

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

步骤 2:将标签上传到远程并确保在继续之前处于另一个分支,例如 main 分支:

git push origin --tags && git checkout main

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

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

需要替换<branch_name>为要存档的分支名称,在origin不是默认远程仓库名的情况下,需要替换为远程仓库的名称。

注释:

  • 步骤1之前和之后,您可能希望运行git tag命令以注意到添加的标签。

  • 步骤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>

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


1
晚来一步,但为了完整起见:您可以通过执行 git tag archive/<branch_name> remotes/origin/<branch_name> 或类似的命令标记一个当前不在的分支。然后,唯一需要的其他命令是 git push origin --tagsgit push origin -d <branch_name> (避免潜在昂贵的检出操作)。 - Plato

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接