 44211e8c2e
			
		
	
	44211e8c2e
	
	
	
		
			
			On FreeBSD, Python does not ship as part of the base system but is available via the ports system, which install the binary in /usr/local/bin. Signed-off-by: R. Tyler Ballance <tyler@monkeypox.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
			
				
	
	
		
			361 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| #
 | |
| # This tool is copyright (c) 2006, Sean Estabrooks.
 | |
| # It is released under the Gnu Public License, version 2.
 | |
| #
 | |
| # Import Perforce branches into Git repositories.
 | |
| # Checking out the files is done by calling the standard p4
 | |
| # client which you must have properly configured yourself
 | |
| #
 | |
| 
 | |
| import marshal
 | |
| import os
 | |
| import sys
 | |
| import time
 | |
| import getopt
 | |
| 
 | |
| from signal import signal, \
 | |
|    SIGPIPE, SIGINT, SIG_DFL, \
 | |
|    default_int_handler
 | |
| 
 | |
| signal(SIGPIPE, SIG_DFL)
 | |
| s = signal(SIGINT, SIG_DFL)
 | |
| if s != default_int_handler:
 | |
|    signal(SIGINT, s)
 | |
| 
 | |
| def die(msg, *args):
 | |
|     for a in args:
 | |
|         msg = "%s %s" % (msg, a)
 | |
|     print "git-p4import fatal error:", msg
 | |
|     sys.exit(1)
 | |
| 
 | |
| def usage():
 | |
|     print "USAGE: git-p4import [-q|-v]  [--authors=<file>]  [-t <timezone>]  [//p4repo/path <branch>]"
 | |
|     sys.exit(1)
 | |
| 
 | |
| verbosity = 1
 | |
| logfile = "/dev/null"
 | |
| ignore_warnings = False
 | |
| stitch = 0
 | |
| tagall = True
 | |
| 
 | |
| def report(level, msg, *args):
 | |
|     global verbosity
 | |
|     global logfile
 | |
|     for a in args:
 | |
|         msg = "%s %s" % (msg, a)
 | |
|     fd = open(logfile, "a")
 | |
|     fd.writelines(msg)
 | |
|     fd.close()
 | |
|     if level <= verbosity:
 | |
|         print msg
 | |
| 
 | |
| class p4_command:
 | |
|     def __init__(self, _repopath):
 | |
|         try:
 | |
|             global logfile
 | |
|             self.userlist = {}
 | |
|             if _repopath[-1] == '/':
 | |
|                 self.repopath = _repopath[:-1]
 | |
|             else:
 | |
|                 self.repopath = _repopath
 | |
|             if self.repopath[-4:] != "/...":
 | |
|                 self.repopath= "%s/..." % self.repopath
 | |
|             f=os.popen('p4 -V 2>>%s'%logfile, 'rb')
 | |
|             a = f.readlines()
 | |
|             if f.close():
 | |
|                 raise
 | |
|         except:
 | |
|                 die("Could not find the \"p4\" command")
 | |
| 
 | |
|     def p4(self, cmd, *args):
 | |
|         global logfile
 | |
|         cmd = "%s %s" % (cmd, ' '.join(args))
 | |
|         report(2, "P4:", cmd)
 | |
|         f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb')
 | |
|         list = []
 | |
|         while 1:
 | |
|            try:
 | |
|                 list.append(marshal.load(f))
 | |
|            except EOFError:
 | |
|                 break
 | |
|         self.ret = f.close()
 | |
|         return list
 | |
| 
 | |
|     def sync(self, id, force=False, trick=False, test=False):
 | |
|         if force:
 | |
|             ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0]
 | |
|         elif trick:
 | |
|             ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0]
 | |
|         elif test:
 | |
|             ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0]
 | |
|         else:
 | |
|             ret = self.p4("sync    %s@%s"%(self.repopath, id))[0]
 | |
|         if ret['code'] == "error":
 | |
|              data = ret['data'].upper()
 | |
|              if data.find('VIEW') > 0:
 | |
|                  die("Perforce reports %s is not in client view"% self.repopath)
 | |
|              elif data.find('UP-TO-DATE') < 0:
 | |
|                  die("Could not sync files from perforce", self.repopath)
 | |
| 
 | |
|     def changes(self, since=0):
 | |
|         try:
 | |
|             list = []
 | |
|             for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)):
 | |
|                 list.append(rec['change'])
 | |
|             list.reverse()
 | |
|             return list
 | |
|         except:
 | |
|             return []
 | |
| 
 | |
|     def authors(self, filename):
 | |
|         f=open(filename)
 | |
|         for l in f.readlines():
 | |
|             self.userlist[l[:l.find('=')].rstrip()] = \
 | |
