Created
May 24, 2013 15:19
-
-
Save cpdean/5644254 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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