Merge branch 'fc/contrib-bzr-hg-fixes'
authorJunio C Hamano <gitster@pobox.com>
Wed, 18 Sep 2013 18:45:49 +0000 (11:45 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 18 Sep 2013 18:45:49 +0000 (11:45 -0700)
* fc/contrib-bzr-hg-fixes:
contrib/remote-helpers: quote variable references in redirection targets
contrib/remote-helpers: style updates for test scripts
remote-hg: use notes to keep track of Hg revisions
remote-helpers: cleanup more global variables
remote-helpers: trivial style fixes
remote-hg: improve basic test
remote-hg: add missing &&s in the test
remote-hg: fix test
remote-bzr: make bzr branches configurable per-repo
remote-bzr: fix export of utf-8 authors

1  2 
contrib/remote-helpers/git-remote-bzr
contrib/remote-helpers/git-remote-hg
index 1e0044b69fc5c87ec03833b6fb1bf434a9e0acc0,42cec58a00aad15f7daef4a26caffca79e839778..054161ae21b0c884d314c984992a6c7aa8276a96
  # or
  # % git clone bzr::lp:myrepo
  #
- # If you want to specify which branches you want track (per repo):
- # git config remote-bzr.branches 'trunk, devel, test'
+ # If you want to specify which branches you want to track (per repo):
+ # % git config remote.origin.bzr-branches 'trunk, devel, test'
+ #
+ # Where 'origin' is the name of the repository you want to specify the
+ # branches.
  #
  
  import sys
@@@ -168,17 -171,16 +171,16 @@@ class Parser
          if not m:
              return None
          _, name, email, date, tz = m.groups()
+         name = name.decode('utf-8')
          committer = '%s <%s>' % (name, email)
          tz = int(tz)
          tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
          return (committer, int(date), tz)
  
  def rev_to_mark(rev):
-     global marks
      return marks.from_rev(rev)
  
  def mark_to_rev(mark):
-     global marks
      return marks.to_rev(mark)
  
  def fixup_user(user):
@@@ -233,8 -235,6 +235,6 @@@ def get_filechanges(cur, prev)
      return modified, removed
  
  def export_files(tree, files):
-     global marks, filenodes
      final = []
      for path, fid in files.iteritems():
          kind = tree.kind(fid)
      return final
  
  def export_branch(repo, name):
-     global prefix
      ref = '%s/heads/%s' % (prefix, name)
      tip = marks.get_tip(name)
  
      marks.set_tip(name, revid)
  
  def export_tag(repo, name):
-     global tags, prefix
      ref = '%s/tags/%s' % (prefix, name)
      print "reset %s" % ref
      print "from :%u" % rev_to_mark(tags[name])
      print
  
  def do_import(parser):
-     global dirname
      repo = parser.repo
      path = os.path.join(dirname, 'marks-git')
  
      sys.stdout.flush()
  
  def parse_blob(parser):
-     global blob_marks
      parser.next()
      mark = parser.get_mark()
      parser.next()
  class CustomTree():
  
      def __init__(self, branch, revid, parents, files):
-         global files_cache
          self.updates = {}
          self.branch = branch
  
              add_entry(fid, dirname, 'directory')
              return fid
  
-         def add_entry(fid, path, kind, mode = None):
+         def add_entry(fid, path, kind, mode=None):
              dirname, basename = os.path.split(path)
              parent_fid = get_parent(dirname, basename)
  
              self.files[path] = [change[0], None]
              changes.append(change)
  
-         def update_entry(fid, path, kind, mode = None):
+         def update_entry(fid, path, kind, mode=None):
              dirname, basename = os.path.split(path)
              parent_fid = get_parent(dirname, basename)
  
@@@ -583,9 -573,6 +573,6 @@@ def c_style_unescape(string)
      return string
  
  def parse_commit(parser):
-     global marks, blob_marks, parsed_refs
-     global mode
      parents = []
  
      ref = parser[1]
      marks.new_mark(revid, commit_mark)
  
  def parse_reset(parser):
-     global parsed_refs
      ref = parser[1]
      parser.next()
  
      parsed_refs[ref] = mark_to_rev(from_mark)
  
  def do_export(parser):
-     global parsed_refs, dirname, transports
      parser.next()
  
      for line in parser.each_block('done'):
              branch.generate_revision_history(revid, marks.get_tip(name))
  
              if name in peers:
 -                peer = bzrlib.branch.Branch.open(peers[name])
 +                peer = bzrlib.branch.Branch.open(peers[name],
 +                                                 possible_transports=transports)
                  try:
                      peer.bzrdir.push_branch(branch, revision_id=revid)
                  except bzrlib.errors.DivergedBranches:
      print
  
  def do_capabilities(parser):
-     global dirname
      print "import"
      print "export"
      print "refspec refs/heads/*:%s/heads/*" % prefix
@@@ -744,8 -724,6 +725,6 @@@ def ref_is_valid(name)
      return not True in [c in name for c in '~^: \\']
  
  def do_list(parser):
-     global tags
      master_branch = None
  
      for name in branches:
      print
  
  def clone(path, remote_branch):
-     global transports
      try:
 -        bdir = bzrlib.bzrdir.BzrDir.create(path)
 +        bdir = bzrlib.bzrdir.BzrDir.create(path, possible_transports=transports)
      except bzrlib.errors.AlreadyControlDirError:
 -        bdir = bzrlib.bzrdir.BzrDir.open(path)
 +        bdir = bzrlib.bzrdir.BzrDir.open(path, possible_transports=transports)
      repo = bdir.find_repository()
      repo.fetch(remote_branch.repository)
      return remote_branch.sprout(bdir, repository=repo)
  
  def get_remote_branch(name):
-     global dirname, branches, transports
 -    remote_branch = bzrlib.branch.Branch.open(branches[name])
 +    remote_branch = bzrlib.branch.Branch.open(branches[name],
 +                                              possible_transports=transports)
      if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
          return remote_branch
  
      branch_path = os.path.join(dirname, 'clone', name)
  
      try:
 -        branch = bzrlib.branch.Branch.open(branch_path)
 +        branch = bzrlib.branch.Branch.open(branch_path,
 +                                           possible_transports=transports)
      except bzrlib.errors.NotBranchError:
          # clone
          branch = clone(branch_path, remote_branch)
@@@ -825,19 -798,15 +801,17 @@@ def find_branches(repo)
              yield name, branch.base
  
  def get_repo(url, alias):
-     global dirname, peer, branches, transports
      normal_url = bzrlib.urlutils.normalize_url(url)
 -    origin = bzrlib.bzrdir.BzrDir.open(url)
 +    origin = bzrlib.bzrdir.BzrDir.open(url, possible_transports=transports)
      is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
  
      shared_path = os.path.join(gitdir, 'bzr')
      try:
 -        shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
 +        shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path,
 +                                               possible_transports=transports)
      except bzrlib.errors.NotBranchError:
 -        shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
 +        shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path,
 +                                                 possible_transports=transports)
      try:
          shared_repo = shared_dir.open_repository()
      except bzrlib.errors.NoRepositoryPresent:
          else:
              # check and remove old organization
              try:
 -                bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
 +                bdir = bzrlib.bzrdir.BzrDir.open(clone_path,
 +                                                 possible_transports=transports)
                  bdir.destroy_repository()
              except bzrlib.errors.NotBranchError:
                  pass
              except bzrlib.errors.NoRepositoryPresent:
                  pass
  
