#!/usr/bin/python #coding=utf-8 ## Filename: rrdt # # rrdt: Remote repositories downloading tools # # Copyright & copy; 2010 by Huyuke. Oversea BU (R&D) of Gionee Communication Equipment Co., Ltd. # # Change log # 2011-3-17 Huyuke # change behaviour of update: for auto make script, and add -f, --force option # 2011-3-3 Huyuke # change behaviour of forall: go on if a command failed # 2011-2-23 Huyuke # change update command for automatically make # 2011-1-30 Huyuke # change update command to synchronize copy files # 2011-1-7 Huyuke # fix fatal error while network is disconnected # 2010-12-30 Huyuke # change behaviour of update: if found a new repository, clone it # 2010-12-29 Huyuke # add reqreview command to request code review # 2010-12-29 Huyuke # change help information to chinese # 2010-12-24 Huyuke # change behaviour of checkout: select project instead of input option # 2010-12-21 Huyuke # implement forall command # 2010-11-9 Huyuke # implement initrepos command # 2010-11-6 Huyuke # implement createrepos command # 2010-11-5 Huyuke # implement checkout function on server # optimize value returned by this script, return 0 if success, else 1 # optimize error message, all error message output into sys.stderr # 2010-11-4 Huyuke # change the prompt of merge and rebase operation # 2010-11-2 Huyuke # change behavior of checkout, check and set the user name and email in global git configuration # add color for print out message # 2010-11-1 Huyuke # implement commit command # 2010-10-29 Huyuke # implement update command # 2010-10-26 Huyuke # implement help command # 2010-10-25 Huyuke # implement option of checkout # 2010-10-21 Huyuke # initial version, implement checkout command # ############################################################################### import sys import os import subprocess import xml.parsers.expat import optparse import socket import fcntl import struct import string import getopt ############################# Constants variables ############################## VERSION = '0.8.7' # out git command GIT = 'git' # minimum supported git version MIN_GIT_VERSION = (1, 5, 4) # name of rrdt's private directory RRDT_DIR = '.rrdt' # special manifest repository S_manifests = 'manifests' # IP of server with git repositories SERVER_IP = '192.168.110.94' # user for git on server GIT_USER = 'git' # directory if git repository GIT_DIR = '.git' # root path of repositories, used on local REPOS_URL_ROOT_LOCAL = '/home/git/repositories/' # root path of repositories, used on client REPOS_URL_ROOT_GIT_SERVER = '[email protected]:' # url of manifest in host # REMOTE_MANIFEST_URL = '[email protected]:android/platform/manifests.git' # path of manifest on server MANIFEST_PATH = 'manifests.git' # default remote DEFAULT_REMOTE = 'origin' # default working branch DEFAULT_WORKING_BRANCH = 'my' # default project name DEFAULT_PROJECT='android' # name of manifest DEFAULT_MANIFEST_XML = 'manifest.xml' # name of project list file PROJECT_CONF = 'project_conf.xml' # postfix of review branch REVIEW_BRANCH_POSTFIX = '-review' # Administrator of repos _admlist = { 'linansong' : '[email protected]', 'huyuke' : '[email protected]', } ############################# Global variables ################################# PROJECT_ROOT = None ON_SERVER = False REMOTE_MANIFEST_URL = None REMOTE_REPOS_URL = None LOCAL_IP = None ############################# class manifestDoc start. ######################### class manifestDoc(): def __init__(self, filename): self.manifestFile = filename self._Unload() self._Load() def _Unload(self): self._projects = [] self._remote = None self._default = None self._depth = 0 self._copyfile = [] self._baseNode = [] def _Load(self): _parser = xml.parsers.expat.ParserCreate() _parser.StartElementHandler = self._Start_element _parser.EndElementHandler = self._End_element _parser.returns_unicode = False f = file(self.manifestFile) _parser.ParseFile(f) f.close() def _Start_element(self, name, attrs): if name == "project": self._projects.append(attrs.copy()) elif name == "remote": self._remote = attrs.copy() elif name == "default": self._default = attrs.copy() elif name == "copyfile": copyfile = attrs.copy() self._Add_pardir(name, copyfile) self._copyfile.append(copyfile) # Save base Node if self._depth > 0: # Ignore manifest node self._baseNode.append(attrs.copy()) self._depth += 1 def _End_element(self, name): self._depth -= 1 # Pop current element from base Node list count = len(self._baseNode) if count > 0: self._baseNode.pop(count - 1) def _Add_pardir(self, name, attrs): if not len(self._baseNode) > 0: return if name == "copyfile": src = attrs['src'] dest = attrs['dest'] count = len(self._baseNode) for index in range(0, count): i = count - index - 1 if self._baseNode[i].has_key('path'): src = os.path.join(self._baseNode[i]['path'], src) attrs['src'] = src def projects(self): return self._projects def remote(self): return self._remote def default(self): return self._default def copyfile(self): return self._copyfile ############################# class manifestDoc end. ########################### #=========================== function definition start ========================# def _Initial(): global ON_SERVER global REMOTE_MANIFEST_URL global REMOTE_REPOS_URL global LOCAL_IP s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect((SERVER_IP,80)) except: print >>sys.stderr, "/033[00;33m/nWarning! cannot connect to server/033[00m" return LOCAL_IP = s.getsockname()[0] ON_SERVER = (SERVER_IP == LOCAL_IP) s.close() REMOTE_MANIFEST_URL = REPOS_URL_ROOT_GIT_SERVER+MANIFEST_PATH REMOTE_REPOS_URL = REPOS_URL_ROOT_GIT_SERVER #if ON_SERVER: #print >>sys.stdout, "You are working on server" #REMOTE_MANIFEST_URL = REPOS_URL_ROOT_LOCAL+MANIFEST_PATH #REMOTE_REPOS_URL = REPOS_URL_ROOT_LOCAL #else: #print >>sys.stdout, "You are working on client" #REMOTE_MANIFEST_URL = REPOS_URL_ROOT_GIT_SERVER+MANIFEST_PATH #REMOTE_REPOS_URL = REPOS_URL_ROOT_GIT_SERVER #=============================================================================# def _GetDefaultBranchConf(): manifest_repo_dir = os.path.join(PROJECT_ROOT, os.path.join(RRDT_DIR, S_manifests)) if not os.path.isdir(os.path.join(manifest_repo_dir, GIT_DIR)): print >>sys.stderr, "Can not find mainifests repository" sys.exit(1) # Get default remote cmd = [GIT, 'config', '--get', 'branch.default.remote'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd = manifest_repo_dir) bdr = proc.stdout.read().strip() proc.stdout.close() proc.wait() # Get default merge branch cmd = [GIT, 'config', '--get', 'branch.default.merge'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd = manifest_repo_dir) bdm = proc.stdout.read().strip() proc.stdout.close() proc.wait() if not bdm or bdr != DEFAULT_REMOTE: print >>sys.stderr, "ERROR: can not get expected default branch" sys.exit(1) manifest = os.path.join(PROJECT_ROOT, os.path.join(RRDT_DIR, DEFAULT_MANIFEST_XML)) if not os.path.isfile(manifest): print >>sys.stderr, "Can not find mainifest of this project" sys.exit(1) return manifest, bdr, bdm #=============================================================================# def _GetReviewBranch(): cmd = [GIT, 'config', '--get', '--global', 'user.name'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd = os.getcwd()) un = proc.stdout.read().strip() proc.stdout.close() proc.wait() if not un: un = 'unkown-user' branch = un + REVIEW_BRANCH_POSTFIX return branch #=============================================================================# def _CommitReview(repo_path, remote, branch, review_branch): if not remote: remote = DEFAULT_REMOTE repo_abspath = os.path.abspath(repo_path) local_branch = 'refs/heads/'+branch remote_branch = 'refs/remotes/'+remote+'/'+branch if not os.path.isdir(os.path.join(repo_abspath, GIT_DIR)): print >>sys.stderr, "This repository(%s) not existed" % repo_abspath sys.exit(1) # Update objects and refs from remote repository, cmd = [GIT, 'fetch', remote] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed in module %s" % (cmd, repo_abspath) sys.exit(1) # Delete the branches which ware removed in remote but still locally available in "remotes/<name>" cmd = [GIT, 'remote', 'prune', remote] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed in module %s" % (cmd, repo_abspath) sys.exit(1) # Find as good common ancestors as possible for a merge# cmd = [GIT, 'merge-base', local_branch, remote_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) merge_base = proc.stdout.read().strip() proc.stdout.close() proc.wait() cmd = [GIT, 'rev-parse', local_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) local_rev = proc.stdout.read().strip() proc.stdout.close() proc.wait() cmd = [GIT, 'rev-parse', remote_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) remote_rev = proc.stdout.read().strip() proc.stdout.close() proc.wait() if local_rev == merge_base: print >>sys.stdout, "This branch(%s) has no any new commit" % local_branch print "Are you sure that you has done 'git add' and 'git commit' before push a commit to remote?" return True elif remote_rev != merge_base: print >>sys.stdout, "/033[01;33mYour branch forked from an old version of upstream branch." print >>sys.stdout, "You should update[rrdt update] it before do commit./033[00m" return False else: cmd = [GIT, 'push', remote, branch+':'+review_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed" % cmd sys.exit(1) return True #=============================================================================# def _GlobalGitConf(): # Get global user name cmd = [GIT, 'config', '--get', '--global', 'user.name'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd = os.getcwd()) un = proc.stdout.read().strip() proc.stdout.close() proc.wait() # Get global user name cmd = [GIT, 'config', '--get', '--global', 'user.email'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd = os.getcwd()) ue = proc.stdout.read().strip() proc.stdout.close() proc.wait() confirm = 'n' while confirm != 'y': name = raw_input("/nYour Name ? [%s]:" % un) email = raw_input("Your Email ? [%s]:" % ue) if not name: name = un if not email: email = ue if not name or not email or not email.endswith('@gionee.com'): print >>sys.stdout, "/nYou should tell me the valid name and email address" continue print >>sys.stdout, "/nYour identity is: /033[01;33m%s <%s>/033[00m" % (name, email) confirm = raw_input("/033[01;33mis this correct [y/n]?/033[00m") if name != un: cmd = [GIT, 'config', '--global', 'user.name', name] proc = subprocess.Popen(cmd) if proc.wait() != 0: print >>sys.stderr, "%s failed" % cmd sys.exit(1) if email != ue: cmd = [GIT, 'config', '--global', 'user.email', email] proc = subprocess.Popen(cmd) if proc.wait() != 0: print >>sys.stderr, "%s failed" % cmd sys.exit(1) cmd = 'git config --global color.ui auto' os.system(cmd) #=============================================================================# def _ProjectSelected(): project_conf = os.path.abspath(os.path.join(os.path.join(RRDT_DIR, S_manifests), PROJECT_CONF)) if not os.path.isfile(project_conf): cmd = 'rm -rf .rrdt 1>/dev/null 2>&1' os.system(cmd) print >>sys.stderr, "Not found project configuration in manifest" sys.exit(1) prj_list = {} index = 0 prj_conf = manifestDoc(project_conf) print >>sys.stdout, "/nProject to checkout:" for pc in prj_conf.projects(): prj_info = (pc['name'], pc['path'], pc['manifest'], pc['branch']) index += 1 prj_list[str(index)] = prj_info print >>sys.stdout, ' '*4, "%d. %s" % (index, prj_info[0]) if len(prj_list) == 0: print >>sys.stderr, "/033[00;31mERROR: Not found any project in manifest/033[00m" cmd = 'rm -rf .rrdt 1>/dev/null 2>&1' os.system(cmd) sys.exit(1) selected = 'no select' while not prj_list.has_key(selected): selected = raw_input("/nWhich would you choose:") if not prj_list.has_key(selected): selected = raw_input("/nNot found this project, please rechoose:") prj_path = prj_list[selected][1] manifest = prj_list[selected][2] branch = prj_list[selected][3] return prj_path, manifest, branch #=============================================================================# def _GetProjectRoot(): global PROJECT_ROOT dir_name = os.getcwd() while dir_name != '/': if os.path.isdir(os.path.join(dir_name, RRDT_DIR)): PROJECT_ROOT = dir_name return dir_name = os.path.dirname(dir_name) print >>sys.stderr, "/033[00;31mCan not find android root directory" print >>sys.stderr, "You should run this command in an android project directory/033[00m" sys.exit(1) #=============================================================================# def _GetModuleRoot(curr_dir): if curr_dir.find(PROJECT_ROOT) != 0: print >>sys.stderr, "ERROR: You are not in an android project directory" sys.exit(1) dir_name = curr_dir while dir_name != '/' and dir_name != PROJECT_ROOT: if os.path.isdir(os.path.join(dir_name, GIT_DIR)): return dir_name dir_name = os.path.dirname(dir_name) print >>sys.stderr, "Can not find module root directory" print >>sys.stderr, "You should run this command in an module directory" sys.exit(1) #=============================================================================# def _CheckGitVersion(): cmd = [GIT, '--version'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) ver_str = proc.stdout.read().strip() proc.stdout.close() proc.wait() if not ver_str or not ver_str.startswith('git version '): print >>sys.stderr, '/033[01;33m"%s" unsupported, please install it' % ver_str, print >>sys.stderr, 'you can install it by followed command:' print >>sys.stderr, '"sudo apt-get install git-core git-svn gitk git-gui git-email"/033[00m' sys.exit(1) ver_str = ver_str[len('git version '):].strip() ver_act = tuple(map(lambda x: int(x), ver_str.split('.')[0:3])) if ver_act < MIN_GIT_VERSION: need = '.'.join(map(lambda x: str(x), MIN_GIT_VERSION)) print >>sys.stderr, 'fatal: git %s or later required' % need sys.exit(1) #=============================================================================# def _CheckEmptyDir(cwd): if len(os.listdir(cwd)) == 0: return True return False #=============================================================================# def _CloneManifests(): dst = os.path.join(os.getcwd(), RRDT_DIR) try: os.mkdir(dst) except OSError, e: print >>sys.stderr, "fatal: cannot make %s directory: %s" % (dst, e.strerror) sys.exit(1) # Exectue git command to clone manifest cmd = [GIT, 'clone', REMOTE_MANIFEST_URL] proc = subprocess.Popen(cmd, cwd = dst) if proc.wait() != 0: print >>sys.stderr, "ERROR: Getting manifest from server failed" cmd = 'rm -rf .rrdt 1>/dev/null 2>&1' os.system(cmd) sys.exit(1) if not os.path.isdir(os.path.join(dst, S_manifests)): print >>sys.stderr, "ERROR, manifests directory not exists" cmd = 'rm -rf .rrdt 1>/dev/null 2>&1' os.system(cmd) sys.exit(1) #=============================================================================# def _UpdateManifests(): manifest_repo_dir = os.path.join(PROJECT_ROOT, os.path.join(RRDT_DIR, S_manifests)) if not os.path.isdir(os.path.join(manifest_repo_dir, GIT_DIR)): print >>sys.stderr, "/033[00;31mCan not find mainifests repository/033[00m" sys.exit(1) if -1 == _UpdateModule(manifest_repo_dir, DEFAULT_REMOTE, 'master', True): print >>sys.stderr, "/033[00;31mCan not update manifests/033[00m" sys.exit(1) #=============================================================================# def _doManifestLink(target): target = os.path.join(S_manifests, target) cmd = ['ln', '-s', target, DEFAULT_MANIFEST_XML] proc = subprocess.Popen(cmd, cwd = RRDT_DIR) if proc.wait() != 0: print >>sys.stderr, "%s failed", cmd sys.exit(1) #=============================================================================# def _CloneRepo(prj_dir, local_path, repository, branch): url = os.path.join(REMOTE_REPOS_URL+prj_dir, repository) cmd = [GIT, 'clone', url, local_path, '-b', branch] proc = subprocess.Popen(cmd, cwd = PROJECT_ROOT) if proc.wait() != 0: print >>sys.stderr, "/033[00;31mCan not clone to %s/033[00m" % local_path #sys.exit(1) some repos are not readly, git clone will be reject by gitosis #=============================================================================# def _CopyFile(src, dest): cmd = 'cp' + ' ' + src + ' ' +dest if os.system(cmd) != 0: print >>sys.stderr, "%s failed" % cmd sys.exit(1) #=============================================================================# def _ConfigDefaultBranch(branch): cmd = [GIT, 'config', 'branch.default.merge', branch] #set branch.default.merge in .rrdt/manifests repositories cwd = os.path.join(PROJECT_ROOT, os.path.join(RRDT_DIR, S_manifests)) proc = subprocess.Popen(cmd, cwd = cwd) if proc.wait() != 0: print >>sys.stderr, "%s failed" % cmd sys.exit(1) cmd = [GIT, 'config', 'branch.default.remote', DEFAULT_REMOTE] #set branch.default.remote in .rrdt/manifests repositories cwd = os.path.join(PROJECT_ROOT, os.path.join(RRDT_DIR, S_manifests)) proc = subprocess.Popen(cmd, cwd = cwd) if proc.wait() != 0: print >>sys.stderr, "%s failed" % cmd sys.exit(1) #=============================================================================# def _DoClone(prj_dir, manifest, branch): """Clone all repositories from server. """ _ConfigDefaultBranch(branch) _doManifestLink(manifest) manifest = os.path.abspath(os.path.join(os.path.join(RRDT_DIR, S_manifests), manifest)) print >>sys.stdout, "manifest name:", manifest m = manifestDoc(manifest) for p in m.projects(): repo_path = p['path'] repo_name = p['name'] _CloneRepo(prj_dir, repo_path, repo_name, branch) for c in m.copyfile(): dest = c['dest'] src = c['src'] _CopyFile(src, dest) #=============================================================================# # return 1 if has update and merge success # return 0 if has no update # return -1 if has update but can not do automatically merge def _UpdateModule(repo_path, remote, branch, force): if not remote: remote = DEFAULT_REMOTE print >>sys.stdout, "Updating module '%s', on branch %s, frome remote %s" %(repo_path, branch, remote) repo_abspath = os.path.abspath(repo_path) local_branch = 'refs/heads/'+branch remote_branch = 'refs/remotes/'+remote+'/'+branch if not os.path.isdir(os.path.join(repo_abspath, GIT_DIR)): print >>sys.stderr, "This repository(%s) not existed" % repo_abspath sys.exit(1) # Update objects and refs from remote repository, cmd = [GIT, 'fetch', remote] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed in module %s" % (cmd, repo_abspath) sys.exit(1) # Delete the branches which ware removed in remote but still locally available in "remotes/<name>" cmd = [GIT, 'remote', 'prune', remote] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed in module %s" % (cmd, repo_abspath) sys.exit(1) # Find as good common ancestors as possible for a merge# cmd = [GIT, 'merge-base', local_branch, remote_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) merge_base = proc.stdout.read().strip() proc.stdout.close() proc.wait() cmd = [GIT, 'rev-parse', local_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) local_rev = proc.stdout.read().strip() proc.stdout.close() proc.wait() cmd = [GIT, 'rev-parse', remote_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) remote_rev = proc.stdout.read().strip() proc.stdout.close() proc.wait() cmd = [GIT, 'symbolic-ref', 'HEAD'] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) head = proc.stdout.read().strip() proc.stdout.close() proc.wait() if remote_rev == merge_base: # remote has no new commits return 0 # remote has some update, but local repository has not any new commit if merge_base == local_rev: # current branch is the to be commited one if head == local_branch: # check diff whether local has update but has not done commit yet cmd = [GIT, 'diff', merge_base] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) diff = proc.stdout.read().strip() proc.stdout.close() proc.wait() if not diff: # there is no modification in work directory and index, do merge cmd = [GIT, 'merge', remote_branch] else: # there is some modification in work directory or index if force == True: cmd = [GIT, 'reset', '--hard', 'HEAD'] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed" % cmd return -1 # I recommend each one do merge by manual work else: cmd = [GIT, 'merge', remote_branch] else: return -1 # I recommend each one do merge by manual work # current branch is not the to be commited one else: cmd = [GIT, 'push', '.', remote_branch+':'+branch] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed" % cmd sys.exit(1) return 1 # remote has some update, and local repository has some new commit, it means: # local branch forked from an old version of upstream branch # we shall do rebase on new remote commit for local elif merge_base != remote_rev: return -1 #=============================================================================# def _CommitModule(repo_path, remote, branch): if not remote: remote = DEFAULT_REMOTE print >>sys.stdout, "Commit '%s' to remote(%s) on branch %s" %(repo_path, remote, branch) repo_abspath = os.path.abspath(repo_path) local_branch = 'refs/heads/'+branch remote_branch = 'refs/remotes/'+remote+'/'+branch if not os.path.isdir(os.path.join(repo_abspath, GIT_DIR)): print >>sys.stderr, "This repository(%s) not existed" % repo_abspath sys.exit(1) # Update objects and refs from remote repository, cmd = [GIT, 'fetch', remote] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed in module %s" % (cmd, repo_abspath) sys.exit(1) # Delete the branches which ware removed in remote but still locally available in "remotes/<name>" cmd = [GIT, 'remote', 'prune', remote] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed in module %s" % (cmd, repo_abspath) sys.exit(1) # Find as good common ancestors as possible for a merge# cmd = [GIT, 'merge-base', local_branch, remote_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) merge_base = proc.stdout.read().strip() proc.stdout.close() proc.wait() cmd = [GIT, 'rev-parse', local_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) local_rev = proc.stdout.read().strip() proc.stdout.close() proc.wait() cmd = [GIT, 'rev-parse', remote_branch] proc = subprocess.Popen(cmd, cwd = repo_abspath, stdout=subprocess.PIPE) remote_rev = proc.stdout.read().strip() proc.stdout.close() proc.wait() if local_rev == merge_base: print >>sys.stdout, "This branch(%s) has no any new commit" % local_branch print "Are you sure that you has done 'git add' and 'git commit' before push a commit to remote?" return True elif remote_rev != merge_base: print >>sys.stdout, "/033[01;33mYour branch forked from an old version of upstream branch." print >>sys.stdout, "You should update[rrdt update] it before do commit./033[00m" return False else: cmd = [GIT, 'push', remote, branch+':'+branch] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "%s failed" % cmd sys.exit(1) return True #===============================_CmdCheckout.start=============================# def _CmdCheckout(args): '''这条命令必须在一个空目录下执行. rrdt checkout: 根据用户选择, 从服务器端批量克隆一个指定项目的所有git仓库到本地. ''' if not _CheckEmptyDir(os.getcwd()): print >>sys.stderr, "/033[01;33m" # bold yellow print >>sys.stderr, "You should run rrdt checkout in an empty directory" print "/033[00m", # reset color sys.exit(1) # by first step: get manifest from server _CloneManifests() # select a project prj_dir, manifest, branch = _ProjectSelected() #if not ON_SERVER: _GlobalGitConf() # clone repository of manifests global PROJECT_ROOT PROJECT_ROOT = os.getcwd() _DoClone(prj_dir, manifest, branch) #===============================_CmdUpdate.start=============================# def _CmdUpdate(args): '''1. 在项目根目录下执行: rrdt update [-f, --force]: 更新所有的模块. -f, --force: 如果某个模块的正式分支有被修改过并且尚未提交到本地仓库的内容, 该选项会在更新此模块前执行git reset --hard HEAD命令使工作目录和本地仓库保持一致; 如果您的工作目录中有需要提交到本地仓库的修改内容,请谨慎使用-f选项 2. 在某个模块(git仓库的工作目录)下执行: rrdt update [-f, --force]: 只更新当前模块. -f, --force: 如果当前模块的正式分支有被修改过并且尚未提交到本地仓库的内容, 该选项会在更新此模块前执行git reset --hard HEAD命令使工作目录和本地仓库保持一致; 如果您的工作目录中有需要提交到本地仓库的修改内容,请谨慎使用-f选项 ''' try: opts, args = getopt.getopt(args, "f", ["force"]) except getopt.GetoptError: print >> sys.stderr, "无效的参数, 用法:/n%s" % _CmdUpdate.__doc__ sys.exit(2) force_update = False for o, a in opts: if o in ("-f", "--force"): force_update = True print >>sys.stdout, "您使用了强制更新选项" _UpdateManifests() manifest, bdr, bdm = _GetDefaultBranchConf() failed_modules = [] curr_dir = os.getcwd() if PROJECT_ROOT == curr_dir: # Update all modules has_changed = False m = manifestDoc(manifest) project_list = m.projects() remote = m.remote() total = len(project_list) count = 0 # update module and clone new module for p in project_list: repo_path = p['path'] count += 1 if not os.path.isdir(repo_path): print >>sys.stdout, "/033[00;33mFound a new repository, try to clone it/033[00m" repo_name = p['name'] _CloneRepo(remote['fetch'], repo_path, repo_name, remote['branch']) has_changed = True else: result = _UpdateModule(repo_path, bdr, bdm, force_update) if result == -1: failed_modules.append(repo_path) elif result == 1: has_changed = True rate = 100*count/total print >>sys.stdout, "The %dth module completed," %count, "%d%%" %rate,"done....../n" # copy files for c in m.copyfile(): dest = c['dest'] src = c['src'] _CopyFile(src, dest) del m if has_changed == True: print >>sys.stdout, "This project has changed" else: print >>sys.stdout, "This project has no changed" else: # Update current module module_root = _GetModuleRoot(curr_dir) if -1 == _UpdateModule(module_root, bdr, bdm, force_update): failed_modules.append(module_root) if len(failed_modules) > 0: print "/033[01;33m" # bold yellow string print >>sys.stdout, "部分模块在更新过程中可能与服务器端存在某些冲突(即本地仓库有新的提交或工作目录中有新的修改, 并且服务器端也有新的提交), 需要人工介入合并工作, 合并使用git merge 或 git rebase命令." print >>sys.stdout, "推荐使用git rebase来进行人工合并" print >>sys.stdout, ' '*4, "如果:你认为在本地仓库中的提交历史记录没有保留的必要(一般指本地仓库的多次提交实际上可以合并成一次提交):" print >>sys.stdout, ' '*8, "则: 使用git rebase进行合并, 这条命令会将你本地仓库的多次提交合并成一次新的提交, 以保证仓库中有一个良好清晰的提交历史" print >>sys.stdout, ' '*4, "否则:" print >>sys.stdout, ' '*8, "请使用git merge, 它会在仓库中保留你每次提交的历史记录" print "/033[01;31m" # bold red string print >>sys.stdout, ' '*4, "1. cd MODULE-DIR && git checkout %s (如果当前的分支不是 %s)/n" % (bdm, bdm) print >>sys.stdout, ' '*4, "使用git rebase的步骤:" print >>sys.stdout, ' '*4, "如果工作目录中的修改还没有git add到index中, 执行第2步, 否则忽略它" print >>sys.stdout, ' '*4, "2. git add <file list>" print >>sys.stdout, ' '*4, "如果index中有内容没有git commit到仓库, 执行第3步, 否则忽略它" print >>sys.stdout, ' '*4, "3. git commit" print >>sys.stdout, ' '*4, "4. git rebase %s/%s" %(bdr, bdm) print >>sys.stdout, ' '*4, "5. 如果rebase过程提示有冲突, 请手工解决冲突, 然后执行第6步" print >>sys.stdout, ' '*4, "6. git add <file list>" print >>sys.stdout, ' '*4, "7. git rebase --continue" print "/033[01;33m" # bold yellow string print >>sys.stdout, ' '*4, "or" print "/033[01;31m" # bold red string print >>sys.stdout, ' '*4, "使用git merge的步骤:" print >>sys.stdout, ' '*4, "如果工作目录中的修改还没有git add到index中, 执行第2步, 否则忽略它" print >>sys.stdout, ' '*4, "2. git add <file list>" print >>sys.stdout, ' '*4, "如果index中的内容没有git commit到仓库中, 执行第3步, 否则忽略它" print >>sys.stdout, ' '*4, "3. git commit" print >>sys.stdout, ' '*4, "4. git merge %s/%s" %(bdr, bdm) print >>sys.stdout, ' '*4, "5. 如果merge过程中提示有冲突, 请手工解决冲突, 然后执行第6步" print >>sys.stdout, ' '*4, "6. git add <file list>" print >>sys.stdout, ' '*4, "7. git commit" print "/033[01;33m" # bold yellow string print >>sys.stdout, ' '*4, "手工解决冲突时可以使用比较工具,如meld,详细用法请参考git文档" print >>sys.stdout, ' '*4, "git mergetool [--tool=<tool>]" print "/033[00m", # reset color print >>sys.stdout, "=============================================================" print "/033[01;33m" # bold yellow string for m in failed_modules: print >>sys.stdout, m print "/033[00m" # reset color print >>sys.stdout, "This project has conflict in updating" #===============================_CmdCommit.start=============================# def _CmdCommit(args): '''在某个模块(git仓库的工作目录)下执行: rrdt commit 将本地仓库中该项目的分支上新的提交推送到服务器端对应的仓库. ''' manifest_dir, bdr, bdm = _GetDefaultBranchConf() review = _GetReviewBranch() review_branch = 'refs/remotes/'+bdr+'/'+review curr_dir = os.getcwd() if PROJECT_ROOT == curr_dir: # Commit all modules print >>sys.stdout, "Not support this command under project root directory" else: # Commit current module module_root = _GetModuleRoot(curr_dir) cmd = [GIT, 'cat-file', '-t', review_branch] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,/ cwd = os.path.abspath(module_root)) ret = proc.stdout.read().strip() proc.stdout.close() proc.wait() # if review exists, delete it if ret == 'commit': cmd = GIT + ' push ' + bdr + ' :' +review os.system(cmd) if False == _CommitModule(module_root, bdr, bdm): print >>sys.stderr, "Commit operation failed !!!" #===============================_CmdHelp.start===============================# def _CmdHelp(args): '''执行 'rrdt help 命令名' 查看指定命令的详细信息 ''' print '' if len(args) > 0: cmd = args[0] if not _CmdsFunc.has_key(cmd): print >>sys.stderr, "rrdt has no command '%s'. See 'rrdt help'/n"/ %cmd sys.exit(1) print _CmdsFunc[cmd][0].__doc__ else: print 'Available commands:' cmd_list = dict.keys(_CmdsFunc) cmd_list.sort() for cmd in cmd_list: print string.ljust(' '*4+'rrdt '+cmd+' :', 30), print _CmdsFunc[cmd][1] print '' #===============================_CmdVersion.start===============================# def _CmdVersion(args): print >>sys.stdout, 'rrdt 当前版本 %s' %VERSION #===============================_CmdCreateRepos.start===========================# def _CmdCreateRepos(args): '''Under root directory of project which not be track by git: rrdt createrepos -p <project name> -c <project configuration xml> -m <manifest xml> create repositories for a project -p <project name> it show the name of project, this name must be include in project configuration xml -c <project configuration xml> it tell me the root dir of remote repos and branch name of a project -m <manifest xml> it show the path and name of each repository ''' # Get global user name cmd = [GIT, 'config', '--get', '--global', 'user.name'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd = os.getcwd()) gl_name = proc.stdout.read().strip() proc.stdout.close() proc.wait() # Get global user name cmd = [GIT, 'config', '--get', '--global', 'user.email'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd = os.getcwd()) gl_email = proc.stdout.read().strip() proc.stdout.close() proc.wait() if not _admlist.has_key(gl_name): print >>sys.stderr, "This is administrator's commmand" sys.exit(1) if gl_email != _admlist[gl_name]: print >>sys.stderr, "This is administrator's commmand" sys.exit(1) usage = "rrdt createrepos -p <project name> -c <project configuration xml> -m <manifest xml>" optParser = optparse.OptionParser(usage) optParser.add_option('-p', '--project', dest='project', action='store') optParser.add_option('-m', '--manifest', dest='manifest', action='store') optParser.add_option('-c', '--project_conf', dest='prj_conf_xml', action='store') opt, args = optParser.parse_args(args) if not opt.project or not opt.manifest or not opt.prj_conf_xml: sys.stderr, "Must give all parameter" sys.exit(1) if not os.path.isfile(opt.prj_conf_xml) or not os.path.isfile(opt.manifest): print >>sys.stderr, "Not found file. prj_conf_xml:%s manifext xml:%s!" % (opt.prj_conf_xml, opt.manifest) sys.exit(1) prj_conf = manifestDoc(opt.prj_conf_xml) found = False for pc in prj_conf.projects(): if pc['name'] == opt.project: if pc.has_key('path') and pc.has_key('branch'): prj_dir = pc['path'] branch = pc['branch'] found = True break if not found: print >>sys.stderr, "ERROR: There is no this project %s in %s" % (opt.project, opt.prj_conf_xml) sys.exit(1) m = manifestDoc(opt.manifest) for p in m.projects(): repo_path = p['path'] repo_abspath = os.path.abspath(repo_path) repo_name = p['name'] if not os.path.isdir(repo_abspath): print >>sys.stderr, "/033[01;31m %s not exist/033[00m" % repo_abspath # create new repository on local. start if not os.path.isdir(os.path.join(repo_abspath, GIT_DIR)): print >>sys.stdout, "Try to init %s repository......" % repo_path # init repository cmd = [GIT, 'init'] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "/033[00;31m%s failed in %s/033[00m" % (cmd, repo_path) continue # add content cmd = [GIT, 'add', '.'] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "/033[00;31m%s failed in %s/033[00m" % (cmd, repo_path) sys.exit(1) # commit cmd = [GIT, 'commit', '-a', '-m', 'Initial import'] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "/033[00;31m%s failed in %s/033[00m" % (cmd, repo_path) sys.exit(1) # create new repository on local. end # create branch cmd = [GIT, 'cat-file', '-t', branch] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd = repo_abspath) ret = proc.stdout.read().strip() proc.stdout.close() proc.wait() if not ret or ret != 'commit': cmd = [GIT, 'branch', branch] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "/033[00;31m%s failed in %s/033[00m" % (cmd, repo_path) sys.exit(1) # push to remote url = os.path.join(REMOTE_REPOS_URL+prj_dir, repo_name) cmd = [GIT, 'push', url, 'master:master', branch+':'+branch] proc = subprocess.Popen(cmd, cwd = repo_abspath) if proc.wait() != 0: print >>sys.stderr, "/033[00;31m%s failed in /'%s/'/033[00m" % (cmd, repo_path) sys.exit(1) print >>sys.stdout, "Create repositories over." #===============================_CmdInitRepos.start================================# def _CmdInitRepos(args): ''' rrdt initrepos -m <manifest> create bare repositories under current directory -m <manifest> must be given, it show a repository list which to be create ''' usage = "rrdt initrepos -m <manifest>" optParser = optparse.OptionParser(usage) optParser.add_option('-m', '--manifest', dest='manifest', action='store') opt, args = optParser.parse_args(args) if not opt.manifest or not os.path.isfile(opt.manifest): print >>sys.stderr, "Not found manifest file %s!" % opt.manifest sys.exit(1) m = manifestDoc(opt.manifest) for p in m.projects(): repo_name = p['name'] repo_name = repo_name+GIT_DIR cmd = [GIT, 'init', '--bare', repo_name] proc = subprocess.Popen(cmd, cwd = os.getcwd()) if proc.wait() != 0: print >>sys.stderr, "/033[00;31m%s failed in %s/033[00m" % (cmd, repo_path) sys.exit(1) #===============================_CmdStatus.start===================================# def _CmdStatus(args): '''在项目根目录下执行. rrdt status: 获取当前项目的所有git仓库的状态. ''' print >>sys.stdout, "TBD" #===============================_CmdForAll.start===================================# def _CmdForAll(args): '''在项目根目录下执行. rrdt forall <command [opt1] [opt2] [opt3] ......>: 在每个模块下(git仓库的工作目录)执行由用户指定的命令, 例如 rrdt forall ls -la, rrdt forall git status. ''' curr_dir = os.getcwd() if PROJECT_ROOT != curr_dir: print >>sys.stderr, "You must execute this command under root directory of project" sys.exit(1) if len(args) < 1: print >>sys.stderr, "You must give a command to execute" sys.exit(1) cmd = args manifest, bdr, bdm = _GetDefaultBranchConf() m = manifestDoc(manifest) failed_modules = [] project_list = m.projects() for p in project_list: repo_path = p['path'] print >>sys.stdout, "/033[01;33mExecute command under %s/033[00m" % repo_path if not os.path.isdir(repo_path): print >>sys.stderr, "/033[00;31m%s not exists/033[00m" % repo_path else: proc = subprocess.Popen(cmd, cwd = repo_path) if proc.wait() != 0: failed_modules.append(repo_path) del m if len(failed_modules) > 0: print "/033[01;33m" # bold yellow string print >>sys.stdout, "以下模块执行命令[%s]失败:" %cmd for m in failed_modules: print >>sys.stdout, m print "/033[00m" # reset color #===============================_CmdReqReview.start===================================# def _CmdReqReview(args): '''在需要执行commit的模块下执行. rrdt reqreview: 将本地的新提交推送到服务器端仓库中一个用来review的分支上, review成员在自己电脑上使用rrdt update来获取review分支上的更新, 然后开始进行review ''' manifest_dir, bdr, bdm = _GetDefaultBranchConf() review = _GetReviewBranch() review_branch = 'refs/remotes/'+bdr+'/'+review curr_dir = os.getcwd() module_root = _GetModuleRoot(curr_dir) cmd = [GIT, 'cat-file', '-t', review_branch] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,/ cwd = os.path.abspath(module_root)) ret = proc.stdout.read().strip() proc.stdout.close() proc.wait() # if review exists, delete it if ret == 'commit': cmd = GIT + ' push ' + bdr + ' :' +review os.system(cmd) if False == _CommitReview(module_root, bdr, bdm, review): print >>sys.stderr, "Commit operation failed !!!" #================== function and help infomation of each command===================# _CmdsFunc = { 'checkout' : (_CmdCheckout, '- 从服务器端检出一个指定项目的源码到本地'), 'update' : (_CmdUpdate, '- 从服务器端仓库中获取更新'), 'commit' : (_CmdCommit, '- 将本地git仓库中新的提交推送到服务器端对应的仓库, 在使用commit之前建议先使用reqreview'), 'help' : (_CmdHelp, '- 执行 /033[01;32mrrdt help 命令名/033[00m 获取指定命令的详细介绍'), 'version' : (_CmdVersion, '- 获取该脚本版本号'), 'createrepos' : (_CmdCreateRepos, '- 仓库管理员的命令, 用来为没有被纳入git仓库的代码批量创建git仓库'), 'initrepos' : (_CmdInitRepos, '- 根据manifest文件的指定, 在当前目录批量建立空的git仓库'), 'status' : (_CmdStatus, '- 获取当前项目中所有git仓库的状态'), 'forall' : (_CmdForAll, '- 在当前项目中所有git仓库的工作目录下执行一条用户指定的命令(可以是git命令或shell命令)'), 'reqreview' : (_CmdReqReview, '- 在正式提交代码到服务器之前, 执行该命令提交review') } def main(orig_args): cmd = orig_args[0] if not _CmdsFunc.has_key(cmd): print >>sys.stderr, "rrdt: '%s' is not a rrdt command. See 'rrdt help'."/ %cmd sys.exit(1) _Initial() if not LOCAL_IP: if cmd != 'help' and cmd != 'version' and cmd != 'initrepos'/ and cmd != 'status': print >>sys.stderr, "Your network is disconnected, can not execute rrdt", cmd sys.exit(1) if cmd == 'update' or cmd == 'commit' or / cmd == 'status' or cmd == 'forall' or cmd == 'reqreview': _GetProjectRoot() _CmdsFunc[cmd][0](orig_args[1:]) ####################################### script main entry ################################ if __name__ == "__main__": if len(sys.argv) < 2: print >>sys.stderr, "You should give the command name, such as /"rrdt help/"" sys.exit(1) _CheckGitVersion() main(sys.argv[1:]) else: print >>sys.stderr, "ERROR: this script only run by it self, not support import" sys.exit(1)