teach the scanner to look at uncommitted files
Update the Scanner to look at staged and unstaged changes to files in the local copy of the repository. We can't detect unknown files, yet. Closes-Bug: #1553155 Change-Id: I77ed60e6f8b8f819aabb361f34cf779623907f7b Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Include the local working copy when scanning the history of the
|
||||
current branch. Notes files must at least be staged to indicate
|
||||
that they will eventually be part of the history, but subsequent
|
||||
changes to the file do not need to also be staged to be seen.
|
@ -20,7 +20,9 @@ import re
|
||||
import sys
|
||||
|
||||
from dulwich import diff_tree
|
||||
from dulwich import index as d_index
|
||||
from dulwich import objects
|
||||
from dulwich import porcelain
|
||||
from dulwich import repo
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -423,7 +425,23 @@ class RenoRepo(repo.Repo):
|
||||
return tree
|
||||
|
||||
def get_file_at_commit(self, filename, sha):
|
||||
"Return the contents of the file if it exists at the commit, or None."
|
||||
"""Return the contents of the file.
|
||||
|
||||
If sha is None, return the working copy of the file. If the
|
||||
file cannot be read from the working dir, return None.
|
||||
|
||||
If the sha is not None and the file exists at the commit,
|
||||
return the data from the stored blob. If the file does not
|
||||
exist at the commit, return None.
|
||||
|
||||
"""
|
||||
if sha is None:
|
||||
# Get the copy from the working directory.
|
||||
try:
|
||||
with open(os.path.join(self.path, filename), 'r') as f:
|
||||
return f.read()
|
||||
except IOError:
|
||||
return None
|
||||
# Get the tree associated with the commit identified by the
|
||||
# input SHA, then look through the items in the tree to find
|
||||
# the one with the path matching the filename. Take the
|
||||
@ -763,10 +781,48 @@ class Scanner(object):
|
||||
LOG.debug('current repository version: %s' % current_version)
|
||||
if current_version not in versions_by_date:
|
||||
versions_by_date.insert(0, current_version)
|
||||
versions_by_date.insert(0, '*working-copy*')
|
||||
|
||||
# Track the versions we have seen and the earliest version for
|
||||
# which we have seen a given note's unique id.
|
||||
tracker = _ChangeTracker()
|
||||
|
||||
# Process the local index, if we are scanning the current
|
||||
# branch.
|
||||
if not branch:
|
||||
prefix = notesdir.rstrip('/') + '/'
|
||||
index = self._repo.open_index()
|
||||
|
||||
# Pretend anything known to the repo and changed but not
|
||||
# staged is part of the fake version '*working-copy*'.
|
||||
LOG.debug('scanning unstaged changes')
|
||||
for fname in d_index.get_unstaged_changes(index, self.reporoot):
|
||||
fname = fname.decode('utf-8')
|
||||
LOG.debug('found unstaged file %s', fname)
|
||||
if fname.startswith(prefix) and _note_file(fname):
|
||||
fullpath = os.path.join(self.reporoot, fname)
|
||||
if os.path.exists(fullpath):
|
||||
LOG.debug('found file %s', fullpath)
|
||||
tracker.add(fname, None, '*working-copy*')
|
||||
else:
|
||||
LOG.debug('deleted file %s', fullpath)
|
||||
tracker.delete(fname, None, '*working-copy*')
|
||||
|
||||
# Pretend anything in the index is part of the fake
|
||||
# version "*working-copy*".
|
||||
LOG.debug('scanning staged schanges')
|
||||
changes = porcelain.get_tree_changes(self._repo)
|
||||
for fname in changes['add']:
|
||||
fname = fname.decode('utf-8')
|
||||
tracker.add(fname, None, '*working-copy*')
|
||||
for fname in changes['modify']:
|
||||
fname = fname.decode('utf-8')
|
||||
tracker.modify(fname, None, '*working-copy*')
|
||||
for fname in changes['delete']:
|
||||
fname = fname.decode('utf-8')
|
||||
tracker.delete(fname, None, '*working-copy*')
|
||||
|
||||
# Process the git commit history.
|
||||
for counter, entry in enumerate(self._topo_traversal(branch), 1):
|
||||
|
||||
sha = entry.commit.id
|
||||
|
@ -20,6 +20,7 @@ import os.path
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from dulwich import diff_tree
|
||||
from dulwich import objects
|
||||
@ -529,6 +530,82 @@ class BasicTest(Base):
|
||||
results,
|
||||
)
|
||||
|
||||
def test_staged_file(self):
|
||||
# Prove that we can get a file we have staged.
|
||||
# Start with a standard commit and tag
|
||||
self._make_python_package()
|
||||
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
|
||||
# Now stage a release note
|
||||
n = self.get_note_num()
|
||||
basename = 'staged-note-%016x.yaml' % n
|
||||
filename = os.path.join(self.reporoot, 'releasenotes', 'notes',
|
||||
basename)
|
||||
create._make_note_file(filename, 'staged note')
|
||||
self.repo.git('add', filename)
|
||||
status_results = self.repo.git('status')
|
||||
self.addDetail('git status', text_content(status_results))
|
||||
# Now run the scanner
|
||||
self.scanner = scanner.Scanner(self.c)
|
||||
raw_results = self.scanner.get_notes_by_version()
|
||||
self.assertEqual(
|
||||
{'*working-copy*': [
|
||||
(os.path.join('releasenotes', 'notes', basename),
|
||||
None)],
|
||||
},
|
||||
raw_results,
|
||||
)
|
||||
|
||||
@unittest.skip('dulwich does not know how to identify new files')
|
||||
def test_added_tagged_not_staged(self):
|
||||
# Prove that we can get a file we have created but not staged.
|
||||
# Start with a standard commit and tag
|
||||
self._make_python_package()
|
||||
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
|
||||
# Now create a note without staging it
|
||||
n = self.get_note_num()
|
||||
basename = 'staged-note-%016x.yaml' % n
|
||||
filename = os.path.join(self.reporoot, 'releasenotes', 'notes',
|
||||
basename)
|
||||
create._make_note_file(filename, 'staged note')
|
||||
status_results = self.repo.git('status')
|
||||
self.addDetail('git status', text_content(status_results))
|
||||
# Now run the scanner
|
||||
self.scanner = scanner.Scanner(self.c)
|
||||
raw_results = self.scanner.get_notes_by_version()
|
||||
# Take the staged version of the file, but associate it with
|
||||
# tagged version 1.0.0 because the file was added before that
|
||||
# version.
|
||||
self.assertEqual(
|
||||
{'1.0.0': [(os.path.join('releasenotes', 'notes', basename),
|
||||
None)],
|
||||
},
|
||||
raw_results,
|
||||
)
|
||||
|
||||
def test_modified_tagged_not_staged(self):
|
||||
# Prove that we can get a file we have changed but not staged.
|
||||
# Start with a standard commit and tag
|
||||
self._make_python_package()
|
||||
f1 = self._add_notes_file('slug1')
|
||||
self.repo.git('tag', '-s', '-m', 'first tag', '1.0.0')
|
||||
# Now modify the note
|
||||
fullpath = os.path.join(self.repo.reporoot, f1)
|
||||
with open(fullpath, 'w') as f:
|
||||
f.write('modified first note')
|
||||
status_results = self.repo.git('status')
|
||||
self.addDetail('git status', text_content(status_results))
|
||||
# Now run the scanner
|
||||
self.scanner = scanner.Scanner(self.c)
|
||||
raw_results = self.scanner.get_notes_by_version()
|
||||
# Take the staged version of the file, but associate it with
|
||||
# tagged version 1.0.0 because the file was added before that
|
||||
# version.
|
||||
self.assertEqual(
|
||||
{'1.0.0': [(f1, None)],
|
||||
},
|
||||
raw_results,
|
||||
)
|
||||
|
||||
|
||||
class FileContentsTest(Base):
|
||||
|
||||
@ -597,6 +674,19 @@ class FileContentsTest(Base):
|
||||
contents,
|
||||
)
|
||||
|
||||
def test_staged_file(self):
|
||||
# Prove we are not picking up the contents from the local
|
||||
# filesystem outside of the git history.
|
||||
f1 = self._add_notes_file(contents='initial-contents')
|
||||
with open(os.path.join(self.reporoot, f1), 'w') as f:
|
||||
f.write('new contents for file')
|
||||
r = scanner.RenoRepo(self.reporoot)
|
||||
contents = r.get_file_at_commit(f1, None)
|
||||
self.assertEqual(
|
||||
'new contents for file',
|
||||
contents,
|
||||
)
|
||||
|
||||
|
||||
class PreReleaseTest(Base):
|
||||
|
||||
|
Reference in New Issue
Block a user