3
# This tool is copyright (c) 2006, Sean Estabrooks.
4
# It is released under the Gnu Public License, version 2.
6
# Import Perforce branches into Git repositories.
7
# Checking out the files is done by calling the standard p4
8
# client which you must have properly configured yourself
17
from signal import signal, \
18
SIGPIPE, SIGINT, SIG_DFL, \
21
signal(SIGPIPE, SIG_DFL)
22
s = signal(SIGINT, SIG_DFL)
23
if s != default_int_handler:
28
msg = "%s %s" % (msg, a)
29
print "git-p4import fatal error:", msg
33
print "USAGE: git-p4import [-q|-v] [--authors=<file>] [-t <timezone>] [//p4repo/path <branch>]"
38
ignore_warnings = False
42
def report(level, msg, *args):
46
msg = "%s %s" % (msg, a)
47
fd = open(logfile, "a")
50
if level <= verbosity:
54
def __init__(self, _repopath):
58
if _repopath[-1] == '/':
59
self.repopath = _repopath[:-1]
61
self.repopath = _repopath
62
if self.repopath[-4:] != "/...":
63
self.repopath= "%s/..." % self.repopath
64
f=os.popen('p4 -V 2>>%s'%logfile, 'rb')
69
die("Could not find the \"p4\" command")
71
def p4(self, cmd, *args):
73
cmd = "%s %s" % (cmd, ' '.join(args))
75
f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb')
79
list.append(marshal.load(f))
85
def sync(self, id, force=False, trick=False, test=False):
87
ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0]
89
ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0]
91
ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0]
93
ret = self.p4("sync %s@%s"%(self.repopath, id))[0]
94
if ret['code'] == "error":
95
data = ret['data'].upper()
96
if data.find('VIEW') > 0:
97
die("Perforce reports %s is not in client view"% self.repopath)
98
elif data.find('UP-TO-DATE') < 0:
99
die("Could not sync files from perforce", self.repopath)
101
def changes(self, since=0):
104
for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)):
105
list.append(rec['change'])
111
def authors(self, filename):
113
for l in f.readlines():
114
self.userlist[l[:l.find('=')].rstrip()] = \
115
(l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')])
117
for f,e in self.userlist.items():
118
report(2, f, ":", e[0], " <", e[1], ">")
120
def _get_user(self, id):
121
if not self.userlist.has_key(id):
123
user = self.p4("users", id)[0]
124
self.userlist[id] = (user['FullName'], user['Email'])
126
self.userlist[id] = (id, "")
127
return self.userlist[id]
129
def _format_date(self, ticks):
131
name = time.tzname[0]
132
offset = time.timezone
134
name = time.tzname[1]
135
offset = time.altzone
139
localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name)
140
tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks)
141
return "%s %s" % (tickso, localo)
145
return self.p4("where %s" % self.repopath)[-1]['path']
149
def describe(self, num):
150
desc = self.p4("describe -s", num)[0]
151
self.msg = desc['desc']
152
self.author, self.email = self._get_user(desc['user'])
153
self.date = self._format_date(time.localtime(long(desc['time'])))
159
self.version = self.git("--version")[0][12:].rstrip()
161
die("Could not find the \"git\" command")
163
self.gitdir = self.get_single("rev-parse --git-dir")
164
report(2, "gdir:", self.gitdir)
166
die("Not a git repository... did you forget to \"git init\" ?")
168
self.cdup = self.get_single("rev-parse --show-cdup")
171
self.topdir = os.getcwd()
172
report(2, "topdir:", self.topdir)
174
die("Could not find top git directory")
178
report(2, "GIT:", cmd)
179
f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb')
184
def get_single(self, cmd):
185
return self.git(cmd)[0].rstrip()
187
def current_branch(self):
189
testit = self.git("rev-parse --verify HEAD")[0]
190
return self.git("symbolic-ref HEAD")[0][11:].rstrip()
194
def get_config(self, variable):
196
return self.git("config --get %s" % variable)[0].rstrip()
200
def set_config(self, variable, value):
202
self.git("config %s %s"%(variable, value) )
204
die("Could not set %s to " % variable, value)
206
def make_tag(self, name, head):
207
self.git("tag -f %s %s"%(name,head))
209
def top_change(self, branch):
211
a=self.get_single("name-rev --tags refs/heads/%s" % branch)
212
loc = a.find(' tags/') + 6
213
if a[loc:loc+3] != "p4/":
215
return int(a[loc+3:][:-2])
219
def update_index(self):
220
self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin")
222
def checkout(self, branch):
223
self.git("checkout %s" % branch)
225
def repoint_head(self, branch):
226
self.git("symbolic-ref HEAD refs/heads/%s" % branch)
228
def remove_files(self):
229
self.git("ls-files | xargs rm")
231
def clean_directories(self):
234
def fresh_branch(self, branch):
235
report(1, "Creating new branch", branch)
236
self.git("ls-files | xargs rm")
237
os.remove(".git/index")
238
self.repoint_head(branch)
244
def commit(self, author, email, date, msg, id):
250
current = self.get_single("rev-parse --verify HEAD")
255
tree = self.get_single("write-tree")
256
for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]:
257
os.environ['GIT_AUTHOR_%s'%r] = l
258
os.environ['GIT_COMMITTER_%s'%r] = l
259
commit = self.get_single("commit-tree %s %s < .msg" % (tree,head))
261
self.make_tag("p4/%s"%id, commit)
262
self.git("update-ref HEAD %s %s" % (commit, current) )
265
opts, args = getopt.getopt(sys.argv[1:], "qhvt:",
266
["authors=","help","stitch=","timezone=","log=","ignore","notags"])
267
except getopt.GetoptError:
277
if o in ("--notags"):
279
if o in ("-h", "--help"):
281
if o in ("--ignore"):
282
ignore_warnings = True
285
branch=git.current_branch()
288
if o in ("-t", "--timezone"):
289
git.set_config("perforce.timezone", a)
290
if o in ("--stitch"):
291
git.set_config("perforce.%s.path" % branch, a)
297
if branch == git.current_branch():
298
die("Branch %s already exists!" % branch)
299
report(1, "Setting perforce to ", args[0])
300
git.set_config("perforce.%s.path" % branch, args[0])
302
die("You must specify the perforce //depot/path and git branch")
304
p4path = git.get_config("perforce.%s.path" % branch)
306
die("Do not know Perforce //depot/path for git branch", branch)
308
p4 = p4_command(p4path)
311
if o in ("-a", "--authors"):
314
localdir = git.basedir()
315
if p4.where()[:len(localdir)] != localdir:
316
report(1, "**WARNING** Appears p4 client is misconfigured")
317
report(1, " for sync from %s to %s" % (p4.repopath, localdir))
318
if ignore_warnings != True:
319
die("Reconfigure or use \"--ignore\" on command line")
322
top = git.top_change(branch)
325
changes = p4.changes(top)
328
report(1, "Already up to date...")
331
ptz = git.get_config("perforce.timezone")
333
report(1, "Setting timezone to", ptz)
334
os.environ['TZ'] = ptz
339
git.clean_directories()
340
p4.sync(changes[0], force=True)
341
elif top == 0 and branch != git.current_branch():
342
p4.sync(changes[0], test=True)
343
report(1, "Creating new initial commit");
344
git.fresh_branch(branch)
345
p4.sync(changes[0], force=True)
347
p4.sync(changes[0], trick=True)
349
report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch))
351
report(1, "Importing changeset", id)
352
change = p4.describe(id)
355
git.commit(change.author, change.email, change.date, change.msg, id)
357
git.commit(change.author, change.email, change.date, change.msg, "import")
359
git.clean_directories()