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() |
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: |