Mercurial > public > mercurial-scm > hg
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 |