comparison mercurial/hg.py @ 2597:5ba8be56fa8f

clone: move code into hg module. make doc better. api in commands module is still same, but version in hg is best for calling within python now.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Tue, 11 Jul 2006 16:18:53 -0700
parents edb66cb05ded
children c4325f0a9b91
comparison
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