blob: 3deaa42559c38484395a81d9ac0963e279878a84 [file] [log] [blame]
Simon Hausmann86949ee2007-03-19 20:59:12 +01001#!/usr/bin/env python
2#
3# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
4#
Simon Hausmannc8cbbee2007-05-28 14:43:25 +02005# Author: Simon Hausmann <simon@lst.de>
6# Copyright: 2007 Simon Hausmann <simon@lst.de>
Simon Hausmann83dce552007-03-19 22:26:36 +01007# 2007 Trolltech ASA
Simon Hausmann86949ee2007-03-19 20:59:12 +01008# License: MIT <http://www.opensource.org/licenses/mit-license.php>
9#
10
Simon Hausmann08483582007-05-15 14:31:06 +020011import optparse, sys, os, marshal, popen2, subprocess, shelve
Simon Hausmann25df95c2007-05-15 15:15:39 +020012import tempfile, getopt, sha, os.path, time, platform
Han-Wen Nienhuysce6f33c2007-05-23 16:46:29 -030013import re
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -030014
Simon Hausmannb9847332007-03-20 20:54:23 +010015from sets import Set;
Simon Hausmann4f5cf762007-03-19 22:25:17 +010016
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030017verbose = False
Simon Hausmann86949ee2007-03-19 20:59:12 +010018
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030019def die(msg):
20 if verbose:
21 raise Exception(msg)
22 else:
23 sys.stderr.write(msg + "\n")
24 sys.exit(1)
25
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030026def write_pipe(c, str):
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030027 if verbose:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030028 sys.stderr.write('Writing pipe: %s\n' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030029
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030030 pipe = os.popen(c, 'w')
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030031 val = pipe.write(str)
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030032 if pipe.close():
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030033 die('Command failed: %s' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030034
35 return val
36
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030037def read_pipe(c, ignore_error=False):
38 if verbose:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030039 sys.stderr.write('Reading pipe: %s\n' % c)
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -030040
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030041 pipe = os.popen(c, 'rb')
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030042 val = pipe.read()
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030043 if pipe.close() and not ignore_error:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030044 die('Command failed: %s' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030045
46 return val
47
48
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030049def read_pipe_lines(c):
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030050 if verbose:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030051 sys.stderr.write('Reading pipe: %s\n' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030052 ## todo: check return status
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030053 pipe = os.popen(c, 'rb')
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030054 val = pipe.readlines()
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030055 if pipe.close():
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030056 die('Command failed: %s' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030057
58 return val
Simon Hausmanncaace112007-05-15 14:57:57 +020059
Anand Kumria23181212008-08-10 19:26:24 +010060def p4_read_pipe_lines(c):
61 """Specifically invoke p4 on the command supplied. """
62 real_cmd = "%s %s" % ("p4", c)
63 if verbose:
64 print real_cmd
65 return read_pipe_lines(real_cmd)
66
Han-Wen Nienhuys6754a292007-05-23 17:41:50 -030067def system(cmd):
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030068 if verbose:
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -030069 sys.stderr.write("executing %s\n" % cmd)
Han-Wen Nienhuys6754a292007-05-23 17:41:50 -030070 if os.system(cmd) != 0:
71 die("command failed: %s" % cmd)
72
David Brownb9fc6ea2007-09-19 13:12:48 -070073def isP4Exec(kind):
74 """Determine if a Perforce 'kind' should have execute permission
75
76 'p4 help filetypes' gives a list of the types. If it starts with 'x',
77 or x follows one of a few letters. Otherwise, if there is an 'x' after
78 a plus sign, it is also executable"""
79 return (re.search(r"(^[cku]?x)|\+.*x", kind) != None)
80
Chris Pettittc65b6702007-11-01 20:43:14 -070081def setP4ExecBit(file, mode):
82 # Reopens an already open file and changes the execute bit to match
83 # the execute bit setting in the passed in mode.
84
85 p4Type = "+x"
86
87 if not isModeExec(mode):
88 p4Type = getP4OpenedType(file)
89 p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
90 p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
91 if p4Type[-1] == "+":
92 p4Type = p4Type[0:-1]
93
94 system("p4 reopen -t %s %s" % (p4Type, file))
95
96def getP4OpenedType(file):
97 # Returns the perforce file type for the given file.
98
99 result = read_pipe("p4 opened %s" % file)
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100100 match = re.match(".*\((.+)\)\r?$", result)
Chris Pettittc65b6702007-11-01 20:43:14 -0700101 if match:
102 return match.group(1)
103 else:
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100104 die("Could not determine file type for %s (result: '%s')" % (file, result))
Chris Pettittc65b6702007-11-01 20:43:14 -0700105
Chris Pettittb43b0a32007-11-01 20:43:13 -0700106def diffTreePattern():
107 # This is a simple generator for the diff tree regex pattern. This could be
108 # a class variable if this and parseDiffTreeEntry were a part of a class.
109 pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
110 while True:
111 yield pattern
112
113def parseDiffTreeEntry(entry):
114 """Parses a single diff tree entry into its component elements.
115
116 See git-diff-tree(1) manpage for details about the format of the diff
117 output. This method returns a dictionary with the following elements:
118
119 src_mode - The mode of the source file
120 dst_mode - The mode of the destination file
121 src_sha1 - The sha1 for the source file
122 dst_sha1 - The sha1 fr the destination file
123 status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc)
124 status_score - The score for the status (applicable for 'C' and 'R'
125 statuses). This is None if there is no score.
126 src - The path for the source file.
127 dst - The path for the destination file. This is only present for
128 copy or renames. If it is not present, this is None.
129
130 If the pattern is not matched, None is returned."""
131
132 match = diffTreePattern().next().match(entry)
133 if match:
134 return {
135 'src_mode': match.group(1),
136 'dst_mode': match.group(2),
137 'src_sha1': match.group(3),
138 'dst_sha1': match.group(4),
139 'status': match.group(5),
140 'status_score': match.group(6),
141 'src': match.group(7),
142 'dst': match.group(10)
143 }
144 return None
145
Chris Pettittc65b6702007-11-01 20:43:14 -0700146def isModeExec(mode):
147 # Returns True if the given git mode represents an executable file,
148 # otherwise False.
149 return mode[-3:] == "755"
150
151def isModeExecChanged(src_mode, dst_mode):
152 return isModeExec(src_mode) != isModeExec(dst_mode)
153
Scott Lamb9f90c732007-07-15 20:58:10 -0700154def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
Simon Hausmann86949ee2007-03-19 20:59:12 +0100155 cmd = "p4 -G %s" % cmd
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300156 if verbose:
157 sys.stderr.write("Opening pipe: %s\n" % cmd)
Scott Lamb9f90c732007-07-15 20:58:10 -0700158
159 # Use a temporary file to avoid deadlocks without
160 # subprocess.communicate(), which would put another copy
161 # of stdout into memory.
162 stdin_file = None
163 if stdin is not None:
164 stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
165 stdin_file.write(stdin)
166 stdin_file.flush()
167 stdin_file.seek(0)
168
169 p4 = subprocess.Popen(cmd, shell=True,
170 stdin=stdin_file,
171 stdout=subprocess.PIPE)
Simon Hausmann86949ee2007-03-19 20:59:12 +0100172
173 result = []
174 try:
175 while True:
Scott Lamb9f90c732007-07-15 20:58:10 -0700176 entry = marshal.load(p4.stdout)
Simon Hausmann86949ee2007-03-19 20:59:12 +0100177 result.append(entry)
178 except EOFError:
179 pass
Scott Lamb9f90c732007-07-15 20:58:10 -0700180 exitCode = p4.wait()
181 if exitCode != 0:
Simon Hausmannac3e0d72007-05-23 23:32:32 +0200182 entry = {}
183 entry["p4ExitCode"] = exitCode
184 result.append(entry)
Simon Hausmann86949ee2007-03-19 20:59:12 +0100185
186 return result
187
188def p4Cmd(cmd):
189 list = p4CmdList(cmd)
190 result = {}
191 for entry in list:
192 result.update(entry)
193 return result;
194
Simon Hausmanncb2c9db2007-03-24 09:15:11 +0100195def p4Where(depotPath):
196 if not depotPath.endswith("/"):
197 depotPath += "/"
198 output = p4Cmd("where %s..." % depotPath)
Simon Hausmanndc524032007-05-21 09:34:56 +0200199 if output["code"] == "error":
200 return ""
Simon Hausmanncb2c9db2007-03-24 09:15:11 +0100201 clientPath = ""
202 if "path" in output:
203 clientPath = output.get("path")
204 elif "data" in output:
205 data = output.get("data")
206 lastSpace = data.rfind(" ")
207 clientPath = data[lastSpace + 1:]
208
209 if clientPath.endswith("..."):
210 clientPath = clientPath[:-3]
211 return clientPath
212
Simon Hausmann86949ee2007-03-19 20:59:12 +0100213def currentGitBranch():
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -0300214 return read_pipe("git name-rev HEAD").split(" ")[1].strip()
Simon Hausmann86949ee2007-03-19 20:59:12 +0100215
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100216def isValidGitDir(path):
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300217 if (os.path.exists(path + "/HEAD")
218 and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100219 return True;
220 return False
221
Simon Hausmann463e8af2007-05-17 09:13:54 +0200222def parseRevision(ref):
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -0300223 return read_pipe("git rev-parse %s" % ref).strip()
Simon Hausmann463e8af2007-05-17 09:13:54 +0200224
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100225def extractLogMessageFromGitCommit(commit):
226 logMessage = ""
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300227
228 ## fixme: title is first line of commit, not 1st paragraph.
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100229 foundTitle = False
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300230 for log in read_pipe_lines("git cat-file commit %s" % commit):
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100231 if not foundTitle:
232 if len(log) == 1:
Simon Hausmann1c094182007-05-01 23:15:48 +0200233 foundTitle = True
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100234 continue
235
236 logMessage += log
237 return logMessage
238
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300239def extractSettingsGitLog(log):
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100240 values = {}
241 for line in log.split("\n"):
242 line = line.strip()
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300243 m = re.search (r"^ *\[git-p4: (.*)\]$", line)
244 if not m:
245 continue
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100246
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300247 assignments = m.group(1).split (':')
248 for a in assignments:
249 vals = a.split ('=')
250 key = vals[0].strip()
251 val = ('='.join (vals[1:])).strip()
252 if val.endswith ('\"') and val.startswith('"'):
253 val = val[1:-1]
254
255 values[key] = val
256
Simon Hausmann845b42c2007-06-07 09:19:34 +0200257 paths = values.get("depot-paths")
258 if not paths:
259 paths = values.get("depot-path")
Simon Hausmanna3fdd572007-06-07 22:54:32 +0200260 if paths:
261 values['depot-paths'] = paths.split(',')
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300262 return values
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100263
Simon Hausmann8136a632007-03-22 21:27:14 +0100264def gitBranchExists(branch):
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300265 proc = subprocess.Popen(["git", "rev-parse", branch],
266 stderr=subprocess.PIPE, stdout=subprocess.PIPE);
Simon Hausmanncaace112007-05-15 14:57:57 +0200267 return proc.wait() == 0;
Simon Hausmann8136a632007-03-22 21:27:14 +0100268
Simon Hausmann01265102007-05-25 10:36:10 +0200269def gitConfig(key):
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -0300270 return read_pipe("git config %s" % key, ignore_error=True).strip()
Simon Hausmann01265102007-05-25 10:36:10 +0200271
Simon Hausmann062410b2007-07-18 10:56:31 +0200272def p4BranchesInGit(branchesAreInRemotes = True):
273 branches = {}
274
275 cmdline = "git rev-parse --symbolic "
276 if branchesAreInRemotes:
277 cmdline += " --remotes"
278 else:
279 cmdline += " --branches"
280
281 for line in read_pipe_lines(cmdline):
282 line = line.strip()
283
284 ## only import to p4/
285 if not line.startswith('p4/') or line == "p4/HEAD":
286 continue
287 branch = line
288
289 # strip off p4
290 branch = re.sub ("^p4/", "", line)
291
292 branches[branch] = parseRevision(line)
293 return branches
294
Simon Hausmann9ceab362007-06-22 00:01:57 +0200295def findUpstreamBranchPoint(head = "HEAD"):
Simon Hausmann86506fe2007-07-18 12:40:12 +0200296 branches = p4BranchesInGit()
297 # map from depot-path to branch name
298 branchByDepotPath = {}
299 for branch in branches.keys():
300 tip = branches[branch]
301 log = extractLogMessageFromGitCommit(tip)
302 settings = extractSettingsGitLog(log)
303 if settings.has_key("depot-paths"):
304 paths = ",".join(settings["depot-paths"])
305 branchByDepotPath[paths] = "remotes/p4/" + branch
306
Simon Hausmann27d2d812007-06-12 14:31:59 +0200307 settings = None
Simon Hausmann27d2d812007-06-12 14:31:59 +0200308 parent = 0
309 while parent < 65535:
Simon Hausmann9ceab362007-06-22 00:01:57 +0200310 commit = head + "~%s" % parent
Simon Hausmann27d2d812007-06-12 14:31:59 +0200311 log = extractLogMessageFromGitCommit(commit)
312 settings = extractSettingsGitLog(log)
Simon Hausmann86506fe2007-07-18 12:40:12 +0200313 if settings.has_key("depot-paths"):
314 paths = ",".join(settings["depot-paths"])
315 if branchByDepotPath.has_key(paths):
316 return [branchByDepotPath[paths], settings]
Simon Hausmann27d2d812007-06-12 14:31:59 +0200317
Simon Hausmann86506fe2007-07-18 12:40:12 +0200318 parent = parent + 1
Simon Hausmann27d2d812007-06-12 14:31:59 +0200319
Simon Hausmann86506fe2007-07-18 12:40:12 +0200320 return ["", settings]
Simon Hausmann27d2d812007-06-12 14:31:59 +0200321
Simon Hausmann5ca44612007-08-24 17:44:16 +0200322def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
323 if not silent:
324 print ("Creating/updating branch(es) in %s based on origin branch(es)"
325 % localRefPrefix)
326
327 originPrefix = "origin/p4/"
328
329 for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
330 line = line.strip()
331 if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
332 continue
333
334 headName = line[len(originPrefix):]
335 remoteHead = localRefPrefix + headName
336 originHead = line
337
338 original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
339 if (not original.has_key('depot-paths')
340 or not original.has_key('change')):
341 continue
342
343 update = False
344 if not gitBranchExists(remoteHead):
345 if verbose:
346 print "creating %s" % remoteHead
347 update = True
348 else:
349 settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
350 if settings.has_key('change') > 0:
351 if settings['depot-paths'] == original['depot-paths']:
352 originP4Change = int(original['change'])
353 p4Change = int(settings['change'])
354 if originP4Change > p4Change:
355 print ("%s (%s) is newer than %s (%s). "
356 "Updating p4 branch from origin."
357 % (originHead, originP4Change,
358 remoteHead, p4Change))
359 update = True
360 else:
361 print ("Ignoring: %s was imported from %s while "
362 "%s was imported from %s"
363 % (originHead, ','.join(original['depot-paths']),
364 remoteHead, ','.join(settings['depot-paths'])))
365
366 if update:
367 system("git update-ref %s %s" % (remoteHead, originHead))
368
369def originP4BranchesExist():
370 return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
371
Simon Hausmann4f6432d2007-08-26 15:56:36 +0200372def p4ChangesForPaths(depotPaths, changeRange):
373 assert depotPaths
Anand Kumriab340fa42008-08-10 19:26:25 +0100374 output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange)
Simon Hausmann4f6432d2007-08-26 15:56:36 +0200375 for p in depotPaths]))
376
377 changes = []
378 for line in output:
379 changeNum = line.split(" ")[1]
380 changes.append(int(changeNum))
381
382 changes.sort()
383 return changes
384
Simon Hausmannb9847332007-03-20 20:54:23 +0100385class Command:
386 def __init__(self):
387 self.usage = "usage: %prog [options]"
Simon Hausmann8910ac02007-03-26 08:18:55 +0200388 self.needsGit = True
Simon Hausmannb9847332007-03-20 20:54:23 +0100389
390class P4Debug(Command):
Simon Hausmann86949ee2007-03-19 20:59:12 +0100391 def __init__(self):
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100392 Command.__init__(self)
Simon Hausmann86949ee2007-03-19 20:59:12 +0100393 self.options = [
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300394 optparse.make_option("--verbose", dest="verbose", action="store_true",
395 default=False),
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -0300396 ]
Simon Hausmannc8c39112007-03-19 21:02:30 +0100397 self.description = "A tool to debug the output of p4 -G."
Simon Hausmann8910ac02007-03-26 08:18:55 +0200398 self.needsGit = False
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300399 self.verbose = False
Simon Hausmann86949ee2007-03-19 20:59:12 +0100400
401 def run(self, args):
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300402 j = 0
Simon Hausmann86949ee2007-03-19 20:59:12 +0100403 for output in p4CmdList(" ".join(args)):
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300404 print 'Element: %d' % j
405 j += 1
Simon Hausmann86949ee2007-03-19 20:59:12 +0100406 print output
Simon Hausmannb9847332007-03-20 20:54:23 +0100407 return True
Simon Hausmann86949ee2007-03-19 20:59:12 +0100408
Simon Hausmann58346842007-05-21 22:57:06 +0200409class P4RollBack(Command):
410 def __init__(self):
411 Command.__init__(self)
412 self.options = [
Simon Hausmann0c66a782007-05-23 20:07:57 +0200413 optparse.make_option("--verbose", dest="verbose", action="store_true"),
414 optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
Simon Hausmann58346842007-05-21 22:57:06 +0200415 ]
416 self.description = "A tool to debug the multi-branch import. Don't use :)"
Simon Hausmann52102d42007-05-21 23:44:24 +0200417 self.verbose = False
Simon Hausmann0c66a782007-05-23 20:07:57 +0200418 self.rollbackLocalBranches = False
Simon Hausmann58346842007-05-21 22:57:06 +0200419
420 def run(self, args):
421 if len(args) != 1:
422 return False
423 maxChange = int(args[0])
Simon Hausmann0c66a782007-05-23 20:07:57 +0200424
Simon Hausmannad192f22007-05-23 23:44:19 +0200425 if "p4ExitCode" in p4Cmd("changes -m 1"):
Simon Hausmann66a2f522007-05-23 23:40:48 +0200426 die("Problems executing p4");
427
Simon Hausmann0c66a782007-05-23 20:07:57 +0200428 if self.rollbackLocalBranches:
429 refPrefix = "refs/heads/"
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300430 lines = read_pipe_lines("git rev-parse --symbolic --branches")
Simon Hausmann0c66a782007-05-23 20:07:57 +0200431 else:
432 refPrefix = "refs/remotes/"
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300433 lines = read_pipe_lines("git rev-parse --symbolic --remotes")
Simon Hausmann0c66a782007-05-23 20:07:57 +0200434
435 for line in lines:
436 if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -0300437 line = line.strip()
438 ref = refPrefix + line
Simon Hausmann58346842007-05-21 22:57:06 +0200439 log = extractLogMessageFromGitCommit(ref)
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300440 settings = extractSettingsGitLog(log)
441
442 depotPaths = settings['depot-paths']
443 change = settings['change']
444
Simon Hausmann58346842007-05-21 22:57:06 +0200445 changed = False
Simon Hausmann52102d42007-05-21 23:44:24 +0200446
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300447 if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange)
448 for p in depotPaths]))) == 0:
Simon Hausmann52102d42007-05-21 23:44:24 +0200449 print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
450 system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
451 continue
452
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300453 while change and int(change) > maxChange:
Simon Hausmann58346842007-05-21 22:57:06 +0200454 changed = True
Simon Hausmann52102d42007-05-21 23:44:24 +0200455 if self.verbose:
456 print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
Simon Hausmann58346842007-05-21 22:57:06 +0200457 system("git update-ref %s \"%s^\"" % (ref, ref))
458 log = extractLogMessageFromGitCommit(ref)
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300459 settings = extractSettingsGitLog(log)
460
461
462 depotPaths = settings['depot-paths']
463 change = settings['change']
Simon Hausmann58346842007-05-21 22:57:06 +0200464
465 if changed:
Simon Hausmann52102d42007-05-21 23:44:24 +0200466 print "%s rewound to %s" % (ref, change)
Simon Hausmann58346842007-05-21 22:57:06 +0200467
468 return True
469
Simon Hausmann711544b2007-04-01 15:40:46 +0200470class P4Submit(Command):
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100471 def __init__(self):
Simon Hausmannb9847332007-03-20 20:54:23 +0100472 Command.__init__(self)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100473 self.options = [
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -0300474 optparse.make_option("--verbose", dest="verbose", action="store_true"),
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100475 optparse.make_option("--origin", dest="origin"),
Chris Pettittd9a5f252007-10-15 22:15:06 -0700476 optparse.make_option("-M", dest="detectRename", action="store_true"),
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100477 ]
478 self.description = "Submit changes from git to the perforce depot."
Simon Hausmannc9b50e62007-03-29 19:15:24 +0200479 self.usage += " [name of git branch to submit into perforce depot]"
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100480 self.interactive = True
Simon Hausmann95124972007-03-23 09:16:07 +0100481 self.origin = ""
Chris Pettittd9a5f252007-10-15 22:15:06 -0700482 self.detectRename = False
Simon Hausmannb0d10df2007-06-07 13:09:14 +0200483 self.verbose = False
Marius Storm-Olsenf7baba82007-06-07 14:07:01 +0200484 self.isWindows = (platform.system() == "Windows")
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100485
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100486 def check(self):
487 if len(p4CmdList("opened ...")) > 0:
488 die("You have files opened with perforce! Close them before starting the sync.")
489
Simon Hausmannedae1e22008-02-19 09:29:06 +0100490 # replaces everything between 'Description:' and the next P4 submit template field with the
491 # commit message
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100492 def prepareLogMessage(self, template, message):
493 result = ""
494
Simon Hausmannedae1e22008-02-19 09:29:06 +0100495 inDescriptionSection = False
496
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100497 for line in template.split("\n"):
498 if line.startswith("#"):
499 result += line + "\n"
500 continue
501
Simon Hausmannedae1e22008-02-19 09:29:06 +0100502 if inDescriptionSection:
503 if line.startswith("Files:"):
504 inDescriptionSection = False
505 else:
506 continue
507 else:
508 if line.startswith("Description:"):
509 inDescriptionSection = True
510 line += "\n"
511 for messageLine in message.split("\n"):
512 line += "\t" + messageLine + "\n"
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100513
Simon Hausmannedae1e22008-02-19 09:29:06 +0100514 result += line + "\n"
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100515
516 return result
517
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200518 def prepareSubmitTemplate(self):
519 # remove lines in the Files section that show changes to files outside the depot path we're committing into
520 template = ""
521 inFilesSection = False
Anand Kumriab340fa42008-08-10 19:26:25 +0100522 for line in p4_read_pipe_lines("change -o"):
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100523 if line.endswith("\r\n"):
524 line = line[:-2] + "\n"
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200525 if inFilesSection:
526 if line.startswith("\t"):
527 # path starts and ends with a tab
528 path = line[1:]
529 lastTab = path.rfind("\t")
530 if lastTab != -1:
531 path = path[:lastTab]
532 if not path.startswith(self.depotPath):
533 continue
534 else:
535 inFilesSection = False
536 else:
537 if line.startswith("Files:"):
538 inFilesSection = True
539
540 template += line
541
542 return template
543
Han-Wen Nienhuys7cb5cbe2007-05-23 16:55:48 -0300544 def applyCommit(self, id):
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100545 print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
546 diffOpts = ("", "-M")[self.detectRename]
547 diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100548 filesToAdd = set()
549 filesToDelete = set()
Simon Hausmannd336c152007-05-16 09:41:26 +0200550 editedFiles = set()
Chris Pettittc65b6702007-11-01 20:43:14 -0700551 filesToChangeExecBit = {}
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100552 for line in diff:
Chris Pettittb43b0a32007-11-01 20:43:13 -0700553 diff = parseDiffTreeEntry(line)
554 modifier = diff['status']
555 path = diff['src']
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100556 if modifier == "M":
Simon Hausmannd336c152007-05-16 09:41:26 +0200557 system("p4 edit \"%s\"" % path)
Chris Pettittc65b6702007-11-01 20:43:14 -0700558 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
559 filesToChangeExecBit[path] = diff['dst_mode']
Simon Hausmannd336c152007-05-16 09:41:26 +0200560 editedFiles.add(path)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100561 elif modifier == "A":
562 filesToAdd.add(path)
Chris Pettittc65b6702007-11-01 20:43:14 -0700563 filesToChangeExecBit[path] = diff['dst_mode']
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100564 if path in filesToDelete:
565 filesToDelete.remove(path)
566 elif modifier == "D":
567 filesToDelete.add(path)
568 if path in filesToAdd:
569 filesToAdd.remove(path)
Chris Pettittd9a5f252007-10-15 22:15:06 -0700570 elif modifier == "R":
Chris Pettittb43b0a32007-11-01 20:43:13 -0700571 src, dest = diff['src'], diff['dst']
Chris Pettittd9a5f252007-10-15 22:15:06 -0700572 system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest))
573 system("p4 edit \"%s\"" % (dest))
Chris Pettittc65b6702007-11-01 20:43:14 -0700574 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
575 filesToChangeExecBit[dest] = diff['dst_mode']
Chris Pettittd9a5f252007-10-15 22:15:06 -0700576 os.unlink(dest)
577 editedFiles.add(dest)
578 filesToDelete.add(src)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100579 else:
580 die("unknown modifier %s for %s" % (modifier, path))
581
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100582 diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
Simon Hausmann47a130b2007-05-20 16:33:21 +0200583 patchcmd = diffcmd + " | git apply "
Simon Hausmannc1b296b2007-05-20 16:55:05 +0200584 tryPatchCmd = patchcmd + "--check -"
585 applyPatchCmd = patchcmd + "--check --apply -"
Simon Hausmann51a26402007-04-15 09:59:56 +0200586
Simon Hausmann47a130b2007-05-20 16:33:21 +0200587 if os.system(tryPatchCmd) != 0:
Simon Hausmann51a26402007-04-15 09:59:56 +0200588 print "Unfortunately applying the change failed!"
589 print "What do you want to do?"
590 response = "x"
591 while response != "s" and response != "a" and response != "w":
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -0300592 response = raw_input("[s]kip this patch / [a]pply the patch forcibly "
593 "and with .rej files / [w]rite the patch to a file (patch.txt) ")
Simon Hausmann51a26402007-04-15 09:59:56 +0200594 if response == "s":
595 print "Skipping! Good luck with the next patches..."
Simon Hausmann20947142007-09-13 22:10:18 +0200596 for f in editedFiles:
597 system("p4 revert \"%s\"" % f);
598 for f in filesToAdd:
599 system("rm %s" %f)
Simon Hausmann51a26402007-04-15 09:59:56 +0200600 return
601 elif response == "a":
Simon Hausmann47a130b2007-05-20 16:33:21 +0200602 os.system(applyPatchCmd)
Simon Hausmann51a26402007-04-15 09:59:56 +0200603 if len(filesToAdd) > 0:
604 print "You may also want to call p4 add on the following files:"
605 print " ".join(filesToAdd)
606 if len(filesToDelete):
607 print "The following files should be scheduled for deletion with p4 delete:"
608 print " ".join(filesToDelete)
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -0300609 die("Please resolve and submit the conflict manually and "
610 + "continue afterwards with git-p4 submit --continue")
Simon Hausmann51a26402007-04-15 09:59:56 +0200611 elif response == "w":
612 system(diffcmd + " > patch.txt")
613 print "Patch saved to patch.txt in %s !" % self.clientPath
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -0300614 die("Please resolve and submit the conflict manually and "
615 "continue afterwards with git-p4 submit --continue")
Simon Hausmann51a26402007-04-15 09:59:56 +0200616
Simon Hausmann47a130b2007-05-20 16:33:21 +0200617 system(applyPatchCmd)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100618
619 for f in filesToAdd:
Simon Hausmanne6b711f2007-06-11 23:40:25 +0200620 system("p4 add \"%s\"" % f)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100621 for f in filesToDelete:
Simon Hausmanne6b711f2007-06-11 23:40:25 +0200622 system("p4 revert \"%s\"" % f)
623 system("p4 delete \"%s\"" % f)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100624
Chris Pettittc65b6702007-11-01 20:43:14 -0700625 # Set/clear executable bits
626 for f in filesToChangeExecBit.keys():
627 mode = filesToChangeExecBit[f]
628 setP4ExecBit(f, mode)
629
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100630 logMessage = extractLogMessageFromGitCommit(id)
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100631 logMessage = logMessage.strip()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100632
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200633 template = self.prepareSubmitTemplate()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100634
635 if self.interactive:
636 submitTemplate = self.prepareLogMessage(template, logMessage)
Shawn Bohrer67abd412008-03-12 19:03:23 -0500637 if os.environ.has_key("P4DIFF"):
638 del(os.environ["P4DIFF"])
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300639 diff = read_pipe("p4 diff -du ...")
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100640
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100641 newdiff = ""
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100642 for newFile in filesToAdd:
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100643 newdiff += "==== new file ====\n"
644 newdiff += "--- /dev/null\n"
645 newdiff += "+++ %s\n" % newFile
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100646 f = open(newFile, "r")
647 for line in f.readlines():
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100648 newdiff += "+" + line
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100649 f.close()
650
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100651 separatorLine = "######## everything below this line is just the diff #######\n"
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100652
Simon Hausmanne96e4002008-01-04 14:27:55 +0100653 [handle, fileName] = tempfile.mkstemp()
654 tmpFile = os.fdopen(handle, "w+")
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100655 if self.isWindows:
656 submitTemplate = submitTemplate.replace("\n", "\r\n")
657 separatorLine = separatorLine.replace("\n", "\r\n")
658 newdiff = newdiff.replace("\n", "\r\n")
659 tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
Simon Hausmanne96e4002008-01-04 14:27:55 +0100660 tmpFile.close()
661 defaultEditor = "vi"
662 if platform.system() == "Windows":
663 defaultEditor = "notepad"
Shawn Bohrer82cea9f2008-03-12 19:03:24 -0500664 if os.environ.has_key("P4EDITOR"):
665 editor = os.environ.get("P4EDITOR")
666 else:
667 editor = os.environ.get("EDITOR", defaultEditor);
Simon Hausmanne96e4002008-01-04 14:27:55 +0100668 system(editor + " " + fileName)
669 tmpFile = open(fileName, "rb")
670 message = tmpFile.read()
671 tmpFile.close()
672 os.remove(fileName)
673 submitTemplate = message[:message.index(separatorLine)]
674 if self.isWindows:
675 submitTemplate = submitTemplate.replace("\r\n", "\n")
Simon Hausmanncb4f1282007-05-25 22:34:30 +0200676
Simon Hausmanne96e4002008-01-04 14:27:55 +0100677 write_pipe("p4 submit -i", submitTemplate)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100678 else:
679 fileName = "submit.txt"
680 file = open(fileName, "w+")
681 file.write(self.prepareLogMessage(template, logMessage))
682 file.close()
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -0300683 print ("Perforce submit template written as %s. "
684 + "Please review/edit and then use p4 submit -i < %s to submit directly!"
685 % (fileName, fileName))
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100686
687 def run(self, args):
Simon Hausmannc9b50e62007-03-29 19:15:24 +0200688 if len(args) == 0:
689 self.master = currentGitBranch()
Simon Hausmann4280e532007-05-25 08:49:18 +0200690 if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
Simon Hausmannc9b50e62007-03-29 19:15:24 +0200691 die("Detecting current git branch failed!")
692 elif len(args) == 1:
693 self.master = args[0]
694 else:
695 return False
696
Jing Xue4c2d5d72008-06-22 14:12:39 -0400697 allowSubmit = gitConfig("git-p4.allowSubmit")
698 if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
699 die("%s is not in git-p4.allowSubmit" % self.master)
700
Simon Hausmann27d2d812007-06-12 14:31:59 +0200701 [upstream, settings] = findUpstreamBranchPoint()
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200702 self.depotPath = settings['depot-paths'][0]
Simon Hausmann27d2d812007-06-12 14:31:59 +0200703 if len(self.origin) == 0:
704 self.origin = upstream
Simon Hausmanna3fdd572007-06-07 22:54:32 +0200705
706 if self.verbose:
707 print "Origin branch is " + self.origin
Simon Hausmann95124972007-03-23 09:16:07 +0100708
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200709 if len(self.depotPath) == 0:
Simon Hausmann95124972007-03-23 09:16:07 +0100710 print "Internal error: cannot locate perforce depot path from existing branches"
711 sys.exit(128)
712
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200713 self.clientPath = p4Where(self.depotPath)
Simon Hausmann95124972007-03-23 09:16:07 +0100714
Simon Hausmann51a26402007-04-15 09:59:56 +0200715 if len(self.clientPath) == 0:
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200716 print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath
Simon Hausmann95124972007-03-23 09:16:07 +0100717 sys.exit(128)
718
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200719 print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
Simon Hausmann7944f142007-05-21 11:04:26 +0200720 self.oldWorkingDirectory = os.getcwd()
Simon Hausmannc1b296b2007-05-20 16:55:05 +0200721
Simon Hausmann51a26402007-04-15 09:59:56 +0200722 os.chdir(self.clientPath)
Simon Hausmann31f9ec12007-08-21 11:53:02 +0200723 print "Syncronizing p4 checkout..."
724 system("p4 sync ...")
Simon Hausmann95124972007-03-23 09:16:07 +0100725
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100726 self.check()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100727
Simon Hausmann4c750c02008-02-19 09:37:16 +0100728 commits = []
729 for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
730 commits.append(line.strip())
731 commits.reverse()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100732
733 while len(commits) > 0:
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100734 commit = commits[0]
735 commits = commits[1:]
Han-Wen Nienhuys7cb5cbe2007-05-23 16:55:48 -0300736 self.applyCommit(commit)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100737 if not self.interactive:
738 break
739
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100740 if len(commits) == 0:
Simon Hausmann4c750c02008-02-19 09:37:16 +0100741 print "All changes applied!"
742 os.chdir(self.oldWorkingDirectory)
Simon Hausmann14594f42007-08-22 09:07:15 +0200743
Simon Hausmann4c750c02008-02-19 09:37:16 +0100744 sync = P4Sync()
745 sync.run([])
Simon Hausmann14594f42007-08-22 09:07:15 +0200746
Simon Hausmann4c750c02008-02-19 09:37:16 +0100747 rebase = P4Rebase()
748 rebase.rebase()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100749
Simon Hausmannb9847332007-03-20 20:54:23 +0100750 return True
751
Simon Hausmann711544b2007-04-01 15:40:46 +0200752class P4Sync(Command):
Simon Hausmannb9847332007-03-20 20:54:23 +0100753 def __init__(self):
754 Command.__init__(self)
755 self.options = [
756 optparse.make_option("--branch", dest="branch"),
757 optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
758 optparse.make_option("--changesfile", dest="changesFile"),
759 optparse.make_option("--silent", dest="silent", action="store_true"),
Simon Hausmannef48f902007-05-17 22:17:49 +0200760 optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
Simon Hausmanna028a982007-05-23 00:03:08 +0200761 optparse.make_option("--verbose", dest="verbose", action="store_true"),
Han-Wen Nienhuysd2c6dd32007-05-23 18:49:35 -0300762 optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
763 help="Import into refs/heads/ , not refs/remotes"),
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -0300764 optparse.make_option("--max-changes", dest="maxChanges"),
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -0300765 optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +0100766 help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
767 optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
768 help="Only sync files that are included in the Perforce Client Spec")
Simon Hausmannb9847332007-03-20 20:54:23 +0100769 ]
770 self.description = """Imports from Perforce into a git repository.\n
771 example:
772 //depot/my/project/ -- to import the current head
773 //depot/my/project/@all -- to import everything
774 //depot/my/project/@1,6 -- to import only from revision 1 to 6
775
776 (a ... is not needed in the path p4 specification, it's added implicitly)"""
777
778 self.usage += " //depot/path[@revRange]"
Simon Hausmannb9847332007-03-20 20:54:23 +0100779 self.silent = False
Simon Hausmannb9847332007-03-20 20:54:23 +0100780 self.createdBranches = Set()
781 self.committedChanges = Set()
Simon Hausmann569d1bd2007-03-22 21:34:16 +0100782 self.branch = ""
Simon Hausmannb9847332007-03-20 20:54:23 +0100783 self.detectBranches = False
Simon Hausmanncb53e1f2007-04-08 00:12:02 +0200784 self.detectLabels = False
Simon Hausmannb9847332007-03-20 20:54:23 +0100785 self.changesFile = ""
Simon Hausmann01265102007-05-25 10:36:10 +0200786 self.syncWithOrigin = True
Simon Hausmann4b97ffb2007-05-18 21:45:23 +0200787 self.verbose = False
Simon Hausmanna028a982007-05-23 00:03:08 +0200788 self.importIntoRemotes = True
Simon Hausmann01a9c9c2007-05-23 00:07:35 +0200789 self.maxChanges = ""
Marius Storm-Olsenc1f91972007-05-24 14:07:55 +0200790 self.isWindows = (platform.system() == "Windows")
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -0300791 self.keepRepoPath = False
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300792 self.depotPaths = None
Simon Hausmann3c699642007-06-16 13:09:21 +0200793 self.p4BranchesInGit = []
Tommy Thorn354081d2008-02-03 10:38:51 -0800794 self.cloneExclude = []
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +0100795 self.useClientSpec = False
796 self.clientSpecDirs = []
Simon Hausmannb9847332007-03-20 20:54:23 +0100797
Simon Hausmann01265102007-05-25 10:36:10 +0200798 if gitConfig("git-p4.syncFromOrigin") == "false":
799 self.syncWithOrigin = False
800
Simon Hausmannb9847332007-03-20 20:54:23 +0100801 def extractFilesFromCommit(self, commit):
Tommy Thorn354081d2008-02-03 10:38:51 -0800802 self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
803 for path in self.cloneExclude]
Simon Hausmannb9847332007-03-20 20:54:23 +0100804 files = []
805 fnum = 0
806 while commit.has_key("depotFile%s" % fnum):
807 path = commit["depotFile%s" % fnum]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300808
Tommy Thorn354081d2008-02-03 10:38:51 -0800809 if [p for p in self.cloneExclude
810 if path.startswith (p)]:
811 found = False
812 else:
813 found = [p for p in self.depotPaths
814 if path.startswith (p)]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300815 if not found:
Simon Hausmannb9847332007-03-20 20:54:23 +0100816 fnum = fnum + 1
817 continue
818
819 file = {}
820 file["path"] = path
821 file["rev"] = commit["rev%s" % fnum]
822 file["action"] = commit["action%s" % fnum]
823 file["type"] = commit["type%s" % fnum]
824 files.append(file)
825 fnum = fnum + 1
826 return files
827
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300828 def stripRepoPath(self, path, prefixes):
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -0300829 if self.keepRepoPath:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300830 prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -0300831
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300832 for p in prefixes:
833 if path.startswith(p):
834 path = path[len(p):]
835
836 return path
Han-Wen Nienhuys6754a292007-05-23 17:41:50 -0300837
Simon Hausmann71b112d2007-05-19 11:54:11 +0200838 def splitFilesIntoBranches(self, commit):
Simon Hausmannd5904672007-05-19 11:07:32 +0200839 branches = {}
Simon Hausmann71b112d2007-05-19 11:54:11 +0200840 fnum = 0
841 while commit.has_key("depotFile%s" % fnum):
842 path = commit["depotFile%s" % fnum]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300843 found = [p for p in self.depotPaths
844 if path.startswith (p)]
845 if not found:
Simon Hausmann71b112d2007-05-19 11:54:11 +0200846 fnum = fnum + 1
847 continue
848
849 file = {}
850 file["path"] = path
851 file["rev"] = commit["rev%s" % fnum]
852 file["action"] = commit["action%s" % fnum]
853 file["type"] = commit["type%s" % fnum]
854 fnum = fnum + 1
855
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300856 relPath = self.stripRepoPath(path, self.depotPaths)
Simon Hausmannb9847332007-03-20 20:54:23 +0100857
Simon Hausmann4b97ffb2007-05-18 21:45:23 +0200858 for branch in self.knownBranches.keys():
Han-Wen Nienhuys6754a292007-05-23 17:41:50 -0300859
860 # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
861 if relPath.startswith(branch + "/"):
Simon Hausmannd5904672007-05-19 11:07:32 +0200862 if branch not in branches:
863 branches[branch] = []
Simon Hausmann71b112d2007-05-19 11:54:11 +0200864 branches[branch].append(file)
Simon Hausmann6555b2c2007-06-17 11:25:34 +0200865 break
Simon Hausmannb9847332007-03-20 20:54:23 +0100866
867 return branches
868
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300869 ## Should move this out, doesn't use SELF.
870 def readP4Files(self, files):
Simon Hausmann30b59402008-03-03 11:55:48 +0100871 filesForCommit = []
872 filesToRead = []
873
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +0100874 for f in files:
Simon Hausmann30b59402008-03-03 11:55:48 +0100875 includeFile = True
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +0100876 for val in self.clientSpecDirs:
877 if f['path'].startswith(val[0]):
Simon Hausmann30b59402008-03-03 11:55:48 +0100878 if val[1] <= 0:
879 includeFile = False
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +0100880 break
881
Simon Hausmann30b59402008-03-03 11:55:48 +0100882 if includeFile:
883 filesForCommit.append(f)
884 if f['action'] != 'delete':
885 filesToRead.append(f)
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300886
Simon Hausmann30b59402008-03-03 11:55:48 +0100887 filedata = []
888 if len(filesToRead) > 0:
889 filedata = p4CmdList('-x - print',
890 stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
891 for f in filesToRead]),
892 stdin_mode='w+')
Han-Wen Nienhuysf2eda792007-05-23 18:49:35 -0300893
Simon Hausmann30b59402008-03-03 11:55:48 +0100894 if "p4ExitCode" in filedata[0]:
895 die("Problems executing p4. Error: [%d]."
896 % (filedata[0]['p4ExitCode']));
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300897
Han-Wen Nienhuysd2c6dd32007-05-23 18:49:35 -0300898 j = 0;
899 contents = {}
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300900 while j < len(filedata):
Han-Wen Nienhuysd2c6dd32007-05-23 18:49:35 -0300901 stat = filedata[j]
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300902 j += 1
Marius Storm-Olsen8ff45f22008-03-03 13:42:47 +0100903 text = [];
Jason McMullanf3e95122007-12-05 12:16:56 -0500904 while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
Marius Storm-Olsen8ff45f22008-03-03 13:42:47 +0100905 text.append(filedata[j]['data'])
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300906 j += 1
Marius Storm-Olsen8ff45f22008-03-03 13:42:47 +0100907 text = ''.join(text)
Han-Wen Nienhuys1b9a4682007-05-23 18:49:35 -0300908
909 if not stat.has_key('depotFile'):
910 sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
911 continue
912
Marius Storm-Olsen8ff45f22008-03-03 13:42:47 +0100913 if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
914 text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
915 elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
Daniel Barkalow2d71bde2008-07-22 12:48:57 -0400916 text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text)
Marius Storm-Olsen8ff45f22008-03-03 13:42:47 +0100917
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300918 contents[stat['depotFile']] = text
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300919
Simon Hausmann30b59402008-03-03 11:55:48 +0100920 for f in filesForCommit:
921 path = f['path']
922 if contents.has_key(path):
923 f['data'] = contents[path]
924
925 return filesForCommit
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300926
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300927 def commit(self, details, files, branch, branchPrefixes, parent = ""):
Simon Hausmannb9847332007-03-20 20:54:23 +0100928 epoch = details["time"]
929 author = details["user"]
930
Simon Hausmann4b97ffb2007-05-18 21:45:23 +0200931 if self.verbose:
932 print "commit into %s" % branch
933
Han-Wen Nienhuys96e07dd2007-05-23 18:49:35 -0300934 # start with reading files; if that fails, we should not
935 # create a commit.
936 new_files = []
937 for f in files:
938 if [p for p in branchPrefixes if f['path'].startswith(p)]:
939 new_files.append (f)
940 else:
941 sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +0100942 files = self.readP4Files(new_files)
Han-Wen Nienhuys96e07dd2007-05-23 18:49:35 -0300943
Simon Hausmannb9847332007-03-20 20:54:23 +0100944 self.gitStream.write("commit %s\n" % branch)
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300945# gitStream.write("mark :%s\n" % details["change"])
Simon Hausmannb9847332007-03-20 20:54:23 +0100946 self.committedChanges.add(int(details["change"]))
947 committer = ""
Simon Hausmannb607e712007-05-20 10:55:54 +0200948 if author not in self.users:
949 self.getUserMapFromPerforceServer()
Simon Hausmannb9847332007-03-20 20:54:23 +0100950 if author in self.users:
Simon Hausmann0828ab12007-03-20 20:59:30 +0100951 committer = "%s %s %s" % (self.users[author], epoch, self.tz)
Simon Hausmannb9847332007-03-20 20:54:23 +0100952 else:
Simon Hausmann0828ab12007-03-20 20:59:30 +0100953 committer = "%s <a@b> %s %s" % (author, epoch, self.tz)
Simon Hausmannb9847332007-03-20 20:54:23 +0100954
955 self.gitStream.write("committer %s\n" % committer)
956
957 self.gitStream.write("data <<EOT\n")
958 self.gitStream.write(details["desc"])
Simon Hausmann6581de02007-06-11 10:01:58 +0200959 self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s"
960 % (','.join (branchPrefixes), details["change"]))
961 if len(details['options']) > 0:
962 self.gitStream.write(": options = %s" % details['options'])
963 self.gitStream.write("]\nEOT\n\n")
Simon Hausmannb9847332007-03-20 20:54:23 +0100964
965 if len(parent) > 0:
Simon Hausmann4b97ffb2007-05-18 21:45:23 +0200966 if self.verbose:
967 print "parent %s" % parent
Simon Hausmannb9847332007-03-20 20:54:23 +0100968 self.gitStream.write("from %s\n" % parent)
969
Simon Hausmannb9847332007-03-20 20:54:23 +0100970 for file in files:
Simon Hausmannb9847332007-03-20 20:54:23 +0100971 if file["type"] == "apple":
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300972 print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
Simon Hausmannb9847332007-03-20 20:54:23 +0100973 continue
974
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300975 relPath = self.stripRepoPath(file['path'], branchPrefixes)
976 if file["action"] == "delete":
Simon Hausmannb9847332007-03-20 20:54:23 +0100977 self.gitStream.write("D %s\n" % relPath)
978 else:
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300979 data = file['data']
Simon Hausmannb9847332007-03-20 20:54:23 +0100980
Simon Hausmann74276ec2007-08-07 12:28:00 +0200981 mode = "644"
David Brownb9fc6ea2007-09-19 13:12:48 -0700982 if isP4Exec(file["type"]):
Simon Hausmann74276ec2007-08-07 12:28:00 +0200983 mode = "755"
984 elif file["type"] == "symlink":
985 mode = "120000"
986 # p4 print on a symlink contains "target\n", so strip it off
987 data = data[:-1]
988
Marius Storm-Olsenc1f91972007-05-24 14:07:55 +0200989 if self.isWindows and file["type"].endswith("text"):
990 data = data.replace("\r\n", "\n")
991
Simon Hausmann74276ec2007-08-07 12:28:00 +0200992 self.gitStream.write("M %s inline %s\n" % (mode, relPath))
Simon Hausmannb9847332007-03-20 20:54:23 +0100993 self.gitStream.write("data %s\n" % len(data))
994 self.gitStream.write(data)
995 self.gitStream.write("\n")
996
997 self.gitStream.write("\n")
998
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +0200999 change = int(details["change"])
1000
Simon Hausmann9bda3a82007-05-19 12:05:40 +02001001 if self.labels.has_key(change):
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001002 label = self.labels[change]
1003 labelDetails = label[0]
1004 labelRevisions = label[1]
Simon Hausmann71b112d2007-05-19 11:54:11 +02001005 if self.verbose:
1006 print "Change %s is labelled %s" % (change, labelDetails)
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001007
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001008 files = p4CmdList("files " + ' '.join (["%s...@%s" % (p, change)
1009 for p in branchPrefixes]))
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001010
1011 if len(files) == len(labelRevisions):
1012
1013 cleanedFiles = {}
1014 for info in files:
1015 if info["action"] == "delete":
1016 continue
1017 cleanedFiles[info["depotFile"]] = info["rev"]
1018
1019 if cleanedFiles == labelRevisions:
1020 self.gitStream.write("tag tag_%s\n" % labelDetails["label"])
1021 self.gitStream.write("from %s\n" % branch)
1022
1023 owner = labelDetails["Owner"]
1024 tagger = ""
1025 if author in self.users:
1026 tagger = "%s %s %s" % (self.users[owner], epoch, self.tz)
1027 else:
1028 tagger = "%s <a@b> %s %s" % (owner, epoch, self.tz)
1029 self.gitStream.write("tagger %s\n" % tagger)
1030 self.gitStream.write("data <<EOT\n")
1031 self.gitStream.write(labelDetails["Description"])
1032 self.gitStream.write("EOT\n\n")
1033
1034 else:
Simon Hausmanna46668f2007-03-28 17:05:38 +02001035 if not self.silent:
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001036 print ("Tag %s does not match with change %s: files do not match."
1037 % (labelDetails["label"], change))
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001038
1039 else:
Simon Hausmanna46668f2007-03-28 17:05:38 +02001040 if not self.silent:
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001041 print ("Tag %s does not match with change %s: file count is different."
1042 % (labelDetails["label"], change))
Simon Hausmannb9847332007-03-20 20:54:23 +01001043
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001044 def getUserCacheFilename(self):
Simon Hausmannb2d2d162007-07-25 09:31:38 +02001045 home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
1046 return home + "/.gitp4-usercache.txt"
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001047
Simon Hausmannb607e712007-05-20 10:55:54 +02001048 def getUserMapFromPerforceServer(self):
Simon Hausmannebd81162007-05-24 00:24:52 +02001049 if self.userMapFromPerforceServer:
1050 return
Simon Hausmannb9847332007-03-20 20:54:23 +01001051 self.users = {}
1052
1053 for output in p4CmdList("users"):
1054 if not output.has_key("User"):
1055 continue
1056 self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
1057
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001058
1059 s = ''
1060 for (key, val) in self.users.items():
1061 s += "%s\t%s\n" % (key, val)
1062
1063 open(self.getUserCacheFilename(), "wb").write(s)
Simon Hausmannebd81162007-05-24 00:24:52 +02001064 self.userMapFromPerforceServer = True
Simon Hausmannb607e712007-05-20 10:55:54 +02001065
1066 def loadUserMapFromCache(self):
1067 self.users = {}
Simon Hausmannebd81162007-05-24 00:24:52 +02001068 self.userMapFromPerforceServer = False
Simon Hausmannb607e712007-05-20 10:55:54 +02001069 try:
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001070 cache = open(self.getUserCacheFilename(), "rb")
Simon Hausmannb607e712007-05-20 10:55:54 +02001071 lines = cache.readlines()
1072 cache.close()
1073 for line in lines:
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -03001074 entry = line.strip().split("\t")
Simon Hausmannb607e712007-05-20 10:55:54 +02001075 self.users[entry[0]] = entry[1]
1076 except IOError:
1077 self.getUserMapFromPerforceServer()
1078
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001079 def getLabels(self):
1080 self.labels = {}
1081
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001082 l = p4CmdList("labels %s..." % ' '.join (self.depotPaths))
Simon Hausmann10c32112007-04-08 10:15:47 +02001083 if len(l) > 0 and not self.silent:
Shun Kei Leung183f8432007-11-21 11:01:19 +08001084 print "Finding files belonging to labels in %s" % `self.depotPaths`
Simon Hausmann01ce1fe2007-04-07 23:46:50 +02001085
1086 for output in l:
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001087 label = output["label"]
1088 revisions = {}
1089 newestChange = 0
Simon Hausmann71b112d2007-05-19 11:54:11 +02001090 if self.verbose:
1091 print "Querying files for label %s" % label
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001092 for file in p4CmdList("files "
1093 + ' '.join (["%s...@%s" % (p, label)
1094 for p in self.depotPaths])):
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001095 revisions[file["depotFile"]] = file["rev"]
1096 change = int(file["change"])
1097 if change > newestChange:
1098 newestChange = change
1099
Simon Hausmann9bda3a82007-05-19 12:05:40 +02001100 self.labels[newestChange] = [output, revisions]
1101
1102 if self.verbose:
1103 print "Label changes: %s" % self.labels.keys()
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001104
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001105 def guessProjectName(self):
1106 for p in self.depotPaths:
Simon Hausmann6e5295c2007-06-11 08:50:57 +02001107 if p.endswith("/"):
1108 p = p[:-1]
1109 p = p[p.strip().rfind("/") + 1:]
1110 if not p.endswith("/"):
1111 p += "/"
1112 return p
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001113
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001114 def getBranchMapping(self):
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001115 lostAndFoundBranches = set()
1116
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001117 for info in p4CmdList("branches"):
1118 details = p4Cmd("branch -o %s" % info["branch"])
1119 viewIdx = 0
1120 while details.has_key("View%s" % viewIdx):
1121 paths = details["View%s" % viewIdx].split(" ")
1122 viewIdx = viewIdx + 1
1123 # require standard //depot/foo/... //depot/bar/... mapping
1124 if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."):
1125 continue
1126 source = paths[0]
1127 destination = paths[1]
Simon Hausmann6509e192007-06-07 09:41:53 +02001128 ## HACK
1129 if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]):
1130 source = source[len(self.depotPaths[0]):-4]
1131 destination = destination[len(self.depotPaths[0]):-4]
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001132
Simon Hausmann1a2edf42007-06-17 15:10:24 +02001133 if destination in self.knownBranches:
1134 if not self.silent:
1135 print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
1136 print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
1137 continue
1138
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001139 self.knownBranches[destination] = source
1140
1141 lostAndFoundBranches.discard(destination)
1142
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001143 if source not in self.knownBranches:
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001144 lostAndFoundBranches.add(source)
1145
1146
1147 for branch in lostAndFoundBranches:
1148 self.knownBranches[branch] = branch
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001149
Simon Hausmann38f9f5e2007-11-15 10:38:45 +01001150 def getBranchMappingFromGitBranches(self):
1151 branches = p4BranchesInGit(self.importIntoRemotes)
1152 for branch in branches.keys():
1153 if branch == "master":
1154 branch = "main"
1155 else:
1156 branch = branch[len(self.projectName):]
1157 self.knownBranches[branch] = branch
1158
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001159 def listExistingP4GitBranches(self):
Simon Hausmann144ff462007-07-18 17:27:50 +02001160 # branches holds mapping from name to commit
1161 branches = p4BranchesInGit(self.importIntoRemotes)
1162 self.p4BranchesInGit = branches.keys()
1163 for branch in branches.keys():
1164 self.initialParents[self.refPrefix + branch] = branches[branch]
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001165
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001166 def updateOptionDict(self, d):
1167 option_keys = {}
1168 if self.keepRepoPath:
1169 option_keys['keepRepoPath'] = 1
1170
1171 d["options"] = ' '.join(sorted(option_keys.keys()))
1172
1173 def readOptions(self, d):
1174 self.keepRepoPath = (d.has_key('options')
1175 and ('keepRepoPath' in d['options']))
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001176
Simon Hausmann8134f692007-08-26 16:44:55 +02001177 def gitRefForBranch(self, branch):
1178 if branch == "main":
1179 return self.refPrefix + "master"
1180
1181 if len(branch) <= 0:
1182 return branch
1183
1184 return self.refPrefix + self.projectName + branch
1185
Simon Hausmann1ca3d712007-08-26 17:36:55 +02001186 def gitCommitByP4Change(self, ref, change):
1187 if self.verbose:
1188 print "looking in ref " + ref + " for change %s using bisect..." % change
1189
1190 earliestCommit = ""
1191 latestCommit = parseRevision(ref)
1192
1193 while True:
1194 if self.verbose:
1195 print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
1196 next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
1197 if len(next) == 0:
1198 if self.verbose:
1199 print "argh"
1200 return ""
1201 log = extractLogMessageFromGitCommit(next)
1202 settings = extractSettingsGitLog(log)
1203 currentChange = int(settings['change'])
1204 if self.verbose:
1205 print "current change %s" % currentChange
1206
1207 if currentChange == change:
1208 if self.verbose:
1209 print "found %s" % next
1210 return next
1211
1212 if currentChange < change:
1213 earliestCommit = "^%s" % next
1214 else:
1215 latestCommit = "%s" % next
1216
1217 return ""
1218
1219 def importNewBranch(self, branch, maxChange):
1220 # make fast-import flush all changes to disk and update the refs using the checkpoint
1221 # command so that we can try to find the branch parent in the git history
1222 self.gitStream.write("checkpoint\n\n");
1223 self.gitStream.flush();
1224 branchPrefix = self.depotPaths[0] + branch + "/"
1225 range = "@1,%s" % maxChange
1226 #print "prefix" + branchPrefix
1227 changes = p4ChangesForPaths([branchPrefix], range)
1228 if len(changes) <= 0:
1229 return False
1230 firstChange = changes[0]
1231 #print "first change in branch: %s" % firstChange
1232 sourceBranch = self.knownBranches[branch]
1233 sourceDepotPath = self.depotPaths[0] + sourceBranch
1234 sourceRef = self.gitRefForBranch(sourceBranch)
1235 #print "source " + sourceBranch
1236
1237 branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"])
1238 #print "branch parent: %s" % branchParentChange
1239 gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
1240 if len(gitParent) > 0:
1241 self.initialParents[self.gitRefForBranch(branch)] = gitParent
1242 #print "parent git commit: %s" % gitParent
1243
1244 self.importChanges(changes)
1245 return True
1246
Simon Hausmanne87f37a2007-08-26 16:00:52 +02001247 def importChanges(self, changes):
1248 cnt = 1
1249 for change in changes:
1250 description = p4Cmd("describe %s" % change)
1251 self.updateOptionDict(description)
1252
1253 if not self.silent:
1254 sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
1255 sys.stdout.flush()
1256 cnt = cnt + 1
1257
1258 try:
1259 if self.detectBranches:
1260 branches = self.splitFilesIntoBranches(description)
1261 for branch in branches.keys():
1262 ## HACK --hwn
1263 branchPrefix = self.depotPaths[0] + branch + "/"
1264
1265 parent = ""
1266
1267 filesForCommit = branches[branch]
1268
1269 if self.verbose:
1270 print "branch is %s" % branch
1271
1272 self.updatedBranches.add(branch)
1273
1274 if branch not in self.createdBranches:
1275 self.createdBranches.add(branch)
1276 parent = self.knownBranches[branch]
1277 if parent == branch:
1278 parent = ""
Simon Hausmann1ca3d712007-08-26 17:36:55 +02001279 else:
1280 fullBranch = self.projectName + branch
1281 if fullBranch not in self.p4BranchesInGit:
1282 if not self.silent:
1283 print("\n Importing new branch %s" % fullBranch);
1284 if self.importNewBranch(branch, change - 1):
1285 parent = ""
1286 self.p4BranchesInGit.append(fullBranch)
1287 if not self.silent:
1288 print("\n Resuming with change %s" % change);
1289
1290 if self.verbose:
1291 print "parent determined through known branches: %s" % parent
Simon Hausmanne87f37a2007-08-26 16:00:52 +02001292
Simon Hausmann8134f692007-08-26 16:44:55 +02001293 branch = self.gitRefForBranch(branch)
1294 parent = self.gitRefForBranch(parent)
Simon Hausmanne87f37a2007-08-26 16:00:52 +02001295
1296 if self.verbose:
1297 print "looking for initial parent for %s; current parent is %s" % (branch, parent)
1298
1299 if len(parent) == 0 and branch in self.initialParents:
1300 parent = self.initialParents[branch]
1301 del self.initialParents[branch]
1302
1303 self.commit(description, filesForCommit, branch, [branchPrefix], parent)
1304 else:
1305 files = self.extractFilesFromCommit(description)
1306 self.commit(description, files, self.branch, self.depotPaths,
1307 self.initialParent)
1308 self.initialParent = ""
1309 except IOError:
1310 print self.gitError.read()
1311 sys.exit(1)
1312
Simon Hausmannc208a242007-08-26 16:07:18 +02001313 def importHeadRevision(self, revision):
1314 print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
1315
1316 details = { "user" : "git perforce import user", "time" : int(time.time()) }
1317 details["desc"] = ("Initial import of %s from the state at revision %s"
1318 % (' '.join(self.depotPaths), revision))
1319 details["change"] = revision
1320 newestRevision = 0
1321
1322 fileCnt = 0
1323 for info in p4CmdList("files "
1324 + ' '.join(["%s...%s"
1325 % (p, revision)
1326 for p in self.depotPaths])):
1327
1328 if info['code'] == 'error':
1329 sys.stderr.write("p4 returned an error: %s\n"
1330 % info['data'])
1331 sys.exit(1)
1332
1333
1334 change = int(info["change"])
1335 if change > newestRevision:
1336 newestRevision = change
1337
1338 if info["action"] == "delete":
1339 # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
1340 #fileCnt = fileCnt + 1
1341 continue
1342
1343 for prop in ["depotFile", "rev", "action", "type" ]:
1344 details["%s%s" % (prop, fileCnt)] = info[prop]
1345
1346 fileCnt = fileCnt + 1
1347
1348 details["change"] = newestRevision
1349 self.updateOptionDict(details)
1350 try:
1351 self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
1352 except IOError:
1353 print "IO error with git fast-import. Is your git version recent enough?"
1354 print self.gitError.read()
1355
1356
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001357 def getClientSpec(self):
1358 specList = p4CmdList( "client -o" )
1359 temp = {}
1360 for entry in specList:
1361 for k,v in entry.iteritems():
1362 if k.startswith("View"):
1363 if v.startswith('"'):
1364 start = 1
1365 else:
1366 start = 0
1367 index = v.find("...")
1368 v = v[start:index]
1369 if v.startswith("-"):
1370 v = v[1:]
1371 temp[v] = -len(v)
1372 else:
1373 temp[v] = len(v)
1374 self.clientSpecDirs = temp.items()
1375 self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) )
1376
Simon Hausmannb9847332007-03-20 20:54:23 +01001377 def run(self, args):
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001378 self.depotPaths = []
Simon Hausmann179caeb2007-03-22 22:17:42 +01001379 self.changeRange = ""
1380 self.initialParent = ""
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001381 self.previousDepotPaths = []
Han-Wen Nienhuysce6f33c2007-05-23 16:46:29 -03001382
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001383 # map from branch depot path to parent branch
1384 self.knownBranches = {}
1385 self.initialParents = {}
Simon Hausmann5ca44612007-08-24 17:44:16 +02001386 self.hasOrigin = originP4BranchesExist()
Simon Hausmanna43ff002007-06-11 09:59:27 +02001387 if not self.syncWithOrigin:
1388 self.hasOrigin = False
Simon Hausmann179caeb2007-03-22 22:17:42 +01001389
Simon Hausmanna028a982007-05-23 00:03:08 +02001390 if self.importIntoRemotes:
1391 self.refPrefix = "refs/remotes/p4/"
1392 else:
Marius Storm-Olsendb775552007-06-07 15:13:59 +02001393 self.refPrefix = "refs/heads/p4/"
Simon Hausmanna028a982007-05-23 00:03:08 +02001394
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001395 if self.syncWithOrigin and self.hasOrigin:
1396 if not self.silent:
1397 print "Syncing with origin first by calling git fetch origin"
1398 system("git fetch origin")
Simon Hausmann10f880f2007-05-24 22:28:28 +02001399
Simon Hausmann569d1bd2007-03-22 21:34:16 +01001400 if len(self.branch) == 0:
Marius Storm-Olsendb775552007-06-07 15:13:59 +02001401 self.branch = self.refPrefix + "master"
Simon Hausmanna028a982007-05-23 00:03:08 +02001402 if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
Simon Hausmann48df6fd2007-05-17 21:18:53 +02001403 system("git update-ref %s refs/heads/p4" % self.branch)
Simon Hausmann48df6fd2007-05-17 21:18:53 +02001404 system("git branch -D p4");
Simon Hausmannfaf1bd22007-05-21 10:05:30 +02001405 # create it /after/ importing, when master exists
Simon Hausmann0058a332007-08-24 17:46:16 +02001406 if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
Simon Hausmanna3c55c02007-05-27 15:48:01 +02001407 system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
Simon Hausmann179caeb2007-03-22 22:17:42 +01001408
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001409 if self.useClientSpec or gitConfig("p4.useclientspec") == "true":
1410 self.getClientSpec()
1411
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001412 # TODO: should always look at previous commits,
1413 # merge with previous imports, if possible.
1414 if args == []:
Simon Hausmannd414c742007-05-25 11:36:42 +02001415 if self.hasOrigin:
Simon Hausmann5ca44612007-08-24 17:44:16 +02001416 createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent)
Simon Hausmannabcd7902007-05-24 22:25:36 +02001417 self.listExistingP4GitBranches()
1418
1419 if len(self.p4BranchesInGit) > 1:
1420 if not self.silent:
1421 print "Importing from/into multiple branches"
1422 self.detectBranches = True
Simon Hausmann967f72e2007-03-23 09:30:41 +01001423
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001424 if self.verbose:
1425 print "branches: %s" % self.p4BranchesInGit
1426
1427 p4Change = 0
1428 for branch in self.p4BranchesInGit:
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001429 logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch)
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001430
1431 settings = extractSettingsGitLog(logMsg)
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001432
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001433 self.readOptions(settings)
1434 if (settings.has_key('depot-paths')
1435 and settings.has_key ('change')):
1436 change = int(settings['change']) + 1
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001437 p4Change = max(p4Change, change)
1438
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001439 depotPaths = sorted(settings['depot-paths'])
1440 if self.previousDepotPaths == []:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001441 self.previousDepotPaths = depotPaths
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001442 else:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001443 paths = []
1444 for (prev, cur) in zip(self.previousDepotPaths, depotPaths):
Simon Hausmann583e1702007-06-07 09:37:13 +02001445 for i in range(0, min(len(cur), len(prev))):
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001446 if cur[i] <> prev[i]:
Simon Hausmann583e1702007-06-07 09:37:13 +02001447 i = i - 1
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001448 break
1449
Simon Hausmann583e1702007-06-07 09:37:13 +02001450 paths.append (cur[:i + 1])
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001451
1452 self.previousDepotPaths = paths
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001453
1454 if p4Change > 0:
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001455 self.depotPaths = sorted(self.previousDepotPaths)
Simon Hausmannd5904672007-05-19 11:07:32 +02001456 self.changeRange = "@%s,#head" % p4Change
Simon Hausmann330f53b2007-06-07 09:39:51 +02001457 if not self.detectBranches:
1458 self.initialParent = parseRevision(self.branch)
Simon Hausmann341dc1c2007-05-21 00:39:16 +02001459 if not self.silent and not self.detectBranches:
Simon Hausmann967f72e2007-03-23 09:30:41 +01001460 print "Performing incremental import into %s git branch" % self.branch
Simon Hausmann569d1bd2007-03-22 21:34:16 +01001461
Simon Hausmannf9162f62007-05-17 09:02:45 +02001462 if not self.branch.startswith("refs/"):
1463 self.branch = "refs/heads/" + self.branch
Simon Hausmann179caeb2007-03-22 22:17:42 +01001464
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001465 if len(args) == 0 and self.depotPaths:
Simon Hausmannb9847332007-03-20 20:54:23 +01001466 if not self.silent:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001467 print "Depot paths: %s" % ' '.join(self.depotPaths)
Simon Hausmannb9847332007-03-20 20:54:23 +01001468 else:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001469 if self.depotPaths and self.depotPaths != args:
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001470 print ("previous import used depot path %s and now %s was specified. "
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001471 "This doesn't work!" % (' '.join (self.depotPaths),
1472 ' '.join (args)))
Simon Hausmannb9847332007-03-20 20:54:23 +01001473 sys.exit(1)
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001474
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001475 self.depotPaths = sorted(args)
Simon Hausmannb9847332007-03-20 20:54:23 +01001476
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001477 revision = ""
Simon Hausmannb9847332007-03-20 20:54:23 +01001478 self.users = {}
Simon Hausmannb9847332007-03-20 20:54:23 +01001479
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001480 newPaths = []
1481 for p in self.depotPaths:
1482 if p.find("@") != -1:
1483 atIdx = p.index("@")
1484 self.changeRange = p[atIdx:]
1485 if self.changeRange == "@all":
1486 self.changeRange = ""
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001487 elif ',' not in self.changeRange:
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001488 revision = self.changeRange
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001489 self.changeRange = ""
Han-Wen Nienhuys7fcff9d2007-07-23 15:56:37 -07001490 p = p[:atIdx]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001491 elif p.find("#") != -1:
1492 hashIdx = p.index("#")
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001493 revision = p[hashIdx:]
Han-Wen Nienhuys7fcff9d2007-07-23 15:56:37 -07001494 p = p[:hashIdx]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001495 elif self.previousDepotPaths == []:
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001496 revision = "#head"
Simon Hausmannb9847332007-03-20 20:54:23 +01001497
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001498 p = re.sub ("\.\.\.$", "", p)
1499 if not p.endswith("/"):
1500 p += "/"
1501
1502 newPaths.append(p)
1503
1504 self.depotPaths = newPaths
1505
Simon Hausmannb9847332007-03-20 20:54:23 +01001506
Simon Hausmannb607e712007-05-20 10:55:54 +02001507 self.loadUserMapFromCache()
Simon Hausmanncb53e1f2007-04-08 00:12:02 +02001508 self.labels = {}
1509 if self.detectLabels:
1510 self.getLabels();
Simon Hausmannb9847332007-03-20 20:54:23 +01001511
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001512 if self.detectBranches:
Simon Hausmanndf450922007-06-08 08:49:22 +02001513 ## FIXME - what's a P4 projectName ?
1514 self.projectName = self.guessProjectName()
1515
Simon Hausmann38f9f5e2007-11-15 10:38:45 +01001516 if self.hasOrigin:
1517 self.getBranchMappingFromGitBranches()
1518 else:
1519 self.getBranchMapping()
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001520 if self.verbose:
1521 print "p4-git branches: %s" % self.p4BranchesInGit
1522 print "initial parents: %s" % self.initialParents
1523 for b in self.p4BranchesInGit:
1524 if b != "master":
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001525
1526 ## FIXME
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001527 b = b[len(self.projectName):]
1528 self.createdBranches.add(b)
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001529
Simon Hausmannf291b4e2007-04-14 11:21:50 +02001530 self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
Simon Hausmannb9847332007-03-20 20:54:23 +01001531
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001532 importProcess = subprocess.Popen(["git", "fast-import"],
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001533 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1534 stderr=subprocess.PIPE);
Simon Hausmann08483582007-05-15 14:31:06 +02001535 self.gitOutput = importProcess.stdout
1536 self.gitStream = importProcess.stdin
1537 self.gitError = importProcess.stderr
Simon Hausmannb9847332007-03-20 20:54:23 +01001538
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001539 if revision:
Simon Hausmannc208a242007-08-26 16:07:18 +02001540 self.importHeadRevision(revision)
Simon Hausmannb9847332007-03-20 20:54:23 +01001541 else:
1542 changes = []
1543
Simon Hausmann0828ab12007-03-20 20:59:30 +01001544 if len(self.changesFile) > 0:
Simon Hausmannb9847332007-03-20 20:54:23 +01001545 output = open(self.changesFile).readlines()
1546 changeSet = Set()
1547 for line in output:
1548 changeSet.add(int(line))
1549
1550 for change in changeSet:
1551 changes.append(change)
1552
1553 changes.sort()
1554 else:
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001555 if self.verbose:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001556 print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001557 self.changeRange)
Simon Hausmann4f6432d2007-08-26 15:56:36 +02001558 changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
Simon Hausmannb9847332007-03-20 20:54:23 +01001559
Simon Hausmann01a9c9c2007-05-23 00:07:35 +02001560 if len(self.maxChanges) > 0:
Han-Wen Nienhuys7fcff9d2007-07-23 15:56:37 -07001561 changes = changes[:min(int(self.maxChanges), len(changes))]
Simon Hausmann01a9c9c2007-05-23 00:07:35 +02001562
Simon Hausmannb9847332007-03-20 20:54:23 +01001563 if len(changes) == 0:
Simon Hausmann0828ab12007-03-20 20:59:30 +01001564 if not self.silent:
Simon Hausmann341dc1c2007-05-21 00:39:16 +02001565 print "No changes to import!"
Simon Hausmann1f52af62007-04-08 00:07:02 +02001566 return True
Simon Hausmannb9847332007-03-20 20:54:23 +01001567
Simon Hausmanna9d1a272007-06-11 23:28:03 +02001568 if not self.silent and not self.detectBranches:
1569 print "Import destination: %s" % self.branch
1570
Simon Hausmann341dc1c2007-05-21 00:39:16 +02001571 self.updatedBranches = set()
1572
Simon Hausmanne87f37a2007-08-26 16:00:52 +02001573 self.importChanges(changes)
Simon Hausmannb9847332007-03-20 20:54:23 +01001574
Simon Hausmann341dc1c2007-05-21 00:39:16 +02001575 if not self.silent:
1576 print ""
1577 if len(self.updatedBranches) > 0:
1578 sys.stdout.write("Updated branches: ")
1579 for b in self.updatedBranches:
1580 sys.stdout.write("%s " % b)
1581 sys.stdout.write("\n")
Simon Hausmannb9847332007-03-20 20:54:23 +01001582
Simon Hausmannb9847332007-03-20 20:54:23 +01001583 self.gitStream.close()
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001584 if importProcess.wait() != 0:
1585 die("fast-import failed: %s" % self.gitError.read())
Simon Hausmannb9847332007-03-20 20:54:23 +01001586 self.gitOutput.close()
1587 self.gitError.close()
1588
Simon Hausmannb9847332007-03-20 20:54:23 +01001589 return True
1590
Simon Hausmann01ce1fe2007-04-07 23:46:50 +02001591class P4Rebase(Command):
1592 def __init__(self):
1593 Command.__init__(self)
Simon Hausmann01265102007-05-25 10:36:10 +02001594 self.options = [ ]
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001595 self.description = ("Fetches the latest revision from perforce and "
1596 + "rebases the current work (branch) against it")
Simon Hausmann68c42152007-06-07 12:51:03 +02001597 self.verbose = False
Simon Hausmann01ce1fe2007-04-07 23:46:50 +02001598
1599 def run(self, args):
1600 sync = P4Sync()
1601 sync.run([])
Simon Hausmannd7e38682007-06-12 14:34:46 +02001602
Simon Hausmann14594f42007-08-22 09:07:15 +02001603 return self.rebase()
1604
1605 def rebase(self):
Simon Hausmann36ee4ee2008-01-07 14:21:45 +01001606 if os.system("git update-index --refresh") != 0:
1607 die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
1608 if len(read_pipe("git diff-index HEAD --")) > 0:
1609 die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
1610
Simon Hausmannd7e38682007-06-12 14:34:46 +02001611 [upstream, settings] = findUpstreamBranchPoint()
1612 if len(upstream) == 0:
1613 die("Cannot find upstream branchpoint for rebase")
1614
1615 # the branchpoint may be p4/foo~3, so strip off the parent
1616 upstream = re.sub("~[0-9]+$", "", upstream)
1617
1618 print "Rebasing the current branch onto %s" % upstream
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -03001619 oldHead = read_pipe("git rev-parse HEAD").strip()
Simon Hausmannd7e38682007-06-12 14:34:46 +02001620 system("git rebase %s" % upstream)
Simon Hausmann1f52af62007-04-08 00:07:02 +02001621 system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
Simon Hausmann01ce1fe2007-04-07 23:46:50 +02001622 return True
1623
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001624class P4Clone(P4Sync):
1625 def __init__(self):
1626 P4Sync.__init__(self)
1627 self.description = "Creates a new git repository and imports from Perforce into it"
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001628 self.usage = "usage: %prog [options] //depot/path[@revRange]"
Tommy Thorn354081d2008-02-03 10:38:51 -08001629 self.options += [
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001630 optparse.make_option("--destination", dest="cloneDestination",
1631 action='store', default=None,
Tommy Thorn354081d2008-02-03 10:38:51 -08001632 help="where to leave result of the clone"),
1633 optparse.make_option("-/", dest="cloneExclude",
1634 action="append", type="string",
1635 help="exclude depot path")
1636 ]
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001637 self.cloneDestination = None
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001638 self.needsGit = False
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001639
Tommy Thorn354081d2008-02-03 10:38:51 -08001640 # This is required for the "append" cloneExclude action
1641 def ensure_value(self, attr, value):
1642 if not hasattr(self, attr) or getattr(self, attr) is None:
1643 setattr(self, attr, value)
1644 return getattr(self, attr)
1645
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001646 def defaultDestination(self, args):
1647 ## TODO: use common prefix of args?
1648 depotPath = args[0]
1649 depotDir = re.sub("(@[^@]*)$", "", depotPath)
1650 depotDir = re.sub("(#[^#]*)$", "", depotDir)
Toby Allsopp053d9e42008-02-05 09:41:43 +13001651 depotDir = re.sub(r"\.\.\.$", "", depotDir)
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001652 depotDir = re.sub(r"/$", "", depotDir)
1653 return os.path.split(depotDir)[1]
1654
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001655 def run(self, args):
1656 if len(args) < 1:
1657 return False
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001658
1659 if self.keepRepoPath and not self.cloneDestination:
1660 sys.stderr.write("Must specify destination for --keep-path\n")
1661 sys.exit(1)
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001662
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001663 depotPaths = args
Simon Hausmann5e100b52007-06-07 21:12:25 +02001664
1665 if not self.cloneDestination and len(depotPaths) > 1:
1666 self.cloneDestination = depotPaths[-1]
1667 depotPaths = depotPaths[:-1]
1668
Tommy Thorn354081d2008-02-03 10:38:51 -08001669 self.cloneExclude = ["/"+p for p in self.cloneExclude]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001670 for p in depotPaths:
1671 if not p.startswith("//"):
1672 return False
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001673
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001674 if not self.cloneDestination:
Marius Storm-Olsen98ad4fa2007-06-07 15:08:33 +02001675 self.cloneDestination = self.defaultDestination(args)
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001676
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001677 print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
Kevin Greenc3bf3f12007-06-11 16:48:07 -04001678 if not os.path.exists(self.cloneDestination):
1679 os.makedirs(self.cloneDestination)
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001680 os.chdir(self.cloneDestination)
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001681 system("git init")
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001682 self.gitdir = os.getcwd() + "/.git"
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001683 if not P4Sync.run(self, depotPaths):
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001684 return False
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001685 if self.branch != "master":
Simon Hausmann8f9b2e02007-05-18 22:13:26 +02001686 if gitBranchExists("refs/remotes/p4/master"):
1687 system("git branch master refs/remotes/p4/master")
1688 system("git checkout -f")
1689 else:
1690 print "Could not detect main branch. No checkout/master branch created."
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001691
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001692 return True
1693
Simon Hausmann09d89de2007-06-20 23:10:28 +02001694class P4Branches(Command):
1695 def __init__(self):
1696 Command.__init__(self)
1697 self.options = [ ]
1698 self.description = ("Shows the git branches that hold imports and their "
1699 + "corresponding perforce depot paths")
1700 self.verbose = False
1701
1702 def run(self, args):
Simon Hausmann5ca44612007-08-24 17:44:16 +02001703 if originP4BranchesExist():
1704 createOrUpdateBranchesFromOrigin()
1705
Simon Hausmann09d89de2007-06-20 23:10:28 +02001706 cmdline = "git rev-parse --symbolic "
1707 cmdline += " --remotes"
1708
1709 for line in read_pipe_lines(cmdline):
1710 line = line.strip()
1711
1712 if not line.startswith('p4/') or line == "p4/HEAD":
1713 continue
1714 branch = line
1715
1716 log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
1717 settings = extractSettingsGitLog(log)
1718
1719 print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
1720 return True
1721
Simon Hausmannb9847332007-03-20 20:54:23 +01001722class HelpFormatter(optparse.IndentedHelpFormatter):
1723 def __init__(self):
1724 optparse.IndentedHelpFormatter.__init__(self)
1725
1726 def format_description(self, description):
1727 if description:
1728 return description + "\n"
1729 else:
1730 return ""
Simon Hausmann4f5cf762007-03-19 22:25:17 +01001731
Simon Hausmann86949ee2007-03-19 20:59:12 +01001732def printUsage(commands):
1733 print "usage: %s <command> [options]" % sys.argv[0]
1734 print ""
1735 print "valid commands: %s" % ", ".join(commands)
1736 print ""
1737 print "Try %s <command> --help for command specific help." % sys.argv[0]
1738 print ""
1739
1740commands = {
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001741 "debug" : P4Debug,
1742 "submit" : P4Submit,
Marius Storm-Olsena9834f52007-10-09 16:16:09 +02001743 "commit" : P4Submit,
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001744 "sync" : P4Sync,
1745 "rebase" : P4Rebase,
1746 "clone" : P4Clone,
Simon Hausmann09d89de2007-06-20 23:10:28 +02001747 "rollback" : P4RollBack,
1748 "branches" : P4Branches
Simon Hausmann86949ee2007-03-19 20:59:12 +01001749}
1750
Simon Hausmann86949ee2007-03-19 20:59:12 +01001751
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001752def main():
1753 if len(sys.argv[1:]) == 0:
1754 printUsage(commands.keys())
1755 sys.exit(2)
Simon Hausmann86949ee2007-03-19 20:59:12 +01001756
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001757 cmd = ""
1758 cmdName = sys.argv[1]
1759 try:
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001760 klass = commands[cmdName]
1761 cmd = klass()
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001762 except KeyError:
1763 print "unknown command %s" % cmdName
1764 print ""
1765 printUsage(commands.keys())
1766 sys.exit(2)
Simon Hausmann4f5cf762007-03-19 22:25:17 +01001767
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001768 options = cmd.options
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001769 cmd.gitdir = os.environ.get("GIT_DIR", None)
Simon Hausmann86949ee2007-03-19 20:59:12 +01001770
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001771 args = sys.argv[2:]
Simon Hausmanne20a9e52007-03-26 00:13:51 +02001772
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001773 if len(options) > 0:
1774 options.append(optparse.make_option("--git-dir", dest="gitdir"))
Simon Hausmanne20a9e52007-03-26 00:13:51 +02001775
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001776 parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
1777 options,
1778 description = cmd.description,
1779 formatter = HelpFormatter())
Simon Hausmann86949ee2007-03-19 20:59:12 +01001780
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001781 (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
1782 global verbose
1783 verbose = cmd.verbose
1784 if cmd.needsGit:
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001785 if cmd.gitdir == None:
1786 cmd.gitdir = os.path.abspath(".git")
1787 if not isValidGitDir(cmd.gitdir):
1788 cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
1789 if os.path.exists(cmd.gitdir):
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001790 cdup = read_pipe("git rev-parse --show-cdup").strip()
1791 if len(cdup) > 0:
1792 os.chdir(cdup);
1793
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001794 if not isValidGitDir(cmd.gitdir):
1795 if isValidGitDir(cmd.gitdir + "/.git"):
1796 cmd.gitdir += "/.git"
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001797 else:
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001798 die("fatal: cannot locate git repository at %s" % cmd.gitdir)
Simon Hausmann8910ac02007-03-26 08:18:55 +02001799
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001800 os.environ["GIT_DIR"] = cmd.gitdir
Simon Hausmann4f5cf762007-03-19 22:25:17 +01001801
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001802 if not cmd.run(args):
1803 parser.print_help()
Simon Hausmann4f5cf762007-03-19 22:25:17 +01001804
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001805
1806if __name__ == '__main__':
1807 main()