Repository: pypa/setuptools_scm

Issue:
support git's export-ignore
`git archive` reads the `export-ignore` gitattribute (https://git-scm.com/docs/gitattributes#_creating_an_archive) which allows marking some files as NOT packaged into the archive.  It would be nice if setuptools_scm did the same for the sdist.

Typical use cases would be to remove CI scripts.

Unfortunately AFAICT there is no way to ask git for the files it would archive short of actually running git archive and examining the resulting tarball.  OTOH it appears that such a step is much faster than the sdist build itself, so perhaps that remains an acceptable approach?

Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index 7bdd112..19ae6c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,9 @@
 ## Directory-based project format:
 .idea/
 
+### Other editors
+.*.swp
+
 
 ### Python template
 # Byte-compiled / optimized
diff --git a/setuptools_scm/git.py b/setuptools_scm/git.py
index 7fa1b32..6ec9962 100644
--- a/setuptools_scm/git.py
+++ b/setuptools_scm/git.py
@@ -1,15 +1,20 @@
 from .utils import do_ex, trace, has_command
 from .version import meta
+
 from os.path import isfile, join
+import subprocess
+import sys
+import tarfile
 import warnings
 
+
 try:
     from os.path import samefile
 except ImportError:
     from .win_py31_compat import samefile
 
 
-FILES_COMMAND = 'git ls-files'
+FILES_COMMAND = sys.executable + ' -m setuptools_scm.git'
 DEFAULT_DESCRIBE = 'git describe --dirty --tags --long --match *.*'
 
 
@@ -116,3 +121,17 @@ def parse(root, describe_command=DEFAULT_DESCRIBE, pre_parse=warn_on_shallow):
         return meta(tag, distance=number, node=node, dirty=dirty)
     else:
         return meta(tag, node=node, dirty=dirty)
+
+
+def _list_files_in_archive():
+    """List the files that 'git archive' generates.
+    """
+    cmd = ['git', 'archive', 'HEAD']
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+    tf = tarfile.open(fileobj=proc.stdout, mode='r|*')
+    for name in tf.getnames():
+        print(name)
+
+
+if __name__ == "__main__":
+    _list_files_in_archive()
diff --git a/setuptools_scm/utils.py b/setuptools_scm/utils.py
index f744337..0e8a555 100644
--- a/setuptools_scm/utils.py
+++ b/setuptools_scm/utils.py
@@ -61,7 +61,7 @@ def _popen_pipes(cmd, cwd):
 
 def do_ex(cmd, cwd='.'):
     trace('cmd', repr(cmd))
-    if not isinstance(cmd, (list, tuple)):
+    if os.name == "posix" and not isinstance(cmd, (list, tuple)):
         cmd = shlex.split(cmd)
 
     p = _popen_pipes(cmd, cwd)


Test patch:
diff --git a/testing/test_git.py b/testing/test_git.py
index ddae5d4..4f4ad53 100644
--- a/testing/test_git.py
+++ b/testing/test_git.py
@@ -112,3 +112,15 @@ def test_alphanumeric_tags_match(wd):
     wd.commit_testfile()
     wd('git tag newstyle-development-started')
     assert wd.version.startswith('0.1.dev1+g')
+
+
+def test_git_archive_export_ignore(wd):
+    wd.write('test1.txt', 'test')
+    wd.write('test2.txt', 'test')
+    wd.write('.git/info/attributes',
+             # Explicitly include test1.txt so that the test is not affected by
+             # a potentially global gitattributes file on the test machine.
+             '/test1.txt -export-ignore\n/test2.txt export-ignore')
+    wd('git add test1.txt test2.txt')
+    wd.commit()
+    assert integration.find_files(str(wd.cwd)) == ['test1.txt']