def check(self, branch, old_sha, new_sha): logging.debug("Run: branch=%s, old_sha=%s, new_sha=%s", branch, old_sha, new_sha) logging.debug("params=%s", self.params) permit = True # Do not run the hook if the branch is being deleted if new_sha == '0' * 40: logging.debug("Deleting the branch, skip the hook") return True, [] # Before the hook is run git has already created # a new_sha commit object log = hookutil.parse_git_log(self.repo_dir, branch, old_sha, new_sha, this_branch_only=False) messages = [] for commit in log: modfiles = hookutil.parse_git_show(self.repo_dir, commit['commit'], ['.py']) def has_mixed_indent(file_contents): ''' Check if file lines start with tabs and spaces file_contents = open(file).read() ''' has_tab = False has_space = False for line in file_contents.split('\n'): if line.startswith('\t'): has_tab = True elif line.startswith(' '): has_space = True if has_tab and has_space: return True return False # Get the files from the repo and check indentation. for modfile in modfiles: # Skip deleted files if modfile['status'] == 'D': logging.debug("Deleted '%s', skip", modfile['path']) continue cmd = ['git', 'show', modfile['new_blob']] _, file_contents, _ = hookutil.run(cmd, self.repo_dir) permit_file = not has_mixed_indent(file_contents) logging.debug("modfile=%s, permit_file=%s", modfile['path'], permit_file) if not permit_file: messages.append({'at': commit['commit'], 'text': "Error: file '%s' has mixed indentation" % modfile['path']}) permit = permit and permit_file logging.debug("Permit: %s", permit) return permit, messages
def check(self, branch, old_sha, new_sha): logging.debug("Run: branch=%s, old_sha=%s, new_sha=%s", branch, old_sha, new_sha) logging.debug("params=%s", self.params) if not self.settings: return True permit = True # Do not run the hook if the branch is being deleted if new_sha == '0' * 40: logging.debug("Deleting the branch, skip the hook") return True, [] # Before the hook is run git has already created # a new_sha commit object log = hookutil.parse_git_log(self.repo_dir, branch, old_sha, new_sha, this_branch_only=False) messages = [] for commit in log: modfiles = hookutil.parse_git_show(self.repo_dir, commit['commit']) def has_good_copyright(file_contents, copyrights): ''' Check if file contains good copyright string ''' for (start, full) in copyrights: if re.search(start, file_contents): if not re.search(full, file_contents): return False return True for modfile in modfiles: # Skip deleted files if modfile['status'] == 'D': logging.debug("Deleted %s, skip", modfile['path']) continue cmd = ['git', 'show', modfile['new_blob']] _, file_contents, _ = hookutil.run(cmd, self.repo_dir) permit_file = has_good_copyright(file_contents, self.settings) logging.debug("modfile='%s', permit_file='%s'", modfile['path'], permit_file) if not permit_file: messages.append({'at': commit['commit'], 'text': "Error: Bad copyright in file '%s'!" % modfile['path']}) permit = permit and permit_file if not permit: text = 'Please update the copyright strings to match one of the following:\n\n\t- ' + '\n\t- '.join([full for (start, full) in self.settings]) messages.append({'at': new_sha, 'text': text + '\n'}) logging.debug("Permit: %s", permit) return permit, messages
def check(self, branch, old_sha, new_sha): logging.debug("Run: branch=%s, old_sha=%s, new_sha=%s", branch, old_sha, new_sha) logging.debug("params=%s", self.params) permit = True # Do not run the hook if the branch is being deleted if new_sha == '0' * 40: logging.debug("Deleting the branch, skip the hook") return True, [] # Before the hook is run git has already created # a new_sha commit object log = hookutil.parse_git_log(self.repo_dir, branch, old_sha, new_sha, this_branch_only=False) messages = [] for commit in log: modfiles = hookutil.parse_git_show(self.repo_dir, commit['commit']) def has_mixed_le(file_contents): ''' Check if file contains both lf and crlf file_contents = open(file).read() ''' if ('\r\n' in file_contents and '\n' in file_contents.replace('\r\n', '')): return True return False for modfile in modfiles: # Skip deleted files if modfile['status'] == 'D': logging.debug("Deleted %s, skip", modfile['path']) continue binary_attr = hookutil.get_attr( self.repo_dir, new_sha, modfile['path'], 'binary') if binary_attr != 'set': cmd = ['git', 'show', modfile['new_blob']] _, file_contents, _ = hookutil.run(cmd, self.repo_dir) permit_file = not has_mixed_le(file_contents) logging.debug("modfile='%s', permit_file='%s'", modfile['path'], permit_file) if not permit_file: messages.append({'at': commit['commit'], 'text': "Error: file '%s' has mixed line endings (CRLF/LF)" % modfile['path']}) permit = permit and permit_file logging.debug("Permit: %s", permit) return permit, messages
def is_ff_push(self, old_sha, new_sha): ''' Check if new_sha is a fast-forward reference. ''' cmd = ['git', 'rev-list'] if old_sha == '0' * 40: cmd += [new_sha] else: cmd += ["%s..%s" % (new_sha, old_sha)] _, refs, _ = hookutil.run(cmd, self.repo_dir) # It is a non-fast-forward push if refs: return False return True
def check(self, branch, old_sha, new_sha): logging.debug("Run: branch=%s, old_sha=%s, new_sha=%s", branch, old_sha, new_sha) logging.debug("params=%s", self.params) permit = True log = hookutil.parse_git_log(self.repo_dir, branch, old_sha, new_sha, this_branch_only=False) for commit in log: print "Checking commit %s ..." % commit['commit'] # Filter python scripts from the files modified in new_sha modfiles = hookutil.parse_git_show(self.repo_dir, commit['commit'], ['.py']) # Exit early if there are no modified python scripts in the changeset if not modfiles: return permit # Set up a working directory for pycodestyle and fill it with the blobs to be checked pycheck_workdir = tempfile.mkdtemp(suffix='pycheck') for modfile in modfiles: # Skip deleted files if modfile['status'] == 'D': logging.debug("Deleted '%s', skip", modfile['path']) continue cmd = ['git', 'show', modfile['new_blob']] _, file_contents, _ = hookutil.run(cmd, self.repo_dir) file_path = os.path.join(pycheck_workdir, modfile['path']) assert(not os.path.exists(file_path)) file_dir = os.path.join(pycheck_workdir, os.path.dirname(modfile['path'])) if not os.path.exists(file_dir): os.makedirs(os.path.join(pycheck_workdir, os.path.dirname(modfile['path']))) with open(file_path, 'w') as fd: fd.write(file_contents) # Copy setup.cfg to the working directory shutil.copy(os.path.join(os.path.dirname(__file__), 'setup.cfg'), pycheck_workdir) # Get the commit's diff; pycodestyle needs it to report only against modified lines cmd = ['git', 'show', commit['commit']] _, diff, _ = hookutil.run(cmd, self.repo_dir) local_dir = os.curdir os.chdir(pycheck_workdir) # Run pycodestyle in the working directory we have just prepared. selected_lines = pycodestyle.parse_udiff(diff, patterns=['*.py'], parent='') pep8style = pycodestyle.StyleGuide( { 'diff' : True, 'paths' : sorted(selected_lines), 'selected_lines': selected_lines, 'reporter' : pycodestyle.DiffReport } ) report = pep8style.check_files() os.chdir(local_dir) if report.total_errors: permit = False # Clean up shutil.rmtree(pycheck_workdir) logging.debug("Permit: %s" % permit) return permit, []
def check(self, branch, old_sha, new_sha): logging.debug("Run: branch=%s, old_sha=%s, new_sha=%s", branch, old_sha, new_sha) logging.debug("params=%s", self.params) permit = True # Do not run the hook if the branch is being deleted if new_sha == '0' * 40: logging.debug("Deleting the branch, skip the hook") return True, [] # Before the hook is run git has already created # a new_sha commit object log = hookutil.parse_git_log(self.repo_dir, branch, old_sha, new_sha, this_branch_only=False) messages = [] for commit in log: modfiles = hookutil.parse_git_show(self.repo_dir, commit['commit']) def has_mixed_le(file_contents): ''' Check if file contains both lf and crlf file_contents = open(file).read() ''' if ('\r\n' in file_contents and '\n' in file_contents.replace('\r\n', '')): return True return False for modfile in modfiles: # Skip deleted files if modfile['status'] == 'D': logging.debug("Deleted %s, skip", modfile['path']) continue binary_attr = hookutil.get_attr(self.repo_dir, new_sha, modfile['path'], 'binary') if binary_attr != 'set': cmd = ['git', 'show', modfile['new_blob']] _, file_contents, _ = hookutil.run(cmd, self.repo_dir) permit_file = not has_mixed_le(file_contents) logging.debug("modfile='%s', permit_file='%s'", modfile['path'], permit_file) if not permit_file: messages.append({ 'at': commit['commit'], 'text': "Error: file '%s' has mixed line endings (CRLF/LF)" % modfile['path'] }) permit = permit and permit_file logging.debug("Permit: %s", permit) return permit, messages
def check(self, branch, old_sha, new_sha): logging.debug("Run: branch=%s, old_sha=%s, new_sha=%s", branch, old_sha, new_sha) logging.debug("params=%s", self.params) if not self.settings: return True, [] permit = True # Do not run the hook if the branch is being deleted if new_sha == '0' * 40: logging.debug("Deleting the branch, skip the hook") return True, [] # Before the hook is run git has already created # a new_sha commit object log = hookutil.parse_git_log(self.repo_dir, branch, old_sha, new_sha, this_branch_only=False) messages = [] for commit in log: modfiles = hookutil.parse_git_show(self.repo_dir, commit['commit']) def has_good_copyright(file_contents, copyrights): ''' Check if file contains good copyright string ''' for (start, full) in copyrights: if re.search(start, file_contents): if not re.search(full, file_contents): return False return True for modfile in modfiles: # Skip deleted files if modfile['status'] == 'D': logging.debug("Deleted %s, skip", modfile['path']) continue cmd = ['git', 'show', modfile['new_blob']] _, file_contents, _ = hookutil.run(cmd, self.repo_dir) permit_file = has_good_copyright(file_contents, self.settings) logging.debug("modfile='%s', permit_file='%s'", modfile['path'], permit_file) if not permit_file: messages.append({ 'at': commit['commit'], 'text': "Error: Bad copyright in file '%s'!" % modfile['path'] }) permit = permit and permit_file if not permit: text = 'Please update the copyright strings to match one of the following:\n\n\t- ' + '\n\t- '.join( [full for (start, full) in self.settings]) messages.append({'at': new_sha, 'text': text + '\n'}) logging.debug("Permit: %s", permit) return permit, messages
def check(self, branch, old_sha, new_sha): logging.debug("Run: branch=%s, old_sha=%s, new_sha=%s", branch, old_sha, new_sha) logging.debug("params=%s", self.params) # Do not run the hook if the branch is being deleted if new_sha == '0' * 40: logging.debug("Deleting the branch, skip the hook") return True, [] def print_commit(commit, formatter='\t%s'): ''' Print a commit using a formatter for each new line. The default formatter is a signle tabulation. ''' return '\n'.join([ formatter % i for i in [ "commit %s" % commit['commit'], "Merge: %s %s" % (parentCommits[0][:7], parentCommits[1][:7]), "Author: %s <%s>" % (commit['author_name'], commit['author_email']), "Date: %s" % commit['date'] ] + [""] + # Add a newline wrap(commit['message'], width=120) + [""] ]) permit = True log = hookutil.parse_git_log(self.repo_dir, branch, old_sha, new_sha, this_branch_only=False) messages = [] for commit in log: # Parse commit parents cmd = ['git', 'rev-list', '--parents', '-n', '1', commit['commit']] _, out, _ = hookutil.run(cmd, self.repo_dir) parentCommits = out.strip().split(' ')[1:] # Skip commit if it is not a merge commit if len(parentCommits) < 2: continue logging.debug("Found merge %s, parents: %s %s", commit['commit'][:7], parentCommits[0][:7], parentCommits[1][:7]) # Find branches that contain parent commits parentBranches = [] for parentCommit in parentCommits: cmd = ['git', 'branch', '--contains', parentCommit] ret, out, err = hookutil.run(cmd, self.repo_dir) # FIXME Skip if parent commit was not found on any branch if not out and not err and not ret: # These are stdout, stderr and return code that Popen.wait produces for 'git branch --continue' # with shell=False. When a commit does not exist in the remote, the command should return 129 # and an error meesage in stderr. With shell=True, it does not work as extected either (returns 1 # and git usage in stdout, similar to running just 'git' instead of 'git branch --continue ...'). continue if not out: parentBranches.append(branch.replace('refs/heads/', '')) else: parentBranches += [ br.replace("* ", "") for br in out.strip().split('\n') ] if len(set(parentBranches)) != 1: continue mergedBranch = parentBranches[0] logging.debug("All parents are on branch '%s'", mergedBranch) # First parent must be on the destination branch firstParent = parentCommits[0] cmd = ['git', 'branch', '--contains', firstParent] _, out, _ = hookutil.run(cmd, self.repo_dir) if not out.startswith('* '): permit = False text = '\n'.join([ "Merging a remote branch onto a local branch is prohibited when updating the remote with that local branch.", "", print_commit(commit) ] + wrap( "You must remove this merge by updating your local branch properly. Please rebase on top of the remote branch:", width=120 ) + ["", "\tgit pull --rebase origin %s" % mergedBranch, ""]) messages += [{'at': commit['commit'], 'text': text}] logging.info("%s is same-branch merge, permit = %s", commit['commit'][:7], permit) logging.debug("Permit: %s", permit) return permit, messages