Mercurial > hg > config
comparison python/hg-merge.py @ 207:7bad4b7281f2
add a file to merge hg repositories
| author | Jeff Hammel <jhammel@mozilla.com> |
|---|---|
| date | Mon, 13 Feb 2012 16:22:59 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 195:171bd3b71e84 | 207:7bad4b7281f2 |
|---|---|
| 1 #!/usr/bin/env python | |
| 2 | |
| 3 """ | |
| 4 merge mercurial repositories | |
| 5 | |
| 6 Example: | |
| 7 hg-merge.py --master http://hg.mozilla.org/build/talos http://hg.mozilla.org/build/pageloader#talos/pageloader | |
| 8 """ | |
| 9 | |
| 10 import optparse | |
| 11 import os | |
| 12 import shutil | |
| 13 import subprocess | |
| 14 import sys | |
| 15 import tempfile | |
| 16 import urlparse | |
| 17 try: | |
| 18 from subprocess import check_call as call | |
| 19 except: | |
| 20 from subprocess import call | |
| 21 | |
| 22 def url2filename(url): | |
| 23 """gets a filename from a url""" | |
| 24 scheme, netloc, path, query, fragment = urlparse.urlsplit(url) | |
| 25 path = path.rstrip('/') | |
| 26 assert path and '/' in path | |
| 27 return path.split('/')[-1] | |
| 28 | |
| 29 def manifest(hgrepo): | |
| 30 """manifest of a local hg repository""" | |
| 31 process = subprocess.Popen(['hg', 'manifest'], cwd=hgrepo, stdout=subprocess.PIPE) | |
| 32 stdout, stderr = process.communicate() | |
| 33 assert not process.returncode | |
| 34 manifest = stdout.strip().splitlines() | |
| 35 return set(manifest) | |
| 36 | |
| 37 def merge(hgroot, repo, subpath=None): | |
| 38 """merge repo to hgroot at subpath (None for repository root)""" | |
| 39 | |
| 40 # get a manifest of the current repository | |
| 41 root_manifest = manifest(hgroot) | |
| 42 toplevel_contents = os.listdir(hgroot) | |
| 43 | |
| 44 # staging area | |
| 45 tempdir = tempfile.mkdtemp() | |
| 46 fd, tmpfile = tempfile.mkstemp() | |
| 47 os.close(fd) | |
| 48 | |
| 49 exception = None | |
| 50 try: | |
| 51 | |
| 52 # clone the repository to be merged in | |
| 53 call(['hg', 'clone', repo, tempdir]) | |
| 54 | |
| 55 # check manifest for conflicts | |
| 56 repo_manifest = manifest(tempdir) | |
| 57 assert repo_manifest, "Empty repository: %s" % repo | |
| 58 intersection = root_manifest.intersection(repo_manifest) | |
| 59 assert not intersection, "Overlap between %s and %s: %s" % (hgroot, repo, intersection) | |
| 60 | |
| 61 # create a bundle | |
| 62 call(['hg', 'bundle', tmpfile, '--all'], cwd=tempdir) | |
| 63 | |
| 64 # apply the bundle | |
| 65 call(['hg', 'unbundle', tmpfile], cwd=hgroot) | |
| 66 call(['hg', 'merge'], cwd=hgroot) | |
| 67 | |
| 68 if subpath: | |
| 69 # move the new files to their new locations | |
| 70 for item in repo_manifest: | |
| 71 path = os.path.join(subpath, item) | |
| 72 fullpath = os.path.join(hgroot, path) | |
| 73 assert not os.path.exists(fullpath), "%s already exists" % fullpath | |
| 74 subdirectory = os.path.dirname(fullpath) | |
| 75 if not os.path.exists(subdirectory): | |
| 76 os.makedirs(subdirectory) | |
| 77 call(['hg', 'mv', item, path], cwd=hgroot) | |
| 78 call(['hg', 'commit', '-m', 'merge %s to %s' % (repo, subpath)], cwd=hgroot) | |
| 79 else: | |
| 80 call(['hg', 'commit', '-m', 'merge in %s' % repo], cwd=hgroot) | |
| 81 | |
| 82 except Exception, exception: | |
| 83 pass # reraise on cleanup | |
| 84 | |
| 85 # cleanup | |
| 86 shutil.rmtree(tempdir) | |
| 87 os.remove(tmpfile) | |
| 88 if exception is not None: | |
| 89 raise exception | |
| 90 | |
| 91 def main(args=sys.argv[1:]): | |
| 92 | |
| 93 # parse command line options | |
| 94 usage = "%prog [options] http://hg.example.com/repository/path#destination/path [...]" | |
| 95 parser = optparse.OptionParser(usage=usage, description=__doc__) | |
| 96 parser.add_option('-m', '--master', dest='master', | |
| 97 help="use this as the master repository (new clone, otherwise use CWD)") | |
| 98 options, args = parser.parse_args(args) | |
| 99 if not args: | |
| 100 parser.print_help() | |
| 101 parser.exit() | |
| 102 | |
| 103 if options.master: | |
| 104 # clone the new repository | |
| 105 directory = url2filename(options.master) | |
| 106 if os.path.exists(directory): | |
| 107 shutil.rmtree(directory) | |
| 108 call(['hg', 'clone', options.master]) | |
| 109 hgroot = os.path.join(os.getcwd(), directory) | |
| 110 else: | |
| 111 # get the root of the repository | |
| 112 process = subprocess.Popen(['hg', 'root'], stdout=subprocess.PIPE) | |
| 113 hgroot, stderr = process.communicate() | |
| 114 hgroot = hgroot.strip() | |
| 115 if process.returncode: | |
| 116 sys.exit(1) | |
| 117 assert os.path.exists(hgroot) and os.path.isdir(hgroot), "%s not found" % hgroot | |
| 118 | |
| 119 # get the other repos to add | |
| 120 for repo in args: | |
| 121 subpath = None | |
| 122 if '#' in repo: | |
| 123 repo, subpath = repo.rsplit('#', 1) | |
| 124 merge(hgroot, repo, subpath) | |
| 125 | |
| 126 if __name__ == '__main__': | |
| 127 main() |
