diff --git a/CHANGES.txt b/CHANGES.txt
index ff5bfd5ef629a83abc273e672417e6b8358107fd..49c5d27225fc5ca61865d0df6e85f528d579a210 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -28,7 +28,12 @@ Next Release
 Feature Changes
 ---------------
 
-Renamed the runsetup command to setup. (The old name still works.)
+- Renamed the runsetup command to setup. (The old name still works.)
+
+- Added a recipe update method. Now install is only called when a part
+  is installed for the first time, or after an uninstall. Otherwise, 
+  update is called.  For backward compatibility, recipes that don't
+  define update methiods are still supported.
 
 1.0.0b9 (2006-10-02)
 ====================
diff --git a/src/zc/buildout/buildout.py b/src/zc/buildout/buildout.py
index 3516a8dbc801705636269ef840d1dce9981a1ec1..c06396c97941c7d927b8fc88da56a2ad270d124f 100644
--- a/src/zc/buildout/buildout.py
+++ b/src/zc/buildout/buildout.py
@@ -288,10 +288,21 @@ class Buildout(dict):
             for part in reversed(installed_parts):
                 if part in install_parts:
                     old_options = installed_part_options[part].copy()
-                    old_options.pop('__buildout_installed__')
+                    installed_files = old_options.pop('__buildout_installed__')
                     new_options = self.get(part)
                     if old_options == new_options:
