cmd = 'fetch %s %s' % (remote, ref)
return self.execute(cmd)
- def hasBranch(self, branch):
- cmd = 'show-ref --verify --quiet "refs/heads/%s"' % branch
+ def hasBranch(self, branch, remote = ''):
+ if remote != '':
+ cmd = 'show-ref --verify --quiet "refs/remotes/%s/%s"' % (remote, branch)
+ else:
+ cmd = 'show-ref --verify --quiet "refs/heads/%s"' % branch
(returncode, stdout, stderr) = self.execute(cmd)
return returncode == 0
proc.wait()
return proc.returncode == 0
+ def pick(self, refs):
+ cmd = 'cherry-pick %s' % refs
+ return self.execute(cmd)
+
def pull(self, remote = '', ref = ''):
cmd = 'pull %s %s' % (remote, ref)
return self.execute(cmd)
cmd = 'push %s%s %s' % (force, toremote, tobranch)
return self.execute(cmd)
+ def reset(self, to, hard = False):
+ mode = ''
+ if hard:
+ mode == '--hard'
+ cmd = 'reset --hard %s' % (to)
+ result = self.execute(cmd)
+ return result[0] == 0
+
def status(self):
return self.execute('status')
identifier = None
path = None
installed = False
- version = {}
- config = {}
+ version = None
+ config = None
_dbo = None
_git = None
def __init__(self, path, identifier = None):
self.path = path
self.identifier = identifier
+ self.version = {}
+ self.config = {}
self._load()
def addConfig(self, name, value):
pass
return self._dbo
+ def generateBranchName(self, issue, suffix='', version=''):
+ """Generates a branch name"""
+ mdl = re.sub(r'MDL(-|_)?', '', issue, flags=re.I)
+ if version == '':
+ version = self.get('branch')
+ branch = C('wording.branchFormat') % (mdl, version)
+ if suffix != None and suffix != '':
+ branch += C('wording.branchSuffixSeparator') + suffix
+ return branch
+
def get(self, param, default = None):
"""Returns a property of this instance"""
info = self.info()
if DB and dbname:
DB.dropdb(dbname)
+ def generateInstanceName(self, version, integration=False, suffix=''):
+ """Creates a name (identifier) from arguments"""
+
+ # Wording version
+ if version == 'master':
+ prefixVersion = C('wording.prefixMaster')
+ else:
+ prefixVersion = version
+
+ # Generating name
+ if integration:
+ name = C('wording.prefixIntegration') + prefixVersion
+ else:
+ name = C('wording.prefixStable') + prefixVersion
+
+ # Append the suffix
+ if suffix != None and suffix != '':
+ name += C('wording.suffixSeparator') + suffix
+
+ return name
+
def get(self, name):
"""Returns an instance defined by its name, or by path"""
# Extracts name from path
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import argparse
+import re
+from lib import config, workplace, moodle, tools
+from lib.tools import debug
+
+C = config.Conf().get
+Wp = workplace.Workplace()
+
+# Arguments
+parser = argparse.ArgumentParser(description="Backports a branch")
+parser.add_argument('-i', '--issue', metavar='issue', required=True, help='the issue to backport')
+parser.add_argument('-s', '--suffix', metavar='suffix', help='the suffix of the branch of this issue')
+parser.add_argument('-r', '--remote', metavar='remote', help='the remote to fetch from. Default is %s.' % C('mineRepo'))
+parser.add_argument('-v', '--versions', metavar='version', required=True, nargs='+', choices=[ str(x) for x in range(13, C('masterBranch')) ] + ['master'], help='versions to backport to')
+parser.add_argument('name', metavar='name', default=None, nargs='?', help='name of the instance to work on')
+args = parser.parse_args()
+
+M = Wp.resolve(args.name)
+if not M:
+ debug('This is not a Moodle instance')
+ sys.exit(1)
+
+remote = args.remote
+if remote == None:
+ remote = C('mineRepo')
+
+
+branch = M.generateBranchName(args.issue, suffix=args.suffix)
+originaltrack = M.get('stablebranch')
+if not M.git().hasBranch(branch):
+ debug('Could not find original branch %s.' % (branch, remote))
+ sys.exit(1)
+if not M.git().hasBranch(branch):
+ debug('Could not find original branch %s.' % (branch, remote))
+ sys.exit(1)
+
+# Begin backport
+for v in args.versions:
+
+ # Gets the instance to cherry-pick to
+ name = Wp.generateInstanceName(v, integration=M.get('integration'))
+ if not Wp.isMoodle(name):
+ debug('Could not find instance %s for version %s' % (name, v))
+ continue
+ M2 = Wp.get(name)
+
+ debug("Preparing cherry-pick of %s/%s in %s" % (remote, branch, name))
+
+ # Fetch the remote to get reference to the branch to backport
+ debug("Fetching remote %s..." % remote)
+ M2.git().fetch(remote)
+
+ # Creates a new branch if necessary
+ newbranch = M2.generateBranchName(args.issue, suffix=args.suffix)
+ track = 'origin/%s' % M2.get('stablebranch')
+ if not M2.git().hasBranch(newbranch):
+ debug('Creating branch %s' % newbranch)
+ if not M2.git().createBranch(newbranch, track=track):
+ debug('Could not create branch %s tracking %s in %s' % (newbranch, track, name))
+ continue
+ M2.git().checkout(newbranch)
+ else:
+ M2.git().checkout(newbranch)
+ debug('Hard reset to %s' % (track))
+ M2.git().reset(to=track, hard=True)
+
+ # Picking the diff origin/MOODLE_23_STABLE..github/MDL-12345-master
+ cherry = 'origin/%s..%s/%s' % (originaltrack, remote, branch)
+ debug('Cherry-picking %s' % (cherry))
+ result = M2.git().pick(cherry)
+ if result[0] != 0:
+ debug('Error while cherry-picking %s/%s in %s.' % (remote, branch, name))
+ debug(result[2])
+ continue
+
+ debug('Instance %s successfully patched!' % name)
+ debug('')
+
+debug('Done.')