Merge walk changes with Matt's tip.
authorBryan O'Sullivan <bos@serpentine.com>
Sat, 13 Aug 2005 15:26:32 -0800
changeset 895 77b52b864249768a0ec615af331be778fdbb65e9
parent 890 391de0bcc72231f3acaa7d9674147e50edafb7de (current diff)
parent 894 62ec665759f25731cba7287407043b754c539d98 (diff)
child 896 01215ad0428341bf0b5806b199c54fe158d9affd
push id1
push usergszorc@mozilla.com
push dateWed, 18 Mar 2015 16:34:57 +0000
Merge walk changes with Matt's tip.
.hgignore
CONTRIBUTORS
TODO
doc/hg.1.txt
mercurial/commands.py
mercurial/hg.py
mercurial/hgweb.py
mercurial/revlog.py
mercurial/util.py
templates/map
tests/test-help
tests/test-help.out
tests/test-merge-revert.out
tests/test-merge-revert2
tests/test-merge-revert2.out
tests/test-walk
tests/test-walk.out
--- a/doc/hg.1.txt
+++ b/doc/hg.1.txt
@@ -504,17 +504,17 @@ SPECIFYING MULTIPLE REVISIONS
     are revision identifiers.  Both BEGIN and END are optional.  If
     BEGIN is not specified, it defaults to revision number 0.  If END
     is not specified, it defaults to the tip.  The range ":" thus
     means "all revisions".
 
     If BEGIN is greater than END, revisions are treated in reverse
     order.
 
-    A range acts as an open interval.  This means that a range of 3:5
+    A range acts as a closed interval.  This means that a range of 3:5
     gives 3, 4 and 5.  Similarly, a range of 4:2 gives 4, 3, and 2.
 
 ENVIRONMENT VARIABLES
 ---------------------
 
 HGEDITOR::
     This is the name of the editor to use when committing. Defaults to the
     value of EDITOR. 
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -458,16 +458,18 @@ def clone(ui, source, dest=None, **opts)
     """make a copy of an existing repository"""
     if dest is None:
         dest = os.path.basename(os.path.normpath(source))
 
     if os.path.exists(dest):
         ui.warn("abort: destination '%s' already exists\n" % dest)
         return 1
 
+    dest = os.path.realpath(dest)
+
     class Dircleanup:
         def __init__(self, dir_):
             self.rmtree = shutil.rmtree
             self.dir_ = dir_
             os.mkdir(dir_)
         def close(self):
             self.dir_ = None
         def __del__(self):
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -392,32 +392,34 @@ class dirstate:
     def copy(self, source, dest):
         self.read()
         self.markdirty()
         self.copies[dest] = source
 
     def copied(self, file):
         return self.copies.get(file, None)
 
-    def update(self, files, state):
+    def update(self, files, state, **kw):
         ''' current states:
         n  normal
         m  needs merging
         r  marked for removal
         a  marked for addition'''
 
         if not files: return
         self.read()
         self.markdirty()
         for f in files:
             if state == "r":
                 self.map[f] = ('r', 0, 0, 0)
             else:
                 s = os.stat(os.path.join(self.root, f))
-                self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
+                st_size = kw.get('st_size', s.st_size)
+                st_mtime = kw.get('st_mtime', s.st_mtime)
+                self.map[f] = (state, s.st_mode, st_size, st_mtime)
 
     def forget(self, files):
         if not files: return
         self.read()
         self.markdirty()
         for f in files:
             try:
                 del self.map[f]
@@ -497,16 +499,17 @@ class dirstate:
                     if ff not in dc: self.ui.warn('%s: %s\n' % (
                         util.pathto(self.getcwd(), ff),
                         inst.strerror))
                     continue
                 if stat.S_ISDIR(st.st_mode):
                     for dir, subdirs, fl in os.walk(f):
                         d = dir[len(self.root) + 1:]
                         nd = util.normpath(d)
+                        if nd == '.': nd = ''
                         if seen(nd):
                             subdirs[:] = []
                             continue
                         for sd in subdirs:
                             ds = os.path.join(nd, sd +'/')
                             if self.ignore(ds) or not match(ds):
                                 subdirs.remove(sd)
                         subdirs.sort()
@@ -538,46 +541,54 @@ class dirstate:
         for src, fn in util.unique(traverse()):
             fn = util.normpath(fn)
             if seen(fn): continue
             if fn not in dc and self.ignore(fn):
                 continue
             if match(fn):
                 yield src, fn
 
-    def changes(self, files = None, match = util.always):
+    def changes(self, files=None, match=util.always):
         self.read()
         if not files:
             dc = self.map.copy()
         else:
             dc = self.filterfiles(files)
-        lookup, changed, added, unknown = [], [], [], []
+        lookup, modified, added, unknown = [], [], [], []
+        removed, deleted = [], []
 
         for src, fn in self.walk(files, match, dc=dc):
-            try: s = os.stat(os.path.join(self.root, fn))
-            except: continue
-
-            if fn in dc:
-                c = dc[fn]
+            try:
+                s = os.stat(os.path.join(self.root, fn))
+            except OSError:
+                continue
+            if not stat.S_ISREG(s.st_mode):
+                continue
+            c = dc.get(fn)
+            if c:
                 del dc[fn]
-
                 if c[0] == 'm':
-                    changed.append(fn)
+                    modified.append(fn)
                 elif c[0] == 'a':
                     added.append(fn)
                 elif c[0] == 'r':
                     unknown.append(fn)
                 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
-                    changed.append(fn)
-                elif c[1] != s.st_mode or c[3] != s.st_mtime:
+                    modified.append(fn)
+                elif c[3] != s.st_mtime:
                     lookup.append(fn)
             else:
-                if match(fn): unknown.append(fn)
+                unknown.append(fn)
 
-        return (lookup, changed, added, filter(match, dc.keys()), unknown)
+        for fn, c in [(fn, c) for fn, c in dc.items() if match(fn)]:
+            if c[0] == 'r':
+                removed.append(fn)
+            else:
+                deleted.append(fn)
+        return (lookup, modified, added, removed + deleted, unknown)
 
 # used to avoid circular references so destructors work
 def opener(base):
     p = base
     def o(path, mode="r"):
         if p.startswith("http://"):
             f = os.path.join(p, urllib.quote(path))
             return httprangereader.httprangereader(f)
@@ -1618,20 +1629,30 @@ class localrepository:
         # merge the tricky bits
         files = merge.keys()
         files.sort()
         for f in files:
             self.ui.status("merging %s\n" % f)
             m, o, flag = merge[f]
             self.merge3(f, m, o)
             util.set_exec(self.wjoin(f), flag)
-            if moddirstate and mode == 'm':
-                # only update dirstate on branch merge, otherwise we
-                # could mark files with changes as unchanged
-                self.dirstate.update([f], mode)
+            if moddirstate:
+                if mode == 'm':
+                    # only update dirstate on branch merge, otherwise we
+                    # could mark files with changes as unchanged
+                    self.dirstate.update([f], mode)
+                elif p2 == nullid:
+                    # update dirstate from parent1's manifest
+                    m1n = self.changelog.read(p1)[0]
+                    m1 = self.manifest.read(m1n)
+                    f_len = len(self.file(f).read(m1[f]))
+                    self.dirstate.update([f], mode, st_size=f_len, st_mtime=0)
+                else:
+                    self.ui.warn("Second parent without branch merge!?\n"
+                                 "Dirstate for file %s may be wrong.\n" % f)
 
         remove.sort()
         for f in remove:
             self.ui.note("removing %s\n" % f)
             try:
                 os.unlink(f)
             except OSError, inst:
                 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
--- a/tests/test-merge-revert.out
+++ b/tests/test-merge-revert.out
@@ -21,17 +21,16 @@ 8633637036c1
 + hg diff
 + hg status
 + hg id
 3aa14bbc23d9 tip
 + hg update -C 0
 + hg update
 merging file1
 + hg diff
-FIXME: This is a known bug:
 + hg status
 + hg id
 3aa14bbc23d9 tip
 + hg revert
 + hg diff
 + hg status
 + hg id
 3aa14bbc23d9 tip
--- a/tests/test-merge-revert2
+++ b/tests/test-merge-revert2
@@ -1,14 +1,15 @@
 #!/bin/sh
 
 mkdir t
 cd t
 hg init
 echo "added file1" > file1
+echo "another line of text" >> file1
 echo "added file2" > file2
 hg add file1 file2
 hg commit -m "added file1 and file2" -d "0 0" -u user
 echo "changed file1" >> file1
 hg commit -m "changed file1" -d "0 0" -u user
 hg -q log
 hg id
 hg update -C 0
--- a/tests/test-merge-revert2.out
+++ b/tests/test-merge-revert2.out
@@ -1,60 +1,60 @@
 + hg init
 + hg add file1 file2
 + hg commit -m added file1 and file2 -d 0 0 -u user
 + hg commit -m changed file1 -d 0 0 -u user
 + hg -q log
-1:3aa14bbc23d90e3f8b5b639b4a43d76509bae76c
-0:8633637036c18f021d771208e16ae3508ab81d28
+1:f4d7a8c73d231bc078e2a5e791325e55e8a4c252
+0:232e179b3f294d467cfa66e1439bc5b0d44e4a93
 + hg id
-3aa14bbc23d9 tip
+f4d7a8c73d23 tip
 + hg update -C 0
 + hg id
-8633637036c1
+232e179b3f29
 + hg id
-8633637036c1+
+232e179b3f29+
 + hg revert
 + hg diff
 + hg status
 + hg id
-8633637036c1
+232e179b3f29
 + hg update
 + hg diff
 + hg status
 + hg id
-3aa14bbc23d9 tip
+f4d7a8c73d23 tip
 + hg update -C 0
 + hg update
 merge: warning: conflicts during merge
 merging file1
 merging file1 failed!
 + hg diff
-diff -r 3aa14bbc23d9 file1
+diff -r f4d7a8c73d23 file1
 --- a/file1
 +++ b/file1
-@@ -1,2 +1,6 @@
+@@ -1,3 +1,7 @@
  added file1
+ another line of text
 +<<<<<<<
 +changed file1 different
 +=======
  changed file1
 +>>>>>>>
 + hg status
 M file1
 + hg id
-3aa14bbc23d9+ tip
+f4d7a8c73d23+ tip
 + hg revert
 + hg diff
-FIXME: This is a known bug:
 + hg status
 + hg id
-3aa14bbc23d9 tip
+f4d7a8c73d23 tip
 + hg revert -r tip
 + hg diff
 + hg status
 + hg id
-3aa14bbc23d9 tip
+f4d7a8c73d23 tip
 + hg update -C
 + hg diff
 + hg status
 + hg id
-3aa14bbc23d9 tip
+f4d7a8c73d23 tip
--- a/tests/test-walk
+++ b/tests/test-walk
@@ -30,18 +30,20 @@ hg debugwalk ../beans
 hg debugwalk
 cd ..
 hg debugwalk -Ibeans
 hg debugwalk 'mammals/../beans/b*'
 hg debugwalk '-X*/Procyonidae' mammals
 hg debugwalk path:mammals
 hg debugwalk ..
 hg debugwalk beans/../..
-hg debugwalk `pwd`/beans
-hg debugwalk `pwd`/..
+# Don't know how to test absolute paths without always getting a false
+# error.
+#hg debugwalk `pwd`/beans
+#hg debugwalk `pwd`/..
 hg debugwalk glob:\*
 hg debugwalk 're:.*[kb]$'
 hg debugwalk path:beans/black
 hg debugwalk beans 'beans/*'
 hg debugwalk 'j*'
 hg debugwalk NOEXIST
 mkfifo fifo
 hg debugwalk fifo
--- a/tests/test-walk.out
+++ b/tests/test-walk.out
@@ -76,25 +76,16 @@ f  mammals/skunk  mammals/skunk
 f  mammals/skunk                   mammals/skunk
 f  mammals/Procyonidae/cacomistle  mammals/Procyonidae/cacomistle
 f  mammals/Procyonidae/coatimundi  mammals/Procyonidae/coatimundi
 f  mammals/Procyonidae/raccoon     mammals/Procyonidae/raccoon
 + hg debugwalk ..
 abort: .. not under repository root
 + hg debugwalk beans/../..
 abort: beans/../.. not under repository root
-+ hg debugwalk /tmp/hgtests.15784.14760.4713.20670/test-walk/t/beans
-f  beans/black     beans/black
-f  beans/borlotti  beans/borlotti
-f  beans/kidney    beans/kidney
-f  beans/navy      beans/navy
-f  beans/pinto     beans/pinto
-f  beans/turtle    beans/turtle
-+ hg debugwalk /tmp/hgtests.15784.14760.4713.20670/test-walk/t/..
-abort: /tmp/hgtests.15784.14760.4713.20670/test-walk/t/.. not under repository root
 + hg debugwalk glob:*
 f  fennel      fennel
 f  fenugreek   fenugreek
 f  fiddlehead  fiddlehead
 f  glob:glob   glob:glob
 + hg debugwalk re:.*[kb]$
 f  fenugreek      fenugreek
 f  glob:glob      glob:glob