迁移项目需要用到一个开源的项目:https://github.com/samrocketman/gitlab-mirrors
同时上传了一个修改后的:git仓库————迁移脚本-Python文档类资源-CSDN下载
这里仅介绍如果把老Git库项目迁移到新Git库,不涉及更新库与删除库
一、原理介绍
主要用到开源项目中的三个文件:config.sh -> add_mirror.sh -> lib/manage_gitlab_project.py,之后会详细讲解这三个文件;
脚本主要是shell+python构成,运行方式比较简单,我这里使用的是Git Bash Here来运行shell命令的,首先进入到gitlab-mirrors目录下,右键即可,如下图:
运行命令如下:
./add_mirror.sh --git --group-id 组的id --project-name abc --mirror http://账号:访问令牌@192.168.15.20/abc.git
运行原理:
1. 在运行的电脑上,将老的Git镜像下来
2. 在新的Git服务器上创建项目
3. 将运行电脑上的Git remote设置为新的Git地址
4. 将运行电脑上的Git同步到新的Git地址
二、配置文件介绍(config.sh)
#Environment file
#
# gitlab-mirrors settings
#
#The user git-mirrors will run as.(随便命名)
system_user="gitmirror"
#The home directory path of the $system_user
# 旧Git与新Git的同步中转站,保证此目录为空即可
user_home="d:/gitmirror/"
#The repository directory where gitlab-mirrors will contain copies of mirrored
#repositories before pushing them to gitlab.
# 具体的中转项目,会在此目录下创建对应的项目
repo_dir="${user_home}/repositories"
#colorize output of add_mirror.sh, update_mirror.sh, and git-mirrors.sh
#commands.(不用管)
enable_colors=true
#These are additional options which should be passed to git-svn. On the command
#line type "git help svn" (不用管)
git_svn_additional_options="-s"
#Force gitlab-mirrors to not create the gitlab remote so a remote URL must be
#provided. (superceded by no_remote_set)(不用管)
no_create_set=false
#Force gitlab-mirrors to only allow local remotes only.(不用管)
no_remote_set=false
#Enable force fetching and pushing. Will overwrite references if upstream
#forced pushed. Applies to git projects only.(不用管)
force_update=false
#This option is for pruning mirrors. If a branch is deleted upstream then that
#change will propagate into your GitLab mirror. Aplies to git projects only.
prune_mirrors=false
#
# Gitlab settings
#
#This is the base web url of your Gitlab server.
#新Git服务地址
gitlab_url="http://192.168.15.20"
#Special user you created in Gitlab whose only purpose is to update mirror sites
#and admin the $gitlab_namespace group.
#新Git服务器上的账号
gitlab_user=git账号名字
#Generate a token for your $gitlab_user and set it here.
#新Git服务器上生成的访问令牌,后面会讲解如何生成
gitlab_user_token_secret=访问令牌
#Sets the Gitlab API version, either 3 or 4
# 不用管
gitlab_api_version=4
#Verify signed SSL certificates?
# 不用管
ssl_verify=true
#Push to GitLab over http? Otherwise will push projects via SSH.
# 不用管
http_remote=false
#
# Gitlab new project default settings. If a project needs to be created by
# gitlab-mirrors then it will assign the following values as defaults.
#
#values must be true or false
# 是否开启讨论功能
issues_enabled=false
wall_enabled=false
# 是否开启Wiki功能
wiki_enabled=true
snippets_enabled=false
#是否开启merge功能
merge_requests_enabled=true
public=false
具体每个字段的含义,在上面的脚本中已经注明
访问令牌的生成,如下图所示:
三、同步脚本(add_mirror.sh)
#!/bin/bash
#Created by Sam Gleske
#MIT License
#Created Tue Sep 10 23:01:08 EDT 2013
#USAGE
# ./add_mirror.sh --git --project-name someproject --mirror http://example.com/project.git
#bash option stop on first error
set -e
#Include all user options and dependencies
# 添加所有的依赖库
git_mirrors_dir="${0%/*}"
source ${git_mirrors_dir}/includes.sh
#check if api version is set
[ -z $gitlab_api_version ] && gitlab_api_version=4
#export env vars for python script
# 导出config.sh中配置的环境变量,python中也可以用
export gitlab_user_token_secret gitlab_url gitlab_user ssl_verify gitlab_api_version
PROGNAME="${0##*/}"
PROGVERSION="${VERSION}"
#Default script options
git=false
project_name=""
mirror=""
group_id=0 # 从命令行读取的组ID
desc=""
force=false
no_create_set="${no_create_set:-false}"
no_remote_set="${no_remote_set:-false}"
http_remote="${http_remote:-false}"
# 自定义变量
# 通过组ID获取到的组的路径名字
group_name="" # 组对应的路径名字
#
# ARGUMENT HANDLING
#
usage()
{
cat <&2;then
echo "Command aborted due to previous errors." 1>&2
exit 1
fi
#Set up project creation options based on config.sh to be passed to create manage_gitlab_project.py
# 构建参数,传递到manage_gitlab_project.py,也就是将shell数据传递到python中
CREATE_OPTS=""
if ${issues_enabled};then
CREATE_OPTS="--issues ${CREATE_OPTS}"
fi
if ${wall_enabled};then
CREATE_OPTS="--wall ${CREATE_OPTS}"
fi
if ${merge_requests_enabled};then
CREATE_OPTS="--merge ${CREATE_OPTS}"
fi
if ${wiki_enabled};then
CREATE_OPTS="--wiki ${CREATE_OPTS}"
fi
if ${snippets_enabled};then
CREATE_OPTS="--snippets ${CREATE_OPTS}"
fi
if ${public};then
CREATE_OPTS="--public ${CREATE_OPTS}"
fi
if ${http_remote};then
CREATE_OPTS="--http ${CREATE_OPTS}"
fi
# 如果远端不存在,则调用python相应的命令,进行项目的创建
#Get the remote gitlab url for the specified project.
#If the project doesn't already exist in gitlab then create it.
if ! ${no_remote_set} && [ -z "${no_create}" ];then
green_echo "Resolving gitlab remote." 1>&2
# 调用python方法,去创建Git仓库
gitlab_remote=$(python lib/manage_gitlab_project.py --create --groupid ${group_id} --desc "${desc}" ${CREATE_OPTS} "${project_name}")
# 接收python的返回值,解析为对应的数据
# retstr=$?
green_echo ${gitlab_remote}
# 以&进行字符串拆分,python传出的数据
array=(${gitlab_remote//,/ })
# 构建咋们新Git的地址
gitlab_remote="http://admin_jt:${gitlab_user_token_secret}@192.168.15.20/${array[0]}/${project_name}.git"
# 同步某个仓库的wiki时,用下面的地址
# gitlab_remote="http://admin_jt:${gitlab_user_token_secret}@192.168.15.20/${array[0]}/${project_name}.wiki.git"
# 设置项目的路径名称
group_name=${array[1]}
# for var in ${array[@]}
# do
# echo $var
# done
else
if ! ${no_remote_set};then
green_echo -n "Using remote: " 1>&2
echo "${no_create}" 1>&2
gitlab_remote="${no_create}"
else
echo "Local only mirror." 1>&2
fi
fi
#Check for namespace directory existence
# 创建项目的Git目录
if [ ! -e "${repo_dir}/${group_name}" ];then
mkdir -p "${repo_dir}/${group_name}"
elif [ ! -d "${repo_dir}/${group_name}" ];then
red_echo "Error: \"${repo_dir}/${group_name}\" exists but is not a directory." 1>&2
exit 1
elif [ -d "${repo_dir}/${group_name}/${project_name}" ] && ! ${force};then
red_echo "Error: \"${repo_dir}/${group_name}/${project_name}\" exists already. Aborting command." 1>&2
exit 1
fi
#Resolve the $authors_file path because of changing working directories
if [ ! -z "${authors_file}" ];then
if ! echo "${authors_file}" | grep '^/' &> /dev/null;then
authors_file="${PWD}/${authors_file}"
authors_file="$(echo ${authors_file} | sed 's#/./#/#g')"
fi
fi
cd "${git_mirrors_dir}"
if ${git};then
green_echo "Creating mirror from ${mirror} ${group_name}" 1>&2
# 进入Git目录
cd "${repo_dir}/${group_name}"
# 将老的Git项目克隆到本地
git clone --mirror "${mirror}" "${project_name}"
cd "${project_name}"
if ! ${no_remote_set};then
green_echo "Adding gitlab remote to project." 1>&2
# 在本地添加新Git地址为新的远端
git remote add gitlab "${gitlab_remote}"
# 配置同步内容
git config --add remote.gitlab.push '+refs/heads/*:refs/heads/*'
git config --add remote.gitlab.push '+refs/tags/*:refs/tags/*'
git config remote.gitlab.mirror true
#Check the initial repository into gitlab
green_echo "Checking the mirror into gitlab." 1>&2
# 从老Git地址拉取最新的内容
git fetch
if ${http_remote};then
git config credential.helper store
fi
# 将Git内容,推送到新的远端(新Git地址)
git push gitlab
if [ ! -z "${no_create}" ];then
git config gitlabmirrors.nocreate true
fi
else
git config gitlabmirrors.noremote true
fi
green_echo "All done!" 1>&2
fi
四、同步脚本(manage_gitlab_project.py)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Created by Sam Gleske
#MIT License
#Created Tue Sep 10 23:01:08 EDT 2013
from sys import argv,exit,stderr
from optparse import OptionParser
import os
try:
import gitlab
except ImportError:
raise ImportError("python-gitlab module is not installed. You probably didn't read the install instructions closely enough. See docs/prerequisites.md.")
def printErr(message):
stderr.write(message + "\n")
stderr.flush()
# 通过组的ID,查找到对应的组位置
def find_group(gid):
groups = git.groups.list(all=True)
for obj in groups:
# printErr(str(obj.id) +" asdasdasd "+ str(gid))
# printErr(str(obj.web_url))
if getattr(obj,"id") == int(gid):
return obj
return None
# 通过组的名字,和项目名字,确定项目对象
def find_project(gid,pname):
projects = git.projects.list(as_list=True)
for obj in projects:
# printErr(str(getattr(obj,"name")) +"-----"+ str(pname))
# printErr(str(getattr(obj,"namespace")) +"-----"+ str(gid))
nspace = getattr(obj,"namespace")
if getattr(obj,"name") == pname and nspace["id"] == int(gid):
return obj
return None
# 创建项目
def createproject(pname):
if options.public:
visibility_level="public"
else:
visibility_level="private"
if len(options.desc) == 0:
if options.public:
description="Public mirror of %s." % project_name
else:
description="Git mirror of %s." % project_name
else:
description=options.desc
project_options={
'issues_enabled': options.issues,
'wall_enabled': options.wall,
'merge_requests_enabled': options.merge,
'wiki_enabled': options.wiki,
'snippets_enabled': options.snippets,
'visibility': visibility_level,
'namespace_id': options.groupid,
}
#make all project options lowercase boolean strings i.e. true instead of True
for x in project_options.keys():
project_options[x] = str(project_options[x]).lower()
printErr("Creating new project %s" % pname)
project_options['name'] = pname
project_options['description'] = description
git.projects.create(project_options)
found_project = find_project(options.groupid,pname)
return found_project
# 开始进入游戏主体
try:
token_secret=os.environ['gitlab_user_token_secret']
gitlab_url=os.environ['gitlab_url']
gitlab_user=os.environ['gitlab_user']
ssl_verify=os.environ['ssl_verify']
gitlab_api_version=os.environ['gitlab_api_version']
except KeyError:
printErr("Environment config missing. Do not run this script standalone.")
exit(1)
parser = OptionParser()
# 解析路径中的参数
parser.add_option("--issues",dest="issues",action="store_true",default=False)
parser.add_option("--wall",dest="wall",action="store_true",default=False)
parser.add_option("--merge",dest="merge",action="store_true",default=False)
parser.add_option("--wiki",dest="wiki",action="store_true",default=False)
parser.add_option("--snippets",dest="snippets",action="store_true",default=False)
parser.add_option("--public",dest="public",action="store_true",default=False)
parser.add_option("--create",dest="create",action="store_true",default=False)
parser.add_option("--delete",dest="delete",action="store_true",default=False)
parser.add_option("--desc",dest="desc",metavar="DESC",default=False)
parser.add_option("--groupid",dest="groupid",metavar="GROUPID",default=False)
parser.add_option("--http",dest="http",action="store_true",default=False)
(options,args) = parser.parse_args()
# printErr(str(options)+" ajskldkljajklf")
if len(args) == 0:
printErr("No project name specified. Do not run this script standalone.")
exit(1)
elif len(args) > 1:
printErr("Too many arguments. Do not run this script standalone.")
exit(1)
project_name=args[0]
# 链接到git仓库
if not eval(ssl_verify.capitalize()):
git=gitlab.Gitlab(gitlab_url,token_secret,ssl_verify=False,api_version=gitlab_api_version)
else:
git=gitlab.Gitlab(gitlab_url,token_secret,ssl_verify=True,api_version=gitlab_api_version)
if options.create:
found_group = find_group(options.groupid)
# 打印git组信息
# printErr(str(found_group))
found_project = None
# 查找项目是否存在
found_project= find_project(options.groupid,project_name)
if not found_project:
# 项目不存在的情况,则创建项目
found_project=createproject(project_name)
if not found_project:
printErr("There was a problem creating {group}/{project}. Did you give {user} user Admin rights in gitlab?".format(group=found_group.name,project=project_name,user=gitlab_user))
exit(1)
# 打印项目的结构体
# printErr(str(found_project))
# 下方的print是为了让shell能接收到返回的数据
print(found_group.full_path+","+found_project.name)
if options.http:
print(found_project.http_url_to_repo)
else:
print(found_project.ssh_url_to_repo)
elif options.delete:
# 执行项目的删除
try:
deleted_project=find_project(options.groupid,project_name).delete()
except Exception as e:
printErr(e)
exit(1)
else:
printErr("No --create or --delete option added.")
exit(1)
五、运行命令