|                     (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')])
 | |
|         f.close()
 | |
|         for f,e in self.userlist.items():
 | |
|                 report(2, f, ":", e[0], "  <", e[1], ">")
 | |
| 
 | |
|     def _get_user(self, id):
 | |
|         if not self.userlist.has_key(id):
 | |
|             try:
 | |
|                 user = self.p4("users", id)[0]
 | |
|                 self.userlist[id] = (user['FullName'], user['Email'])
 | |
|             except:
 | |
|                 self.userlist[id] = (id, "")
 | |
|         return self.userlist[id]
 | |
| 
 | |
|     def _format_date(self, ticks):
 | |
|         symbol='+'
 | |
|         name = time.tzname[0]
 | |
|         offset = time.timezone
 | |
|         if ticks[8]:
 | |
|             name = time.tzname[1]
 | |
|             offset = time.altzone
 | |
|         if offset < 0:
 | |
|             offset *= -1
 | |
|             symbol = '-'
 | |
|         localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name)
 | |
|         tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks)
 | |
|         return "%s %s" % (tickso, localo)
 | |
| 
 | |
|     def where(self):
 | |
|         try:
 | |
|             return self.p4("where %s" % self.repopath)[-1]['path']
 | |
|         except:
 | |
|             return ""
 | |
| 
 | |
|     def describe(self, num):
 | |
|         desc = self.p4("describe -s", num)[0]
 | |
|         self.msg = desc['desc']
 | |
|         self.author, self.email = self._get_user(desc['user'])
 | |
|         self.date = self._format_date(time.localtime(long(desc['time'])))
 | |
|         return self
 | |
| 
 | |
| class git_command:
 | |
|     def __init__(self):
 | |
|         try:
 | |
|             self.version = self.git("--version")[0][12:].rstrip()
 | |
|         except:
 | |
|             die("Could not find the \"git\" command")
 | |
|         try:
 | |
|             self.gitdir = self.get_single("rev-parse --git-dir")
 | |
|             report(2, "gdir:", self.gitdir)
 | |
|         except:
 | |
|             die("Not a git repository... did you forget to \"git init\" ?")
 | |
|         try:
 | |
|             self.cdup = self.get_single("rev-parse --show-cdup")
 | |
|             if self.cdup != "":
 | |
|                 os.chdir(self.cdup)
 | |
|             self.topdir = os.getcwd()
 | |
|             report(2, "topdir:", self.topdir)
 | |
|         except:
 | |
|             die("Could not find top git directory")
 | |
| 
 | |
|     def git(self, cmd):
 | |
|         global logfile
 | |
|         report(2, "GIT:", cmd)
 | |
|         f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb')
 | |
|         r=f.readlines()
 | |
|         self.ret = f.close()
 | |
|         return r
 | |
| 
 | |
|     def get_single(self, cmd):
 | |
|         return self.git(cmd)[0].rstrip()
 | |
| 
 | |
|     def current_branch(self):
 | |
|         try:
 | |
|             testit = self.git("rev-parse --verify HEAD")[0]
 | |
|             return self.git("symbolic-ref HEAD")[0][11:].rstrip()
 | |
|         except:
 | |
|             return None
 | |
| 
 | |
|     def get_config(self, variable):
 | |
|         try:
 | |
|             return self.git("config --get %s" % variable)[0].rstrip()
 | |
|         except:
 | |
|             return None
 | |
| 
 | |
|     def set_config(self, variable, value):
 | |
|         try:
 | |
|             self.git("config %s %s"%(variable, value) )
 | |
|         except:
 | |
|             die("Could not set %s to " % variable, value)
 | |
| 
 | |
|     def make_tag(self, name, head):
 | |
|         self.git("tag -f %s %s"%(name,head))
 | |
| 
 | |
|     def top_change(self, branch):
 | |
|         try:
 | |
|             a=self.get_single("name-rev --tags refs/heads/%s" % branch)
 | |
|             loc = a.find(' tags/') + 6
 | |
|             if a[loc:loc+3] != "p4/":
 | |
|                 raise
 | |
|             return int(a[loc+3:][:-2])
 | |
|         except:
 | |
|             return 0
 | |
| 
 | |
|     def update_index(self):
 | |
|         self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin")
 | |
| 
 | |
|     def checkout(self, branch):
 | |
|         self.git("checkout %s" % branch)
 | |
| 
 | |
|     def repoint_head(self, branch):
 | |
|         self.git("symbolic-ref HEAD refs/heads/%s" % branch)
 | |
| 
 | |
|     def remove_files(self):
 | |
|         self.git("ls-files | xargs rm")
 | |
| 
 | |
|     def clean_directories(self):
 | |
|         self.git("clean -d")
 | |
| 
 | |
|     def fresh_branch(self, branch):
 | |
|         report(1, "Creating new branch", branch)
 | |
|         self.git("ls-files | xargs rm")
 | |
|         os.remove(".git/index")
 | |
|         self.repoint_head(branch)
 | |
|         self.git("clean -d")
 | |
| 
 | |
|     def basedir(self):
 | |
|         return self.topdir
 | |
| 
 | |
|     def commit(self, author, email, date, msg, id):
 | |
|         self.update_index()
 | |
|         fd=open(".msg", "w")
 | |
