def __init__(self, ui, path, rev=None): converter_source.__init__(self, ui, path, rev) self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False) self.ignored = set() self.saverev = ui.configbool('convert', 'hg.saverev', False) try: self.repo = hg.repository(self.ui, path) # try to provoke an exception if this isn't really a hg # repo, but some other bogus compatible-looking url if not self.repo.local(): raise error.RepoError() except error.RepoError: ui.traceback() raise NoRepo(_("%s is not a local Mercurial repository") % path) self.lastrev = None self.lastctx = None self._changescache = None self.convertfp = None # Restrict converted revisions to startrev descendants startnode = ui.config('convert', 'hg.startrev') if startnode is not None: try: startnode = self.repo.lookup(startnode) except error.RepoError: raise util.Abort( _('%s is not a valid start revision') % startnode) startrev = self.repo.changelog.rev(startnode) children = {startnode: 1} for rev in self.repo.changelog.descendants(startrev): children[self.repo.changelog.node(rev)] = 1 self.keep = children.__contains__ else: self.keep = util.always
def pull(oldpull, ui, repo, source="default", **opts): # translate bookmark args to rev args for actual pull if opts.get('bookmark'): # this is an unpleasant hack as pull will do this internally source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) other = hg.repository(hg.remoteui(repo, opts), source) rb = other.listkeys('bookmarks') for b in opts['bookmark']: if b not in rb: raise util.Abort(_('remote bookmark %s not found!') % b) opts.setdefault('rev', []).append(b) result = oldpull(ui, repo, source, **opts) # update specified bookmarks if opts.get('bookmark'): for b in opts['bookmark']: # explicit pull overrides local bookmark if any ui.status(_("importing bookmark %s\n") % b) repo._bookmarks[b] = repo[rb[b]].node() write(repo) return result
def rawentries(subdir="", **map): descend = self.ui.configbool('web', 'descend', True) for name, path in self.repos: if not name.startswith(subdir): continue name = name[len(subdir):] if not descend and '/' in name: continue u = self.ui.copy() try: u.readconfig(os.path.join(path, '.hg', 'hgrc')) except Exception, e: u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e)) continue def get(section, name, default=None): return u.config(section, name, default, untrusted=True) if u.configbool("web", "hidden", untrusted=True): continue if not self.read_allowed(u, req): continue parts = [name] if 'PATH_INFO' in req.env: parts.insert(0, req.env['PATH_INFO'].rstrip('/')) if req.env['SCRIPT_NAME']: parts.insert(0, req.env['SCRIPT_NAME']) url = re.sub(r'/+', '/', '/'.join(parts) + '/') # update time with local timezone try: r = hg.repository(self.ui, path) except error.RepoError: u.warn(_('error accessing repository at %s\n') % path) continue try: d = (get_mtime(r.spath), util.makedate()[1]) except OSError: continue contact = get_contact(get) description = get("web", "description", "") name = get("web", "name", name) row = dict(contact=contact or "unknown", contact_sort=contact.upper() or "unknown", name=name, name_sort=name, url=url, description=description or "unknown", description_sort=description.upper() or "unknown", lastchange=d, lastchange_sort=d[1]-d[0], archives=archivelist(u, "tip", url)) yield row
def outgoing(oldoutgoing, ui, repo, dest=None, **opts): if opts.get('bookmarks'): dest = ui.expandpath(dest or 'default-push', dest or 'default') dest, branches = hg.parseurl(dest, opts.get('branch')) other = hg.repository(hg.remoteui(repo, opts), dest) ui.status(_('comparing with %s\n') % url.hidepassword(dest)) return diffbookmarks(ui, other, repo) else: return oldoutgoing(ui, repo, dest, **opts)
def incoming(oldincoming, ui, repo, source="default", **opts): if opts.get('bookmarks'): source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) other = hg.repository(hg.remoteui(repo, opts), source) ui.status(_('comparing with %s\n') % url.hidepassword(source)) return diffbookmarks(ui, repo, other) else: return oldincoming(ui, repo, source, **opts)
def create_repository(self): """ Create a source code repository """ import os #os.umask(0) u = ui.ui() if self.anonymous_access: hg.repository( u, os.path.join( global_settings.DJANGO_HG_REPOSITORIES_DIR['public'], self.repo_path), True) else: hg.repository( u, os.path.join( global_settings.DJANGO_HG_REPOSITORIES_DIR['public'], self.repo_path), True)
def setbranch(self, branch, pbranches): if not self.clonebranches: return setbranch = (branch != self.lastbranch) self.lastbranch = branch if not branch: branch = 'default' pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches] pbranch = pbranches and pbranches[0][1] or 'default' branchpath = os.path.join(self.path, branch) if setbranch: self.after() try: self.repo = hg.repository(self.ui, branchpath) except: self.repo = hg.repository(self.ui, branchpath, create=True) self.before() # pbranches may bring revisions from other branches (merge parents) # Make sure we have them, or pull them. missings = {} for b in pbranches: try: self.repo.lookup(b[0]) except: missings.setdefault(b[1], []).append(b[0]) if missings: self.after() for pbranch, heads in missings.iteritems(): pbranchpath = os.path.join(self.path, pbranch) prepo = hg.repository(self.ui, pbranchpath) self.ui.note( _('pulling from %s into %s\n') % (pbranch, branch)) self.repo.pull(prepo, [prepo.lookup(h) for h in heads]) self.before()
def refresh(self, request=None): if request: self.repo.ui.environ = request.env mtime = get_mtime(self.repo.spath) if mtime != self.mtime: self.mtime = mtime self.repo = hg.repository(self.repo.ui, self.repo.root) self.maxchanges = int(self.config("web", "maxchanges", 10)) self.stripecount = int(self.config("web", "stripes", 1)) self.maxshortchanges = int(self.config("web", "maxshortchanges", 60)) self.maxfiles = int(self.config("web", "maxfiles", 10)) self.allowpull = self.configbool("web", "allowpull", True) encoding.encoding = self.config("web", "encoding", encoding.encoding)
def getoutgoing(dest, revs): '''Return the revisions present locally but not in dest''' dest = ui.expandpath(dest or 'default-push', dest or 'default') dest, branches = hg.parseurl(dest) revs, checkout = hg.addbranchrevs(repo, repo, branches, revs) if revs: revs = [repo.lookup(rev) for rev in revs] other = hg.repository(hg.remoteui(repo, opts), dest) ui.status(_('comparing with %s\n') % url.hidepassword(dest)) o = discovery.findoutgoing(repo, other) if not o: ui.status(_("no changes found\n")) return [] o = repo.changelog.nodesbetween(o, revs)[0] return [str(repo.changelog.rev(r)) for r in o]
def __init__(self, ui, path): converter_sink.__init__(self, ui, path) self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True) self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False) self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default') self.lastbranch = None if os.path.isdir(path) and len(os.listdir(path)) > 0: try: self.repo = hg.repository(self.ui, path) if not self.repo.local(): raise NoRepo( _('%s is not a local Mercurial repository') % path) except error.RepoError, err: ui.traceback() raise NoRepo(err.args[0])
def __init__(self, repo, hash=None): if hash is None: hash = 'tip' # get a repo object for the current directory self.repository = hg.repository(ui.ui(), os.path.join(repo.repo_path)) # get a context object for the "rev" revision self.ctx = self.repository[hash] self.user = self.ctx.user() self.description = self.ctx.description() self.time = datetime.fromtimestamp(self.ctx.date()[0]).time() self.date = date.fromtimestamp(self.ctx.date()[0]) self.hash = self.ctx #hash self.rev = self.ctx.rev() self.files_count = len(self.ctx.files()) self.revision = self.repository[self.rev]
def relink(ui, repo, origin=None, **opts): """recreate hardlinks between two repositories When repositories are cloned locally, their data files will be hardlinked so that they only use the space of a single repository. Unfortunately, subsequent pulls into either repository will break hardlinks for any files touched by the new changesets, even if both repositories end up pulling the same changes. Similarly, passing --rev to "hg clone" will fail to use any hardlinks, falling back to a complete copy of the source repository. This command lets you recreate those hardlinks and reclaim that wasted space. This repository will be relinked to share space with ORIGIN, which must be on the same local disk. If ORIGIN is omitted, looks for "default-relink", then "default", in [paths]. Do not attempt any read operations on this repository while the command is running. (Both repositories will be locked against writes.) """ if not hasattr(util, 'samefile') or not hasattr(util, 'samedevice'): raise util.Abort(_('hardlinks are not supported on this system')) src = hg.repository( hg.remoteui(repo, opts), ui.expandpath(origin or 'default-relink', origin or 'default')) if not src.local(): raise util.Abort('must specify local origin repository') ui.status(_('relinking %s to %s\n') % (src.store.path, repo.store.path)) locallock = repo.lock() try: remotelock = src.lock() try: candidates = sorted(collect(src, ui)) targets = prune(candidates, src.store.path, repo.store.path, ui) do_relink(src.store.path, repo.store.path, targets, ui) finally: remotelock.release() finally: locallock.release()
def __init__(self, repo, name=None, baseui=None): if isinstance(repo, str): if baseui: u = baseui.copy() else: u = ui.ui() self.repo = hg.repository(u, repo) else: self.repo = repo self.repo.ui.setconfig('ui', 'report_untrusted', 'off') self.repo.ui.setconfig('ui', 'interactive', 'off') hook.redirect(True) self.mtime = -1 self.reponame = name self.archives = 'zip', 'gz', 'bz2' self.stripecount = 1 # a repo owner may set web.templates in .hg/hgrc to get any file # readable by the user running the CGI script self.templatepath = self.config('web', 'templates')
def run_wsgi(self, req): try: try: self.refresh() virtual = req.env.get("PATH_INFO", "").strip('/') tmpl = self.templater(req) ctype = tmpl('mimetype', encoding=encoding.encoding) ctype = templater.stringify(ctype) # a static file if virtual.startswith('static/') or 'static' in req.form: if virtual.startswith('static/'): fname = virtual[7:] else: fname = req.form['static'][0] static = templater.templatepath('static') return (staticfile(static, fname, req),) # top-level index elif not virtual: req.respond(HTTP_OK, ctype) return self.makeindex(req, tmpl) # nested indexes and hgwebs repos = dict(self.repos) virtualrepo = virtual while virtualrepo: real = repos.get(virtualrepo) if real: req.env['REPO_NAME'] = virtualrepo try: repo = hg.repository(self.ui, real) return hgweb(repo).run_wsgi(req) except IOError, inst: msg = inst.strerror raise ErrorResponse(HTTP_SERVER_ERROR, msg) except error.RepoError, inst: raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
def push(oldpush, ui, repo, dest=None, **opts): dopush = True if opts.get('bookmark'): dopush = False for b in opts['bookmark']: if b in repo._bookmarks: dopush = True opts.setdefault('rev', []).append(b) result = 0 if dopush: result = oldpush(ui, repo, dest, **opts) if opts.get('bookmark'): # this is an unpleasant hack as push will do this internally dest = ui.expandpath(dest or 'default-push', dest or 'default') dest, branches = hg.parseurl(dest, opts.get('branch')) other = hg.repository(hg.remoteui(repo, opts), dest) rb = other.listkeys('bookmarks') for b in opts['bookmark']: # explicit push overrides remote bookmark if any if b in repo._bookmarks: ui.status(_("exporting bookmark %s\n") % b) new = repo[b].hex() elif b in rb: ui.status(_("deleting remote bookmark %s\n") % b) new = '' # delete else: ui.warn( _('bookmark %s does not exist on the local ' 'or remote repository!\n') % b) return 2 old = rb.get(b, '') r = other.pushkey('bookmarks', b, old, new) if not r: ui.warn(_('updating bookmark %s failed!\n') % b) if not result: result = 2 return result
def fetch(ui, repo, source='default', **opts): '''pull changes from a remote repository, merge new changes if needed. This finds all changes from the repository at the specified path or URL and adds them to the local repository. If the pulled changes add a new branch head, the head is automatically merged, and the result of the merge is committed. Otherwise, the working directory is updated to include the new changes. When a merge occurs, the newly pulled changes are assumed to be "authoritative". The head of the new changes is used as the first parent, with local changes as the second. To switch the merge order, use --switch-parent. See :hg:`help dates` for a list of formats valid for -d/--date. Returns 0 on success. ''' date = opts.get('date') if date: opts['date'] = util.parsedate(date) parent, p2 = repo.dirstate.parents() branch = repo.dirstate.branch() branchnode = repo.branchtags().get(branch) if parent != branchnode: raise util.Abort( _('working dir not at branch tip ' '(use "hg update" to check out branch tip)')) if p2 != nullid: raise util.Abort(_('outstanding uncommitted merge')) wlock = lock = None try: wlock = repo.wlock() lock = repo.lock() mod, add, rem, del_ = repo.status()[:4] if mod or add or rem: raise util.Abort(_('outstanding uncommitted changes')) if del_: raise util.Abort(_('working directory is missing some files')) bheads = repo.branchheads(branch) bheads = [head for head in bheads if len(repo[head].children()) == 0] if len(bheads) > 1: raise util.Abort( _('multiple heads in this branch ' '(use "hg heads ." and "hg merge" to merge)')) other = hg.repository(hg.remoteui(repo, opts), ui.expandpath(source)) ui.status( _('pulling from %s\n') % url.hidepassword(ui.expandpath(source))) revs = None if opts['rev']: try: revs = [other.lookup(rev) for rev in opts['rev']] except error.CapabilityError: err = _("Other repository doesn't support revision lookup, " "so a rev cannot be specified.") raise util.Abort(err) # Are there any changes at all? modheads = repo.pull(other, heads=revs) if modheads == 0: return 0 # Is this a simple fast-forward along the current branch? newheads = repo.branchheads(branch) newchildren = repo.changelog.nodesbetween([parent], newheads)[2] if len(newheads) == 1: if newchildren[0] != parent: return hg.clean(repo, newchildren[0]) else: return 0 # Are there more than one additional branch heads? newchildren = [n for n in newchildren if n != parent] newparent = parent if newchildren: newparent = newchildren[0] hg.clean(repo, newparent) newheads = [n for n in newheads if n != newparent] if len(newheads) > 1: ui.status( _('not merging with %d other new branch heads ' '(use "hg heads ." and "hg merge" to merge them)\n') % (len(newheads) - 1)) return 1 # Otherwise, let's merge. err = False if newheads: # By default, we consider the repository we're pulling # *from* as authoritative, so we merge our changes into # theirs. if opts['switch_parent']: firstparent, secondparent = newparent, newheads[0] else: firstparent, secondparent = newheads[0], newparent ui.status( _('updating to %d:%s\n') % (repo.changelog.rev(firstparent), short(firstparent))) hg.clean(repo, firstparent) ui.status( _('merging with %d:%s\n') % (repo.changelog.rev(secondparent), short(secondparent))) err = hg.merge(repo, secondparent, remind=False) if not err: # we don't translate commit messages message = (cmdutil.logmessage(opts) or ('Automated merge with %s' % url.removeauth(other.url()))) editor = cmdutil.commiteditor if opts.get('force_editor') or opts.get('edit'): editor = cmdutil.commitforceeditor n = repo.commit(message, opts['user'], opts['date'], editor=editor) ui.status( _('new changeset %d:%s merges remote changes ' 'with local\n') % (repo.changelog.rev(n), short(n))) return err finally: release(lock, wlock)
def repo(request, name, action, rev, path='', display=None): """ display a repository or handles the commands sended by an hg client (clone, pull, push) if a 'cmd' argument is given ``name`` the name of the repository ``action`` the action to display ``rev`` the revision of the repository. By default, it's equal to ``tip`` ``path`` optional. A valid path within the repository. If not given, the root of the repository is returned """ if request.GET.get('cmd') and request.GET.get('cmd') in HgRepository.cmds: # A clone/pull/push command from Mercurial from my_mercurial import hg, ui from my_mercurial.hgweb.request import wsgirequest from my_mercurial.hgweb import protocol repo = __get_repo(request, name, 'tip') req = wsgirequest(request.META, None) r = hg.repository(ui.ui(), repo.repo_path) try: resp = protocol.__getattribute__(request.GET.get('cmd'))(r, req) #update the repository size only after a push #if (request.GET.get('cmd') == 'unbundle'): # print repo.get_size() except: from my_mercurial.hgweb.common import ErrorResponse, HTTP_OK resp = ErrorResponse(HTTP_OK, 'command unrecognized') return HttpResponse(resp, protocol.HGTYPE) else: # Display a view of a repository repo = __get_repo(request, name, rev) if not repo.user_can_read(request.user.username): return HttpResponseRedirect(global_settings.LOGIN_URL) # dispatcher if action == 'binary': return __binary(request, repo, rev, path) elif action == 'browse': if path.rfind('/') == len(path) - 1: # browse the repo tree return __browse(request, repo, rev, path) else: # show a file return __show(request, repo, rev, path) elif action == 'changesets': if path == '': if "q" in request.GET: #perform a search return __search(request, repo) # display changesets elif (rev == 'tip'): # pager of changesets return __changesets(request, repo) else: # show a changeset return __changeset(request, repo, rev) else: if rev == 'tip': # log of a file return __log(request, repo, rev, path) else: return __diff(request, repo, rev, path) elif action == 'overview': return __overview(request, repo, rev)
def transplant(ui, repo, *revs, **opts): '''transplant changesets from another branch Selected changesets will be applied on top of the current working directory with the log of the original changeset. If --log is specified, log messages will have a comment appended of the form:: (transplanted from CHANGESETHASH) You can rewrite the changelog message with the --filter option. Its argument will be invoked with the current changelog message as $1 and the patch as $2. If --source/-s is specified, selects changesets from the named repository. If --branch/-b is specified, selects changesets from the branch holding the named revision, up to that revision. If --all/-a is specified, all changesets on the branch will be transplanted, otherwise you will be prompted to select the changesets you want. :hg:`transplant --branch REVISION --all` will rebase the selected branch (up to the named revision) onto your current working directory. You can optionally mark selected transplanted changesets as merge changesets. You will not be prompted to transplant any ancestors of a merged transplant, and you can merge descendants of them normally instead of transplanting them. If no merges or revisions are provided, :hg:`transplant` will start an interactive changeset browser. If a changeset application fails, you can fix the merge by hand and then resume where you left off by calling :hg:`transplant --continue/-c`. ''' def incwalk(repo, incoming, branches, match=util.always): if not branches: branches = None for node in repo.changelog.nodesbetween(incoming, branches)[0]: if match(node): yield node def transplantwalk(repo, root, branches, match=util.always): if not branches: branches = repo.heads() ancestors = [] for branch in branches: ancestors.append(repo.changelog.ancestor(root, branch)) for node in repo.changelog.nodesbetween(ancestors, branches)[0]: if match(node): yield node def checkopts(opts, revs): if opts.get('continue'): if opts.get('branch') or opts.get('all') or opts.get('merge'): raise util.Abort(_('--continue is incompatible with ' 'branch, all or merge')) return if not (opts.get('source') or revs or opts.get('merge') or opts.get('branch')): raise util.Abort(_('no source URL, branch tag or revision ' 'list provided')) if opts.get('all'): if not opts.get('branch'): raise util.Abort(_('--all requires a branch revision')) if revs: raise util.Abort(_('--all is incompatible with a ' 'revision list')) checkopts(opts, revs) if not opts.get('log'): opts['log'] = ui.config('transplant', 'log') if not opts.get('filter'): opts['filter'] = ui.config('transplant', 'filter') tp = transplanter(ui, repo) p1, p2 = repo.dirstate.parents() if len(repo) > 0 and p1 == revlog.nullid: raise util.Abort(_('no revision checked out')) if not opts.get('continue'): if p2 != revlog.nullid: raise util.Abort(_('outstanding uncommitted merges')) m, a, r, d = repo.status()[:4] if m or a or r or d: raise util.Abort(_('outstanding local changes')) bundle = None source = opts.get('source') if source: sourcerepo = ui.expandpath(source) source = hg.repository(ui, sourcerepo) source, incoming, bundle = bundlerepo.getremotechanges(ui, repo, source, force=True) else: source = repo try: if opts.get('continue'): tp.resume(repo, source, opts) return tf = tp.transplantfilter(repo, source, p1) if opts.get('prune'): prune = [source.lookup(r) for r in cmdutil.revrange(source, opts.get('prune'))] matchfn = lambda x: tf(x) and x not in prune else: matchfn = tf branches = map(source.lookup, opts.get('branch', ())) merges = map(source.lookup, opts.get('merge', ())) revmap = {} if revs: for r in cmdutil.revrange(source, revs): revmap[int(r)] = source.lookup(r) elif opts.get('all') or not merges: if source != repo: alltransplants = incwalk(source, incoming, branches, match=matchfn) else: alltransplants = transplantwalk(source, p1, branches, match=matchfn) if opts.get('all'): revs = alltransplants else: revs, newmerges = browserevs(ui, source, alltransplants, opts) merges.extend(newmerges) for r in revs: revmap[source.changelog.rev(r)] = r for r in merges: revmap[source.changelog.rev(r)] = r tp.apply(repo, source, revmap, merges, opts) finally: if bundle: source.close() os.unlink(bundle)
False) self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default') self.lastbranch = None if os.path.isdir(path) and len(os.listdir(path)) > 0: try: self.repo = hg.repository(self.ui, path) if not self.repo.local(): raise NoRepo( _('%s is not a local Mercurial repository') % path) except error.RepoError, err: ui.traceback() raise NoRepo(err.args[0]) else: try: ui.status(_('initializing destination %s repository\n') % path) self.repo = hg.repository(self.ui, path, create=True) if not self.repo.local(): raise NoRepo( _('%s is not a local Mercurial repository') % path) self.created.append(path) except error.RepoError: ui.traceback() raise NoRepo( _("could not create hg repository %s as sink") % path) self.lock = None self.wlock = None self.filemapmode = False def before(self): self.ui.debug('run hg sink pre-conversion action\n') self.wlock = self.repo.wlock()