-     wanted = get_config('remote-bzr.branches').rstrip().split(', ')
+     wanted = get_config('remote.%s.bzr-branches' % alias).rstrip().split(', ')
      # stupid python
      wanted = [e for e in wanted if e]
+     if not wanted:
+         wanted = get_config('remote-bzr.branches').rstrip().split(', ')
+         # stupid python
+         wanted = [e for e in wanted if e]
  
      if not wanted:
          try:
@@@ -904,7 -876,6 +882,7 @@@ def main(args)
      global files_cache
      global is_tmp
      global branches, peers
 +    global transports
  
      alias = args[1]
      url = args[2]
      marks = None
      branches = {}
      peers = {}
 +    transports = []
  
      if alias[5:] == url:
          is_tmp = True
index c27603965ab6970dd71003603d6a77e637a22ab5,3db7bd1e430155d5e82a3280b995d15119fc1bb3..92d994e470f05db8536ba443a6afb192be8c2452
@@@ -23,7 -23,11 +23,11 @@@ import subproces
  import urllib
  import atexit
  import urlparse, hashlib
+ import time as ptime
  
+ #
+ # If you want to see Mercurial revisions as Git commit notes:
+ # git config core.notesRef refs/notes/hg
  #
  # If you are not in hg-git-compat mode and want to disable the tracking of
  # named branches:
@@@ -126,6 -130,7 +130,7 @@@ class Marks
          self.rev_marks = {}
          self.last_mark = 0
          self.version = 0
+         self.last_note = 0
  
      def load(self):
          if not os.path.exists(self.path):
          self.marks = tmp['marks']
          self.last_mark = tmp['last-mark']
          self.version = tmp.get('version', 1)
+         self.last_note = tmp.get('last-note', 0)
  
          for rev, mark in self.marks.iteritems():
              self.rev_marks[mark] = rev
          self.version = 2
  
      def dict(self):
-         return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark, 'version' : self.version }
+         return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark, 'version' : self.version, 'last-note' : self.last_note }
  
      def store(self):
          json.dump(self.dict(), open(self.path, 'w'))
@@@ -227,8 -233,6 +233,6 @@@ class Parser
          return sys.stdin.read(size)
  
      def get_author(self):
-         global bad_mail
          ex = None
          m = RAW_AUTHOR_RE.match(self.line)
          if not m:
@@@ -261,8 -265,6 +265,6 @@@ def fix_file_path(path)
      return os.path.relpath(path, '/')
  
  def export_files(files):
-     global marks, filenodes
      final = []
      for f in files:
          fid = node.hex(f.filenode())
@@@ -344,8 -346,6 +346,6 @@@ def fixup_user_hg(user)
      return (name, mail)
  
  def fixup_user(user):
-     global mode, bad_mail
      if mode == 'git':
          name, mail = fixup_user_git(user)
      else:
@@@ -374,7 -374,7 +374,7 @@@ def updatebookmarks(repo, peer)
          bookmarks.write(repo)
  
  def get_repo(url, alias):
-     global dirname, peer
+     global peer
  
      myui = ui.ui()
      myui.setconfig('ui', 'interactive', 'off')
              os.makedirs(dirname)
      else:
          shared_path = os.path.join(gitdir, 'hg')
 -        if not os.path.exists(shared_path):
 -            try:
 -                hg.clone(myui, {}, url, shared_path, update=False, pull=True)
 -            except:
 -                die('Repository error')
 +
 +        # check and upgrade old organization
 +        hg_path = os.path.join(shared_path, '.hg')
 +        if os.path.exists(shared_path) and not os.path.exists(hg_path):
 +            repos = os.listdir(shared_path)
 +            for x in repos:
 +                local_hg = os.path.join(shared_path, x, 'clone', '.hg')
 +                if not os.path.exists(local_hg):
 +                    continue
 +                if not os.path.exists(hg_path):
 +                    shutil.move(local_hg, hg_path)
 +                shutil.rmtree(os.path.join(shared_path, x, 'clone'))
 +
 +        # setup shared repo (if not there)
 +        try:
 +            hg.peer(myui, {}, shared_path, create=True)
 +        except error.RepoError:
 +            pass
  
          if not os.path.exists(dirname):
              os.makedirs(dirname)
      return repo
  
  def rev_to_mark(rev):
-     global marks
      return marks.from_rev(rev.hex())
  
  def mark_to_rev(mark):
-     global marks
      return marks.to_rev(mark)
  
  def export_ref(repo, name, kind, head):