|         fd.writelines(msg)
 | |
|         fd.close()
 | |
|         try:
 | |
|                 current = self.get_single("rev-parse --verify HEAD")
 | |
|                 head = "-p HEAD"
 | |
|         except:
 | |
|                 current = ""
 | |
|                 head = ""
 | |
|         tree = self.get_single("write-tree")
 | |
|         for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]:
 | |
|             os.environ['GIT_AUTHOR_%s'%r] = l
 | |
|             os.environ['GIT_COMMITTER_%s'%r] = l
 | |
|         commit = self.get_single("commit-tree %s %s < .msg" % (tree,head))
 | |
|         os.remove(".msg")
 | |
|         self.make_tag("p4/%s"%id, commit)
 | |
|         self.git("update-ref HEAD %s %s" % (commit, current) )
 | |
| 
 | |
| try:
 | |
|     opts, args = getopt.getopt(sys.argv[1:], "qhvt:",
 | |
|             ["authors=","help","stitch=","timezone=","log=","ignore","notags"])
 | |
| except getopt.GetoptError:
 | |
|     usage()
 | |
| 
 | |
| for o, a in opts:
 | |
|     if o == "-q":
 | |
|         verbosity = 0
 | |
|     if o == "-v":
 | |
|         verbosity += 1
 | |
|     if o in ("--log"):
 | |
|         logfile = a
 | |
|     if o in ("--notags"):
 | |
|         tagall = False
 | |
|     if o in ("-h", "--help"):
 | |
|         usage()
 | |
|     if o in ("--ignore"):
 | |
|         ignore_warnings = True
 | |
| 
 | |
| git = git_command()
 | |
| branch=git.current_branch()
 | |
| 
 | |
| for o, a in opts:
 | |
|     if o in ("-t", "--timezone"):
 | |
|         git.set_config("perforce.timezone", a)
 | |
|     if o in ("--stitch"):
 | |
|         git.set_config("perforce.%s.path" % branch, a)
 | |
|         stitch = 1
 | |
| 
 | |
| if len(args) == 2:
 | |
|     branch = args[1]
 | |
|     git.checkout(branch)
 | |
|     if branch == git.current_branch():
 | |
|         die("Branch %s already exists!" % branch)
 | |
|     report(1, "Setting perforce to ", args[0])
 | |
|     git.set_config("perforce.%s.path" % branch, args[0])
 | |
| elif len(args) != 0:
 | |
|     die("You must specify the perforce //depot/path and git branch")
 | |
| 
 | |
| p4path = git.get_config("perforce.%s.path" % branch)
 | |
| if p4path == None:
 | |
|     die("Do not know Perforce //depot/path for git branch", branch)
 | |
| 
 | |
| p4 = p4_command(p4path)
 | |
| 
 | |
| for o, a in opts:
 | |
|     if o in ("-a", "--authors"):
 | |
|         p4.authors(a)
 | |
| 
 | |
| localdir = git.basedir()
 | |
| if p4.where()[:len(localdir)] != localdir:
 | |
|     report(1, "**WARNING** Appears p4 client is misconfigured")
 | |
|     report(1, "   for sync from %s to %s" % (p4.repopath, localdir))
 | |
|     if ignore_warnings != True:
 | |
|         die("Reconfigure or use \"--ignore\" on command line")
 | |
| 
 | |
| if stitch == 0:
 | |
|     top = git.top_change(branch)
 | |
| else:
 | |
|     top = 0
 | |
| changes = p4.changes(top)
 | |
| count = len(changes)
 | |
| if count == 0:
 | |
|     report(1, "Already up to date...")
 | |
|     sys.exit(0)
 | |
| 
 | |
| ptz = git.get_config("perforce.timezone")
 | |
| if ptz:
 | |
|     report(1, "Setting timezone to", ptz)
 | |
|     os.environ['TZ'] = ptz
 | |
|     time.tzset()
 | |
| 
 | |
| if stitch == 1:
 | |
|     git.remove_files()
 | |
|     git.clean_directories()
 | |
|     p4.sync(changes[0], force=True)
 | |
| elif top == 0 and branch != git.current_branch():
 | |
|     p4.sync(changes[0], test=True)
 | |
|     report(1, "Creating new initial commit");
 | |
|     git.fresh_branch(branch)
 | |
|     p4.sync(changes[0], force=True)
 | |
| else:
 | |
|     p4.sync(changes[0], trick=True)
 | |
| 
 | |
| report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch))
 | |
| for id in changes:
 | |
|     report(1, "Importing changeset", id)
 | |
|     change = p4.describe(id)
 | |
|     p4.sync(id)
 | |
|     if tagall :
 | |
|             git.commit(change.author, change.email, change.date, change.msg, id)
 | |
|     else:
 | |
|             git.commit(change.author, change.email, change.date, change.msg, "import")
 | |
|     if stitch == 1:
 | |
|         git.clean_directories()
 | |
|         stitch = 0
 |