def dodiff(tmproot): assert not (hascopies and len(MAR) > 1), \ 'dodiff cannot handle copies when diffing dirs' sa = [mod_a, add_a, rem_a] sb = [mod_b, add_b, rem_b] ctxs = [ctx1a, ctx1b, ctx2] # If more than one file, diff on working dir copy. copyworkingdir = len(MAR) > 1 dirs, labels, fns_and_mtimes = snapshotset(repo, ctxs, sa, sb, cpy, tmproot, copyworkingdir) dir1a, dir1b, dir2 = dirs label1a, label1b, label2 = labels fns_and_mtime = fns_and_mtimes[2] if len(MAR) > 1 and label2 == '': label2 = 'working files' def getfile(fname, dir, label): file = os.path.join(tmproot, dir, fname) if os.path.isfile(file): return fname+label, file nullfile = os.path.join(tmproot, 'empty') fp = open(nullfile, 'w') fp.close() return _nonexistant+label, nullfile # If only one change, diff the files instead of the directories # Handle bogus modifies correctly by checking if the files exist if len(MAR) == 1: file2 = util.localpath(MAR.pop()) if file2 in cto: file1 = util.localpath(cpy[file2]) else: file1 = file2 label1a, dir1a = getfile(file1, dir1a, label1a) if do3way: label1b, dir1b = getfile(file1, dir1b, label1b) label2, dir2 = getfile(file2, dir2, label2) if do3way: label1a += '[local]' label1b += '[other]' label2 += '[merged]' replace = dict(parent=dir1a, parent1=dir1a, parent2=dir1b, plabel1=label1a, plabel2=label1b, phash1=str(ctx1a), phash2=str(ctx1b), repo=hglib.get_reponame(repo), clabel=label2, child=dir2, chash=str(ctx2)) launchtool(diffcmd, args, replace, True) # detect if changes were made to mirrored working files for copy_fn, working_fn, mtime in fns_and_mtime: if os.path.getmtime(copy_fn) != mtime: ui.debug('file changed while diffing. ' 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)) util.copyfile(copy_fn, working_fn)
def master_hook(ui, repo, **kwargs): ui.debug("running tamarin master_hook\n") ui.debug("kwargs: %s\n" % kwargs) # The mercurial hook script expects the equivalent of an exit code back from # this call: # False = 0 = No Error : allow push # True = 1 = Error : abort push error = False error = security_check(ui, repo, **kwargs) or error return error
def preoutgoing_hook(ui, repo, **kwargs): ui.debug('running tamarin preoutgoing_hook\n') ui.debug('kwargs: %s\n' % kwargs) operation = kwargs['source'] # Like master_hook, return code False implies No Error, allow push. error = False error = error or heuristic_log_check(ui, repo, operation, **kwargs) return error
def master_hook(ui, repo, **kwargs): ui.debug('running tamarin master_hook\n') ui.debug('kwargs: %s\n' % kwargs) # The mercurial hook script expects the equivalent of an exit code back from # this call: # False = 0 = No Error : allow push # True = 1 = Error : abort push error = False error = security_check(ui, repo, **kwargs) or error return error
def reposetup(ui, repo): # TODO: decide use of config section for this extension global _ui _ui = ui if not os.path.supports_unicode_filenames: ui.warn(_("[win32mbcs] cannot activate on this platform.\n")) return # install features of this extension install() ui.debug(_("[win32mbcs] activeted with encoding: %s\n") % util._encoding)
def shortreponame(ui): name = ui.config('web', 'name') if not name: return src = ui.configsource('web', 'name') # path:line if '/.hg/hgrc:' not in util.pconvert(src): # global web.name will set the same name to all repositories ui.debug('ignoring global web.name defined at %s\n' % src) return return name
def diff_check(ui, repo, **kwargs): ui.debug('running diff_check\n') # get all the change contexts for this commit # kwargs['node'] returns the first changecontext nodeid changecontexts = [repo[i] for i in range(repo[kwargs['node']].rev(), len(repo))] # check for tabs def tabCheck(line): tab = line.find('\t') if tab >= 0: # find returns -1 if not found return True, tab return False, tab def windowsLineendingsCheck(line): if line.endswith('\r'): return True, len(line)-1 return False, 0 def trailingWhitespaceCheck(line): if len(line.strip()) > 1: # skip empty lines (will have a +) see bug 600536 m = re.match(r'\+.*?(\s+$)', line) if m: return True, m.start(1) return False, 0 def securityCheck(line): loc = line.find('MARK_SECURITY_CHANGE') if loc != -1: # found security change ifdef return True, loc loc = line.find('SECURITYFIX_') if loc != -1: # found security change ifdef return True, loc return False, 0 # check for tabs - exit if user chooses to abort if checkChangeCtxDiff(ui, repo, changecontexts, tabCheck, 'Tab', ('.cpp', '.c', '.h', '.as', '.abs', '.py')): return True if checkChangeCtxDiff(ui, repo, changecontexts, windowsLineendingsCheck, 'Windows line ending', ('.cpp', '.c', '.h', '.as', '.abs', '.py')): return True if checkChangeCtxDiff(ui, repo, changecontexts, trailingWhitespaceCheck, 'Trailing Whitespace', ('.cpp', '.c', '.h', '.as', '.abs', '.py')): return True if checkChangeCtxDiff(ui, repo, changecontexts, securityCheck, 'Security Check', ('.cpp', '.c', '.h', '.as', '.abs', '.py')): return True return False
def security_check(ui, repo, **kwargs): ui.debug('running security_check\n') error = False ui.pushbuffer() _quiet( ui, lambda: commands.log(ui, repo, rev=['%s:tip' % kwargs['node']], template='{node}\n', date=None, user=None, logfile=None)) nodes = ui.popbuffer().split('\n') # reenable this code if we need to blacklist a node for node in nodes: if node.startswith('8555e8551203') or node.startswith('e09bb3ece6c7'): ui.warn('blacklisted changeid found: node %s is blacklisted\n' % node) error = True # fail the push # Look for blacklisted bugs blacklist = [ # Serrano 580489, 604354, # Wasabi 507624, # Cyril 570049, 628834, ] bugs = re.compile('(%s)' % '|'.join([str(bug) for bug in blacklist])) ui.pushbuffer() _quiet( ui, lambda: commands.log(ui, repo, rev=['%s:tip' % kwargs['node']], template='{desc}', date=None, user=None, logfile=None)) descs = ui.popbuffer() searchDescs = bugs.search(descs) if searchDescs: ui.warn('blacklisted bug found: %s\n' % searchDescs.groups()[0]) error = True return error
def expull(orig, repo, remote, *args, **kwargs): res = orig(repo, remote, *args, **kwargs) lock = repo.lock() try: try: path = repo._activepath(remote) if path: repo.saveremotebranches(path, remote.branchmap()) except Exception, e: ui.debug('remote branches for path %s not saved: %s\n' % (path, e)) finally: lock.release() return res
def push(self, remote, *args, **kwargs): res = opush(remote, *args, **kwargs) lock = self.lock() try: try: path = self._activepath(remote) if path: self.saveremotebranches(path, remote.branchmap()) except Exception, e: ui.debug("remote branches for path %s not saved: %s\n" % (path, e)) finally: lock.release() return res
def expush(orig, repo, remote, *args, **kwargs): res = orig(repo, remote, *args, **kwargs) lock = repo.lock() try: try: path = repo._activepath(remote) if path: repo.saveremotebranches(path, remote.branchmap()) except Exception, e: ui.debug('remote branches for path %s not saved: %s\n' % (path, e)) finally: lock.release() return res
def push(self, remote, *args, **kwargs): res = opush(remote, *args, **kwargs) lock = self.lock() try: try: path = self._activepath(remote) if path: self.saveremotebranches(path, remote.branchmap()) except Exception, e: ui.debug('remote branches for path %s not saved: %s\n' % (path, e)) finally: lock.release() return res
def security_check(ui, repo, **kwargs): ui.debug("running security_check\n") error = False ui.pushbuffer() _quiet( ui, lambda: commands.log( ui, repo, rev=["%s:tip" % kwargs["node"]], template="{node}\n", date=None, user=None, logfile=None ), ) nodes = ui.popbuffer().split("\n") # reenable this code if we need to blacklist a node for node in nodes: if node.startswith("8555e8551203") or node.startswith("e09bb3ece6c7"): ui.warn("blacklisted changeid found: node %s is blacklisted\n" % node) error = True # fail the push # Look for blacklisted bugs blacklist = [ # Serrano 580489, 604354, # Wasabi 507624, # Cyril 570049, 628834, ] bugs = re.compile("(%s)" % "|".join([str(bug) for bug in blacklist])) ui.pushbuffer() _quiet( ui, lambda: commands.log( ui, repo, rev=["%s:tip" % kwargs["node"]], template="{desc}", date=None, user=None, logfile=None ), ) descs = ui.popbuffer() searchDescs = bugs.search(descs) if searchDescs: ui.warn("blacklisted bug found: %s\n" % searchDescs.groups()[0]) error = True return error
def retrieve_pass_from_server(ui, uri, path, proposed_user): port = int(ui.config("hg4ideapass", "port", None, True)) if port is None: raise util.Abort("No port was specified") client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ui.debug("connecting ...") client.connect(("127.0.0.1", port)) ui.debug("connected, sending data ...") send(client, "getpass") send(client, uri) send(client, path) send(client, proposed_user) user = receiveWithMessage(client, "http authorization required") password = receiveWithMessage(client, "http authorization required") return user, password
def retrieve_pass_from_server(ui, uri, path, proposed_user): port = int(ui.config(b'hg4ideapass', b'port', None, True)) if port is None: raise error.Abort("No port was specified") client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ui.debug("connecting ...") client.connect(('127.0.0.1', port)) ui.debug("connected, sending data ...") send(client, "getpass") send(client, uri) send(client, path) send(client, proposed_user) user = receiveWithMessage(client, b"http authorization required") password = receiveWithMessage(client, b"http authorization required") return user, password
def find_branch_parent(ui, ctx): '''Find the parent revision of the 'ctx' branch.''' branchname = ctx.branch() getparent = lambda ctx: ctx.parents()[0] currctx = ctx while getparent(currctx) and currctx.branch() == branchname: currctx = getparent(currctx) ui.debug('currctx rev: %s; branch: %s\n' % (currctx.rev(), currctx.branch())) # return the root of the repository if the first # revision is on the branch if not getparent(currctx) and currctx.branch() == branchname: return currctx._repo['000000000000'] return currctx
def create_review_data(ui, repo, c, parent, rparent): 'Returns a tuple of the diff and parent diff for the review.' diff = getdiff(ui, repo, c, parent) ui.debug('\n=== Diff from parent to rev ===\n') ui.debug(diff + '\n') if rparent != None and parent != rparent: parentdiff = getdiff(ui, repo, parent, rparent) ui.debug('\n=== Diff from rparent to parent ===\n') ui.debug(parentdiff + '\n') else: parentdiff = '' return diff, parentdiff
def master_hook(ui, repo, **kwargs): ui.debug('running tamarin master_hook\n') ui.debug('kwargs: %s\n' % kwargs) # The mercurial hook script expects the equivalent of an exit code back from # this call: # False = 0 = No Error : allow push # True = 1 = Error : abort push error = False error = error or diff_check(ui, repo, **kwargs) if error: # Save the commit message so it can be reused by user desc = repo[repo[kwargs['node']].rev()].description() ui.debug('Description: %s\n' % desc) try: f = open('%s/.hg/commit.save' % repo.root, 'w') f.write(desc) f.close() ui.warn( 'Commit message saved to .hg/commit.save\nSaved message can be recommitted using -l .hg/commit.save\n' ) except IOError: ui.warn('Error writing .hg/commit.save file') return error
def check_user_for_valid_email(ui, changeset, operation): user = changeset.user() ui.debug('\ncheck_user_for_valid_email: %s' % user) has_email = has_email_in_brackets(user) if not has_email: ui.warn('\nUser missing email address for changeset %s: \n %s\n' % (changeset, user)) response = prompt_yesno(ui, operation) if response == 0: ui.warn('Aborting %s due to user missing email.\n' % operation) return True; else: has_domain = has_email_with_domain(user) if not has_domain: ui.warn('\nUser email missing domain for changeset %s: \n %s\n' % (changeset, user)) response = prompt_yesno(ui, operation) if response == 0: ui.warn('Aborting %s due to email without domain.\n' % operation) return True; return False;
def check_user_for_valid_email(ui, changeset, operation): user = changeset.user() ui.debug('\ncheck_user_for_valid_email: %s' % user) has_email = has_email_in_brackets(user) if not has_email: ui.warn('\nUser missing email address for changeset %s: \n %s\n' % (changeset, user)) response = prompt_yesno(ui, operation) if response == 0: ui.warn('Aborting %s due to user missing email.\n' % operation) return True else: has_domain = has_email_with_domain(user) if not has_domain: ui.warn('\nUser email missing domain for changeset %s: \n %s\n' % (changeset, user)) response = prompt_yesno(ui, operation) if response == 0: ui.warn('Aborting %s due to email without domain.\n' % operation) return True return False
def checkChangeCtxDiff(ui, repo, changecontexts, testFunc, testDesc, fileEndings): '''Loop through each diff for each change and run the testFunc against each line''' ui.debug('Checking %s\n' % testDesc) for ctx in changecontexts: # Get the diff for each change and file for file in [f for f in ctx.files() if f.endswith(fileEndings)]: ui.debug('checking change: %s, file: %s\n' % (short(ctx.node()), file)) fmatch = matchfiles(repo, [file]) # diff from this nodes parent to current node diff = ''.join( patch.diff(repo, ctx.parents()[0].node(), ctx.node(), fmatch)).split('\n') for i in range(3, len(diff)): # start checking after diff header line = diff[i] if line.startswith('@@'): diffLocation = line # only check new lines added/modified in the file if line.startswith('+'): ui.debug('\nchecking line for %s: %s\n\n' % (testDesc, line)) testResult, errorLocation = testFunc(line) if testResult: ui.warn( '\n%s(s) found in %s for rev %s (change %s):\n' % (testDesc, file, ctx.rev(), short(ctx.node()))) ui.warn('%s\n' % diffLocation) ui.warn('%s\n' % line) ui.warn( '%s^\n' % (' ' * errorLocation, )) # show a pointer to error try: response = ui.promptchoice( '(n)o, (y)es, (a)llow %ss for current file\n' % testDesc + 'Are you sure you want to commit this change? [n]: ', (('&No'), ('&Yes'), ('&Allow')), 0) except AttributeError: ui.warn( 'This commit hook requires that you have mercurial 1.4+ installed. Please upgrade your hg installation.' ) response = 0 if response == 1: # next occurance in file continue elif response == 2: # next file break else: ui.warn('Aborting commit due to %s.\n' % testDesc) # error = True return True return False
def checkChangeCtxDiff(ui, repo, changecontexts, testFunc, testDesc, fileEndings): '''Loop through each diff for each change and run the testFunc against each line''' ui.debug('Checking %s\n' % testDesc) for ctx in changecontexts: # Get the diff for each change and file for file in [f for f in ctx.files() if f.endswith(fileEndings)]: ui.debug('checking change: %s, file: %s\n' % (short(ctx.node()), file)) fmatch = matchfiles(repo,[file]) # diff from this nodes parent to current node diff = ''.join(patch.diff(repo, ctx.parents()[0].node(), ctx.node(), fmatch)).split('\n') for i in range(3, len(diff)): # start checking after diff header line = diff[i] if line.startswith('@@'): diffLocation = line # only check new lines added/modified in the file if line.startswith('+'): ui.debug('\nchecking line for %s: %s\n\n' % (testDesc, line)) testResult, errorLocation = testFunc(line) if testResult: ui.warn('\n%s(s) found in %s for rev %s (change %s):\n' % (testDesc, file, ctx.rev(), short(ctx.node()))) ui.warn('%s\n' % diffLocation) ui.warn('%s\n' % line) ui.warn('%s^\n' % (' '*errorLocation,)) # show a pointer to error try: response = ui.promptchoice('(n)o, (y)es, (a)llow %ss for current file\n' % testDesc + 'Are you sure you want to commit this change? [n]: ' , (('&No'), ('&Yes'), ('&Allow')), 0) except AttributeError: ui.warn('This commit hook requires that you have mercurial 1.4+ installed. Please upgrade your hg installation.') response = 0 if response == 1: # next occurance in file continue elif response == 2: # next file break else: ui.warn('Aborting commit due to %s.\n' % testDesc) # error = True return True return False
def master_hook(ui, repo, **kwargs): ui.debug('running tamarin master_hook\n') ui.debug('kwargs: %s\n' % kwargs) # The mercurial hook script expects the equivalent of an exit code back from # this call: # False = 0 = No Error : allow push # True = 1 = Error : abort push error = False error = error or diff_check(ui, repo, **kwargs) if error: # Save the commit message so it can be reused by user desc = repo[repo[kwargs['node']].rev()].description() ui.debug('Description: %s\n' % desc) try: f = open('%s/.hg/commit.save' % repo.root, 'w') f.write(desc) f.close() ui.warn('Commit message saved to .hg/commit.save\nSaved message can be recommitted using -l .hg/commit.save\n') except IOError: ui.warn('Error writing .hg/commit.save file') return error
def hook(ui, repo, **kwargs): # type: (ui, repo, **Text) -> None """ Invoked by configuring a [hook] entry in .hg/hgrc. """ hooktype = kwargs["hooktype"] node = kwargs["node"] ui.debug("Zulip: received {hooktype} event\n".format(hooktype=hooktype)) if hooktype != "changegroup": ui.warn("Zulip: {hooktype} not supported\n".format(hooktype=hooktype)) sys.exit(1) ctx = repo.changectx(node) branch = ctx.branch() # If `branches` isn't specified, notify on all branches. branch_whitelist = get_config(ui, "branches") branch_blacklist = get_config(ui, "ignore_branches") if branch_whitelist: # Only send notifications on branches we are watching. watched_branches = [ b.lower().strip() for b in branch_whitelist.split(",") ] if branch.lower() not in watched_branches: ui.debug( "Zulip: ignoring event for {branch}\n".format(branch=branch)) sys.exit(0) if branch_blacklist: # Don't send notifications for branches we've ignored. ignored_branches = [ b.lower().strip() for b in branch_blacklist.split(",") ] if branch.lower() in ignored_branches: ui.debug( "Zulip: ignoring event for {branch}\n".format(branch=branch)) sys.exit(0) # The first and final commits in the changeset. base = repo[node].rev() tip = len(repo) email = get_config(ui, "email") api_key = get_config(ui, "api_key") site = get_config(ui, "site") if not (email and api_key): ui.warn("Zulip: missing email or api_key configurations\n") ui.warn("in the [zulip] section of your .hg/hgrc.\n") sys.exit(1) stream = get_config(ui, "stream") # Give a default stream if one isn't provided. if not stream: stream = "commits" web_url = get_config(ui, "web_url") user = ctx.user() content = format_summary_line(web_url, user, base, tip, branch, node) content += format_commit_lines(web_url, repo, base, tip) subject = branch ui.debug("Sending to Zulip:\n") ui.debug(content + "\n") send_zulip(email, api_key, site, stream, subject, content)
def postreview(ui, repo, rev='.', **opts): '''post a changeset to a Review Board server This command creates a new review request on a Review Board server, or updates an existing review request, based on a changeset in the repository. If no revision number is specified the parent revision of the working directory is used. By default, the diff uploaded to the server is based on the parent of the revision to be reviewed. A different parent may be specified using the --parent or --longdiff options. --parent r specifies the revision to use on the left side while --longdiff looks at the upstream repository specified in .hg/hgrc to find a common ancestor to use on the left side. --parent may need one of the options below if the Review Board server can't see the parent. If the parent revision is not available to the Review Board server (e.g. it exists in your local repository but not in the one that Review Board has access to) you must tell postreview how to determine the base revision to use for a parent diff. The --outgoing, --outgoingrepo or --master options may be used for this purpose. The --outgoing option is the simplest of these; it assumes that the upstream repository specified in .hg/hgrc is the same as the one known to Review Board. The other two options offer more control if this is not the case. In these cases two diffs are uploaded to Review Board: the first is the difference between Reviewboard's view of the repo and your parent revision(left side), the second is the difference between your parent revision and your review revision(right side). Only the second diff is under review. If you wish to review all the changes local to your repo use the --longdiff option above. The --outgoing option recognizes the path entries 'reviewboard', 'default-push' and 'default' in this order of precedence. 'reviewboard' may be used if the repository accessible to Review Board is not the upstream repository. The --git option causes postreview to generate diffs in Git extended format, which includes information about file renames and copies. ReviewBoard 1.6 beta 2 or later is required in order to use this feature. The --submit_as option allows to submit the review request as another user. This requires that the actual logged in user is either a superuser or has the "reviews.can_submit_as_another_user" permission. The reviewboard extension may be configured by adding a [reviewboard] section to your .hgrc or mercurial.ini file, or to the .hg/hgrc file of an individual repository. The following options are available:: [reviewboard] # REQUIRED server = <server_url> # The URL of your ReviewBoard server # OPTIONAL http_proxy = <proxy_url> # HTTP proxy to use for the connection user = <rb_username> # Username to use for ReviewBoard # connections password = <rb_password> # Password to use for ReviewBoard # connections repoid = <repoid> # ReviewBoard repository ID (normally only # useful in a repository-specific hgrc) target_groups = <groups> # Default groups for new review requests # (comma-separated list) target_people = <users> # Default users for new review requests # (comma-separated list) explicit_publish_update = <bool> # If True, updates posted using the -e # option will not be published immediately # unless the -p option is also used launch_webbrowser = <bool> # If True, new or updated requests will # always be shown in a web browser after # posting. ''' server = opts.get('server') if not server: server = ui.config('reviewboard', 'server') if not server: raise util.Abort( _('please specify a reviewboard server in your .hgrc file')) '''We are going to fetch the setting string from hg prefs, there we can set our own proxy, or specify 'none' to pass an empty dictionary to urllib2 which overides the default autodetection when we want to force no proxy''' http_proxy = ui.config('reviewboard', 'http_proxy') if http_proxy: if http_proxy == 'none': proxy = {} else: proxy = {'http': http_proxy} else: proxy = None def getdiff(ui, repo, r, parent, opts): '''return diff for the specified revision''' output = "" if opts.get('git') or ui.configbool('diff', 'git'): # Git diffs don't include the revision numbers with each file, so # we have to put them in the header instead. output += "# Node ID " + node.hex(r.node()) + "\n" output += "# Parent " + node.hex(parent.node()) + "\n" diffopts = patch.diffopts(ui, opts) for chunk in patch.diff(repo, parent.node(), r.node(), opts=diffopts): output += chunk return output parent = opts.get('parent') if parent: parent = repo[parent] else: parent = repo[rev].parents()[0] outgoing = opts.get('outgoing') outgoingrepo = opts.get('outgoingrepo') master = opts.get('master') repo_id_opt = opts.get('repoid') longdiff = opts.get('longdiff') if not repo_id_opt: repo_id_opt = ui.config('reviewboard', 'repoid') if master: rparent = repo[master] elif outgoingrepo: rparent = remoteparent(ui, repo, opts, rev, upstream=outgoingrepo) elif outgoing: rparent = remoteparent(ui, repo, opts, rev) elif longdiff: parent = remoteparent(ui, repo, opts, rev) rparent = None else: rparent = None ui.debug(_('Parent is %s\n' % parent)) ui.debug(_('Remote parent is %s\n' % rparent)) request_id = None if opts.get('existing'): request_id = opts.get('existing') fields = {} c = repo.changectx(rev) changesets_string = get_changesets_string(repo, parent, c) # Don't clobber the summary and description for an existing request # unless specifically asked for if opts.get('update') or not request_id: fields['summary'] = toascii(c.description().splitlines()[0]) fields['description'] = toascii(changesets_string) fields['branch'] = toascii(c.branch()) if opts.get('summary'): fields['summary'] = toascii(opts.get('summary')) if opts.get('description'): fields['description'] = toascii(opts.get('description')) diff = getdiff(ui, repo, c, parent, opts) ui.debug('\n=== Diff from parent to rev ===\n') ui.debug(diff + '\n') if rparent and parent != rparent: parentdiff = getdiff(ui, repo, parent, rparent, opts) ui.debug('\n=== Diff from rparent to parent ===\n') ui.debug(parentdiff + '\n') else: parentdiff = '' if opts.get('update') or not request_id: for field in ('target_groups', 'target_people', 'bugs_closed'): if opts.get(field): value = ','.join(opts.get(field)) else: value = ui.config('reviewboard', field) if value: fields[field] = toascii(value) ui.status('\n%s\n' % changesets_string) ui.status('reviewboard:\t%s\n' % server) ui.status('\n') username = opts.get('username') or ui.config('reviewboard', 'user') if username: ui.status('username: %s\n' % username) password = opts.get('password') or ui.config('reviewboard', 'password') if password: ui.status('password: %s\n' % '**********') try: reviewboard = make_rbclient(server, username, password, proxy=proxy, apiver=opts.get('apiver'), trace=opts.get('apitrace')) except Exception, e: raise util.Abort(_(str(e)))
def postreview(ui, repo, rev='.', **opts): '''post a changeset to a Review Board server This command creates a new review request on a Review Board server, or updates an existing review request, based on a changeset in the repository. If no revision number is specified the parent revision of the working directory is used. By default, the diff uploaded to the server is based on the parent of the revision to be reviewed. A different parent may be specified using the --parent or --longdiff options. --parent r specifies the revision to use on the left side while --longdiff looks at the upstream repository specified in .hg/hgrc to find a common ancestor to use on the left side. --parent may need one of the options below if the Review Board server can't see the parent. If the parent revision is not available to the Review Board server (e.g. it exists in your local repository but not in the one that Review Board has access to) you must tell postreview how to determine the base revision to use for a parent diff. The --outgoing, --outgoingrepo or --master options may be used for this purpose. The --outgoing option is the simplest of these; it assumes that the upstream repository specified in .hg/hgrc is the same as the one known to Review Board. The other two options offer more control if this is not the case. In these cases two diffs are uploaded to Review Board: the first is the difference between Reviewboard's view of the repo and your parent revision(left side), the second is the difference between your parent revision and your review revision(right side). Only the second diff is under review. If you wish to review all the changes local to your repo use the --longdiff option above. ''' server = ui.config('reviewboard', 'server') if not server: raise util.Abort( _('please specify a reviewboard server in your .hgrc file') ) '''We are going to fetch the setting string from hg prefs, there we can set our own proxy, or specify 'none' to pass an empty dictionary to urllib2 which overides the default autodetection when we want to force no proxy''' http_proxy = ui.config('reviewboard', 'http_proxy' ) if http_proxy: if http_proxy == 'none': proxy = {} else: proxy = { 'http':http_proxy } else: proxy=None def getdiff(ui, repo, r, parent): '''return diff for the specified revision''' output = "" for chunk in patch.diff(repo, parent.node(), r.node()): output += chunk return output parent = opts.get('parent') if parent: parent = repo[parent] else: parent = repo[rev].parents()[0] outgoing = opts.get('outgoing') outgoingrepo = opts.get('outgoingrepo') master = opts.get('master') repo_id_opt = opts.get('repoid') longdiff = opts.get('longdiff') if not repo_id_opt: repo_id_opt = ui.config('reviewboard','repoid') if master: rparent = repo[master] elif outgoingrepo: rparent = remoteparent(ui, repo, rev, upstream=outgoingrepo) elif outgoing: rparent = remoteparent(ui, repo, rev) elif longdiff: parent = rparent = remoteparent(ui, repo, rev) rparent = None else: rparent = None ui.debug(_('Parent is %s\n' % parent)) ui.debug(_('Remote parent is %s\n' % rparent)) request_id = None if opts.get('existing'): request_id = opts.get('existing') fields = {} c = repo.changectx(rev) # Don't clobber the summary and description for an existing request # unless specifically asked for if opts.get('update') or not request_id: fields['summary'] = c.description().splitlines()[0] fields['description'] = c.description() diff = getdiff(ui, repo, c, parent) ui.debug('\n=== Diff from parent to rev ===\n') ui.debug(diff + '\n') if rparent and parent != rparent: parentdiff = getdiff(ui, repo, parent, rparent) ui.debug('\n=== Diff from rparent to parent ===\n') ui.debug(parentdiff + '\n') else: parentdiff = '' for field in ('target_groups', 'target_people'): if opts.get(field): value = ','.join(opts.get(field)) else: value = ui.config('reviewboard', field) if value: fields[field] = value reviewboard = ReviewBoard(server,proxy=proxy) ui.status('changeset:\t%s:%s "%s"\n' % (rev, c, c.description()) ) ui.status('reviewboard:\t%s\n' % server) ui.status('\n') username = opts.get('username') or ui.config('reviewboard', 'user') if username: ui.status('username: %s\n' % username) password = opts.get('password') or ui.config('reviewboard', 'password') if password: ui.status('password: %s\n' % '**********') try: reviewboard.login(username, password) except ReviewBoardError, msg: raise util.Abort(_(msg))
def hook(ui, repo, **kwargs): # type: (ui, repo, Optional[Text]) -> None """ Invoked by configuring a [hook] entry in .hg/hgrc. """ hooktype = kwargs["hooktype"] node = kwargs["node"] ui.debug("Zulip: received {hooktype} event\n".format(hooktype=hooktype)) if hooktype != "changegroup": ui.warn("Zulip: {hooktype} not supported\n".format(hooktype=hooktype)) exit(1) ctx = repo.changectx(node) branch = ctx.branch() # If `branches` isn't specified, notify on all branches. branch_whitelist = get_config(ui, "branches") branch_blacklist = get_config(ui, "ignore_branches") if branch_whitelist: # Only send notifications on branches we are watching. watched_branches = [b.lower().strip() for b in branch_whitelist.split(",")] if branch.lower() not in watched_branches: ui.debug("Zulip: ignoring event for {branch}\n".format(branch=branch)) exit(0) if branch_blacklist: # Don't send notifications for branches we've ignored. ignored_branches = [b.lower().strip() for b in branch_blacklist.split(",")] if branch.lower() in ignored_branches: ui.debug("Zulip: ignoring event for {branch}\n".format(branch=branch)) exit(0) # The first and final commits in the changeset. base = repo[node].rev() tip = len(repo) email = get_config(ui, "email") api_key = get_config(ui, "api_key") site = get_config(ui, "site") if not (email and api_key): ui.warn("Zulip: missing email or api_key configurations\n") ui.warn("in the [zulip] section of your .hg/hgrc.\n") exit(1) stream = get_config(ui, "stream") # Give a default stream if one isn't provided. if not stream: stream = "commits" web_url = get_config(ui, "web_url") user = ctx.user() content = format_summary_line(web_url, user, base, tip, branch, node) content += format_commit_lines(web_url, repo, base, tip) subject = branch ui.debug("Sending to Zulip:\n") ui.debug(content + "\n") send_zulip(email, api_key, site, stream, subject, content)
def postreview(ui, repo, rev='.', **opts): '''post a changeset to a Review Board server This command creates a new review request on a Review Board server, or updates an existing review request, based on a changeset in the repository. If no revision number is specified the parent revision of the working directory is used. By default, the diff uploaded to the server is based on the parent of the revision to be reviewed. A different parent may be specified using the --parent or --longdiff options. --parent r specifies the revision to use on the left side while --longdiff looks at the upstream repository specified in .hg/hgrc to find a common ancestor to use on the left side. --parent may need one of the options below if the Review Board server can't see the parent. If the parent revision is not available to the Review Board server (e.g. it exists in your local repository but not in the one that Review Board has access to) you must tell postreview how to determine the base revision to use for a parent diff. The --outgoing, --outgoingrepo or --master options may be used for this purpose. The --outgoing option is the simplest of these; it assumes that the upstream repository specified in .hg/hgrc is the same as the one known to Review Board. The other two options offer more control if this is not the case. In these cases two diffs are uploaded to Review Board: the first is the difference between Reviewboard's view of the repo and your parent revision(left side), the second is the difference between your parent revision and your review revision(right side). Only the second diff is under review. If you wish to review all the changes local to your repo use the --longdiff option above. The --outgoing option recognizes the path entries 'reviewboard', 'default-push' and 'default' in this order of precedence. 'reviewboard' may be used if the repository accessible to Review Board is not the upstream repository. The --git option causes postreview to generate diffs in Git extended format, which includes information about file renames and copies. ReviewBoard 1.6 beta 2 or later is required in order to use this feature. The --submit_as option allows to submit the review request as another user. This requires that the actual logged in user is either a superuser or has the "reviews.can_submit_as_another_user" permission. The reviewboard extension may be configured by adding a [reviewboard] section to your .hgrc or mercurial.ini file, or to the .hg/hgrc file of an individual repository. The following options are available:: [reviewboard] # REQUIRED server = <server_url> # The URL of your ReviewBoard server # OPTIONAL http_proxy = <proxy_url> # HTTP proxy to use for the connection user = <rb_username> # Username to use for ReviewBoard # connections password = <rb_password> # Password to use for ReviewBoard # connections repoid = <repoid> # ReviewBoard repository ID (normally only # useful in a repository-specific hgrc) target_groups = <groups> # Default groups for new review requests # (comma-separated list) target_people = <users> # Default users for new review requests # (comma-separated list) explicit_publish_update = <bool> # If True, updates posted using the -e # option will not be published immediately # unless the -p option is also used launch_webbrowser = <bool> # If True, new or updated requests will # always be shown in a web browser after # posting. ''' server = opts.get('server') if not server: server = ui.config('reviewboard', 'server') if not server: raise util.Abort( _('please specify a reviewboard server in your .hgrc file') ) '''We are going to fetch the setting string from hg prefs, there we can set our own proxy, or specify 'none' to pass an empty dictionary to urllib2 which overides the default autodetection when we want to force no proxy''' http_proxy = ui.config('reviewboard', 'http_proxy' ) if http_proxy: if http_proxy == 'none': proxy = {} else: proxy = { 'http':http_proxy } else: proxy=None def getdiff(ui, repo, r, parent, opts): '''return diff for the specified revision''' output = "" if opts.get('git') or ui.configbool('diff', 'git'): # Git diffs don't include the revision numbers with each file, so # we have to put them in the header instead. output += "# Node ID " + node.hex(r.node()) + "\n" output += "# Parent " + node.hex(parent.node()) + "\n" diffopts = patch.diffopts(ui, opts) for chunk in patch.diff(repo, parent.node(), r.node(), opts=diffopts): output += chunk return output parent = opts.get('parent') if parent: parent = repo[parent] else: parent = repo[rev].parents()[0] outgoing = opts.get('outgoing') outgoingrepo = opts.get('outgoingrepo') master = opts.get('master') repo_id_opt = opts.get('repoid') longdiff = opts.get('longdiff') if not repo_id_opt: repo_id_opt = ui.config('reviewboard','repoid') if master: rparent = repo[master] elif outgoingrepo: rparent = remoteparent(ui, repo, opts, rev, upstream=outgoingrepo) elif outgoing: rparent = remoteparent(ui, repo, opts, rev) elif longdiff: parent = remoteparent(ui, repo, opts, rev) rparent = None else: rparent = None ui.debug(_('Parent is %s\n' % parent)) ui.debug(_('Remote parent is %s\n' % rparent)) request_id = None if opts.get('existing'): request_id = opts.get('existing') fields = {} c = repo.changectx(rev) changesets_string = get_changesets_string(repo, parent, c) # Don't clobber the summary and description for an existing request # unless specifically asked for if opts.get('update') or not request_id: fields['summary'] = toascii(c.description().splitlines()[0]) fields['description'] = toascii(changesets_string) fields['branch'] = toascii(c.branch()) if opts.get('summary'): fields['summary'] = toascii(opts.get('summary')) if opts.get('description'): fields['description'] = toascii(opts.get('description')) diff = getdiff(ui, repo, c, parent, opts) ui.debug('\n=== Diff from parent to rev ===\n') ui.debug(diff + '\n') if rparent and parent != rparent: parentdiff = getdiff(ui, repo, parent, rparent, opts) ui.debug('\n=== Diff from rparent to parent ===\n') ui.debug(parentdiff + '\n') else: parentdiff = '' if opts.get('update') or not request_id: for field in ('target_groups', 'target_people', 'bugs_closed'): if opts.get(field): value = ','.join(opts.get(field)) else: value = ui.config('reviewboard', field) if value: fields[field] = toascii(value) ui.status('\n%s\n' % changesets_string) ui.status('reviewboard:\t%s\n' % server) ui.status('\n') username = opts.get('username') or ui.config('reviewboard', 'user') if username: ui.status('username: %s\n' % username) password = opts.get('password') or ui.config('reviewboard', 'password') if password: ui.status('password: %s\n' % '**********') try: reviewboard = make_rbclient(server, username, password, proxy=proxy, apiver=opts.get('apiver'), trace=opts.get('apitrace')) except Exception, e: raise util.Abort(_(str(e)))
def diff_check(ui, repo, **kwargs): ui.debug('running diff_check\n') # get all the change contexts for this commit # kwargs['node'] returns the first changecontext nodeid changecontexts = [ repo[i] for i in range(repo[kwargs['node']].rev(), len(repo)) ] # check for tabs def tabCheck(line): tab = line.find('\t') if tab >= 0: # find returns -1 if not found return True, tab return False, tab def windowsLineendingsCheck(line): if line.endswith('\r'): return True, len(line) - 1 return False, 0 def trailingWhitespaceCheck(line): if len(line.strip() ) > 1: # skip empty lines (will have a +) see bug 600536 m = re.match(r'\+.*?(\s+$)', line) if m: return True, m.start(1) return False, 0 def securityCheck(line): loc = line.find('MARK_SECURITY_CHANGE') if loc != -1: # found security change ifdef return True, loc loc = line.find('SECURITYFIX_') if loc != -1: # found security change ifdef return True, loc return False, 0 # check for tabs - exit if user chooses to abort if checkChangeCtxDiff(ui, repo, changecontexts, tabCheck, 'Tab', ('.cpp', '.c', '.h', '.as', '.abs', '.py')): return True if checkChangeCtxDiff(ui, repo, changecontexts, windowsLineendingsCheck, 'Windows line ending', ('.cpp', '.c', '.h', '.as', '.abs', '.py')): return True if checkChangeCtxDiff(ui, repo, changecontexts, trailingWhitespaceCheck, 'Trailing Whitespace', ('.cpp', '.c', '.h', '.as', '.abs', '.py')): return True if checkChangeCtxDiff(ui, repo, changecontexts, securityCheck, 'Security Check', ('.cpp', '.c', '.h', '.as', '.abs', '.py')): return True return False
def postreview(ui, repo, rev='.', **opts): '''post a changeset to a Review Board server This command creates a new review request on a Review Board server, or updates an existing review request, based on a changeset in the repository. If no revision number is specified the parent revision of the working directory is used. By default, the diff uploaded to the server is based on the parent of the revision to be reviewed. A different parent may be specified using the --parent option. Alternatively you may specify --outgoingchanges to calculate the parent based on the outgoing changesets or --branch to choose the parent revision of the branch. If the parent revision is not available to the Review Board server (e.g. it exists in your local repository but not in the one that Review Board has access to) you must tell postreview how to determine the base revision to use for a parent diff. The --outgoing, --outgoingrepo or --master options may be used for this purpose. The --outgoing option is the simplest of these; it assumes that the upstream repository specified in .hg/hgrc is the same as the one known to Review Board. The other two options offer more control if this is not the case. The --outgoing option recognizes the path entries 'reviewboard', 'default-push' and 'default' in this order of precedence. 'reviewboard' may be used if the repository accessible to Review Board is not the upstream repository. ''' ''' HG issue 3841 workaround https://bitbucket.org/tortoisehg/thg/issue/3841/reviewboard-extension-error-unknown ''' oldin, oldout, olderr = sys.stdin, sys.stdout, sys.stderr sys.stdin, sys.stdout, sys.stderr = ui.fin, ui.fout, ui.ferr ui.status('postreview plugin, version %s\n' % __version__) # checks to see if the server was set find_server(ui, opts) check_parent_options(opts) rev_no = repo.revs(rev).first() c = repo[rev_no] rparent = find_rparent(ui, repo, c, opts) ui.debug('remote parent: %s\n' % rparent) parent = find_parent(ui, repo, c, rparent, opts) ui.debug('parent: %s\n' % parent) if parent is None: msg = "Unable to determine parent revision for diff. " if opts.get('outgoingchanges'): msg += _("If using -g/--outgoingchanges, make sure you have some " "(type 'hg out'). Did you forget to commit ('hg st')?") raise error.Abort(msg) diff, parentdiff = create_review_data(ui, repo, c, parent, rparent) send_review(ui, repo, c, parent, diff, parentdiff, opts) sys.stdin, sys.stdout, sys.stderr = oldin, oldout, olderr