-     global prefix, marks, mode
      ename = '%s/%s' % (kind, name)
      try:
          tip = marks.get_tip(ename)
      print "from :%u" % rev_to_mark(head)
      print
  
+     pending_revs = set(revs) - notes
+     if pending_revs:
+         note_mark = marks.next_mark()
+         ref = "refs/notes/hg"
+         print "commit %s" % ref
+         print "mark :%d" % (note_mark)
+         print "committer remote-hg <> %s" % (ptime.strftime('%s %z'))
+         desc = "Notes for %s\n" % (name)
+         print "data %d" % (len(desc))
+         print desc
+         if marks.last_note:
+             print "from :%u" % marks.last_note
+         for rev in pending_revs:
+             notes.add(rev)
+             c = repo[rev]
+             print "N inline :%u" % rev_to_mark(c)
+             msg = c.hex()
+             print "data %d" % (len(msg))
+             print msg
+         print
+         marks.last_note = note_mark
      marks.set_tip(ename, head.hex())
  
  def export_tag(repo, tag):
@@@ -550,12 -558,9 +571,9 @@@ def export_branch(repo, branch)
      export_ref(repo, branch, 'branches', head)
  
  def export_head(repo):
-     global g_head
      export_ref(repo, g_head[0], 'bookmarks', g_head[1])
  
  def do_capabilities(parser):
-     global prefix, dirname
      print "import"
      print "export"
      print "refspec refs/heads/branches/*:%s/branches/*" % prefix
@@@ -575,8 -580,6 +593,6 @@@ def branch_tip(branch)
      return branches[branch][-1]
  
  def get_branch_tip(repo, branch):
-     global branches
      heads = branches.get(hgref(branch), None)
      if not heads:
          return None
      return heads[0]
  
  def list_head(repo, cur):
-     global g_head, bmarks, fake_bmark
+     global g_head, fake_bmark
  
      if 'default' not in branches:
          # empty repo
      g_head = (head, node)
  
  def do_list(parser):
-     global branches, bmarks, track_branches
      repo = parser.repo
      for bmark, node in bookmarks.listbookmarks(repo).iteritems():
          bmarks[bmark] = repo[node]
@@@ -674,8 -675,6 +688,6 @@@ def do_import(parser)
      print 'done'
  
  def parse_blob(parser):
-     global blob_marks
      parser.next()
      mark = parser.get_mark()
      parser.next()
@@@ -692,9 -691,6 +704,6 @@@ def get_merge_files(repo, p1, p2, files
              files[e] = f
  
  def parse_commit(parser):
-     global marks, blob_marks, parsed_refs
-     global mode
      from_mark = merge_mark = None
  
      ref = parser[1]
      marks.new_mark(node, commit_mark)
  
  def parse_reset(parser):
-     global parsed_refs
      ref = parser[1]
      parser.next()
      # ugh
@@@ -1006,8 -1000,6 +1013,6 @@@ def check_tip(ref, kind, name, heads)
          return tip in heads
  
  def do_export(parser):
-     global parsed_refs, bmarks, peer
      p_bmarks = []
      p_revs = {}
  
              author, msg = parsed_tags.get(tag, (None, None))
              if mode == 'git':
                  if not msg:
-                     msg = 'Added tag %s for changeset %s' % (tag, node[:12]);
+                     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:
@@@ -1137,7 -1129,7 +1142,7 @@@ def do_option(parser)
  
  def fix_path(alias, repo, orig_url):
      url = urlparse.urlparse(orig_url, 'file')
 -    if url.scheme != 'file' or os.path.isabs(url.path):
 +    if url.scheme != 'file' or os.path.isabs(os.path.expanduser(url.path)):
          return
      abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
      cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
@@@ -1152,6 -1144,7 +1157,7 @@@ def main(args)
      global filenodes
      global fake_bmark, hg_version
      global dry_run
+     global notes, alias
  
      alias = args[1]
      url = args[2]
      except:
          hg_version = None
      dry_run = False
+     notes = set()
  
      repo = get_repo(url, alias)
      prefix = 'refs/hg/%s' % alias