unify checkout and resolve into update
authormpm@selenic.com
Sat, 04 Jun 2005 18:34:35 -0800
changeset 254 c03f58e5fd2d
parent 253 2da0a56aa1fd
child 255 20a44c82795f
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-----
mercurial/commands.py
mercurial/hg.py
--- a/mercurial/commands.py	Sat Jun 04 15:16:48 2005 -0800
+++ b/mercurial/commands.py	Sat Jun 04 18:34:35 2005 -0800
@@ -164,18 +164,6 @@
     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 @@
     """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 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 @@
                      'hg annotate [-u] [-c] [-n] [-r id] [files]'),
     "branch|clone": (branch, [], 'hg branch [path]'),
     "cat|dump": (cat, [], 'hg cat <file> [rev]'),
-    "checkout|co": (checkout, [], 'hg checkout [changeset]'),
     "commit|ci": (commit, [], 'hg commit [files]'),
     "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'),
     "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'),
@@ -501,7 +496,6 @@
                   '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 @@
     "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'),
     }
 
--- a/mercurial/hg.py	Sat Jun 04 15:16:48 2005 -0800
+++ b/mercurial/hg.py	Sat Jun 04 18:34:35 2005 -0800
@@ -327,11 +327,17 @@
         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 @@
         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 @@
         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 @@
 
         # 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 @@
 
         # 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 @@
                         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 @@
             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"""