diff options
author | Junio C Hamano <gitster@pobox.com> | 2013-06-11 13:30:24 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2013-06-11 13:30:24 -0700 |
commit | 8d3b97ae51a0fd77443b3b71a169858e28d12261 (patch) | |
tree | e4c7d028c5d9cfc27d333d1c2d8947826bc3948f /contrib | |
parent | Merge branch 'rj/mingw-cygwin' (diff) | |
parent | remote-hg: add support for --force (diff) | |
download | tgif-8d3b97ae51a0fd77443b3b71a169858e28d12261.tar.xz |
Merge branch 'fc/remote-hg'
* fc/remote-hg: (50 commits)
remote-hg: add support for --force
remote-hg: add support for --dry-run
remote-hg: check if a fetch is needed
remote-hg: trivial cleanup
remote-helpers: improve marks usage
remote-hg: add check_push() helper
remote-hg: add setup_big_push() helper
remote-hg: remove files before modifications
remote-hg: improve lightweight tag author
remote-hg: use remote 'default' not local one
remote-hg: improve branch listing
remote-hg: simplify branch_tip()
remote-hg: check diverged bookmarks
remote-hg: pass around revision refs
remote-hg: implement custom checkheads()
remote-hg: implement custom push()
remote-hg: only update necessary revisions
remote-hg: force remote bookmark push selectively
remote-hg: reorganize bookmark handling
remote-hg: add test for failed double push
...
Diffstat (limited to 'contrib')
-rwxr-xr-x | contrib/remote-helpers/git-remote-bzr | 5 | ||||
-rwxr-xr-x | contrib/remote-helpers/git-remote-hg | 510 | ||||
-rwxr-xr-x | contrib/remote-helpers/test-bzr.sh | 492 | ||||
-rwxr-xr-x | contrib/remote-helpers/test-hg-bidi.sh | 35 | ||||
-rwxr-xr-x | contrib/remote-helpers/test-hg-hg-git.sh | 55 | ||||
-rwxr-xr-x | contrib/remote-helpers/test-hg.sh | 725 |
6 files changed, 1300 insertions, 522 deletions
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index 10300c63d1..f0e5be1d24 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -116,7 +116,10 @@ class Marks: self.last_mark = mark def get_tip(self, branch): - return self.tips.get(branch, None) + try: + return str(self.tips[branch]) + except KeyError: + return None def set_tip(self, branch, tip): self.tips[branch] = tip diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 1dd3d7030e..0194c67fb1 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -12,7 +12,7 @@ # For remote repositories a local clone is stored in # "$GIT_DIR/hg/origin/clone/.hg/". -from mercurial import hg, ui, bookmarks, context, encoding, node, error, extensions +from mercurial import hg, ui, bookmarks, context, encoding, node, error, extensions, discovery, util import re import sys @@ -29,9 +29,6 @@ import urlparse, hashlib # named branches: # git config --global remote-hg.track-branches false # -# If you don't want to force pushes (and thus risk creating new remote heads): -# git config --global remote-hg.force-push false -# # If you want the equivalent of hg's clone/pull--insecure option: # git config --global remote-hg.insecure true # @@ -55,6 +52,8 @@ EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\ AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$') RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)') +VERSION = 2 + def die(msg, *args): sys.stderr.write('ERROR: %s\n' % (msg % args)) sys.exit(1) @@ -72,8 +71,11 @@ def hgmode(mode): m = { '100755': 'x', '120000': 'l' } return m.get(mode, '') -def hghex(node): - return hg.node.hex(node) +def hghex(n): + return node.hex(n) + +def hgbin(n): + return node.bin(n) def hgref(ref): return ref.replace('___', ' ') @@ -81,6 +83,11 @@ def hgref(ref): def gitref(ref): return ref.replace(' ', '___') +def check_version(*check): + if not hg_version: + return True + return hg_version >= check + def get_config(config): cmd = ['git', 'config', '--get', config] process = subprocess.Popen(cmd, stdout=subprocess.PIPE) @@ -98,14 +105,27 @@ def get_config_bool(config, default=False): class Marks: - def __init__(self, path): + def __init__(self, path, repo): self.path = path + self.repo = repo + self.clear() + self.load() + + if self.version < VERSION: + if self.version == 1: + self.upgrade_one() + + # upgraded? + if self.version < VERSION: + self.clear() + self.version = VERSION + + def clear(self): self.tips = {} self.marks = {} self.rev_marks = {} self.last_mark = 0 - - self.load() + self.version = 0 def load(self): if not os.path.exists(self.path): @@ -116,12 +136,21 @@ class Marks: self.tips = tmp['tips'] self.marks = tmp['marks'] self.last_mark = tmp['last-mark'] + self.version = tmp.get('version', 1) for rev, mark in self.marks.iteritems(): - self.rev_marks[mark] = int(rev) + self.rev_marks[mark] = rev + + def upgrade_one(self): + def get_id(rev): + return hghex(self.repo.changelog.node(int(rev))) + self.tips = dict((name, get_id(rev)) for name, rev in self.tips.iteritems()) + self.marks = dict((get_id(rev), mark) for rev, mark in self.marks.iteritems()) + self.rev_marks = dict((mark, get_id(rev)) for mark, rev in self.rev_marks.iteritems()) + self.version = 2 def dict(self): - return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark } + return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark, 'version' : self.version } def store(self): json.dump(self.dict(), open(self.path, 'w')) @@ -130,10 +159,10 @@ class Marks: return str(self.dict()) def from_rev(self, rev): - return self.marks[str(rev)] + return self.marks[rev] def to_rev(self, mark): - return self.rev_marks[mark] + return str(self.rev_marks[mark]) def next_mark(self): self.last_mark += 1 @@ -141,19 +170,19 @@ class Marks: def get_mark(self, rev): self.last_mark += 1 - self.marks[str(rev)] = self.last_mark + self.marks[rev] = self.last_mark return self.last_mark def new_mark(self, rev, mark): - self.marks[str(rev)] = mark + self.marks[rev] = mark self.rev_marks[mark] = rev self.last_mark = mark def is_marked(self, rev): - return str(rev) in self.marks + return rev in self.marks def get_tip(self, branch): - return self.tips.get(branch, 0) + return str(self.tips[branch]) def set_tip(self, branch, tip): self.tips[branch] = tip @@ -261,7 +290,7 @@ def get_filechanges(repo, ctx, parent): removed = set() # load earliest manifest first for caching reasons - prev = repo[parent].manifest().copy() + prev = parent.manifest().copy() cur = ctx.manifest() for fn in cur: @@ -329,6 +358,21 @@ def fixup_user(user): return '%s <%s>' % (name, mail) +def updatebookmarks(repo, peer): + remotemarks = peer.listkeys('bookmarks') + localmarks = repo._bookmarks + + if not remotemarks: + return + + for k, v in remotemarks.iteritems(): + localmarks[k] = hgbin(v) + + if hasattr(localmarks, 'write'): + localmarks.write() + else: + bookmarks.write(repo) + def get_repo(url, alias): global dirname, peer @@ -339,35 +383,41 @@ def get_repo(url, alias): if get_config_bool('remote-hg.insecure'): myui.setconfig('web', 'cacerts', '') - try: - mod = extensions.load(myui, 'hgext.schemes', None) - mod.extsetup(myui) - except ImportError: - pass + extensions.loadall(myui) - if hg.islocal(url): + if hg.islocal(url) and not os.environ.get('GIT_REMOTE_HG_TEST_REMOTE'): repo = hg.repository(myui, url) + if not os.path.exists(dirname): + os.makedirs(dirname) else: - local_path = os.path.join(dirname, 'clone') - if not os.path.exists(local_path): - try: - peer, dstpeer = hg.clone(myui, {}, url, local_path, update=True, pull=True) - except: - die('Repository error') - repo = dstpeer.local() - else: - repo = hg.repository(myui, local_path) + shared_path = os.path.join(gitdir, 'hg') + if not os.path.exists(shared_path): try: - peer = hg.peer(myui, {}, url) + hg.clone(myui, {}, url, shared_path, update=False, pull=True) except: die('Repository error') - repo.pull(peer, heads=None, force=True) + + if not os.path.exists(dirname): + os.makedirs(dirname) + + local_path = os.path.join(dirname, 'clone') + if not os.path.exists(local_path): + hg.share(myui, shared_path, local_path, update=False) + + repo = hg.repository(myui, local_path) + try: + peer = hg.peer(myui, {}, url) + except: + die('Repository error') + repo.pull(peer, heads=None, force=True) + + updatebookmarks(repo, peer) return repo def rev_to_mark(rev): global marks - return marks.from_rev(rev) + return marks.from_rev(rev.hex()) def mark_to_rev(mark): global marks @@ -377,17 +427,24 @@ def export_ref(repo, name, kind, head): global prefix, marks, mode ename = '%s/%s' % (kind, name) - tip = marks.get_tip(ename) + try: + tip = marks.get_tip(ename) + tip = repo[tip].rev() + except: + tip = 0 revs = xrange(tip, head.rev() + 1) - count = 0 - - revs = [rev for rev in revs if not marks.is_marked(rev)] + total = len(revs) for rev in revs: c = repo[rev] - (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node()) + node = c.node() + + if marks.is_marked(c.hex()): + continue + + (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(node) rev_branch = extra['branch'] author = "%s %d %s" % (fixup_user(user), time, gittz(tz)) @@ -397,7 +454,7 @@ def export_ref(repo, name, kind, head): else: committer = author - parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0] + parents = [repo[p] for p in repo.changelog.parentrevs(rev) if p >= 0] if len(parents) == 0: modified = c.manifest().keys() @@ -439,7 +496,7 @@ def export_ref(repo, name, kind, head): modified_final = export_files(c.filectx(f) for f in modified) print "commit %s/%s" % (prefix, ename) - print "mark :%d" % (marks.get_mark(rev)) + print "mark :%d" % (marks.get_mark(c.hex())) print "author %s" % (author) print "committer %s" % (committer) print "data %d" % (len(desc)) @@ -450,22 +507,22 @@ def export_ref(repo, name, kind, head): if len(parents) > 1: print "merge :%s" % (rev_to_mark(parents[1])) - for f in modified_final: - print "M %s :%u %s" % f for f in removed: print "D %s" % (fix_file_path(f)) + for f in modified_final: + print "M %s :%u %s" % f print - count += 1 - if (count % 100 == 0): - print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs)) + progress = (rev - tip) + if (progress % 100 == 0): + print "progress revision %d '%s' (%d/%d)" % (rev, name, progress, total) # make sure the ref is updated print "reset %s/%s" % (prefix, ename) - print "from :%u" % rev_to_mark(rev) + print "from :%u" % rev_to_mark(head) print - marks.set_tip(ename, rev) + marks.set_tip(ename, head.hex()) def export_tag(repo, tag): export_ref(repo, tag, 'tags', repo[hgref(tag)]) @@ -497,15 +554,12 @@ def do_capabilities(parser): if os.path.exists(path): print "*import-marks %s" % path print "*export-marks %s" % path + print "option" print -def branch_tip(repo, branch): - # older versions of mercurial don't have this - if hasattr(repo, 'branchtip'): - return repo.branchtip(branch) - else: - return repo.branchtags()[branch] +def branch_tip(branch): + return branches[branch][-1] def get_branch_tip(repo, branch): global branches @@ -517,27 +571,21 @@ def get_branch_tip(repo, branch): # verify there's only one head if (len(heads) > 1): warn("Branch '%s' has more than one head, consider merging" % branch) - return branch_tip(repo, hgref(branch)) + return branch_tip(hgref(branch)) return heads[0] def list_head(repo, cur): - global g_head, bmarks + global g_head, bmarks, fake_bmark - head = bookmarks.readcurrent(repo) - if head: - node = repo[head] - else: - # fake bookmark from current branch - head = cur - node = repo['.'] - if not node: - node = repo['tip'] - if not node: - return - if head == 'default': - head = 'master' - bmarks[head] = node + if 'default' not in branches: + # empty repo + return + + node = repo[branch_tip('default')] + head = 'master' if not 'master' in bmarks else 'default' + fake_bmark = head + bmarks[head] = node head = gitref(head) print "@refs/heads/%s HEAD" % head @@ -551,15 +599,17 @@ def do_list(parser): bmarks[bmark] = repo[node] cur = repo.dirstate.branch() + orig = peer if peer else repo + + for branch, heads in orig.branchmap().iteritems(): + # only open heads + heads = [h for h in heads if 'close' not in repo.changelog.read(h)[5]] + if heads: + branches[branch] = heads list_head(repo, cur) if track_branches: - for branch in repo.branchmap(): - heads = repo.branchheads(branch) - if len(heads): - branches[branch] = heads - for branch in branches: print "? refs/heads/branches/%s" % gitref(branch) @@ -582,6 +632,7 @@ def do_import(parser): if os.path.exists(path): print "feature import-marks=%s" % path print "feature export-marks=%s" % path + print "feature force" sys.stdout.flush() tmp = encoding.encoding @@ -671,6 +722,11 @@ def parse_commit(parser): die('Unknown file command: %s' % line) files[path] = f + # only export the commits if we are on an internal proxy repo + if dry_run and not peer: + parsed_refs[ref] = None + return + def getfilectx(repo, memctx, f): of = files[f] if 'deleted' in of: @@ -692,14 +748,14 @@ def parse_commit(parser): extra['committer'] = "%s %u %u" % committer if from_mark: - p1 = repo.changelog.node(mark_to_rev(from_mark)) + p1 = mark_to_rev(from_mark) else: - p1 = '\0' * 20 + p1 = '0' * 40 if merge_mark: - p2 = repo.changelog.node(mark_to_rev(merge_mark)) + p2 = mark_to_rev(merge_mark) else: - p2 = '\0' * 20 + p2 = '0' * 40 # # If files changed from any of the parents, hg wants to know, but in git if @@ -735,14 +791,12 @@ def parse_commit(parser): tmp = encoding.encoding encoding.encoding = 'utf-8' - node = repo.commitctx(ctx) + node = hghex(repo.commitctx(ctx)) encoding.encoding = tmp - rev = repo[node].rev() - parsed_refs[ref] = node - marks.new_mark(rev, commit_mark) + marks.new_mark(node, commit_mark) def parse_reset(parser): global parsed_refs @@ -758,8 +812,11 @@ def parse_reset(parser): from_mark = parser.get_mark() parser.next() - node = parser.repo.changelog.node(mark_to_rev(from_mark)) - parsed_refs[ref] = node + try: + rev = mark_to_rev(from_mark) + except KeyError: + rev = None + parsed_refs[ref] = rev def parse_tag(parser): name = parser[1] @@ -775,7 +832,7 @@ def parse_tag(parser): def write_tag(repo, tag, node, msg, author): branch = repo[node].branch() - tip = branch_tip(repo, branch) + tip = branch_tip(branch) tip = repo[tip] def getfilectx(repo, memctx, f): @@ -784,18 +841,28 @@ def write_tag(repo, tag, node, msg, author): data = fctx.data() except error.ManifestLookupError: data = "" - content = data + "%s %s\n" % (hghex(node), tag) + content = data + "%s %s\n" % (node, tag) return context.memfilectx(f, content, False, False, None) p1 = tip.hex() - p2 = '\0' * 20 - if not author: - author = (None, 0, 0) - user, date, tz = author + p2 = '0' * 40 + if author: + user, date, tz = author + date_tz = (date, tz) + else: + cmd = ['git', 'var', 'GIT_COMMITTER_IDENT'] + process = subprocess.Popen(cmd, stdout=subprocess.PIPE) + output, _ = process.communicate() + m = re.match('^.* <.*>', output) + if m: + user = m.group(0) + else: + user = repo.ui.username() + date_tz = None ctx = context.memctx(repo, (p1, p2), msg, ['.hgtags'], getfilectx, - user, (date, tz), {'branch' : branch}) + user, date_tz, {'branch' : branch}) tmp = encoding.encoding encoding.encoding = 'utf-8' @@ -804,12 +871,132 @@ def write_tag(repo, tag, node, msg, author): encoding.encoding = tmp - return tagnode + return (tagnode, branch) + +def checkheads_bmark(repo, ref, ctx): + bmark = ref[len('refs/heads/'):] + if not bmark in bmarks: + # new bmark + return True + + ctx_old = bmarks[bmark] + ctx_new = ctx + if not repo.changelog.descendant(ctx_old.rev(), ctx_new.rev()): + if force_push: + print "ok %s forced update" % ref + else: + print "error %s non-fast forward" % ref + return False + + return True + +def checkheads(repo, remote, p_revs): + + remotemap = remote.branchmap() + if not remotemap: + # empty repo + return True + + new = {} + ret = True + + for node, ref in p_revs.iteritems(): + ctx = repo[node] + branch = ctx.branch() + if not branch in remotemap: + # new branch + continue + if not ref.startswith('refs/heads/branches'): + if ref.startswith('refs/heads/'): + if not checkheads_bmark(repo, ref, ctx): + ret = False + + # only check branches + continue + new.setdefault(branch, []).append(ctx.rev()) + + for branch, heads in new.iteritems(): + old = [repo.changelog.rev(x) for x in remotemap[branch]] + for rev in heads: + if check_version(2, 3): + ancestors = repo.changelog.ancestors([rev], stoprev=min(old)) + else: + ancestors = repo.changelog.ancestors(rev) + found = False + + for x in old: + if x in ancestors: + found = True + break + + if found: + continue + + node = repo.changelog.node(rev) + ref = p_revs[node] + if force_push: + print "ok %s forced update" % ref + else: + print "error %s non-fast forward" % ref + ret = False + + return ret + +def push_unsafe(repo, remote, parsed_refs, p_revs): + + force = force_push + + fci = discovery.findcommonincoming + commoninc = fci(repo, remote, force=force) + common, _, remoteheads = commoninc + + if not checkheads(repo, remote, p_revs): + return None + + cg = repo.getbundle('push', heads=list(p_revs), common=common) + + unbundle = remote.capable('unbundle') + if unbundle: + if force: + remoteheads = ['force'] + return remote.unbundle(cg, remoteheads, 'push') + else: + return remote.addchangegroup(cg, 'push', repo.url()) + +def push(repo, remote, parsed_refs, p_revs): + if hasattr(remote, 'canpush') and not remote.canpush(): + print "error cannot push" + + if not p_revs: + # nothing to push + return + + lock = None + unbundle = remote.capable('unbundle') + if not unbundle: + lock = remote.lock() + try: + ret = push_unsafe(repo, remote, parsed_refs, p_revs) + finally: + if lock is not None: + lock.release() + + return ret + +def check_tip(ref, kind, name, heads): + try: + ename = '%s/%s' % (kind, name) + tip = marks.get_tip(ename) + except KeyError: + return True + else: + return tip in heads def do_export(parser): global parsed_refs, bmarks, peer p_bmarks = [] + p_revs = {} parser.next() @@ -827,72 +1014,114 @@ def do_export(parser): else: die('unhandled export command: %s' % line) + need_fetch = False + for ref, node in parsed_refs.iteritems(): + bnode = hgbin(node) if node else None if ref.startswith('refs/heads/branches'): branch = ref[len('refs/heads/branches/'):] - if branch in branches and node in branches[branch]: + if branch in branches and bnode in branches[branch]: # up to date continue + + if peer: + remotemap = peer.branchmap() + if remotemap and branch in remotemap: + heads = [hghex(e) for e in remotemap[branch]] + if not check_tip(ref, 'branches', branch, heads): + print "error %s fetch first" % ref + need_fetch = True + continue + + p_revs[bnode] = ref print "ok %s" % ref elif ref.startswith('refs/heads/'): bmark = ref[len('refs/heads/'):] - p_bmarks.append((bmark, node)) - continue + new = node + old = bmarks[bmark].hex() if bmark in bmarks else '' + + if old == new: + continue + + print "ok %s" % ref + if bmark != fake_bmark and \ + not (bmark == 'master' and bmark not in parser.repo._bookmarks): + p_bmarks.append((ref, bmark, old, new)) + + if peer: + remote_old = peer.listkeys('bookmarks').get(bmark) + if remote_old: + if not check_tip(ref, 'bookmarks', bmark, remote_old): + print "error %s fetch first" % ref + need_fetch = True + continue + + p_revs[bnode] = ref elif ref.startswith('refs/tags/'): + if dry_run: + print "ok %s" % ref + continue tag = ref[len('refs/tags/'):] tag = hgref(tag) author, msg = parsed_tags.get(tag, (None, None)) if mode == 'git': if not msg: - msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6])); - write_tag(parser.repo, tag, node, msg, author) + msg = 'Added tag %s for changeset %s' % (tag, node[:12]); + tagnode, branch = write_tag(parser.repo, tag, node, msg, author) + p_revs[tagnode] = 'refs/heads/branches/' + gitref(branch) else: fp = parser.repo.opener('localtags', 'a') - fp.write('%s %s\n' % (hghex(node), tag)) + fp.write('%s %s\n' % (node, tag)) fp.close() + p_revs[bnode] = ref print "ok %s" % ref else: # transport-helper/fast-export bugs continue - if peer: - parser.repo.push(peer, force=force_push, newbranch=True) - - # handle bookmarks - for bmark, node in p_bmarks: - ref = 'refs/heads/' + bmark - new = hghex(node) - - if bmark in bmarks: - old = bmarks[bmark].hex() - else: - old = '' + if need_fetch: + print + return - if old == new: - continue + if dry_run: + if peer and not force_push: + checkheads(parser.repo, peer, p_revs) + print + return - if bmark == 'master' and 'master' not in parser.repo._bookmarks: - # fake bookmark - print "ok %s" % ref - continue - elif bookmarks.pushbookmark(parser.repo, bmark, old, new): - # updated locally - pass - else: - print "error %s" % ref - continue + if peer: + if not push(parser.repo, peer, parsed_refs, p_revs): + # do not update bookmarks + print + return - if peer: - rb = peer.listkeys('bookmarks') - old = rb.get(bmark, '') + # update remote bookmarks + remote_bmarks = peer.listkeys('bookmarks') + for ref, bmark, old, new in p_bmarks: + if force_push: + old = remote_bmarks.get(bmark, '') if not peer.pushkey('bookmarks', bmark, old, new): print "error %s" % ref - continue - - print "ok %s" % ref + else: + # update local bookmarks + for ref, bmark, old, new in p_bmarks: + if not bookmarks.pushbookmark(parser.repo, bmark, old, new): + print "error %s" % ref print +def do_option(parser): + global dry_run, force_push + _, key, value = parser.line.split(' ') + if key == 'dry-run': + dry_run = (value == 'true') + print 'ok' + elif key == 'force': + force_push = (value == 'true') + print 'ok' + else: + print 'unsupported' + def fix_path(alias, repo, orig_url): url = urlparse.urlparse(orig_url, 'file') if url.scheme != 'file' or os.path.isabs(url.path): @@ -902,12 +1131,14 @@ def fix_path(alias, repo, orig_url): subprocess.call(cmd) def main(args): - global prefix, dirname, branches, bmarks + global prefix, gitdir, dirname, branches, bmarks global marks, blob_marks, parsed_refs global peer, mode, bad_mail, bad_name global track_branches, force_push, is_tmp global parsed_tags global filenodes + global fake_bmark, hg_version + global dry_run alias = args[1] url = args[2] @@ -915,7 +1146,7 @@ def main(args): hg_git_compat = get_config_bool('remote-hg.hg-git-compat') track_branches = get_config_bool('remote-hg.track-branches', True) - force_push = get_config_bool('remote-hg.force-push') + force_push = False if hg_git_compat: mode = 'hg' @@ -941,6 +1172,12 @@ def main(args): marks = None parsed_tags = {} filenodes = {} + fake_bmark = None + try: + hg_version = tuple(int(e) for e in util.version().split('.')) + except: + hg_version = None + dry_run = False repo = get_repo(url, alias) prefix = 'refs/hg/%s' % alias @@ -948,11 +1185,8 @@ def main(args): if not is_tmp: fix_path(alias, peer or repo, url) - if not os.path.exists(dirname): - os.makedirs(dirname) - marks_path = os.path.join(dirname, 'marks-hg') - marks = Marks(marks_path) + marks = Marks(marks_path, repo) if sys.platform == 'win32': import msvcrt @@ -968,6 +1202,8 @@ def main(args): do_import(parser) elif parser.check('export'): do_export(parser) + elif parser.check('option'): + do_option(parser) else: die('unhandled command: %s' % line) sys.stdout.flush() diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh index 5dfa070b64..dce281f911 100755 --- a/contrib/remote-helpers/test-bzr.sh +++ b/contrib/remote-helpers/test-bzr.sh @@ -12,86 +12,90 @@ if ! test_have_prereq PYTHON; then test_done fi -if ! "$PYTHON_PATH" -c 'import bzrlib'; then +if ! python -c 'import bzrlib'; then skip_all='skipping remote-bzr tests; bzr not available' test_done fi check () { - (cd $1 && - git log --format='%s' -1 && - git symbolic-ref HEAD) > actual && - (echo $2 && - echo "refs/heads/$3") > expected && + echo $3 > expected && + git --git-dir=$1/.git log --format='%s' -1 $2 > actual test_cmp expected actual } bzr whoami "A U Thor <author@example.com>" test_expect_success 'cloning' ' - (bzr init bzrrepo && - cd bzrrepo && - echo one > content && - bzr add content && - bzr commit -m one - ) && - - git clone "bzr::$PWD/bzrrepo" gitrepo && - check gitrepo one master + ( + bzr init bzrrepo && + cd bzrrepo && + echo one > content && + bzr add content && + bzr commit -m one + ) && + + git clone "bzr::bzrrepo" gitrepo && + check gitrepo HEAD one ' test_expect_success 'pulling' ' - (cd bzrrepo && - echo two > content && - bzr commit -m two - ) && + ( + cd bzrrepo && + echo two > content && + bzr commit -m two + ) && - (cd gitrepo && git pull) && + (cd gitrepo && git pull) && - check gitrepo two master + check gitrepo HEAD two ' test_expect_success 'pushing' ' - (cd gitrepo && - echo three > content && - git commit -a -m three && - git push - ) && - - echo three > expected && - cat bzrrepo/content > actual && - test_cmp expected actual + ( + cd gitrepo && + echo three > content && + git commit -a -m three && + git push + ) && + + echo three > expected && + cat bzrrepo/content > actual && + test_cmp expected actual ' test_expect_success 'roundtrip' ' - (cd gitrepo && - git pull && - git log --format="%s" -1 origin/master > actual) && - echo three > expected && - test_cmp expected actual && + ( + cd gitrepo && + git pull && + git log --format="%s" -1 origin/master > actual + ) && + echo three > expected && + test_cmp expected actual && - (cd gitrepo && git push && git pull) && + (cd gitrepo && git push && git pull) && - (cd bzrrepo && - echo four > content && - bzr commit -m four - ) && + ( + cd bzrrepo && + echo four > content && + bzr commit -m four + ) && - (cd gitrepo && git pull && git push) && + (cd gitrepo && git pull && git push) && - check gitrepo four master && + check gitrepo HEAD four && - (cd gitrepo && - echo five > content && - git commit -a -m five && - git push && git pull - ) && + ( + cd gitrepo && + echo five > content && + git commit -a -m five && + git push && git pull + ) && - (cd bzrrepo && bzr revert) && + (cd bzrrepo && bzr revert) && - echo five > expected && - cat bzrrepo/content > actual && - test_cmp expected actual + echo five > expected && + cat bzrrepo/content > actual && + test_cmp expected actual ' cat > expected <<EOF @@ -101,29 +105,35 @@ cat > expected <<EOF EOF test_expect_success 'special modes' ' - (cd bzrrepo && - echo exec > executable - chmod +x executable && - bzr add executable - bzr commit -m exec && - ln -s content link - bzr add link - bzr commit -m link && - mkdir dir && - bzr add dir && - bzr commit -m dir) && - - (cd gitrepo && - git pull - git ls-tree HEAD > ../actual) && - - test_cmp expected actual && - - (cd gitrepo && - git cat-file -p HEAD:link > ../actual) && - - printf content > expected && - test_cmp expected actual + ( + cd bzrrepo && + echo exec > executable + chmod +x executable && + bzr add executable + bzr commit -m exec && + ln -s content link + bzr add link + bzr commit -m link && + mkdir dir && + bzr add dir && + bzr commit -m dir + ) && + + ( + cd gitrepo && + git pull + git ls-tree HEAD > ../actual + ) && + + test_cmp expected actual && + + ( + cd gitrepo && + git cat-file -p HEAD:link > ../actual + ) && + + printf content > expected && + test_cmp expected actual ' cat > expected <<EOF @@ -134,134 +144,145 @@ cat > expected <<EOF EOF test_expect_success 'moving directory' ' - (cd bzrrepo && - mkdir movedir && - echo one > movedir/one && - echo two > movedir/two && - bzr add movedir && - bzr commit -m movedir && - bzr mv movedir movedir-new && - bzr commit -m movedir-new) && - - (cd gitrepo && - git pull && - git ls-tree HEAD > ../actual) && - - test_cmp expected actual + ( + cd bzrrepo && + mkdir movedir && + echo one > movedir/one && + echo two > movedir/two && + bzr add movedir && + bzr commit -m movedir && + bzr mv movedir movedir-new && + bzr commit -m movedir-new + ) && + + ( + cd gitrepo && + git pull && + git ls-tree HEAD > ../actual + ) && + + test_cmp expected actual ' test_expect_success 'different authors' ' - (cd bzrrepo && - echo john >> content && - bzr commit -m john \ - --author "Jane Rey <jrey@example.com>" \ - --author "John Doe <jdoe@example.com>") && - - (cd gitrepo && - git pull && - git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual) && - - echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected && - test_cmp expected actual + ( + cd bzrrepo && + echo john >> content && + bzr commit -m john \ + --author "Jane Rey <jrey@example.com>" \ + --author "John Doe <jdoe@example.com>" + ) && + + ( + cd gitrepo && + git pull && + git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual + ) && + + echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected && + test_cmp expected actual ' +# cleanup previous stuff +rm -rf bzrrepo gitrepo + test_expect_success 'fetch utf-8 filenames' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" && - - LC_ALL=en_US.UTF-8 - export LC_ALL - ( - bzr init bzrrepo && - cd bzrrepo && - - echo test >> "ærø" && - bzr add "ærø" && - echo test >> "ø~?" && - bzr add "ø~?" && - bzr commit -m add-utf-8 && - echo test >> "ærø" && - bzr commit -m test-utf-8 && - bzr rm "ø~?" && - bzr mv "ærø" "ø~?" && - bzr commit -m bzr-mv-utf-8 - ) && - - ( - git clone "bzr::$PWD/bzrrepo" gitrepo && - cd gitrepo && - git -c core.quotepath=false ls-files > ../actual - ) && - echo "ø~?" > expected && - test_cmp expected actual + test_when_finished "rm -rf bzrrepo gitrepo && LC_ALL=C" && + + LC_ALL=en_US.UTF-8 + export LC_ALL + + ( + bzr init bzrrepo && + cd bzrrepo && + + echo test >> "ærø" && + bzr add "ærø" && + echo test >> "ø~?" && + bzr add "ø~?" && + bzr commit -m add-utf-8 && + echo test >> "ærø" && + bzr commit -m test-utf-8 && + bzr rm "ø~?" && + bzr mv "ærø" "ø~?" && + bzr commit -m bzr-mv-utf-8 + ) && + + ( + git clone "bzr::bzrrepo" gitrepo && + cd gitrepo && + git -c core.quotepath=false ls-files > ../actual + ) && + echo "ø~?" > expected && + test_cmp expected actual ' test_expect_success 'push utf-8 filenames' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" && + test_when_finished "rm -rf bzrrepo gitrepo && LC_ALL=C" && + + mkdir -p tmp && cd tmp && - LC_ALL=en_US.UTF-8 - export LC_ALL + LC_ALL=en_US.UTF-8 + export LC_ALL - ( - bzr init bzrrepo && - cd bzrrepo && + ( + bzr init bzrrepo && + cd bzrrepo && - echo one >> content && - bzr add content && - bzr commit -m one - ) && + echo one >> content && + bzr add content && + bzr commit -m one + ) && - ( - git clone "bzr::$PWD/bzrrepo" gitrepo && - cd gitrepo && + ( + git clone "bzr::bzrrepo" gitrepo && + cd gitrepo && - echo test >> "ærø" && - git add "ærø" && - git commit -m utf-8 && + echo test >> "ærø" && + git add "ærø" && + git commit -m utf-8 && - git push - ) && + git push + ) && - (cd bzrrepo && bzr ls > ../actual) && - printf "content\nærø\n" > expected && - test_cmp expected actual + (cd bzrrepo && bzr ls > ../actual) && + printf "content\nærø\n" > expected && + test_cmp expected actual ' test_expect_success 'pushing a merge' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && - - ( - bzr init bzrrepo && - cd bzrrepo && - echo one > content && - bzr add content && - bzr commit -m one - ) && - - git clone "bzr::$PWD/bzrrepo" gitrepo && - - ( - cd bzrrepo && - echo two > content && - bzr commit -m two - ) && - - ( - cd gitrepo && - echo three > content && - git commit -a -m three && - git fetch && - git merge origin/master || true && - echo three > content && - git commit -a --no-edit && - git push - ) && - - echo three > expected && - cat bzrrepo/content > actual && - test_cmp expected actual + test_when_finished "rm -rf bzrrepo gitrepo" && + + ( + bzr init bzrrepo && + cd bzrrepo && + echo one > content && + bzr add content && + bzr commit -m one + ) && + + git clone "bzr::bzrrepo" gitrepo && + + ( + cd bzrrepo && + echo two > content && + bzr commit -m two + ) && + + ( + cd gitrepo && + echo three > content && + git commit -a -m three && + git fetch && + git merge origin/master || true && + echo three > content && + git commit -a --no-edit && + git push + ) && + + echo three > expected && + cat bzrrepo/content > actual && + test_cmp expected actual ' cat > expected <<EOF @@ -271,71 +292,70 @@ origin/trunk EOF test_expect_success 'proper bzr repo' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && - - bzr init-repo bzrrepo && - - bzr init bzrrepo/trunk && - ( - cd bzrrepo/trunk && - echo one >> content && - bzr add content && - bzr commit -m one - ) && - - bzr branch bzrrepo/trunk bzrrepo/branch && - ( - cd bzrrepo/branch && - echo two >> content && - bzr commit -m one - ) && - - git clone "bzr::$PWD/bzrrepo" gitrepo && - ( - cd gitrepo && - git for-each-ref --format "%(refname:short)" refs/remotes/origin > ../actual - ) && - - test_cmp ../expected actual + test_when_finished "rm -rf bzrrepo gitrepo" && + + bzr init-repo bzrrepo && + + ( + bzr init bzrrepo/trunk && + cd bzrrepo/trunk && + echo one >> content && + bzr add content && + bzr commit -m one + ) && + + ( + bzr branch bzrrepo/trunk bzrrepo/branch && + cd bzrrepo/branch && + echo two >> content && + bzr commit -m one + ) && + + ( + git clone "bzr::bzrrepo" gitrepo && + cd gitrepo && + git for-each-ref --format "%(refname:short)" refs/remotes/origin > ../actual + ) && + + test_cmp expected actual ' test_expect_success 'strip' ' - # Do not imitate this style; always chdir inside a subshell instead - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf bzrrepo gitrepo" && - ( - bzr init bzrrepo && - cd bzrrepo && + ( + bzr init bzrrepo && + cd bzrrepo && - echo one >> content && - bzr add content && - bzr commit -m one && + echo one >> content && + bzr add content && + bzr commit -m one && - echo two >> content && - bzr commit -m two - ) && + echo two >> content && + bzr commit -m two + ) && - git clone "bzr::$PWD/bzrrepo" gitrepo && + git clone "bzr::bzrrepo" gitrepo && - ( - cd bzrrepo && - bzr uncommit --force && + ( + cd bzrrepo && + bzr uncommit --force && - echo three >> content && - bzr commit -m three && + echo three >> content && + bzr commit -m three && - echo four >> content && - bzr commit -m four && - bzr log --line | sed -e "s/^[0-9][0-9]*: //" > ../expected - ) && + echo four >> content && + bzr commit -m four && + bzr log --line | sed -e "s/^[0-9][0-9]*: //" > ../expected + ) && - (cd gitrepo && - git fetch && - git log --format="%an %ad %s" --date=short origin/master > ../actual) && + ( + cd gitrepo && + git fetch && + git log --format="%an %ad %s" --date=short origin/master > ../actual + ) && - test_cmp expected actual + test_cmp expected actual ' test_done diff --git a/contrib/remote-helpers/test-hg-bidi.sh b/contrib/remote-helpers/test-hg-bidi.sh index f569697734..f83d67d74f 100755 --- a/contrib/remote-helpers/test-hg-bidi.sh +++ b/contrib/remote-helpers/test-hg-bidi.sh @@ -15,23 +15,22 @@ if ! test_have_prereq PYTHON; then test_done fi -if ! "$PYTHON_PATH" -c 'import mercurial'; then +if ! python -c 'import mercurial'; then skip_all='skipping remote-hg tests; mercurial not available' test_done fi # clone to a git repo git_clone () { - git clone -q "hg::$PWD/$1" $2 + git clone -q "hg::$1" $2 } # clone to an hg repo hg_clone () { ( hg init $2 && - hg -R $2 bookmark -i master && cd $1 && - git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' + git push -q "hg::../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' ) && (cd $2 && hg -q update) @@ -41,17 +40,15 @@ hg_clone () { hg_push () { ( cd $2 - old=$(git symbolic-ref --short HEAD) git checkout -q -b tmp && - git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' && - git checkout -q $old && + git fetch -q "hg::../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' && + git checkout -q @{-1} && git branch -q -D tmp 2> /dev/null || true ) } hg_log () { - hg -R $1 log --graph --debug >log && - grep -v 'tag: *default/' log + hg -R $1 log --graph --debug } setup () { @@ -67,6 +64,7 @@ setup () { echo "graphlog =" ) >> "$HOME"/.hgrc && git config --global remote-hg.hg-git-compat true + git config --global remote-hg.track-branches true HGEDITOR=/usr/bin/true GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" @@ -77,8 +75,7 @@ setup () { setup test_expect_success 'encoding' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -115,8 +112,7 @@ test_expect_success 'encoding' ' ' test_expect_success 'file removal' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -148,8 +144,7 @@ test_expect_success 'file removal' ' ' test_expect_success 'git tags' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -177,8 +172,7 @@ test_expect_success 'git tags' ' ' test_expect_success 'hg branch' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -194,7 +188,7 @@ test_expect_success 'hg branch' ' hg_clone gitrepo hgrepo && cd hgrepo && - hg -q co master && + hg -q co default && hg mv alpha beta && hg -q commit -m "rename alpha to beta" && hg branch gamma | grep -v "permanent and global" && @@ -214,8 +208,7 @@ test_expect_success 'hg branch' ' ' test_expect_success 'hg tags' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -231,7 +224,7 @@ test_expect_success 'hg tags' ' hg_clone gitrepo hgrepo && cd hgrepo && - hg co master && + hg co default && hg tag alpha ) && diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh index 7f579c8436..2219284382 100755 --- a/contrib/remote-helpers/test-hg-hg-git.sh +++ b/contrib/remote-helpers/test-hg-hg-git.sh @@ -15,19 +15,20 @@ if ! test_have_prereq PYTHON; then test_done fi -if ! "$PYTHON_PATH" -c 'import mercurial'; then +if ! python -c 'import mercurial'; then skip_all='skipping remote-hg tests; mercurial not available' test_done fi -if ! "$PYTHON_PATH" -c 'import hggit'; then +if ! python -c 'import hggit'; then skip_all='skipping remote-hg tests; hg-git not available' test_done fi # clone to a git repo with git git_clone_git () { - git clone -q "hg::$PWD/$1" $2 + git clone -q "hg::$1" $2 && + (cd $2 && git checkout master && git branch -D default) } # clone to an hg repo with git @@ -36,7 +37,7 @@ hg_clone_git () { hg init $2 && hg -R $2 bookmark -i master && cd $1 && - git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' + git push -q "hg::../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' ) && (cd $2 && hg -q update) @@ -61,10 +62,10 @@ hg_clone_hg () { hg_push_git () { ( cd $2 - old=$(git symbolic-ref --short HEAD) git checkout -q -b tmp && - git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' && - git checkout -q $old && + git fetch -q "hg::../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' && + git branch -D default && + git checkout -q @{-1} && git branch -q -D tmp 2> /dev/null || true ) } @@ -104,18 +105,18 @@ setup () { git config --global remote-hg.hg-git-compat true git config --global remote-hg.track-branches false - HGEDITOR=/usr/bin/true + HGEDITOR=true + HGMERGE=true GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" - export HGEDITOR GIT_AUTHOR_DATE GIT_COMMITTER_DATE + export HGEDITOR HGMERGE GIT_AUTHOR_DATE GIT_COMMITTER_DATE } setup test_expect_success 'executable bit' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -150,8 +151,7 @@ test_expect_success 'executable bit' ' ' test_expect_success 'symlink' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -181,8 +181,7 @@ test_expect_success 'symlink' ' ' test_expect_success 'merge conflict 1' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( hg init hgrepo1 && @@ -198,7 +197,7 @@ test_expect_success 'merge conflict 1' ' echo C > afile && hg ci -m "A->C" && - hg merge -r1 || true && + hg merge -r1 && echo C > afile && hg resolve -m afile && hg ci -m "merge to C" @@ -216,8 +215,7 @@ test_expect_success 'merge conflict 1' ' ' test_expect_success 'merge conflict 2' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( hg init hgrepo1 && @@ -251,8 +249,7 @@ test_expect_success 'merge conflict 2' ' ' test_expect_success 'converged merge' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( hg init hgrepo1 && @@ -287,8 +284,7 @@ test_expect_success 'converged merge' ' ' test_expect_success 'encoding' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -327,8 +323,7 @@ test_expect_success 'encoding' ' ' test_expect_success 'file removal' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -367,8 +362,7 @@ test_expect_success 'file removal' ' ' test_expect_success 'git tags' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && ( git init -q gitrepo && @@ -394,8 +388,7 @@ test_expect_success 'git tags' ' ' test_expect_success 'hg author' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && for x in hg git; do ( @@ -461,8 +454,7 @@ test_expect_success 'hg author' ' ' test_expect_success 'hg branch' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && for x in hg git; do ( @@ -498,8 +490,7 @@ test_expect_success 'hg branch' ' ' test_expect_success 'hg tags' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && + test_when_finished "rm -rf gitrepo* hgrepo*" && for x in hg git; do ( diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh index 8de2aa7fec..f7ce8aa853 100755 --- a/contrib/remote-helpers/test-hg.sh +++ b/contrib/remote-helpers/test-hg.sh @@ -15,143 +15,678 @@ if ! test_have_prereq PYTHON; then test_done fi -if ! "$PYTHON_PATH" -c 'import mercurial'; then +if ! python -c 'import mercurial'; then skip_all='skipping remote-hg tests; mercurial not available' test_done fi check () { - (cd $1 && - git log --format='%s' -1 && - git symbolic-ref HEAD) > actual && - (echo $2 && - echo "refs/heads/$3") > expected && + echo $3 > expected && + git --git-dir=$1/.git log --format='%s' -1 $2 > actual test_cmp expected actual } +check_branch () { + if [ -n "$3" ]; then + echo $3 > expected && + hg -R $1 log -r $2 --template '{desc}\n' > actual && + test_cmp expected actual + else + hg -R $1 branches > out && + ! grep $2 out + fi +} + +check_bookmark () { + if [ -n "$3" ]; then + echo $3 > expected && + hg -R $1 log -r "bookmark('$2')" --template '{desc}\n' > actual && + test_cmp expected actual + else + hg -R $1 bookmarks > out && + ! grep $2 out + fi +} + +check_push () { + local expected_ret=$1 ret=0 ref_ret=0 IFS=':' + + shift + git push origin "$@" 2> error + ret=$? + cat error + + while read branch kind + do + case "$kind" in + 'new') + grep "^ \* \[new branch\] *${branch} -> ${branch}$" error || ref_ret=1 + ;; + 'non-fast-forward') + grep "^ ! \[rejected\] *${branch} -> ${branch} (non-fast-forward)$" error || ref_ret=1 + ;; + 'fetch-first') + grep "^ ! \[rejected\] *${branch} -> ${branch} (fetch first)$" error || ref_ret=1 + ;; + 'forced-update') + grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *${branch} -> ${branch} (forced update)$" error || ref_ret=1 + ;; + '') + grep "^ [a-f0-9]*\.\.[a-f0-9]* *${branch} -> ${branch}$" error || ref_ret=1 + ;; + esac + let 'ref_ret' && echo "match for '$branch' failed" && break + done + + if let 'expected_ret != ret || ref_ret' + then + return 1 + fi + + return 0 +} + setup () { ( echo "[ui]" echo "username = H G Wells <wells@example.com>" - ) >> "$HOME"/.hgrc + echo "[extensions]" + echo "mq =" + ) >> "$HOME"/.hgrc && + + GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" && + GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" && + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE } setup test_expect_success 'cloning' ' - test_when_finished "rm -rf gitrepo*" && - - ( - hg init hgrepo && - cd hgrepo && - echo zero > content && - hg add content && - hg commit -m zero - ) && + test_when_finished "rm -rf gitrepo*" && - git clone "hg::$PWD/hgrepo" gitrepo && - check gitrepo zero master + ( + hg init hgrepo && + cd hgrepo && + echo zero > content && + hg add content && + hg commit -m zero + ) && + + git clone "hg::hgrepo" gitrepo && + check gitrepo HEAD zero ' test_expect_success 'cloning with branches' ' - test_when_finished "rm -rf gitrepo*" && + test_when_finished "rm -rf gitrepo*" && + + ( + cd hgrepo && + hg branch next && + echo next > content && + hg commit -m next + ) && + + git clone "hg::hgrepo" gitrepo && + check gitrepo origin/branches/next next +' + +test_expect_success 'cloning with bookmarks' ' + test_when_finished "rm -rf gitrepo*" && + + ( + cd hgrepo && + hg checkout default && + hg bookmark feature-a && + echo feature-a > content && + hg commit -m feature-a + ) && + + git clone "hg::hgrepo" gitrepo && + check gitrepo origin/feature-a feature-a +' + +test_expect_success 'update bookmark' ' + test_when_finished "rm -rf gitrepo*" && - ( - cd hgrepo && - hg branch next && - echo next > content && - hg commit -m next - ) && + ( + cd hgrepo && + hg bookmark devel + ) && - git clone "hg::$PWD/hgrepo" gitrepo && - check gitrepo next next && + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + git checkout --quiet devel && + echo devel > content && + git commit -a -m devel && + git push --quiet + ) && + + check_bookmark hgrepo devel devel +' - (cd hgrepo && hg checkout default) && +test_expect_success 'new bookmark' ' + test_when_finished "rm -rf gitrepo*" && - git clone "hg::$PWD/hgrepo" gitrepo2 && - check gitrepo2 zero master + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + git checkout --quiet -b feature-b && + echo feature-b > content && + git commit -a -m feature-b && + git push --quiet origin feature-b + ) && + + check_bookmark hgrepo feature-b feature-b ' -test_expect_success 'cloning with bookmarks' ' - test_when_finished "rm -rf gitrepo*" && +# cleanup previous stuff +rm -rf hgrepo + +author_test () { + echo $1 >> content && + hg commit -u "$2" -m "add $1" && + echo "$3" >> ../expected +} - ( - cd hgrepo && - hg bookmark feature-a && - echo feature-a > content && - hg commit -m feature-a - ) && +test_expect_success 'authors' ' + test_when_finished "rm -rf hgrepo gitrepo" && + + ( + hg init hgrepo && + cd hgrepo && + + touch content && + hg add content && + + > ../expected && + author_test alpha "" "H G Wells <wells@example.com>" && + author_test beta "test" "test <unknown>" && + author_test beta "test <test@example.com> (comment)" "test <test@example.com>" && + author_test gamma "<test@example.com>" "Unknown <test@example.com>" && + author_test delta "name<test@example.com>" "name <test@example.com>" && + author_test epsilon "name <test@example.com" "name <test@example.com>" && + author_test zeta " test " "test <unknown>" && + author_test eta "test < test@example.com >" "test <test@example.com>" && + author_test theta "test >test@example.com>" "test <test@example.com>" && + author_test iota "test < test <at> example <dot> com>" "test <unknown>" && + author_test kappa "test@example.com" "Unknown <test@example.com>" + ) && + + git clone "hg::hgrepo" gitrepo && + git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" > actual && - git clone "hg::$PWD/hgrepo" gitrepo && - check gitrepo feature-a feature-a + test_cmp expected actual ' -test_expect_success 'cloning with detached head' ' - test_when_finished "rm -rf gitrepo*" && +test_expect_success 'strip' ' + test_when_finished "rm -rf hgrepo gitrepo" && + + ( + hg init hgrepo && + cd hgrepo && + + echo one >> content && + hg add content && + hg commit -m one && + + echo two >> content && + hg commit -m two + ) && + + git clone "hg::hgrepo" gitrepo && + + ( + cd hgrepo && + hg strip 1 && - ( - cd hgrepo && - hg update -r 0 - ) && + echo three >> content && + hg commit -m three && - git clone "hg::$PWD/hgrepo" gitrepo && - check gitrepo zero master + echo four >> content && + hg commit -m four + ) && + + ( + cd gitrepo && + git fetch && + git log --format="%s" origin/master > ../actual + ) && + + hg -R hgrepo log --template "{desc}\n" > expected && + test_cmp actual expected ' -test_expect_success 'update bookmark' ' - test_when_finished "rm -rf gitrepo*" && +test_expect_success 'remote push with master bookmark' ' + test_when_finished "rm -rf hgrepo gitrepo*" && - ( - cd hgrepo && - hg bookmark devel - ) && + ( + hg init hgrepo && + cd hgrepo && + echo zero > content && + hg add content && + hg commit -m zero && + hg bookmark master && + echo one > content && + hg commit -m one + ) && - ( - git clone "hg::$PWD/hgrepo" gitrepo && - cd gitrepo && - git checkout devel && - echo devel > content && - git commit -a -m devel && - git push - ) && + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + echo two > content && + git commit -a -m two && + git push + ) && + + check_branch hgrepo default two +' - hg -R hgrepo bookmarks | egrep "devel[ ]+3:" +cat > expected <<EOF +changeset: 0:6e2126489d3d +tag: tip +user: A U Thor <author@example.com> +date: Mon Jan 01 00:00:00 2007 +0230 +summary: one + +EOF + +test_expect_success 'remote push from master branch' ' + test_when_finished "rm -rf hgrepo gitrepo*" && + + hg init hgrepo && + + ( + git init gitrepo && + cd gitrepo && + git remote add origin "hg::../hgrepo" && + echo one > content && + git add content && + git commit -a -m one && + git push origin master + ) && + + hg -R hgrepo log > actual && + cat actual && + test_cmp expected actual && + + check_branch hgrepo default one ' -author_test () { - echo $1 >> content && - hg commit -u "$2" -m "add $1" && - echo "$3" >> ../expected +GIT_REMOTE_HG_TEST_REMOTE=1 +export GIT_REMOTE_HG_TEST_REMOTE + +test_expect_success 'remote cloning' ' + test_when_finished "rm -rf gitrepo*" && + + ( + hg init hgrepo && + cd hgrepo && + echo zero > content && + hg add content && + hg commit -m zero + ) && + + git clone "hg::hgrepo" gitrepo && + check gitrepo HEAD zero +' + +test_expect_success 'remote update bookmark' ' + test_when_finished "rm -rf gitrepo*" && + + ( + cd hgrepo && + hg bookmark devel + ) && + + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + git checkout --quiet devel && + echo devel > content && + git commit -a -m devel && + git push --quiet + ) && + + check_bookmark hgrepo devel devel +' + +test_expect_success 'remote new bookmark' ' + test_when_finished "rm -rf gitrepo*" && + + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + git checkout --quiet -b feature-b && + echo feature-b > content && + git commit -a -m feature-b && + git push --quiet origin feature-b + ) && + + check_bookmark hgrepo feature-b feature-b +' + +test_expect_success 'remote push diverged' ' + test_when_finished "rm -rf gitrepo*" && + + git clone "hg::hgrepo" gitrepo && + + ( + cd hgrepo && + hg checkout default && + echo bump > content && + hg commit -m bump + ) && + + ( + cd gitrepo && + echo diverge > content && + git commit -a -m diverged && + check_push 1 <<-EOF + master:non-fast-forward + EOF + ) && + + check_branch hgrepo default bump +' + +test_expect_success 'remote update bookmark diverge' ' + test_when_finished "rm -rf gitrepo*" && + + ( + cd hgrepo && + hg checkout tip^ && + hg bookmark diverge + ) && + + git clone "hg::hgrepo" gitrepo && + + ( + cd hgrepo && + echo "bump bookmark" > content && + hg commit -m "bump bookmark" + ) && + + ( + cd gitrepo && + git checkout --quiet diverge && + echo diverge > content && + git commit -a -m diverge && + check_push 1 <<-EOF + diverge:fetch-first + EOF + ) && + + check_bookmark hgrepo diverge "bump bookmark" +' + +test_expect_success 'remote new bookmark multiple branch head' ' + test_when_finished "rm -rf gitrepo*" && + + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + git checkout --quiet -b feature-c HEAD^ && + echo feature-c > content && + git commit -a -m feature-c && + git push --quiet origin feature-c + ) && + + check_bookmark hgrepo feature-c feature-c +' + +# cleanup previous stuff +rm -rf hgrepo + +setup_big_push () { + ( + hg init hgrepo && + cd hgrepo && + echo zero > content && + hg add content && + hg commit -m zero && + hg bookmark bad_bmark1 && + echo one > content && + hg commit -m one && + hg bookmark bad_bmark2 && + hg bookmark good_bmark && + hg bookmark -i good_bmark && + hg -q branch good_branch && + echo "good branch" > content && + hg commit -m "good branch" && + hg -q branch bad_branch && + echo "bad branch" > content && + hg commit -m "bad branch" + ) && + + git clone "hg::hgrepo" gitrepo && + + ( + cd gitrepo && + echo two > content && + git commit -q -a -m two && + + git checkout -q good_bmark && + echo three > content && + git commit -q -a -m three && + + git checkout -q bad_bmark1 && + git reset --hard HEAD^ && + echo four > content && + git commit -q -a -m four && + + git checkout -q bad_bmark2 && + git reset --hard HEAD^ && + echo five > content && + git commit -q -a -m five && + + git checkout -q -b new_bmark master && + echo six > content && + git commit -q -a -m six && + + git checkout -q branches/good_branch && + echo seven > content && + git commit -q -a -m seven && + echo eight > content && + git commit -q -a -m eight && + + git checkout -q branches/bad_branch && + git reset --hard HEAD^ && + echo nine > content && + git commit -q -a -m nine && + + git checkout -q -b branches/new_branch master && + echo ten > content && + git commit -q -a -m ten + ) } -test_expect_success 'authors' ' - mkdir -p tmp && cd tmp && - test_when_finished "cd .. && rm -rf tmp" && - - ( - hg init hgrepo && - cd hgrepo && - - touch content && - hg add content && - - author_test alpha "" "H G Wells <wells@example.com>" && - author_test beta "test" "test <unknown>" && - author_test beta "test <test@example.com> (comment)" "test <test@example.com>" && - author_test gamma "<test@example.com>" "Unknown <test@example.com>" && - author_test delta "name<test@example.com>" "name <test@example.com>" && - author_test epsilon "name <test@example.com" "name <test@example.com>" && - author_test zeta " test " "test <unknown>" && - author_test eta "test < test@example.com >" "test <test@example.com>" && - author_test theta "test >test@example.com>" "test <test@example.com>" && - author_test iota "test < test <at> example <dot> com>" "test <unknown>" && - author_test kappa "test@example.com" "Unknown <test@example.com>" - ) && - - git clone "hg::$PWD/hgrepo" gitrepo && - git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" > actual && - - test_cmp expected actual +test_expect_success 'remote big push' ' + test_when_finished "rm -rf hgrepo gitrepo*" && + + setup_big_push + + ( + cd gitrepo && + + check_push 1 --all <<-EOF + master + good_bmark + branches/good_branch + new_bmark:new + branches/new_branch:new + bad_bmark1:non-fast-forward + bad_bmark2:non-fast-forward + branches/bad_branch:non-fast-forward + EOF + ) && + + check_branch hgrepo default one && + check_branch hgrepo good_branch "good branch" && + check_branch hgrepo bad_branch "bad branch" && + check_branch hgrepo new_branch '' && + check_bookmark hgrepo good_bmark one && + check_bookmark hgrepo bad_bmark1 one && + check_bookmark hgrepo bad_bmark2 one && + check_bookmark hgrepo new_bmark '' +' + +test_expect_success 'remote big push fetch first' ' + test_when_finished "rm -rf hgrepo gitrepo*" && + + ( + hg init hgrepo && + cd hgrepo && + echo zero > content && + hg add content && + hg commit -m zero && + hg bookmark bad_bmark && + hg bookmark good_bmark && + hg bookmark -i good_bmark && + hg -q branch good_branch && + echo "good branch" > content && + hg commit -m "good branch" && + hg -q branch bad_branch && + echo "bad branch" > content && + hg commit -m "bad branch" + ) && + + git clone "hg::hgrepo" gitrepo && + + ( + cd hgrepo && + hg bookmark -f bad_bmark && + echo update_bmark > content && + hg commit -m "update bmark" + ) && + + ( + cd gitrepo && + echo two > content && + git commit -q -a -m two && + + git checkout -q good_bmark && + echo three > content && + git commit -q -a -m three && + + git checkout -q bad_bmark && + echo four > content && + git commit -q -a -m four && + + git checkout -q branches/bad_branch && + echo five > content && + git commit -q -a -m five && + + check_push 1 --all <<-EOF + master + good_bmark + new_bmark:new + new_branch:new + bad_bmark:fetch-first + branches/bad_branch:festch-first + EOF + + git fetch && + + check_push 1 --all <<-EOF + master + good_bmark + bad_bmark:non-fast-forward + branches/bad_branch:non-fast-forward + EOF + ) +' + +test_expect_failure 'remote big push force' ' + test_when_finished "rm -rf hgrepo gitrepo*" && + + setup_big_push + + ( + cd gitrepo && + + check_push 0 --force --all <<-EOF + master + good_bmark + branches/good_branch + new_bmark:new + branches/new_branch:new + bad_bmark1:forced-update + bad_bmark2:forced-update + branches/bad_branch:forced-update + EOF + ) && + + check_branch hgrepo default six && + check_branch hgrepo good_branch eight && + check_branch hgrepo bad_branch nine && + check_branch hgrepo new_branch ten && + check_bookmark hgrepo good_bmark three && + check_bookmark hgrepo bad_bmark1 four && + check_bookmark hgrepo bad_bmark2 five && + check_bookmark hgrepo new_bmark six +' + +test_expect_failure 'remote big push dry-run' ' + test_when_finished "rm -rf hgrepo gitrepo*" && + + setup_big_push + + ( + cd gitrepo && + + check_push 0 --dry-run --all <<-EOF + master + good_bmark + branches/good_branch + new_bmark:new + branches/new_branch:new + bad_bmark1:non-fast-forward + bad_bmark2:non-fast-forward + branches/bad_branch:non-fast-forward + EOF + + check_push 0 --dry-run master good_bmark new_bmark branches/good_branch branches/new_branch <<-EOF + master + good_bmark + branches/good_branch + new_bmark:new + branches/new_branch:new + EOF + ) && + + check_branch hgrepo default one && + check_branch hgrepo good_branch "good branch" && + check_branch hgrepo bad_branch "bad branch" && + check_branch hgrepo new_branch '' && + check_bookmark hgrepo good_bmark one && + check_bookmark hgrepo bad_bmark1 one && + check_bookmark hgrepo bad_bmark2 one && + check_bookmark hgrepo new_bmark '' +' + +test_expect_success 'remote double failed push' ' + test_when_finished "rm -rf hgrepo gitrepo*" && + + ( + hg init hgrepo && + cd hgrepo && + echo zero > content && + hg add content && + hg commit -m zero && + echo one > content && + hg commit -m one + ) && + + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + git reset --hard HEAD^ && + echo two > content && + git commit -a -m two && + test_expect_code 1 git push && + test_expect_code 1 git push + ) ' test_done |