comparison mercurial/hg.py @ 220:3113a94c1bff

change dircache into dirstate -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 change dircache into dirstate The dircache now tracks adds and removes directly diffdir now makes a proper distinction between added and unknown files Add a forget command to unadd files Undo tries to fix up the state of just the files in the undone commit Add and remove complain about files that are not in a proper state of existence manifest hash: ca0cd6abc5e119670acf11a54fefa2bc986eadf3 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.0 (GNU/Linux) iD8DBQFCn7TRywK+sNU5EO8RAhnSAKC2oHg1HJOCGsvpUYj4SBEq0HmuJQCgr5gl jEBTs5AFD5IhF73YAgrcnkE= =prQA -----END PGP SIGNATURE-----
author mpm@selenic.com
date Thu, 02 Jun 2005 17:39:29 -0800
parents e6d6497a6331
children 87484f627422
comparison
equal deleted inserted replaced
219:8ff4532376a4 220:3113a94c1bff
147 list.sort() 147 list.sort()
148 l = [hex(manifest), user, date] + list + ["", desc] 148 l = [hex(manifest), user, date] + list + ["", desc]
149 text = "\n".join(l) 149 text = "\n".join(l)
150 return self.addrevision(text, transaction, self.count(), p1, p2) 150 return self.addrevision(text, transaction, self.count(), p1, p2)
151 151
152 class dircache: 152 class dirstate:
153 def __init__(self, opener, ui): 153 def __init__(self, opener, ui):
154 self.opener = opener 154 self.opener = opener
155 self.dirty = 0 155 self.dirty = 0
156 self.ui = ui 156 self.ui = ui
157 self.map = None 157 self.map = None
158
158 def __del__(self): 159 def __del__(self):
159 if self.dirty: self.write() 160 if self.dirty:
161 self.write()
162
160 def __getitem__(self, key): 163 def __getitem__(self, key):
161 try: 164 try:
162 return self.map[key] 165 return self.map[key]
163 except TypeError: 166 except TypeError:
164 self.read() 167 self.read()
165 return self[key] 168 return self[key]
166 169
170 def __contains__(self, key):
171 if not self.map: self.read()
172 return key in self.map
173
174 def state(self, key):
175 try:
176 return self[key][0]
177 except KeyError:
178 return "?"
179
167 def read(self): 180 def read(self):
168 if self.map is not None: return self.map 181 if self.map is not None: return self.map
169 182
170 self.map = {} 183 self.map = {}
171 try: 184 try:
172 st = self.opener("dircache").read() 185 st = self.opener("dirstate").read()
173 except: return 186 except: return
174 187
175 pos = 0 188 pos = 0
176 while pos < len(st): 189 while pos < len(st):
177 e = struct.unpack(">llll", st[pos:pos+16]) 190 e = struct.unpack(">cllll", st[pos:pos+17])
178 l = e[3] 191 l = e[4]
179 pos += 16 192 pos += 17
180 f = st[pos:pos + l] 193 f = st[pos:pos + l]
181 self.map[f] = e[:3] 194 self.map[f] = e[:4]
182 pos += l 195 pos += l
183 196
184 def update(self, files): 197 def update(self, files, state):
198 ''' current states:
199 n normal
200 i invalid
201 r marked for removal
202 a marked for addition'''
203
185 if not files: return 204 if not files: return
186 self.read() 205 self.read()
187 self.dirty = 1 206 self.dirty = 1
188 for f in files: 207 for f in files:
189 try: 208 if state == "r":
190 s = os.stat(f) 209 self.map[f] = ('r', 0, 0, 0)
191 self.map[f] = (s.st_mode, s.st_size, s.st_mtime) 210 else:
192 except IOError: 211 try:
193 self.remove(f) 212 s = os.stat(f)
194 213 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
195 def taint(self, files): 214 except OSError:
196 if not files: return 215 if state != "i": raise
197 self.read() 216 self.map[f] = ('r', 0, 0, 0)
198 self.dirty = 1 217
199 for f in files: 218 def forget(self, files):
200 self.map[f] = (0, -1, 0)
201
202 def remove(self, files):
203 if not files: return 219 if not files: return
204 self.read() 220 self.read()
205 self.dirty = 1 221 self.dirty = 1
206 for f in files: 222 for f in files:
207 try: 223 try:
208 del self.map[f] 224 del self.map[f]
209 except KeyError: 225 except KeyError:
210 self.ui.warn("Not in dircache: %s\n" % f) 226 self.ui.warn("not in dirstate: %s!\n" % f)
211 pass 227 pass
212 228
213 def clear(self): 229 def clear(self):
214 self.map = {} 230 self.map = {}
215 self.dirty = 1 231 self.dirty = 1
216 232
217 def write(self): 233 def write(self):
218 st = self.opener("dircache", "w") 234 st = self.opener("dirstate", "w")
219 for f, e in self.map.items(): 235 for f, e in self.map.items():
220 e = struct.pack(">llll", e[0], e[1], e[2], len(f)) 236 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
221 st.write(e + f) 237 st.write(e + f)
222 self.dirty = 0 238 self.dirty = 0
223 239
224 def copy(self): 240 def copy(self):
225 self.read() 241 self.read()
278 self.changelog = changelog(self.opener) 294 self.changelog = changelog(self.opener)
279 self.ignorelist = None 295 self.ignorelist = None
280 self.tags = None 296 self.tags = None
281 297
282 if not self.remote: 298 if not self.remote:
283 self.dircache = dircache(self.opener, ui) 299 self.dirstate = dirstate(self.opener, ui)
284 try: 300 try:
285 self.current = bin(self.opener("current").read()) 301 self.current = bin(self.opener("current").read())
286 except IOError: 302 except IOError:
287 self.current = None 303 self.current = None
288 304
338 self.ui.warn("no interrupted transaction available\n") 354 self.ui.warn("no interrupted transaction available\n")
339 355
340 def undo(self): 356 def undo(self):
341 self.lock() 357 self.lock()
342 if os.path.exists(self.join("undo")): 358 if os.path.exists(self.join("undo")):
359 f = self.changelog.read(self.changelog.tip())[3]
343 self.ui.status("attempting to rollback last transaction\n") 360 self.ui.status("attempting to rollback last transaction\n")
344 rollback(self.opener, self.join("undo")) 361 rollback(self.opener, self.join("undo"))
345 self.manifest = manifest(self.opener) 362 self.manifest = manifest(self.opener)
346 self.changelog = changelog(self.opener) 363 self.changelog = changelog(self.opener)
347 364
348 self.ui.status("discarding dircache\n") 365 self.ui.status("discarding dirstate\n")
349 node = self.changelog.tip() 366 node = self.changelog.tip()
350 mf = self.changelog.read(node)[0]
351 mm = self.manifest.read(mf)
352 f = mm.keys()
353 f.sort() 367 f.sort()
354 368
355 self.setcurrent(node) 369 self.setcurrent(node)
356 self.dircache.clear() 370 self.dirstate.update(f, 'i')
357 self.dircache.taint(f)
358 371
359 else: 372 else:
360 self.ui.warn("no undo information available\n") 373 self.ui.warn("no undo information available\n")
361 374
362 def lock(self, wait = 1): 375 def lock(self, wait = 1):
387 400
388 mnode = self.manifest.add(mmap, tr, linkrev, pchange[0]) 401 mnode = self.manifest.add(mmap, tr, linkrev, pchange[0])
389 n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, ) 402 n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, )
390 tr.close() 403 tr.close()
391 self.setcurrent(n) 404 self.setcurrent(n)
392 self.dircache.clear() 405 self.dirstate.clear()
393 self.dircache.update(mmap) 406 self.dirstate.update(mmap.keys(), "n")
394 407
395 def commit(self, parent, update = None, text = ""): 408 def commit(self, parent, files = None, text = ""):
396 self.lock() 409 self.lock()
397 try: 410
398 remove = [ l[:-1] for l in self.opener("to-remove") ] 411 commit = []
399 os.unlink(self.join("to-remove")) 412 remove = []
400 413 if files:
401 except IOError: 414 for f in files:
402 remove = [] 415 s = self.dirstate.state(f)
403 416 if s in 'cai':
404 if update == None: 417 commit.append(f)
405 update = self.diffdir(self.root, parent)[0] 418 elif s == 'r':
406 419 remove.append(f)
407 if not update: 420 else:
421 self.warn("%s not tracked!\n")
422 else:
423 (c, a, d, u) = self.diffdir(self.root, parent)
424 commit = c + a
425 remove = d
426
427 if not commit and not remove:
408 self.ui.status("nothing changed\n") 428 self.ui.status("nothing changed\n")
409 return 429 return
410 430
411 tr = self.transaction() 431 tr = self.transaction()
412 432
413 # check in files 433 # check in files
414 new = {} 434 new = {}
415 linkrev = self.changelog.count() 435 linkrev = self.changelog.count()
416 update.sort() 436 commit.sort()
417 for f in update: 437 for f in commit:
418 self.ui.note(f + "\n") 438 self.ui.note(f + "\n")
419 try: 439 try:
420 t = file(f).read() 440 t = file(f).read()
421 except IOError: 441 except IOError:
422 remove.append(f) 442 self.warn("trouble committing %s!\n" % f)
423 continue 443 raise
444
424 r = self.file(f) 445 r = self.file(f)
425 new[f] = r.add(t, tr, linkrev) 446 new[f] = r.add(t, tr, linkrev)
426 447
427 # update manifest 448 # update manifest
428 mmap = self.manifest.read(self.manifest.tip()) 449 mmap = self.manifest.read(self.manifest.tip())
442 463
443 n = self.changelog.add(mnode, new, edittext, tr) 464 n = self.changelog.add(mnode, new, edittext, tr)
444 tr.close() 465 tr.close()
445 466
446 self.setcurrent(n) 467 self.setcurrent(n)
447 self.dircache.update(new) 468 self.dirstate.update(new, "n")
448 self.dircache.remove(remove) 469 self.dirstate.forget(remove)
449 470
450 def checkout(self, node): 471 def checkout(self, node):
451 # checkout is really dumb at the moment 472 # checkout is really dumb at the moment
452 # it ought to basically merge 473 # it ought to basically merge
453 change = self.changelog.read(node) 474 change = self.changelog.read(node)
463 except IOError: 484 except IOError:
464 os.makedirs(os.path.dirname(f)) 485 os.makedirs(os.path.dirname(f))
465 file(f, "w").write(t) 486 file(f, "w").write(t)
466 487
467 self.setcurrent(node) 488 self.setcurrent(node)
468 self.dircache.clear() 489 self.dirstate.clear()
469 self.dircache.update([f for f,n in l]) 490 self.dirstate.update([f for f,n in l], "n")
470 491
471 def diffdir(self, path, changeset): 492 def diffdir(self, path, changeset):
472 changed = [] 493 changed = []
494 added = []
495 unknown = []
473 mf = {} 496 mf = {}
474 added = []
475 497
476 if changeset: 498 if changeset:
477 change = self.changelog.read(changeset) 499 change = self.changelog.read(changeset)
478 mf = self.manifest.read(change[0]) 500 mf = self.manifest.read(change[0])
479 501
480 if changeset == self.current: 502 if changeset == self.current:
481 dc = self.dircache.copy() 503 dc = self.dirstate.copy()
482 else: 504 else:
483 dc = dict.fromkeys(mf) 505 dc = dict.fromkeys(mf)
484 506
485 def fcmp(fn): 507 def fcmp(fn):
486 t1 = file(os.path.join(self.root, fn)).read() 508 t1 = file(os.path.join(self.root, fn)).read()
496 try: s = os.stat(os.path.join(self.root, fn)) 518 try: s = os.stat(os.path.join(self.root, fn))
497 except: continue 519 except: continue
498 if fn in dc: 520 if fn in dc:
499 c = dc[fn] 521 c = dc[fn]
500 del dc[fn] 522 del dc[fn]
501 if not c or c[1] < 0: 523 if not c:
502 if fcmp(fn): 524 if fcmp(fn):
503 changed.append(fn) 525 changed.append(fn)
504 elif c[1] != s.st_size: 526 if c[0] == 'i':
527 if fn not in mf:
528 added.append(fn)
529 elif fcmp(fn):
530 changed.append(fn)
531 elif c[0] == 'a':
532 added.append(fn)
533 elif c[0] == 'r':
534 unknown.append(fn)
535 elif c[2] != s.st_size:
505 changed.append(fn) 536 changed.append(fn)
506 elif c[0] != s.st_mode or c[2] != s.st_mtime: 537 elif c[1] != s.st_mode or c[3] != s.st_mtime:
507 if fcmp(fn): 538 if fcmp(fn):
508 changed.append(fn) 539 changed.append(fn)
509 else: 540 else:
510 if self.ignore(fn): continue 541 if self.ignore(fn): continue
511 added.append(fn) 542 unknown.append(fn)
512 543
513 deleted = dc.keys() 544 deleted = dc.keys()
514 deleted.sort() 545 deleted.sort()
515 546
516 return (changed, added, deleted) 547 return (changed, added, deleted, unknown)
517 548
518 def diffrevs(self, node1, node2): 549 def diffrevs(self, node1, node2):
519 changed, added = [], [] 550 changed, added = [], []
520 551
521 change = self.changelog.read(node1) 552 change = self.changelog.read(node1)
535 deleted.sort() 566 deleted.sort()
536 567
537 return (changed, added, deleted) 568 return (changed, added, deleted)
538 569
539 def add(self, list): 570 def add(self, list):
540 self.dircache.taint(list) 571 for f in list:
572 p = os.path.join(self.root, f)
573 if not os.path.isfile(p):
574 self.ui.warn("%s does not exist!\n" % f)
575 elif self.dirstate.state(f) == 'n':
576 self.ui.warn("%s already tracked!\n" % f)
577 else:
578 self.dirstate.update([f], "a")
579
580 def forget(self, list):
581 for f in list:
582 if self.dirstate.state(f) not in 'ai':
583 self.ui.warn("%s not added!\n" % f)
584 else:
585 self.dirstate.forget([f])
541 586
542 def remove(self, list): 587 def remove(self, list):
543 dl = self.opener("to-remove", "a")
544 for f in list: 588 for f in list:
545 dl.write(f + "\n") 589 p = os.path.join(self.root, f)
590 if os.path.isfile(p):
591 self.ui.warn("%s still exists!\n" % f)
592 elif f not in self.dirstate:
593 self.ui.warn("%s not tracked!\n" % f)
594 else:
595 self.dirstate.update([f], "r")
546 596
547 def branches(self, nodes): 597 def branches(self, nodes):
548 if not nodes: nodes = [self.changelog.tip()] 598 if not nodes: nodes = [self.changelog.tip()]
549 b = [] 599 b = []
550 for n in nodes: 600 for n in nodes: