275 self.dirty = 0 |
275 self.dirty = 0 |
276 self.ui = ui |
276 self.ui = ui |
277 self.map = None |
277 self.map = None |
278 self.pl = None |
278 self.pl = None |
279 self.copies = {} |
279 self.copies = {} |
|
280 self.ignorefunc = None |
|
281 |
|
282 def wjoin(self, f): |
|
283 return os.path.join(self.root, f) |
|
284 |
|
285 def ignore(self, f): |
|
286 if not self.ignorefunc: |
|
287 bigpat = [] |
|
288 try: |
|
289 l = file(self.wjoin(".hgignore")) |
|
290 for pat in l: |
|
291 if pat != "\n": |
|
292 p = util.pconvert(pat[:-1]) |
|
293 try: |
|
294 r = re.compile(p) |
|
295 except: |
|
296 self.ui.warn("ignoring invalid ignore" |
|
297 + " regular expression '%s'\n" % p) |
|
298 else: |
|
299 bigpat.append(util.pconvert(pat[:-1])) |
|
300 except IOError: pass |
|
301 |
|
302 if bigpat: |
|
303 s = "(?:%s)" % (")|(?:".join(bigpat)) |
|
304 r = re.compile(s) |
|
305 self.ignorefunc = r.search |
|
306 else: |
|
307 self.ignorefunc = util.never |
|
308 |
|
309 return self.ignorefunc(f) |
280 |
310 |
281 def __del__(self): |
311 def __del__(self): |
282 if self.dirty: |
312 if self.dirty: |
283 self.write() |
313 self.write() |
284 |
314 |
332 self.map[f] = e[:4] |
366 self.map[f] = e[:4] |
333 pos += l |
367 pos += l |
334 |
368 |
335 def copy(self, source, dest): |
369 def copy(self, source, dest): |
336 self.read() |
370 self.read() |
337 self.dirty = 1 |
371 self.markdirty() |
338 self.copies[dest] = source |
372 self.copies[dest] = source |
339 |
373 |
340 def copied(self, file): |
374 def copied(self, file): |
341 return self.copies.get(file, None) |
375 return self.copies.get(file, None) |
342 |
376 |
347 r marked for removal |
381 r marked for removal |
348 a marked for addition''' |
382 a marked for addition''' |
349 |
383 |
350 if not files: return |
384 if not files: return |
351 self.read() |
385 self.read() |
352 self.dirty = 1 |
386 self.markdirty() |
353 for f in files: |
387 for f in files: |
354 if state == "r": |
388 if state == "r": |
355 self.map[f] = ('r', 0, 0, 0) |
389 self.map[f] = ('r', 0, 0, 0) |
356 else: |
390 else: |
357 s = os.stat(os.path.join(self.root, f)) |
391 s = os.stat(os.path.join(self.root, f)) |
358 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime) |
392 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime) |
359 |
393 |
360 def forget(self, files): |
394 def forget(self, files): |
361 if not files: return |
395 if not files: return |
362 self.read() |
396 self.read() |
363 self.dirty = 1 |
397 self.markdirty() |
364 for f in files: |
398 for f in files: |
365 try: |
399 try: |
366 del self.map[f] |
400 del self.map[f] |
367 except KeyError: |
401 except KeyError: |
368 self.ui.warn("not in dirstate: %s!\n" % f) |
402 self.ui.warn("not in dirstate: %s!\n" % f) |
369 pass |
403 pass |
370 |
404 |
371 def clear(self): |
405 def clear(self): |
372 self.map = {} |
406 self.map = {} |
373 self.dirty = 1 |
407 self.markdirty() |
374 |
408 |
375 def write(self): |
409 def write(self): |
376 st = self.opener("dirstate", "w") |
410 st = self.opener("dirstate", "w") |
377 st.write("".join(self.pl)) |
411 st.write("".join(self.pl)) |
378 for f, e in self.map.items(): |
412 for f, e in self.map.items(): |
381 f = f + "\0" + c |
415 f = f + "\0" + c |
382 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f)) |
416 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f)) |
383 st.write(e + f) |
417 st.write(e + f) |
384 self.dirty = 0 |
418 self.dirty = 0 |
385 |
419 |
386 def changes(self, files, ignore): |
420 def walk(self, files = None, match = util.always): |
387 self.read() |
421 self.read() |
388 dc = self.map.copy() |
422 dc = self.map.copy() |
389 lookup, changed, added, unknown = [], [], [], [] |
423 # walk all files by default |
390 |
|
391 # compare all files by default |
|
392 if not files: files = [self.root] |
424 if not files: files = [self.root] |
393 |
425 def traverse(): |
394 # recursive generator of all files listed |
|
395 def walk(files): |
|
396 for f in util.unique(files): |
426 for f in util.unique(files): |
397 f = os.path.join(self.root, f) |
427 f = os.path.join(self.root, f) |
398 if os.path.isdir(f): |
428 if os.path.isdir(f): |
399 for dir, subdirs, fl in os.walk(f): |
429 for dir, subdirs, fl in os.walk(f): |
400 d = dir[len(self.root) + 1:] |
430 d = dir[len(self.root) + 1:] |
|
431 if d == '.hg': |
|
432 subdirs[:] = [] |
|
433 continue |
401 for sd in subdirs: |
434 for sd in subdirs: |
402 if ignore(os.path.join(d, sd +'/')): |
435 ds = os.path.join(d, sd +'/') |
|
436 if self.ignore(ds) or not match(ds): |
403 subdirs.remove(sd) |
437 subdirs.remove(sd) |
404 for fn in fl: |
438 for fn in fl: |
405 fn = util.pconvert(os.path.join(d, fn)) |
439 fn = util.pconvert(os.path.join(d, fn)) |
406 yield fn |
440 yield 'f', fn |
407 else: |
441 else: |
408 yield f[len(self.root) + 1:] |
442 yield 'f', f[len(self.root) + 1:] |
409 |
443 |
410 for k in dc.keys(): |
444 for k in dc.keys(): |
411 yield k |
445 yield 'm', k |
412 |
446 |
413 for fn in util.unique(walk(files)): |
447 # yield only files that match: all in dirstate, others only if |
|
448 # not in .hgignore |
|
449 |
|
450 for src, fn in util.unique(traverse()): |
|
451 if fn in dc: |
|
452 del dc[fn] |
|
453 elif self.ignore(fn): |
|
454 continue |
|
455 if match(fn): |
|
456 yield src, fn |
|
457 |
|
458 def changes(self, files = None, match = util.always): |
|
459 self.read() |
|
460 dc = self.map.copy() |
|
461 lookup, changed, added, unknown = [], [], [], [] |
|
462 |
|
463 for src, fn in self.walk(files, match): |
414 try: s = os.stat(os.path.join(self.root, fn)) |
464 try: s = os.stat(os.path.join(self.root, fn)) |
415 except: continue |
465 except: continue |
416 |
466 |
417 if fn in dc: |
467 if fn in dc: |
418 c = dc[fn] |
468 c = dc[fn] |
427 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100: |
477 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100: |
428 changed.append(fn) |
478 changed.append(fn) |
429 elif c[1] != s.st_mode or c[3] != s.st_mtime: |
479 elif c[1] != s.st_mode or c[3] != s.st_mtime: |
430 lookup.append(fn) |
480 lookup.append(fn) |
431 else: |
481 else: |
432 if not ignore(fn): unknown.append(fn) |
482 if match(fn): unknown.append(fn) |
433 |
483 |
434 return (lookup, changed, added, dc.keys(), unknown) |
484 return (lookup, changed, added, filter(match, dc.keys()), unknown) |
435 |
485 |
436 # used to avoid circular references so destructors work |
486 # used to avoid circular references so destructors work |
437 def opener(base): |
487 def opener(base): |
438 p = base |
488 p = base |
439 def o(path, mode="r"): |
489 def o(path, mode="r"): |
491 |
541 |
492 self.opener = opener(self.path) |
542 self.opener = opener(self.path) |
493 self.wopener = opener(self.root) |
543 self.wopener = opener(self.root) |
494 self.manifest = manifest(self.opener) |
544 self.manifest = manifest(self.opener) |
495 self.changelog = changelog(self.opener) |
545 self.changelog = changelog(self.opener) |
496 self.ignorefunc = None |
|
497 self.tagscache = None |
546 self.tagscache = None |
498 self.nodetagscache = None |
547 self.nodetagscache = None |
499 |
548 |
500 if not self.remote: |
549 if not self.remote: |
501 self.dirstate = dirstate(self.opener, ui, self.root) |
550 self.dirstate = dirstate(self.opener, ui, self.root) |
502 try: |
551 try: |
503 self.ui.readconfig(self.opener("hgrc")) |
552 self.ui.readconfig(self.opener("hgrc")) |
504 except IOError: pass |
553 except IOError: pass |
505 |
|
506 def ignore(self, f): |
|
507 if not self.ignorefunc: |
|
508 bigpat = ["^.hg/$"] |
|
509 try: |
|
510 l = file(self.wjoin(".hgignore")) |
|
511 for pat in l: |
|
512 if pat != "\n": |
|
513 p = util.pconvert(pat[:-1]) |
|
514 try: |
|
515 r = re.compile(p) |
|
516 except: |
|
517 self.ui.warn("ignoring invalid ignore" |
|
518 + " regular expression '%s'\n" % p) |
|
519 else: |
|
520 bigpat.append(util.pconvert(pat[:-1])) |
|
521 except IOError: pass |
|
522 |
|
523 s = "(?:%s)" % (")|(?:".join(bigpat)) |
|
524 r = re.compile(s) |
|
525 self.ignorefunc = r.search |
|
526 |
|
527 return self.ignorefunc(f) |
|
528 |
554 |
529 def hook(self, name, **args): |
555 def hook(self, name, **args): |
530 s = self.ui.config("hooks", name) |
556 s = self.ui.config("hooks", name) |
531 if s: |
557 if s: |
532 self.ui.note("running hook %s: %s\n" % (name, s)) |
558 self.ui.note("running hook %s: %s\n" % (name, s)) |
736 elif s == 'r': |
762 elif s == 'r': |
737 remove.append(f) |
763 remove.append(f) |
738 else: |
764 else: |
739 self.ui.warn("%s not tracked!\n" % f) |
765 self.ui.warn("%s not tracked!\n" % f) |
740 else: |
766 else: |
741 (c, a, d, u) = self.changes(None, None) |
767 (c, a, d, u) = self.changes() |
742 commit = c + a |
768 commit = c + a |
743 remove = d |
769 remove = d |
744 |
770 |
745 if not commit and not remove: |
771 if not commit and not remove: |
746 self.ui.status("nothing changed\n") |
772 self.ui.status("nothing changed\n") |
813 self.dirstate.forget(remove) |
839 self.dirstate.forget(remove) |
814 |
840 |
815 if not self.hook("commit", node=hex(n)): |
841 if not self.hook("commit", node=hex(n)): |
816 return 1 |
842 return 1 |
817 |
843 |
818 def changes(self, node1, node2, files=None): |
844 def walk(self, node = None, files = [], match = util.always): |
|
845 if node: |
|
846 for fn in self.manifest.read(self.changelog.read(node)[0]): |
|
847 yield 'm', fn |
|
848 else: |
|
849 for src, fn in self.dirstate.walk(files, match): |
|
850 yield src, fn |
|
851 |
|
852 def changes(self, node1 = None, node2 = None, files = [], |
|
853 match = util.always): |
819 mf2, u = None, [] |
854 mf2, u = None, [] |
820 |
855 |
821 def fcmp(fn, mf): |
856 def fcmp(fn, mf): |
822 t1 = self.wfile(fn).read() |
857 t1 = self.wfile(fn).read() |
823 t2 = self.file(fn).revision(mf[fn]) |
858 t2 = self.file(fn).revision(mf[fn]) |
824 return cmp(t1, t2) |
859 return cmp(t1, t2) |
825 |
860 |
|
861 def mfmatches(node): |
|
862 mf = dict(self.manifest.read(node)) |
|
863 for fn in mf.keys(): |
|
864 if not match(fn): |
|
865 del mf[fn] |
|
866 return mf |
|
867 |
826 # are we comparing the working directory? |
868 # are we comparing the working directory? |
827 if not node2: |
869 if not node2: |
828 l, c, a, d, u = self.dirstate.changes(files, self.ignore) |
870 l, c, a, d, u = self.dirstate.changes(files, match) |
829 |
871 |
830 # are we comparing working dir against its parent? |
872 # are we comparing working dir against its parent? |
831 if not node1: |
873 if not node1: |
832 if l: |
874 if l: |
833 # do a full compare of any files that might have changed |
875 # do a full compare of any files that might have changed |
834 change = self.changelog.read(self.dirstate.parents()[0]) |
876 change = self.changelog.read(self.dirstate.parents()[0]) |
835 mf2 = self.manifest.read(change[0]) |
877 mf2 = mfmatches(change[0]) |
836 for f in l: |
878 for f in l: |
837 if fcmp(f, mf2): |
879 if fcmp(f, mf2): |
838 c.append(f) |
880 c.append(f) |
839 |
881 |
840 for l in c, a, d, u: |
882 for l in c, a, d, u: |
845 # are we comparing working dir against non-tip? |
887 # are we comparing working dir against non-tip? |
846 # generate a pseudo-manifest for the working dir |
888 # generate a pseudo-manifest for the working dir |
847 if not node2: |
889 if not node2: |
848 if not mf2: |
890 if not mf2: |
849 change = self.changelog.read(self.dirstate.parents()[0]) |
891 change = self.changelog.read(self.dirstate.parents()[0]) |
850 mf2 = self.manifest.read(change[0]).copy() |
892 mf2 = mfmatches(change[0]) |
851 for f in a + c + l: |
893 for f in a + c + l: |
852 mf2[f] = "" |
894 mf2[f] = "" |
853 for f in d: |
895 for f in d: |
854 if f in mf2: del mf2[f] |
896 if f in mf2: del mf2[f] |
855 else: |
897 else: |
856 change = self.changelog.read(node2) |
898 change = self.changelog.read(node2) |
857 mf2 = self.manifest.read(change[0]) |
899 mf2 = mfmatches(change[0]) |
858 |
900 |
859 # flush lists from dirstate before comparing manifests |
901 # flush lists from dirstate before comparing manifests |
860 c, a = [], [] |
902 c, a = [], [] |
861 |
903 |
862 change = self.changelog.read(node1) |
904 change = self.changelog.read(node1) |
863 mf1 = self.manifest.read(change[0]).copy() |
905 mf1 = mfmatches(change[0]) |
864 |
906 |
865 for fn in mf2: |
907 for fn in mf2: |
866 if mf1.has_key(fn): |
908 if mf1.has_key(fn): |
867 if mf1[fn] != mf2[fn]: |
909 if mf1[fn] != mf2[fn]: |
868 if mf2[fn] != "" or fcmp(fn, mf1): |
910 if mf2[fn] != "" or fcmp(fn, mf1): |
883 p = self.wjoin(f) |
925 p = self.wjoin(f) |
884 if not os.path.exists(p): |
926 if not os.path.exists(p): |
885 self.ui.warn("%s does not exist!\n" % f) |
927 self.ui.warn("%s does not exist!\n" % f) |
886 elif not os.path.isfile(p): |
928 elif not os.path.isfile(p): |
887 self.ui.warn("%s not added: mercurial only supports files currently\n" % f) |
929 self.ui.warn("%s not added: mercurial only supports files currently\n" % f) |
888 elif self.dirstate.state(f) == 'n': |
930 elif self.dirstate.state(f) in 'an': |
889 self.ui.warn("%s already tracked!\n" % f) |
931 self.ui.warn("%s already tracked!\n" % f) |
890 else: |
932 else: |
891 self.dirstate.update([f], "a") |
933 self.dirstate.update([f], "a") |
892 |
934 |
893 def forget(self, list): |
935 def forget(self, list): |
1266 m2 = self.manifest.read(m2n) |
1308 m2 = self.manifest.read(m2n) |
1267 mf2 = self.manifest.readflags(m2n) |
1309 mf2 = self.manifest.readflags(m2n) |
1268 ma = self.manifest.read(man) |
1310 ma = self.manifest.read(man) |
1269 mfa = self.manifest.readflags(man) |
1311 mfa = self.manifest.readflags(man) |
1270 |
1312 |
1271 (c, a, d, u) = self.changes(None, None) |
1313 (c, a, d, u) = self.changes() |
1272 |
1314 |
1273 # is this a jump, or a merge? i.e. is there a linear path |
1315 # is this a jump, or a merge? i.e. is there a linear path |
1274 # from p1 to p2? |
1316 # from p1 to p2? |
1275 linear_path = (pa == p1 or pa == p2) |
1317 linear_path = (pa == p1 or pa == p2) |
1276 |
1318 |