diff --git a/conans/__init__.py b/conans/__init__.py
index f6ca9c132..7f4434ff6 100644
--- a/conans/__init__.py
+++ b/conans/__init__.py
@@ -18,5 +18,4 @@ REVISIONS = "revisions"  # Only when enabled in config, not by default look at s
 SERVER_CAPABILITIES = [COMPLEX_SEARCH_CAPABILITY, REVISIONS]  # Server is always with revisions
 DEFAULT_REVISION_V1 = "0"
 
-__version__ = '1.12.0-dev'
-
+__version__ = '1.13.0-dev'
diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py
index 31addfda9..44110243a 100644
--- a/conans/client/cmd/export.py
+++ b/conans/client/cmd/export.py
@@ -10,7 +10,7 @@ from conans.client.output import ScopedOutput
 from conans.errors import ConanException
 from conans.model.manifest import FileTreeManifest
 from conans.model.scm import SCM, get_scm_data
-from conans.paths import CONANFILE, CONAN_MANIFEST
+from conans.paths import CONANFILE
 from conans.search.search import search_recipes
 from conans.util.files import is_dirty, load, mkdir, rmdir, save, set_dirty, remove
 from conans.util.log import logger
@@ -42,9 +42,11 @@ def cmd_export(conanfile_path, conanfile, ref, keep_source, output, cache, hook_
     hook_manager.execute("pre_export", conanfile=conanfile, conanfile_path=conanfile_path,
                          reference=ref)
     logger.debug("EXPORT: %s" % conanfile_path)
-    output.highlight("Exporting package recipe")
 
+    output.highlight("Exporting package recipe")
     conan_linter(conanfile_path, output)
+
+    # Check for casing conflict
     # Maybe a platform check could be added, but depends on disk partition
     reference = str(ref)
     refs = search_recipes(cache, reference, ignorecase=True)
@@ -53,11 +55,65 @@ def cmd_export(conanfile_path, conanfile, ref, keep_source, output, cache, hook_
                              "You exported '%s' but already existing '%s'"
                              % (reference, " ".join(str(s) for s in refs)))
 
+    output = conanfile.output
+
+    # Get previous digest
+    package_layout = cache.package_layout(ref, conanfile.short_paths)
+    try:
+        previous_digest = FileTreeManifest.load(package_layout.export())
+    except IOError:
+        previous_digest = None
+    finally:
+        _recreate_folders(package_layout.export(), package_layout.export_sources())
+
+    # Copy sources to target folders
     with cache.conanfile_write_lock(ref):
-        _export_conanfile(conanfile_path, conanfile.output, cache, conanfile, ref, keep_source)
-    conanfile_cache_path = cache.conanfile(ref)
-    hook_manager.execute("post_export", conanfile=conanfile, conanfile_path=conanfile_cache_path,
-                         reference=ref)
+        origin_folder = os.path.dirname(conanfile_path)
+        export_recipe(conanfile, origin_folder, package_layout.export())
+        export_source(conanfile, origin_folder, package_layout.export_sources())
+        shutil.copy2(conanfile_path, package_layout.conanfile())
+
+        scm_data, captured_revision = _capture_export_scm_data(conanfile,
+                                                               os.path.dirname(conanfile_path),
+                                                               package_layout.export(),
+                                                               output, cache, ref)
+
+        # Execute post-export hook before computing the digest
+        hook_manager.execute("post_export", conanfile=conanfile, reference=ref,
+                             conanfile_path=package_layout.conanfile())
+
+        # Compute the new digest
+        digest = FileTreeManifest.create(package_layout.export(), package_layout.export_sources())
+        modified_recipe = not previous_digest or previous_digest != digest
+        if modified_recipe:
+            output.success('A new %s version was exported' % CONANFILE)
+            output.info('Folder: %s' % package_layout.export())
+        else:
+            output.info("The stored package has not changed")
+            digest = previous_digest  # Use the old one, keep old timestamp
+        digest.save(package_layout.export())
+
+    # Compute the revision for the recipe
+    revision = scm_data.revision if scm_data and captured_revision else digest.summary_hash
+    with package_layout.update_metadata() as metadata:
+        # Note that there is no time set, the time will come from the remote
+        metadata.recipe.revision = revision
+
+    # FIXME: Conan 2.0 Clear the registry entry if the recipe has changed
+    source_folder = package_layout.source()
+    if os.path.exists(source_folder):
+        try:
+            if is_dirty(source_folder):
+                output.info("Source folder is corrupted, forcing removal")
+                rmdir(source_folder)
+            elif modified_recipe and not keep_source:
+                output.info("Package recipe modified in export, forcing source folder removal")
+                output.info("Use the --keep-source, -k option to skip it")
+                rmdir(source_folder)
+        except BaseException as e:
+            output.error("Unable to delete source folder. Will be marked as corrupted for deletion")
+            output.warn(str(e))
+            set_dirty(source_folder)
 
 
 def _capture_export_scm_data(conanfile, conanfile_dir, destination_folder, output, paths, ref):
@@ -152,80 +208,21 @@ def _replace_scm_data_in_conanfile(conanfile_path, scm_data):
     save(conanfile_path, content)
 
 
-def _export_conanfile(conanfile_path, output, cache, conanfile, ref, keep_source):
-
-    exports_folder = cache.export(ref)
-    exports_source_folder = cache.export_sources(ref, conanfile.short_paths)
-
-    previous_digest = _init_export_folder(exports_folder, exports_source_folder)
-    origin_folder = os.path.dirname(conanfile_path)
-    export_recipe(conanfile, origin_folder, exports_folder)
-    export_source(conanfile, origin_folder, exports_source_folder)
-    shutil.copy2(conanfile_path, os.path.join(exports_folder, CONANFILE))
-
-    scm_data, captured_revision = _capture_export_scm_data(conanfile,
-                                                           os.path.dirname(conanfile_path),
-                                                           exports_folder,
-                                                           output, cache, ref)
-
-    digest = FileTreeManifest.create(exports_folder, exports_source_folder)
-
-    if previous_digest and previous_digest == digest:
-        output.info("The stored package has not changed")
-        modified_recipe = False
-        digest = previous_digest  # Use the old one, keep old timestamp
-    else:
-        output.success('A new %s version was exported' % CONANFILE)
-        output.info('Folder: %s' % exports_folder)
-        modified_recipe = True
-
-    digest.save(exports_folder)
-
-    revision = scm_data.revision if scm_data and captured_revision else digest.summary_hash
-    with cache.package_layout(ref).update_metadata() as metadata:
-        # Note that there is no time set, the time will come from the remote
-        metadata.recipe.revision = revision
-
-    # FIXME: Conan 2.0 Clear the registry entry if the recipe has changed
-    source = cache.source(ref, conanfile.short_paths)
-    remove = False
-    if is_dirty(source):
-        output.info("Source folder is corrupted, forcing removal")
-        remove = True
-    elif modified_recipe and not keep_source and os.path.exists(source):
-        output.info("Package recipe modified in export, forcing source folder removal")
-        output.info("Use the --keep-source, -k option to skip it")
-        remove = True
-    if remove:
-        output.info("Removing 'source' folder, this can take a while for big packages")
-        try:
-            # remove only the internal
-            rmdir(source)
-        except BaseException as e:
-            output.error("Unable to delete source folder. "
-                         "Will be marked as corrupted for deletion")
-            output.warn(str(e))
-            set_dirty(source)
-
-
-def _init_export_folder(destination_folder, destination_src_folder):
-    previous_digest = None
+def _recreate_folders(destination_folder, destination_src_folder):
     try:
         if os.path.exists(destination_folder):
-            if os.path.exists(os.path.join(destination_folder, CONAN_MANIFEST)):
-                previous_digest = FileTreeManifest.load(destination_folder)
             # Maybe here we want to invalidate cache
             rmdir(destination_folder)
         os.makedirs(destination_folder)
     except Exception as e:
         raise ConanException("Unable to create folder %s\n%s" % (destination_folder, str(e)))
+
     try:
         if os.path.exists(destination_src_folder):
             rmdir(destination_src_folder)
         os.makedirs(destination_src_folder)
     except Exception as e:
         raise ConanException("Unable to create folder %s\n%s" % (destination_src_folder, str(e)))
-    return previous_digest
 
 
 def _classify_patterns(patterns):
diff --git a/conans/client/cmd/uploader.py b/conans/client/cmd/uploader.py
index 544d0d2e9..4c4400b9c 100644
--- a/conans/client/cmd/uploader.py
+++ b/conans/client/cmd/uploader.py
@@ -1,5 +1,6 @@
 import os
 import time
+from collections import defaultdict
 
 from conans.client.source import complete_recipe_sources
 from conans.errors import ConanException, NotFoundException
@@ -45,18 +46,34 @@ class CmdUpload(object):
                 raise NotFoundException(("No packages found matching pattern '%s'" %
                                          reference_or_pattern))
 
+        # Group recipes by remote
+        refs_by_remote = defaultdict(list)
+        default_remote = (self._registry.remotes.get(remote_name) if remote_name else
+                          self._registry.remotes.default)
+
         for ref in refs:
+            if not remote_name:
+                remote = self._registry.refs.get(ref) or default_remote
+            else:
+                remote = default_remote
+
             upload = True
             if not confirm:
-                msg = "Are you sure you want to upload '%s'?" % str(ref)
+                msg = "Are you sure you want to upload '%s' to '%s'?" % (str(ref), remote.name)
                 upload = self._user_io.request_boolean(msg)
             if upload:
                 try:
                     conanfile_path = self._cache.conanfile(ref)
-                    conan_file = self._loader.load_class(conanfile_path)
+                    conanfile = self._loader.load_class(conanfile_path)
                 except NotFoundException:
                     raise NotFoundException(("There is no local conanfile exported as %s" %
                                              str(ref)))
+                refs_by_remote[remote].append((ref, conanfile))
+
+        # Do the job
+        for remote, refs in refs_by_remote.items():
+            self._user_io.out.info("Uploading to remote '{}':".format(remote.name))
+            for (ref, conanfile) in refs:
                 if all_packages:
                     packages_ids = self._cache.conan_packages(ref)
                 elif query:
@@ -66,21 +83,14 @@ class CmdUpload(object):
                     packages_ids = [package_id, ]
                 else:
                     packages_ids = []
-                self._upload(conan_file, ref, packages_ids, retry, retry_wait,
-                             integrity_check, policy, remote_name, upload_recorder)
+                self._upload(conanfile, ref, packages_ids, retry, retry_wait,
+                             integrity_check, policy, remote, upload_recorder)
 
         logger.debug("UPLOAD: Time manager upload: %f" % (time.time() - t1))
 
     def _upload(self, conan_file, ref, packages_ids, retry, retry_wait,
-                integrity_check, policy, remote_name, upload_recorder):
-        """Uploads the recipes and binaries identified by ref"""
-
-        default_remote = self._registry.remotes.default
-        cur_recipe_remote = self._registry.refs.get(ref)
-        if remote_name:  # If remote_name is given, use it
-            recipe_remote = self._registry.remotes.get(remote_name)
-        else:
-            recipe_remote = cur_recipe_remote or default_remote
+                integrity_check, policy, recipe_remote, upload_recorder):
+        """Uploads the recipes and binaries identified by conan_ref"""
 
         conanfile_path = self._cache.conanfile(ref)
         # FIXME: I think it makes no sense to specify a remote to "pre_upload"
