您的位置:首页 > 移动开发 > Android开发

用python脚本实现的android代码管理工具rrdt

2011-03-31 18:17 1841 查看
#!/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 = 'git@192.168.110.94:'
# url of manifest in host
# REMOTE_MANIFEST_URL = 'git@192.168.110.94: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' : 'linansong@gionee.com',
'huyuke' : 'huyk@gionee.com',
}
############################# 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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