[V 站有 git 大神吗? ]删除仓库的某个时间点之前的历史记录,减少.git 目录大小。 救命啊啊。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
git
Pro Git
Atlassian Git Tutorial
Pro Git 简体中文翻译
GitX
Yancey
V2EX    git

[V 站有 git 大神吗? ]删除仓库的某个时间点之前的历史记录,减少.git 目录大小。 救命啊啊。

  •  2
     
  •   Yancey 2016-08-08 09:48:05 +08:00 12933 次点击
    这是一个创建于 3376 天前的主题,其中的信息可能已经有所发展或是发生改变。
    安卓仓库。有很多 commits 和分支。想删除某个时间点之前的所有 commits 减少.git 目录大小,找了很多办法。感觉只有 grafts+ filter-brnach 靠谱。不过还是遇到很多问题、
    1.要截断的 commits 之前有很多分支。。用 filter-branch 不会删除这些分支,怎么解决?
    2.这个仓库是公开仓库。很多人已经 clone 在本地。所以截断操作应该是在服务器还是本地?我试过在本地更改,但是推不到服务器上。如果在服务器操作,怎么将本地的改动变为最小?

    示意图如下:
    56 条回复    2016-08-09 11:14:58 +08:00
    JhZ7z587cYROBgVQ
        1
    JhZ7z587cYROBgVQ  
       2016-08-08 09:55:00 +08:00   1
    新开一个仓库?(手动滑稽)坐等楼下大神给出答案
    Yancey
        2
    Yancey  
    OP
       2016-08-08 09:56:25 +08:00
    @jason0916 能新建...也不会这么麻烦了
    vitovan
        3
    vitovan  
       2016-08-08 09:56:31 +08:00
    rebase ?
    vitovan
        4
    vitovan  
       2016-08-08 09:57:07 +08:00
    https://git-scm.com/docs/git-rebase

    git-rebase - Reapply commits on top of another base tip
    Yancey
        5
    Yancey  
    OP
       2016-08-08 09:57:38 +08:00
    @vitovan rebase 不行。。 rebase 是当前的回到某个时间点或 commits 。 现在是要把某个时间点之前的丢掉
        6
    Yancey  
    OP
       2016-08-08 10:00:04 +08:00
    @vitovan sorry 看错,以为你说的 reset 。 rebase 是可以。但是一般服务器的仓库是一个裸仓库(没有工作目录), git rebase , git branch 命令都没法使用。。如果在本地操作,强推到服务端。所有分支都会混乱、、
    lijianying10
        7
    lijianying10  
       2016-08-08 10:02:59 +08:00
    删掉本地仓库然后
    git clone --depth=6 https://xxxxxxx.git
    看看这种行不行?
    mengzhuo
        8
    mengzhuo  
       2016-08-08 10:07:08 +08:00 via iPhone
    本地 gc 一次 减少空间
    而且删 commit 很不可取啊
    relaxgo
        9
    relaxgo  
       2016-08-08 10:12:29 +08:00 via Android
    试试这个方案 [git 替换]( https://git-scm.com/book/zh/v2/Git-工具-替换)
    Yancey
        10
    Yancey  
    OP
       2016-08-08 10:13:08 +08:00
    @lijianying10 这种治标不治本啊。。
    vitovan
        11
    vitovan  
       2016-08-08 10:13:59 +08:00
    r#6 @Yancey 「如果在本地操作,强推到服务端。所有分支都会混乱」,为啥(扣鼻状)?
    我是 Git 小白,求教...
    shenqi
        12
    shenqi  
       2016-08-08 10:14:52 +08:00
    既然都打算这样了,就直接那个新的仓库,旧的仓库随时能查看就行了。
    Yancey
        13
    Yancey  
    OP
       2016-08-08 10:15:04 +08:00
    @mengzhuo commits 是确定不会再用的, 所以要删掉。。 gc 一次 200M 顶多到 160M 。
    forcecharlie
        14
    forcecharlie  
       2016-08-08 10:18:41 +08:00
    git clone --depth 会从最新的 commit 迭代回去,并不是完整的 repository ,一些额外的操作需要还原成完整的仓库, 在服务器上进行 git gc ---prune=now 可以一定程度的减小仓库体积. 一个思路是,扫描出 仓库中大文件提交时的 父 commit, 然后将分支强制设置为此 commit,即修改 .git/refs/heads/xxxx 的 commitid ,工作目录删除此大文件,然后创建提交,运行 git gc, 强制推送到服务器,服务器运行 git gc . 这样仓库的体积就可以减小了,这个实际上和 git rebase 类似,但不会丢失当前的 commit. 对于你的需求, git commit 的创建由父 commit 根 tree,提交者 提交信息等通过 sha1 创建,是链式的,牵一发而动全身. 所以你的不太好实现.
    Yancey
        15
    Yancey  
    OP
       2016-08-08 10:19:43 +08:00
    @vitovan 因为丢掉历史,会让从截断的地方到现在最新的 commit 的 ID 都改变,也就是相当于把所有的留下的 commits 都重新提交了一遍。 这个时候服务端和本地 commits 没有一个是能对应上的。强行 push 我们期望的效果是 服务端的 commits 和分支记录 都完全被更改过的本地 commits 替换掉。。但是 git 并不会这样。
    Yancey
        16
    Yancey  
    OP
       2016-08-08 10:22:22 +08:00
    @forcecharlie 对啊。截断的效果就是从截断到现在最新的 commits 所有的 SHA
    Yancey
        17
    Yancey  
    OP
       2016-08-08 10:24:26 +08:00
    @Yancey 对啊。截断导致结果就是从截断到现在最新的 commits 所有的 SHA1 值都会改变。。因为是公开的项目。很多人已经克隆了。。对本地的影响很大。
    @forcecharlie
    kukat
        18
    kukat  
       2016-08-08 10:27:22 +08:00
    ~ du -sh .git
    758M .git
    Yancey
        20
    Yancey  
    OP
       2016-08-08 10:29:04 +08:00
    @kukat 你这还不减? 我们一个工程就这么大。总共几十个工程。。。源码编译的时候硬盘受不了
    zzn
        21
    zzn  
       2016-08-08 10:30:15 +08:00
    公开项目不应该修改已提交的 commit ,除非让全部人重新 clone

    其实很好奇,究竟.git 是有多大? 怎么会有这种需求。。。
    Yancey
        22
    Yancey  
    OP
       2016-08-08 10:32:23 +08:00
    @bjzhou1990 我就是参考这个。如图的 git 结构,遇到问题是,因为截断点之前还有 commit 。会导致截断后除了 之后想要的 commit 线外。 branch1 branch2 不会被操作。保持原样。 分支混乱。。
    Yancey
        23
    Yancey  
    OP
       2016-08-08 10:35:06 +08:00
    @zzn 背景是这样。 最开始一个主工程。然后 clone 了好几份工程作为不同的库(这个点我们暂定位 date 点)。后来这些库往不同业务发展。所以想将 date 点之前的所有 commit 删除。毕竟后来分成不同库了, date 之前和这些库完全没有啥关系。。
    vitovan
        24
    vitovan  
       2016-08-08 10:35:52 +08:00
    r#15 @Yancey 嗯,刚才想说 --force 来着,如果不能让别人都接受 rebase ,好像确实有些难办:
    http://stackoverflow.com/questions/8939977/git-push-rejected-after-feature-branch-rebase

    `rebasing feature branches on master and force-pushing them back to remote repository is OK as long as you're the only one who works on that branch.`
    jianyunet
        25
    jianyunet  
       2016-08-08 10:36:55 +08:00   1
    既然都已经是公开项目了,在服务器端删掉也必须强推到各客户端啊。最简单的办法还是开一个新仓库做个 rebase
    mengzhuo
        26
    mengzhuo  
       2016-08-08 10:40:48 +08:00   1
    @Yancey 公开项目只能另开,或者删了再来了。要不然其他人会拉不到原始 commit 各种报错的
    bjzhou1990
        27
    bjzhou1990  
       2016-08-08 10:45:27 +08:00   1
    @Yancey 用 git checkout --orphan 创建新分支,然后在新分支上开发,丢弃原始分支?
    Yancey
        28
    Yancey  
    OP
       2016-08-08 10:59:12 +08:00
    @bjzhou1990 恩。是个好办法。
    我测试还是遇到问题
    1. 在服务端。我自己测试的时候是通过 git init --bare 来建仓库的,所以服务端 git checkout --orphan 命令没法使用。。
    2. 服务端进行操作后,所有的本地仓库都要克隆一遍。。


    我考虑的办法。
    假设我们要截断的点 hash 值为 1234abc
    在服务端:
    echo 1234abc > info/grafts
    git filter-branch -- --all
    以及后续删除 grafts 和 gc 操作

    写个脚本让所有本地克隆的都执行。内容大概是
    echo 1234abc > .git/info/grafts
    git filter-branch -- --all
    删除 grafts
    git fetch --all

    目的是让本地和服务端改变一模一样。这样本地的分支。没有 push 的 commits 都可以保留


    还是遇到问题

    在服务端;
    执行 git filter-branch -- --all 之前要将所有的 1234abc 之前的分支都删除。

    在本地
    git filter-branch -- --all 执行的时候 origin/xx 这种分支也会被操作。总之结果很混乱。不是想要的效果


    看来真的是无解啊。。
    jason19659
        29
    jason19659  
       2016-08-08 10:59:25 +08:00
    删除之后别人的项目跟你的肯定不是一个项目了。
    subpo
        30
    subpo  
    PRO
       2016-08-08 11:08:53 +08:00
    迷之需求
    bjzhou1990
        31
    bjzhou1990  
       2016-08-08 11:21:33 +08:00
    @Yancey 所有 git 操作都应该是本地修改然后提交服务器,不要直接在服务器端修改啊。另外 git filter-branch 里的--all 意思是修改所有分支和 tag ,可以单独指定分支的吧?建议看看 git filter-branch --help ,文档很详细
    kukat
        32
    kukat  
       2016-08-08 11:35:49 +08:00
    @Yancey 编译跟.git 有什么关系?
    SpicyCat
        33
    SpicyCat  
       2016-08-08 11:45:20 +08:00
    我觉在做这个操作之前,先要反思下你们对 git 的使用。单纯的 git 提交历史长不不会显著增加 git repo 的大小,一般发现 git repo 突然增大,都是误添加了大文件。
    因为 git history 太长觉得影响效率是误解。有些大项目,上万的 commit ,太稀松平常了。

    具体到你的需求,你想删掉某个时间点以前的 commit ,是肯定能做到的, git rebase -i 就可以。但是那样做以后,我估计你的项目就出错了,因为必然有些文件是在某个 commit 被加进来的,然后你把那个 commit 删除了,那以后某个 commit 要修改那个文件,会出什么现象我也不知道。总之这样做隐患很大。
    更不用说你想改的是服务器,会影响所有人,真是要慎重。

    另外推荐一款清理 git repo 的工具 https://rtyley.github.io/bfg-repo-cleaner/
    不过我一般是用它删除特定文件,可能不太符合你的需求。
    Yancey
        34
    Yancey  
    OP
       2016-08-08 12:05:03 +08:00
    @SpicyCat 因为是 android 项目。我查了下大文件, 基本是图片或 jar 包。这些图片或 jar 包经过迭代现在很可能已经不再使用了,但是还在仓库历史里面。所以.git folder 比较大。

    看来这问题是无解了。
    Yancey
        35
    Yancey  
    OP
       2016-08-08 12:05:51 +08:00
    @kukat 编译没关系。。这不是编译服务器磁盘小么。装不下所有带.git 文件夹的工程了
    9hills
        36
    9hills  
       2016-08-08 12:07:00 +08:00
    rebase 可以只合并某个范围内的 commit 。但是修改后需要约定下所有人,同时重新拉取
    mrcode
        37
    mrcode  
       2016-08-08 12:20:07 +08:00
    可以试试 revert 他会生成一个新的提交, 来抵消掉你指定的提交的更改. 然后 git 垃圾回收就回收掉没用的那部分了
    SpicyCat
        38
    SpicyCat  
       2016-08-08 12:22:43 +08:00
    @Yancey 那么解决问题的方法就是确定哪些图片文件或者 jar 不需要了,利用 bfg(就是我上个回复提到的清理工具)清理一下。 bfg 是把目标文件彻底从 git repo 里删除。
    SpicyCat
        39
    SpicyCat  
       2016-08-08 12:23:23 +08:00
    当然,最好还是新建一个 repo ,保留原有 repo 。
    fy
        40
    fy  
       2016-08-08 13:38:57 +08:00
    翻出一篇笔记:

    git clone [email protected]:jfinal/jfinal.git
    git filter-branch --tree-filter 'rm -f WebRoot/WEB-INF/lib/*.jar' --tag-name-filter cat -- --all
    git push origin --tags --force
    git push origin --all --force
    skydiver
        41
    skydiver  
       2016-08-08 14:13:37 +08:00   1
    如果误添加了大文件导致,可以用楼上 @fy 这种方法

    如果要彻底修改历史, rebase -i 就行了,保留第一个,保留现在的,中间的都 fixup
    AnyOfYou
        42
    AnyOfYou  
       2016-08-08 14:23:06 +08:00
    官方的用来管理 Android 源码下众多 git 仓库的 repo 工具, init 的时候可以指定 branch 。
    这个 repo 实际就是一堆 python 脚本。 init 后到会把 subcmds 下载到本地。你可以看其 sync 的实现并修改。
    AnyOfYou
        43
    AnyOfYou  
       2016-08-08 14:25:13 +08:00
    另外, init 的时候可以直接指定 --depth 。
    Yancey
        44
    Yancey  
    OP
       2016-08-08 14:28:23 +08:00
    @skydiver 几百个 branch 几千个 commits 怎么 rebase -i 不行吧
    SourceMan
        45
    SourceMan  
       2016-08-08 15:21:30 +08:00
    楼主没说规模,我这边几个 G 的算什么程度?
    Yancey
        46
    Yancey  
    OP
       2016-08-08 15:35:12 +08:00
    @SourceMan 我这边磁盘小。。编译的时候拉十几个工程也是几个 G
    skydiver
        47
    skydiver  
       2016-08-08 15:35:26 +08:00
    @Yancey 为什么不行?麻烦一点儿而已
    402124773
        48
    402124773  
       2016-08-08 17:05:38 +08:00
    7.2G .git
    也没见过提出这个需求。
    哈哈
    wweir
        49
    wweir  
       2016-08-08 18:50:42 +08:00
    不知道是否有更直接的操作方式
    只知道 squash 命令可以压缩大量 commit 为一个

    结合楼主的需求,也许结合 rebase 可以实现相应功能。

    我们这边的做法是,在下一次大重构之前,拷出所有有用的代码为一个新的 repo 。老代码就自己慢慢滚下去,直到成为一个历史档案
    jimages
        50
    jimages  
       2016-08-08 22:56:46 +08:00
    这笔记本太熟悉了。渡边本。
    msg7086
        51
    msg7086  
       2016-08-08 23:10:17 +08:00
    修改历史一定会导致 commit hash 变动,亲你不会连这点知识都没有吧……
    sodatea
        52
    sodatea  
       2016-08-09 00:47:15 +08:00
    @Yancey git clone --single-branch 的体积也仍然无法接受么?
    tinyproxy
        53
    tinyproxy  
       2016-08-09 08:44:22 +08:00 via iPhone
    @Yancey 一个建议,资源文件等大体积的不要入库,传到某云存储去,通过一个 configure 脚本下载回来就好了
    yuankui
        54
    yuankui  
       2016-08-09 08:51:46 +08:00
    @lijianying10 这个方法不错~
    mrsatangel
        55
    mrsatangel  
       2016-08-09 09:40:21 +08:00   1
    `root -> A -> B -> C -> newRoot -> D `
    也就是想要将 newRoot 之前的 commit 对象全部删除。

    在`.git/info/`下建立`grafts`文件,在其中输入`newRoot`对象的 SHA1 值,保存。此时使用`git log`命令应该只能看到`newRoot`的 commit 记录。
    之后使用`git filter-branch`使这个新建的 root 生效.

    注意,这个 chop 操作可能会导致出现一个新的 detached 的 branch ,为当前的这个 branch 新建一个临时的 branch:`git branch tmp`,然后将 tmp branch 推送到远程的 repo 里想要更新的 branch (如 master ):`git push <remoteRepoName> tmp:<remoteBranchName>`,此时远程 repo 里面的**&lt;remoteBranchName&gt;**已经将**newRoot**之前的所有 commit 对象删除。
    Yancey
        56
    Yancey  
    OP
       2016-08-09 11:14:58 +08:00
    @mrsatangel 感谢。你的方案可行。

    我原本以为本地截断后,不能强行推送到远端。原来是可以的。

    不过你提到
    “为当前的这个 branch 新建一个临时的 branch:`git branch tmp`,然后将 tmp branch 推送到远程的 repo 里想要更新的 branch (如 master ):`git push <remoteRepoName> tmp:<remoteBranchName>`,此时远程 repo 里面的**&lt;remoteBranchName&gt;**已经将**newRoot**之前的所有 commit 对象删除。”

    感觉这个 tem branch 是多余的吧。直接强推截断的 master 就可以的。


    另外对你的答案补充下;
    假如你有如下的分支结构图



    现在想从"57dd13f even"这个点截断。保留这个点上面的所有分支和 commit
    首先需要 git push origin :test 将 test 分支删除。也就是说截断点以下的所有都删除。
    再者 将截断点以上的所有分支都在本地有追踪分支 git checkout -b new origin/new

    之后就执行

    “在`.git/info/`下建立`grafts`文件,在其中输入`newRoot`对象的 SHA1 值,保存。此时使用`git log`命令应该只能看到`newRoot`的 commit 记录。
    之后使用`git filter-branch`使这个新建的 root 生效. ”


    最后执行
    将本地所有分支都强推到远端一遍。
    本例就是 git push --force origin master:master 和 git push --force origin new:new


    哦对了,还要删除 original 文件夹保存的临时记录
    rm -r .git/refs/original/


    最后效果:





    其实这样的好处还有一个就是,所有 clone 这个仓库的本地仓库都可以执行一个相同的脚本。这样就不用大家都重新 clone 了,本地的 branch 和没有推送的 commit 也可以保留。


    不知道理解对不对。可以再讨论。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     921 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 21:41 PVG 05:41 LAX 13:41 JFK 16:41
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86