# HG changeset patch # User mpm@selenic.com # Date 1117938875 28800 # Node ID c03f58e5fd2d4c704c2b7434915f8d7eaf9b51f3 # Parent 2da0a56aa1fde4fb29f619bd0bec4e66accfb37b unify checkout and resolve into update -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 unify checkout and resolve into update This replaces checkout and resolve with a single command: $ hg help co hg update [node] update or merge working directory If there are no outstanding changes in the working directory and there is a linear relationship between the current version and the requested version, the result is the requested version. Otherwise the result is a merge between the contents of the current working directory and the requested version. Files that changed between either parent are marked as changed for the next commit and a commit must be performed before any further updates are allowed. manifest hash: 513d285d7fb775d0560de49387042a685ea062f7 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.0 (GNU/Linux) iD8DBQFComS7ywK+sNU5EO8RAmgRAJ96GA6qvHLy0Jp0fzUrR2os2azPuACePsdC YBldZtA7yIuTnV2vIbn7OSE= =QtM/ -----END PGP SIGNATURE----- diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -164,18 +164,6 @@ def cat(ui, repo, file, rev = []): if rev: n = r.lookup(rev) sys.stdout.write(r.read(n)) -def checkout(ui, repo, changeset=None): - '''checkout a given changeset or the current tip''' - (c, a, d, u) = repo.diffdir(repo.root) - if c or a or d: - ui.warn("aborting (outstanding changes in working directory)\n") - sys.exit(1) - - node = repo.changelog.tip() - if changeset: - node = repo.lookup(changeset) - repo.checkout(node) - def commit(ui, repo, *files): """commit the specified files or all outstanding changes""" repo.commit(relpath(repo, files)) @@ -405,14 +393,6 @@ def remove(ui, repo, file, *files): """remove the specified files on the next commit""" repo.remove(relpath(repo, (file,) + files)) -def resolve(ui, repo, node=None): - '''merge a given node or the current tip into the working dir''' - if not node: - node = repo.changelog.tip() - else: - node = repo.lookup(node) - repo.resolve(node) - def serve(ui, repo, **opts): from mercurial import hgweb hgweb.server(repo.root, opts["name"], opts["templates"], @@ -453,6 +433,22 @@ def tip(ui, repo): def undo(ui, repo): repo.undo() +def update(ui, repo, node=None): + '''update or merge working directory + + If there are no outstanding changes in the working directory and + there is a linear relationship between the current version and the + requested version, the result is the requested version. + + Otherwise the result is a merge between the contents of the + current working directory and the requested version. Files that + changed between either parent are marked as changed for the next + commit and a commit must be performed before any further updates + are allowed. + ''' + node = node and repo.lookup(node) or repo.changelog.tip() + repo.update(node) + def verify(ui, repo): """verify the integrity of the repository""" return repo.verify() @@ -468,7 +464,6 @@ table = { 'hg annotate [-u] [-c] [-n] [-r id] [files]'), "branch|clone": (branch, [], 'hg branch [path]'), "cat|dump": (cat, [], 'hg cat [rev]'), - "checkout|co": (checkout, [], 'hg checkout [changeset]'), "commit|ci": (commit, [], 'hg commit [files]'), "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'), "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'), @@ -501,7 +496,6 @@ table = { 'hg rawcommit [options] [files]'), "recover": (recover, [], "hg recover"), "remove": (remove, [], "hg remove [files]"), - "resolve": (resolve, [], 'hg resolve [node]'), "serve": (serve, [('p', 'port', 8000, 'listen port'), ('a', 'address', '', 'interface address'), ('n', 'name', os.getcwd(), 'repository name'), @@ -511,6 +505,7 @@ table = { "tags": (tags, [], 'hg tags'), "tip": (tip, [], 'hg tip'), "undo": (undo, [], 'hg undo'), + "update|up|checkout|co|resolve": (update, [], 'hg update [node]'), "verify": (verify, [], 'hg verify'), } diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -327,11 +327,17 @@ class localrepository: if self.tags is None: self.tags = {} try: + # read each head of the tags file, ending with the tip + # and add each tag found to the map, with "newer" ones + # taking precedence fl = self.file(".hgtags") - for l in fl.revision(fl.tip()).splitlines(): - if l: - n, k = l.split(" ") - self.tags[k] = bin(n) + h = fl.heads() + h.reverse() + for r in h: + for l in fl.revision(r).splitlines(): + if l: + n, k = l.split(" ") + self.tags[k] = bin(n) except KeyError: pass try: return self.tags[key] @@ -476,27 +482,6 @@ class localrepository: self.dirstate.update(new, "n") self.dirstate.forget(remove) - def checkout(self, node): - # checkout is really dumb at the moment - # it ought to basically merge - change = self.changelog.read(node) - l = self.manifest.read(change[0]).items() - l.sort() - - for f,n in l: - if f[0] == "/": continue - self.ui.note(f, "\n") - t = self.file(f).revision(n) - try: - file(self.wjoin(f), "w").write(t) - except IOError: - os.makedirs(os.path.dirname(f)) - file(self.wjoin(f), "w").write(t) - - self.dirstate.setparents(node) - self.dirstate.clear() - self.dirstate.update([f for f,n in l], "n") - def diffdir(self, path, changeset = None): changed = [] added = [] @@ -848,10 +833,10 @@ class localrepository: tr.close() return - def resolve(self, node): + def update(self, node): pl = self.dirstate.parents() if pl[1] != nullid: - self.ui.warn("last merge not committed") + self.ui.warn("aborting: outstanding uncommitted merges\n") return p1, p2 = pl[0], node @@ -866,7 +851,7 @@ class localrepository: # resolve the manifest to determine which files # we care about merging - self.ui.status("resolving manifests\n") + self.ui.note("resolving manifests\n") self.ui.debug(" ancestor %s local %s remote %s\n" % (short(man), short(m1n), short(m2n))) @@ -876,16 +861,20 @@ class localrepository: # construct a working dir manifest mw = m1.copy() - for f in a + c: - mw[f] = nullid + for f in a + c + u: + mw[f] = "" for f in d: - del mw[f] + if f in mw: del mw[f] for f, n in mw.iteritems(): if f in m2: if n != m2[f]: - self.ui.debug(" %s versions differ, do resolve\n" % f) - merge[f] = (m1.get(f, nullid), m2[f]) + a = ma.get(f, nullid) + if n != a and m2[f] != a: + self.ui.debug(" %s versions differ, do resolve\n" % f) + merge[f] = (m1.get(f, nullid), m2[f]) + else: + get[f] = m2[f] del m2[f] elif f in ma: if n != ma[f]: @@ -896,25 +885,36 @@ class localrepository: remove.append(f) else: self.ui.debug("other deleted %s\n" % f) - pass # other deleted it + remove.append(f) # other deleted it else: - self.ui.debug("local created %s\n" %f) + if n == m1.get(f, nullid): # same as parent + self.ui.debug("remote deleted %s\n" % f) + remove.append(f) + else: + self.ui.debug("working dir created %s, keeping\n" % f) for f, n in m2.iteritems(): - if f in ma: - if n != ma[f]: + if f in ma and n != ma[f]: r = self.ui.prompt( ("remote changed %s which local deleted\n" % f) + "(k)eep or (d)elete?", "[kd]", "k") if r == "d": remove.append(f) - else: - pass # probably safe else: - self.ui.debug("remote created %s, do resolve\n" % f) + self.ui.debug("remote created %s\n" % f) get[f] = n del mw, m1, m2, ma + if not merge: + # we don't need to do any magic, just jump to the new rev + mode = 'n' + p1, p2 = p2, nullid + else: + # we have to remember what files we needed to get/change + # because any file that's different from either one of its + # parents must be in the changeset + mode = 'm' + self.dirstate.setparents(p1, p2) # get the files we don't need to change @@ -929,26 +929,24 @@ class localrepository: except IOError: os.makedirs(os.path.dirname(f)) file(self.wjoin(f), "w").write(t) - - # we have to remember what files we needed to get/change - # because any file that's different from either one of its - # parents must be in the changeset - self.dirstate.update(files, 'm') + self.dirstate.update([f], mode) # merge the tricky bits files = merge.keys() files.sort() for f in files: + self.status("mering %f\n" % f) m, o = merge[f] self.merge3(f, m, o) - - # same here - self.dirstate.update(files, 'm') + self.dirstate.update([f], 'm') for f in remove: self.ui.note("removing %s\n" % f) - #os.unlink(f) - self.dirstate.update(remove, 'r') + os.unlink(f) + if mode == 'n': + self.dirstate.forget(remove) + else: + self.dirstate.update(remove, 'r') def merge3(self, fn, my, other): """perform a 3-way merge in the working directory"""