-                        continue
+                        # The options are the same, but are all of the
+                        # installed files still there?  If not, we should
+                        # reinstall.
+                        if not installed_files:
+                            continue
+                        for f in installed_files.split('\n'):
+                            if not os.path.exists(self._buildout_path(f)):
+                                break
+                        else:
+                            continue
+
+                    # output debugging info
                     for k in old_options:
                         if k not in new_options:
                             self._logger.debug("Part: %s, dropped option %s",
@@ -305,6 +316,7 @@ class Buildout(dict):
                         if k not in old_options:
                             self._logger.debug("Part: %s, new option %s",
                                                part, k)
+
                 elif not uninstall_missing:
                     continue
 
@@ -316,17 +328,52 @@ class Buildout(dict):
 
             # install new parts
             for part in install_parts:
-                self._logger.info('Installing %s', part)
-                installed_part_options[part] = self[part].copy()
-                del self[part]['__buildout_signature__']
-                installed_files = recipes[part].install() or ()
+                signature = self[part].pop('__buildout_signature__')
+                saved_options = self[part].copy()
+                if part in installed_parts:
+                    self._logger.info('Updating %s', part)
+                    old_options = installed_part_options[part]
+                    old_installed_files = old_options['__buildout_installed__']
+                    try:
+                        update = recipes[part].update
+                    except AttributeError:
+                        update = recipes[part].install
+                        self._logger.warning(
+                            "The recipe for %s doesn't define an update "
+                            "method. Using it's install method",
+                            part)
+                        
+                    try:
+                        installed_files = update()
+                    except:
+                        installed_parts.remove(part)
+                        self._uninstall(old_installed_files)
+                        raise
+                    
+                    if installed_files is None:
+                        installed_files = old_installed_files.split('\n')
+
+                else:
+                    self._logger.info('Installing %s', part)
+                    installed_files = recipes[part].install()
+                    if installed_files is None:
+                        self._logger.warning(
+                            "The %s install returned None.  A path or "
+                            "iterable os paths should be returned.",
+                            part)
+                        installed_files = ()
+                    
                 if isinstance(installed_files, str):
                     installed_files = [installed_files]
-                installed_part_options[part]['__buildout_installed__'] = (
-                    '\n'.join(installed_files)
-                    )
+
+                installed_part_options[part] = saved_options
+                saved_options['__buildout_installed__'
+                              ] = '\n'.join(installed_files)
+                saved_options['__buildout_signature__'] = signature
+
                 if part not in installed_parts:
                     installed_parts.append(part)
+
         finally:
             installed_part_options['buildout']['parts'] = ' '.join(
                 [p for p in conf_parts if p in installed_parts]
@@ -475,7 +522,9 @@ class Buildout(dict):
         return self._buildout_path(self['buildout']['installed'])
 
     def _uninstall(self, installed):
-        for f in installed.split():
+        for f in installed.split('\n'):
+            if not f:
+                continue
             f = self._buildout_path(f)
             if os.path.isdir(f):
                 shutil.rmtree(f)
diff --git a/src/zc/buildout/buildout.txt b/src/zc/buildout/buildout.txt
index 39ce191fc886f0db0a404611ef5879fc441abacb..86eae41e5fea2169515ebdbd685d068efa0f85a6 100644
--- a/src/zc/buildout/buildout.txt
+++ b/src/zc/buildout/buildout.txt
@@ -14,9 +14,8 @@ This document describes how to define buildouts using buildout
 configuration files and recipes.  There are three ways to set up the
 buildout software and create a buildout instance:
 
-1. Install the zc.buildout egg with easy_install `easy_install
-   <http://peak.telecommunity.com/DevCenter/EasyInstall>`_ and use the
-   buildout script installed in a Python scripts area.
+1. Install the zc.buildout egg with easy_install and use the buildout
+   script installed in a Python scripts area.
 
 2. Use the buildout bootstrap script to create a buildout that
    includes both the setuptools and zc.buildout eggs.  This allows you
@@ -31,7 +30,7 @@ buildout software and create a buildout instance:
 Often, a software project will be managed in a software repository,
 such as a subversion repository, that includes some software source
 directories, buildout configuration files, and a copy of the buildout
-bootstrap script,  To work on the project, one would check out the
+bootstrap script.  To work on the project, one would check out the
 project from the repository and run the bootstrap script which
 installs setuptools and zc.buildout into the checkout as well as any
 parts defined.
@@ -92,14 +91,23 @@ A part is simply something to be created by a buildout.  It can be
 almost anything, such as a Python package, a program, a directory, or
 even a configuration file.  
 
+Recipes
+-------
+
 A part is created by a recipe.  Recipes are always installed as Python
 eggs. They can be downloaded from a package server, such as the
-Python Package Index, or they can be developed as part of a project.
-Let's create a recipe as part of the sample project.  We'll create a
-recipe for creating directories.  
+Python Package Index, or they can be developed as part of a project
+using a "develop" egg.  
+
+A develop egg is a special kind of egg that gets installed as an "egg
+link" that contains the name of a source directory.  Develop eggs
+don't have to be packaged for distribution to be used and can be
+modified in place, which is especially useful while they are being
+developed.
 
-First, we'll create a recipes directory for
-our local recipes:
+Let's create a recipe as part of the sample project.  We'll create a
+recipe for creating directories.  First, we'll create a recipes source
+directory for our local recipes:
 
     >>> mkdir(sample_buildout, 'recipes')
 
@@ -128,56 +136,81 @@ and then we'll create a source file for our mkdir recipe:
     ...
     ...     def install(self):
     ...         path = self.options['path']
-    ...         if not os.path.isdir(path):
-    ...             logging.getLogger(self.name).info(
-    ...                 'Creating directory %s', os.path.basename(path))
-    ...             os.mkdir(path)
+    ...         logging.getLogger(self.name).info(
+    ...             'Creating directory %s', os.path.basename(path))
+    ...         os.mkdir(path)
     ...         return path
+    ...
+    ...     def update(self):
+    ...         pass
     ... """)
 
-The recipe defines a constructor that takes a buildout object, a part
-name, and an options dictionary. It saves them in instance attributes.
+Currently, recipes must define 3 methods [#future_recipe_methods]_:
 
-If the path is relative, we'll interpret it as relative to the
-buildout directory.  The buildout object passed in is a mapping from
-section name to a mapping of options for that section. The buildout
-directory is available as the directory option of the buildout
-section.  We normalize the path and save it back into the options
-directory.  
+- a constructor,
+
+- an install method, and
 
-Any time we use data from another section, it is important to reflect
-that data in the recipe's options when the recipe is constructed.
+- an update method.
+
+The constructor is responsible for updating a parts options to reflect
+data read from other sections.  The buildout system keeps track of
+whether a part specification has changed.  A part specification has
+changed if it's options, after ajusting for data read from other
+sections, has changed, or if the recipe has changed.  Only the options
+for the part are considered.  If data are read from other sections,
+then that information has to be reflected in the parts options.  In
+the Mkdir example, the given path is interpreted relative to the
+buildout directory, and data from the buildout directory is read.  The
+path option is updated to reflect this.  If the directory option was
+changed in the buildout sections, we would know to update parts
+created using the mkdir recipe using relative path names.
 
 When buildout is run, it saves configuration data for installed parts
-in a file named installed.cfg.  In subsequent runs, it compares
-part-configuration data stored in the installed.cfg file and the
+in a file named ".installed.cfg".  In subsequent runs, it compares
+part-configuration data stored in the .installed.cfg file and the
 part-configuration data loaded from the configuration files as
 modified by recipe constructors to decide if the configuration of a
 part has changed. If the configuration has changed, or if the recipe
-has changed, then the part is uninstalled before reinstalling it.  The
+has changed, then the part is uninstalled and reinstalled.  The
 buildout only looks at the part's options, so any data used to
 configure the part needs to be reflected in the part's options.  It is
 the job of a recipe constructor to make sure that the options include
-all rel event data.
+all relevent data.
 
 Of course, parts are also uninstalled if they are no-longer used.
 
-The install method is responsible for creating the part.  In this
-case, we need the path of the directory to create.  We'll use a
-path option from our options dictionary.
-
-The install method logs what it's doing using the Python logging call.
+The recipe defines a constructor that takes a buildout object, a part
+name, and an options dictionary. It saves them in instance attributes.
+If the path is relative, we'll interpret it as relative to the
+buildout directory.  The buildout object passed in is a mapping from
+section name to a mapping of options for that section. The buildout
+directory is available as the directory option of the buildout
+section.  We normalize the path and save it back into the options
+directory.  
 
-We return the path that we installed.  If the part is uninstalled or
-reinstalled, then the path returned will be removed by the buildout
-machinery.  A recipe install method is expected to return None, a
-string, or an iterable of strings containing paths to be removed if a
-part is uninstalled.  For most recipes, this is all of the uninstall
-support needed.  A recipe can provide custom uninstall support as will
-be described later.
+The install method is responsible for creating the part.  In this
+case, we need the path of the directory to create.  We'll use a path
+option from our options dictionary.  The install method logs what it's
+doing using the Python logging call.  We return the path that we
+installed.  If the part is uninstalled or reinstalled, then the path
+returned will be removed by the buildout machinery.  A recipe install
+method is expected to return a string, or an iterable of strings
+containing paths to be removed if a part is uninstalled.  For most
+recipes, this is all of the uninstall support needed.
+
+The update method is responsible for updating an already installed
+part.  An empty method is often provided, as in this example, if parts
+can't be updated.  An update method can return None, a string, or an
+iterable of strings.  If a string or iterable of strings is returned,
+then the saved list of paths to be uninstalled is updated with the new
+information. 
 
 We need to provide packaging information so that our recipe can be
-installed as an egg.  We need to define a setup script for this:
+installed as a develop egg. The minimum information we need to specify
+[#packaging_info]_ is a name.  For recipes, we also need to define the
+names of the recipe classes as entry points.  Packaging information is
+provided via a setup.py script:
 
     >>> write(sample_buildout, 'recipes', 'setup.py',
     ... """
@@ -189,21 +222,11 @@ installed as an egg.  We need to define a setup script for this:
     ...     )
     ... """)
 
-This setup script is incomplete.  It doesn't describe what is to be 
-included in a distribution.  This is fine if we never actually create
-a distribution. If recipes are going to be used only internally in a
-buildout, then we needn't include distribution information.  If we
-wanted to use the same recipes in multiple buildouts, then we'd need
-to include proper distribution data.  To find out more about creating
-distributions, see the setuptools documentation.
-
 Our setup script defines an entry point. Entry points provide
 a way for an egg to define the services it provides.  Here we've said
-that we define a zc.buildout entry point named default.  Recipe
+that we define a zc.buildout entry point named mkdir.  Recipe
 classes must be exposed as entry points in the zc.buildout group.  we
-give entry points names within the group.  The name "default" is
-somewhat special because it allows a recipe to be referenced using a
-package name without naming an entry point.
+give entry points names within the group.
 
 We also need a README.txt for our recipes to avoid an annoying warning
 from distutils, on which setuptools and zc.buildout are based:
@@ -326,6 +349,15 @@ we'll see that the directory gets removed and recreated:
     d  parts
     d  recipes
 
+If any of the files or directories created by a recipe are removed,
+the part will be reinstalled:
+
+    >>> rmdir(sample_buildout, 'mydata')
+    >>> print system(buildout),
+    buildout: Develop: /sample-buildout/recipes/setup.py
+    buildout: Uninstalling data-dir
+    buildout: Installing data-dir
+    data-dir: Creating directory mydata
 
 Error reporting
 ---------------
@@ -404,6 +436,9 @@ allow us to see interactions with the buildout:
     ...         items.sort()
     ...         for option, value in items:
     ...             print option, value
+    ...         return ()
+    ...
+    ...     update = install
     ... """)
 
 In this example, we've used a simple base class that provides a
@@ -488,8 +523,8 @@ the buildout:
 
     >>> print system(buildout),
     buildout: Develop: /sample-buildout/recipes/setup.py
-    buildout: Installing data-dir
-    buildout: Installing debug
+    buildout: Updating data-dir
+    buildout: Updating debug
     File 1 mydata/file
     File 2 mydata/file.out
     File 3 var/file3
@@ -799,7 +834,7 @@ Options can also be combined in the usual Unix way, as in:
     
     >>> print system(buildout+' -vcother.cfg debug:op1=foo'),
     buildout: Develop: /sample-buildout/recipes/setup.py
-    buildout: Installing debug
+    buildout: Updating debug
     name other
     op1 foo
     recipe recipes:debug
@@ -1000,8 +1035,8 @@ Now, if we run the buildout without the install command:
     x 1
     buildout: Installing d2
     d2: Creating directory data2
-    buildout: Installing d3
-    buildout: Installing d4
+    buildout: Updating d3
+    buildout: Updating d4
 
 We see the output of the debug recipe and that data2 was created.  We
 also see that d1 and d2 have gone away:
@@ -1346,3 +1381,13 @@ We see that out extension is loaded and executed:
     ext ['buildout']
     buildout: Develop: /sample-bootstrapped/demo/setup.py
 
+
+
+.. [#future_recipe_methods] In the future, additional mathods may be
+       added. Older recipes with fewer methods will still be
+       supported.
+
+.. [#packaging_info] If we wanted to create a distribution from this
+       package, we would need specify much more information.  See the
+       `setuptools documentation
+       <http://peak.telecommunity.com/DevCenter/setuptools>`_.
diff --git a/src/zc/buildout/tests.py b/src/zc/buildout/tests.py
index 5d70b120d2cfd121ede32ae8632eda454be81751..9dc0d395645f4e5ffa4fc5bc06eff8f27c0babdb 100644
--- a/src/zc/buildout/tests.py
+++ b/src/zc/buildout/tests.py
@@ -178,6 +178,8 @@ def test_comparing_saved_options_with_funny_characters():
     ...     def install(self):
     ...         open('t', 'w').write('t')
     ...         return 't'
+    ...
+    ...     update = install
     ... ''')
 
 
@@ -214,7 +216,7 @@ uninstalling anything because the configuration hasn't changed.
 
     >>> print system(buildout), # doctest: +ELLIPSIS
     buildout: Develop: ...setup.py
-    buildout: Installing debug
+    buildout: Updating debug
 """
 
 
@@ -277,22 +279,22 @@ Options:
 <BLANKLINE>
   -q
 <BLANKLINE>
-     Deccreaae the level of verbosity.  This option can be used multiple times.
+     Decrease the level of verbosity.  This option can be used multiple times.
 <BLANKLINE>
   -c config_file
 <BLANKLINE>
      Specify the path to the buildout configuration file to be used.
-     This defaults to the file named"buildout.cfg" in the current
-     working directory. 
+     This defaults to the file named "buildout.cfg" in the current
+     working directory.
 <BLANKLINE>
 Assignments are of the form: section:option=value and are used to
-provide configuration options that override those givem in the
+provide configuration options that override those given in the
 configuration file.  For example, to run the buildout in offline mode,
 use buildout:offline=true.
 <BLANKLINE>
 Options and assignments can be interspersed.
 <BLANKLINE>
-Commmonds:
+Commands:
 <BLANKLINE>
   install [parts]
 <BLANKLINE>
@@ -324,22 +326,22 @@ Options:
 <BLANKLINE>
   -q
 <BLANKLINE>
-     Deccreaae the level of verbosity.  This option can be used multiple times.
+     Decrease the level of verbosity.  This option can be used multiple times.
 <BLANKLINE>
   -c config_file
 <BLANKLINE>
      Specify the path to the buildout configuration file to be used.
-     This defaults to the file named"buildout.cfg" in the current
-     working directory. 
+     This defaults to the file named "buildout.cfg" in the current
+     working directory.
 <BLANKLINE>
 Assignments are of the form: section:option=value and are used to
-provide configuration options that override those givem in the
+provide configuration options that override those given in the
 configuration file.  For example, to run the buildout in offline mode,
 use buildout:offline=true.
 <BLANKLINE>
 Options and assignments can be interspersed.
 <BLANKLINE>
-Commmonds:
+Commands:
 <BLANKLINE>
   install [parts]
 <BLANKLINE>
diff --git a/src/zc/buildout/update.txt b/src/zc/buildout/update.txt
index 8af8773b94762ed49d01a3a975cd24db277cceb6..3eecbdfbe5680a3230cbb2564d4730d5cb0717d6 100644
--- a/src/zc/buildout/update.txt
+++ b/src/zc/buildout/update.txt
@@ -44,6 +44,7 @@ zc.buildout used:
     ...         for project in 'zc.buildout', 'setuptools':
     ...             req = pkg_resources.Requirement.parse(project)
     ...             print project, pkg_resources.working_set.find(req).version
+    ...         return ()
     ... """)
 
 
diff --git a/zc.recipe.egg_/CHANGES.txt b/zc.recipe.egg_/CHANGES.txt
index 3ad5837ba674083bab996953765a6fdd9f8c1b05..41a3aa393b3a1259ff0022a494b9638c1fb3e7da 100644
--- a/zc.recipe.egg_/CHANGES.txt
+++ b/zc.recipe.egg_/CHANGES.txt
@@ -8,6 +8,8 @@ To do
 Change History
 **************
 
+Updated to work with zc.buildout 1.0.0b10.
+
 1.0.0b1
 =======
 
diff --git a/zc.recipe.egg_/src/zc/recipe/egg/api.txt b/zc.recipe.egg_/src/zc/recipe/egg/api.txt
index 5a69068ef67510089818b9d10bb0e63d942cabfc..f439305b5c4a9efe74eb11141e968c6df747872e 100644
--- a/zc.recipe.egg_/src/zc/recipe/egg/api.txt
+++ b/zc.recipe.egg_/src/zc/recipe/egg/api.txt
@@ -38,6 +38,9 @@ around the egg recipe:
     ...         for d in ws:
     ...             print d
     ...         print 'extra paths:', self.egg.extra_paths
+    ...         return ()
+    ...
+    ...     update = install
     ... """)
 
 Here we instantiated the egg recipe in the constructor, saving it in
diff --git a/zc.recipe.egg_/src/zc/recipe/egg/custom.py b/zc.recipe.egg_/src/zc/recipe/egg/custom.py
index da202afea31e4d7484ea2ce7b2d4501fb9f87861..ba081b6dd54aa384d12f1208a29d59702f947e49 100644
--- a/zc.recipe.egg_/src/zc/recipe/egg/custom.py
+++ b/zc.recipe.egg_/src/zc/recipe/egg/custom.py
@@ -67,7 +67,7 @@ class Custom:
 
     def install(self):
         if self.buildout['buildout'].get('offline') == 'true':
-            return
+            return ()
         options = self.options
         distribution = options.get('eggs', self.name).strip()
         build_ext = dict([
@@ -80,3 +80,6 @@ class Custom:
             self.links, self.index, options['executable'], [options['_e']],
             )
         
+        return ()
+
+    update = install
diff --git a/zc.recipe.egg_/src/zc/recipe/egg/egg.py b/zc.recipe.egg_/src/zc/recipe/egg/egg.py
index 1fa9d47e3277a2e0d27485e5c40b58b5211a3530..599e96f1804a1f4e114a50958e3095ac5221182e 100644
--- a/zc.recipe.egg_/src/zc/recipe/egg/egg.py
+++ b/zc.recipe.egg_/src/zc/recipe/egg/egg.py
@@ -119,3 +119,6 @@ class Egg:
                 interpreter=options.get('interpreter'),
                 )
 
+        return ()
+
+    update = install
diff --git a/zc.recipe.testrunner/CHANGES.txt b/zc.recipe.testrunner/CHANGES.txt
index 8f5bf9e99526d081e0ff6be2e0fcc468a8364d55..f6e5ec0ed68747207c7a8e52e675ebf19ca2aa84 100644
--- a/zc.recipe.testrunner/CHANGES.txt
+++ b/zc.recipe.testrunner/CHANGES.txt
@@ -2,6 +2,8 @@
 Change History
 **************
 
+Updated to work with zc.buildout 1.0.0b10.
+
 1.0.0b2
 =======
 
diff --git a/zc.recipe.testrunner/src/zc/recipe/testrunner/__init__.py b/zc.recipe.testrunner/src/zc/recipe/testrunner/__init__.py
index cd67f2e49455f3dde9caa39bb307ae0fdd4c3dcb..1e10b7dd3b658a82f8155e130db22e5b961b7062 100644
--- a/zc.recipe.testrunner/src/zc/recipe/testrunner/__init__.py
+++ b/zc.recipe.testrunner/src/zc/recipe/testrunner/__init__.py
@@ -54,6 +54,8 @@ class TestRunner:
                 )),
             )
 
+    update = install
+
 arg_template = """[
   '--test-path', %(TESTPATH)s,
   ]"""