def _update(cmd, ui, repo, node=None, rev=None, clean=False, date=None, check=False, **opts): ui.status('[%s]:\n' % repo.root) update_num_args = len(inspect.getargspec(commands.update)[0]) # hg 4.6: any arg after the 3rd must be specified with name if update_num_args >= 7 or update_num_args <= 3: trc = cmd(ui, repo, node=node, rev=rev, clean=clean, date=date, check=check) else: trc = cmd(ui, repo, node, rev, clean, date) rc = trc != None and trc or 0 for subtree in _subtreelist(ui, repo, opts): ui.status('\n') lr = hg.repository(ui, repo.wjoin(subtree)) trc = _update(cmd, lr.ui, lr, node, rev, clean, date, check, **opts) rc += trc != None and trc or 0 return rc
def squash(ui, repo, **opts): """Simple extension that squashes multiple revisions into a single one""" revrange = scmutil.revrange(repo, opts["rev"]) if not revrange: raise util.Abort(_("Please specify a revision")) start = revrange[0] end = revrange[-1] revs = find_revisions(start, end, repo) if not revs: raise util.Abort(_("The revision %s is not an ancestor of %s\n") % (start, end)) elif len(revs) == 1: raise util.Abort(_("Please specify a start and an end revision")) verify_user(ui, repo, revs) no_children(ui, repo, end) has_parent(ui, repo, start) verify_pending_commits(repo) squash_revisions(ui, repo, revs, start, end) for r in revs: ui.status("rev: %s, owner: %s\n" % (repo[r], ui.username()))
def _clonesubtrees(ui, src, dst, opts): subtrees = [] for src, subtree in _subtreegen(src.ui, src, opts): ui.status('\n') _clone(ui, _subtreejoin(src, subtree), dst.wjoin(subtree), opts) subtrees.append(subtree) return subtrees
def _get_reviewers(ui, repo, token, reviewers=[]): folks = _slurp(_api("Person"), dict(token=token)) names = [] for person in reviewers: names += person.split(',') if len(names) == 0: names += [repo.ui.config("auth","kiln.username")] reviewers = [[p for p in folks if reviewer in p["sName"].lower() or reviewer in p["sEmail"].lower()] for reviewer in names] actual_reviewers = [] for shortname, reviewer in zip(names,reviewers): if len(reviewer) > 1: choices = ['%s. %s\n' % (x+1, y['sName']) for (x,y) in enumerate(reviewer)] ui.status('\nHmm... There are a couple folks named "%s"\n' % shortname) [ui.status('%s' % m) for m in choices] pick = ui.promptchoice('Which "%s" did you mean?' % shortname, ["&"+c for c in choices]) actual_reviewers += [ reviewer[pick] ] else: actual_reviewers += reviewer return actual_reviewers
def _get_reviewers(ui, repo, token, reviewers=[]): folks = _slurp(_api("Person"), dict(token=token)) names = [] for person in reviewers: names += person.split(',') if len(names) == 0: names += [repo.ui.config("auth", "kiln.username")] reviewers = [[ p for p in folks if reviewer in p["sName"].lower() or reviewer in p["sEmail"].lower() ] for reviewer in names] actual_reviewers = [] for shortname, reviewer in zip(names, reviewers): if len(reviewer) > 1: choices = [ '%s. %s\n' % (x + 1, y['sName']) for (x, y) in enumerate(reviewer) ] ui.status('\nHmm... There are a couple folks named "%s"\n' % shortname) [ui.status('%s' % m) for m in choices] pick = ui.promptchoice('Which "%s" did you mean?' % shortname, ["&" + c for c in choices]) actual_reviewers += [reviewer[pick]] else: actual_reviewers += reviewer return actual_reviewers
def _paths(cmd, ui, repo, search=None, **opts): ui.status('[%s]:\n' % repo.root) cmd(ui, repo, search) for subtree in _subtreelist(ui, repo, opts): ui.status('\n') lr = hg.repository(ui, repo.wjoin(subtree)) _paths(cmd, lr.ui, lr, search, **opts) return 0
def condcommit(ui, repo, *pats, **opts): '''commit conditionally - only if there is something to commit''' needcommit = len(repo[None].parents()) > 1 # merge if not needcommit: mar = repo.status()[:3] # modified, added, removed needcommit = bool(mar[0] or mar[1] or mar[2]) if needcommit: return hgcommit(ui, repo, *pats, **opts) ui.status('nothing to commit\n') return 0
def ISTYLE(ui, repo, **opts): ui.status('repo.root: %s\n' % repo.root) fmod, fadd, frem, fdel, funk, fign, fcln = repo.status() files = [(f) for f in fmod] + \ [(f) for f in fadd] ui.status('Formatting modified and added files ending in .h, .c or .cpp:\n') for f in files: if f.endswith('.h') or f.endswith('.cpp') or f.endswith('.c'): filePath = repo.root + '/' + f os.system("istyle %s\n" % filePath)
def launch_browser(ui, request_url): # not all python installations have the webbrowser module from mercurial import demandimport demandimport.disable() try: import webbrowser webbrowser.open(request_url) except: ui.status('unable to launch browser - webbrowser module not available.') demandimport.enable()
def launch_browser(ui, request_url): # not all python installations have the webbrowser module from mercurial import demandimport demandimport.disable() try: import webbrowser webbrowser.open(request_url) except: ui.status( 'unable to launch browser - webbrowser module not available.') demandimport.enable()
def getreviewboard(ui, opts): '''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 server = find_server(ui, opts) 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: return make_rbclient(server, username, password, proxy=proxy, apiver=opts.get('apiver')) except ReviewBoardError, msg: raise error.Abort(_(unicode(msg)))
def _update(cmd, ui, repo, node=None, rev=None, clean=False, date=None, check=False, **opts): ui.status('[%s]:\n' % repo.root) if _newupdate: trc = cmd(ui, repo, node, rev, clean, date, check) else: trc = cmd(ui, repo, node, rev, clean, date) rc = trc != None and trc or 0 for subtree in _subtreelist(ui, repo, opts): ui.status('\n') lr = hg.repository(ui, repo.wjoin(subtree)) trc = _update(cmd, lr.ui, lr, node, rev, clean, date, check, **opts) rc += trc != None and trc or 0 return rc
def _docmd2(cmd, ui, repo, remote, adjust, **opts): """Call cmd for repo and each configured/specified subtree. This is for commands which operate on two trees (e.g., tpull, tpush).""" ui.status('[%s]:\n' % repo.root) trc = cmd(ui, repo, remote, **opts) rc = trc != None and trc or 0 for subtree in _subtreelist(ui, repo, opts): ui.status('\n') lr = hg.repository(ui, repo.wjoin(subtree)) remote2 = adjust and os.path.join(remote, subtree) or remote trc = _docmd2(cmd, lr.ui, lr, remote2, adjust, **opts) rc += trc != None and trc or 0 return rc
def _command(ui, repo, argv, stop, opts): ui.status('[%s]:\n' % repo.root) ui.flush() # Mercurial bug? util.system() drops elements of argv after the first. # rc = util.system(argv, cwd=repo.root) rc = subprocess.call(argv, cwd=repo.root) if rc and stop: return rc for subtree in _subtreelist(ui, repo, opts): ui.status('\n') ui.flush() lr = hg.repository(ui, repo.wjoin(subtree)) rc += _command(lr.ui, lr, argv, stop, opts) if rc and stop: return rc return rc
def _docmd2(cmd, ui, repo, remote, adjust, **opts): """Call cmd for repo and each configured/specified subtree. This is for commands which operate on two trees (e.g., tpull, tpush).""" ui.status('[%s]:\n' % repo.root) trc = cmd(ui, repo, remote, **opts) ui.flush() rc = trc != None and trc or 0 for subtree in _subtreelist(ui, repo, opts): ui.status('\n') lr = hg.repository(ui, repo.wjoin(subtree)) remote2 = adjust and os.path.join(remote, subtree) or remote trc = _docmd2(cmd, lr.ui, lr, remote2, adjust, **opts) rc += trc != None and trc or 0 return rc
def get_shipable_bundles(ui, repo, rev='.', **opts): """TBD """ ui.status('postreview plugin, version %s\n' % __version__) find_server(ui, opts) reviewboard = getreviewboard(ui, opts) opts['unbundle'] = opts['submit'] or opts['unbundle'] try: repo_id = find_reviewboard_repo_id(ui, reviewboard, opts) shipable = reviewboard.shipable_requests(repo_id) fnames_per_request = [(reviewboard.download_attachement_with_given_caption(request.id, BUNDLE_ATTACHMENT_CAPTION), request.id) for request in shipable] if opts['unbundle']: for fnames, request_id in fnames_per_request: [unbundle(ui, repo, fname) for fname in fnames] if opts['submit']: reviewboard.submit(request_id) print "submitted" except ReviewBoardError, msg: raise error.Abort(_(unicode(msg)))
def _docmd1(cmd, ui, repo, *args, **opts): """Call cmd for repo and each configured/specified subtree. This is for commands which operate on a single tree (e.g., tstatus, tupdate).""" ui.status('[%s]:\n' % repo.root) # XXX - should be done just once. cmdopts = dict(opts) for o in subtreesopts: if o[1] in cmdopts: del cmdopts[o[1]] trc = cmd(ui, repo, *args, **cmdopts) rc = trc != None and trc or 0 for subtree in _subtreelist(ui, repo, opts): ui.status('\n') lr = hg.repository(ui, repo.wjoin(subtree)) trc = _docmd1(cmd, lr.ui, lr, *args, **opts) rc += trc != None and trc or 0 return rc
def _docmd1(cmd, ui, repo, *args, **opts): """Call cmd for repo and each configured/specified subtree. This is for commands which operate on a single tree (e.g., tstatus, tupdate).""" ui.status('[%s]:\n' % repo.root) # XXX - should be done just once. cmdopts = dict(opts) for o in subtreesopts: if o[1] in cmdopts: del cmdopts[o[1]] trc = cmd(ui, repo, *args, **cmdopts) ui.flush() rc = trc != None and trc or 0 for subtree in _subtreelist(ui, repo, opts): ui.status('\n') lr = hg.repository(ui, repo.wjoin(subtree)) trc = _docmd1(cmd, lr.ui, lr, *args, **opts) rc += trc != None and trc or 0 return rc
def send_review(ui, repo, c, parentc, diff, parentdiff, opts): files = None if opts['attachbundle']: tmpfile = tempfile.NamedTemporaryFile(prefix='review_', suffix='.hgbundle', delete=False) tmpfile.close() if opts['old_server']: ui.status('postreview using old server compatibility mode (bundle format v1)\n') # request explicit 'v1' bundle format for our old creaky reviewboard server (running mercurial 2.0.x) # because it would be unable to read new 'v2' bundle format that mercurial 3.x uses bundle(ui, repo, tmpfile.name, dest=None, base=(parentc.rev(),), rev=(c.rev(),), type='bzip2-v1') else: bundle(ui, repo, tmpfile.name, dest=None, base=(parentc.rev(),), rev=(c.rev(),)) f = open(tmpfile.name,'rb') files = {BUNDLE_ATTACHMENT_CAPTION: {'filename': tmpfile.name, 'content': f.read()}} f.close() os.remove(tmpfile.name) fields = createfields(ui, repo, c, parentc, opts) request_id = opts['existing'] if request_id: update_review(request_id, ui, fields, diff, parentdiff, opts, files) else: request_id = new_review(ui, fields, diff, parentdiff, opts, files) request_url = '%s/%s/%s/' % (find_server(ui, opts), "r", request_id) if not request_url.startswith('http'): request_url = 'http://%s' % request_url msg = 'review request draft saved: %s\n' if opts['publish']: msg = 'review request published: %s\n' ui.status(msg % request_url) if ui.configbool('reviewboard', 'launch_webbrowser'): launch_webbrowser(ui, request_url)
def _clone(ui, source, dest, opts, skiproot=False): if not skiproot and not os.path.exists(os.path.join(dest, '.hg')): ui.status('cloning %s\n' % source) src, dst = _clonerepo(ui, source, dest, opts) ui.status(_('created %s\n') % dst.root) else: msg = 'skipping %s (destination exists)\n' if skiproot: msg = 'skipping root %s\n' ui.status(msg % source) src, dst = _skiprepo(ui, source, dest) subtrees = _clonesubtrees(ui, src, dst, opts) addconfig(ui, dst, subtrees, opts, True)
def _clone(ui, source, dest, opts, skiproot = False): if not skiproot and not os.path.exists(os.path.join(dest, '.hg')): ui.status('cloning %s\n' % source) src, dst = _clonerepo(ui, source, dest, opts) ui.status(_('created %s\n') % dst.root) else: msg = 'skipping %s (destination exists)\n' if skiproot: msg = 'skipping root %s\n' ui.status(msg % source) src, dst = _skiprepo(ui, source, dest) subtrees = _clonesubtrees(ui, src, dst, opts) addconfig(ui, dst, subtrees, opts, True)
def review(ui, repo, *dest, **opts): """create a code review for some changesets on kiln Review creates a brand new code review on kiln for a changeset on kiln. If no revision is specified, the code review defaults to the most recent changeset. Specify people to peek at your review by passing a comma-separated list of people to review your code, by passing multiple -p flags, or both. hg review -p tim,alex,ben -p joey You can specify revisions by passing a hash-range, hg review -r 13bs32abc:tip or by passing individual changesets hg review -r 75c471319a5b -r 41056495619c Using -e will open up your favorite editor and includes all the changeset descriptions for any revisions selected as the code review comment. """ prefix = repo.ui.config("auth", "kiln.prefix") current_user = repo.ui.config("auth", "kiln.username") or repo.ui.config( "ui", "username") if prefix is None: ui.warn("In order to work, in your hgrc please set:\n\n") ui.warn("[auth]\n") ui.warn("kiln.prefix = https://kilnrepo.kilnhg.com\n") ui.warn("kiln.username = [email protected]\n") ui.warn("kiln.password = keymash\n") return 0 review_params = {} changesets = ['tip'] if opts.get('revs'): revs = opts.get('revs') changesets = [ repo[rev].hex()[:12] for rev in scmutil.revrange(repo, revs) ] review_params['revs'] = changesets comment = opts.get('comment') if opts.get('editor'): if opts.get('comment'): default_comment = opts['comment'] else: changeset_descs = [repo[rev].description() for rev in changesets] default_comment = "\n".join(changeset_descs) comment = ui.edit(default_comment, current_user) if comment: review_params['sDescription'] = comment if opts.get('title'): review_params['sTitle'] = opts.get('title') token = _token() review_params['token'] = token if dest: dest = dest[0] rev_repo = _get_user_repos(ui, repo, dest) if rev_repo: all_repos = _get_kiln_paths(ui, repo, token) kiln_repo = all_repos[rev_repo] review_params["ixRepo"] = kiln_repo["ixRepo"] reviewers = _get_reviewers(ui, repo, token, opts.get('people')) review_params['ixReviewers'] = [r['ixPerson'] for r in reviewers] review_status = _make_review(review_params) if review_status: ui.status("Review created!\n") ui.status("%s/Review/%s" % (prefix, review_status['ixReview'])) return 1 else: return 0
def version(ui, **opts): '''show version information''' ui.status('trees extension (version 0.7)\n')
def check(ui, repo, node, **kwargs): if HOOKS_PATH is None: ui.warn( "HG_HOOKS_PATH environmental variable isn't set, StyleCop cannot be hooked\n" + "Go to your \"hooks\" directory and run Initialize.bat to set this variable\n" ) return 0 #Путь до StyleCopCli. Сейчас он находится в папке с хуком. #Если будет смысл в дальнейшем перенести его отдельно - нужно будет сделать новую переменную окружения STYLECOP_PATH = HOOKS_PATH + "\\StyleCopCLI" #имя файла с выводом StyleCop'а. #Инициализируем только после того, как убедились, что HOOKS_PATH не None OUT_FILE = HOOKS_PATH + "\\out.xml" #Парсим репозитории в файле repositories reps = parseRepositories(HOOKS_PATH + "\\" + REPOSITORIES_FILE_NAME) #Если нашего репозитория нет в списке - не делаем ничего if (reps[repo.root] is None): return 0 #Если репозиторий есть в списке, но проверка синтаксиса отключена - #Выводим предупреждение (чтобы не забыли когда-нибудь включить) if (reps[repo.root] == "0"): ui.warn( "StyleCop is disabled for this repository, syntax won't be checked\n" ) return 0 #Получаем repoContext для нашего коммита. В нём лежит, в частности, список файлов в коммите ctx = repo[node] #Инициализируем новый set, где будем хранить файлы, которые нужно передать в StyleCop fileSet = set() #Итерируемся по списку файлов в коммите for currentFile in ctx.files(): #Если это не .cs-файл - пропускаем его (TODO: добавить ещё расширения?) if re.match('.+\.cs', currentFile) and currentFile in ctx: fileSet.add(currentFile) #Если ни одного C#-файла не было изменено - выходим if len(fileSet) == 0: return 0 #Подгружаем файл настроек только если он существует settingsFile = repo.root + "\Settings.StyleCop" settingsString = " -set " + settingsFile if os.path.isfile( settingsFile) else "" #Инициализируем строку для запуска cmd = STYLECOP_PATH + "\\StyleCopCLI.exe -out " + OUT_FILE + settingsString + " -cs \"" + "\", \"".join( fileSet) + "\"" ui.status("[StyleCop]:") process = subprocess.Popen(cmd) process.wait() if process.returncode == 2: ui.warn(parseXml(OUT_FILE)) elif process.returncode == 1: ui.warn("Error while executing StyleCop syntax check\n") return process.returncode
except ReviewBoardError, msg: raise util.Abort(_(str(msg))) if not repositories: raise util.Abort(_('no repositories configured at %s' % server)) if repo_name: repo_dict = dict((r.name, r.id) for r in repositories) if repo_name in repo_dict: repo_id = repo_dict[repo_id_opt] else: raise util.Abort( _('invalid repository name: %s') % repo_name) else: ui.status('Repositories:\n') repo_ids = set() for r in repositories: ui.status('[%s] %s\n' % (r.id, r.name)) repo_ids.add(str(r.id)) if len(repositories) > 1: repo_id = ui.prompt('repository id:', 0) if not repo_id in repo_ids: raise util.Abort( _('invalid repository ID: %s') % repo_id) else: repo_id = str(repositories[0].id) ui.status('repository id: %s\n' % repo_id) try: request_id = reviewboard.new_request(repo_id, fields, diff,
def review(ui, repo, *dest, **opts): """create a code review for some changesets on kiln Review creates a brand new code review on kiln for a changeset on kiln. If no revision is specified, the code review defaults to the most recent changeset. Specify people to peek at your review by passing a comma-separated list of people to review your code, by passing multiple -p flags, or both. hg review -p tim,alex,ben -p joey You can specify revisions by passing a hash-range, hg review -r 13bs32abc:tip or by passing individual changesets hg review -r 75c471319a5b -r 41056495619c Using -e will open up your favorite editor and includes all the changeset descriptions for any revisions selected as the code review comment. """ prefix = repo.ui.config("auth","kiln.prefix") current_user = repo.ui.config("auth","kiln.username") or repo.ui.config("ui","username") if prefix is None: ui.warn("In order to work, in your hgrc please set:\n\n") ui.warn("[auth]\n") ui.warn("kiln.prefix = https://kilnrepo.kilnhg.com\n") ui.warn("kiln.username = [email protected]\n") ui.warn("kiln.password = keymash\n") return 0 review_params = {} changesets = ['tip'] if opts.get('revs'): revs = opts.get('revs') changesets = [repo[rev].hex()[:12] for rev in scmutil.revrange(repo, revs)] review_params['revs'] = changesets comment = opts.get('comment') if opts.get('editor'): if opts.get('comment'): default_comment = opts['comment'] else: changeset_descs = [repo[rev].description() for rev in changesets] default_comment = "\n".join(changeset_descs) comment = ui.edit(default_comment, current_user) if comment: review_params['sDescription'] = comment if opts.get('title'): review_params['sTitle'] = opts.get('title') token = _token() review_params['token'] = token if dest: dest = dest[0] rev_repo = _get_user_repos(ui, repo, dest) if rev_repo: all_repos = _get_kiln_paths(ui, repo, token) kiln_repo = all_repos[rev_repo] review_params["ixRepo"] = kiln_repo["ixRepo"] reviewers = _get_reviewers(ui, repo, token, opts.get('people')) review_params['ixReviewers'] = [r['ixPerson'] for r in reviewers] review_status = _make_review(review_params) if review_status: ui.status("Review created!\n") ui.status("%s/Review/%s" % (prefix, review_status['ixReview'])) return 1 else: return 0
def condmerge(ui, repo, node=None, **opts): if len(repo.heads()) > 1: return hgmerge(ui, repo, node, **opts) ui.status('nothing to merge\n') return 0
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. 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)))
raise error.Abort(_(unicode(msg))) if not repositories: raise error.Abort(_('no repositories configured at %s' % find_server(ui, opts))) repositories = sorted(repositories, key=operator.attrgetter('name'), cmp=lambda x, y: cmp(x.lower(), y.lower())) remotepath = remove_username(expandpath(ui, opts['outgoingrepo']).lower()) repo_id = None for r in repositories: if r.tool != 'Mercurial': continue if is_same_repo(r.path, remotepath): repo_id = str(r.id) ui.status('Using repository: %s\n' % r.name) break if repo_id == None and opts['interactive']: ui.status('Repositories:\n') repo_ids = set() for r in repositories: if r.tool != 'Mercurial': continue ui.status('[%s] %s\n' % (r.id, r.name) ) repo_ids.add(str(r.id)) if len(repositories) > 1: repo_id = ui.prompt('repository id:', 0) if not repo_id in repo_ids: raise error.Abort(_('invalid repository ID: %s') % repo_id) else:
def createfields(ui, repo, c, parentc, opts): fields = {} all_contexts = find_contexts(repo, parentc, c, opts) # The latest unambiguous prefix of global changeset id (Commit field on UI) # Should be set on creation and on any update of review request. # commit_id field is introduced in reviewboard API 2.0 fields['commit_id'] = str(all_contexts[0]) changesets_string = 'changesets:\n' changesets_string += \ ''.join(['\t%s:%s "%s"\n' % (ctx.rev(), ctx, ctx.description()) \ for ctx in all_contexts]) if opts['branch']: branch_msg = "review of branch: %s\n\n" % (c.branch()) changesets_string = branch_msg + changesets_string ui.status(changesets_string + '\n') interactive = opts['interactive'] request_id = opts['existing'] # Don't clobber the summary and description for an existing request # unless specifically asked for if opts['update'] or not request_id: # summary if opts["summary"] and opts["summary"] != " ": default_summary = opts["summary"] else: default_summary = c.description().splitlines()[0] if interactive: ui.status('default summary: %s\n' % default_summary) ui.status('enter summary (or return for default):\n') summary = readline().strip() if summary: fields['summary'] = summary else: fields['summary'] = default_summary else: fields['summary'] = default_summary # description if interactive: ui.status('enter description:\n') description = readline().strip() ui.status('append changesets to description? (Y/n):\n') choice = readline().strip() if choice != 'n': if description: description += '\n\n' description += changesets_string else: description = changesets_string fields['description'] = description if not opts.get('bugs_closed') and fields['summary']: bugs_list = re.findall( r'([A-Z]+-[0-9]+)', fields['summary']) bugs_list = list(set(bugs_list)) augumented_bugs_list = [] for bug in bugs_list: if bug is bugs_list[-1]: augumented_bugs_list.append(str(bug)) else: augumented_bugs_list.append(str(bug) + ", ") fields['bugs_closed'] = "".join(augumented_bugs_list) fields['branch'] = c.branch() for field in ('target_groups', 'target_people', 'bugs_closed'): if opts.get(field): value = opts.get(field) else: value = ui.config('reviewboard', field) if value: fields[field] = value return fields
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
def reject_improperly_formatted_push(ui, repo, node, **kwargs): ui.status('Mercurial repo.root: %s\n' % repo.root) ui.status('The node being created is: %s \n' % node) fileSet = set() #Grab the tip's file context ctx = repo['tip'] # Loop through each changeset being added to the repository for change_id in xrange(repo[node].rev(), len(repo)): ui.status('Style format checking will include changeset: %d\n' % change_id) for currentFile in repo[change_id].files(): if currentFile.endswith('.h') or currentFile.endswith( '.cpp') or currentFile.endswith('.c'): fileSet.add(currentFile) forbid = False for currentFile in fileSet: # Do not check the file if it is being deleted if currentFile not in ctx: continue userHome = os.getenv('HOME') outFilePath = userHome + '/tmp/hg_push_format_chk' ff = open(outFilePath, 'w') # Get the file context fctx = ctx[currentFile] # Save the contents of the current file to the temp file ff.write(fctx.data()) # Close the temp file ff.close() output = os.popen('istyle %s\n' % '~/tmp/hg_push_format_chk', 'r') for lines in output: if lines.find('formatted') >= 0: ui.status("Invalid Style Format within file: %s\n" % currentFile) forbid = True if forbid: ui.status( "Push/Pull Failed: Fix style format of offending files (using istyle and hg commit) and then retry push/pull.\n" ) else: ui.status("All added and modified files are correctly formatted.\n") return forbid
def are_changes_properly_formatted(ui, repo, **kwargs): ui.status( 'Checking if added and modified source files are properly style formatted..\n' ) fmod, fadd, frem, fdel, funk, fign, fcln = repo.status() files = [(f) for f in fmod] + \ [(f) for f in fadd] forbid = False executableForbid = False for f in files: filePath = repo.root + '/' + f status = os.stat(filePath) if bool(status.st_mode & stat.S_IXGRP) or bool( status.st_mode & stat.S_IXUSR) or bool(status.st_mode & stat.S_IXOTH): ui.status('Invalid Execute Flag for file : %s\n' % filePath) executableForbid = True if f.endswith('.h') or f.endswith('.cpp') or f.endswith('.c'): os.system('cp %s ~/tmp/hg_commit_format_chk \n' % filePath) newFilePath = '~/tmp/hg_commit_format_chk' output = os.popen('istyle %s\n' % newFilePath, 'r') for lines in output: if lines.find('formatted') >= 0: ui.status('Invalid Style Format within file: %s\n' % filePath) forbid = True if forbid: ui.status( 'Commit Failed: Fix style format of offending files (use hg istyle)\n' ) elif executableForbid: ui.status( 'Commit Failed: Remove execute permission on offending file(s)\n') else: ui.status( 'All added and modified source files are correctly style formatted and none are executable.\n' ) return (forbid or executableForbid)