Repository: ofek/hatch-vcs

Issue:
setuptools-scm "root" parameter is ignored in raw-options 
Consider this config in the pyproject.toml:

```toml
[tool.hatch.version]
raw-options = { root = ".." }
source = "vcs"
```

and such source tree:

```
.
├── .git
├── package1
|   ...
│   └── pyproject.toml
├── package2
|   ...
│   └── pyproject.toml
...
└── README.md
```

If I try to build a package with dynamic versioning then it will fail because it `setuptools-scm` is not able to find `.git` directory

```
LookupError: Error getting the version from source `vcs`: setuptools-scm was unable to detect version for /some/path/package1.

Make sure you're either building from a fully intact git repository or PyPI tarballs. Most other sources (such as GitHub's tarballs, a git checkout without the .git folder) don't contain the necessary metadata and will not work.
```

It happens due to this line https://github.com/ofek/hatch-vcs/blob/388c947133061a2056de9fe2f1102685a648f248/hatch_vcs/version_source.py#L56. I'm not sure what 's the intention of this but this prevents to work with projects where `pyproject.toml` is not located in the same directory with `.git`. 

Discussion:

Patch:
diff --git a/hatch_vcs/version_source.py b/hatch_vcs/version_source.py
index cc78d37..71d1e37 100644
--- a/hatch_vcs/version_source.py
+++ b/hatch_vcs/version_source.py
@@ -47,13 +47,11 @@ class VCSVersionSource(VersionSourceInterface):
 
         return self.__config_raw_options
 
-    def get_version_data(self):
+    def construct_setuptools_scm_config(self):
         from copy import deepcopy
 
-        from setuptools_scm import get_version
-
         config = deepcopy(self.config_raw_options)
-        config['root'] = self.root
+        config.setdefault('root', self.root)
 
         config.setdefault('tag_regex', self.config_tag_pattern)
 
@@ -64,6 +62,10 @@ class VCSVersionSource(VersionSourceInterface):
         # Writing only occurs when the build hook is enabled
         config.pop('write_to', None)
         config.pop('write_to_template', None)
+        return config
+
+    def get_version_data(self):
+        from setuptools_scm import get_version
 
-        version = get_version(**config)
+        version = get_version(**self.construct_setuptools_scm_config())
         return {'version': version}


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 60ad2ba..14898cd 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -33,13 +33,17 @@ def temp_dir():
 
 
 @contextmanager
-def create_project(directory, metadata, setup_vcs=True):  # noqa: FBT002
-    project_dir = os.path.join(directory, 'my-app')
-    os.mkdir(project_dir)
+def create_project(directory, metadata, setup_vcs=True, nested=False):  # noqa: FBT002
+    root_dir = project_dir = os.path.join(directory, 'my-app')
+    os.mkdir(root_dir)
 
-    gitignore_file = os.path.join(project_dir, '.gitignore')
+    gitignore_file = os.path.join(root_dir, '.gitignore')
     write_file(gitignore_file, '/my_app/version.py')
 
+    if nested:
+        project_dir = os.path.join(root_dir, 'project')
+        os.mkdir(project_dir)
+
     project_file = os.path.join(project_dir, 'pyproject.toml')
     write_file(project_file, metadata)
 
@@ -55,6 +59,9 @@ def create_project(directory, metadata, setup_vcs=True):  # noqa: FBT002
     os.chdir(project_dir)
     try:
         if setup_vcs:
+            if nested:
+                os.chdir(root_dir)
+
             git('init')
             git('config', '--local', 'user.name', 'foo')
             git('config', '--local', 'user.email', 'foo@bar.baz')
@@ -62,6 +69,9 @@ def create_project(directory, metadata, setup_vcs=True):  # noqa: FBT002
             git('commit', '-m', 'test')
             git('tag', '1.2.3')
 
+            if nested:
+                os.chdir(project_dir)
+
         yield project_dir
     finally:
         os.chdir(origin)
@@ -130,3 +140,25 @@ fallback-version = "7.8.9"
         setup_vcs=False,
     ) as project:
         yield project
+
+
+@pytest.fixture
+def new_project_root_elsewhere(temp_dir):
+    with create_project(
+        temp_dir,
+        """\
+[build-system]
+requires = ["hatchling", "hatch-vcs"]
+build-backend = "hatchling.build"
+
+[project]
+name = "my-app"
+dynamic = ["version"]
+
+[tool.hatch.version]
+source = "vcs"
+raw-options = { root = ".." }
+""",
+        nested=True,
+    ) as project:
+        yield project
diff --git a/tests/test_build.py b/tests/test_build.py
index 6b907ef..a3fdcb6 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -109,3 +109,34 @@ def test_fallback(new_project_fallback):
     assert os.path.isfile(os.path.join(package_directory, 'foo.py'))
     assert os.path.isfile(os.path.join(package_directory, 'bar.py'))
     assert os.path.isfile(os.path.join(package_directory, 'baz.py'))
+
+
+def test_root(new_project_root_elsewhere):
+    build_project('-t', 'wheel')
+
+    build_dir = os.path.join(new_project_root_elsewhere, 'dist')
+    assert os.path.isdir(build_dir)
+
+    artifacts = os.listdir(build_dir)
+    assert len(artifacts) == 1
+    wheel_file = artifacts[0]
+
+    assert wheel_file == 'my_app-1.2.3-py2.py3-none-any.whl'
+
+    extraction_directory = os.path.join(os.path.dirname(new_project_root_elsewhere), '_archive')
+    os.mkdir(extraction_directory)
+
+    with zipfile.ZipFile(os.path.join(build_dir, wheel_file), 'r') as zip_archive:
+        zip_archive.extractall(extraction_directory)
+
+    metadata_directory = os.path.join(extraction_directory, 'my_app-1.2.3.dist-info')
+    assert os.path.isdir(metadata_directory)
+
+    package_directory = os.path.join(extraction_directory, 'my_app')
+    assert os.path.isdir(package_directory)
+    assert len(os.listdir(package_directory)) == 4
+
+    assert os.path.isfile(os.path.join(package_directory, '__init__.py'))
+    assert os.path.isfile(os.path.join(package_directory, 'foo.py'))
+    assert os.path.isfile(os.path.join(package_directory, 'bar.py'))
+    assert os.path.isfile(os.path.join(package_directory, 'baz.py'))