Repository: conan-io/conan
Issue category: Bug Report

Issue:
[bug] git.fetch_commit() throws an error when downloading sources to subfolder
### Describe the bug

`conan.tools.scm.Git.fetch_commit()` method throws an error when `folder` argument is passed to Git class constructor:

```python
def source(self):
    git = Git(self, folder="myFolder")
    git.fetch_commit(url, commit=self.conan_data["sources"]["commit"])
``` 

Error message:
```bash
ERROR: mypackage/1.2.3: Error in source() method, line 65
        git.fetch_commit(url, commit=self.conan_data["sources"]["commit"])
        FileNotFoundError: [Errno 2] No such file or directory: 'myFolder'
```

It seems that `mkdir(self.folder)` is missing in the body of `fetch_commit()` method - similarly as it is done in `clone()` method.

### How to reproduce it

_No response_


Hints:
Hi @bkarasm 

Thanks for your report.
It seems it could be a small UX improvement (what we call a ``fix``, not a ``bugfix``), and I think it should be very straightforward, would you like to do it yourself and contribute a PR? 
Sure, I can give it a try.

Patch:
diff --git a/conan/tools/scm/git.py b/conan/tools/scm/git.py
index c1aeb577a74..f3578e2c9ff 100644
--- a/conan/tools/scm/git.py
+++ b/conan/tools/scm/git.py
@@ -231,6 +231,7 @@ def fetch_commit(self, url, commit, hide_url=True):
         """
         if os.path.exists(url):
             url = url.replace("\\", "/")  # Windows local directory
+        mkdir(self.folder)
         self._conanfile.output.info("Shallow fetch of git repo")
         self.run('init')
         self.run(f'remote add origin "{url}"', hidden_output=url if hide_url else None)


Test patch:
diff --git a/test/functional/tools/scm/test_git.py b/test/functional/tools/scm/test_git.py
index 47214e461bc..ceef1e68610 100644
--- a/test/functional/tools/scm/test_git.py
+++ b/test/functional/tools/scm/test_git.py
@@ -504,6 +504,46 @@ def source(self):
         c.run("source .")
         assert f'conanfile.py (pkg/0.1): RUN: git remote add origin "{url}"' in c.out
 
+    @pytest.mark.skipif(platform.system() == "Linux", reason="Git version in Linux not support it")
+    def test_clone_to_subfolder(self):
+        conanfile = textwrap.dedent("""
+            import os
+            from conan import ConanFile
+            from conan.tools.scm import Git
+            from conan.tools.files import load
+
+            class Pkg(ConanFile):
+                name = "pkg"
+                version = "0.1"
+
+                def layout(self):
+                    self.folders.source = "source"
+
+                def source(self):
+                    git = Git(self, folder="folder")
+                    git.fetch_commit(url="{url}", commit="{commit}")
+                    self.output.info("MYCMAKE: {{}}".format(load(self, "folder/CMakeLists.txt")))
+                    self.output.info("MYFILE: {{}}".format(load(self, "folder/src/myfile.h")))
+            """)
+        folder = os.path.join(temp_folder(), "myrepo")
+        url, commit = create_local_git_repo(files={"src/myfile.h": "myheader!",
+                                                   "CMakeLists.txt": "mycmake"}, folder=folder)
+        # This second commit will NOT be used, as I will use the above commit in the conanfile
+        save_files(path=folder, files={"src/myfile.h": "my2header2!"})
+        git_add_changes_commit(folder=folder)
+
+        c = TestClient()
+        c.save({"conanfile.py": conanfile.format(url=url, commit=commit)})
+        c.run("create . -v")
+        assert "pkg/0.1: MYCMAKE: mycmake" in c.out
+        assert "pkg/0.1: MYFILE: myheader!" in c.out
+
+        # It also works in local flow
+        c.run("source .")
+        assert "conanfile.py (pkg/0.1): MYCMAKE: mycmake" in c.out
+        assert "conanfile.py (pkg/0.1): MYFILE: myheader!" in c.out
+        assert c.load("source/folder/CMakeLists.txt") == "mycmake"
+        assert c.load("source/folder/src/myfile.h") == "myheader!"
 
 class TestGitCloneWithArgs:
     """ Git cloning passing additional arguments