# HG changeset patch # User mpm@selenic.com # Date 1117762769 28800 # Node ID 3113a94c1bff3f166a3f33ec8698b549ed1811e0 # Parent 8ff4532376a41375b54ffade603d524c706606b4 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----- diff -r 8ff4532376a4 -r 3113a94c1bff hg --- a/hg Wed Jun 01 19:19:38 2005 -0800 +++ b/hg Thu Jun 02 17:39:29 2005 -0800 @@ -67,7 +67,7 @@ date2 = time.asctime() if not node1: node1 = repo.current - (c, a, d) = repo.diffdir(repo.root, node1) + (c, a, d, u) = repo.diffdir(repo.root, node1) a = [] # ignore unknown files in repo, by popular request def read(f): return file(os.path.join(repo.root, f)).read() @@ -79,9 +79,7 @@ c, a, d = map(lambda x: filterfiles(x, files), (c, a, d)) for f in c: - to = "" - if mmap.has_key(f): - to = repo.file(f).read(mmap[f]) + to = repo.file(f).read(mmap[f]) tn = read(f) sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f)) for f in a: @@ -132,6 +130,9 @@ elif cmd == "add": repo.add(args) +elif cmd == "forget": + repo.forget(args) + elif cmd == "remove" or cmd == "rm" or cmd == "del" or cmd == "delete": repo.remove(args) @@ -250,7 +251,7 @@ repo.addchangegroup(data) elif cmd == "addremove": - (c, a, d) = repo.diffdir(repo.root, repo.current) + (c, a, d, u) = repo.diffdir(repo.root, repo.current) repo.add(a) repo.remove(d) @@ -355,8 +356,8 @@ print "}" elif cmd == "merge": - (c, a, d) = repo.diffdir(repo.root, repo.current) - if c: + (c, a, d, u) = repo.diffdir(repo.root, repo.current) + if c or a or d: ui.warn("aborting (outstanding changes in working directory)\n") sys.exit(1) diff -r 8ff4532376a4 -r 3113a94c1bff mercurial/commands.py --- a/mercurial/commands.py Wed Jun 01 19:19:38 2005 -0800 +++ b/mercurial/commands.py Thu Jun 02 17:39:29 2005 -0800 @@ -70,8 +70,8 @@ def checkout(ui, repo, changeset=None): '''checkout a given changeset or the current tip''' - (c, a, d) = repo.diffdir(repo.root, repo.current) - if c: + (c, a, d, u) = repo.diffdir(repo.root, repo.current) + if c or a or d: ui.warn("aborting (outstanding changes in working directory)\n") sys.exit(1) @@ -129,12 +129,13 @@ A = added R = removed ? = not tracked''' - (c, a, d) = repo.diffdir(repo.root, repo.current) - (c, a, d) = map(lambda x: relfilter(repo, x), (c, a, d)) + (c, a, d, u) = repo.diffdir(repo.root, repo.current) + (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u)) for f in c: print "C", f - for f in a: print "?", f + for f in a: print "A", f for f in d: print "R", f + for f in u: print "?", f def undo(ui, repo): repo.undo() diff -r 8ff4532376a4 -r 3113a94c1bff mercurial/hg.py --- a/mercurial/hg.py Wed Jun 01 19:19:38 2005 -0800 +++ b/mercurial/hg.py Thu Jun 02 17:39:29 2005 -0800 @@ -149,57 +149,73 @@ text = "\n".join(l) return self.addrevision(text, transaction, self.count(), p1, p2) -class dircache: +class dirstate: def __init__(self, opener, ui): self.opener = opener self.dirty = 0 self.ui = ui self.map = None + def __del__(self): - if self.dirty: self.write() + if self.dirty: + self.write() + def __getitem__(self, key): try: return self.map[key] except TypeError: self.read() return self[key] - + + def __contains__(self, key): + if not self.map: self.read() + return key in self.map + + def state(self, key): + try: + return self[key][0] + except KeyError: + return "?" + def read(self): if self.map is not None: return self.map self.map = {} try: - st = self.opener("dircache").read() + st = self.opener("dirstate").read() except: return pos = 0 while pos < len(st): - e = struct.unpack(">llll", st[pos:pos+16]) - l = e[3] - pos += 16 + e = struct.unpack(">cllll", st[pos:pos+17]) + l = e[4] + pos += 17 f = st[pos:pos + l] - self.map[f] = e[:3] + self.map[f] = e[:4] pos += l - def update(self, files): + def update(self, files, state): + ''' current states: + n normal + i invalid + r marked for removal + a marked for addition''' + if not files: return self.read() self.dirty = 1 for f in files: - try: - s = os.stat(f) - self.map[f] = (s.st_mode, s.st_size, s.st_mtime) - except IOError: - self.remove(f) + if state == "r": + self.map[f] = ('r', 0, 0, 0) + else: + try: + s = os.stat(f) + self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime) + except OSError: + if state != "i": raise + self.map[f] = ('r', 0, 0, 0) - def taint(self, files): - if not files: return - self.read() - self.dirty = 1 - for f in files: - self.map[f] = (0, -1, 0) - - def remove(self, files): + def forget(self, files): if not files: return self.read() self.dirty = 1 @@ -207,7 +223,7 @@ try: del self.map[f] except KeyError: - self.ui.warn("Not in dircache: %s\n" % f) + self.ui.warn("not in dirstate: %s!\n" % f) pass def clear(self): @@ -215,9 +231,9 @@ self.dirty = 1 def write(self): - st = self.opener("dircache", "w") + st = self.opener("dirstate", "w") for f, e in self.map.items(): - e = struct.pack(">llll", e[0], e[1], e[2], len(f)) + e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f)) st.write(e + f) self.dirty = 0 @@ -280,7 +296,7 @@ self.tags = None if not self.remote: - self.dircache = dircache(self.opener, ui) + self.dirstate = dirstate(self.opener, ui) try: self.current = bin(self.opener("current").read()) except IOError: @@ -340,21 +356,18 @@ def undo(self): self.lock() if os.path.exists(self.join("undo")): + f = self.changelog.read(self.changelog.tip())[3] self.ui.status("attempting to rollback last transaction\n") rollback(self.opener, self.join("undo")) self.manifest = manifest(self.opener) self.changelog = changelog(self.opener) - self.ui.status("discarding dircache\n") + self.ui.status("discarding dirstate\n") node = self.changelog.tip() - mf = self.changelog.read(node)[0] - mm = self.manifest.read(mf) - f = mm.keys() f.sort() self.setcurrent(node) - self.dircache.clear() - self.dircache.taint(f) + self.dirstate.update(f, 'i') else: self.ui.warn("no undo information available\n") @@ -389,22 +402,29 @@ n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, ) tr.close() self.setcurrent(n) - self.dircache.clear() - self.dircache.update(mmap) + self.dirstate.clear() + self.dirstate.update(mmap.keys(), "n") - def commit(self, parent, update = None, text = ""): + def commit(self, parent, files = None, text = ""): self.lock() - try: - remove = [ l[:-1] for l in self.opener("to-remove") ] - os.unlink(self.join("to-remove")) - except IOError: - remove = [] + commit = [] + remove = [] + if files: + for f in files: + s = self.dirstate.state(f) + if s in 'cai': + commit.append(f) + elif s == 'r': + remove.append(f) + else: + self.warn("%s not tracked!\n") + else: + (c, a, d, u) = self.diffdir(self.root, parent) + commit = c + a + remove = d - if update == None: - update = self.diffdir(self.root, parent)[0] - - if not update: + if not commit and not remove: self.ui.status("nothing changed\n") return @@ -413,14 +433,15 @@ # check in files new = {} linkrev = self.changelog.count() - update.sort() - for f in update: + commit.sort() + for f in commit: self.ui.note(f + "\n") try: t = file(f).read() except IOError: - remove.append(f) - continue + self.warn("trouble committing %s!\n" % f) + raise + r = self.file(f) new[f] = r.add(t, tr, linkrev) @@ -444,8 +465,8 @@ tr.close() self.setcurrent(n) - self.dircache.update(new) - self.dircache.remove(remove) + self.dirstate.update(new, "n") + self.dirstate.forget(remove) def checkout(self, node): # checkout is really dumb at the moment @@ -465,20 +486,21 @@ file(f, "w").write(t) self.setcurrent(node) - self.dircache.clear() - self.dircache.update([f for f,n in l]) + self.dirstate.clear() + self.dirstate.update([f for f,n in l], "n") def diffdir(self, path, changeset): changed = [] + added = [] + unknown = [] mf = {} - added = [] if changeset: change = self.changelog.read(changeset) mf = self.manifest.read(change[0]) if changeset == self.current: - dc = self.dircache.copy() + dc = self.dirstate.copy() else: dc = dict.fromkeys(mf) @@ -498,22 +520,31 @@ if fn in dc: c = dc[fn] del dc[fn] - if not c or c[1] < 0: + if not c: if fcmp(fn): changed.append(fn) - elif c[1] != s.st_size: + if c[0] == 'i': + if fn not in mf: + added.append(fn) + elif fcmp(fn): + changed.append(fn) + elif c[0] == 'a': + added.append(fn) + elif c[0] == 'r': + unknown.append(fn) + elif c[2] != s.st_size: changed.append(fn) - elif c[0] != s.st_mode or c[2] != s.st_mtime: + elif c[1] != s.st_mode or c[3] != s.st_mtime: if fcmp(fn): changed.append(fn) else: if self.ignore(fn): continue - added.append(fn) + unknown.append(fn) deleted = dc.keys() deleted.sort() - return (changed, added, deleted) + return (changed, added, deleted, unknown) def diffrevs(self, node1, node2): changed, added = [], [] @@ -537,12 +568,31 @@ return (changed, added, deleted) def add(self, list): - self.dircache.taint(list) + for f in list: + p = os.path.join(self.root, f) + if not os.path.isfile(p): + self.ui.warn("%s does not exist!\n" % f) + elif self.dirstate.state(f) == 'n': + self.ui.warn("%s already tracked!\n" % f) + else: + self.dirstate.update([f], "a") + + def forget(self, list): + for f in list: + if self.dirstate.state(f) not in 'ai': + self.ui.warn("%s not added!\n" % f) + else: + self.dirstate.forget([f]) def remove(self, list): - dl = self.opener("to-remove", "a") for f in list: - dl.write(f + "\n") + p = os.path.join(self.root, f) + if os.path.isfile(p): + self.ui.warn("%s still exists!\n" % f) + elif f not in self.dirstate: + self.ui.warn("%s not tracked!\n" % f) + else: + self.dirstate.update([f], "r") def branches(self, nodes): if not nodes: nodes = [self.changelog.tip()]