comparison mercurial/localrepo.py @ 1782:b9671b41e360

merge with crew
author Benoit Boissinot <benoit.boissinot@ens-lyon.org>
date Tue, 21 Feb 2006 16:46:38 +0100
parents 284fc722c342 fdfe89a3962d
children 2e0a288ca93e e431344e604c
comparison
equal deleted inserted replaced
1781:284fc722c342 1782:b9671b41e360
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
284 else: 309 else:
285 update_dirstate = 0 310 update_dirstate = 0
286 311
287 if not wlock: 312 if not wlock:
288 wlock = self.wlock() 313 wlock = self.wlock()
289 lock = self.lock() 314 l = self.lock()
290 tr = self.transaction() 315 tr = self.transaction()
291 mm = m1.copy() 316 mm = m1.copy()
292 mfm = mf1.copy() 317 mfm = mf1.copy()
293 linkrev = self.changelog.count() 318 linkrev = self.changelog.count()
294 for f in files: 319 for f in files:
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")
370 378
371 if not commit and not remove and not force and p2 == nullid: 379 if not commit and not remove and not force and p2 == nullid:
372 self.ui.status(_("nothing changed\n")) 380 self.ui.status(_("nothing changed\n"))
373 return None 381 return None
374 382
375 if not self.hook("precommit"): 383 xp1 = hex(p1)
376 return None 384 if p2 == nullid: xp2 = ''
385 else: xp2 = hex(p2)
386
387 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
377 388
378 if not wlock: 389 if not wlock:
379 wlock = self.wlock() 390 wlock = self.wlock()
380 lock = self.lock() 391 l = self.lock()
381 tr = self.transaction() 392 tr = self.transaction()
382 393
383 # check in files 394 # check in files
384 new = {} 395 new = {}
385 linkrev = self.changelog.count() 396 linkrev = self.changelog.count()
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")
931 929
932 # this is the set of all roots we have to push 930 # this is the set of all roots we have to push
933 return subset 931 return subset
934 932
935 def pull(self, remote, heads=None): 933 def pull(self, remote, heads=None):
936 lock = self.lock() 934 l = self.lock()
937 935
938 # if we have an empty repo, fetch everything 936 # if we have an empty repo, fetch everything
939 if self.changelog.tip() == nullid: 937 if self.changelog.tip() == nullid:
940 self.ui.status(_("requesting all changes\n")) 938 self.ui.status(_("requesting all changes\n"))
941 fetch = [nullid] 939 fetch = [nullid]
945 if not fetch: 943 if not fetch:
946 self.ui.status(_("no changes found\n")) 944 self.ui.status(_("no changes found\n"))
947 return 1 945 return 1
948 946
949 if heads is None: 947 if heads is None:
950 cg = remote.changegroup(fetch) 948 cg = remote.changegroup(fetch, 'pull')
951 else: 949 else:
952 cg = remote.changegroupsubset(fetch, heads) 950 cg = remote.changegroupsubset(fetch, heads, 'pull')
953 return self.addchangegroup(cg) 951 return self.addchangegroup(cg)
954 952
955 def push(self, remote, force=False, revs=None): 953 def push(self, remote, force=False, revs=None):
956 lock = remote.lock() 954 lock = remote.lock()
957 955
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):
1306 lookup = lookuprevlink_func(filerevlog) 1311 lookup = lookuprevlink_func(filerevlog)
1307 for chnk in filerevlog.group(nodeiter, lookup): 1312 for chnk in filerevlog.group(nodeiter, lookup):
1308 yield chnk 1313 yield chnk
1309 1314
1310 yield struct.pack(">l", 0) 1315 yield struct.pack(">l", 0)
1316 self.hook('outgoing', node=hex(nodes[0]), source=source)
1311 1317
1312 return util.chunkbuffer(gengroup()) 1318 return util.chunkbuffer(gengroup())
1313 1319
1314 def addchangegroup(self, source): 1320 def addchangegroup(self, source):
1315 1321
1341 def revmap(x): 1347 def revmap(x):
1342 return self.changelog.rev(x) 1348 return self.changelog.rev(x)
1343 1349
1344 if not source: 1350 if not source:
1345 return 1351 return
1352
1353 self.hook('prechangegroup', throw=True)
1354
1346 changesets = files = revisions = 0 1355 changesets = files = revisions = 0
1347 1356
1348 tr = self.transaction() 1357 tr = self.transaction()
1349 1358
1350 oldheads = len(self.changelog.heads()) 1359 oldheads = len(self.changelog.heads())
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: