Rev 2250: Merge HEAD. in file:///home/robertc/source/baz/branch-hooks/

Robert Collins robertc at robertcollins.net
Tue Feb 6 23:53:46 GMT 2007


------------------------------------------------------------
revno: 2250
revision-id: robertc at robertcollins.net-20070206235330-2sy67ccuq0ksubqa
parent: robertc at robertcollins.net-20070206023342-hv5o5qh6pdktiwbc
parent: pqm at pqm.ubuntu.com-20070206205442-4a0752247e60cabf
committer: Robert Collins <robertc at robertcollins.net>
branch nick: branch-hooks
timestamp: Wed 2007-02-07 10:53:30 +1100
message:
  Merge HEAD.
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/commit.py               commit.py-20050511101309-79ec1a0168e0e825
  bzrlib/delta.py                delta.py-20050729221636-54cf14ef94783d0a
  bzrlib/inventory.py            inventory.py-20050309040759-6648b84ca2005b37
  bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
  bzrlib/memorytree.py           memorytree.py-20060906023413-4wlkalbdpsxi2r4y-1
  bzrlib/status.py               status.py-20050505062338-431bfa63ec9b19e6
  bzrlib/tests/blackbox/test_init.py test_init.py-20060309032856-a292116204d86eb7
  bzrlib/tests/blackbox/test_merge.py test_merge.py-20060323225809-9bc0459c19917f41
  bzrlib/tests/blackbox/test_status.py teststatus.py-20050712014354-508855eb9f29f7dc
  bzrlib/tests/test_commit.py    test_commit.py-20050914060732-279f057f8c295434
  bzrlib/tests/test_delta.py     test_delta.py-20070110134455-sqpd1y7mbjndelxf-1
  bzrlib/tests/test_merge.py     testmerge.py-20050905070950-c1b5aa49ff911024
  bzrlib/tests/test_transport.py testtransport.py-20050718175618-e5cdb99f4555ddce
  bzrlib/tests/test_transport_implementations.py test_transport_implementations.py-20051227111451-f97c5c7d5c49fce7
  bzrlib/transport/local.py      local_transport.py-20050711165921-9b1f142bfe480c24
  bzrlib/tree.py                 tree.py-20050309040759-9d5f2496be663e77
  bzrlib/urlutils.py             urlutils.py-20060502195429-e8a161ecf8fac004
  bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
    ------------------------------------------------------------
    revno: 2246.1.22
    merged: pqm at pqm.ubuntu.com-20070206205442-4a0752247e60cabf
    parent: pqm at pqm.ubuntu.com-20070206193545-d5eac67ff047ce5c
    parent: abentley at panoramicfeedback.com-20070206153230-39dpslyj1bkk781l
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Tue 2007-02-06 20:54:42 +0000
    message:
      Commit supports kind changes
        ------------------------------------------------------------
        revno: 1959.4.6
        merged: abentley at panoramicfeedback.com-20070206153230-39dpslyj1bkk781l
        parent: abentley at panoramicfeedback.com-20070206145804-a71ecyr3hgh8vhma
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: typechange
        timestamp: Tue 2007-02-06 10:32:30 -0500
        message:
          Ensure merge works across kind changes
        ------------------------------------------------------------
        revno: 1959.4.5
        merged: abentley at panoramicfeedback.com-20070206145804-a71ecyr3hgh8vhma
        parent: abentley at panoramicfeedback.com-20070206145406-yconkndx4kesw9q3
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: typechange
        timestamp: Tue 2007-02-06 09:58:04 -0500
        message:
          add NEWS entry
        ------------------------------------------------------------
        revno: 1959.4.4
        merged: abentley at panoramicfeedback.com-20070206145406-yconkndx4kesw9q3
        parent: abentley at panoramicfeedback.com-20070206145216-fcpi8o3ufvuzwbp9
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: typechange
        timestamp: Tue 2007-02-06 09:54:06 -0500
        message:
          Trim trailing whitespace
        ------------------------------------------------------------
        revno: 1959.4.3
        merged: abentley at panoramicfeedback.com-20070206145216-fcpi8o3ufvuzwbp9
        parent: abentley at panoramicfeedback.com-20060928134810-2c8ae086a4a70f43
        parent: pqm at pqm.ubuntu.com-20070205115536-6fb4bf1e665880ac
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: typechange
        timestamp: Tue 2007-02-06 09:52:16 -0500
        message:
          Merge bzr.dev
        ------------------------------------------------------------
        revno: 1959.4.2
        merged: abentley at panoramicfeedback.com-20060928134810-2c8ae086a4a70f43
        parent: aaron.bentley at utoronto.ca-20060826203316-d7112744d0ea94fc
        parent: pqm at pqm.ubuntu.com-20060927204431-5871ab3ef6affaf3
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: typechange
        timestamp: Thu 2006-09-28 09:48:10 -0400
        message:
          Merge bzr.dev
        ------------------------------------------------------------
        revno: 1959.4.1
        merged: aaron.bentley at utoronto.ca-20060826203316-d7112744d0ea94fc
        parent: pqm at pqm.ubuntu.com-20060825200624-719b2ca847a04dfa
        committer: Aaron Bentley <aaron.bentley at utoronto.ca>
        branch nick: typechange
        timestamp: Sat 2006-08-26 16:33:16 -0400
        message:
          Correctly handle all file kind changes
    ------------------------------------------------------------
    revno: 2246.1.21
    merged: pqm at pqm.ubuntu.com-20070206193545-d5eac67ff047ce5c
    parent: pqm at pqm.ubuntu.com-20070205115536-6fb4bf1e665880ac
    parent: abentley at panoramicfeedback.com-20070206171212-65zcmjif0byeszgi
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Tue 2007-02-06 19:35:45 +0000
    message:
      Change status --short output to use ChangeReporter
        ------------------------------------------------------------
        revno: 1551.2.49.1.40.1.22.1.42.1.31.1.12
        merged: abentley at panoramicfeedback.com-20070206171212-65zcmjif0byeszgi
        parent: abentley at panoramicfeedback.com-20070206160926-36crsd8hhr6724o8
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: Aaron's mergeable stuff
        timestamp: Tue 2007-02-06 12:12:12 -0500
        message:
          Handle simultaneous creation+rename
        ------------------------------------------------------------
        revno: 1551.2.49.1.40.1.22.1.42.1.31.1.11
        merged: abentley at panoramicfeedback.com-20070206160926-36crsd8hhr6724o8
        parent: abentley at panoramicfeedback.com-20070206155925-952ik6mflb0oz652
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: Aaron's mergeable stuff
        timestamp: Tue 2007-02-06 11:09:26 -0500
        message:
          Handle case where file-id only is added
        ------------------------------------------------------------
        revno: 1551.2.49.1.40.1.22.1.42.1.31.1.10
        merged: abentley at panoramicfeedback.com-20070206155925-952ik6mflb0oz652
        parent: abentley at panoramicfeedback.com-20070206140232-gquogvtlahelx4ha
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: Aaron's mergeable stuff
        timestamp: Tue 2007-02-06 10:59:25 -0500
        message:
          Add help text
        ------------------------------------------------------------
        revno: 1551.2.49.1.40.1.22.1.42.1.31.1.9
        merged: abentley at panoramicfeedback.com-20070206140232-gquogvtlahelx4ha
        parent: abentley at panoramicfeedback.com-20070205153447-fx4eq36mk9q4idue
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: Aaron's mergeable stuff
        timestamp: Tue 2007-02-06 09:02:32 -0500
        message:
          Add test cases for status with kind change
        ------------------------------------------------------------
        revno: 1551.2.49.1.40.1.22.1.42.1.31.1.8
        merged: abentley at panoramicfeedback.com-20070205153447-fx4eq36mk9q4idue
        parent: abentley at panoramicfeedback.com-20070205152754-n4y80twdvl69eguj
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: Aaron's mergeable stuff
        timestamp: Mon 2007-02-05 10:34:47 -0500
        message:
          Make ChangeReporter interface nicer
        ------------------------------------------------------------
        revno: 1551.2.49.1.40.1.22.1.42.1.31.1.7
        merged: abentley at panoramicfeedback.com-20070205152754-n4y80twdvl69eguj
        parent: abentley at panoramicfeedback.com-20070202152938-96tdyxy8lcg7j802
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: Aaron's mergeable stuff
        timestamp: Mon 2007-02-05 10:27:54 -0500
        message:
          Use new-style output for status
    ------------------------------------------------------------
    revno: 2246.1.20
    merged: pqm at pqm.ubuntu.com-20070205115536-6fb4bf1e665880ac
    parent: pqm at pqm.ubuntu.com-20070205073805-fe53a1d3e1184c95
    parent: mbp at sourcefrog.net-20070205111322-cbd3yty86inw76ro
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Mon 2007-02-05 11:55:36 +0000
    message:
      (mbp) make TestSFTPInit.test_init_existing_branch less fragile with working directory name (trivial)
        ------------------------------------------------------------
        revno: 2246.1.18.1.1
        merged: mbp at sourcefrog.net-20070205111322-cbd3yty86inw76ro
        parent: pqm at pqm.ubuntu.com-20070204152137-9848ed97ad8e6a88
        committer: Martin Pool <mbp at sourcefrog.net>
        branch nick: trivial
        timestamp: Mon 2007-02-05 22:13:22 +1100
        message:
          make TestSFTPInit.test_init_existing_branch less fragile (jamesw)
    ------------------------------------------------------------
    revno: 2246.1.19
    merged: pqm at pqm.ubuntu.com-20070205073805-fe53a1d3e1184c95
    parent: pqm at pqm.ubuntu.com-20070204152137-9848ed97ad8e6a88
    parent: bialix at ukr.net-20070205061738-t0pt6euik97cespt
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Mon 2007-02-05 07:38:05 +0000
    message:
      (bialix) Fix recursive upwalk for win32 UNC path (r=robert)
        ------------------------------------------------------------
        revno: 2245.6.3
        merged: bialix at ukr.net-20070205061738-t0pt6euik97cespt
        parent: bialix at ukr.net-20070205051913-2yipyjathfr6638z
        committer: Alexander Belchenko <bialix at ukr.net>
        branch nick: win32.unc.path
        timestamp: Mon 2007-02-05 08:17:38 +0200
        message:
          EmulatedWin32LocalTransport should provide their own 'clone' method
        ------------------------------------------------------------
        revno: 2245.6.2
        merged: bialix at ukr.net-20070205051913-2yipyjathfr6638z
        parent: bialix at ukr.net-20070128214332-wgvrj0xkb31ltfbc
        committer: Alexander Belchenko <bialix at ukr.net>
        branch nick: win32.unc.path
        timestamp: Mon 2007-02-05 07:19:13 +0200
        message:
          Fix name of emulated Win32LocalTransport as Robert suggested.
        ------------------------------------------------------------
        revno: 2245.6.1
        merged: bialix at ukr.net-20070128214332-wgvrj0xkb31ltfbc
        parent: pqm at pqm.ubuntu.com-20070125194626-4ded330415b7276d
        committer: Alexander Belchenko <bialix at ukr.net>
        branch nick: win32.unc.path
        timestamp: Sun 2007-01-28 23:43:32 +0200
        message:
          win32 UNC path: recursive cloning UNC path to root stops on //HOST, not on //
    ------------------------------------------------------------
    revno: 2246.1.18
    merged: pqm at pqm.ubuntu.com-20070204152137-9848ed97ad8e6a88
    parent: pqm at pqm.ubuntu.com-20070204143007-ee1d0712bf5bccdf
    parent: abentley at panoramicfeedback.com-20070202152938-96tdyxy8lcg7j802
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Sun 2007-02-04 15:21:37 +0000
    message:
      Support kind changes in tree deltas
        ------------------------------------------------------------
        revno: 1551.2.49.1.40.1.22.1.42.1.31.1.6
        merged: abentley at panoramicfeedback.com-20070202152938-96tdyxy8lcg7j802
        parent: abentley at panoramicfeedback.com-20070202135202-78r1yntnepqcuic2
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: Aaron's mergeable stuff
        timestamp: Fri 2007-02-02 10:29:38 -0500
        message:
          Support kind changes in tree deltas
        ------------------------------------------------------------
        revno: 1551.2.49.1.40.1.22.1.42.1.31.1.5
        merged: abentley at panoramicfeedback.com-20070202135202-78r1yntnepqcuic2
        parent: abentley at panoramicfeedback.com-20070201145904-nrzbl3vkwjssrkhb
        parent: pqm at pqm.ubuntu.com-20070202101652-5367171684fe2eec
        committer: Aaron Bentley <abentley at panoramicfeedback.com>
        branch nick: Aaron's mergeable stuff
        timestamp: Fri 2007-02-02 08:52:02 -0500
        message:
          Merge bzr.dev
    ------------------------------------------------------------
    revno: 2246.1.17
    merged: pqm at pqm.ubuntu.com-20070204143007-ee1d0712bf5bccdf
    parent: pqm at pqm.ubuntu.com-20070204135042-a3daa4b7a72e7596
    parent: larstiq at larstiq.dyndns.org-20070204141152-h21zd23n0igf59ja
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Sun 2007-02-04 14:30:07 +0000
    message:
      (John Arbash Meinel) Don't create a progress bar for simple knit reading.
        ------------------------------------------------------------
        revno: 2246.1.16.1.1
        merged: larstiq at larstiq.dyndns.org-20070204141152-h21zd23n0igf59ja
        parent: pqm at pqm.ubuntu.com-20070204135042-a3daa4b7a72e7596
        parent: john at arbash-meinel.com-20070131185810-dh3c6xgjgo684hw8
        committer: Wouter van Heyst <larstiq at larstiq.dyndns.org>
        branch nick: bzr.dev
        timestamp: Sun 2007-02-04 15:11:52 +0100
        message:
          (John Arbash Meinel) Don't create a progress bar for simple knit reading.
        ------------------------------------------------------------
        revno: 2246.1.1.2.1
        merged: john at arbash-meinel.com-20070131185810-dh3c6xgjgo684hw8
        parent: pqm at pqm.ubuntu.com-20070131140456-56881c31a01089a3
        committer: John Arbash Meinel <john at arbash-meinel.com>
        branch nick: knit_no_pb
        timestamp: Wed 2007-01-31 12:58:10 -0600
        message:
          Don't create pb for simple knit reading.
    ------------------------------------------------------------
    revno: 2246.1.16
    merged: pqm at pqm.ubuntu.com-20070204135042-a3daa4b7a72e7596
    parent: pqm at pqm.ubuntu.com-20070204124741-bc510e3fe6fb2962
    parent: larstiq at larstiq.dyndns.org-20070204124955-w2l163jtnw28i46n
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Sun 2007-02-04 13:50:42 +0000
    message:
      Merge `bzr rm unversioned` fix. (Marius Kruger)
    ------------------------------------------------------------
    revno: 2246.1.12.2.1
    merged: larstiq at larstiq.dyndns.org-20070204124955-w2l163jtnw28i46n
    parent: pqm at pqm.ubuntu.com-20070202204950-910381483d737306
    parent: marius.kruger at enerweb.co.za-20070130155642-jrjusc3yj2rvqobi
    committer: Wouter van Heyst <larstiq at larstiq.dyndns.org>
    branch nick: bzr.dev
    timestamp: Sun 2007-02-04 13:49:55 +0100
    message:
      Merge `bzr rm unversioned` fix. (Marius Kruger)
    ------------------------------------------------------------
    revno: 2245.5.1
    merged: marius.kruger at enerweb.co.za-20070130155642-jrjusc3yj2rvqobi
    parent: pqm at pqm.ubuntu.com-20070125194626-4ded330415b7276d
    committer: Marius Kruger <marius.kruger at enerweb.co.za>
    branch nick: bzr.rm_error_to_note
    timestamp: Tue 2007-01-30 17:56:42 +0200
    message:
      Let bzr rm rather give a warning than an error when trying to remove a non-versioned file.
=== modified file 'NEWS'
--- a/NEWS	2007-02-06 02:33:42 +0000
+++ b/NEWS	2007-02-06 23:53:30 +0000
@@ -25,6 +25,10 @@
     * ``bzr pull --overwrite`` will now correctly overwrite checkouts.
       (Robert Collins)
 
+    * Files are now allowed to change kind (e.g. from file to symlink).
+      Supported by ``commit``, ``revert`` and ``status``
+      (Aaron Bentley)
+
   INTERNALS:
 
     * Reserved ids (any revision-id ending in a colon) are rejected by

=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2007-02-02 09:17:23 +0000
+++ b/bzrlib/builtins.py	2007-02-06 15:59:25 +0000
@@ -121,29 +121,49 @@
     This reports on versioned and unknown files, reporting them
     grouped by state.  Possible states are:
 
-    added / A
+    added
         Versioned in the working copy but not in the previous revision.
 
-    removed / D
+    removed
         Versioned in the previous revision but removed or deleted
         in the working copy.
 
-    renamed / R
+    renamed
         Path of this file changed from the previous revision;
         the text may also have changed.  This includes files whose
         parent directory was renamed.
 
-    modified / M
+    modified
         Text has changed since the previous revision.
 
-    unknown / ?
+    kind changed
+        File kind has been changed (e.g. from file to directory).
+
+    unknown
         Not versioned and not matching an ignore pattern.
 
     To see ignored files use 'bzr ignored'.  For details in the
     changes to file texts, use 'bzr diff'.
     
-    --short gives a one character status flag for each item, similar
-    to the SVN's status command.
+    --short gives a status flags for each item, similar to the SVN's status
+    command.
+
+    Column 1: versioning / renames
+      + File versioned
+      - File unversioned
+      R File renamed
+      ? File unknown
+      C File has conflicts
+      P Entry for a pending merge (not a file)
+
+    Column 2: Contents
+      N File created
+      D File deleted
+      K File kind changed
+      M File modified
+
+    Column 3: Execute
+      * The execute bit was changed
 
     If no arguments are specified, the status of the entire working
     directory is shown.  Otherwise, only the status of the specified

=== modified file 'bzrlib/commit.py'
--- a/bzrlib/commit.py	2007-02-06 02:33:42 +0000
+++ b/bzrlib/commit.py	2007-02-06 23:53:30 +0000
@@ -58,6 +58,7 @@
 
 from bzrlib import (
     errors,
+    inventory,
     tree,
     )
 from bzrlib.branch import Branch
@@ -582,6 +583,13 @@
         for path, new_ie in entries:
             self._emit_progress_update()
             file_id = new_ie.file_id
+            try:
+                kind = self.work_tree.kind(file_id)
+                if kind != new_ie.kind:
+                    new_ie = inventory.make_entry(kind, new_ie.name,
+                                                  new_ie.parent_id, file_id)
+            except errors.NoSuchFile:
+                pass
             # mutter('check %s {%s}', path, file_id)
             if (not self.specific_files or 
                 is_inside_or_parent_of_any(self.specific_files, path)):

=== modified file 'bzrlib/delta.py'
--- a/bzrlib/delta.py	2007-02-01 14:04:15 +0000
+++ b/bzrlib/delta.py	2007-02-06 17:12:12 +0000
@@ -56,6 +56,7 @@
         self.added = []
         self.removed = []
         self.renamed = []
+        self.kind_changed = []
         self.modified = []
         self.unchanged = []
 
@@ -66,21 +67,24 @@
                and self.removed == other.removed \
                and self.renamed == other.renamed \
                and self.modified == other.modified \
-               and self.unchanged == other.unchanged
+               and self.unchanged == other.unchanged \
+               and self.kind_changed == other.kind_changed
 
     def __ne__(self, other):
         return not (self == other)
 
     def __repr__(self):
-        return "TreeDelta(added=%r, removed=%r, renamed=%r, modified=%r," \
-            " unchanged=%r)" % (self.added, self.removed, self.renamed,
-            self.modified, self.unchanged)
+        return "TreeDelta(added=%r, removed=%r, renamed=%r," \
+            " kind_changed=%r, modified=%r, unchanged=%r)" % (self.added,
+            self.removed, self.renamed, self.kind_changed, self.modified,
+            self.unchanged)
 
     def has_changed(self):
         return bool(self.modified
                     or self.added
                     or self.removed
-                    or self.renamed)
+                    or self.renamed
+                    or self.kind_changed)
 
     def touches_file_id(self, file_id):
         """Return True if file_id is modified by this delta."""
@@ -91,10 +95,14 @@
         for v in self.renamed:
             if v[2] == file_id:
                 return True
+        for v in self.kind_changed:
+            if v[1] == file_id:
+                return True
         return False
             
 
-    def show(self, to_file, show_ids=False, show_unchanged=False, short_status=False):
+    def show(self, to_file, show_ids=False, show_unchanged=False,
+             short_status=False):
         """output this delta in status-like form to to_file."""
         def show_list(files, short_status_letter=''):
             for item in files:
@@ -109,7 +117,8 @@
                     path += '*'
 
                 if show_ids:
-                    print >>to_file, '%s  %-30s %s' % (short_status_letter, path, fid)
+                    print >>to_file, '%s  %-30s %s' % (short_status_letter,
+                        path, fid)
                 else:
                     print >>to_file, '%s  %s' % (short_status_letter, path)
             
@@ -142,12 +151,26 @@
                 if meta_modified:
                     newpath += '*'
                 if show_ids:
-                    print >>to_file, '%s  %s => %s %s' % (short_status_letter,
-                                                          oldpath, newpath, fid)
-                else:
-                    print >>to_file, '%s  %s => %s' % (short_status_letter,
-                                                       oldpath, newpath)
-                    
+                    print >>to_file, '%s  %s => %s %s' % (
+                        short_status_letter, oldpath, newpath, fid)
+                else:
+                    print >>to_file, '%s  %s => %s' % (
+                        short_status_letter, oldpath, newpath)
+
+        if self.kind_changed:
+            if short_status:
+                short_status_letter = 'K'
+            else:
+                print >>to_file, 'kind changed:'
+                short_status_letter = ''
+            for (path, fid, old_kind, new_kind) in self.kind_changed:
+                if show_ids:
+                    suffix = ' '+fid
+                else:
+                    suffix = ''
+                print >>to_file, '%s  %s (%s => %s)%s' % (
+                    short_status_letter, path, old_kind, new_kind, suffix)
+
         if self.modified or extra_modified:
             short_status_letter = 'M'
             if not short_status:
@@ -187,8 +210,6 @@
                                                specific_file_ids):
         if not include_root and (None, None) == parent_id:
             continue
-        assert kind[0] == kind[1] or None in kind
-        # the only 'kind change' permitted is creation/deletion
         fully_present = tuple((versioned[x] and kind[x] is not None) for
                               x in range(2))
         if fully_present[0] != fully_present[1]:
@@ -211,6 +232,8 @@
                                   kind[1],
                                   content_change, 
                                   (executable[0] != executable[1])))
+        elif kind[0] != kind[1]:
+            delta.kind_changed.append((path, file_id, kind[0], kind[1]))
         elif content_change is True or executable[0] != executable[1]:
             delta.modified.append((path, file_id, kind[1],
                                    content_change, 
@@ -232,12 +255,29 @@
 class ChangeReporter(object):
     """Report changes between two trees"""
 
-    def __init__(self, old_inventory, output=None):
+    def __init__(self, old_inventory, output=None, suppress_root_add=True,
+                 output_file=None):
+        """Constructor
+
+        :param old_inventory: The inventory of the old tree
+        :param output: a function with the signature of trace.note, i.e.
+            accepts a format and parameters.
+        :param supress_root_add: If true, adding the root will be ignored
+            (i.e. when a tree has just been initted)
+        :param output_file: If supplied, a file-like object to write to.
+            Only one of output and output_file may be supplied.
+        """
         self.old_inventory = old_inventory
+        if output_file is not None:
+            if output is not None:
+                raise BzrError('Cannot specify both output and output_file')
+            def output(fmt, *args):
+                output_file.write((fmt % args) + '\n')
         self.output = output
         if self.output is None:
             from bzrlib import trace
             self.output = trace.note
+        self.suppress_root_add = suppress_root_add
 
     def report(self, file_id, path, versioned, renamed, modified, exe_change,
                kind):
@@ -254,6 +294,8 @@
         :param kind: A pair of file kinds, as generated by Tree._iter_changes.
             None indicates no file present.
         """
+        if path == '' and versioned == 'added' and self.suppress_root_add:
+            return
         modified_map = {'kind changed': 'K',
                         'unchanged': ' ',
                         'created': 'N',
@@ -273,10 +315,12 @@
                 old_path = path
         if modified == 'deleted':
             path += osutils.kind_marker(kind[0])
-        else:
+        elif kind[1] is not None:
             path += osutils.kind_marker(kind[1])
         if old_path != "":
-            old_path += "%s => " % osutils.kind_marker(kind[0])
+            if kind[0] is not None:
+                old_path += osutils.kind_marker(kind[0])
+            old_path += " => "
         if exe_change:
             exe = '*'
         else:

=== modified file 'bzrlib/inventory.py'
--- a/bzrlib/inventory.py	2006-12-22 17:11:36 +0000
+++ b/bzrlib/inventory.py	2007-02-06 14:52:16 +0000
@@ -384,6 +384,8 @@
             return 'added'
         elif new_entry is None:
             return 'removed'
+        if old_entry.kind != new_entry.kind:
+            return 'modified'
         text_modified, meta_modified = new_entry.detect_changes(old_entry)
         if text_modified or meta_modified:
             modified = True

=== modified file 'bzrlib/knit.py'
--- a/bzrlib/knit.py	2007-01-17 15:37:08 +0000
+++ b/bzrlib/knit.py	2007-01-31 18:58:10 +0000
@@ -1130,29 +1130,22 @@
         # so - wc -l of a knit index is != the number of unique names
         # in the knit.
         self._history = []
-        decode_utf8 = cache_utf8.decode
-        pb = ui.ui_factory.nested_progress_bar()
         try:
-            pb.update('read knit index', 0, 1)
+            fp = self._transport.get(self._filename)
             try:
-                fp = self._transport.get(self._filename)
-                try:
-                    # _load_data may raise NoSuchFile if the target knit is
-                    # completely empty.
-                    self._load_data(fp)
-                finally:
-                    fp.close()
-            except NoSuchFile:
-                if mode != 'w' or not create:
-                    raise
-                elif delay_create:
-                    self._need_to_create = True
-                else:
-                    self._transport.put_bytes_non_atomic(
-                        self._filename, self.HEADER, mode=self._file_mode)
-        finally:
-            pb.update('read knit index', 1, 1)
-            pb.finished()
+                # _load_data may raise NoSuchFile if the target knit is
+                # completely empty.
+                self._load_data(fp)
+            finally:
+                fp.close()
+        except NoSuchFile:
+            if mode != 'w' or not create:
+                raise
+            elif delay_create:
+                self._need_to_create = True
+            else:
+                self._transport.put_bytes_non_atomic(
+                    self._filename, self.HEADER, mode=self._file_mode)
 
     def _load_data(self, fp):
         cache = self._cache

=== modified file 'bzrlib/memorytree.py'
--- a/bzrlib/memorytree.py	2006-09-22 04:52:17 +0000
+++ b/bzrlib/memorytree.py	2007-02-06 14:52:16 +0000
@@ -99,6 +99,9 @@
     def is_executable(self, file_id, path=None):
         return self._inventory[file_id].executable
 
+    def kind(self, file_id):
+        return self._inventory[file_id].kind
+
     def mkdir(self, path, file_id=None):
         """See MutableTree.mkdir()."""
         self.add(path, file_id, 'directory')

=== modified file 'bzrlib/status.py'
--- a/bzrlib/status.py	2006-12-07 20:25:00 +0000
+++ b/bzrlib/status.py	2007-02-05 15:34:47 +0000
@@ -16,6 +16,10 @@
 
 import sys
 
+from bzrlib import (
+    delta as _mod_delta,
+    tree,
+    )
 from bzrlib.diff import _raise_if_nonexistent
 import bzrlib.errors as errors
 from bzrlib.log import line_log
@@ -135,13 +139,22 @@
             else:
                 new = wt
         _raise_if_nonexistent(specific_files, old, new)
-        delta = new.changes_from(old, want_unchanged=show_unchanged,
-                              specific_files=specific_files)
-        delta.show(to_file,
-                   show_ids=show_ids,
-                   show_unchanged=show_unchanged,
-                   short_status=short)
-        short_status_letter = '?'
+        if short:
+            specific_file_ids = tree.find_ids_across_trees(specific_files,
+                (old, new), require_versioned=False)
+            changes = new._iter_changes(old, show_unchanged,
+                                        specific_file_ids)
+            reporter = _mod_delta.ChangeReporter(old.inventory,
+                output_file=to_file)
+            _mod_delta.report_changes(changes, reporter)
+        else:
+            delta = new.changes_from(old, want_unchanged=show_unchanged,
+                                  specific_files=specific_files)
+            delta.show(to_file,
+                       show_ids=show_ids,
+                       show_unchanged=show_unchanged,
+                       short_status=short)
+        short_status_letter = '? '
         if not short:
             short_status_letter = ''
         list_paths('unknown', new.unknowns(), specific_files, to_file,
@@ -153,7 +166,7 @@
                 print >> to_file, "conflicts:"
                 conflict_title = True
             if short:
-                prefix = 'C '
+                prefix = 'C  '
             else:
                 prefix = ' '
             print >> to_file, "%s %s" % (prefix, conflict)
@@ -190,7 +203,7 @@
             width = terminal_width()
             m_revision = branch.repository.get_revision(merge)
             if short:
-                prefix = 'P '
+                prefix = 'P  '
             else:
                 prefix = ' '
             print >> to_file, prefix, line_log(m_revision, width - 4)
@@ -203,14 +216,14 @@
                     continue
                 mm_revision = branch.repository.get_revision(mmerge)
                 if short:
-                    prefix = 'P. '
+                    prefix = 'P.  '
                 else:
                     prefix = '   '
                 print >> to_file, prefix, line_log(mm_revision, width - 5)
                 ignore.add(mmerge)
         except errors.NoSuchRevision:
             if short:
-                prefix = 'P '
+                prefix = 'P  '
             else:
                 prefix = ' '
             print >> to_file, prefix, merge

=== modified file 'bzrlib/tests/blackbox/test_init.py'
--- a/bzrlib/tests/blackbox/test_init.py	2006-10-11 23:08:27 +0000
+++ b/bzrlib/tests/blackbox/test_init.py	2007-02-05 11:13:22 +0000
@@ -124,7 +124,7 @@
 
         # make sure using 'bzr checkout' is not suggested
         # for remote locations missing a working tree
-        self.assertFalse(re.search(r'checkout', err))
+        self.assertFalse(re.search(r'use bzr checkout', err))
 
     def test_init_existing_branch_with_workingtree(self):
         # don't distinguish between the branch having a working tree or not

=== modified file 'bzrlib/tests/blackbox/test_merge.py'
--- a/bzrlib/tests/blackbox/test_merge.py	2006-11-25 20:38:39 +0000
+++ b/bzrlib/tests/blackbox/test_merge.py	2007-02-06 15:32:30 +0000
@@ -23,8 +23,8 @@
 
 from bzrlib.branch import Branch
 from bzrlib.bzrdir import BzrDir
-from bzrlib.conflicts import ConflictList
-from bzrlib.osutils import abspath
+from bzrlib.conflicts import ConflictList, ContentsConflict
+from bzrlib.osutils import abspath, file_kind
 from bzrlib.tests.blackbox import ExternalBase
 import bzrlib.urlutils as urlutils
 from bzrlib.workingtree import WorkingTree
@@ -269,3 +269,23 @@
         self.assertContainsRe(err, '1 revision\\(s\\) pulled')
         tree_a = WorkingTree.open('.')
         self.assertEqual([self.id2], tree_a.get_parent_ids())
+
+    def test_merge_kind_change(self):
+        tree_a = self.make_branch_and_tree('tree_a')
+        self.build_tree_contents([('tree_a/file', 'content_1')])
+        tree_a.add('file', 'file-id')
+        tree_a.commit('added file')
+        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
+        os.unlink('tree_a/file')
+        self.build_tree(['tree_a/file/'])
+        tree_a.commit('changed file to directory')
+        os.chdir('tree_b')
+        self.run_bzr('merge', '../tree_a')
+        self.assertEqual('directory', file_kind('file'))
+        tree_b.revert([])
+        self.assertEqual('file', file_kind('file'))
+        self.build_tree_contents([('file', 'content_2')])
+        tree_b.commit('content change')
+        self.run_bzr('merge', '../tree_a', retcode=1)
+        self.assertEqual(tree_b.conflicts(),
+                         [ContentsConflict('file', file_id='file-id')])

=== modified file 'bzrlib/tests/blackbox/test_status.py'
--- a/bzrlib/tests/blackbox/test_status.py	2006-11-23 18:56:25 +0000
+++ b/bzrlib/tests/blackbox/test_status.py	2007-02-06 14:02:32 +0000
@@ -24,7 +24,7 @@
 
 from cStringIO import StringIO
 import codecs
-from os import mkdir, chdir
+from os import mkdir, chdir, rmdir, unlink
 import sys
 from tempfile import TemporaryFile
 
@@ -76,8 +76,8 @@
             ],
             wt)
         self.assertStatus([
-                '?  bye.c\n',
-                '?  hello.c\n',
+                '?   bye.c\n',
+                '?   hello.c\n',
             ],
             wt, short=True)
 
@@ -94,9 +94,9 @@
             ],
             wt)
         self.assertStatus([
-                '?  bye.c\n',
-                '?  hello.c\n',
-                'P  pending at pending-0-0\n',
+                '?   bye.c\n',
+                '?   hello.c\n',
+                'P   pending at pending-0-0\n',
             ],
             wt, short=True)
 
@@ -174,9 +174,9 @@
                 wt)
 
         self.assertStatus([
-                '?  bye.c\n',
-                '?  dir2\n',
-                '?  directory/hello.c\n'
+                '?   bye.c\n',
+                '?   dir2\n',
+                '?   directory/hello.c\n'
                 ],
                 wt, short=True)
 
@@ -197,7 +197,7 @@
         show_tree_status(wt, specific_files=['directory'], to_file=tof,
                          short=True)
         tof.seek(0)
-        self.assertEquals(tof.readlines(), ['?  directory/hello.c\n'])
+        self.assertEquals(tof.readlines(), ['?   directory/hello.c\n'])
 
         tof = StringIO()
         show_tree_status(wt, specific_files=['dir2'], to_file=tof)
@@ -209,7 +209,7 @@
         tof = StringIO()
         show_tree_status(wt, specific_files=['dir2'], to_file=tof, short=True)
         tof.seek(0)
-        self.assertEquals(tof.readlines(), ['?  dir2\n'])
+        self.assertEquals(tof.readlines(), ['?   dir2\n'])
 
     def test_status_nonexistent_file(self):
         # files that don't exist in either the basis tree or working tree
@@ -258,32 +258,50 @@
         result = self.run_bzr("status")[0]
         self.assert_("unknown:\n  hello.txt\n" in result, result)
         result = self.run_bzr("status","--short")[0]
-        self.assert_("?  hello.txt\n" in result, result)
+        self.assertContainsRe(result, "[?]   hello.txt\n")
 
         self.run_bzr("add", "hello.txt")
         result = self.run_bzr("status")[0]
-        self.assert_("added:\n  hello.txt\n" in result, result)
+        self.assertContainsRe(result, "added:\n  hello.txt\n")
         result = self.run_bzr("status","--short")[0]
-        self.assert_("A  hello.txt\n" in result, result)
+        self.assertContainsRe(result, "[+]N  hello.txt\n")
 
         self.run_bzr("commit", "-m", "added")
         result = self.run_bzr("status", "-r", "0..1")[0]
-        self.assert_("added:\n  hello.txt\n" in result, result)
+        self.assertContainsRe(result, "added:\n  hello.txt\n")
         result = self.run_bzr("status", "--short", "-r", "0..1")[0]
-        self.assert_("A  hello.txt\n" in result, result)
+        self.assertContainsRe(result, "[+]N  hello.txt\n")
 
         self.build_tree(['world.txt'])
         result = self.run_bzr("status", "-r", "0")[0]
-        self.assert_("added:\n  hello.txt\n" \
-                     "unknown:\n  world.txt\n" in result, result)
+        self.assertContainsRe(result, "added:\n  hello.txt\n" \
+                                      "unknown:\n  world.txt\n")
         result2 = self.run_bzr("status", "-r", "0..")[0]
         self.assertEquals(result2, result)
         result = self.run_bzr("status", "--short", "-r", "0")[0]
-        self.assert_("A  hello.txt\n" \
-                     "?  world.txt\n" in result, result)
+        self.assertContainsRe(result, "[+]N  hello.txt\n" \
+                                      "[?]   world.txt\n")
         result2 = self.run_bzr("status", "--short", "-r", "0..")[0]
         self.assertEquals(result2, result)
 
+    def assertStatusContains(self, pattern):
+        """Run status, and assert it contains the given pattern"""
+        result = self.run_bzr("status", "--short")[0]
+        self.assertContainsRe(result, pattern)
+
+    def test_kind_change_short(self):
+        tree = self.make_branch_and_tree('.')
+        self.build_tree(['file'])
+        tree.add('file')
+        tree.commit('added file')
+        unlink('file')
+        self.build_tree(['file/'])
+        self.assertStatusContains('K  file => file/')
+        tree.rename_one('file', 'directory')
+        self.assertStatusContains('RK  file => directory/')
+        rmdir('directory')
+        self.assertStatusContains('RD  file => directory')
+
 
 class TestStatusEncodings(TestCaseWithTransport):
     

=== modified file 'bzrlib/tests/test_commit.py'
--- a/bzrlib/tests/test_commit.py	2006-11-28 19:19:15 +0000
+++ b/bzrlib/tests/test_commit.py	2007-02-06 14:52:16 +0000
@@ -21,6 +21,8 @@
 from bzrlib import (
     errors,
     lockdir,
+    osutils,
+    tests,
     )
 from bzrlib.branch import Branch
 from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
@@ -552,6 +554,51 @@
         timestamp_1ms = round(timestamp, 3)
         self.assertEqual(timestamp_1ms, timestamp)
 
+    def test_commit_kind_changes(self):
+        if not osutils.has_symlinks():
+            raise tests.TestSkipped('Test requires symlink support')
+        tree = self.make_branch_and_tree('.')
+        os.symlink('target', 'name')
+        tree.add('name', 'a-file-id')
+        tree.commit('Added a symlink')
+        self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
+
+        os.unlink('name')
+        self.build_tree(['name'])
+        tree.commit('Changed symlink to file')
+        self.assertEqual('file', tree.basis_tree().kind('a-file-id'))
+
+        os.unlink('name')
+        os.symlink('target', 'name')
+        tree.commit('file to symlink')
+        self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
+
+        os.unlink('name')
+        os.mkdir('name')
+        tree.commit('symlink to directory')
+        self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
+
+        os.rmdir('name')
+        os.symlink('target', 'name')
+        tree.commit('directory to symlink')
+        self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
+
+        # prepare for directory <-> file tests
+        os.unlink('name')
+        os.mkdir('name')
+        tree.commit('symlink to directory')
+        self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
+
+        os.rmdir('name')
+        self.build_tree(['name'])
+        tree.commit('Changed directory to file')
+        self.assertEqual('file', tree.basis_tree().kind('a-file-id'))
+
+        os.unlink('name')
+        os.mkdir('name')
+        tree.commit('file to directory')
+        self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
+
     def test_commit_unversioned_specified(self):
         """Commit should raise if specified files isn't in basis or worktree"""
         tree = self.make_branch_and_tree('.')

=== modified file 'bzrlib/tests/test_delta.py'
--- a/bzrlib/tests/test_delta.py	2007-02-01 14:04:15 +0000
+++ b/bzrlib/tests/test_delta.py	2007-02-06 17:12:12 +0000
@@ -14,8 +14,11 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+import os
+from StringIO import StringIO
+
 from bzrlib import (
-    delta,
+    delta as _mod_delta,
     inventory,
     tests,
     )
@@ -45,7 +48,7 @@
         if old_path is not None:
             inv.add(inventory.InventoryFile(file_id, old_path,
                                             inv.root.file_id))
-        reporter = delta.ChangeReporter(inv, result_line)
+        reporter = _mod_delta.ChangeReporter(inv, result_line)
         reporter.report(file_id, path, versioned_change, renamed, modified,
                          exe_change, kind)
         self.assertEqualDiff(expected, result[0])
@@ -53,6 +56,8 @@
     def test_rename(self):
         self.assertReport('R   old => path', renamed=True, old_path='old')
         self.assertReport('    path')
+        self.assertReport('RN  old => path', renamed=True, old_path='old',
+                          modified='created', kind=(None, 'file'))
 
     def test_kind(self):
         self.assertReport(' K  path => path/', modified='kind changed',
@@ -67,6 +72,8 @@
                           kind=(None, 'directory'))
         self.assertReport('+   path/', versioned_change='added',
                           modified='unchanged', kind=(None, 'directory'))
+        self.assertReport('+   path', versioned_change='added',
+                          modified='unchanged', kind=(None, None))
         self.assertReport('+N  path/', versioned_change='added',
                           modified='created', kind=(None, 'directory'))
         self.assertReport('+M  path/', versioned_change='added',
@@ -100,8 +107,8 @@
                            modified='unchanged',
                            exe_change=False):
         reporter = InstrumentedReporter()
-        delta.report_changes([(file_id, path, content_change, versioned,
-                               parent_id, name, kind, executable)], reporter)
+        _mod_delta.report_changes([(file_id, path, content_change, versioned,
+            parent_id, name, kind, executable)], reporter)
         output = reporter.calls[0]
         self.assertEqual(file_id, output[0])
         self.assertEqual(path, output[1])
@@ -150,3 +157,56 @@
                                 exe_change=True, versioned=(True, False),
                                 content_change=True, name=('old', 'new'),
                                 executable=(False, True))
+
+
+class TestChangesFrom (tests.TestCaseWithTransport):
+
+    def show_string(self, delta, *args,  **kwargs):
+        to_file = StringIO()
+        delta.show(to_file, *args, **kwargs)
+        return to_file.getvalue()
+
+    def test_kind_change(self):
+        """Doing a status when a file has changed kind should work"""
+        tree = self.make_branch_and_tree('.')
+        self.build_tree(['filename'])
+        tree.add('filename', 'file-id')
+        tree.commit('added filename')
+        os.unlink('filename')
+        self.build_tree(['filename/'])
+        delta = tree.changes_from(tree.basis_tree())
+        self.assertEqual([('filename', 'file-id', 'file', 'directory')],
+                         delta.kind_changed)
+        self.assertEqual([], delta.added)
+        self.assertEqual([], delta.removed)
+        self.assertEqual([], delta.renamed)
+        self.assertEqual([], delta.modified)
+        self.assertEqual([], delta.unchanged)
+        self.assertTrue(delta.has_changed())
+        self.assertTrue(delta.touches_file_id('file-id'))
+        self.assertEqual('kind changed:\n  filename (file => directory)\n',
+                         self.show_string(delta))
+        other_delta = _mod_delta.TreeDelta()
+        self.assertNotEqual(other_delta, delta)
+        other_delta.kind_changed = [('filename', 'file-id', 'file',
+                                     'symlink')]
+        self.assertNotEqual(other_delta, delta)
+        other_delta.kind_changed = [('filename', 'file-id', 'file',
+                                     'directory')]
+        self.assertEqual(other_delta, delta)
+        self.assertEqualDiff("TreeDelta(added=[], removed=[], renamed=[],"
+            " kind_changed=[(u'filename', 'file-id', 'file', 'directory')],"
+            " modified=[], unchanged=[])", repr(delta))
+        self.assertEqual('K  filename (file => directory) file-id\n',
+                         self.show_string(delta, show_ids=True,
+                         short_status=True))
+
+        tree.rename_one('filename', 'dirname')
+        delta = tree.changes_from(tree.basis_tree())
+        self.assertEqual([], delta.kind_changed)
+        # This loses the fact that kind changed, remembering it as a
+        # modification
+        self.assertEqual([('filename', 'dirname', 'file-id', 'directory',
+                           True, False)], delta.renamed)
+        self.assertTrue(delta.has_changed())
+        self.assertTrue(delta.touches_file_id('file-id'))

=== modified file 'bzrlib/tests/test_merge.py'
--- a/bzrlib/tests/test_merge.py	2007-01-31 03:51:36 +0000
+++ b/bzrlib/tests/test_merge.py	2007-02-06 15:32:30 +0000
@@ -23,7 +23,7 @@
 from bzrlib.conflicts import ConflictList, TextConflict
 from bzrlib.errors import UnrelatedBranches, NoCommits, BzrCommandError
 from bzrlib.merge import transform_tree, merge_inner
-from bzrlib.osutils import pathjoin
+from bzrlib.osutils import pathjoin, file_kind
 from bzrlib.revision import common_ancestor
 from bzrlib.tests import TestCaseWithTransport
 from bzrlib.trace import (enable_test_log, disable_test_log)
@@ -186,3 +186,23 @@
         other_tree = tree_a.basis_tree()
         os.unlink('tree_b/file')
         merge_inner(tree_b.branch, other_tree, base_tree, this_tree=tree_b)
+
+    def test_merge_kind_change(self):
+        tree_a = self.make_branch_and_tree('tree_a')
+        self.build_tree_contents([('tree_a/file', 'content_1')])
+        tree_a.add('file', 'file-id')
+        tree_a.commit('added file')
+        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
+        os.unlink('tree_a/file')
+        self.build_tree(['tree_a/file/'])
+        tree_a.commit('changed file to directory')
+        tree_b.merge_from_branch(tree_a.branch)
+        self.assertEqual('directory', file_kind('tree_b/file'))
+        tree_b.revert([])
+        self.assertEqual('file', file_kind('tree_b/file'))
+        self.build_tree_contents([('tree_b/file', 'content_2')])
+        tree_b.commit('content change')
+        tree_b.merge_from_branch(tree_a.branch)
+        self.assertEqual(tree_b.conflicts(),
+                         [conflicts.ContentsConflict('file',
+                          file_id='file-id')])

=== modified file 'bzrlib/tests/test_transport.py'
--- a/bzrlib/tests/test_transport.py	2006-11-30 14:55:34 +0000
+++ b/bzrlib/tests/test_transport.py	2007-02-05 05:19:13 +0000
@@ -39,7 +39,8 @@
                               Transport,
                               )
 from bzrlib.transport.memory import MemoryTransport
-from bzrlib.transport.local import LocalTransport
+from bzrlib.transport.local import (LocalTransport,
+                                    EmulatedWin32LocalTransport)
 
 
 # TODO: Should possibly split transport-specific tests into their own files.
@@ -551,3 +552,18 @@
         t = get_transport(here_url)
         self.assertIsInstance(t, LocalTransport)
         self.assertEquals(t.base, here_url)
+
+
+class TestWin32LocalTransport(TestCase):
+
+    def test_unc_clone_to_root(self):
+        # Win32 UNC path like \\HOST\path
+        # clone to root should stop at least at \\HOST part
+        # not on \\
+        t = EmulatedWin32LocalTransport('file://HOST/path/to/some/dir/')
+        for i in xrange(4):
+            t = t.clone('..')
+        self.assertEquals(t.base, 'file://HOST/')
+        # make sure we reach the root
+        t = t.clone('..')
+        self.assertEquals(t.base, 'file://HOST/')

=== modified file 'bzrlib/tests/test_transport_implementations.py'
--- a/bzrlib/tests/test_transport_implementations.py	2006-12-01 15:06:29 +0000
+++ b/bzrlib/tests/test_transport_implementations.py	2007-01-28 21:43:32 +0000
@@ -1030,14 +1030,14 @@
         # directory of this transport
         root_transport = orig_transport
         new_transport = root_transport.clone("..")
-        # as we are walking up directories, the path must be must be 
+        # as we are walking up directories, the path must be
         # growing less, except at the top
         self.assertTrue(len(new_transport.base) < len(root_transport.base)
             or new_transport.base == root_transport.base)
         while new_transport.base != root_transport.base:
             root_transport = new_transport
             new_transport = root_transport.clone("..")
-            # as we are walking up directories, the path must be must be 
+            # as we are walking up directories, the path must be
             # growing less, except at the top
             self.assertTrue(len(new_transport.base) < len(root_transport.base)
                 or new_transport.base == root_transport.base)

=== modified file 'bzrlib/transport/local.py'
--- a/bzrlib/transport/local.py	2006-12-17 19:11:54 +0000
+++ b/bzrlib/transport/local.py	2007-02-05 06:17:38 +0000
@@ -75,7 +75,13 @@
         if offset is None:
             return LocalTransport(self.base)
         else:
-            return LocalTransport(self.abspath(offset))
+            abspath = self.abspath(offset)
+            if abspath == 'file://':
+                # fix upwalk for UNC path
+                # when clone from //HOST/path updir recursively
+                # we should stop at least at //HOST part
+                abspath = self.base
+            return LocalTransport(abspath)
 
     def _abspath(self, relative_reference):
         """Return a path for use in os calls.
@@ -474,6 +480,38 @@
             return True
 
 
+class EmulatedWin32LocalTransport(LocalTransport):
+    """Special transport for testing Win32 [UNC] paths on non-windows"""
+
+    def __init__(self, base):
+        if base[-1] != '/':
+            base = base + '/'
+        super(LocalTransport, self).__init__(base)
+        self._local_base = urlutils._win32_local_path_from_url(base)
+
+    def abspath(self, relpath):
+        assert isinstance(relpath, basestring), (type(relpath), relpath)
+        path = osutils.normpath(osutils.pathjoin(
+                    self._local_base, urlutils.unescape(relpath)))
+        return urlutils._win32_local_path_to_url(path)
+
+    def clone(self, offset=None):
+        """Return a new LocalTransport with root at self.base + offset
+        Because the local filesystem does not require a connection, 
+        we can just return a new object.
+        """
+        if offset is None:
+            return EmulatedWin32LocalTransport(self.base)
+        else:
+            abspath = self.abspath(offset)
+            if abspath == 'file://':
+                # fix upwalk for UNC path
+                # when clone from //HOST/path updir recursively
+                # we should stop at least at //HOST part
+                abspath = self.base
+            return EmulatedWin32LocalTransport(abspath)
+
+
 class LocalURLServer(Server):
     """A pretend server for local transports, using file:// urls.
     

=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py	2007-01-11 15:57:49 +0000
+++ b/bzrlib/tree.py	2007-02-05 15:27:54 +0000
@@ -347,7 +347,8 @@
     All matches in all trees will be used, and all children of matched
     directories will be used.
 
-    :param filenames: The filenames to find file_ids for
+    :param filenames: The filenames to find file_ids for (if None, returns
+        None)
     :param trees: The trees to find file_ids within
     :param require_versioned: if true, all specified filenames must occur in
     at least one tree.

=== modified file 'bzrlib/urlutils.py'
--- a/bzrlib/urlutils.py	2006-12-08 10:01:38 +0000
+++ b/bzrlib/urlutils.py	2007-01-28 21:43:32 +0000
@@ -405,7 +405,7 @@
     if not url.endswith('/'):
         # Nothing to do
         return url
-    if sys.platform == 'win32' and url.startswith('file:///'):
+    if sys.platform == 'win32' and url.startswith('file://'):
         return _win32_strip_local_trailing_slash(url)
 
     scheme_loc, first_path_slash = _find_scheme_and_separator(url)

=== modified file 'bzrlib/workingtree.py'
--- a/bzrlib/workingtree.py	2007-02-02 09:17:23 +0000
+++ b/bzrlib/workingtree.py	2007-02-04 12:49:55 +0000
@@ -1637,18 +1637,17 @@
         for f in files:
             fid = inv.path2id(f)
             if not fid:
-                # TODO: Perhaps make this just a warning, and continue?
-                # This tends to happen when 
-                raise errors.NotVersionedError(path=f)
-            if verbose:
-                # having remove it, it must be either ignored or unknown
-                if self.is_ignored(f):
-                    new_status = 'I'
-                else:
-                    new_status = '?'
-                textui.show_status(new_status, inv[fid].kind, f,
-                                   to_file=to_file)
-            del inv[fid]
+                note("%s is not versioned."%f)
+            else:
+                if verbose:
+                    # having remove it, it must be either ignored or unknown
+                    if self.is_ignored(f):
+                        new_status = 'I'
+                    else:
+                        new_status = '?'
+                    textui.show_status(new_status, inv[fid].kind, f,
+                                       to_file=to_file)
+                del inv[fid]
 
         self._write_inventory(inv)
 



More information about the bazaar-commits mailing list