mercurial/hg.py
changeset 2597 5ba8be56fa8f
parent 2595 edb66cb05ded
child 2600 c4325f0a9b91
equal deleted inserted replaced
2596:e3258cc3ed63 2597:5ba8be56fa8f
     8 from node import *
     8 from node import *
     9 from repo import *
     9 from repo import *
    10 from demandload import *
    10 from demandload import *
    11 from i18n import gettext as _
    11 from i18n import gettext as _
    12 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
    12 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
    13 demandload(globals(), "os util")
    13 demandload(globals(), "errno lock os shutil util")
    14 
    14 
    15 def bundle(ui, path):
    15 def bundle(ui, path):
    16     if path.startswith('bundle://'):
    16     if path.startswith('bundle://'):
    17         path = path[9:]
    17         path = path[9:]
    18     else:
    18     else:
    71             return ctor(ui, path, create)
    71             return ctor(ui, path, create)
    72         except TypeError:
    72         except TypeError:
    73             raise util.Abort(_('cannot create new repository over "%s" protocol') %
    73             raise util.Abort(_('cannot create new repository over "%s" protocol') %
    74                              scheme)
    74                              scheme)
    75     return ctor(ui, path)
    75     return ctor(ui, path)
       
    76 
       
    77 def clone(ui, source, dest=None, pull=False, rev=None, update=True):
       
    78     """Make a copy of an existing repository.
       
    79 
       
    80     Create a copy of an existing repository in a new directory.  The
       
    81     source and destination are URLs, as passed to the repository
       
    82     function.  Returns a pair of repository objects, the source and
       
    83     newly created destination.
       
    84 
       
    85     The location of the source is added to the new repository's
       
    86     .hg/hgrc file, as the default to be used for future pulls and
       
    87     pushes.
       
    88 
       
    89     If an exception is raised, the partly cloned/updated destination
       
    90     repository will be deleted.
       
    91     
       
    92     Keyword arguments:
       
    93 
       
    94     dest: URL of destination repository to create (defaults to base
       
    95     name of source repository)
       
    96 
       
    97     pull: always pull from source repository, even in local case
       
    98 
       
    99     rev: revision to clone up to (implies pull=True)
       
   100 
       
   101     update: update working directory after clone completes, if
       
   102     destination is local repository
       
   103     """
       
   104     if dest is None:
       
   105         dest = os.path.basename(os.path.normpath(source))
       
   106 
       
   107     if os.path.exists(dest):
       
   108         raise util.Abort(_("destination '%s' already exists"), dest)
       
   109 
       
   110     class DirCleanup(object):
       
   111         def __init__(self, dir_):
       
   112             self.rmtree = shutil.rmtree
       
   113             self.dir_ = dir_
       
   114         def close(self):
       
   115             self.dir_ = None
       
   116         def __del__(self):
       
   117             if self.dir_:
       
   118                 self.rmtree(self.dir_, True)
       
   119 
       
   120     src_repo = repository(ui, source)
       
   121 
       
   122     dest_repo = None
       
   123     try:
       
   124         dest_repo = repository(ui, dest)
       
   125         raise util.Abort(_("destination '%s' already exists." % dest))
       
   126     except RepoError:
       
   127         dest_repo = repository(ui, dest, create=True)
       
   128 
       
   129     dest_path = None
       
   130     dir_cleanup = None
       
   131     if dest_repo.local():
       
   132         dest_path = os.path.realpath(dest)
       
   133         dir_cleanup = DirCleanup(dest_path)
       
   134 
       
   135     abspath = source
       
   136     copy = False
       
   137     if src_repo.local() and dest_repo.local():
       
   138         abspath = os.path.abspath(source)
       
   139         copy = not pull and not rev
       
   140 
       
   141     src_lock, dest_lock = None, None
       
   142     if copy:
       
   143         try:
       
   144             # we use a lock here because if we race with commit, we
       
   145             # can end up with extra data in the cloned revlogs that's
       
   146             # not pointed to by changesets, thus causing verify to
       
   147             # fail
       
   148             src_lock = src_repo.lock()
       
   149         except lock.LockException:
       
   150             copy = False
       
   151 
       
   152     if copy:
       
   153         # we lock here to avoid premature writing to the target
       
   154         dest_lock = lock.lock(os.path.join(dest_path, ".hg", "lock"))
       
   155 
       
   156 	# we need to remove the (empty) data dir in dest so copyfiles
       
   157 	# can do its work
       
   158 	os.rmdir(os.path.join(dest_path, ".hg", "data"))
       
   159         files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
       
   160         for f in files.split():
       
   161             src = os.path.join(source, ".hg", f)
       
   162             dst = os.path.join(dest_path, ".hg", f)
       
   163             try:
       
   164                 util.copyfiles(src, dst)
       
   165             except OSError, inst:
       
   166                 if inst.errno != errno.ENOENT:
       
   167                     raise
       
   168 
       
   169 	# we need to re-init the repo after manually copying the data
       
   170 	# into it
       
   171         dest_repo = repository(ui, dest)
       
   172 
       
   173     else:
       
   174         revs = None
       
   175         if rev:
       
   176             if not src_repo.local():
       
   177                 raise util.Abort(_("clone by revision not supported yet "
       
   178                                    "for remote repositories"))
       
   179             revs = [src_repo.lookup(r) for r in rev]
       
   180 
       
   181         if dest_repo.local():
       
   182             dest_repo.pull(src_repo, heads=revs)
       
   183         elif src_repo.local():
       
   184             src_repo.push(dest_repo, revs=revs)
       
   185         else:
       
   186             raise util.Abort(_("clone from remote to remote not supported"))
       
   187 
       
   188     if src_lock:
       
   189         src_lock.release()
       
   190 
       
   191     if dest_repo.local():
       
   192         fp = dest_repo.opener("hgrc", "w", text=True)
       
   193         fp.write("[paths]\n")
       
   194         fp.write("default = %s\n" % abspath)
       
   195         fp.close()
       
   196 
       
   197         if dest_lock:
       
   198             dest_lock.release()
       
   199 
       
   200         if update:
       
   201             dest_repo.update(dest_repo.changelog.tip())
       
   202     if dir_cleanup:
       
   203         dir_cleanup.close()
       
   204 
       
   205     return src_repo, dest_repo