46 try: |
46 try: |
47 self.ui.readconfig(self.join("hgrc")) |
47 self.ui.readconfig(self.join("hgrc")) |
48 except IOError: |
48 except IOError: |
49 pass |
49 pass |
50 |
50 |
51 def hook(self, name, **args): |
51 def hook(self, name, throw=False, **args): |
52 def runhook(name, cmd): |
52 def runhook(name, cmd): |
53 self.ui.note(_("running hook %s: %s\n") % (name, cmd)) |
53 self.ui.note(_("running hook %s: %s\n") % (name, cmd)) |
54 old = {} |
54 old = {} |
55 for k, v in args.items(): |
55 for k, v in args.items(): |
56 k = k.upper() |
56 k = k.upper() |
|
57 old['HG_' + k] = os.environ.get(k, None) |
57 old[k] = os.environ.get(k, None) |
58 old[k] = os.environ.get(k, None) |
58 os.environ[k] = v |
59 os.environ['HG_' + k] = str(v) |
59 |
60 os.environ[k] = str(v) |
60 # Hooks run in the repository root |
61 |
61 olddir = os.getcwd() |
62 try: |
62 os.chdir(self.root) |
63 # Hooks run in the repository root |
63 r = os.system(cmd) |
64 olddir = os.getcwd() |
64 os.chdir(olddir) |
65 os.chdir(self.root) |
65 |
66 r = os.system(cmd) |
66 for k, v in old.items(): |
67 finally: |
67 if v != None: |
68 for k, v in old.items(): |
68 os.environ[k] = v |
69 if v is not None: |
69 else: |
70 os.environ[k] = v |
70 del os.environ[k] |
71 else: |
|
72 del os.environ[k] |
|
73 |
|
74 os.chdir(olddir) |
71 |
75 |
72 if r: |
76 if r: |
73 self.ui.warn(_("abort: %s hook failed with status %d!\n") % |
77 desc, r = util.explain_exit(r) |
74 (name, r)) |
78 if throw: |
|
79 raise util.Abort(_('%s hook %s') % (name, desc)) |
|
80 self.ui.warn(_('error: %s hook %s\n') % (name, desc)) |
75 return False |
81 return False |
76 return True |
82 return True |
77 |
83 |
78 r = True |
84 r = True |
79 for hname, cmd in self.ui.configitems("hooks"): |
85 for hname, cmd in self.ui.configitems("hooks"): |
223 |
229 |
224 return transaction.transaction(self.ui.warn, self.opener, |
230 return transaction.transaction(self.ui.warn, self.opener, |
225 self.join("journal"), after) |
231 self.join("journal"), after) |
226 |
232 |
227 def recover(self): |
233 def recover(self): |
228 lock = self.lock() |
234 l = self.lock() |
229 if os.path.exists(self.join("journal")): |
235 if os.path.exists(self.join("journal")): |
230 self.ui.status(_("rolling back interrupted transaction\n")) |
236 self.ui.status(_("rolling back interrupted transaction\n")) |
231 transaction.rollback(self.opener, self.join("journal")) |
237 transaction.rollback(self.opener, self.join("journal")) |
232 self.manifest = manifest.manifest(self.opener) |
238 self.manifest = manifest.manifest(self.opener) |
233 self.changelog = changelog.changelog(self.opener) |
239 self.changelog = changelog.changelog(self.opener) |
237 return False |
243 return False |
238 |
244 |
239 def undo(self, wlock=None): |
245 def undo(self, wlock=None): |
240 if not wlock: |
246 if not wlock: |
241 wlock = self.wlock() |
247 wlock = self.wlock() |
242 lock = self.lock() |
248 l = self.lock() |
243 if os.path.exists(self.join("undo")): |
249 if os.path.exists(self.join("undo")): |
244 self.ui.status(_("rolling back last transaction\n")) |
250 self.ui.status(_("rolling back last transaction\n")) |
245 transaction.rollback(self.opener, self.join("undo")) |
251 transaction.rollback(self.opener, self.join("undo")) |
246 util.rename(self.join("undo.dirstate"), self.join("dirstate")) |
252 util.rename(self.join("undo.dirstate"), self.join("dirstate")) |
247 self.dirstate.read() |
253 self.dirstate.read() |
248 else: |
254 else: |
249 self.ui.warn(_("no undo information available\n")) |
255 self.ui.warn(_("no undo information available\n")) |
250 |
256 |
251 def lock(self, wait=1): |
257 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None): |
252 try: |
258 try: |
253 return lock.lock(self.join("lock"), 0) |
259 l = lock.lock(self.join(lockname), 0, releasefn) |
254 except lock.LockHeld, inst: |
|
255 if wait: |
|
256 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0]) |
|
257 return lock.lock(self.join("lock"), wait) |
|
258 raise inst |
|
259 |
|
260 def wlock(self, wait=1): |
|
261 try: |
|
262 wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write) |
|
263 except lock.LockHeld, inst: |
260 except lock.LockHeld, inst: |
264 if not wait: |
261 if not wait: |
265 raise inst |
262 raise inst |
266 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0]) |
263 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0]) |
267 wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write) |
264 l = lock.lock(self.join(lockname), wait, releasefn) |
268 self.dirstate.read() |
265 if acquirefn: |
269 return wlock |
266 acquirefn() |
|
267 return l |
|
268 |
|
269 def lock(self, wait=1): |
|
270 return self.do_lock("lock", wait) |
|
271 |
|
272 def wlock(self, wait=1): |
|
273 return self.do_lock("wlock", wait, |
|
274 self.dirstate.write, |
|
275 self.dirstate.read) |
|
276 |
|
277 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2): |
|
278 "determine whether a new filenode is needed" |
|
279 fp1 = manifest1.get(filename, nullid) |
|
280 fp2 = manifest2.get(filename, nullid) |
|
281 |
|
282 if fp2 != nullid: |
|
283 # is one parent an ancestor of the other? |
|
284 fpa = filelog.ancestor(fp1, fp2) |
|
285 if fpa == fp1: |
|
286 fp1, fp2 = fp2, nullid |
|
287 elif fpa == fp2: |
|
288 fp2 = nullid |
|
289 |
|
290 # is the file unmodified from the parent? report existing entry |
|
291 if fp2 == nullid and text == filelog.read(fp1): |
|
292 return (fp1, None, None) |
|
293 |
|
294 return (None, fp1, fp2) |
270 |
295 |
271 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None): |
296 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None): |
272 orig_parent = self.dirstate.parents()[0] or nullid |
297 orig_parent = self.dirstate.parents()[0] or nullid |
273 p1 = p1 or self.dirstate.parents()[0] or nullid |
298 p1 = p1 or self.dirstate.parents()[0] or nullid |
274 p2 = p2 or self.dirstate.parents()[1] or nullid |
299 p2 = p2 or self.dirstate.parents()[1] or nullid |
296 t = self.wread(f) |
321 t = self.wread(f) |
297 tm = util.is_exec(self.wjoin(f), mfm.get(f, False)) |
322 tm = util.is_exec(self.wjoin(f), mfm.get(f, False)) |
298 r = self.file(f) |
323 r = self.file(f) |
299 mfm[f] = tm |
324 mfm[f] = tm |
300 |
325 |
301 fp1 = m1.get(f, nullid) |
326 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2) |
302 fp2 = m2.get(f, nullid) |
327 if entry: |
303 |
328 mm[f] = entry |
304 # is the same revision on two branches of a merge? |
329 continue |
305 if fp2 == fp1: |
|
306 fp2 = nullid |
|
307 |
|
308 if fp2 != nullid: |
|
309 # is one parent an ancestor of the other? |
|
310 fpa = r.ancestor(fp1, fp2) |
|
311 if fpa == fp1: |
|
312 fp1, fp2 = fp2, nullid |
|
313 elif fpa == fp2: |
|
314 fp2 = nullid |
|
315 |
|
316 # is the file unmodified from the parent? |
|
317 if t == r.read(fp1): |
|
318 # record the proper existing parent in manifest |
|
319 # no need to add a revision |
|
320 mm[f] = fp1 |
|
321 continue |
|
322 |
330 |
323 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2) |
331 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2) |
324 changed.append(f) |
332 changed.append(f) |
325 if update_dirstate: |
333 if update_dirstate: |
326 self.dirstate.update([f], "n") |
334 self.dirstate.update([f], "n") |
401 meta["copy"] = cp |
412 meta["copy"] = cp |
402 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid))) |
413 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid))) |
403 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"])) |
414 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"])) |
404 fp1, fp2 = nullid, nullid |
415 fp1, fp2 = nullid, nullid |
405 else: |
416 else: |
406 fp1 = m1.get(f, nullid) |
417 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2) |
407 fp2 = m2.get(f, nullid) |
418 if entry: |
408 |
419 new[f] = entry |
409 if fp2 != nullid: |
|
410 # is one parent an ancestor of the other? |
|
411 fpa = r.ancestor(fp1, fp2) |
|
412 if fpa == fp1: |
|
413 fp1, fp2 = fp2, nullid |
|
414 elif fpa == fp2: |
|
415 fp2 = nullid |
|
416 |
|
417 # is the file unmodified from the parent? |
|
418 if not meta and t == r.read(fp1) and fp2 == nullid: |
|
419 # record the proper existing parent in manifest |
|
420 # no need to add a revision |
|
421 new[f] = fp1 |
|
422 continue |
420 continue |
423 |
421 |
424 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2) |
422 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2) |
425 # remember what we've added so that we can later calculate |
423 # remember what we've added so that we can later calculate |
426 # the files to pull from a set of changesets |
424 # the files to pull from a set of changesets |
457 return None |
455 return None |
458 text = edittext |
456 text = edittext |
459 |
457 |
460 user = user or self.ui.username() |
458 user = user or self.ui.username() |
461 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date) |
459 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date) |
|
460 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1, |
|
461 parent2=xp2) |
462 tr.close() |
462 tr.close() |
463 |
463 |
464 self.dirstate.setparents(n) |
464 self.dirstate.setparents(n) |
465 self.dirstate.update(new, "n") |
465 self.dirstate.update(new, "n") |
466 self.dirstate.forget(remove) |
466 self.dirstate.forget(remove) |
467 |
467 |
468 if not self.hook("commit", node=hex(n)): |
468 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2) |
469 return None |
|
470 return n |
469 return n |
471 |
470 |
472 def walk(self, node=None, files=[], match=util.always): |
471 def walk(self, node=None, files=[], match=util.always): |
473 if node: |
472 if node: |
474 fdict = dict.fromkeys(files) |
473 fdict = dict.fromkeys(files) |
507 # are we comparing the working directory? |
506 # are we comparing the working directory? |
508 if not node2: |
507 if not node2: |
509 if not wlock: |
508 if not wlock: |
510 try: |
509 try: |
511 wlock = self.wlock(wait=0) |
510 wlock = self.wlock(wait=0) |
512 except lock.LockHeld: |
511 except lock.LockException: |
513 wlock = None |
512 wlock = None |
514 lookup, modified, added, removed, deleted, unknown = ( |
513 lookup, modified, added, removed, deleted, unknown = ( |
515 self.dirstate.changes(files, match)) |
514 self.dirstate.changes(files, match)) |
516 |
515 |
517 # are we comparing working dir against its parent? |
516 # are we comparing working dir against its parent? |
596 for f in list: |
595 for f in list: |
597 p = self.wjoin(f) |
596 p = self.wjoin(f) |
598 if os.path.exists(p): |
597 if os.path.exists(p): |
599 self.ui.warn(_("%s still exists!\n") % f) |
598 self.ui.warn(_("%s still exists!\n") % f) |
600 elif self.dirstate.state(f) == 'a': |
599 elif self.dirstate.state(f) == 'a': |
601 self.ui.warn(_("%s never committed!\n") % f) |
|
602 self.dirstate.forget([f]) |
600 self.dirstate.forget([f]) |
603 elif f not in self.dirstate: |
601 elif f not in self.dirstate: |
604 self.ui.warn(_("%s not tracked!\n") % f) |
602 self.ui.warn(_("%s not tracked!\n") % f) |
605 else: |
603 else: |
606 self.dirstate.update([f], "r") |
604 self.dirstate.update([f], "r") |
978 self.ui.status(_("(did you forget to merge?" |
976 self.ui.status(_("(did you forget to merge?" |
979 " use push -f to force)\n")) |
977 " use push -f to force)\n")) |
980 return 1 |
978 return 1 |
981 |
979 |
982 if revs is None: |
980 if revs is None: |
983 cg = self.changegroup(update) |
981 cg = self.changegroup(update, 'push') |
984 else: |
982 else: |
985 cg = self.changegroupsubset(update, revs) |
983 cg = self.changegroupsubset(update, revs, 'push') |
986 return remote.addchangegroup(cg) |
984 return remote.addchangegroup(cg) |
987 |
985 |
988 def changegroupsubset(self, bases, heads): |
986 def changegroupsubset(self, bases, heads, source): |
989 """This function generates a changegroup consisting of all the nodes |
987 """This function generates a changegroup consisting of all the nodes |
990 that are descendents of any of the bases, and ancestors of any of |
988 that are descendents of any of the bases, and ancestors of any of |
991 the heads. |
989 the heads. |
992 |
990 |
993 It is fairly complex as determining which filenodes and which |
991 It is fairly complex as determining which filenodes and which |
994 manifest nodes need to be included for the changeset to be complete |
992 manifest nodes need to be included for the changeset to be complete |
995 is non-trivial. |
993 is non-trivial. |
996 |
994 |
997 Another wrinkle is doing the reverse, figuring out which changeset in |
995 Another wrinkle is doing the reverse, figuring out which changeset in |
998 the changegroup a particular filenode or manifestnode belongs to.""" |
996 the changegroup a particular filenode or manifestnode belongs to.""" |
|
997 |
|
998 self.hook('preoutgoing', throw=True, source=source) |
999 |
999 |
1000 # Set up some initial variables |
1000 # Set up some initial variables |
1001 # Make it easy to refer to self.changelog |
1001 # Make it easy to refer to self.changelog |
1002 cl = self.changelog |
1002 cl = self.changelog |
1003 # msng is short for missing - compute the list of changesets in this |
1003 # msng is short for missing - compute the list of changesets in this |
1247 # Don't need this anymore, toss it to free memory. |
1247 # Don't need this anymore, toss it to free memory. |
1248 del msng_filenode_set[fname] |
1248 del msng_filenode_set[fname] |
1249 # Signal that no more groups are left. |
1249 # Signal that no more groups are left. |
1250 yield struct.pack(">l", 0) |
1250 yield struct.pack(">l", 0) |
1251 |
1251 |
|
1252 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source) |
|
1253 |
1252 return util.chunkbuffer(gengroup()) |
1254 return util.chunkbuffer(gengroup()) |
1253 |
1255 |
1254 def changegroup(self, basenodes): |
1256 def changegroup(self, basenodes, source): |
1255 """Generate a changegroup of all nodes that we have that a recipient |
1257 """Generate a changegroup of all nodes that we have that a recipient |
1256 doesn't. |
1258 doesn't. |
1257 |
1259 |
1258 This is much easier than the previous function as we can assume that |
1260 This is much easier than the previous function as we can assume that |
1259 the recipient has any changenode we aren't sending them.""" |
1261 the recipient has any changenode we aren't sending them.""" |
|
1262 |
|
1263 self.hook('preoutgoing', throw=True, source=source) |
|
1264 |
1260 cl = self.changelog |
1265 cl = self.changelog |
1261 nodes = cl.nodesbetween(basenodes, None)[0] |
1266 nodes = cl.nodesbetween(basenodes, None)[0] |
1262 revset = dict.fromkeys([cl.rev(n) for n in nodes]) |
1267 revset = dict.fromkeys([cl.rev(n) for n in nodes]) |
1263 |
1268 |
1264 def identity(x): |
1269 def identity(x): |
1383 |
1392 |
1384 self.ui.status(_("added %d changesets" |
1393 self.ui.status(_("added %d changesets" |
1385 " with %d changes to %d files%s\n") |
1394 " with %d changes to %d files%s\n") |
1386 % (changesets, revisions, files, heads)) |
1395 % (changesets, revisions, files, heads)) |
1387 |
1396 |
|
1397 self.hook('pretxnchangegroup', throw=True, |
|
1398 node=hex(self.changelog.node(cor+1))) |
|
1399 |
1388 tr.close() |
1400 tr.close() |
1389 |
1401 |
1390 if changesets > 0: |
1402 if changesets > 0: |
1391 if not self.hook("changegroup", |
1403 self.hook("changegroup", node=hex(self.changelog.node(cor+1))) |
1392 node=hex(self.changelog.node(cor+1))): |
|
1393 self.ui.warn(_("abort: changegroup hook returned failure!\n")) |
|
1394 return 1 |
|
1395 |
1404 |
1396 for i in range(cor + 1, cnr + 1): |
1405 for i in range(cor + 1, cnr + 1): |
1397 self.hook("incoming", node=hex(self.changelog.node(i))) |
1406 self.hook("incoming", node=hex(self.changelog.node(i))) |
1398 |
|
1399 return |
|
1400 |
1407 |
1401 def update(self, node, allow=False, force=False, choose=None, |
1408 def update(self, node, allow=False, force=False, choose=None, |
1402 moddirstate=True, forcemerge=False, wlock=None): |
1409 moddirstate=True, forcemerge=False, wlock=None): |
1403 pl = self.dirstate.parents() |
1410 pl = self.dirstate.parents() |
1404 if not force and pl[1] != nullid: |
1411 if not force and pl[1] != nullid: |