Repository: conda/conda

Issue:
conda env update --prune does not remove installed packages not defined in environment.yml
**I'm submitting a...**
  - [x] bug report
  - [ ] feature request


## Current Behavior
`conda env update --prune` does not remove packages no longer referenced in the `environment.yml` file.


### Steps to Reproduce
<!-- If the current behavior is a bug, please provide specific, minimal steps to independently reproduce.
     Include the exact conda commands that reproduce the issue and their output between the ticks below. -->

Define an `environment.yml` file in a project directory as follows:

    name: new-project-env
    dependencies:
    - python=3
    - numpy

In the project directory run the following commands:

    conda env create
    conda activate new-project-env
    python
    import numpy
    exit()

This succeeds as expected.
Update the `environment.yml` file as follows, **removing numpy**,

    name: new-project-env
    dependencies:
    - python=3

Run the following commands in the project directory:

    conda deactivate
    conda env update --prune
    conda activate new-project-env
    python
    import numpy
    exit()

**This numpy import succeeds and seems like a bug**. 

## Expected Behavior
Per [the docs](https://conda.io/docs/commands/env/conda-env-update.html) (--prune means "remove installed packages not defined in environment.yml") I expect that after installing a package (numpy in the example above) and subsquently removing it from `environment.yml`  the package should be removed from the relevant conda environment.

## Current Workaround
I've found that we can use `conda env create --force` to overwrite the existing environment and thus ensure that the environment accurately reflects the `environment.yml` file. But this requires recreating the environment from scratch.

## Environment Information
<details open><summary><code>`conda info`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda info' -->

```
     active environment : new-project-env
    active env location : /home/mikhailgolubitsky/anaconda3/envs/new-project-env
            shell level : 2
       user config file : /home/mikhailgolubitsky/.condarc
 populated config files : /home/mikhailgolubitsky/.condarc
          conda version : 4.5.3
    conda-build version : 3.0.27
         python version : 3.6.3.final.0
       base environment : /home/mikhailgolubitsky/anaconda3  (writable)
           channel URLs : https://conda.anaconda.org/conda-forge/linux-64
                          https://conda.anaconda.org/conda-forge/noarch
                          https://repo.anaconda.com/pkgs/main/linux-64
                          https://repo.anaconda.com/pkgs/main/noarch
                          https://repo.anaconda.com/pkgs/free/linux-64
                          https://repo.anaconda.com/pkgs/free/noarch
                          https://repo.anaconda.com/pkgs/r/linux-64
                          https://repo.anaconda.com/pkgs/r/noarch
                          https://repo.anaconda.com/pkgs/pro/linux-64
                          https://repo.anaconda.com/pkgs/pro/noarch
          package cache : /home/mikhailgolubitsky/anaconda3/pkgs
                          /home/mikhailgolubitsky/.conda/pkgs
       envs directories : /home/mikhailgolubitsky/anaconda3/envs
                          /home/mikhailgolubitsky/.conda/envs
               platform : linux-64
             user-agent : conda/4.5.3 requests/2.18.4 CPython/3.6.3 Linux/4.13.0-39-generic ubuntu/17.10 glibc/2.26
                UID:GID : 1000:1000
             netrc file : None
           offline mode : False
```
</p></details>


<details open><summary><code>`conda config --show-sources`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda config --show-sources' -->

```
==> /home/mikhailgolubitsky/.condarc <==
channels:
  - conda-forge
  - defaults
```
</p></details>


<details><summary><code>`conda list --show-channel-urls`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda list --show-channel-urls' -->

```
# packages in environment at /home/mikhailgolubitsky/anaconda3/envs/new-project-env:
#
# Name                    Version                   Build  Channel
ca-certificates           2018.4.16                     0    conda-forge
certifi                   2018.4.16                py36_0    conda-forge
intel-openmp              2018.0.0                      8    defaults
libgcc-ng                 7.2.0                hdf63c60_3    defaults
libgfortran-ng            7.2.0                hdf63c60_3    defaults
mkl                       2018.0.2                      1    defaults
mkl_fft                   1.0.2                    py36_0    conda-forge
mkl_random                1.0.1                    py36_0    conda-forge
ncurses                   5.9                          10    conda-forge
numpy                     1.14.3           py36h14a74c5_0    defaults
numpy-base                1.14.3           py36hdbf6ddf_0    defaults
openssl                   1.0.2o                        0    conda-forge
pip                       9.0.3                    py36_0    conda-forge
python                    3.6.5                         1    conda-forge
readline                  7.0                           0    conda-forge
setuptools                39.1.0                   py36_0    conda-forge
sqlite                    3.20.1                        2    conda-forge
tk                        8.6.7                         0    conda-forge
wheel                     0.31.0                   py36_0    conda-forge
xz                        5.2.3                         0    conda-forge
zlib                      1.2.11                        0    conda-forge


```
</p></details>



Discussion:
This has the same root cause as #6809, but is a better summary of the issue. The problem is that conda looks through its history file and thinks that you've requested those specs in the past, so it adds them back. So, the behaviour of `--prune` since 4.4.0 has been that only dependencies, rather than explicitly requested packages, have been pruned.

In my opinion, the fix would either be for `conda env update` to not take specs from the history at all, or for `conda env` operations to not populate the history at all, or both.

If you'd like a workaround that is faster than `conda env create --force` you can truncate the history file located at `<env>/conda-meta/history` (the file needs to be present but can be empty).

This confused me a lot also. 
The ```truncate the history file located at `<env>/conda-meta/history` ``` hack does not work any longer for me.
I was testing conda 4.7.5 (windows and linux) when stumbled upon this.

> In my opinion, the fix would either be for conda env update to not take specs from the history at all, or for conda env operations to not populate the history at all, or both.

Seems like the correct approach, IMHO. Any comments from the team? We are willing to work on this and provide a PR, as we dearly depend on this and is preventing us from upgrading.
I'm not sure I fully understand the desired behavior, but yes, absolutely, a PR to implement better behavior is welcome.

The party I don't understand is why basing it on the history is bad. It seems like the history of explicit specs would be there best source of minimal information for env specs, but I must be missing the reasons why that doesn't work in practice. Perhaps the reason is that the removal part of the update is not factored in correctly?
> I'm not sure I fully understand the desired behavior, but yes, absolutely, a PR to implement better behavior is welcome.

Sure, let me explain with an example.

## conda <4.4

Suppose we have this `environment.yaml` file:

```yaml
name: env-app

dependencies:
- python
- pytest-mock
```

When running `conda env update` for the 1st time, conda creates an environment with `python`, `pytest-mock`, and their dependencies, including `pytest`, which `pytest-mock` depends on. 

If we change the `environment.yaml` to:

```yaml
name: env-app

dependencies:
- python
```

and execute `conda env update` again, nothing happens, because by default it will only add to the environment, never remove.

When we implemented `--prune` originally, its purpose was to remove every package and dependency not listed in the `environment.yaml` file. 

So adding `--prune` to the 2nd run, conda would uninstall `pytest-mock` and `pytest`, as they are no longer listed.

## conda 4.4+

This has changed since version 4.4 with the introduction of the history feature: conda keeps track of what was installed previously, so users typing `conda install` multiple times would get a consistent environment (that's my understanding of the feature).

But this has also affected `conda env update --prune`, most likely unintentionally (I suspect the reason is that it uses `conda install` behind the scenes).

So what happens in the 2nd run now, using `--prune`, is that `pytest-mock` and `pytest` are kept in the environment.

So effectively `--prune` has been broken since 4.4, as it no longer removes previous packages.


> The party I don't understand is why basing it on the history is bad.

The main use case is to get the exact environment described in the yaml file, exactly what you would get with `conda create --force`, but much quicker as you don't have to delete/re-link everything.



@msarahan are you OK with a PR fixing the behavior of `--prune` as I describe above?
I see, thanks for the explanation.  Yes, any PR that fixes this is welcome. It's not immediately obvious to me how to do it. Perhaps a flag like --replace-specs that ignores the history and only keeps the specs that are passed explicitly.

The implementation of history specs being added in is in conda/core/solve.py in the _add_specs method. Let me know if you have any questions.


Thanks for the pointers! 
Can we raise a warning or `NotImplementedError` for the update `--prune` option until it is fixed? It seems that the Conda project has relatively frequent releases, vs the expected time for this to get fixed.
I am having trouble with the workaround as well (truncating the history file). Step to reproduce (conda 4.8.2):

1. conda create --clone base -n update-test
2. conda activate update-test
3. conda env export > orig.yml
4. conda install ipython
5. truncate --size 0 ~/miniconda3/envs/update-test/conda-meta/history
6. conda env update --prune -f orig.yml

after those steps ipython is still installed.

Resolving patch:
diff --git a/conda/core/solve.py b/conda/core/solve.py
--- a/conda/core/solve.py
+++ b/conda/core/solve.py
@@ -281,6 +281,8 @@ def solve_final_state(
                 the solved state of the environment.
 
         """
+        if prune and update_modifier == UpdateModifier.FREEZE_INSTALLED:
+            update_modifier = NULL
         if update_modifier is NULL:
             update_modifier = context.update_modifier
         else:
@@ -520,54 +522,60 @@ def empty_package_list(pkg):
 
     @time_recorder(module_name=__name__)
     def _collect_all_metadata(self, ssc):
-        # add in historically-requested specs
-        ssc.specs_map.update(ssc.specs_from_history_map)
-
-        # these are things that we want to keep even if they're not explicitly specified.  This
-        #     is to compensate for older installers not recording these appropriately for them
-        #     to be preserved.
-        for pkg_name in (
-            "anaconda",
-            "conda",
-            "conda-build",
-            "python.app",
-            "console_shortcut",
-            "powershell_shortcut",
-        ):
-            if pkg_name not in ssc.specs_map and ssc.prefix_data.get(pkg_name, None):
-                ssc.specs_map[pkg_name] = MatchSpec(pkg_name)
-
-        # Add virtual packages so they are taken into account by the solver
-        virtual_pkg_index = {}
-        _supplement_index_with_system(virtual_pkg_index)
-        virtual_pkgs = [p.name for p in virtual_pkg_index.keys()]
-        for virtual_pkgs_name in virtual_pkgs:
-            if virtual_pkgs_name not in ssc.specs_map:
-                ssc.specs_map[virtual_pkgs_name] = MatchSpec(virtual_pkgs_name)
-
-        for prec in ssc.prefix_data.iter_records():
-            # first check: add everything if we have no history to work with.
-            #    This happens with "update --all", for example.
-            #
-            # second check: add in aggressively updated packages
-            #
-            # third check: add in foreign stuff (e.g. from pip) into the specs
-            #    map. We add it so that it can be left alone more. This is a
-            #    declaration that it is manually installed, much like the
-            #    history map. It may still be replaced if it is in conflict,
-            #    but it is not just an indirect dep that can be pruned.
-            if (
-                not ssc.specs_from_history_map
-                or MatchSpec(prec.name) in context.aggressive_update_packages
-                or prec.subdir == "pypi"
+        if ssc.prune:
+            # When pruning DO NOT consider history of already installed packages when solving.
+            prepared_specs = {*self.specs_to_remove, *self.specs_to_add}
+        else:
+            # add in historically-requested specs
+            ssc.specs_map.update(ssc.specs_from_history_map)
+
+            # these are things that we want to keep even if they're not explicitly specified.  This
+            #     is to compensate for older installers not recording these appropriately for them
+            #     to be preserved.
+            for pkg_name in (
+                "anaconda",
+                "conda",
+                "conda-build",
+                "python.app",
+                "console_shortcut",
+                "powershell_shortcut",
             ):
-                ssc.specs_map.update({prec.name: MatchSpec(prec.name)})
+                if pkg_name not in ssc.specs_map and ssc.prefix_data.get(
+                    pkg_name, None
+                ):
+                    ssc.specs_map[pkg_name] = MatchSpec(pkg_name)
+
+            # Add virtual packages so they are taken into account by the solver
+            virtual_pkg_index = {}
+            _supplement_index_with_system(virtual_pkg_index)
+            virtual_pkgs = [p.name for p in virtual_pkg_index.keys()]
+            for virtual_pkgs_name in virtual_pkgs:
+                if virtual_pkgs_name not in ssc.specs_map:
+                    ssc.specs_map[virtual_pkgs_name] = MatchSpec(virtual_pkgs_name)
+
+            for prec in ssc.prefix_data.iter_records():
+                # first check: add everything if we have no history to work with.
+                #    This happens with "update --all", for example.
+                #
+                # second check: add in aggressively updated packages
+                #
+                # third check: add in foreign stuff (e.g. from pip) into the specs
+                #    map. We add it so that it can be left alone more. This is a
+                #    declaration that it is manually installed, much like the
+                #    history map. It may still be replaced if it is in conflict,
+                #    but it is not just an indirect dep that can be pruned.
+                if (
+                    not ssc.specs_from_history_map
+                    or MatchSpec(prec.name) in context.aggressive_update_packages
+                    or prec.subdir == "pypi"
+                ):
+                    ssc.specs_map.update({prec.name: MatchSpec(prec.name)})
 
-        prepared_specs = {
-            *self.specs_to_remove,
-            *self.specs_to_add,
-            *ssc.specs_from_history_map.values(),
-        }
+            prepared_specs = {
+                *self.specs_to_remove,
+                *self.specs_to_add,
+                *ssc.specs_from_history_map.values(),
+            }
 
         index, r = self._prepare(prepared_specs)
         ssc.set_repository_metadata(index, r)
@@ -731,17 +739,18 @@ def _add_specs(self, ssc):
         # the only things we should consider freezing are things that don't conflict with the new
         #    specs being added.
         explicit_pool = ssc.r._get_package_pool(self.specs_to_add)
+        if ssc.prune:
+            # Ignore installed specs on prune.
+            installed_specs = ()
+        else:
+            installed_specs = [
+                record.to_match_spec() for record in ssc.prefix_data.iter_records()
+            ]
 
         conflict_specs = (
-            ssc.r.get_conflicting_specs(
-                tuple(
-                    record.to_match_spec() for record in ssc.prefix_data.iter_records()
-                ),
-                self.specs_to_add,
-            )
-            or ()
+            ssc.r.get_conflicting_specs(installed_specs, self.specs_to_add) or tuple()
         )
-        conflict_specs = {_.name for _ in conflict_specs}
+        conflict_specs = {spec.name for spec in conflict_specs}
 
         for pkg_name, spec in ssc.specs_map.items():
             matches_for_spec = tuple(
@@ -1342,7 +1351,8 @@ def __init__(
 
         # Group 4. Mutable working containers
         self.specs_map = {}
-        self.solution_precs = tuple(self.prefix_data.iter_records())
+        self.solution_precs = None
+        self._init_solution_precs()
         self.add_back_map = {}  # name: (prec, spec)
         self.final_environment_specs = None
 
@@ -1365,9 +1375,16 @@ def pinned_specs(self):
     def set_repository_metadata(self, index, r):
         self.index, self.r = index, r
 
+    def _init_solution_precs(self):
+        if self.prune:
+            # DO NOT add existing prefix data to solution on prune
+            self.solution_precs = tuple()
+        else:
+            self.solution_precs = tuple(self.prefix_data.iter_records())
+
     def working_state_reset(self):
         self.specs_map = {}
-        self.solution_precs = tuple(self.prefix_data.iter_records())
+        self._init_solution_precs()
         self.add_back_map = {}  # name: (prec, spec)
         self.final_environment_specs = None
 


Test patch:
diff --git a/tests/core/test_solve.py b/tests/core/test_solve.py
--- a/tests/core/test_solve.py
+++ b/tests/core/test_solve.py
@@ -429,6 +429,297 @@ def test_prune_1(tmpdir):
         assert convert_to_dist_str(link_precs) == link_order
 
 
+def test_update_prune_1(tmpdir):
+    """Regression test: Ensure that update with prune is not taking the history
+    into account, since that stops it from removing packages that were removed
+    from the environment specs.
+    """
+    specs = (
+        MatchSpec("numpy"),
+        MatchSpec("python=2.7.3"),
+    )
+
+    with get_solver(tmpdir, specs) as solver:
+        final_state_1 = solver.solve_final_state()
+        pprint(convert_to_dist_str(final_state_1))
+        order = add_subdir_to_iter(
+            (
+                "channel-1::openssl-1.0.1c-0",
+                "channel-1::readline-6.2-0",
+                "channel-1::sqlite-3.7.13-0",
+                "channel-1::system-5.8-1",
+                "channel-1::tk-8.5.13-0",
+                "channel-1::zlib-1.2.7-0",
+                "channel-1::python-2.7.3-7",
+                "channel-1::numpy-1.7.1-py27_0",
+            )
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+    # Here numpy is removed from the specs (but the old specs are kept as
+    # history).
+    new_environment_specs = (MatchSpec("python=2.7.3"),)
+
+    with get_solver(
+        tmpdir,
+        new_environment_specs,
+        prefix_records=final_state_1,
+        history_specs=specs,
+    ) as solver:
+        final_state_1 = solver.solve_final_state(prune=True)
+        pprint(convert_to_dist_str(final_state_1))
+
+        # Numpy should now be absent from the solved packages.
+        order = add_subdir_to_iter(
+            (
+                "channel-1::openssl-1.0.1c-0",
+                "channel-1::readline-6.2-0",
+                "channel-1::sqlite-3.7.13-0",
+                "channel-1::system-5.8-1",
+                "channel-1::tk-8.5.13-0",
+                "channel-1::zlib-1.2.7-0",
+                "channel-1::python-2.7.3-7",
+            )
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+
+def test_update_prune_2(tmpdir):
+    """Regression test: Ensure that update with prune is pruning dependencies
+    of packages that are removed from environment as well.
+    """
+    specs = (
+        MatchSpec("python=2.7.3"),
+        MatchSpec("accelerate"),
+    )
+
+    with get_solver(tmpdir, specs) as solver:
+        final_state_1 = solver.solve_final_state()
+        pprint(convert_to_dist_str(final_state_1))
+        order = add_subdir_to_iter(
+            (
+                "channel-1::libnvvm-1.0-p0",
+                "channel-1::mkl-rt-11.0-p0",
+                "channel-1::openssl-1.0.1c-0",
+                "channel-1::readline-6.2-0",
+                "channel-1::sqlite-3.7.13-0",
+                "channel-1::system-5.8-1",
+                "channel-1::tk-8.5.13-0",
+                "channel-1::zlib-1.2.7-0",
+                "channel-1::llvm-3.2-0",
+                "channel-1::python-2.7.3-7",
+                "channel-1::bitarray-0.8.1-py27_0",
+                "channel-1::llvmpy-0.11.2-py27_0",
+                "channel-1::meta-0.4.2.dev-py27_0",
+                "channel-1::mkl-service-1.0.0-py27_p0",
+                "channel-1::numpy-1.7.1-py27_p0",
+                "channel-1::numba-0.8.1-np17py27_0",
+                "channel-1::numexpr-2.1-np17py27_p0",
+                "channel-1::scipy-0.12.0-np17py27_p0",
+                "channel-1::numbapro-0.11.0-np17py27_p0",
+                "channel-1::scikit-learn-0.13.1-np17py27_p0",
+                "channel-1::mkl-11.0-np17py27_p0",
+                "channel-1::accelerate-1.1.0-np17py27_p0",
+            )
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+    # Here accelerate is removed from the specs (but the old specs are kept as
+    # history). This should cause every dependency of accelerate to disappear
+    # from the pruned package list.
+    new_environment_specs = (MatchSpec("python=2.7.3"),)
+
+    with get_solver(
+        tmpdir,
+        new_environment_specs,
+        prefix_records=final_state_1,
+        history_specs=specs,
+    ) as solver:
+        final_state_1 = solver.solve_final_state(prune=True)
+        pprint(convert_to_dist_str(final_state_1))
+
+        # Every dependecy of accelerate should now be absent from the solved
+        # packages.
+        order = add_subdir_to_iter(
+            (
+                "channel-1::openssl-1.0.1c-0",
+                "channel-1::readline-6.2-0",
+                "channel-1::sqlite-3.7.13-0",
+                "channel-1::system-5.8-1",
+                "channel-1::tk-8.5.13-0",
+                "channel-1::zlib-1.2.7-0",
+                "channel-1::python-2.7.3-7",
+            )
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+
+def test_update_prune_3(tmpdir):
+    """Ensure that update with prune is not removing packages that are still
+    needed by remaining specs.
+    """
+    specs = (
+        MatchSpec("numpy"),
+        MatchSpec("python=2.7.3"),
+        MatchSpec("accelerate"),
+    )
+
+    with get_solver(tmpdir, specs) as solver:
+        final_state_1 = solver.solve_final_state()
+        pprint(convert_to_dist_str(final_state_1))
+        order = add_subdir_to_iter(
+            (
+                "channel-1::libnvvm-1.0-p0",
+                "channel-1::mkl-rt-11.0-p0",
+                "channel-1::openssl-1.0.1c-0",
+                "channel-1::readline-6.2-0",
+                "channel-1::sqlite-3.7.13-0",
+                "channel-1::system-5.8-1",
+                "channel-1::tk-8.5.13-0",
+                "channel-1::zlib-1.2.7-0",
+                "channel-1::llvm-3.2-0",
+                "channel-1::python-2.7.3-7",
+                "channel-1::bitarray-0.8.1-py27_0",
+                "channel-1::llvmpy-0.11.2-py27_0",
+                "channel-1::meta-0.4.2.dev-py27_0",
+                "channel-1::mkl-service-1.0.0-py27_p0",
+                "channel-1::numpy-1.7.1-py27_p0",
+                "channel-1::numba-0.8.1-np17py27_0",
+                "channel-1::numexpr-2.1-np17py27_p0",
+                "channel-1::scipy-0.12.0-np17py27_p0",
+                "channel-1::numbapro-0.11.0-np17py27_p0",
+                "channel-1::scikit-learn-0.13.1-np17py27_p0",
+                "channel-1::mkl-11.0-np17py27_p0",
+                "channel-1::accelerate-1.1.0-np17py27_p0",
+            )
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+    # Here accelerate is removed from the specs (but the old specs are kept as
+    # history). This should cause every dependency of accelerate to disappear
+    # from the pruned package list, but numpy should be kept, because it is still
+    # in the specs.
+    new_environment_specs = (
+        MatchSpec("numpy"),
+        MatchSpec("python=2.7.3"),
+    )
+
+    with get_solver(
+        tmpdir,
+        new_environment_specs,
+        prefix_records=final_state_1,
+        history_specs=specs,
+    ) as solver:
+        final_state_1 = solver.solve_final_state(prune=True)
+        pprint(convert_to_dist_str(final_state_1))
+
+        # Every dependecy of accelerate should now be absent from the solved packages,
+        # but numpy should remain.
+        order = add_subdir_to_iter(
+            (
+                "channel-1::openssl-1.0.1c-0",
+                "channel-1::readline-6.2-0",
+                "channel-1::sqlite-3.7.13-0",
+                "channel-1::system-5.8-1",
+                "channel-1::tk-8.5.13-0",
+                "channel-1::zlib-1.2.7-0",
+                "channel-1::python-2.7.3-7",
+                "channel-1::numpy-1.7.1-py27_0",
+            )
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+
+def test_update_prune_4(tmpdir):
+    """Regression test: Ensure that update with prune is not taking the history
+    into account, since that stops it from removing packages that were removed
+    from the environment specs.
+    """
+    specs = (
+        MatchSpec("numpy"),
+        MatchSpec("python=2.7.3"),
+    )
+
+    with get_solver(tmpdir, specs) as solver:
+        final_state_1 = solver.solve_final_state()
+        pprint(convert_to_dist_str(final_state_1))
+        order = add_subdir_to_iter(
+            (
+                "channel-1::openssl-1.0.1c-0",
+                "channel-1::readline-6.2-0",
+                "channel-1::sqlite-3.7.13-0",
+                "channel-1::system-5.8-1",
+                "channel-1::tk-8.5.13-0",
+                "channel-1::zlib-1.2.7-0",
+                "channel-1::python-2.7.3-7",
+                "channel-1::numpy-1.7.1-py27_0",
+            )
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+    # Here numpy is removed from the specs (but the old specs are kept as
+    # history).
+    new_environment_specs = (MatchSpec("python=2.7.3"),)
+
+    with get_solver(
+        tmpdir,
+        new_environment_specs,
+        prefix_records=final_state_1,
+        history_specs=specs,
+    ) as solver:
+        final_state_1 = solver.solve_final_state(
+            prune=True,
+            update_modifier=UpdateModifier.FREEZE_INSTALLED,
+        )
+        pprint(convert_to_dist_str(final_state_1))
+
+        # Numpy should now be absent from the solved packages.
+        order = add_subdir_to_iter(
+            (
+                "channel-1::openssl-1.0.1c-0",
+                "channel-1::readline-6.2-0",
+                "channel-1::sqlite-3.7.13-0",
+                "channel-1::system-5.8-1",
+                "channel-1::tk-8.5.13-0",
+                "channel-1::zlib-1.2.7-0",
+                "channel-1::python-2.7.3-7",
+            )
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+
+@pytest.mark.parametrize("prune", [True, False])
+def test_update_prune_5(tmpdir, prune, capsys):
+    """Regression test: Check that prefix data is not taken into account when solving on prune."""
+    # "Create" a conda env with specs that "pin" dependencies.
+    specs = (
+        MatchSpec("python=2.7"),
+        MatchSpec("numexpr==2.0.1=np17py27_p3"),
+    )
+    with get_solver(tmpdir, specs) as solver:
+        final_state_1 = solver.solve_final_state()
+
+    out, _ = capsys.readouterr()
+    assert "Updating numexpr is constricted by" not in out
+
+    # If prefix data is evaluated it will conflict with "pinned" dependencies of new specs.
+    new_environment_specs = (
+        MatchSpec("python=2.7"),
+        MatchSpec("numexpr==2.0.1=np17py27_p2"),
+    )
+    with get_solver(
+        tmpdir,
+        new_environment_specs,
+        prefix_records=final_state_1,
+        history_specs=specs,
+    ) as solver:
+        solver.solve_final_state(prune=prune)
+
+    out, _ = capsys.readouterr()
+    solve_using_prefix_data = not prune
+    assert ("Updating numexpr is constricted by" in out) is solve_using_prefix_data
+
+
 def test_force_remove_1(tmpdir):
     specs = (MatchSpec("numpy[version=*,build=*py27*]"),)
     with get_solver(tmpdir, specs) as solver: