diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ef96782e..4d2e62f8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,8 @@ +v1.13.1 +======= + +* fix issue #86 - detect dirty git workdir without tags + v1.13.0 ======= diff --git a/setuptools_scm/git.py b/setuptools_scm/git.py index 6864f962..10280e50 100644 --- a/setuptools_scm/git.py +++ b/setuptools_scm/git.py @@ -1,40 +1,69 @@ -from .utils import do, do_ex, trace +from .utils import do_ex, trace from .version import meta from os.path import abspath, normcase, realpath FILES_COMMAND = 'git ls-files' -DEFAULT_DESCRIBE = 'git describe --dirty --tags --long --match *.*' +DEFAULT_DESCRIBE = 'git describe --tags --long --match *.*' + + +def _normalized(path): + return normcase(abspath(realpath(path))) + + +class GitWorkdir(object): + def __init__(self, path): + self.path = path + + def do_ex(self, cmd): + return do_ex(cmd, cwd=self.path) + + @classmethod + def from_potential_worktree(cls, wd): + real_wd, _, ret = do_ex('git rev-parse --show-toplevel', wd) + if ret: + return + trace('real root', real_wd) + if _normalized(real_wd) != _normalized(wd): + return + + return cls(real_wd) + + def is_dirty(self): + out, _, _ = self.do_ex("git status --porcelain") + return bool(out) + + def node(self): + rev_node, _, ret = self.do_ex('git rev-parse --verify --quiet HEAD') + if not ret: + return rev_node[:7] + + def count_all_nodes(self): + revs, _, _ = self.do_ex('git rev-list HEAD') + return revs.count('\n') + 1 def parse(root, describe_command=DEFAULT_DESCRIBE): - real_root, _, ret = do_ex('git rev-parse --show-toplevel', root) - if ret: - return - trace('real root', real_root) - if (normcase(abspath(realpath(real_root))) != - normcase(abspath(realpath(root)))): - return - rev_node, _, ret = do_ex('git rev-parse --verify --quiet HEAD', root) - if ret: - return meta('0.0') - rev_node = rev_node[:7] + wd = GitWorkdir(root) + + rev_node = wd.node() + dirty = wd.is_dirty() + + if rev_node is None: + return meta('0.0', dirty=dirty) + out, err, ret = do_ex(describe_command, root) - if '-' not in out and '.' not in out: - revs = do('git rev-list HEAD', root) - count = revs.count('\n') - if ret: - out = rev_node - return meta('0.0', distance=count + 1, node=out) if ret: - return - dirty = out.endswith('-dirty') - if dirty: - out = out.rsplit('-', 1)[0] + return meta( + '0.0', + distance=wd.count_all_nodes(), + node=rev_node, + dirty=dirty, + ) tag, number, node = out.rsplit('-', 2) number = int(number) if number: return meta(tag, distance=number, node=node, dirty=dirty) else: - return meta(tag, dirty=dirty, node=node) + return meta(tag, node=node, dirty=dirty) diff --git a/setuptools_scm/utils.py b/setuptools_scm/utils.py index 5623ff0f..7ea068d9 100644 --- a/setuptools_scm/utils.py +++ b/setuptools_scm/utils.py @@ -43,8 +43,11 @@ def _always_strings(env_dict): def do_ex(cmd, cwd='.'): trace('cmd', repr(cmd)) + if not isinstance(cmd, (list, tuple)): + cmd = shlex.split(cmd) + p = subprocess.Popen( - shlex.split(cmd), + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=str(cwd), @@ -70,7 +73,6 @@ def do_ex(cmd, cwd='.'): def do(cmd, cwd='.'): out, err, ret = do_ex(cmd, cwd) if ret: - trace('ret', ret) print(err) return out diff --git a/testing/test_git.py b/testing/test_git.py index 5401b666..0819fad9 100644 --- a/testing/test_git.py +++ b/testing/test_git.py @@ -1,5 +1,6 @@ from setuptools_scm import integration import pytest +from datetime import date @pytest.fixture @@ -32,6 +33,17 @@ def test_version_from_git(wd): assert wd.version.startswith('0.2') +@pytest.mark.issue(86) +def test_git_dirty_notag(wd): + wd.commit_testfile() + wd.write('test.txt', 'test2') + wd("git add test.txt") + assert wd.version.startswith('0.1.dev1') + today = date.today() + # we are dirty, check for the tag + assert today.strftime('.d%Y%m%d') in wd.version + + def test_find_files_stop_at_root_git(wd): wd.commit_testfile() wd.cwd.ensure('project/setup.cfg')