def test_get_tree_changes_add(self): """Unit test for get_tree_changes add.""" # Make a dummy file, stage filename = 'bar' fullpath = os.path.join(self.repo.path, filename) with open(fullpath, 'w') as f: f.write('stuff') porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test status', author=b'', committer=b'') filename = 'foo' fullpath = os.path.join(self.repo.path, filename) with open(fullpath, 'w') as f: f.write('stuff') porcelain.add(repo=self.repo.path, paths=fullpath) changes = porcelain.get_tree_changes(self.repo.path) self.assertEqual(changes['add'][0], filename.encode('ascii')) self.assertEqual(len(changes['add']), 1) self.assertEqual(len(changes['modify']), 0) self.assertEqual(len(changes['delete']), 0)
def test_get_tree_changes_delete(self): """Unit test for get_tree_changes delete.""" # Make a dummy file, stage, commit, remove filename = 'foo' fullpath = os.path.join(self.repo.path, filename) with open(fullpath, 'w') as f: f.write('stuff') porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test status', author=b'author <email>', committer=b'committer <email>') cwd = os.getcwd() try: os.chdir(self.repo.path) porcelain.remove(repo=self.repo.path, paths=[filename]) finally: os.chdir(cwd) changes = porcelain.get_tree_changes(self.repo.path) self.assertEqual(changes['delete'][0], filename.encode('ascii')) self.assertEqual(len(changes['add']), 0) self.assertEqual(len(changes['modify']), 0) self.assertEqual(len(changes['delete']), 1)
def test_get_tree_changes_delete(self): """Unit test for get_tree_changes delete.""" # Make a dummy file, stage, commit, remove filename = "foo" with open(os.path.join(self.repo.path, filename), "w") as f: f.write("stuff") porcelain.add(repo=self.repo.path, paths=filename) porcelain.commit(repo=self.repo.path, message=b"test status", author=b"", committer=b"") porcelain.rm(repo=self.repo.path, paths=[filename]) changes = porcelain.get_tree_changes(self.repo.path) self.assertEqual(changes["delete"][0], filename.encode("ascii")) self.assertEqual(len(changes["add"]), 0) self.assertEqual(len(changes["modify"]), 0) self.assertEqual(len(changes["delete"]), 1)
def test_get_tree_changes_delete(self): """Unit test for get_tree_changes delete.""" # Make a dummy file, stage, commit, remove filename = 'foo' with open(os.path.join(self.repo.path, filename), 'w') as f: f.write('stuff') porcelain.add(repo=self.repo.path, paths=filename) porcelain.commit(repo=self.repo.path, message=b'test status', author=b'', committer=b'') porcelain.rm(repo=self.repo.path, paths=[filename]) changes = porcelain.get_tree_changes(self.repo.path) self.assertEqual(changes['delete'][0], filename.encode('ascii')) self.assertEqual(len(changes['add']), 0) self.assertEqual(len(changes['modify']), 0) self.assertEqual(len(changes['delete']), 1)
def test_get_tree_changes_modify(self): """Unit test for get_tree_changes modify.""" # Make a dummy file, stage, commit, modify filename = 'foo' fullpath = os.path.join(self.repo.path, filename) with open(fullpath, 'w') as f: f.write('stuff') porcelain.add(repo=self.repo.path, paths=filename) porcelain.commit(repo=self.repo.path, message='test status', author='', committer='') with open(fullpath, 'w') as f: f.write('otherstuff') porcelain.add(repo=self.repo.path, paths=filename) changes = porcelain.get_tree_changes(self.repo.path) self.assertEqual(changes['modify'][0], filename) self.assertEqual(len(changes['add']), 0) self.assertEqual(len(changes['modify']), 1) self.assertEqual(len(changes['delete']), 0)
def get_notes_by_version(self): """Return an OrderedDict mapping versions to lists of notes files. The versions are presented in reverse chronological order. Notes files are associated with the earliest version for which they were available, regardless of whether they changed later. :param reporoot: Path to the root of the git repository. :type reporoot: str """ reporoot = self.reporoot notesdir = self.conf.notespath branch = self.conf.branch earliest_version = self.conf.earliest_version collapse_pre_releases = self.conf.collapse_pre_releases stop_at_branch_base = self.conf.stop_at_branch_base LOG.info('scanning %s/%s (branch=%s earliest_version=%s)', reporoot.rstrip('/'), notesdir.lstrip('/'), branch or '*current*', earliest_version) # Determine the current version, which might be an unreleased or # dev version if there are unreleased commits at the head of the # branch in question. current_version = self._get_current_version(branch) LOG.debug('current repository version: %s' % current_version) # Determine all of the tags known on the branch, in their date # order. We scan the commit history in topological order to ensure # we have the commits in the right version, so we might encounter # the tags in a different order during that phase. versions_by_date = self._get_tags_on_branch(branch) LOG.debug('versions by date %r' % (versions_by_date,)) if earliest_version and earliest_version not in versions_by_date: raise ValueError( 'earliest-version set to unknown revision {!r}'.format( earliest_version)) # If the user has told us where to stop, use that as the # default. scan_stop_tag = self._find_scan_stop_point( earliest_version, versions_by_date, collapse_pre_releases, branch) # If the user has not told us where to stop, try to work it # out for ourselves. if not branch and not earliest_version and stop_at_branch_base: # On the current branch, stop at the point where the most # recent branch was created, if we can find one. LOG.debug('working on current branch without earliest_version') branches = self._get_series_branches() if branches: LOG.debug('looking at base of %s to stop scanning master', branches[-1]) scan_stop_tag = self._get_branch_base(branches[-1]) earliest_version = current_version elif branch and stop_at_branch_base and not earliest_version: # If branch is set and is not "master", # then we want to stop at the version before the tag at the # base of the branch, which involves a bit of searching. LOG.debug('determining earliest_version from branch') branch_base = self._get_branch_base(branch) scan_stop_tag = self._find_scan_stop_point( branch_base, versions_by_date, collapse_pre_releases, branch) if not scan_stop_tag: earliest_version = branch_base else: idx = versions_by_date.index(scan_stop_tag) earliest_version = versions_by_date[idx - 1] if earliest_version and collapse_pre_releases: if self.pre_release_tag_re.search(earliest_version): # The earliest version won't actually be the pre-release # that might have been tagged when the branch was created, # but the final version. Strip the pre-release portion of # the version number. earliest_version = self._strip_pre_release( earliest_version ) if earliest_version: LOG.info('earliest version to include is %s', earliest_version) else: LOG.info('including entire branch history') if scan_stop_tag: LOG.info('stopping scan at %s', scan_stop_tag) # Since the version may not already be known, make sure it is # in the list of versions by date. And since it is the most # recent version, go ahead and insert it at the front of the # list. 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') if fname.startswith(prefix) and _note_file(fname): tracker.add(fname, None, '*working-copy*') for fname in changes['modify']: fname = fname.decode('utf-8') if fname.startswith(prefix) and _note_file(fname): tracker.modify(fname, None, '*working-copy*') for fname in changes['delete']: fname = fname.decode('utf-8') if fname.startswith(prefix) and _note_file(fname): 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 tags_on_commit = self._get_valid_tags_on_commit(sha) LOG.debug('%06d %s %s', counter, sha, tags_on_commit) # If there are no tags in this block, assume the most recently # seen version. tags = tags_on_commit if not tags: tags = [current_version] else: current_version = tags_on_commit[-1] LOG.info('%06d %s updating current version to %s', counter, sha, current_version) # Look for changes to notes files in this commit. The # change has only the basename of the path file, so we # need to prefix that with the notesdir before giving it # to the tracker. changes = _changes_in_subdir(self._repo, entry, notesdir) for change in _aggregate_changes(entry, changes, notesdir): uniqueid = change[0] c_type = change[1] if c_type == diff_tree.CHANGE_ADD: path, blob_sha = change[-2:] fullpath = os.path.join(notesdir, path) tracker.add(fullpath, sha, current_version) elif c_type == diff_tree.CHANGE_DELETE: path = change[-1] fullpath = os.path.join(notesdir, path) tracker.delete(fullpath, sha, current_version) elif c_type == diff_tree.CHANGE_RENAME: path, blob_sha = change[-2:] fullpath = os.path.join(notesdir, path) tracker.rename(fullpath, sha, current_version) elif c_type == diff_tree.CHANGE_MODIFY: path, blob_sha = change[-2:] fullpath = os.path.join(notesdir, path) tracker.modify(fullpath, sha, current_version) else: raise ValueError( 'unknown change instructions {!r}'.format(change) ) if scan_stop_tag and scan_stop_tag in tags: LOG.info( ('reached end of branch after %d commits at %s ' 'with tags %s'), counter, sha, tags) break # Invert earliest_seen to make a list of notes files for each # version. files_and_tags = collections.OrderedDict() for v in tracker.versions: files_and_tags[v] = [] # Produce a list of the actual files present in the repository. If # a note is removed, this step should let us ignore it. for uniqueid, version in tracker.earliest_seen.items(): try: base, sha = tracker.last_name_by_id[uniqueid] LOG.debug('%s: sorting %s into version %s', uniqueid, base, version) files_and_tags[version].append((base, sha)) except KeyError: # Unable to find the file again, skip it to avoid breaking # the build. msg = ('unable to find release notes file associated ' 'with unique id %r, skipping') % uniqueid LOG.debug(msg) print(msg, file=sys.stderr) # Combine pre-releases into the final release, if we are told to # and the final release exists. if collapse_pre_releases: LOG.debug('collapsing pre-release versions into final releases') collapsing = files_and_tags files_and_tags = collections.OrderedDict() for ov in versions_by_date: if ov not in collapsing: # We don't need to collapse this one because there are # no notes attached to it. continue pre_release_match = self.pre_release_tag_re.search(ov) LOG.debug('checking %r', ov) if pre_release_match: # Remove the trailing pre-release part of the version # from the string. canonical_ver = self._strip_pre_release(ov) if canonical_ver not in versions_by_date: # This canonical version was never tagged, so we # do not want to collapse the pre-releases. Reset # to the original version. canonical_ver = ov else: LOG.debug('combining into %r', canonical_ver) else: canonical_ver = ov if canonical_ver not in files_and_tags: files_and_tags[canonical_ver] = [] files_and_tags[canonical_ver].extend(collapsing[ov]) LOG.debug('files_and_tags %s', {k: len(v) for k, v in files_and_tags.items()}) # Only return the parts of files_and_tags that actually have # filenames associated with the versions. LOG.debug('trimming') trimmed = collections.OrderedDict() for ov in versions_by_date: if not files_and_tags.get(ov): continue LOG.debug('keeping %s', ov) # Sort the notes associated with the version so they are in a # deterministic order, to avoid having the same data result in # different output depending on random factors. Earlier # versions of the scanner assumed the notes were recorded in # chronological order based on the commit date, but with the # change to use topological sorting that is no longer # necessarily true. We want the notes to always show up in the # same order, but it doesn't really matter what order that is, # so just sort based on the unique id. trimmed[ov] = sorted(files_and_tags[ov]) # If we have been told to stop at a version, we can do that # now. if earliest_version and ov == earliest_version: LOG.debug('stopping trimming at %s', earliest_version) break LOG.debug( 'found %d versions and %d files', len(trimmed.keys()), sum(len(ov) for ov in trimmed.values()), ) return trimmed
elif command == "status": print >> sys.stderr, "status" status = porcelain.status(repoPath) # status(tracked_changes, unstaged_changes, untracked_changes) results = [] result = collections.OrderedDict() # { result["add"] = status[0]["add"] result["modify"] = status[0]["modify"] result["delete"] = status[0]["delete"] result["unstaged_changes"] = status[1] #unstaged result["untracked_changes"] = status[2] #unstaged results.append(result) splunk.Intersplunk.outputResults(results) elif command == "treechanges": porcelain.get_tree_changes(repoPath) elif command == "branch": """ def branch_list(repo): List all branches. Parameters repo Path to the repository """ branches = porcelain.branch_list(repoPath) print "branches" for item in branches: print item elif command == "fetch": """ def fetch(repo, remote_location, outstream=sys.stdout, errstream=default_bytes_err_stream): Fetch objects from a remote server. Parameters repo Path to the repository