Git仓库的打包与还原 - bundle相关命令介绍

1、使用bundle命令打包仓库

众所周知,git仓库中有一个 “.git” 文件夹,存储了和当前git仓库有关的所有信息。

但是这个文件夹里面的文件太多了,碎片文件太多影响拷贝速度,如果先打包成压缩包还要多一个步骤。

然而git实际上提供了打包的命令:

gitbundle create FILE TAGS

将 “TAGS” 替换为 “–all”,可以打包所有分支,包括本地分支、标签、origin分支、stash分支。

将 “TAGS” 替换为分支名,可以打包指定分支。

将 “TAGS” 替换为 “–all -10”,可以打包所有分支的最后10次提交,避免仓库太大,产生的bundle文件太大,可以用于后续的增量更新。

还有更简单的办法,在环境变量路径下创建一个 “bundle.bat” 文件,内容如下:

for/f %%iin('cd')dogitbundle create"%%~nxi.git"--all%1

然后在项目文件夹中,在文件浏览器的地址栏中输入 “bundle”,然后回车,就可以将当前git仓库打包成于当前文件夹同名的git后缀文件。

例如项目文件夹是 “repo_name”,在地址栏输入 “bundle”,就可以产生 “repo_name.git” 文件。

如果输入 “bundle -10”,就只打包最后10次提交。

2、还原bundle命令打包的仓库

使用 “repo_name.git” 的方法几乎完全与网络的git仓库地址一致。

例如克隆命令:

gitclone D:\repos\repo_name.git repo_name_new

但是这样操作会将 “repo_name.git” 看做一个远端,所以 “repo_name” 目录下的本地仓库,会在克隆后变成 “origin” 里的分支。而 “repo_name” 目录下远端分支,不会进入 “repo_name_new” 目录下。

使用 “–bare” 参数也不行,但是可以使用 “–mirror” 参数,这样会完整复制 “repo_name.git” 中记录的所有分支。

但问题是 “–mirror” 模式会包含 “–bare” 参数,也就是这是一个裸仓库,看不到代码仓里的项目文件,这还是不太方便的。

所以解决方法是创建一个空文件夹,然后在这个空文件夹内依次运行:

gitinitgitfetch D:\repos\repo_name.gitgitcheckout FETCH_HEADgitfetch D:\repos\repo_name.git *:*

其中最后一条命令会将远端的分支全部覆写当前的分支,也包括当前的分支。

但是当前分支不能被覆写,所以需要先检出到脱离分支。同时空仓库时也不能被覆写,所以需要检出到一个具体的commit。

所以我的做法是fetch两次,第一次是为了检出到FETCH_HEAD(这是一个脱离分支),然后再次fetch获取所有远端分支。(包括远端的本地分支、远端的远端分支、标签、和特殊分支)

而如果一开始不是一个空仓库(不是从 git init 建立),需要先检出到脱离分支:

gitcheckout--detach

然后再做fetch覆写:

gitfetch D:\repos\repo_name.git *:*

这个命令的做法可以将 “repo_name.git” 完全还原到最初的 “repo_name” 文件夹内的分支状态。

或者可以创建一个 “bundle-clone.bat” 文件,内容如下:

:: bundle-clone.bat @echo offsetREPO_PATH="%~f1"md"%~n1"cd"%~n1"gitinitgitfetch %REPO_PATH%gitcheckout FETCH_HEADgitfetch %REPO_PATH% *:*

然后可以快速克隆仓库:

bundle-clone D:\repos\repo_name.git

或者可以采用和 “bundle.bat” 反向的操作,创建一个 “unbundle.bat” 文件,内容如下:

@echo offfor%%iin(*.git)dosetname=%%~nxigitinitgitfetch %name%gitcheckout FETCH_HEADgitfetch %name% *:*

实现效果自动获取当前文件夹内的最后一个 “.git” 文件,然后展开为git仓库。

3、将本地仓库推送到其他远端

将一个平台的仓库在不联网互通的情况下迁移到另一个平台,最好的做法是使用 “git clone --mirror” 克隆,然后再用 “git push origin --mirror” 推送。

但是如果你下载仓库的时候不小心没有使用 “–mirror” 参数克隆,也是有办法补救的。

举例来说,如果一个远端的仓库有 master、dev、feature 几个分支,其中默认分支是 master 分支。

那么如果你用 “git clone” 命令,没有增加 “–mirror” 参数,克隆下来的仓库分支会是:

master remotes/origin/master remotes/origin/dev remotes/origin/feature

这时如果使用 “git push origin” 推送,只会推送上去 “master” 分支。

如果将追踪的远端分支一个一个 checkout 再推送,那么操作繁琐。

如果使用 “git push origin --mirror” 推送,那也没用,因为将 “remotes/origin/dev” 推送上去在远端的引用也是 “remotes/origin/dev”,还是看不见。

所以解决的方法是:

:: 推送本地分支+标签gitpush URL * :: 推送本地的远端分支gitpush URL refs/remotes/origin/*:refs/heads/* :: 推送本地的远端分支+标签gitpush URL refs/remotes/origin/*:refs/heads/*--tags

其中第3种做法是将本地的远端分支推送到了远端的本地分支,并且推送了标签。

原因是你用 “git clone” 克隆下来的仓库,远端分支会记录在 “refs/remotes/origin/*” 下,后续检出为本地分支进行二次开发,不会影响到这些记录的远端分支。

然后运行这个命令,就会将 “refs/remotes/origin/*” 下的分支再推送到远端的本地分支,还原远端的仓库效果。

或者说你克隆下来是什么样,再次推上去就是什么样。

4、将外网git仓库迁移到内网git仓库

将上面几个步骤连起来,就是迁移步骤:

1、从外网URL1克隆仓库和制作bundle文件:

gitclone URL1 REPO_NAMEcdREPO_NAMEgitbundle create REPO_NAME.git--all

2、还原bundle文件和推送到内网仓库URL2:

cdREPO_NAMEgitinitgitfetch REPO_NAME.gitgitcheckout FETCH_HEADgitfetch REPO_NAME.git *:*gitpush URL2 refs/remotes/origin/*:refs/heads/*--tags

或者可以创建一个 “push-origin.bat” 文件,在文件浏览器地址栏输入后快速启用(需要首先在内网服务器建立空项目):

@echo offsetURL_BASE=http://192.168.1.100/reposfor/f %%iin('cd')dogitpush"%URL_BASE%/%%~nxi"refs/remotes/origin/*:refs/heads/*--tags