diff --git a/conans/client/conf/config_installer.py b/conans/client/conf/config_installer.py
index 6ce6830f0..2550bbaef 100644
--- a/conans/client/conf/config_installer.py
+++ b/conans/client/conf/config_installer.py
@@ -116,8 +116,8 @@ def _process_download(item, cache, output, tmp_folder, verify_ssl, requester):
         raise ConanException("Error while installing config from %s\n%s" % (item, str(e)))
 
 
-def configuration_install(path_or_url, cache, output, verify_ssl, requester,
-                          config_type=None, args=None):
+def configuration_install(path_or_url, cache, output, verify_ssl, requester, config_type=None,
+                          args=None):
     if path_or_url is None:
         try:
             item = cache.conan_config.get_item("general.config_install")
@@ -202,18 +202,13 @@ def _handle_hooks(src_hooks_path, dst_hooks_path, output):
     :param dst_hooks_path:  Folder where the hooks should finally go
     :param output: Output to indicate the files copied
     """
-    hooks_dirs = []
     for root, dirs, files in walk(src_hooks_path):
-        if root == src_hooks_path:
-            hooks_dirs = dirs
-        else:
-            copied_files = False
-            relpath = os.path.relpath(root, src_hooks_path)
-            for f in files:
-                if ".git" not in f:
-                    dst = os.path.join(dst_hooks_path, relpath)
-                    mkdir(dst)
-                    shutil.copy(os.path.join(root, f), dst)
-                    copied_files = True
-            if copied_files and relpath in hooks_dirs:
+        if ".git" in root:
+            continue
+        relpath = os.path.relpath(root, src_hooks_path)
+        for f in files:
+            if ".git" not in f:
+                dst = os.path.join(dst_hooks_path, relpath)
+                mkdir(dst)
+                shutil.copy(os.path.join(root, f), dst)
                 output.info(" - %s" % relpath)
diff --git a/conans/client/graph/graph_manager.py b/conans/client/graph/graph_manager.py
index ffdc8710d..804200850 100644
--- a/conans/client/graph/graph_manager.py
+++ b/conans/client/graph/graph_manager.py
@@ -236,11 +236,10 @@ class GraphManager(object):
 
     def _load_graph(self, root_node, check_updates, update, build_mode, remote_name,
                     profile_build_requires, recorder, workspace, processed_profile):
+        assert isinstance(build_mode, BuildMode)
         builder = DepsGraphBuilder(self._proxy, self._output, self._loader, self._resolver,
                                    workspace, recorder)
         graph = builder.load_graph(root_node, check_updates, update, remote_name, processed_profile)
-        if build_mode is None:
-            return graph
         binaries_analyzer = GraphBinariesAnalyzer(self._cache, self._output,
                                                   self._remote_manager, workspace)
         binaries_analyzer.evaluate_graph(graph, build_mode, update, remote_name)
diff --git a/conans/client/hook_manager.py b/conans/client/hook_manager.py
index 7076f9679..be634d2ee 100644
--- a/conans/client/hook_manager.py
+++ b/conans/client/hook_manager.py
@@ -53,7 +53,7 @@ class HookManager(object):
         for name, method in self.hooks[method_name]:
             try:
                 output = ScopedOutput("[HOOK - %s] %s()" % (name, method_name), self.output)
-                method(output, **kwargs)
+                method(output=output, **kwargs)
             except Exception as e:
                 raise ConanException("[HOOK - %s] %s(): %s\n%s" % (name, method_name, str(e),
                                                                    traceback.format_exc()))
diff --git a/conans/client/tools/files.py b/conans/client/tools/files.py
index 4201ae6c2..250a2c458 100644
--- a/conans/client/tools/files.py
+++ b/conans/client/tools/files.py
@@ -194,24 +194,30 @@ def patch(base_path=None, patch_file=None, patch_string=None, strip=0, output=No
     if not patchset:
         raise ConanException("Failed to parse patch: %s" % (patch_file if patch_file else "string"))
 
+    def decode_clean(path, prefix):
+        path = path.decode("utf-8").replace("\\", "/")
+        if path.startswith(prefix):
+            path = path[2:]
+        return path
+
+    def strip_path(path):
+        tokens = path.split("/")[strip:]
+        path = "/".join(tokens)
+        if base_path:
+            path = os.path.join(base_path, path)
+        return path
     # account for new and deleted files, upstream dep won't fix them
     items = []
     for p in patchset:
-        source = p.source.decode("utf-8")
-        if source.startswith("a/"):
-            source = source[2:]
-        target = p.target.decode("utf-8")
-        if target.startswith("b/"):
-            target = target[2:]
+        source = decode_clean(p.source, "a/")
+        target = decode_clean(p.target, "b/")
         if "dev/null" in source:
-            if base_path:
-                target = os.path.join(base_path, target)
+            target = strip_path(target)
             hunks = [s.decode("utf-8") for s in p.hunks[0].text]
             new_file = "".join(hunk[1:] for hunk in hunks)
             save(target, new_file)
         elif "dev/null" in target:
-            if base_path:
-                source = os.path.join(base_path, source)
+            source = strip_path(source)
             os.unlink(source)
         else:
             items.append(p)
diff --git a/conans/client/tools/win.py b/conans/client/tools/win.py
index 82869bd99..0ea3ebe93 100644
--- a/conans/client/tools/win.py
+++ b/conans/client/tools/win.py
@@ -147,7 +147,8 @@ def msvc_build_command(settings, sln_path, targets=None, upgrade_project=True, b
 @deprecation.deprecated(deprecated_in="1.2", removed_in="2.0",
                         details="Use the MSBuild() build helper instead")
 def build_sln_command(settings, sln_path, targets=None, upgrade_project=True, build_type=None,
-                      arch=None, parallel=True, toolset=None, platforms=None, output=None, verbosity=None, definitions=None):
+                      arch=None, parallel=True, toolset=None, platforms=None, output=None,
+                      verbosity=None, definitions=None):
     """
     Use example:
         build_command = build_sln_command(self.settings, "myfile.sln", targets=["SDL2_image"])
diff --git a/conans/util/files.py b/conans/util/files.py
index d2029f492..1046ea6b0 100644
--- a/conans/util/files.py
+++ b/conans/util/files.py
@@ -170,14 +170,14 @@ def save_files(path, files, only_if_modified=False):
 
 
 def load(path, binary=False):
-    '''Loads a file content'''
+    """ Loads a file content """
     with open(path, 'rb') as handle:
         tmp = handle.read()
         return tmp if binary else decode_text(tmp)
 
 
 def relative_dirs(path):
-    ''' Walks a dir and return a list with the relative paths '''
+    """ Walks a dir and return a list with the relative paths """
     ret = []
     for dirpath, _, fnames in walk(path):
         for filename in fnames:

conan upload should print the remote it is using
```
conan upload "*" 
Are you sure you want to upload 'XXX@XXX/XXXX'? (yes/no): 
````
It should be `Are you sure you want to upload 'XXX@XXX/XXXX' to remote 'conan-center'? (yes/no):

Or print first:


```
> conan upload "*" 
Uploading to remote 'conan-center':
Are you sure you want to upload 'XXX@XXX/XXXX'? (yes/no):
```

diff --git a/conans/test/functional/command/config_install_test.py b/conans/test/functional/command/config_install_test.py
index cb7231d66..0c869b8b8 100644
--- a/conans/test/functional/command/config_install_test.py
+++ b/conans/test/functional/command/config_install_test.py
@@ -74,7 +74,6 @@ def zipdir(path, zipfilename):
 
 
 class ConfigInstallTest(unittest.TestCase):
-
     def setUp(self):
         self.client = TestClient()
         # Save to the old registry, it has to be migrated
@@ -100,8 +99,7 @@ class ConfigInstallTest(unittest.TestCase):
  }
 }        
 """)
-        save(os.path.join(self.client.cache.profiles_path, "default"),
-             "#default profile empty")
+        save(os.path.join(self.client.cache.profiles_path, "default"), "#default profile empty")
         save(os.path.join(self.client.cache.profiles_path, "linux"), "#empty linux profile")
 
         self.old_env = dict(os.environ)
@@ -116,10 +114,16 @@ class ConfigInstallTest(unittest.TestCase):
                             "remotes.txt": remotes,
                             "profiles/linux": linux_profile,
                             "profiles/windows": win_profile,
+                            "hooks/dummy": "#hook dummy",
+                            "hooks/foo.py": "#hook foo",
+                            "hooks/custom/custom.py": "#hook custom",
+                            ".git/hooks/foo": "foo",
+                            "hooks/.git/hooks/before_push": "before_push",
                             "config/conan.conf": conan_conf,
                             "pylintrc": "#Custom pylint",
                             "python/myfuncs.py": myfuncpy,
-                            "python/__init__.py": ""})
+                            "python/__init__.py": ""
+                            })
         return folder
 
     def _create_zip(self, zippath=None):
@@ -132,10 +136,9 @@ class ConfigInstallTest(unittest.TestCase):
         settings_path = self.client.cache.settings_path
         self.assertEqual(load(settings_path).splitlines(), settings_yml.splitlines())
         registry = self.client.cache.registry
-        self.assertEqual(registry.remotes.list,
-                         [Remote("myrepo1", "https://myrepourl.net", False),
-                          Remote("my-repo-2", "https://myrepo2.com", True),
-                          ])
+        self.assertEqual(registry.remotes.list, [Remote("myrepo1", "https://myrepourl.net", False),
+                                                 Remote("my-repo-2", "https://myrepo2.com", True),
+                                                 ])
         self.assertEqual(registry.refs.list, {"MyPkg/0.1@user/channel": "my-repo-2"})
         self.assertEqual(sorted(os.listdir(self.client.cache.profiles_path)),
                          sorted(["default", "linux", "windows"]))
@@ -157,8 +160,17 @@ class ConfigInstallTest(unittest.TestCase):
         self.assertEqual("#Custom pylint",
                          load(os.path.join(self.client.cache.conan_folder, "pylintrc")))
         self.assertEqual("",
-                         load(os.path.join(self.client.cache.conan_folder, "python",
-                                           "__init__.py")))
+                         load(os.path.join(self.client.cache.conan_folder, "python", "__init__.py")))
+        self.assertEqual("#hook dummy",
+                         load(os.path.join(self.client.cache.conan_folder, "hooks", "dummy")))
+        self.assertEqual("#hook foo",
+                         load(os.path.join(self.client.cache.conan_folder, "hooks", "foo.py")))
+        self.assertEqual("#hook custom",
+                         load(os.path.join(self.client.cache.conan_folder, "hooks", "custom",
+                                           "custom.py")))
+        self.assertFalse(os.path.exists(os.path.join(self.client.cache.conan_folder, "hooks",
+                                                     ".git")))
+        self.assertFalse(os.path.exists(os.path.join(self.client.cache.conan_folder, ".git")))
 
     def reuse_python_test(self):
         zippath = self._create_zip()
@@ -225,8 +237,7 @@ class Pkg(ConanFile):
         """ should install from a http zip
         """
         self.client.run('config install httpnonexisting', assert_error=True)
-        self.assertIn("ERROR: Error while installing config from httpnonexisting",
-                      self.client.out)
+        self.assertIn("ERROR: Error while installing config from httpnonexisting", self.client.out)
 
     def install_repo_test(self):
         """ should install from a git repo
@@ -336,7 +347,7 @@ class Pkg(ConanFile):
             self._create_zip(filename)
 
         with patch.object(Downloader, 'download', new=my_download):
-            self.client.run("config install %s" % (fake_url_with_credentials,))
+            self.client.run("config install %s" % fake_url_with_credentials)
 
             # Check credentials are not displayed in output
             self.assertNotIn(fake_url_with_credentials, self.client.out)
@@ -400,8 +411,7 @@ class Pkg(ConanFile):
             self.client.runner('git add .')
             self.client.runner('git commit -m "my other file"')
             self.client.runner('git checkout master')
-        other_path = os.path.join(self.client.cache.conan_folder, "hooks", "other",
-                                  "other.py")
+        other_path = os.path.join(self.client.cache.conan_folder, "hooks", "other", "other.py")
         self.assertFalse(os.path.exists(other_path))
         self.client.run('config install')
         check_path = os.path.join(folder, ".git")
diff --git a/conans/test/functional/command/info_command_test.py b/conans/test/functional/command/info_command_test.py
index 22da91958..d4dc2173c 100644
--- a/conans/test/functional/command/info_command_test.py
+++ b/conans/test/functional/command/info_command_test.py
@@ -130,8 +130,7 @@ class InfoTest(unittest.TestCase):
         self.clients["H3"].run("info . --build missing --json=\"{}\"".format(json_file))
         self.assertEqual(load(json_file), json_output)
 
-        json_file = os.path.join(temp_folder(), "output.json")
-        self.clients["H3"].run("info . --build missing --json".format(json_file))
+        self.clients["H3"].run("info . --build missing --json")
         self.assert_last_line(self.clients["H3"], json_output)
 
 
diff --git a/conans/test/functional/command/upload_complete_test.py b/conans/test/functional/command/upload_complete_test.py
index b937bdd78..6067372bf 100644
--- a/conans/test/functional/command/upload_complete_test.py
+++ b/conans/test/functional/command/upload_complete_test.py
@@ -322,12 +322,13 @@ class TestConan(ConanFile):
                              stat.S_IRWXU, stat.S_IRWXU)
 
     def upload_all_test(self):
-        '''Upload conans and package together'''
+        """Upload conans and package together"""
         # Try to upload all conans and packages
         self.client.run('upload %s --all' % str(self.ref))
         lines = [line.strip() for line in str(self.client.user_io.out).splitlines()
                  if line.startswith("Uploading")]
-        self.assertEqual(lines, ["Uploading Hello/1.2.1@frodo/stable to remote 'default'",
+        self.assertEqual(lines, ["Uploading to remote 'default':",
+                                 "Uploading Hello/1.2.1@frodo/stable to remote 'default'",
                                  "Uploading conanmanifest.txt",
                                  "Uploading conanfile.py",
                                  "Uploading conan_export.tgz",
diff --git a/conans/test/functional/command/upload_test.py b/conans/test/functional/command/upload_test.py
index 9b03d958a..7b5744851 100644
--- a/conans/test/functional/command/upload_test.py
+++ b/conans/test/functional/command/upload_test.py
@@ -55,7 +55,7 @@ class UploadTest(unittest.TestCase):
     def non_existing_recipe_error_test(self):
         """ Trying to upload a non-existing recipe must raise an Error
         """
-        client = TestClient()
+        client = TestClient(servers={"default": TestServer()})
         client.run("upload Pkg/0.1@user/channel", assert_error=True)
         self.assertIn("ERROR: There is no local conanfile exported as Pkg/0.1@user/channel",
                       client.user_io.out)
@@ -63,7 +63,8 @@ class UploadTest(unittest.TestCase):
     def non_existing_package_error_test(self):
         """ Trying to upload a non-existing package must raise an Error
         """
-        client = TestClient()
+        servers = {"default": TestServer()}
+        client = TestClient(servers=servers)
         client.run("upload Pkg/0.1@user/channel -p hash1", assert_error=True)
         self.assertIn("ERROR: There is no local conanfile exported as Pkg/0.1@user/channel",
                       client.user_io.out)
@@ -114,10 +115,8 @@ class UploadTest(unittest.TestCase):
         self.assertNotIn("Uploading package", client.user_io.out)
 
         # Check that an invalid query fails
-        try:
-            client.run("upload Hello1/*@user/testing --confirm -q 'blah blah blah'")
-        except:
-            self.assertIn("Invalid package query", client.user_io.out)
+        client.run("upload Hello1/*@user/testing --confirm -q 'blah blah blah'", assert_error=True)
+        self.assertIn("Invalid package query", client.user_io.out)
 
     def broken_sources_tgz_test(self):
         # https://github.com/conan-io/conan/issues/2854
@@ -129,7 +128,8 @@ class UploadTest(unittest.TestCase):
 
         def gzopen_patched(name, mode="r", fileobj=None, compresslevel=None, **kwargs):
             raise ConanException("Error gzopen %s" % name)
-        with mock.patch('conans.client.remote_manager.gzopen_without_timestamps', new=gzopen_patched):
+        with mock.patch('conans.client.remote_manager.gzopen_without_timestamps',
+                        new=gzopen_patched):
             client.run("upload * --confirm", assert_error=True)
             self.assertIn("ERROR: Error gzopen conan_sources.tgz", client.out)
 
@@ -156,7 +156,8 @@ class UploadTest(unittest.TestCase):
             if name == PACKAGE_TGZ_NAME:
                 raise ConanException("Error gzopen %s" % name)
             return gzopen_without_timestamps(name, mode, fileobj, compresslevel, **kwargs)
-        with mock.patch('conans.client.remote_manager.gzopen_without_timestamps', new=gzopen_patched):
+        with mock.patch('conans.client.remote_manager.gzopen_without_timestamps',
+                        new=gzopen_patched):
             client.run("upload * --confirm --all", assert_error=True)
             self.assertIn("ERROR: Error gzopen conan_package.tgz", client.out)
 
diff --git a/conans/test/functional/hooks/__init__.py b/conans/test/functional/hooks/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/conans/test/functional/hooks/test_post_export.py b/conans/test/functional/hooks/test_post_export.py
new file mode 100644
index 000000000..525129c5c
--- /dev/null
+++ b/conans/test/functional/hooks/test_post_export.py
@@ -0,0 +1,46 @@
+# coding=utf-8
+
+import os
+import textwrap
+import unittest
+
+from mock import patch
+
+from conans.client.hook_manager import HookManager
+from conans.model.manifest import FileTreeManifest
+from conans.model.ref import ConanFileReference
+from conans.paths import CONAN_MANIFEST
+from conans.test.utils.tools import TestClient
+
+
+class PostExportTestCase(unittest.TestCase):
+
+    def test_called_before_digest(self):
+        """ Test that 'post_export' hook is called before computing the digest of the
+            exported folders
+        """
+
+        ref = ConanFileReference.loads("name/version@user/channel")
+        conanfile = textwrap.dedent("""\
+            from conans import ConanFile
+            
+            class MyLib(ConanFile):
+                pass
+        """)
+
+        t = TestClient()
+        t.save({'conanfile.py': conanfile})
+        package_layout = t.cache.package_layout(ref)
+
+        def mocked_post_export(*args, **kwargs):
+            # There shouldn't be a digest yet
+            with self.assertRaisesRegexp(IOError, "No such file or directory"):
+                FileTreeManifest.load(package_layout.export())
+            self.assertFalse(os.path.exists(os.path.join(package_layout.export(), CONAN_MANIFEST)))
+
+        def mocked_load_hooks(hook_manager):
+            hook_manager.hooks["post_export"] = [("_", mocked_post_export)]
+
+        with patch.object(HookManager, "load_hooks", new=mocked_load_hooks):
+            t.run("export . {}".format(ref))
+        self.assertTrue(os.path.exists(os.path.join(package_layout.export(), CONAN_MANIFEST)))
diff --git a/conans/test/functional/scm/test_command_export.py b/conans/test/functional/scm/test_command_export.py
index 09de95728..dca978c1e 100644
--- a/conans/test/functional/scm/test_command_export.py
+++ b/conans/test/functional/scm/test_command_export.py
@@ -5,33 +5,46 @@ import textwrap
 import unittest
 
 from parameterized import parameterized
+from nose.plugins.attrib import attr
 
 from conans.test.utils.tools import TestClient, create_local_git_repo
 
 
-class ExportCommandTestCase(unittest.TestCase):
+@attr("svn")
+class ExportErrorCommandTestCase(unittest.TestCase):
     conanfile = textwrap.dedent("""\
         from conans import ConanFile
 
         class Lib(ConanFile):
-            scm = {{"type": "{type}",
+            scm = {{"type": "{repo_type}",
                     "url": "{url_value}",
                     "revision": "{rev_value}"}}
     """)
 
     @parameterized.expand(itertools.product(["SVN", "git"], [(True, False), (False, True)]))
-    def test_no_repo(self, type, autos):
+    def test_no_repo(self, repo_type, autos):
         auto_url, auto_rev = autos
         url_value = "auto" if auto_url else "http://this.url"
         rev_value = "auto" if auto_rev else "123"
 
         self.client = TestClient()
-        self.client.save({"conanfile.py": self.conanfile.format(type=type.lower(),
+        self.client.save({"conanfile.py": self.conanfile.format(repo_type=repo_type.lower(),
                                                                 url_value=url_value,
                                                                 rev_value=rev_value)
                           })
         self.client.run("export . lib/version@user/channel", assert_error=True)
-        self.assertIn("ERROR: Not a valid {} repository".format(type), self.client.out)
+        self.assertIn("ERROR: Not a valid {} repository".format(repo_type), self.client.out)
+
+
+class ExportCommandTestCase(unittest.TestCase):
+    conanfile = textwrap.dedent("""\
+        from conans import ConanFile
+
+        class Lib(ConanFile):
+            scm = {{"type": "{repo_type}",
+                    "url": "{url_value}",
+                    "revision": "{rev_value}"}}
+    """)
 
     @parameterized.expand([(True, False), (False, True)])
     def test_non_existing_remote(self, auto_url, auto_rev):
@@ -39,7 +52,7 @@ class ExportCommandTestCase(unittest.TestCase):
         rev_value = "auto" if auto_rev else "123"
 
         self.path, _ = create_local_git_repo({"conanfile.py":
-                                              self.conanfile.format(type="git",
+                                              self.conanfile.format(repo_type="git",
                                                                     url_value=url_value,
                                                                     rev_value=rev_value)})
         self.client = TestClient()
diff --git a/conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py b/conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py
index 573ec3186..7431c2c64 100644
--- a/conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py
+++ b/conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py
@@ -1,7 +1,6 @@
 # coding=utf-8
 
 import os
-import textwrap
 import unittest
 
 from nose.plugins.attrib import attr
diff --git a/conans/test/unittests/client/conf/config_installer_test.py b/conans/test/unittests/client/conf/config_installer_test.py
index e3df486cc..c17f6f2bd 100644
--- a/conans/test/unittests/client/conf/config_installer_test.py
+++ b/conans/test/unittests/client/conf/config_installer_test.py
@@ -1,15 +1,15 @@
 import os
 import unittest
-from parameterized import parameterized
+import tempfile
 
-from conans.client.conf.config_installer import _process_config_install_item
+from conans.client.conf.config_installer import _process_config_install_item, _handle_hooks
 from conans.errors import ConanException
 from conans.test.utils.test_files import temp_folder
-from conans.util.files import save
+from conans.test.utils.tools import TestBufferConanOutput
+from conans.util.files import save, mkdir
 
 
 class ConfigInstallerTests(unittest.TestCase):
-
     def process_config_install_item_test(self):
         config_type, url_or_path, verify_ssl, args = _process_config_install_item(
                 "git, whaterver.url.com/repo.git, False, --recusrive --other -b 0.3.4")
@@ -18,7 +18,8 @@ class ConfigInstallerTests(unittest.TestCase):
         self.assertFalse(verify_ssl)
         self.assertEqual("--recusrive --other -b 0.3.4", args)
 
-        config_type, url_or_path, verify_ssl, args = _process_config_install_item("whaterver.url.com/repo.git")
+        config_type, url_or_path, verify_ssl, args = _process_config_install_item(
+                "whaterver.url.com/repo.git")
         self.assertEqual("git", config_type)
         self.assertEqual("whaterver.url.com/repo.git", url_or_path)
         self.assertIsNone(verify_ssl)
@@ -29,7 +30,7 @@ class ConfigInstallerTests(unittest.TestCase):
             config_type, url_or_path, verify_ssl, args = _process_config_install_item(dir_item)
             self.assertEqual("dir", config_type)
             self.assertEqual(dir_path, url_or_path)
-            self.assertTrue(verify_ssl) if dir_item.startswith("dir,")\
+            self.assertTrue(verify_ssl) if dir_item.startswith("dir,") \
                 else self.assertIsNone(verify_ssl)
             self.assertIsNone(args)
 
@@ -39,7 +40,7 @@ class ConfigInstallerTests(unittest.TestCase):
             config_type, url_or_path, verify_ssl, args = _process_config_install_item(file_item)
             self.assertEqual("file", config_type)
             self.assertEqual(file_path, url_or_path)
-            self.assertTrue(verify_ssl) if file_item.startswith("file,") \
+            self.assertTrue(verify_ssl) if file_item.startswith("file,")\
                 else self.assertIsNone(verify_ssl)
             self.assertIsNone(args)
 
@@ -60,7 +61,28 @@ class ConfigInstallerTests(unittest.TestCase):
         self.assertEqual("--option", args)
 
         # Test wrong input
-        for item in ["git@github.com:conan-io/conan.git, None"
-                     "file/not/exists.zip"]:
+        for item in ["git@github.com:conan-io/conan.git, None", "file/not/exists.zip"]:
             with self.assertRaisesRegexp(ConanException, "Unable to process config install"):
                 _, _, _, _ = _process_config_install_item(item)
+
+    def handle_hooks_test(self):
+        src_dir = temp_folder()
+        subsrc_dir = os.path.join(src_dir, "foo")
+
+        mkdir(subsrc_dir)
+        save(os.path.join(subsrc_dir, "foo"), "foo")
+        save(os.path.join(src_dir, "bar"), "bar")
+
+        git_dir = os.path.join(src_dir, ".git")
+        mkdir(git_dir)
+        mkdir(os.path.join(git_dir, "hooks"))
+        save(os.path.join(git_dir, "hooks", "before_push"), "before_push")
+
+        target_dir = temp_folder()
+        output = TestBufferConanOutput()
+
+        _handle_hooks(src_hooks_path=src_dir, dst_hooks_path=target_dir, output=output)
+
+        self.assertTrue(os.path.isfile(os.path.join(target_dir, "bar")))
+        self.assertTrue(os.path.isfile(os.path.join(target_dir, "foo", "foo")))
+        self.assertFalse(os.path.isfile(os.path.join(target_dir, ".git", "hooks", "before_push")))
diff --git a/conans/test/unittests/client/source/test_run_scm.py b/conans/test/unittests/client/source/test_run_scm.py
index f1f29e5c4..ab4b38786 100644
--- a/conans/test/unittests/client/source/test_run_scm.py
+++ b/conans/test/unittests/client/source/test_run_scm.py
@@ -35,7 +35,8 @@ class RunSCMTest(unittest.TestCase):
             self.assertEqual(folder, self.src_folder)
 
         with mock.patch("conans.client.source.merge_directories", side_effect=merge_directories):
-            with mock.patch("conans.client.source._clean_source_folder", side_effect=clean_source_folder):
+            with mock.patch("conans.client.source._clean_source_folder",
+                            side_effect=clean_source_folder):
                 _run_scm(conanfile=conanfile,
                          src_folder=self.src_folder,
                          local_sources_path=local_sources_path,
@@ -60,7 +61,8 @@ class RunSCMTest(unittest.TestCase):
         def scm_checkout(scm_itself):
             self.assertEqual(scm_itself.repo_folder, os.path.join(self.src_folder, subfolder))
 
-        with mock.patch("conans.client.source._clean_source_folder", side_effect=clean_source_folder):
+        with mock.patch("conans.client.source._clean_source_folder",
+                        side_effect=clean_source_folder):
             with mock.patch.object(SCM, "checkout", new=scm_checkout):
                 _run_scm(conanfile=conanfile,
                          src_folder=self.src_folder,
@@ -72,7 +74,6 @@ class RunSCMTest(unittest.TestCase):
 
     def test_user_space_with_local_sources(self):
         output = TestBufferConanOutput()
-        local_sources_path = self.tmp_dir
 
         # Need a real repo to get a working SCM object
         local_sources_path = os.path.join(self.tmp_dir, 'git_repo').replace('\\', '/')
@@ -99,7 +100,8 @@ class RunSCMTest(unittest.TestCase):
                      output=output,
                      cache=False)
 
-        self.assertIn("Getting sources from folder: {}".format(local_sources_path), output)
+        self.assertIn("getting sources from folder: {}".format(local_sources_path).lower(),
+                      str(output).lower())
 
     def test_user_space_no_local_sources(self):
         output = TestBufferConanOutput()
diff --git a/conans/test/unittests/tools/__init__.py b/conans/test/unittests/tools/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/conans/test/unittests/tools/files_patch_test.py b/conans/test/unittests/tools/files_patch_test.py
new file mode 100644
index 000000000..9f248fe04
--- /dev/null
+++ b/conans/test/unittests/tools/files_patch_test.py
@@ -0,0 +1,171 @@
+import os
+import unittest
+from textwrap import dedent
+
+from parameterized.parameterized import parameterized
+
+from conans.client.graph.python_requires import ConanPythonRequire
+from conans.client.loader import ConanFileLoader
+from conans.test.utils.test_files import temp_folder
+from conans.test.utils.tools import TestClient, TestBufferConanOutput,\
+    test_processed_profile
+from conans.util.files import save, load
+
+base_conanfile = '''
+from conans import ConanFile
+from conans.tools import patch, replace_in_file
+import os
+
+class ConanFileToolsTest(ConanFile):
+    name = "test"
+    version = "1.9.10"
+'''
+
+
+class ToolsFilesPatchTest(unittest.TestCase):
+
+    @parameterized.expand([(0, ), (1, )])
+    def test_patch_from_file(self, strip):
+        if strip:
+            file_content = base_conanfile + '''
+    def build(self):
+        patch(patch_file="file.patch", strip=%s)
+''' % strip
+            patch_content = '''--- %s/text.txt\t2016-01-25 17:57:11.452848309 +0100
++++ %s/text_new.txt\t2016-01-25 17:57:28.839869950 +0100
+@@ -1 +1 @@
+-ONE TWO THREE
++ONE TWO FOUR''' % ("old_path", "new_path")
+        else:
+            file_content = base_conanfile + '''
+    def build(self):
+        patch(patch_file="file.patch")
+'''
+            patch_content = '''--- text.txt\t2016-01-25 17:57:11.452848309 +0100
++++ text_new.txt\t2016-01-25 17:57:28.839869950 +0100
+@@ -1 +1 @@
+-ONE TWO THREE
++ONE TWO FOUR'''
+
+        tmp_dir, file_path, text_file = self._save_files(file_content)
+        patch_file = os.path.join(tmp_dir, "file.patch")
+        save(patch_file, patch_content)
+        self._build_and_check(tmp_dir, file_path, text_file, "ONE TWO FOUR")
+
+    def test_patch_from_str(self):
+        file_content = base_conanfile + '''
+    def build(self):
+        patch_content = \'''--- text.txt\t2016-01-25 17:57:11.452848309 +0100
++++ text_new.txt\t2016-01-25 17:57:28.839869950 +0100
+@@ -1 +1 @@
+-ONE TWO THREE
++ONE TWO DOH!\'''
+        patch(patch_string=patch_content)
+
+'''
+        tmp_dir, file_path, text_file = self._save_files(file_content)
+        self._build_and_check(tmp_dir, file_path, text_file, "ONE TWO DOH!")
+
+    def test_patch_strip_new(self):
+        conanfile = dedent("""
+            from conans import ConanFile, tools
+            class PatchConan(ConanFile):
+                def source(self):
+                    tools.patch(self.source_folder, "example.patch", strip=1)""")
+        patch = dedent("""
+            --- /dev/null
+            +++ b/src/newfile
+            @@ -0,0 +0,1 @@
+            +New file!""")
+
+        client = TestClient()
+        client.save({"conanfile.py": conanfile,
+                     "example.patch": patch})
+        client.run("source .")
+        self.assertEqual(load(os.path.join(client.current_folder, "newfile")),
+                         "New file!")
+
+    def test_patch_strip_delete(self):
+        conanfile = dedent("""
+            from conans import ConanFile, tools
+            class PatchConan(ConanFile):
+                def source(self):
+                    tools.patch(self.source_folder, "example.patch", strip=1)""")
+        patch = dedent("""
+            --- a\src\oldfile
+            +++ b/dev/null
+            @@ -0,1 +0,0 @@
+            -legacy code""")
+        client = TestClient()
+        client.save({"conanfile.py": conanfile,
+                     "example.patch": patch,
+                     "oldfile": "legacy code"})
+        path = os.path.join(client.current_folder, "oldfile")
+        self.assertTrue(os.path.exists(path))
+        client.run("source .")
+        self.assertFalse(os.path.exists(path))
+
+    def test_patch_new_delete(self):
+        conanfile = base_conanfile + '''
+    def build(self):
+        from conans.tools import load, save
+        save("oldfile", "legacy code")
+        assert(os.path.exists("oldfile"))
+        patch_content = """--- /dev/null
++++ b/newfile
+@@ -0,0 +0,3 @@
++New file!
++New file!
++New file!
+--- a/oldfile
++++ b/dev/null
+@@ -0,1 +0,0 @@
+-legacy code
+"""
+        patch(patch_string=patch_content)
+        self.output.info("NEW FILE=%s" % load("newfile"))
+        self.output.info("OLD FILE=%s" % os.path.exists("oldfile"))
+'''
+        client = TestClient()
+        client.save({"conanfile.py": conanfile})
+        client.run("create . user/testing")
+        self.assertIn("test/1.9.10@user/testing: NEW FILE=New file!\nNew file!\nNew file!\n",
+                      client.out)
+        self.assertIn("test/1.9.10@user/testing: OLD FILE=False", client.out)
+
+    def test_error_patch(self):
+        file_content = base_conanfile + '''
+    def build(self):
+        patch_content = "some corrupted patch"
+        patch(patch_string=patch_content, output=self.output)
+
+'''
+        client = TestClient()
+        client.save({"conanfile.py": file_content})
+        client.run("install .")
+        client.run("build .", assert_error=True)
+        self.assertIn("patch: error: no patch data found!", client.out)
+        self.assertIn("ERROR: conanfile.py (test/1.9.10@None/None): "
+                      "Error in build() method, line 12", client.out)
+        self.assertIn("Failed to parse patch: string", client.out)
+
+    def _save_files(self, file_content):
+        tmp_dir = temp_folder()
+        file_path = os.path.join(tmp_dir, "conanfile.py")
+        text_file = os.path.join(tmp_dir, "text.txt")
+        save(file_path, file_content)
+        save(text_file, "ONE TWO THREE")
+        return tmp_dir, file_path, text_file
+
+    def _build_and_check(self, tmp_dir, file_path, text_file, msg):
+        loader = ConanFileLoader(None, TestBufferConanOutput(), ConanPythonRequire(None, None))
+        ret = loader.load_consumer(file_path, test_processed_profile())
+        curdir = os.path.abspath(os.curdir)
+        os.chdir(tmp_dir)
+        try:
+            ret.build()
+        finally:
+            os.chdir(curdir)
+
+        content = load(text_file)
+        self.assertEquals(content, msg)
diff --git a/conans/test/unittests/util/conanfile_tools_test.py b/conans/test/unittests/util/conanfile_tools_test.py
index ad64b124f..e58cacf78 100644
--- a/conans/test/unittests/util/conanfile_tools_test.py
+++ b/conans/test/unittests/util/conanfile_tools_test.py
@@ -2,16 +2,14 @@ import os
 import sys
 import unittest
 
-from parameterized.parameterized import parameterized
 
 from conans.client import tools
-from conans.client.graph.python_requires import ConanPythonRequire
-from conans.client.loader import ConanFileLoader
 from conans.client.output import ConanOutput
 from conans.test.utils.test_files import temp_folder
-from conans.test.utils.tools import TestClient, test_processed_profile,\
-    TestBufferConanOutput
+from conans.test.utils.tools import TestClient, TestBufferConanOutput
 from conans.util.files import load, save
+from conans.client.tools.files import replace_in_file
+
 
 base_conanfile = '''
 from conans import ConanFile
@@ -61,116 +59,9 @@ class Pkg(ConanFile):
         self.assertEqual(content, "Hello world!")
 
     def test_replace_in_file(self):
-        file_content = base_conanfile + '''
-    def build(self):
-        replace_in_file("text.txt", "ONE TWO THREE", "FOUR FIVE SIX")
-'''
-        tmp_dir, file_path, text_file = self._save_files(file_content)
-        self._build_and_check(tmp_dir, file_path, text_file, "FOUR FIVE SIX")
-
-    @parameterized.expand([(0, ), (1, )])
-    def test_patch_from_file(self, strip):
-        if strip:
-            file_content = base_conanfile + '''
-    def build(self):
-        patch(patch_file="file.patch", strip=%s)
-''' % strip
-            patch_content = '''--- %s/text.txt\t2016-01-25 17:57:11.452848309 +0100
-+++ %s/text_new.txt\t2016-01-25 17:57:28.839869950 +0100
-@@ -1 +1 @@
--ONE TWO THREE
-+ONE TWO FOUR''' % ("old_path", "new_path")
-        else:
-            file_content = base_conanfile + '''
-    def build(self):
-        patch(patch_file="file.patch")
-'''
-            patch_content = '''--- text.txt\t2016-01-25 17:57:11.452848309 +0100
-+++ text_new.txt\t2016-01-25 17:57:28.839869950 +0100
-@@ -1 +1 @@
--ONE TWO THREE
-+ONE TWO FOUR'''
-
-        tmp_dir, file_path, text_file = self._save_files(file_content)
-        patch_file = os.path.join(tmp_dir, "file.patch")
-        save(patch_file, patch_content)
-        self._build_and_check(tmp_dir, file_path, text_file, "ONE TWO FOUR")
-
-    def test_patch_from_str(self):
-        file_content = base_conanfile + '''
-    def build(self):
-        patch_content = \'''--- text.txt\t2016-01-25 17:57:11.452848309 +0100
-+++ text_new.txt\t2016-01-25 17:57:28.839869950 +0100
-@@ -1 +1 @@
--ONE TWO THREE
-+ONE TWO DOH!\'''
-        patch(patch_string=patch_content)
-
-'''
-        tmp_dir, file_path, text_file = self._save_files(file_content)
-        self._build_and_check(tmp_dir, file_path, text_file, "ONE TWO DOH!")
-
-    def test_patch_new_delete(self):
-        conanfile = base_conanfile + '''
-    def build(self):
-        from conans.tools import load, save
-        save("oldfile", "legacy code")
-        assert(os.path.exists("oldfile"))
-        patch_content = """--- /dev/null
-+++ b/newfile
-@@ -0,0 +0,3 @@
-+New file!
-+New file!
-+New file!
---- a/oldfile
-+++ b/dev/null
-@@ -0,1 +0,0 @@
--legacy code
-"""
-        patch(patch_string=patch_content)
-        self.output.info("NEW FILE=%s" % load("newfile"))
-        self.output.info("OLD FILE=%s" % os.path.exists("oldfile"))
-'''
-        client = TestClient()
-        client.save({"conanfile.py": conanfile})
-        client.run("create . user/testing")
-        self.assertIn("test/1.9.10@user/testing: NEW FILE=New file!\nNew file!\nNew file!\n",
-                      client.out)
-        self.assertIn("test/1.9.10@user/testing: OLD FILE=False", client.out)
-
-    def test_error_patch(self):
-        file_content = base_conanfile + '''
-    def build(self):
-        patch_content = "some corrupted patch"
-        patch(patch_string=patch_content, output=self.output)
-
-'''
-        client = TestClient()
-        client.save({"conanfile.py": file_content})
-        client.run("install .")
-        client.run("build .", assert_error=True)
-        self.assertIn("patch: error: no patch data found!", client.user_io.out)
-        self.assertIn("ERROR: conanfile.py (test/1.9.10@None/None): Error in build() method, line 12",
-                      client.user_io.out)
-        self.assertIn("Failed to parse patch: string", client.user_io.out)
-
-    def _save_files(self, file_content):
         tmp_dir = temp_folder()
-        file_path = os.path.join(tmp_dir, "conanfile.py")
         text_file = os.path.join(tmp_dir, "text.txt")
-        save(file_path, file_content)
         save(text_file, "ONE TWO THREE")
-        return tmp_dir, file_path, text_file
-
-    def _build_and_check(self, tmp_dir, file_path, text_file, msg):
-        loader = ConanFileLoader(None, TestBufferConanOutput(), ConanPythonRequire(None, None))
-        ret = loader.load_consumer(file_path, test_processed_profile())
-        curdir = os.path.abspath(os.curdir)
-        os.chdir(tmp_dir)
-        try:
-            ret.build()
-        finally:
-            os.chdir(curdir)
-
-        content = load(text_file)
-        self.assertEquals(content, msg)
+        replace_in_file(text_file, "ONE TWO THREE", "FOUR FIVE SIX",
+                        output=TestBufferConanOutput())
+        self.assertEqual(load(text_file), "FOUR FIVE SIX")
diff --git a/conans/test/utils/test_files.py b/conans/test/utils/test_files.py
index 67b7c0f75..001fdbfec 100644
--- a/conans/test/utils/test_files.py
+++ b/conans/test/utils/test_files.py
@@ -10,6 +10,7 @@ from conans.paths import PACKAGE_TGZ_NAME
 from conans.test import CONAN_TEST_FOLDER
 from conans.test.utils.cpp_test_files import cpp_hello_conan_files, cpp_hello_source_files
 from conans.test.utils.go_test_files import go_hello_conan_files, go_hello_source_files
+from conans.client.tools.win import get_cased_path
 
 
 def wait_until_removed(folder):
@@ -27,6 +28,8 @@ def wait_until_removed(folder):
 
 def temp_folder(path_with_spaces=True):
     t = tempfile.mkdtemp(suffix='conans', dir=CONAN_TEST_FOLDER)
+    # Make sure that the temp folder is correctly cased, as tempfile return lowercase for Win
+    t = get_cased_path(t)
     # necessary for Mac OSX, where the temp folders in /var/ are symlinks to /private/var/
     t = os.path.realpath(t)
     # FreeBSD and Solaris do not use GNU Make as a the default 'make' program which has trouble

pip install -e .[dev]
parse_log_pytest
requirements.txt
nose pytest
apt-get update apt-get install -y gcc
3.6
conans/requirements.txt conans/requirements_server.txt conans/requirements_dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astroid==2.11.7
attrs==22.2.0
beautifulsoup4==4.12.3
bottle==0.12.25
certifi==2021.5.30
charset-normalizer==2.0.12
codecov==2.1.13
colorama==0.4.5
-e git+https://github.com/conan-io/conan.git@5e4e2585ca1e8ae4562f779635d885a7b9dbb38d#egg=conan
coverage==4.2
deprecation==2.0.7
dill==0.3.4
distro==1.1.0
fasteners==0.19
future==0.16.0
idna==3.10
importlib-metadata==4.8.3
importlib-resources==5.4.0
iniconfig==1.1.1
isort==5.10.1
lazy-object-proxy==1.7.1
mccabe==0.7.0
mock==1.3.0
node-semver==0.6.1
nose==1.3.7
packaging==21.3
parameterized==0.8.1
patch==1.16
pbr==6.1.1
platformdirs==2.4.0
pluggy==1.0.0
pluginbase==0.7
py==1.11.0
Pygments==2.14.0
PyJWT==1.7.1
pylint==2.13.9
pyparsing==3.1.4
pytest==7.0.1
PyYAML==3.13
requests==2.27.1
six==1.17.0
soupsieve==2.3.2.post1
tomli==1.2.3
tqdm==4.64.1
typed-ast==1.5.5
typing_extensions==4.1.1
urllib3==1.26.20
waitress==2.0.0
WebOb==1.8.9
WebTest==2.0.35
wrapt==1.16.0
zipp==3.6.0

name: conan
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astroid==2.11.7
      - attrs==22.2.0
      - beautifulsoup4==4.12.3
      - bottle==0.12.25
      - charset-normalizer==2.0.12
      - codecov==2.1.13
      - colorama==0.4.5
      - coverage==4.2
      - deprecation==2.0.7
      - dill==0.3.4
      - distro==1.1.0
      - fasteners==0.19
      - future==0.16.0
      - idna==3.10
      - importlib-metadata==4.8.3
      - importlib-resources==5.4.0
      - iniconfig==1.1.1
      - isort==5.10.1
      - lazy-object-proxy==1.7.1
      - mccabe==0.7.0
      - mock==1.3.0
      - node-semver==0.6.1
      - nose==1.3.7
      - packaging==21.3
      - parameterized==0.8.1
      - patch==1.16
      - pbr==6.1.1
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - pluginbase==0.7
      - py==1.11.0
      - pygments==2.14.0
      - pyjwt==1.7.1
      - pylint==2.13.9
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pyyaml==3.13
      - requests==2.27.1
      - six==1.17.0
      - soupsieve==2.3.2.post1
      - tomli==1.2.3
      - tqdm==4.64.1
      - typed-ast==1.5.5
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - waitress==2.0.0
      - webob==1.8.9
      - webtest==2.0.35
      - wrapt==1.16.0
      - zipp==3.6.0
prefix: /opt/conda/envs/conan

conans/test/functional/command/config_install_test.py::ConfigInstallTest::test_git_checkout_is_possible conans/test/functional/hooks/test_post_export.py::PostExportTestCase::test_called_before_digest
conans/test/functional/command/info_command_test.py::InfoTest::test_json_output_build conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py::SVNConanfileInRepoRootTest::test_local_monorepo conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py::SVNConanfileInRepoRootTest::test_local_monorepo_chdir conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py::SVNConanfileInRepoRootTest::test_local_root_folder conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py::SVNConanfileInRepoRootTest::test_remote_monorepo conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py::SVNConanfileInRepoRootTest::test_remote_monorepo_chdir conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py::SVNConanfileInRepoRootTest::test_remote_root_folder conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py::GitConanfileInRepoRootTest::test_local_root_folder conans/test/functional/scm/workflows/test_conanfile_in_repo_root.py::GitConanfileInRepoRootTest::test_remote_root_folder conans/test/unittests/tools/files_patch_test.py::ToolsFilesPatchTest::test_error_patch conans/test/unittests/tools/files_patch_test.py::ToolsFilesPatchTest::test_patch_new_delete conans/test/unittests/tools/files_patch_test.py::ToolsFilesPatchTest::test_patch_strip_delete conans/test/unittests/tools/files_patch_test.py::ToolsFilesPatchTest::test_patch_strip_new
conans/test/functional/command/config_install_test.py::ConfigInstallTest::test_config_install_requester conans/test/functional/command/config_install_test.py::ConfigInstallTest::test_without_profile_folder conans/test/functional/scm/test_command_export.py::ExportErrorCommandTestCase::test_no_repo_0_SVN conans/test/functional/scm/test_command_export.py::ExportErrorCommandTestCase::test_no_repo_1_SVN conans/test/functional/scm/test_command_export.py::ExportErrorCommandTestCase::test_no_repo_2_git conans/test/functional/scm/test_command_export.py::ExportErrorCommandTestCase::test_no_repo_3_git conans/test/functional/scm/test_command_export.py::ExportCommandTestCase::test_non_existing_remote_0 conans/test/functional/scm/test_command_export.py::ExportCommandTestCase::test_non_existing_remote_1 conans/test/unittests/client/source/test_run_scm.py::RunSCMTest::test_in_cache_no_local_sources conans/test/unittests/client/source/test_run_scm.py::RunSCMTest::test_in_cache_with_local_sources conans/test/unittests/client/source/test_run_scm.py::RunSCMTest::test_user_space_no_local_sources conans/test/unittests/client/source/test_run_scm.py::RunSCMTest::test_user_space_with_local_sources conans/test/unittests/tools/files_patch_test.py::test_processed_profile conans/test/unittests/tools/files_patch_test.py::ToolsFilesPatchTest::test_patch_from_file_0 conans/test/unittests/tools/files_patch_test.py::ToolsFilesPatchTest::test_patch_from_file_1 conans/test/unittests/tools/files_patch_test.py::ToolsFilesPatchTest::test_patch_from_str conans/test/unittests/util/conanfile_tools_test.py::ConanfileToolsTest::test_replace_in_file conans/test/unittests/util/conanfile_tools_test.py::ConanfileToolsTest::test_untar
conans/client/tools/win.py conans/client/graph/graph_manager.py conans/client/cmd/export.py conans/client/tools/files.py conans/__init__.py conans/client/cmd/uploader.py conans/client/conf/config_installer.py conans/util/files.py conans/client/hook_manager.py
conans/client/tools/win.py conans/client/graph/graph_manager.py conans/client/cmd/export.py conans/client/tools/files.py conans/__init__.py conans/client/cmd/uploader.py conans/client/conf/config_installer.py conans/util/files.py conans/client/hook_manager.py