Skip to content

Instantly share code, notes, and snippets.

@parrotbait
Created August 25, 2017 16:20
Show Gist options
  • Save parrotbait/c2f39aca41ad12f81206754ca33678ef to your computer and use it in GitHub Desktop.
Save parrotbait/c2f39aca41ad12f81206754ca33678ef to your computer and use it in GitHub Desktop.
Little utility to migrate a git repo from one remote to another (from Bitbucket to Github in this case)
#!/usr/bin/env python
import platform
import os
import sys
import io
import getopt
import subprocess
import shutil
def usage():
print sys.exit('migrate_git.py -s <source_repo> -t <target_repo> -d <dry_run>')
def log(string):
print("--- " + string)
# Flush here so we can see the output in XCode
sys.stdout.flush()
def parse_arguments(argv):
source_repo = None
target_repo = None
dry_run = False
try:
opts, args = getopt.getopt(argv[1:],"s:t:d",["source_repo=","target_repo=","dry_run"])
except getopt.GetoptError:
usage()
for opt, arg in opts:
if opt == ('-h', '--help'):
usage()
elif opt in ("-s", "--source_repo"):
source_repo = arg
elif opt in ("-t", "--target_repo"):
target_repo = arg
elif opt in ("-d", "--dry_run"):
dry_run = True
if source_repo is None:
usage()
return source_repo, target_repo, dry_run
def executeCommand(command, reportExitCode = True, printCommand = False, quiet = False):
out = None
err = None
if quiet:
out = open(os.devnull, 'w')
err = subprocess.STDOUT
if printCommand:
log(">>> " + command)
if reportExitCode:
result = subprocess.call(command, shell = True, stdout=out, stderr=err)
if result != 0:
log ("Error running command with code: " + str(result))
sys.exit(result)
return result
return subprocess.check_output([command], shell = True)
dir_path = os.path.dirname(os.path.realpath(__file__))
test_repo_clone = "test_repo_clone"
test_repo_name = "test_repo.git"
source_repo, target_repo, dry_run = parse_arguments(sys.argv)
if len(source_repo) == 0:
log("Missing source repo arguments")
usage()
sys.exit(1)
if dry_run:
target_repo = os.path.join(dir_path, test_repo_name)
if os.path.exists(target_repo):
shutil.rmtree(target_repo)
target_repo_clone = os.path.join(dir_path, test_repo_clone)
if os.path.exists(target_repo_clone):
shutil.rmtree(target_repo_clone)
executeCommand("git init --bare " + test_repo_name)
else:
if len(target_repo) == 0:
log("Missing target repo arguments")
usage()
sys.exit(1)
source_directory = os.path.splitext(os.path.basename(source_repo))[0]
if os.path.exists(source_directory):
shutil.rmtree(source_directory)
executeCommand("git clone --recursive -j8 " + source_repo + " " + source_directory)
os.chdir(source_directory)
executeCommand("git fetch --tags")
executeCommand("git remote rename origin old")
executeCommand("git remote add origin " + target_repo)
list_of_branches_str = executeCommand("git branch -r", False)
log ("branches " + list_of_branches_str)
list_of_branches = list_of_branches_str.split('\n')
for branch in list_of_branches:
log("Cloning branch: " + branch.strip())
branch_split = branch.strip().split(" ")
if len(branch_split) == 0:
continue;
actual_branch = branch_split[0].strip()
if actual_branch.startswith("old/"):
actual_branch = actual_branch[4:]
else:
log("Invalid branch '%s' - does not start with old/ - must be a local branch" % actual_branch)
continue;
log ("checking out branch '" + actual_branch + "'")
# Checkout and push the branch
executeCommand("git checkout " + actual_branch)
executeCommand("git push -u origin " + actual_branch)
# Push tags
executeCommand("git push origin --tags")
if dry_run:
# Clone our new repo locally
executeCommand("git clone --recursive " + target_repo + " " + target_repo_clone)
@parrotbait
Copy link
Author

A dry run (-d option) creates a bare git repo in the same directory as the script and pushes everything to that. Useful to verify everything is ok before pushing to actual target repo.

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