def test_push_two_that_modify_same_file(self): ''' Push performs a rebase if two commits touch the same file. This test verifies that code path works. ''' oldlen = test_util.repolen(self.repo) oldtiphash = revsymbol(self.repo, 'default').node() changes = [('gamma', 'gamma', 'sometext')] newhash = self.commitchanges(changes) changes = [ ('gamma', 'gamma', 'sometext\n moretext'), ('delta', 'delta', 'sometext\n moretext'), ] newhash = self.commitchanges(changes) repo = self.repo hg.update(repo, newhash) commands.push(repo.ui, repo) self.assertEqual(test_util.repolen(self.repo), oldlen + 2) # verify that both commits are pushed commit1 = revsymbol(self.repo, 'tip') self.assertEqual(commit1.files(), ['delta', 'gamma']) prefix = 'svn:' + self.repo.svnmeta().uuid self.assertEqual(util.getsvnrev(commit1), prefix + '/branches/the_branch@6') commit2 = commit1.parents()[0] self.assertEqual(commit2.files(), ['gamma']) self.assertEqual(util.getsvnrev(commit2), prefix + '/branches/the_branch@5')
def test_push_two_that_modify_same_file(self): ''' Push performs a rebase if two commits touch the same file. This test verifies that code path works. ''' oldlen = test_util.repolen(self.repo) oldtiphash = self.repo['default'].node() changes = [('gamma', 'gamma', 'sometext')] newhash = self.commitchanges(changes) changes = [('gamma', 'gamma', 'sometext\n moretext'), ('delta', 'delta', 'sometext\n moretext'), ] newhash = self.commitchanges(changes) repo = self.repo hg.update(repo, newhash) commands.push(repo.ui, repo) self.assertEqual(test_util.repolen(self.repo), oldlen + 2) # verify that both commits are pushed commit1 = self.repo['tip'] self.assertEqual(commit1.files(), ['delta', 'gamma']) prefix = 'svn:' + self.repo.svnmeta().uuid self.assertEqual(util.getsvnrev(commit1), prefix + '/branches/the_branch@6') commit2 = commit1.parents()[0] self.assertEqual(commit2.files(), ['gamma']) self.assertEqual(util.getsvnrev(commit2), prefix + '/branches/the_branch@5')
def test_push_without_pushing_children(self): ''' Verify that a push of a nontip node, keeps the tip child on top of the pushed commit. ''' oldlen = test_util.repolen(self.repo) oldtiphash = revsymbol(self.repo, 'default').node() changes = [('gamma', 'gamma', 'sometext')] newhash1 = self.commitchanges(changes) changes = [('delta', 'delta', 'sometext')] newhash2 = self.commitchanges(changes) # push only the first commit repo = self.repo hg.update(repo, newhash1) commands.push(repo.ui, repo) self.assertEqual(test_util.repolen(self.repo), oldlen + 2) # verify that the first commit is pushed, and the second is not commit2 = revsymbol(self.repo, 'tip') self.assertEqual(commit2.files(), [ 'delta', ]) self.assertEqual(util.getsvnrev(commit2), None) commit1 = commit2.parents()[0] self.assertEqual(commit1.files(), [ 'gamma', ]) prefix = 'svn:' + self.repo.svnmeta().uuid self.assertEqual(util.getsvnrev(commit1), prefix + '/branches/the_branch@5')
def test_push_two_revs_different_local_branch(self): def filectxfn(repo, memctx, path): return context.memfilectx(path=path, data=path, islink=False, isexec=False, copied=False) oldtiphash = self.repo['default'].node() ctx = context.memctx(self.repo, (self.repo[0].node(), revlog.nullid, ), 'automated test', ['gamma', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', {'branch': 'localbranch', }) newhash = self.repo.commitctx(ctx) ctx = context.memctx(self.repo, (newhash, revlog.nullid), 'automated test2', ['delta', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', {'branch': 'localbranch', }) newhash = self.repo.commitctx(ctx) repo = self.repo hg.update(repo, newhash) commands.push(repo.ui, repo) self.assertEqual(self.repo['tip'].parents()[0].parents()[0].node(), oldtiphash) self.assertEqual(self.repo['tip'].files(), ['delta', ]) self.assertEqual(self.repo['tip'].manifest().keys(), ['alpha', 'beta', 'gamma', 'delta'])
def test_push_without_pushing_children(self): ''' Verify that a push of a nontip node, keeps the tip child on top of the pushed commit. ''' oldlen = test_util.repolen(self.repo) oldtiphash = self.repo['default'].node() changes = [('gamma', 'gamma', 'sometext')] newhash1 = self.commitchanges(changes) changes = [('delta', 'delta', 'sometext')] newhash2 = self.commitchanges(changes) # push only the first commit repo = self.repo hg.update(repo, newhash1) commands.push(repo.ui, repo) self.assertEqual(test_util.repolen(self.repo), oldlen + 2) # verify that the first commit is pushed, and the second is not commit2 = self.repo['tip'] self.assertEqual(commit2.files(), ['delta', ]) self.assertEqual(util.getsvnrev(commit2), None) commit1 = commit2.parents()[0] self.assertEqual(commit1.files(), ['gamma', ]) prefix = 'svn:' + self.repo.svnmeta().uuid self.assertEqual(util.getsvnrev(commit1), prefix + '/branches/the_branch@5')
def commit(self, path=None, message=None, user=None): from mercurial import commands, hg, ui, error log.debug("Commit to Mercurial repository.") path = path or self.path message = message or self.message user = user or self.user strings = [user.first_name, '<%s>' % user.email] author = ' '.join(filter(None, strings)) # Only if not empty # For some reason default push path is not set properly import configparser, codecs config = configparser.ConfigParser() with codecs.open(os.path.join(path, '.hg/hgrc'), 'r', 'utf-8') as f: try: config.read_file(f) except Exception as e: raise CommitToRepositoryException(str(e)) default_path = config.get('paths', 'default') try: u = ui.ui() u.setconfig('paths', 'default', default_path) repo = hg.repository(u, path) commands.commit(u, repo, message=message, user=author) commands.push(u, repo) log.info(message) except Exception as e: raise CommitToRepositoryException(str(e))
def internal_push_over_svnserve(self, subdir='', commit=True): test_util.load_svndump_fixture(self.repo_path, 'simple_branch.svndump') open(os.path.join(self.repo_path, 'conf', 'svnserve.conf'), 'w').write('[general]\nanon-access=write\n[sasl]\n') self.port = random.randint(socket.IPPORT_USERRESERVED, 65535) self.host = 'localhost' args = ['svnserve', '--daemon', '--foreground', '--listen-port=%d' % self.port, '--listen-host=%s' % self.host, '--root=%s' % self.repo_path] svnserve = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) self.svnserve_pid = svnserve.pid try: time.sleep(2) import shutil shutil.rmtree(self.wc_path) commands.clone(self.ui(), 'svn://%s:%d/%s' % (self.host, self.port, subdir), self.wc_path, noupdate=True) repo = self.repo old_tip = repo['tip'].node() expected_parent = repo['default'].node() def file_callback(repo, memctx, path): if path == 'adding_file': return context.memfilectx(path=path, data='foo', islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, parents=(repo['default'].node(), node.nullid), text='automated test', files=['adding_file'], filectxfn=file_callback, user='******', date='2008-10-07 20:59:48 -0500', extra={'branch': 'default',}) new_hash = repo.commitctx(ctx) if not commit: return # some tests use this test as an extended setup. hg.update(repo, repo['tip'].node()) oldauthor = repo['tip'].user() commands.push(repo.ui, repo) tip = self.repo['tip'] self.assertNotEqual(oldauthor, tip.user()) self.assertNotEqual(tip.node(), old_tip) self.assertEqual(tip.parents()[0].node(), expected_parent) self.assertEqual(tip['adding_file'].data(), 'foo') self.assertEqual(tip.branch(), 'default') # unintended behaviour: self.assertNotEqual('an_author', tip.user()) self.assertEqual('(no author)', tip.user().rsplit('@', 1)[0]) finally: # TODO: use svnserve.kill() in Python >2.5 test_util.kill_process(svnserve)
def push_to_try(ui, repo, server, message=None): nodate = ui.configbool('push-to-try', 'nodate') if not message or 'try:' not in message: ui.status("STOP! A commit message with try syntax is required.\n") return cctx = context.workingctx(repo) status = repo.status() if status.modified + status.added + status.removed: ui.status('The following will be pushed to %s:\n' % server) # TODO: Achieve this by re-using the status call above to avoid the # cost of running it twice. commands.status(ui, repo) preserve_ctx = preservefilectx(cctx) def mk_memfilectx(repo, memctx, path): if path not in status.removed: return preserve_ctx(repo, memctx, path) return None # Invent a temporary commit with our message. ui.status("Creating temporary commit for remote...\n") mctx = context.memctx(repo, repo.dirstate.parents(), message, cctx.files(), mk_memfilectx, date="0 0" if nodate else None) # These messages are expected when we abort our transaction, but aren't # helpful to a user and may be misleading so we surpress them here. filtered_phrases = {_("transaction abort!\n"), _("rollback completed\n")} def filtered_warn(*msgs, **opts): if msgs: filtered = [m for m in msgs if m not in filtered_phrases] if filtered: ui.warn(*filtered, **opts) lock = tr = None try: lock = repo.lock() tr = repo.transaction('push-to-try', report=filtered_warn) m = mctx.commit() # Push to try. commands.push(ui, repo, server, force=True, rev=[repo[m].rev()]) ui.status('push complete\n') # And rollback to the previous state. tr.abort() finally: if tr: tr.release() if lock: lock.release() ui.status("temporary commit removed, repository restored\n")
def function(tree, destpath, opts): try: commands.push(ui, tree.getrepo(ui), destpath, **opts) except Exception, err: ui.warn(_("skipped: %s\n") % err) try: tree.repo.transaction().__del__() except AttributeError: pass
def _cycle(ui, root, commitopts, fetchopts, pushopts): """Run a single 'commit, fetch, push' cycle""" repo = hg.repository(ui, path=root) ui.status("sync: commit working copy changes\n") commands.commit(ui, repo, **commitopts) ui.status("sync: fetch changes from other repository\n") fetch(ui, repo, **fetchopts) ui.status("sync: push local changes to other repository\n") commands.push(ui, repo, **pushopts)
def submit(self, auth, msg): _user_auth = auth logger.debug("Perform submit %s [%s]" % (self.location, msg)) self._send_callback(self.callback_on_action_notify,_('Checking in')) commands.commit(self.repo.ui, self.repo, message=msg.encode('utf-8'), addremove=True, logfile=None, date=None) commands.push(self.repo.ui, self.repo, force=False, rev=None) return 0
def run_try(ui, repo, *args, **opts): """Push the current head to try """ if not opts['build'] or not opts['platform']: raise util.Abort('Both -b and -p are required') # We rely on the try server to validate anything beyond that simple # check above, so let's just blindly go about our business! tryopts = [] tryopts.append('-b') tryopts.extend(opts['build']) tryopts.append('-p') tryopts.extend(opts['platform']) if opts.get('unit'): tryopts.append('-u') tryopts.extend(opts['unit']) if opts.get('talos'): tryopts.append('-t') tryopts.extend(opts['talos']) trymsg = 'try: %s' % (' '.join(tryopts),) if repo[None].dirty(): raise util.Abort('You have outstanding changes') try: strip = extensions.find('strip') except KeyError: ui.warn('strip extension not found, use the following syntax:\n') ui.write('%s\n' % (trymsg,)) return ui.write('setting try selections...\n') # This next bit here is a hack to get an empty commit cwd = os.getcwd() junkfile = tempfile.mktemp(prefix='hgjunk', dir='') os.chdir(repo.root) file(junkfile, 'w').close() commands.add(ui, repo, junkfile) commands.commit(ui, repo, message='add junk file (will be gone)') commands.remove(ui, repo, junkfile) commands.commit(ui, repo, amend=True, message=trymsg, logfile=None) os.chdir(cwd) # Get the revision of our try commit so we can strip it later node = repo[None].p1().hex() ui.write('pushing to try...\n') commands.push(ui, repo, 'try', force=True) # Now we must clean up after ourslves by stripping the try commit strip.stripcmd(ui, repo, node, rev=[], no_backup=True)
def commit(self, message='',stdout=None): """ Commit all changes into hg repository Note that local repo must be setup for access without key, and commit must not create new head, because push is not forced """ u, r = self._hg(stdout) commands.addremove(u, r) commands.commit(u, r, message=message) commands.push(u, r) del u, r
def pushall(ui, repo, **opts): """The Publishall core function. Makes your life easier.""" repos = ui.configitems('paths') if not repos: ui.warn("No paths defined in your hgrc. Pushall aborted.\n") ui.status("%s paths found\n" % len(repos)) for path in repos: ui.status("* pushing to %s\n" % path[0]) try: commands.push(ui, repo, path[1], **opts) except Exception, e: print e
def test_update_after_push(self): repo = self.repo ui = repo.ui ui.setconfig( 'hooks', 'debug-hgsubversion-between-push-and-pull-for-tests', lambda ui, repo, hooktype: self.add_svn_rev( self.repo_path, {'trunk/racey_file': 'race conditions suck'})) self.test_push_to_branch(push=False) commands.push(ui, repo) newctx = revsymbol(self.repo, '.') self.assertNotEqual(newctx.node(), revsymbol(self.repo, 'tip').node()) self.assertEqual(newctx['adding_file'].data(), 'foo') self.assertEqual(newctx.branch(), 'the_branch')
def test_update_after_push(self): repo = self.repo ui = repo.ui ui.setconfig('hooks', 'debug-hgsubversion-between-push-and-pull-for-tests', lambda ui, repo, hooktype: self.add_svn_rev( self.repo_path, {'trunk/racey_file': 'race conditions suck'})) self.test_push_to_branch(push=False) commands.push(ui, repo) newctx = self.repo['.'] self.assertNotEqual(newctx.node(), self.repo['tip'].node()) self.assertEqual(newctx['adding_file'].data(), 'foo') self.assertEqual(newctx.branch(), 'the_branch')
def pushrevisions(self, stupid=False, expected_extra_back=0): before = len(self.repo) self.repo.ui.setconfig("hgsubversion", "stupid", str(stupid)) res = commands.push(self.repo.ui, self.repo) after = len(self.repo) self.assertEqual(expected_extra_back, after - before) return res
def pushrevisions(self, expected_extra_back=0): before = repolen(self.repo) self.repo.ui.setconfig('hgsubversion', 'stupid', str(self.stupid)) res = commands.push(self.repo.ui, self.repo) after = repolen(self.repo) self.assertEqual(expected_extra_back, after - before) return res
def post_push(ui, repo, pats, opts, *args, **kwargs): dest = pats and pats[0] dest = ui.expandpath(dest or 'default-push', dest or 'default') if 'bitbucket.org' in dest: github = ui.config('paths', 'github') if github: return commands.push(ui, repo, github, **opts) ui.warn('no github mirror!?\n')
def test_push_two_revs_different_local_branch(self): def filectxfn(repo, memctx, path): return compathacks.makememfilectx(repo, memctx=memctx, path=path, data=path, islink=False, isexec=False, copied=False) oldtiphash = revsymbol(self.repo, 'default').node() lr = self.repo ctx = context.memctx(lr, ( lr[0].node(), revlog.nullid, ), 'automated test', [ 'gamma', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', { 'branch': 'localbranch', }) newhash = lr.commitctx(ctx) ctx = context.memctx(lr, (newhash, revlog.nullid), 'automated test2', [ 'delta', ], filectxfn, 'testy', '2008-12-21 16:32:00 -0500', { 'branch': 'localbranch', }) newhash = lr.commitctx(ctx) repo = self.repo hg.update(repo, newhash) commands.push(repo.ui, repo) self.assertEqual( revsymbol(self.repo, 'tip').parents()[0].parents()[0].node(), oldtiphash) self.assertEqual(revsymbol(self.repo, 'tip').files(), [ 'delta', ]) self.assertEqual(sorted(revsymbol(self.repo, 'tip').manifest().keys()), ['alpha', 'beta', 'delta', 'gamma'])
def create_repo(self, dest, ui): vct = 'http://hg.mozilla.org/hgcustom/version-control-tools' commands.clone(ui, vct, dest=os.path.join(dest, 'vct.hg')) ui.setconfig('extensions', 'pushlog', os.path.join(dest, 'vct.hg/hgext/pushlog')) srcdir = os.path.join(dest, 'test') destdir = os.path.join(dest, 'testwork') if not os.path.exists(srcdir): os.makedirs(srcdir) commands.init(ui, srcdir) commands.init(ui, destdir) repo = hg.repository(ui, destdir) myfile1 = os.path.join(destdir, 'myfile1') myfile2 = os.path.join(destdir, 'myfile2') for i in range(5): with open(myfile1, 'a') as In: In.write(str(i)) with open(myfile2, 'a') as In: In.write(str(i)) commands.commit(ui, repo, myfile1, myfile2, message='message' + str(i), user='******', addremove=True) commands.push(ui, repo, dest=srcdir) time.sleep(1.01) return srcdir
def post_push(ui, repo, pats, opts, *args, **kwargs): """ Push to Github after pushing to BitBucket """ dest = pats and pats[0] dest = ui.expandpath(dest or 'default-push', dest or 'default') if 'bitbucket.org' in dest: dest = dest.rstrip('/') reponame = dest.split('/')[-1] username = dest.split('/')[-2] github = 'git+ssh://[email protected]/{username}/{reponame}.git' github = github.format(username=username, reponame=reponame) # Move "master" bookmark to point to tip commands.bookmark(ui, repo, mark='master', rev='tip', force=True) return commands.push(ui, repo, github, **opts)
def push_reviewed(self): push_result = commands.push(self.ui, self.repo, self.rbrepo.path, new_branch=True) self.ui.status(_("Push result %d\n") % push_result) if (push_result != 0): if (push_result == 1): self.ui.status( _("Nothing to push. Push command returned: %d\n") % push_result) else: self.ui.status( _("Cannot push. Please resubmit review request. Push command returned: %d\n" ) % push_result) raise util.Abort( "Cannot push. Please resubmit review request. Push command returned: %d" % push_result)
def pushtree(ui, repo, tree=None, rev=None, **opts): """Push changesets to a Mozilla repository. If only the tree argument is defined, we will attempt to push the current tip to the repository specified. This may fail due to pushed mq patches, local changes, etc. Please note we only attempt to push the current tip and it's ancestors, not all changesets not in the remote repository. This is different from the default behavior of |hg push| and is the distinguishing difference from that command. If you would like to push a non-active head, specify it with -r REV. For example, if you are currently on mozilla-central but wish to push inbound to mozilla-inbound, run `hg pushtree -r inbound/default inbound`. """ if not tree: raise util.Abort(_('A tree must be specified.')) tree, uri = resolve_trees_to_uris([tree], write_access=True)[0] if not uri: raise util.Abort("Don't know about tree: %s" % tree) return push(ui, repo, rev=[rev], dest=uri)
def push(self, wire, revisions, dest_path, hooks=True, push_branches=False): repo = self._factory.repo(wire) baseui = self._factory._create_config(wire['config'], hooks=hooks) commands.push(baseui, repo, dest=dest_path, rev=revisions, new_branch=push_branches)
def pushq(ui, repo, dest=None, **opts): """Runs the hg push command for the patch queue repo of a regular repo""" q = qrepo(ui, repo) commands.push(ui, hg.repository(q.path), dest, **opts)
def tclose(ui, repo, *args, **opts): """ close the current topic branch and push to the central repository """ mustBeTopicRepo(repo) # Sanity check if not isClean(ui, repo): return 1 if args: branches = args else: if not onTopicBranch(ui, repo): return 1 branches = [repo.dirstate.branch()] if 'tmenu' in opts: if ui.prompt("Branch '%s': close it?" % branches[0]).upper() != 'Y': return 1 opts = { 'nopull':False, 'nopush':False } pulled = False # only pull once for branch in branches: # Pull new changes from the central repo to avoid multiple-heads problem if not opts['nopull'] and not pulled: if tryCommand(ui, "pull", lambda:commands.pull(ui, repo, **opts) >= 2): return 1 pulled = True # Can't close already closed branches, nor any of the special branches if not repo.branchheads(branch) or branch in repo.topicSpecialBranches: ui.warn("Error: %s is not an open topic branch\n" % branch) return 1 # Now update to the head of the branch being closed if repo.dirstate.parents()[0] not in repo.branchheads(branch): if tryCommand(ui, "update %s" % quoteBranch(branch), lambda:commands.update(ui, repo, node=branch)): return 1 # Unlike a normal hg commit, if no text is specified we supply a reasonable default. branch = repo.dirstate.branch() text = opts.get('message') if text is None: text = "Closing %s" % branch # Close it if tryCommand(ui, "commit --close-branch", lambda:repo.commit(text, extra = {'close':'True'}) is None): return 1 # Aditionally, for this to not be considered a "head" it has to have a # child commit. So we have to merge into prod. First, update. # if tryCommand(ui, "update %s" % repo.topicProdBranch, lambda:commands.update(ui, repo, node=repo.topicProdBranch)): return 1 # Now merge, ignoring all conflicts. mergeOpts = copy.deepcopy(opts) mergeOpts['tool'] = "internal:fail" mergeOpts['noninteractive'] = True # Ignore return value... ok if merge fails tryCommand(ui, "merge -r %s" % quoteBranch(branch), lambda:commands.merge(ui, repo, node=branch, **mergeOpts), repo = repo) # Revert all files to prod (regardless of what happened on the branch) revertOpts = copy.deepcopy(opts) revertOpts['all'] = True revertOpts['rev'] = "." if tryCommand(ui, "revert -a -r .", lambda:commands.revert(ui, repo, **revertOpts), repo = repo): return 1 # Were there any merge conflicts? resolveOpts = copy.deepcopy(opts) resolveOpts['list'] = True if tryCommand(ui, "resolve -l", lambda:commands.resolve(ui, repo, **resolveOpts), repo = repo): return 1 # Anything that had a merge conflict, mark it resolved (by the revert) if ui.lastTryCommandOutput != '': resolveOpts = copy.deepcopy(opts) resolveOpts['all'] = True resolveOpts['mark'] = True if tryCommand(ui, "resolve -a -m", lambda:commands.resolve(ui, repo, **resolveOpts), repo = repo): return 1 # Commit the merge if tryCommand(ui, "commit", lambda:repo.commit(text) is None): return 1 # And push. if not opts['nopush']: pushOpts = copy.deepcopy(opts) if 'message' in pushOpts: del pushOpts['message'] pushOpts['force'] = True nameSet = set() for name, path in ui.configitems("paths"): nameSet.add(name) if tryCommand(ui, "push -f -b %s -b %s default" % (quoteBranch(branch), repo.topicProdBranch), lambda:commands.push(ui, repo, branch=(branch,repo.topicProdBranch), **pushOpts), repo=repo) > 1: return 1 if "dev" in nameSet: if tryCommand(ui, "push -f -b %s -b %s dev" % (quoteBranch(branch), repo.topicProdBranch), lambda:commands.push(ui, repo, branch=(branch,repo.topicProdBranch), dest="dev", **pushOpts), repo=repo) > 1: return 1 if "stage" in nameSet: if tryCommand(ui, "push -f -b %s -b %s stage" % (quoteBranch(branch), repo.topicProdBranch), lambda:commands.push(ui, repo, branch=(branch,repo.topicProdBranch), dest="stage", **pushOpts), repo=repo) > 1: return 1 if "prod" in nameSet: if tryCommand(ui, "push -f -b %s -b %s prod" % (quoteBranch(branch), repo.topicProdBranch), lambda:commands.push(ui, repo, branch=(branch,repo.topicProdBranch), dest="prod", **pushOpts), repo=repo) > 1: return 1 ui.status("Done.\n")
def sync_hg(hg_interface, local_repo_path, remote_repo_path=None): if remote_repo_path and not os.path.exists(remote_repo_path): #remote_repo_path does not exist #could sync with default source (e.g. github) here remote_repo_path = None #first pull down any changes that may exist on remote repo = hg.repository(hg_interface, local_repo_path) hg_interface.pushbuffer() commands.pull(hg_interface, repo, remote_repo_path) result = hg_interface.popbuffer() #hg_interface won't catch all of the output of a pull #if there were changes in the pull, it gets the line: #(run 'hg update' to get a working copy) # #if not, only has: # pulling from /media/CHARLES/charles #keep track if we find something that changes #so that we can pause at the end #otherwise we should just move on automatically changes = False print("%s" % result) lines = result.splitlines() if len(lines) > 1: #changes = True need_update = False for line in lines: #sometimes might be only 2 lines #sometimes might be 3: #['pulling from /media/charles/CHARLES/moments', 'updating bookmark master', "(run 'hg update' to get a working copy)"] if re.search('hg update', line): print("updating") hg_interface.pushbuffer() commands.update(hg_interface, repo) result = hg_interface.popbuffer() print("%s" % result) response = hg_interface.prompt("everything ok? (ctl-c to exit)", default='y') print("moving on then...") need_update = True if not need_update: #if we didn't update, the lines must be telling us #something else needs to happen... #must be a merge: print(lines) print("merge detected, all yours:") print("cd %s" % local_repo_path) exit() #at this point all changes from remote media should be applied locally #now we should check if we have any changes here: hg_interface.pushbuffer() commands.status(hg_interface, repo) result = hg_interface.popbuffer() if result: print("looks like there are some local changes:") print(result) changes = True new_files = False for line in result.splitlines(): if line.startswith('?'): new_files = True if new_files: print("new files found") response = hg_interface.prompt("would you like to add the new files?", default='y') if response == 'y': commands.add(hg_interface, repo) response = hg_interface.prompt("log (ctl-c to exit):", default='') commands.commit(hg_interface, repo, message=response) #push changes: print("hg push %s" % remote_repo_path) commands.push(hg_interface, repo, remote_repo_path) lines = result.splitlines() if len(lines) > 1: changes = True #only do this if remote repo exists: if remote_repo_path: #on remote repo, remote_repo = hg.repository(hg_interface, remote_repo_path) #update print("updating remote:") hg_interface.pushbuffer() commands.update(hg_interface, remote_repo) result = hg_interface.popbuffer() print("%s" % result) #show remote status commands.status(hg_interface, remote_repo) else: print("skipping remote update: %s" % remote_repo_path) #pass if changes: response = hg_interface.prompt("everything ok? (ctl-c to exit)", default='y')
def push_to_try(ui, repo, server, message=None): nodate = ui.configbool(b'push-to-try', b'nodate') if not server: if b'try' in ui.paths: server = b'try' else: server = b'ssh://hg.mozilla.org/try' if not message: ui.status(b"STOP! A commit message is required.\n") return cctx = context.workingctx(repo) if b'try_task_config.json' not in cctx and b'try:' not in message: ui.status(b"STOP! Either try_task_config.json must be added or the commit " b"message must contain try syntax.\n") return if b'try_task_config.json' in cctx: data = repo.wvfs.tryread(b'try_task_config.json') try: # data could be an empty string if tryread failed, which will # produce a ValueError here. data = json.loads(data) except ValueError as e: ui.status(b"Error reading try_task_config.json: could not decode as JSON\n") return # Invent a temporary commit with our message. ui.status(b"Creating temporary commit for remote...\n") status = repo.status() if status.modified + status.added + status.removed: # TODO: Achieve this by re-using the status call above to avoid the # cost of running it twice. commands.status(ui, repo) preserve_ctx = preservefilectx(cctx) def mk_memfilectx(repo, memctx, path): if path not in status.removed: return preserve_ctx(repo, memctx, path) return None mctx = context.memctx(repo, repo.dirstate.parents(), message, cctx.files(), mk_memfilectx, date=b"0 0" if nodate else None) # These messages are expected when we abort our transaction, but aren't # helpful to a user and may be misleading so we surpress them here. filtered_phrases = {_(b"transaction abort!\n"), _(b"rollback completed\n")} def filtered_warn(*msgs, **opts): if msgs: filtered = [m for m in msgs if m not in filtered_phrases] if filtered: ui.warn(*filtered, **opts) lock = tr = None try: lock = repo.lock() tr = repo.transaction(b'push-to-try', report=filtered_warn) m = mctx.commit() # Push to try. commands.push(ui, repo, server, force=True, rev=[repo[m].rev()]) ui.status(b'push complete\n') # And rollback to the previous state. tr.abort() finally: if tr: tr.release() if lock: lock.release() ui.status(b"temporary commit removed, repository restored\n")
def trychooser_command(ui, repo, *args, **opts): from mercurial import extensions, commands, util try: from mercurial import phases except: pass # older version with no phases if not sys.stdin.isatty(): # rxvt on Windows disables ui.prompt, but installing our own seems to work def ui_prompt(prompt, default): sys.stdout.write(prompt) sys.stdout.write(" ") sys.stdout.flush() response = sys.stdin.readline().rstrip() if response == '': return default return response ui.prompt = ui_prompt display_only = opts['nopush'] try: mq = extensions.find('mq') except KeyError: display_only = True ui.warn("Warning: mq hasn't been found, this is going to print the syntax only.\n") if repo[None].dirty(): raise util.Abort("local changes found, refresh first") if opts.get('message'): msg = opts.get('message') if msg.find('try:') == -1: msg = 'try: ' + msg elif not menu or opts['question']: question(ui, headers, data['questions']) msg = "try: " + generateTrySyntax(headers, data['syntax']) else: syntaxGenerator = lambda h: generateTrySyntax(h, data['syntax']) msg = menu({ "syntaxGenerator": syntaxGenerator }, headers, ui) if opts['desc']: msg = opts['desc'] + ", " + msg #bugnum = suggest_results_bug(ui, repo) #if bugnum: # msg += ' --post-to-bugzilla Bug %s' % bugnum if display_only: ui.write("Use the following try message:\n%s\n" % msg) return ui.write("The following try message is going to be used:\n%s\n" % msg) ui.write("Create the trychooser mq entry...\n") mq.new(ui, repo, 'trychooser', message=msg) ui.write("Push to try server...\n") try: commands.push(ui, repo, opts['server'], force=True, rev=[repo['.'].rev()]) # try server is running an old mercurial version that doesn't support # phases, so is assumed to be publishing. Undo the effects. lock = repo.lock() try: draft = phases.phasenames.index('draft') phases.retractboundary(repo, draft, [ repo.mq.applied[0].node ]) except: pass # probably running under pre-phase version finally: lock.release() finally: mq.pop(ui, repo) mq.delete(ui, repo, 'trychooser')
def tpush(ui, repo, *args, **opts): """ Push current branch to dev, stage, or production (and merge if prod). """ mustBeTopicRepo(repo) # Sanity checks if not(onTopicBranch(ui, repo) and isClean(ui, repo)): return 1 topicBranch = repo.dirstate.branch() # If called from the menu, allow user to choose target branch if 'tmenu' in opts: resp = ui.prompt("Push to which machine(es) [dsp], or blank to just share:", "") for c in resp: if c.upper() not in ('D', 'S', 'P'): ui.warn("Unknown push target '%s'\n" % resp) return 1 args = [] resp = resp.upper() if 'D' in resp: args.append("dev") if 'S' in resp: args.append("stage") if 'P' in resp: args.append(repo.topicProdBranch) opts = { 'nopull':False, 'nocommit':False, 'message':None } # If pushing to prod, get extra verification from the user nameSet = set() for name, path in ui.configitems("paths"): nameSet.add(name) if repo.topicProdBranch in args: if not repo.topicProdBranch in nameSet: raise util.Abort("Error: '%s' server path not configured in .hg/hgrc." % repo.topicProdBranch) res = ui.prompt("This will push to production. Okay to proceed?") if res.lower() != "y" and res.lower() != "yes": raise util.Abort("Ok.") if "stage" in nameSet and not "stage" in args: args = ["stage"] + list(args) if "dev" in nameSet and not "dev" in args: args = ["dev"] + list(args) # We'll use a special set of options for hg push commands pushOpts = copy.deepcopy(opts) pushOpts['new_branch'] = True # allow pushing new branches # If no branch was specified, just share the current branch if len(args) < 1: if tryCommand(ui, "push --new-branch -b %s" % quoteBranch(topicBranch), lambda:commands.push(ui, repo, branch=(topicBranch,), **pushOpts), repo=repo): return 1 ui.status("Done.\n") return 0 commitStop = False # note if we get to that step # If pushing to prod, merge changes from this branch into the prod branch. alreadyMerged = [p.branch() for p in repo[None].parents()[0].children()] if repo.topicProdBranch in args and not repo.topicProdBranch in alreadyMerged: # Freshen with recent changes from prod freshenOpts = copy.deepcopy(opts) freshenOpts['terse'] = True # avoid printing "Done." at end if tfreshen(ui, repo, *args, **freshenOpts): return 1 # Update to the prod if tryCommand(ui, "update %s" % quoteBranch(repo.topicProdBranch), lambda:hg.update(repo, revsymbol(repo, repo.topicProdBranch))): return 1 # Merge from the topic branch if doMerge(ui, repo, topicBranch): return 1 # Stop if requested. if opts['nocommit']: ui.status("\nStopping before commit as requested.\n") commitStop = True return 0 # Unlike a normal hg commit, if no text is specified we supply a reasonable default. text = opts.get('message') if text is None: text = "Merge %s to %s" % (topicBranch, repo.topicProdBranch) # Commit the merge. if tryCommand(ui, "commit", lambda:repo.commit(text) is None): return 1 # And return to the original topic branch if repo.dirstate.branch() != topicBranch: if tryCommand(ui, "update %s" % quoteBranch(topicBranch), lambda:hg.update(repo, revsymbol(repo, topicBranch))): return 1 # For prod, push to the central repo as well as the servers. if repo.topicProdBranch in args: try: if tryCommand(ui, "push --new-branch -b %s default" % repo.topicProdBranch, lambda:commands.push(ui, repo, branch=(repo.topicProdBranch,), **pushOpts), repo=repo) > 1: return 1 except Exception, e: if "Not allowed to create multiple heads in the same branch" in ui.lastTryCommandOutput \ or "push creates new remote head" in ui.lastTryCommandOutput \ or "push creates new remote head" in str(e): print("Attempting to repair multiple heads.") if repo.dirstate.branch() != topicBranch: if tryCommand(ui, "update %s" % quoteBranch(topicBranch), lambda:hg.update(repo, revsymbol(repo, topicBranch))) > 1: return 1 topicDir = os.path.dirname(__file__) repairScript = os.path.join(topicDir, "repair.py") subprocess.check_call([repairScript, repo.topicProdBranch, 'default']) else: raise
def push(self, dest=None, rev=None): self.invalidate_cache() commands.push(self.ui, self.repo, dest, rev=rev)
def internal_push_over_svnserve(self, subdir='', commit=True): repo_path = self.load_svndump('simple_branch.svndump') open(os.path.join(repo_path, 'conf', 'svnserve.conf'), 'w').write('[general]\nanon-access=write\n[sasl]\n') self.port = random.randint(socket.IPPORT_USERRESERVED, 65535) self.host = socket.gethostname() # The `svnserve` binary appears to use the obsolete `gethostbyname(3)` # function, which always returns an IPv4 address, even on hosts that # support and expect IPv6. As a workaround, resolve the hostname # within the test harness with `getaddrinfo(3)` to ensure that the # client and server both use the same IPv4 or IPv6 address. try: addrinfo = socket.getaddrinfo(self.host, self.port) except socket.gaierror as e: # gethostname() can give a hostname that doesn't # resolve. Seems bad, but let's fall back to `localhost` in # that case and hope for the best. self.host = 'localhost' addrinfo = socket.getaddrinfo(self.host, self.port) # On macOS svn seems to have issues with IPv6 at least some of # the time, so try and bias towards IPv4. This works because # AF_INET is less than AF_INET6 on all platforms I've # checked. Hopefully any platform where that's not true will # be fine with IPv6 all the time. :) selected = sorted(addrinfo)[0] self.host = selected[4][0] # If we're connecting via IPv6 the need to put brackets around the # hostname in the URL. ipv6 = selected[0] == socket.AF_INET6 # Ditch any interface information since that's not helpful in # a URL if ipv6 and ':' in self.host and '%' in self.host: self.host = self.host.rsplit('%', 1)[0] urlfmt = 'svn://[%s]:%d/%s' if ipv6 else 'svn://%s:%d/%s' args = [ 'svnserve', '--daemon', '--foreground', '--listen-port=%d' % self.port, '--listen-host=%s' % self.host, '--root=%s' % repo_path ] svnserve = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) self.svnserve_pid = svnserve.pid try: time.sleep(2) import shutil shutil.rmtree(self.wc_path) commands.clone(self.ui(), urlfmt % (self.host, self.port, subdir), self.wc_path, noupdate=True) repo = self.repo old_tip = revsymbol(repo, 'tip').node() expected_parent = revsymbol(repo, 'default').node() def file_callback(repo, memctx, path): if path == 'adding_file': return compathacks.makememfilectx(repo, memctx=memctx, path=path, data='foo', islink=False, isexec=False, copied=False) raise IOError(errno.EINVAL, 'Invalid operation: ' + path) ctx = context.memctx(repo, parents=(revsymbol(repo, 'default').node(), node.nullid), text='automated test', files=['adding_file'], filectxfn=file_callback, user='******', date='2008-10-07 20:59:48 -0500', extra={ 'branch': 'default', }) new_hash = repo.commitctx(ctx) if not commit: return # some tests use this test as an extended setup. hg.update(repo, revsymbol(repo, 'tip').node()) oldauthor = revsymbol(repo, 'tip').user() commands.push(repo.ui, repo) tip = revsymbol(self.repo, 'tip') self.assertNotEqual(oldauthor, tip.user()) self.assertNotEqual(tip.node(), old_tip) self.assertEqual(tip.parents()[0].node(), expected_parent) self.assertEqual(tip['adding_file'].data(), 'foo') self.assertEqual(tip.branch(), 'default') # unintended behaviour: self.assertNotEqual('an_author', tip.user()) self.assertEqual('(no author)', tip.user().rsplit('@', 1)[0]) finally: if sys.version_info >= (2, 6): svnserve.kill() else: test_util.kill_process(svnserve)
else: raise # Now push to each server branchToPush = repo.topicProdBranch if repo.topicProdBranch in args else topicBranch for mergeTo in args: # Cannot merge to default, or to a topic branch if mergeTo in repo.topicIgnoreBranches and mergeTo not in ('dev', 'stage'): ui.warn("Cannot merge to '%s' branch.\n" % mergeTo) return 1 # Push to the correct server if mergeTo in nameSet: if tryCommand(ui, "push --new-branch -b %s %s" % (branchToPush, mergeTo), lambda:commands.push(ui, repo, branch=(branchToPush,), dest=mergeTo, **pushOpts), repo=repo) > 1: # 1 means nothing to push, which is ok return 1 else: print("Note: No path for server '%s' is configured in .hg/hgrc." % mergeTo) # Record the push so we can display the right branch status later readTopicState(repo) if not topicBranch in topicState: topicState[topicBranch] = {} topicState[topicBranch][mergeTo] = repo[repo.dirstate.parents()[0]].hex() writeTopicState(repo) ui.status("Done.\n") ###############################################################################