Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Migrate repositories from Gitolite to GitLab.
#!/usr/bin/php -qC
* @file gitolab.php
* @author Benoit Zohar
* @link
* @last-edited 2015-01-09
* @description Migrate projects from Gitolite to GitLab
* @requisite cURL library for PHP
* @usage 1. Change the value of the private variables of the GitoLab class
* 2. Launch "php gitolab.php" to start the process
* 3. Enter Y or N to import each pre-specified project
error_reporting(E_ALL ^ E_NOTICE);
class GitoLab {
//Gitolite prefix url (you'll need to have you ssh key configured to make this work)
private $gitolite_url = 'ssh://';
//Gitlab API url prefix
private $gitlab_url = '';
//Gitlab ssh access prefoex
private $gitlab_ssh = '';
//Gitlab private token (see Gitlab documentation)
private $gitlab_private_token = '1234567890AZERTY';
private $projects = array(
//there are two ways of defining the repositories you want to import:
//first: use the very nice long keys of an associative array:
//Name of the project in gitolite (name of the repository)
'liteProjectName' => 'example',
//Complete name of the project in GitLab
'labProjectName' => 'ExampleLab',
//Repository name of the project in GitLab
'labProjectPath' => 'exampleLab',
//Namespace in GitLab (must already exist !)
'labNamespace' => 'NameSpaceEx',
//Namespace ID in GitLab (must already exist !) use the _ID_ (check GET to get it)
'labNamespaceId' => '10',
//[Optionnal] : description of the project in gitlab
'labDescription' => 'DescriptionExample'
//second: use the quick'n'dirty "no-key" of a standard array: (the keys are in the same order.)
//Root of temporary directory used to clone repositories
private $root_dir = '';
public function __construct() {
//init variables
if (empty($this->root_dir)) {
//if the temporary directory is not set: use the current directory of the script
$this->root_dir = getcwd();
//start Migration process
try {
catch(Exception $e) {
die("An error occured : ".$e->getMessage()."\n\n");
echo "\n\n";
private function checkRights() {
echo "Checking rights for directory ".$this->root_dir." ...\n";
if (!is_dir($this->root_dir)) throw new Exception("'".$this->root_dir."' is not a directory");
if (!is_writable($this->root_dir)) throw new Exception("'".$this->root_dir."' is not a writable");
private function startMigration() {
echo "Starting migration ... \n";
$autoyes = false; //set to true to automatically accept every repository
foreach($this->projects as $project) {
if (!$autoyes) $r = readline("Migrate project ".($project['liteProjectName'] ? $project['liteProjectName'] : $project[0])." (Y/N) ? ");
//set to auto true if the user uses YYY once.
if ($r === 'YYY') $autoyes = true;
if ($autoyes || empty($r) || strtolower($r) == 'y') {
if (array_key_exists('liteProjectName', $project)) {
} else if (!empty($project[0])) {
private function migrateProject($liteProjectName,$labProjectName,$labProjectPath,$labNamespace,$labNamespaceId,$labDescription = '') {
echo "Migrating project ".$liteProjectName." ...\n";
//create the projet on gitlab
$url = $this->gitlab_url.'api/v3/projects';
$data = array(
'name' => $labProjectName,
'path' => $labProjectPath,
'namespace_id' => $labNamespaceId
if (!empty($labDescription)) {
$data['description'] = $labDescription;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("PRIVATE-TOKEN: ".$this->gitlab_private_token));
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$head = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if(!$head) {
throw new Exception("Could not curl ".$url);
if($httpCode != 201) { //201 for "created" for GitLab API
throw new Exception("Could not curl ".$url." httpcode=".$httpCode);
$path = 'gitolab-tmp/'.$labProjectPath;
//clone the project from gitolite
echo "> 'git clone --mirror ".$this->gitolite_url.$liteProjectName.".git \"".$path."\"'\n";
exec('git clone --mirror '.$this->gitolite_url.$liteProjectName.'.git "'.$path.'"');
//move inside temporary path
//clean gitolite remote
echo "> 'git remote rm origin'\n";
exec('git remote rm origin');
//add gitlab remote
echo "> 'git remote add origin ".$this->gitlab_ssh.":".$labNamespace."/".$labProjectPath.".git'\n";
exec('git remote add origin '.$this->gitlab_ssh.':'.$labNamespace.'/'.$labProjectPath.'.git');
//push all branches to the new remote
echo "> 'git push --all'\n";
exec("git push --all");
//push all tags to the new remote
echo "> 'git push --tags'\n";
exec("git push --tags");
//go back to original dir
private function cleanTemporaryDirectory() {
echo "Cleaning temporary directory ...\n";
//recursively remove directory
private function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir."/".$object) == "dir") $this->rrmdir($dir."/".$object); else unlink($dir."/".$object);
new GitoLab(); // launch migration
Copy link

Ulrar commented May 15, 2015


I'm trying to use that to migrate but I get a "An error occured : Could not curl https://git.local/api/v3/projects httpcode=400"
Do you have any idea why ? May be the API changed since you did that script ?

Thanks a lot

Copy link

Ulrar commented May 15, 2015

Okay my bad, the repository is created by the script, if it exists it does an error 400. Works fine now ! Thanks a lot for that script !

Copy link

tleish commented May 21, 2015

Note: The quick'n'dirty "no-key" example has 5 elements in the array, but should have 6. It is missing the Gitlab Namespace ID ('labNamespaceId' => '10' in the associative array example).

Should look like:

//second: use the quick'n'dirty "no-key" of a standard array: (the keys are in the same order.)

Copy link

grobm commented Dec 15, 2016

trying to use this script and I keep getting:

fatal: The remote end hung up unexpectedly
error: failed to push some refs to 'git@mygitlaburl:adminID/testing.git

Any ideas? cloning works fine... via commandline but "--clone" does not work inside the script.

CentOS 7 is the platform I have it running on?

Copy link

grobm commented Dec 15, 2016

Figured out my issues... it was a firewall issues with the gitlab and gitolite... working now. Great Script!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment