赞
踩
在git中,--mirror
是一个用于克隆和推送操作的参数。它用于创建一个镜像仓库,包含了源仓库的所有分支、标签和提交历史记录。
当使用git clone --mirror <source-repo>
命令时,会创建一个完全相同的镜像仓库,其中包含源仓库的所有内容。与普通克隆不同,镜像仓库会将所有的分支和标签都设为跟踪远程仓库的分支和标签。这意味着可以使用镜像仓库进行完整的版本控制操作,包括查看历史记录、切换分支等。
对于已经clone下载的仓库,如果远程仓库更新了,则可以用git fetch --prune
对本地仓库保持同步。当执行该命令时,git会从远程仓库获取最新的提交和分支信息,并将这些更新同步到本地仓库的远程跟踪分支中。同时,它还会检查本地的远程跟踪分支,如果在远程仓库中已经删除了某个分支,那么它也会将这个本地的远程跟踪分支删除。
当在镜像仓库上执行git push --mirror <destination-repo>
命令时,将会把镜像仓库的所有内容推送到目标仓库中,包括分支、标签和提交历史记录。
利用这些特性我们就可以完整备份git仓库。
$ git clone --mirror git@git.example.com/example.git Cloning into bare repository 'example.git'... remote: Enumerating objects: 760, done. remote: Counting objects: 100% (760/760), done. remote: Compressing objects: 100% (438/438), done. remote: Total 760 (delta 280), reused 759 (delta 279) Receiving objects: 100% (760/760), 4.79 MiB | 9.97 MiB/s, done. Resolving deltas: 100% (280/280), done. $ cd example.git/ $ tree -L 1 . ├── branches ├── config ├── description ├── HEAD ├── hooks ├── info ├── objects ├── packed-refs └── refs 5 directories, 4 files
需要使用ssh或者git地址,不要使用http地址,并把ssh public key传输到git服务器上,避免clone的时候需要输入密码。对于单个仓库,手动执行足够了,但对于批量仓库操作需要封装成api待用。
import os import sys import time class GitMirror(): def __init__(self): pass @staticmethod def os_system(cmd): print("execute command [%s]" % cmd) os.system(cmd) @staticmethod def mirror_fetch(local_path, remote_path): local_git_path = local_path + ".git" parent_path = os.path.dirname(local_git_path) # not first run if os.path.exists(local_git_path): print("enter [%s] and fetch" % (local_git_path)) GitMirror.os_system("git --git-dir=%s fetch --prune" % local_git_path) else: # first run, git clone print("git clone mirror from [%s] " % (remote_path)) GitMirror.os_system("mkdir -p %s; cd %s; git clone --mirror %s" % (parent_path, parent_path, remote_path)) time.sleep(0.2) if __name__ == "__main__": GitMirror.mirror_fetch("/home/dev/backup/example", "git@git.example.com/example.git")
gitlab, github, gitee,gerrit等git托管服务都提供了REST api,可以通过这些api批量获取仓库信息。以gitlab为例(https://docs.gitlab.com/ee/api/rest/ )
首先需要获取Acess Tokens, 在gitlab用户管理界面中找到 “Access Tokens” 标签,输入token名字,勾选 "api"生成token。注意,生成token后务必保存下来,因为后面就再也看不到了,如果没存下来,只能重新生成。
通过curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects"
可以获取本人以及本人可见的项目(Projects API | GitLab)。
把curl命令转换成python。
在获取全部项目的时候,每次只能返回1页的项目,所以想要获取全部项目,需要分页获取。
返回的项目信息中,有一个非常重要的字段"ssh_url_to_repo",有了项目地址就可以进行完整备份了。
import requests class GitlabApi(): def __init__(self, token, url): self.token = token self.url = "%s%s" %(url, "api/v4/") self.header = { "Content-Type": "application/json", "Private-Token": self.token } def get_all_projects(self): data = [] page = 0 total_pages = 1 while page < total_pages: api_url = "%s%s%s" % (self.url, "projects", "?page=%d" % page) res = requests.get(api_url, headers = self.header) total_pages = int(res.headers["X-Total-Pages"]) data += res.json() page += 1 return data if __name__ == "__main__": api = GitlabApi("your_token", "http://git.example.com/") res = api.get_all_projects() for i in res: print(i)
根据服务器的路径,在本地也做一个对应的目录结构。
def signal_handler(signum, frame): sys.exit(0) if __name__ == "__main__": signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) api = GitlabApi("your_token", "http://git.example.com/") download_path = os.path.join(os.path.abspath(os.getcwd()), "tmp") res = api.get_all_projects() for i in res: path = os.path.join(download_path, i["path_with_namespace"]) GitMirror.mirror_fetch(path, i["ssh_url_to_repo"]) with open("%s%s.json"%(path, i["name"]), "w") as fout: fout.write(json.dumps(i, indent=4)) print("total %d" % len(res))
import json import os.path import sys import signal import time import requests class GitlabApi(): def __init__(self, token, url): self.token = token self.url = "%s%s" %(url, "api/v4/") self.header = { "Content-Type": "application/json", "Private-Token": self.token } def get_all_projects(self): data = [] page = 0 total_pages = 1 while page < total_pages: api_url = "%s%s%s" % (self.url, "projects", "?page=%d" % page) res = requests.get(api_url, headers = self.header) total_pages = int(res.headers["X-Total-Pages"]) data += res.json() page += 1 return data class GitMirror(): def __init__(self): pass @staticmethod def os_system(cmd): print("execute command [%s]" % cmd) os.system(cmd) @staticmethod def mirror_fetch(local_path, remote_path): local_git_path = local_path + ".git" parent_path = os.path.dirname(local_git_path) # not first run if os.path.exists(local_git_path): print("enter [%s] and fetch" % (local_git_path)) GitMirror.os_system("git --git-dir=%s fetch --prune" % local_git_path) else: # first run, git clone print("git clone mirror from [%s] " % (remote_path)) GitMirror.os_system("mkdir -p %s; cd %s; git clone --mirror %s" % (parent_path, parent_path, remote_path)) time.sleep(0.2) def signal_handler(signum, frame): sys.exit(0) if __name__ == "__main__": signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) api = GitlabApi("your_token", "http://git.example.com/") download_path = os.path.join(os.path.abspath(os.getcwd()), "tmp") res = api.get_all_projects() for i in res: path = os.path.join(download_path, i["path_with_namespace"]) GitMirror.mirror_fetch(path, i["ssh_url_to_repo"]) with open("%s%s.json"%(path, i["name"]), "w") as fout: fout.write(json.dumps(i, indent=4)) print("total %d" % len(res))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。