def __get_notes_filename(cls, notes_rev): """Return the filename used to store the git notes. PARAMETERS notes_rev: The revision of the notes change. """ # A git note for any given commit (called the "annotated commit" # in this function) is maintained through a file in the special # refs/notes/commits namespace. The name of that file seems # to vary a little bit from case to case (sometimes it is just # equal to "%s" % rev, while some other times it is equal to # "%s/%s" % (rev[:2], rev[2:]) where rev is the revision of # the annotated commit. The one constant is that it is easy # to deduce the annotated commit ID, by just stripping the '/' # characters. # Look at the files modified by the git notes commit via # diff-tree. There should be only one, pointing us towards # the annotated commit. all_changes = diff_tree('-r', notes_rev) if not all_changes: # notes_rev is probably the root commit. Just use the empty # tree's sha1 as the reference. empty_tree_rev = git.mktree(_input='') all_changes = diff_tree('-r', empty_tree_rev, notes_rev) # The output should be 2 lines... # - The first line contains the hash of what is being compared, # which should be notes_rev; # - The second line contains the file change that interests us. # ... except in the case where the notes_rev does not have # a parent (first note). In that case, we diff-tree'ed against # the empty tree rev, and the first line is omitted. # # Normally, there should only be one entry returned by diff_tree. # However, there is a situation where the output is more than # one entry: Newer version of git sometimes rename some of the # files created by older versions of "git notes" during notes # updates, and bunches those renamings together with a note # update, thus creating commits that actually touch multiple # files (N707-041). In that situation, it appears as though # the first entry is always the one corresponding to the commit # being annotated, so discard anything past the first line. assert all_changes (_, _, _, _, _, filename) = all_changes[0] return filename
def files_changed(self): """Return the list of files changed by this commit. Cache the result in self.__files_changed so that subsequent calls to this method do not require calling git again. """ if self.__files_changed is None: self.__files_changed = [] all_changes = diff_tree('-r', self.base_rev_for_git(), self.rev) for item in all_changes: (old_mode, new_mode, old_sha1, new_sha1, status, filename) \ = item debug('diff-tree entry: %s %s %s %s %s %s' % (old_mode, new_mode, old_sha1, new_sha1, status, filename), level=5) self.__files_changed.append(filename) return self.__files_changed
def style_check_commit(old_rev, new_rev, project_name): """Call check_file for every file changed between old_rev and new_rev. Raise InvalidUpdate if one or more style violation are detected. PARAMETERS old_rev: The commit to be used as a reference to determine the list of files that have been modified/added by the new commit. Must be a valid revision. new_rev: The commit to be checked. project_name: The name of the project (same as the attribute in updates.emails.EmailInfo). """ debug("style_check_commit(old_rev=%s, new_rev=%s)" % (old_rev, new_rev)) # We allow users to explicitly disable pre-commit checks for # specific commits via the use of a special keyword placed anywhere # in the revision log. If found, then return immediately. raw_revlog = git.log("-1", new_rev, pretty="format:%B", _decode=True) if "no-precommit-check" in raw_revlog: debug("pre-commit checks explicity disabled for commit %s" % new_rev) return changes = diff_tree("-r", old_rev, new_rev) files_to_check = [] for item in changes: (old_mode, new_mode, old_sha1, new_sha1, status, filename) = item debug( "diff-tree entry: %s %s %s %s %s %s" % (old_mode, new_mode, old_sha1, new_sha1, status, filename), level=5, ) if status in ("D"): debug("deleted file ignored: %s" % filename, level=2) elif new_mode == "160000": debug("subproject entry ignored: %s" % filename, level=2) else: # Note: We treat a file rename as the equivalent of the old # file being deleted and the new file being added. This means # that we should run the pre-commit checks if applicable. # This is why we did not tell the `git diff-tree' command # above to detect renames, and why we do not have a special # branch for status values starting with `R'. files_to_check.append(filename) no_style_check_map = git_attribute(new_rev, files_to_check, "no-precommit-check") def needs_style_check_p(filename): """Return True if the file should be style-checked, False otherwise. In addition to returning True/False, it generates a debug log when the file does have a no-precommit-check attribute. """ if no_style_check_map[filename] == "set": debug("no-precommit-check: %s commit_rev=%s" % (filename, new_rev)) return False else: return True files_to_check = tuple(filter(needs_style_check_p, files_to_check)) if not files_to_check: debug("style_check_commit: no files to style-check") return style_check_files(files_to_check, new_rev, project_name)