Skip to content

Instantly share code, notes, and snippets.

@cpdean
Created May 24, 2013 15:19
Show Gist options
  • Save cpdean/5644254 to your computer and use it in GitHub Desktop.
Save cpdean/5644254 to your computer and use it in GitHub Desktop.
# combines several repositories into one,
# taking care to combine all trunks naiively,
# and all branches intelligently
import sys
import pysvn
import functools
def indempotent(f):
"""decorator that silently continues if the given
svn function failed.
Hopefully the only error we encounter is when you copy
a directory to a place that already exists.
"""
@functools.wraps(f)
def w(*args, **kwds):
try:
return f(*args, **kwds)
except pysvn.ClientError:
## assume folder already existed
pass
return w
svn = pysvn.Client()
# all actions sent to the server need a comment
# this will be that comment
svn.callback_get_log_message = lambda: (True, "automatic script stuff")
def dir_list(url):
""" returns the named contents of the svn url """
# TODO: refactor other parts that manually re-add the front of
# the url
contents = svn.ls(url)
full_urls = [i["name"] for i in contents]
x = len(url) + 1 # plus the trailing /
return [i[x:] for i in full_urls] # just the filenames
def get_branch_name(branch_url):
assert "branches" in branch_url
name_half = branch_url.split("branches")[1]
return name_half.strip("/")
def fold_into(target_url, branch_url):
""" copies a branch url into a target repo,
creating the branch on target if necessary """
project_name_half = branch_url.split("/branches")[0]
project_name = project_name_half.split("/")[-1]
target_branch_url = target_url + "/branches/" + get_branch_name(branch_url)
create_if_not_exists(target_branch_url)
destination = target_branch_url + "/" + project_name
indempotent(svn.copy)(branch_url, destination)
def fold_trunk(target, source):
assert "trunk" in dir_list(source), source
# get last dir name in url
project_name = source.split("/")[-1]
try:
svn.copy(source + "/trunk", target + "/trunk/" + project_name)
except pysvn.ClientError:
# assuming you re-ran this and this project already exists
svn.remove(target + "/trunk/" + project_name)
svn.copy(source + "/trunk", target + "/trunk/" + project_name)
def get_branches(repo_url):
contents = dir_list(repo_url)
assert "branches" in contents, "{0}".format(contents)
branches = dir_list(repo_url + "/branches")
root = repo_url + "/branches/"
return [root + b for b in branches]
def add_from_trunk(branch_url, project_name):
repo_url = branch_url.split("/branches")[0]
trunk = repo_url + "/trunk"
dest = branch_url + "/" + project_name
source = trunk + "/" + project_name
indempotent(svn.copy)(source, dest)
def create_if_not_exists(url):
try:
svn.mkdir(url, "making this dude if it doesnt exist", make_parents=True)
except pysvn.ClientError:
# assume the only error happens when you run
# mkdir on an existing directory
pass
def svnfold(urls):
""" folds the given svn repositories together.
the first URL should be pointing to the target repository
The remaining urls should be pointing to seperate projects each with a
standard svn layout, containing "trunk" and "branches".
Every "projectName/trunk" is then copied to a corresponding
"target/trunk/projectName".
Every "projectName/branches/branchName" is copied into a corresponding
"target/branches/branchname/projectName". Because not every project
will have a congruent set of branches, svnfold() will fill missing
projects on a branch by copying them over from trunk.
A more faithful implementation would instead copy them from the trunk
revision from which the branch started, but then you run into the question:
From which branch init do we draw from, considering every project branches
from a different svn revision?
We leave this as an exercise to the reader.
"""
target_repo = urls[0]
source_repos = urls[1:]
create_if_not_exists(target_repo + "/trunk")
create_if_not_exists(target_repo + "/branches")
print("folding all trunks")
for s in source_repos:
fold_trunk(target_repo, s)
for s in source_repos:
print("folding over branches for {0}".format(s))
for branch in get_branches(s):
print("\t" + get_branch_name(branch))
fold_into(target_repo, branch)
for branch in get_branches(target_repo):
print(get_branch_name(branch) + " missing projects to copy from trunk:")
for p in dir_list(target_repo + "/trunk"):
if p not in dir_list(branch):
print("\t" + p)
add_from_trunk(branch, p)
def main():
if len(sys.argv) < 2:
print("print help stuff")
if len(sys.argv) == 2:
print("only one argument, assuming that it's a filename")
# file code
with open(sys.argv[1], "rb") as f:
urls = (u.strip() for u in f.readlines())
urls = [u.rstrip("/") for u in urls]
return svnfold(urls)
elif len(sys.argv) > 2:
urls = sys.argv[1:]
urls = [u.rstrip("/") for u in urls]
return svnfold(urls)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment