Repository: conan-io/conan

Issue:
[SCM] SCM feature + recipe in a repo subfolder leads to "same computer clone" bug
Getting back to our Conan recipe, conan `1.7.1` solves the problem described #3322 thus (re)allowing a recipe living in subfolder of the repository to use the `scm` feature.
Sadly, another bug, initially described in #3069 (under the name _same computer optimization problem_) is still present: when `conan create`ing the recipe, in this situation, the creation fails, complaining there are no files in the `build` folder (and indeed, there are none).

It seems the analysis made in #3069 is still relevant:
Basically, when calling `conan create`, an optimization described in the [tip box of this documentation section](https://docs.conan.io/en/latest/creating_packages/package_repo.html#capturing-the-remote-and-commit-from-git-scm-experimental) creates an `scm_folder.txt` file in the conan package. When this file is present, conan tries to clone the repo from the path in this file instead of the git url stored in the exported recipe. 

Sadly, this file contains the path to the folder containing the recipe, instead of the path to the root of the repository. So, when the recipe is at the root of the repository, the behaviour appears correct. But if the recipe is stored in a subfolder, then it seems nothing is cloned over to the build folder, failing the process.


Discussion:
Ok, if I understood it correctly, this is a bug, and the solution would be that the ``scm_folder.txt`` points to the root of the repo, not to the current conanfile.py folder. 
Seems necessary steps would be:
- Augment the SCM and Git helpers with ``get_root_folder()`` functionality that will retrieve the root folder of the repo.
- Use that in ``_capture_export_scm_data()``

Resolving patch:
diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py
--- a/conans/client/cmd/export.py
+++ b/conans/client/cmd/export.py
@@ -60,7 +60,7 @@ def cmd_export(conanfile_path, conanfile, reference, keep_source, output, client
                           keep_source)
 
 
-def _capture_export_scm_data(conanfile, src_path, destination_folder, output, paths, conan_ref):
+def _capture_export_scm_data(conanfile, conanfile_dir, destination_folder, output, paths, conan_ref):
 
     scm_src_file = paths.scm_folder(conan_ref)
     if os.path.exists(scm_src_file):
@@ -71,7 +71,7 @@ def _capture_export_scm_data(conanfile, src_path, destination_folder, output, pa
     if not scm_data or not (scm_data.capture_origin or scm_data.capture_revision):
         return
 
-    scm = SCM(scm_data, src_path)
+    scm = SCM(scm_data, conanfile_dir)
 
     if scm_data.url == "auto":
         origin = scm.get_remote_url()
@@ -87,6 +87,7 @@ def _capture_export_scm_data(conanfile, src_path, destination_folder, output, pa
         output.success("Revision deduced by 'auto': %s" % scm_data.revision)
 
     # Generate the scm_folder.txt file pointing to the src_path
+    src_path = scm.get_repo_root()
     save(scm_src_file, src_path.replace("\\", "/"))
     scm_data.replace_in_file(os.path.join(destination_folder, "conanfile.py"))
 
diff --git a/conans/client/tools/scm.py b/conans/client/tools/scm.py
--- a/conans/client/tools/scm.py
+++ b/conans/client/tools/scm.py
@@ -13,8 +13,8 @@
 
 class Git(object):
 
-    def __init__(self, folder=None, verify_ssl=True, username=None, password=None, force_english=True,
-                 runner=None):
+    def __init__(self, folder=None, verify_ssl=True, username=None, password=None,
+                 force_english=True, runner=None):
         self.folder = folder or os.getcwd()
         if not os.path.exists(self.folder):
             os.makedirs(self.folder)
@@ -33,6 +33,9 @@ def run(self, command):
                 else:
                     return self._runner(command)
 
+    def get_repo_root(self):
+        return self.run("rev-parse --show-toplevel")
+
     def get_url_with_credentials(self, url):
         if not self._username or not self._password:
             return url
diff --git a/conans/model/scm.py b/conans/model/scm.py
--- a/conans/model/scm.py
+++ b/conans/model/scm.py
@@ -75,3 +75,6 @@ def get_remote_url(self):
 
     def get_revision(self):
         return self.repo.get_revision()
+
+    def get_repo_root(self):
+        return self.repo.get_repo_root()


Test patch:
diff --git a/conans/test/functional/scm_test.py b/conans/test/functional/scm_test.py
--- a/conans/test/functional/scm_test.py
+++ b/conans/test/functional/scm_test.py
@@ -149,6 +149,21 @@ def test_auto_subfolder(self):
         self.assertTrue(os.path.exists(os.path.join(folder, "mysub", "myfile.txt")))
         self.assertFalse(os.path.exists(os.path.join(folder, "mysub", "conanfile.py")))
 
+    def test_auto_conanfile_no_root(self):
+        """
+        Conanfile is not in the root of the repo: https://github.com/conan-io/conan/issues/3465
+        """
+        curdir = self.client.current_folder
+        conanfile = base.format(url="auto", revision="auto")
+        self.client.save({"conan/conanfile.py": conanfile, "myfile.txt": "content of my file"})
+        self._commit_contents()
+        self.client.runner('git remote add origin https://myrepo.com.git', cwd=curdir)
+
+        # Create the package
+        self.client.run("create conan/ user/channel")
+        sources_dir = self.client.client_cache.scm_folder(self.reference)
+        self.assertEquals(load(sources_dir), curdir.replace('\\', '/'))  # Root of git is 'curdir'
+
     def test_deleted_source_folder(self):
         path, commit = create_local_git_repo({"myfile": "contents"}, branch="my_release")
         curdir = self.client.current_folder.replace("\\", "/")
diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py
--- a/conans/test/util/tools_test.py
+++ b/conans/test/util/tools_test.py
@@ -1100,6 +1100,19 @@ def get_file2():
 
 class GitToolTest(unittest.TestCase):
 
+    def test_repo_root(self):
+        root_path, _ = create_local_git_repo({"myfile": "anything"})
+
+        # Initialized in the root folder
+        git = Git(root_path)
+        self.assertEqual(root_path, git.get_repo_root())
+
+        # Initialized elsewhere
+        subfolder = os.path.join(root_path, 'subfolder')
+        os.makedirs(subfolder)
+        git = Git(subfolder)
+        self.assertEqual(root_path, git.get_repo_root())
+
     def test_clone_git(self):
         path, _ = create_local_git_repo({"myfile": "contents"})
         tmp = temp_folder()