Bash下的git.multi_gc尝试(一)

要在Bash下实现 git.multi_gc 功能,首先需要了解gc功能的实质。gc功能实际上由Git中几个不常用的辅助命令和低级命令组合而成。

GC功能涉及的过程

git-pack-refs

git-pack-refs会对$GIT_DIR/refs目录下的heads和tags的打包操作,将结果并保存到$GIT_DIR/packed-refs中。

命令格式:

git pack-refs [--all] [--no-prune]

常用选项:

--all

打包所有的heads和tags,如果不开启该项,则会只打包已经被打包过的heads和所有tags。

git-reflog

git-reflog有3个子命令:expire,show和delete。其中,expire用于删除陈旧的reflog记录。

命令格式:

git reflog expire [--dry-run] [--stale-fix] [--verbose] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>…

常用选项:

--expire=<time>

在此期限之前的记录将被删除。

--expire-unreachable=<time>

在此期限之前的且从当前分支顶端不可达的记录将被删除。

--all

作用于所有引用。

git-repack

git-repack用于打包仓库中未被打包的对象,也可用于将现存的pack文件重新组织成一个新的pack文件。
命令格式:
git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [--window=<n>] [--depth=<n>]
常用选项:
-a
将所有被引用的对象打包成一个pack,而非增量式的打包现有的松散对象。与-d选项一起使用,会直接删除pack文件中的悬空对象。
-A
当-d选项未被使用时与-a选项一致。与-d选项一起使用时,不会直接删除pack文件中的悬空对象,而是将其“吐出来”,成为松散对象。
-d
打包完成后删除冗余的pack。
-l
将 --local 选项传递给低级命令“git pack-objects”,使其忽略在 alternate 仓库中的对象。
-f
将 --no-reuse-delta 选项传递给低级命令“git pack-objects”,告诉它不要复用已存在的增量关系,而是重新计算。
-q
将 -q 选项传递给低级命令“git pack-objects”,关闭进度报告。
--window=<n>
--depth=<n>
两个选项会影响pack内对象的增量压缩存储的过程。

git-prune

从数据库中删除所有不可达的对象。
命令格式:
git prune [-n] [-v] [--expire <expire>] [--] [<head>…]
常用选项:
--expire <time>
只处理<time>之前的松散对象。

git-rerere

git rerere gc
如是。

其他需要了解的过程

git-rev-list

以时间逆序列出commit对象。
常用选项:
--all
作用于所有引用。

git-fsck

检查数据库中对象的连通性和有效性。

问题描述

在多repo做gc时需要注意仓库间的依赖关系,如果不注意,会出现意想不到的结果:

定义的函数有:
#!/bin/sh

function do_init() {
mkdir -p ta;
git --git-dir=ta/.git --work-tree=ta init;

echo "OK, first content!" > ta/a.txt;
git --git-dir=ta/.git --work-tree=ta add a.txt;
git --git-dir=ta/.git --work-tree=ta commit -am "initial commit";

mkdir -p ta/p;
echo "OK, second content!" > ta/p/b.txt;
git --git-dir=ta/.git --work-tree=ta add p/b.txt;
git --git-dir=ta/.git --work-tree=ta commit -am "second commit";

# new branch
git --git-dir=ta/.git --work-tree=ta branch new;

echo "Oh, I am wrong!" > ta/a.txt;
git --git-dir=ta/.git --work-tree=ta commit -am "third commit";

# switch
git --git-dir=ta/.git --work-tree=ta checkout new;

echo "No, I am right actually!" > ta/a.txt;
git --git-dir=ta/.git --work-tree=ta commit -am "forth commit";

echo "I am confused now!" > ta/a.txt;
git --git-dir=ta/.git --work-tree=ta commit -am "fifth commit";

# switch back
#git --git-dir=ta/.git --work-tree=ta checkout master;

git clone -s ta/.git tb;
}

function do_log() {
repo=$1;
git --git-dir=${repo}/.git --work-tree=${repo} log --all --graph;
}

function do_mod() {
# make some commits unreferenced
git  --git-dir=ta/.git --work-tree=ta reset --hard HEAD^;
}

function do_gc() {
git --git-dir=ta/.git --work-tree=ta fsck;  # show if there is dangling commit
git --git-dir=ta/.git --work-tree=ta fsck --no-reflogs; # show if there is unreferenced commit from reflog
# remove unreachable reflog
git --git-dir=ta/.git --work-tree=ta reflog expire --expire=now --all;
git --git-dir=ta/.git --work-tree=ta fsck;  # show if there is dangling commit
# remove unreferenced objects
git --git-dir=ta/.git --work-tree=ta prune;
git --git-dir=ta/.git --work-tree=ta fsck;  # show if there is dangling commit
}

function do_sta() {
repo=$1;
git --git-dir=${repo}/.git --work-tree=${repo} status;
}

调用的顺序如下:
do_init -> do_log ta -> do_mod -> do_gc -> do_sta tb
在 do_init 进行初始化后,通过 “do_log ta” 显示 ta 仓库的日志,如下图:

之后 do_mod 函数中对 ta 仓库进行更改,reset 会将分支的head从 commit 3b7958回复到 commit 0b7ee04,导致 commit 3b7958 编程未被引用状态(悬空状态)。do_gc 中的 reflog expire 和 prune 是 gc 功能的两个关键步骤,将会删除所有悬空对象,从而commit 3b7958被从数据库中删除,并再也无法恢复。

这一过程对 ta 仓库来说是完全合理的,但对从它派生的 tb 仓库来说却是致命的错误,我们调用 "do_sta tb" 显示 tb 仓库的状态,结果是:

fatal: bad object HEAD

原因很显然:对应的 commit 3b7958 早在对 ta 仓库进行更改的过程中被删除了。

同理,对 ta 仓库做 gc 时也会造成类似问题,所以,我们必须考虑好如何解决依赖带来的问题。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值