blob: d47596f3280d0e7765ba59e67c481a95dce7ab4c [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
Reilly Grant1d7367d2009-09-10 00:02:38 -070011import optparse, sys, os, marshal, subprocess, shelve
12import tempfile, getopt, os.path, time, platform
Han-Wen Nienhuysce6f33c2007-05-23 16:46:29 -030013import re
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -030014
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030015verbose = False
Simon Hausmann86949ee2007-03-19 20:59:12 +010016
Anand Kumria21a50752008-08-10 19:26:28 +010017
18def p4_build_cmd(cmd):
19 """Build a suitable p4 command line.
20
21 This consolidates building and returning a p4 command line into one
22 location. It means that hooking into the environment, or other configuration
23 can be done more easily.
24 """
Anand Kumriaabcaf072008-08-10 19:26:31 +010025 real_cmd = "%s " % "p4"
26
27 user = gitConfig("git-p4.user")
28 if len(user) > 0:
29 real_cmd += "-u %s " % user
30
31 password = gitConfig("git-p4.password")
32 if len(password) > 0:
33 real_cmd += "-P %s " % password
34
35 port = gitConfig("git-p4.port")
36 if len(port) > 0:
37 real_cmd += "-p %s " % port
38
39 host = gitConfig("git-p4.host")
40 if len(host) > 0:
41 real_cmd += "-h %s " % host
42
43 client = gitConfig("git-p4.client")
44 if len(client) > 0:
45 real_cmd += "-c %s " % client
46
47 real_cmd += "%s" % (cmd)
Anand Kumriaee064272008-08-10 19:26:29 +010048 if verbose:
49 print real_cmd
Anand Kumria21a50752008-08-10 19:26:28 +010050 return real_cmd
51
Robert Blum053fd0c2008-08-01 12:50:03 -070052def chdir(dir):
53 if os.name == 'nt':
54 os.environ['PWD']=dir
55 os.chdir(dir)
56
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030057def die(msg):
58 if verbose:
59 raise Exception(msg)
60 else:
61 sys.stderr.write(msg + "\n")
62 sys.exit(1)
63
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030064def write_pipe(c, str):
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030065 if verbose:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030066 sys.stderr.write('Writing pipe: %s\n' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030067
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030068 pipe = os.popen(c, 'w')
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030069 val = pipe.write(str)
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030070 if pipe.close():
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030071 die('Command failed: %s' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030072
73 return val
74
Anand Kumriad9429192008-08-14 23:40:38 +010075def p4_write_pipe(c, str):
76 real_cmd = p4_build_cmd(c)
Tor Arvid Lund893d3402008-08-21 23:11:40 +020077 return write_pipe(real_cmd, str)
Anand Kumriad9429192008-08-14 23:40:38 +010078
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030079def read_pipe(c, ignore_error=False):
80 if verbose:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030081 sys.stderr.write('Reading pipe: %s\n' % c)
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -030082
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030083 pipe = os.popen(c, 'rb')
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030084 val = pipe.read()
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030085 if pipe.close() and not ignore_error:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030086 die('Command failed: %s' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030087
88 return val
89
Anand Kumriad9429192008-08-14 23:40:38 +010090def p4_read_pipe(c, ignore_error=False):
91 real_cmd = p4_build_cmd(c)
92 return read_pipe(real_cmd, ignore_error)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030093
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030094def read_pipe_lines(c):
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -030095 if verbose:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -030096 sys.stderr.write('Reading pipe: %s\n' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030097 ## todo: check return status
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -030098 pipe = os.popen(c, 'rb')
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -030099 val = pipe.readlines()
Han-Wen Nienhuysbce4c5f2007-05-23 17:14:33 -0300100 if pipe.close():
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -0300101 die('Command failed: %s' % c)
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300102
103 return val
Simon Hausmanncaace112007-05-15 14:57:57 +0200104
Anand Kumria23181212008-08-10 19:26:24 +0100105def p4_read_pipe_lines(c):
106 """Specifically invoke p4 on the command supplied. """
Anand Kumria155af832008-08-10 19:26:30 +0100107 real_cmd = p4_build_cmd(c)
Anand Kumria23181212008-08-10 19:26:24 +0100108 return read_pipe_lines(real_cmd)
109
Han-Wen Nienhuys6754a292007-05-23 17:41:50 -0300110def system(cmd):
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -0300111 if verbose:
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300112 sys.stderr.write("executing %s\n" % cmd)
Han-Wen Nienhuys6754a292007-05-23 17:41:50 -0300113 if os.system(cmd) != 0:
114 die("command failed: %s" % cmd)
115
Anand Kumriabf9320f2008-08-10 19:26:26 +0100116def p4_system(cmd):
117 """Specifically invoke p4 as the system command. """
Anand Kumria155af832008-08-10 19:26:30 +0100118 real_cmd = p4_build_cmd(cmd)
Anand Kumriabf9320f2008-08-10 19:26:26 +0100119 return system(real_cmd)
120
David Brownb9fc6ea2007-09-19 13:12:48 -0700121def isP4Exec(kind):
122 """Determine if a Perforce 'kind' should have execute permission
123
124 'p4 help filetypes' gives a list of the types. If it starts with 'x',
125 or x follows one of a few letters. Otherwise, if there is an 'x' after
126 a plus sign, it is also executable"""
127 return (re.search(r"(^[cku]?x)|\+.*x", kind) != None)
128
Chris Pettittc65b6702007-11-01 20:43:14 -0700129def setP4ExecBit(file, mode):
130 # Reopens an already open file and changes the execute bit to match
131 # the execute bit setting in the passed in mode.
132
133 p4Type = "+x"
134
135 if not isModeExec(mode):
136 p4Type = getP4OpenedType(file)
137 p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
138 p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
139 if p4Type[-1] == "+":
140 p4Type = p4Type[0:-1]
141
Anand Kumria87b611d2008-08-10 19:26:27 +0100142 p4_system("reopen -t %s %s" % (p4Type, file))
Chris Pettittc65b6702007-11-01 20:43:14 -0700143
144def getP4OpenedType(file):
145 # Returns the perforce file type for the given file.
146
Anand Kumriaa7d3ef92008-08-14 23:40:39 +0100147 result = p4_read_pipe("opened %s" % file)
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100148 match = re.match(".*\((.+)\)\r?$", result)
Chris Pettittc65b6702007-11-01 20:43:14 -0700149 if match:
150 return match.group(1)
151 else:
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100152 die("Could not determine file type for %s (result: '%s')" % (file, result))
Chris Pettittc65b6702007-11-01 20:43:14 -0700153
Chris Pettittb43b0a32007-11-01 20:43:13 -0700154def diffTreePattern():
155 # This is a simple generator for the diff tree regex pattern. This could be
156 # a class variable if this and parseDiffTreeEntry were a part of a class.
157 pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
158 while True:
159 yield pattern
160
161def parseDiffTreeEntry(entry):
162 """Parses a single diff tree entry into its component elements.
163
164 See git-diff-tree(1) manpage for details about the format of the diff
165 output. This method returns a dictionary with the following elements:
166
167 src_mode - The mode of the source file
168 dst_mode - The mode of the destination file
169 src_sha1 - The sha1 for the source file
170 dst_sha1 - The sha1 fr the destination file
171 status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc)
172 status_score - The score for the status (applicable for 'C' and 'R'
173 statuses). This is None if there is no score.
174 src - The path for the source file.
175 dst - The path for the destination file. This is only present for
176 copy or renames. If it is not present, this is None.
177
178 If the pattern is not matched, None is returned."""
179
180 match = diffTreePattern().next().match(entry)
181 if match:
182 return {
183 'src_mode': match.group(1),
184 'dst_mode': match.group(2),
185 'src_sha1': match.group(3),
186 'dst_sha1': match.group(4),
187 'status': match.group(5),
188 'status_score': match.group(6),
189 'src': match.group(7),
190 'dst': match.group(10)
191 }
192 return None
193
Chris Pettittc65b6702007-11-01 20:43:14 -0700194def isModeExec(mode):
195 # Returns True if the given git mode represents an executable file,
196 # otherwise False.
197 return mode[-3:] == "755"
198
199def isModeExecChanged(src_mode, dst_mode):
200 return isModeExec(src_mode) != isModeExec(dst_mode)
201
Luke Diamandb9327052009-07-30 00:13:46 +0100202def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
Anand Kumria155af832008-08-10 19:26:30 +0100203 cmd = p4_build_cmd("-G %s" % (cmd))
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -0300204 if verbose:
205 sys.stderr.write("Opening pipe: %s\n" % cmd)
Scott Lamb9f90c732007-07-15 20:58:10 -0700206
207 # Use a temporary file to avoid deadlocks without
208 # subprocess.communicate(), which would put another copy
209 # of stdout into memory.
210 stdin_file = None
211 if stdin is not None:
212 stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
213 stdin_file.write(stdin)
214 stdin_file.flush()
215 stdin_file.seek(0)
216
217 p4 = subprocess.Popen(cmd, shell=True,
218 stdin=stdin_file,
219 stdout=subprocess.PIPE)
Simon Hausmann86949ee2007-03-19 20:59:12 +0100220
221 result = []
222 try:
223 while True:
Scott Lamb9f90c732007-07-15 20:58:10 -0700224 entry = marshal.load(p4.stdout)
Luke Diamandb9327052009-07-30 00:13:46 +0100225 if cb is not None:
226 cb(entry)
227 else:
228 result.append(entry)
Simon Hausmann86949ee2007-03-19 20:59:12 +0100229 except EOFError:
230 pass
Scott Lamb9f90c732007-07-15 20:58:10 -0700231 exitCode = p4.wait()
232 if exitCode != 0:
Simon Hausmannac3e0d72007-05-23 23:32:32 +0200233 entry = {}
234 entry["p4ExitCode"] = exitCode
235 result.append(entry)
Simon Hausmann86949ee2007-03-19 20:59:12 +0100236
237 return result
238
239def p4Cmd(cmd):
240 list = p4CmdList(cmd)
241 result = {}
242 for entry in list:
243 result.update(entry)
244 return result;
245
Simon Hausmanncb2c9db2007-03-24 09:15:11 +0100246def p4Where(depotPath):
247 if not depotPath.endswith("/"):
248 depotPath += "/"
Tor Arvid Lund7f705dc2008-12-04 14:37:33 +0100249 depotPath = depotPath + "..."
250 outputList = p4CmdList("where %s" % depotPath)
251 output = None
252 for entry in outputList:
Tor Arvid Lund75bc9572008-12-09 16:41:50 +0100253 if "depotFile" in entry:
254 if entry["depotFile"] == depotPath:
255 output = entry
256 break
257 elif "data" in entry:
258 data = entry.get("data")
259 space = data.find(" ")
260 if data[:space] == depotPath:
261 output = entry
262 break
Tor Arvid Lund7f705dc2008-12-04 14:37:33 +0100263 if output == None:
264 return ""
Simon Hausmanndc524032007-05-21 09:34:56 +0200265 if output["code"] == "error":
266 return ""
Simon Hausmanncb2c9db2007-03-24 09:15:11 +0100267 clientPath = ""
268 if "path" in output:
269 clientPath = output.get("path")
270 elif "data" in output:
271 data = output.get("data")
272 lastSpace = data.rfind(" ")
273 clientPath = data[lastSpace + 1:]
274
275 if clientPath.endswith("..."):
276 clientPath = clientPath[:-3]
277 return clientPath
278
Simon Hausmann86949ee2007-03-19 20:59:12 +0100279def currentGitBranch():
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -0300280 return read_pipe("git name-rev HEAD").split(" ")[1].strip()
Simon Hausmann86949ee2007-03-19 20:59:12 +0100281
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100282def isValidGitDir(path):
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300283 if (os.path.exists(path + "/HEAD")
284 and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100285 return True;
286 return False
287
Simon Hausmann463e8af2007-05-17 09:13:54 +0200288def parseRevision(ref):
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -0300289 return read_pipe("git rev-parse %s" % ref).strip()
Simon Hausmann463e8af2007-05-17 09:13:54 +0200290
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100291def extractLogMessageFromGitCommit(commit):
292 logMessage = ""
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300293
294 ## fixme: title is first line of commit, not 1st paragraph.
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100295 foundTitle = False
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300296 for log in read_pipe_lines("git cat-file commit %s" % commit):
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100297 if not foundTitle:
298 if len(log) == 1:
Simon Hausmann1c094182007-05-01 23:15:48 +0200299 foundTitle = True
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100300 continue
301
302 logMessage += log
303 return logMessage
304
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300305def extractSettingsGitLog(log):
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100306 values = {}
307 for line in log.split("\n"):
308 line = line.strip()
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300309 m = re.search (r"^ *\[git-p4: (.*)\]$", line)
310 if not m:
311 continue
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100312
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300313 assignments = m.group(1).split (':')
314 for a in assignments:
315 vals = a.split ('=')
316 key = vals[0].strip()
317 val = ('='.join (vals[1:])).strip()
318 if val.endswith ('\"') and val.startswith('"'):
319 val = val[1:-1]
320
321 values[key] = val
322
Simon Hausmann845b42c2007-06-07 09:19:34 +0200323 paths = values.get("depot-paths")
324 if not paths:
325 paths = values.get("depot-path")
Simon Hausmanna3fdd572007-06-07 22:54:32 +0200326 if paths:
327 values['depot-paths'] = paths.split(',')
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300328 return values
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100329
Simon Hausmann8136a632007-03-22 21:27:14 +0100330def gitBranchExists(branch):
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300331 proc = subprocess.Popen(["git", "rev-parse", branch],
332 stderr=subprocess.PIPE, stdout=subprocess.PIPE);
Simon Hausmanncaace112007-05-15 14:57:57 +0200333 return proc.wait() == 0;
Simon Hausmann8136a632007-03-22 21:27:14 +0100334
John Chapman36bd8442008-11-08 14:22:49 +1100335_gitConfig = {}
Tor Arvid Lund99f790f2011-03-15 13:08:01 +0100336def gitConfig(key, args = None): # set args to "--bool", for instance
John Chapman36bd8442008-11-08 14:22:49 +1100337 if not _gitConfig.has_key(key):
Tor Arvid Lund99f790f2011-03-15 13:08:01 +0100338 argsFilter = ""
339 if args != None:
340 argsFilter = "%s " % args
341 cmd = "git config %s%s" % (argsFilter, key)
342 _gitConfig[key] = read_pipe(cmd, ignore_error=True).strip()
John Chapman36bd8442008-11-08 14:22:49 +1100343 return _gitConfig[key]
Simon Hausmann01265102007-05-25 10:36:10 +0200344
Simon Hausmann062410b2007-07-18 10:56:31 +0200345def p4BranchesInGit(branchesAreInRemotes = True):
346 branches = {}
347
348 cmdline = "git rev-parse --symbolic "
349 if branchesAreInRemotes:
350 cmdline += " --remotes"
351 else:
352 cmdline += " --branches"
353
354 for line in read_pipe_lines(cmdline):
355 line = line.strip()
356
357 ## only import to p4/
358 if not line.startswith('p4/') or line == "p4/HEAD":
359 continue
360 branch = line
361
362 # strip off p4
363 branch = re.sub ("^p4/", "", line)
364
365 branches[branch] = parseRevision(line)
366 return branches
367
Simon Hausmann9ceab362007-06-22 00:01:57 +0200368def findUpstreamBranchPoint(head = "HEAD"):
Simon Hausmann86506fe2007-07-18 12:40:12 +0200369 branches = p4BranchesInGit()
370 # map from depot-path to branch name
371 branchByDepotPath = {}
372 for branch in branches.keys():
373 tip = branches[branch]
374 log = extractLogMessageFromGitCommit(tip)
375 settings = extractSettingsGitLog(log)
376 if settings.has_key("depot-paths"):
377 paths = ",".join(settings["depot-paths"])
378 branchByDepotPath[paths] = "remotes/p4/" + branch
379
Simon Hausmann27d2d812007-06-12 14:31:59 +0200380 settings = None
Simon Hausmann27d2d812007-06-12 14:31:59 +0200381 parent = 0
382 while parent < 65535:
Simon Hausmann9ceab362007-06-22 00:01:57 +0200383 commit = head + "~%s" % parent
Simon Hausmann27d2d812007-06-12 14:31:59 +0200384 log = extractLogMessageFromGitCommit(commit)
385 settings = extractSettingsGitLog(log)
Simon Hausmann86506fe2007-07-18 12:40:12 +0200386 if settings.has_key("depot-paths"):
387 paths = ",".join(settings["depot-paths"])
388 if branchByDepotPath.has_key(paths):
389 return [branchByDepotPath[paths], settings]
Simon Hausmann27d2d812007-06-12 14:31:59 +0200390
Simon Hausmann86506fe2007-07-18 12:40:12 +0200391 parent = parent + 1
Simon Hausmann27d2d812007-06-12 14:31:59 +0200392
Simon Hausmann86506fe2007-07-18 12:40:12 +0200393 return ["", settings]
Simon Hausmann27d2d812007-06-12 14:31:59 +0200394
Simon Hausmann5ca44612007-08-24 17:44:16 +0200395def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
396 if not silent:
397 print ("Creating/updating branch(es) in %s based on origin branch(es)"
398 % localRefPrefix)
399
400 originPrefix = "origin/p4/"
401
402 for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
403 line = line.strip()
404 if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
405 continue
406
407 headName = line[len(originPrefix):]
408 remoteHead = localRefPrefix + headName
409 originHead = line
410
411 original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
412 if (not original.has_key('depot-paths')
413 or not original.has_key('change')):
414 continue
415
416 update = False
417 if not gitBranchExists(remoteHead):
418 if verbose:
419 print "creating %s" % remoteHead
420 update = True
421 else:
422 settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
423 if settings.has_key('change') > 0:
424 if settings['depot-paths'] == original['depot-paths']:
425 originP4Change = int(original['change'])
426 p4Change = int(settings['change'])
427 if originP4Change > p4Change:
428 print ("%s (%s) is newer than %s (%s). "
429 "Updating p4 branch from origin."
430 % (originHead, originP4Change,
431 remoteHead, p4Change))
432 update = True
433 else:
434 print ("Ignoring: %s was imported from %s while "
435 "%s was imported from %s"
436 % (originHead, ','.join(original['depot-paths']),
437 remoteHead, ','.join(settings['depot-paths'])))
438
439 if update:
440 system("git update-ref %s %s" % (remoteHead, originHead))
441
442def originP4BranchesExist():
443 return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
444
Simon Hausmann4f6432d2007-08-26 15:56:36 +0200445def p4ChangesForPaths(depotPaths, changeRange):
446 assert depotPaths
Anand Kumriab340fa42008-08-10 19:26:25 +0100447 output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange)
Simon Hausmann4f6432d2007-08-26 15:56:36 +0200448 for p in depotPaths]))
449
Pete Wyckoffb4b0ba02009-02-18 13:12:14 -0500450 changes = {}
Simon Hausmann4f6432d2007-08-26 15:56:36 +0200451 for line in output:
Pete Wyckoffb4b0ba02009-02-18 13:12:14 -0500452 changeNum = int(line.split(" ")[1])
453 changes[changeNum] = True
Simon Hausmann4f6432d2007-08-26 15:56:36 +0200454
Pete Wyckoffb4b0ba02009-02-18 13:12:14 -0500455 changelist = changes.keys()
456 changelist.sort()
457 return changelist
Simon Hausmann4f6432d2007-08-26 15:56:36 +0200458
Tor Arvid Lundd53de8b2011-03-15 13:08:02 +0100459def p4PathStartsWith(path, prefix):
460 # This method tries to remedy a potential mixed-case issue:
461 #
462 # If UserA adds //depot/DirA/file1
463 # and UserB adds //depot/dira/file2
464 #
465 # we may or may not have a problem. If you have core.ignorecase=true,
466 # we treat DirA and dira as the same directory
467 ignorecase = gitConfig("core.ignorecase", "--bool") == "true"
468 if ignorecase:
469 return path.lower().startswith(prefix.lower())
470 return path.startswith(prefix)
471
Simon Hausmannb9847332007-03-20 20:54:23 +0100472class Command:
473 def __init__(self):
474 self.usage = "usage: %prog [options]"
Simon Hausmann8910ac02007-03-26 08:18:55 +0200475 self.needsGit = True
Simon Hausmannb9847332007-03-20 20:54:23 +0100476
477class P4Debug(Command):
Simon Hausmann86949ee2007-03-19 20:59:12 +0100478 def __init__(self):
Simon Hausmann6ae8de82007-03-22 21:10:25 +0100479 Command.__init__(self)
Simon Hausmann86949ee2007-03-19 20:59:12 +0100480 self.options = [
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300481 optparse.make_option("--verbose", dest="verbose", action="store_true",
482 default=False),
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -0300483 ]
Simon Hausmannc8c39112007-03-19 21:02:30 +0100484 self.description = "A tool to debug the output of p4 -G."
Simon Hausmann8910ac02007-03-26 08:18:55 +0200485 self.needsGit = False
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300486 self.verbose = False
Simon Hausmann86949ee2007-03-19 20:59:12 +0100487
488 def run(self, args):
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300489 j = 0
Simon Hausmann86949ee2007-03-19 20:59:12 +0100490 for output in p4CmdList(" ".join(args)):
Han-Wen Nienhuysb1ce9442007-05-23 18:49:35 -0300491 print 'Element: %d' % j
492 j += 1
Simon Hausmann86949ee2007-03-19 20:59:12 +0100493 print output
Simon Hausmannb9847332007-03-20 20:54:23 +0100494 return True
Simon Hausmann86949ee2007-03-19 20:59:12 +0100495
Simon Hausmann58346842007-05-21 22:57:06 +0200496class P4RollBack(Command):
497 def __init__(self):
498 Command.__init__(self)
499 self.options = [
Simon Hausmann0c66a782007-05-23 20:07:57 +0200500 optparse.make_option("--verbose", dest="verbose", action="store_true"),
501 optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
Simon Hausmann58346842007-05-21 22:57:06 +0200502 ]
503 self.description = "A tool to debug the multi-branch import. Don't use :)"
Simon Hausmann52102d42007-05-21 23:44:24 +0200504 self.verbose = False
Simon Hausmann0c66a782007-05-23 20:07:57 +0200505 self.rollbackLocalBranches = False
Simon Hausmann58346842007-05-21 22:57:06 +0200506
507 def run(self, args):
508 if len(args) != 1:
509 return False
510 maxChange = int(args[0])
Simon Hausmann0c66a782007-05-23 20:07:57 +0200511
Simon Hausmannad192f22007-05-23 23:44:19 +0200512 if "p4ExitCode" in p4Cmd("changes -m 1"):
Simon Hausmann66a2f522007-05-23 23:40:48 +0200513 die("Problems executing p4");
514
Simon Hausmann0c66a782007-05-23 20:07:57 +0200515 if self.rollbackLocalBranches:
516 refPrefix = "refs/heads/"
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300517 lines = read_pipe_lines("git rev-parse --symbolic --branches")
Simon Hausmann0c66a782007-05-23 20:07:57 +0200518 else:
519 refPrefix = "refs/remotes/"
Han-Wen Nienhuysb016d392007-05-23 17:10:46 -0300520 lines = read_pipe_lines("git rev-parse --symbolic --remotes")
Simon Hausmann0c66a782007-05-23 20:07:57 +0200521
522 for line in lines:
523 if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -0300524 line = line.strip()
525 ref = refPrefix + line
Simon Hausmann58346842007-05-21 22:57:06 +0200526 log = extractLogMessageFromGitCommit(ref)
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300527 settings = extractSettingsGitLog(log)
528
529 depotPaths = settings['depot-paths']
530 change = settings['change']
531
Simon Hausmann58346842007-05-21 22:57:06 +0200532 changed = False
Simon Hausmann52102d42007-05-21 23:44:24 +0200533
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300534 if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange)
535 for p in depotPaths]))) == 0:
Simon Hausmann52102d42007-05-21 23:44:24 +0200536 print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
537 system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
538 continue
539
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300540 while change and int(change) > maxChange:
Simon Hausmann58346842007-05-21 22:57:06 +0200541 changed = True
Simon Hausmann52102d42007-05-21 23:44:24 +0200542 if self.verbose:
543 print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
Simon Hausmann58346842007-05-21 22:57:06 +0200544 system("git update-ref %s \"%s^\"" % (ref, ref))
545 log = extractLogMessageFromGitCommit(ref)
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -0300546 settings = extractSettingsGitLog(log)
547
548
549 depotPaths = settings['depot-paths']
550 change = settings['change']
Simon Hausmann58346842007-05-21 22:57:06 +0200551
552 if changed:
Simon Hausmann52102d42007-05-21 23:44:24 +0200553 print "%s rewound to %s" % (ref, change)
Simon Hausmann58346842007-05-21 22:57:06 +0200554
555 return True
556
Simon Hausmann711544b2007-04-01 15:40:46 +0200557class P4Submit(Command):
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100558 def __init__(self):
Simon Hausmannb9847332007-03-20 20:54:23 +0100559 Command.__init__(self)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100560 self.options = [
Han-Wen Nienhuys4addad22007-05-23 18:49:35 -0300561 optparse.make_option("--verbose", dest="verbose", action="store_true"),
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100562 optparse.make_option("--origin", dest="origin"),
Vitor Antunesae901092011-02-20 01:18:24 +0000563 optparse.make_option("-M", dest="detectRenames", action="store_true"),
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100564 ]
565 self.description = "Submit changes from git to the perforce depot."
Simon Hausmannc9b50e62007-03-29 19:15:24 +0200566 self.usage += " [name of git branch to submit into perforce depot]"
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100567 self.interactive = True
Simon Hausmann95124972007-03-23 09:16:07 +0100568 self.origin = ""
Vitor Antunesae901092011-02-20 01:18:24 +0000569 self.detectRenames = False
Simon Hausmannb0d10df2007-06-07 13:09:14 +0200570 self.verbose = False
Marius Storm-Olsenf7baba82007-06-07 14:07:01 +0200571 self.isWindows = (platform.system() == "Windows")
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100572
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100573 def check(self):
574 if len(p4CmdList("opened ...")) > 0:
575 die("You have files opened with perforce! Close them before starting the sync.")
576
Simon Hausmannedae1e22008-02-19 09:29:06 +0100577 # replaces everything between 'Description:' and the next P4 submit template field with the
578 # commit message
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100579 def prepareLogMessage(self, template, message):
580 result = ""
581
Simon Hausmannedae1e22008-02-19 09:29:06 +0100582 inDescriptionSection = False
583
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100584 for line in template.split("\n"):
585 if line.startswith("#"):
586 result += line + "\n"
587 continue
588
Simon Hausmannedae1e22008-02-19 09:29:06 +0100589 if inDescriptionSection:
Michael Horowitzc9dbab02011-02-25 21:31:13 -0500590 if line.startswith("Files:") or line.startswith("Jobs:"):
Simon Hausmannedae1e22008-02-19 09:29:06 +0100591 inDescriptionSection = False
592 else:
593 continue
594 else:
595 if line.startswith("Description:"):
596 inDescriptionSection = True
597 line += "\n"
598 for messageLine in message.split("\n"):
599 line += "\t" + messageLine + "\n"
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100600
Simon Hausmannedae1e22008-02-19 09:29:06 +0100601 result += line + "\n"
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100602
603 return result
604
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200605 def prepareSubmitTemplate(self):
606 # remove lines in the Files section that show changes to files outside the depot path we're committing into
607 template = ""
608 inFilesSection = False
Anand Kumriab340fa42008-08-10 19:26:25 +0100609 for line in p4_read_pipe_lines("change -o"):
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100610 if line.endswith("\r\n"):
611 line = line[:-2] + "\n"
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200612 if inFilesSection:
613 if line.startswith("\t"):
614 # path starts and ends with a tab
615 path = line[1:]
616 lastTab = path.rfind("\t")
617 if lastTab != -1:
618 path = path[:lastTab]
Tor Arvid Lundd53de8b2011-03-15 13:08:02 +0100619 if not p4PathStartsWith(path, self.depotPath):
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200620 continue
621 else:
622 inFilesSection = False
623 else:
624 if line.startswith("Files:"):
625 inFilesSection = True
626
627 template += line
628
629 return template
630
Han-Wen Nienhuys7cb5cbe2007-05-23 16:55:48 -0300631 def applyCommit(self, id):
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100632 print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
Vitor Antunesae901092011-02-20 01:18:24 +0000633
634 if not self.detectRenames:
635 # If not explicitly set check the config variable
636 self.detectRenames = gitConfig("git-p4.detectRenames").lower() == "true"
637
638 if self.detectRenames:
639 diffOpts = "-M"
640 else:
641 diffOpts = ""
642
Vitor Antunes4fddb412011-02-20 01:18:25 +0000643 if gitConfig("git-p4.detectCopies").lower() == "true":
644 diffOpts += " -C"
645
646 if gitConfig("git-p4.detectCopiesHarder").lower() == "true":
647 diffOpts += " --find-copies-harder"
648
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100649 diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100650 filesToAdd = set()
651 filesToDelete = set()
Simon Hausmannd336c152007-05-16 09:41:26 +0200652 editedFiles = set()
Chris Pettittc65b6702007-11-01 20:43:14 -0700653 filesToChangeExecBit = {}
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100654 for line in diff:
Chris Pettittb43b0a32007-11-01 20:43:13 -0700655 diff = parseDiffTreeEntry(line)
656 modifier = diff['status']
657 path = diff['src']
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100658 if modifier == "M":
Anand Kumria87b611d2008-08-10 19:26:27 +0100659 p4_system("edit \"%s\"" % path)
Chris Pettittc65b6702007-11-01 20:43:14 -0700660 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
661 filesToChangeExecBit[path] = diff['dst_mode']
Simon Hausmannd336c152007-05-16 09:41:26 +0200662 editedFiles.add(path)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100663 elif modifier == "A":
664 filesToAdd.add(path)
Chris Pettittc65b6702007-11-01 20:43:14 -0700665 filesToChangeExecBit[path] = diff['dst_mode']
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100666 if path in filesToDelete:
667 filesToDelete.remove(path)
668 elif modifier == "D":
669 filesToDelete.add(path)
670 if path in filesToAdd:
671 filesToAdd.remove(path)
Vitor Antunes4fddb412011-02-20 01:18:25 +0000672 elif modifier == "C":
673 src, dest = diff['src'], diff['dst']
674 p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
675 if diff['src_sha1'] != diff['dst_sha1']:
676 p4_system("edit \"%s\"" % (dest))
677 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
678 p4_system("edit \"%s\"" % (dest))
679 filesToChangeExecBit[dest] = diff['dst_mode']
680 os.unlink(dest)
681 editedFiles.add(dest)
Chris Pettittd9a5f252007-10-15 22:15:06 -0700682 elif modifier == "R":
Chris Pettittb43b0a32007-11-01 20:43:13 -0700683 src, dest = diff['src'], diff['dst']
Anand Kumria87b611d2008-08-10 19:26:27 +0100684 p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
Vitor Antunesae901092011-02-20 01:18:24 +0000685 if diff['src_sha1'] != diff['dst_sha1']:
686 p4_system("edit \"%s\"" % (dest))
Chris Pettittc65b6702007-11-01 20:43:14 -0700687 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
Vitor Antunesae901092011-02-20 01:18:24 +0000688 p4_system("edit \"%s\"" % (dest))
Chris Pettittc65b6702007-11-01 20:43:14 -0700689 filesToChangeExecBit[dest] = diff['dst_mode']
Chris Pettittd9a5f252007-10-15 22:15:06 -0700690 os.unlink(dest)
691 editedFiles.add(dest)
692 filesToDelete.add(src)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100693 else:
694 die("unknown modifier %s for %s" % (modifier, path))
695
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100696 diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
Simon Hausmann47a130b2007-05-20 16:33:21 +0200697 patchcmd = diffcmd + " | git apply "
Simon Hausmannc1b296b2007-05-20 16:55:05 +0200698 tryPatchCmd = patchcmd + "--check -"
699 applyPatchCmd = patchcmd + "--check --apply -"
Simon Hausmann51a26402007-04-15 09:59:56 +0200700
Simon Hausmann47a130b2007-05-20 16:33:21 +0200701 if os.system(tryPatchCmd) != 0:
Simon Hausmann51a26402007-04-15 09:59:56 +0200702 print "Unfortunately applying the change failed!"
703 print "What do you want to do?"
704 response = "x"
705 while response != "s" and response != "a" and response != "w":
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -0300706 response = raw_input("[s]kip this patch / [a]pply the patch forcibly "
707 "and with .rej files / [w]rite the patch to a file (patch.txt) ")
Simon Hausmann51a26402007-04-15 09:59:56 +0200708 if response == "s":
709 print "Skipping! Good luck with the next patches..."
Simon Hausmann20947142007-09-13 22:10:18 +0200710 for f in editedFiles:
Anand Kumria87b611d2008-08-10 19:26:27 +0100711 p4_system("revert \"%s\"" % f);
Simon Hausmann20947142007-09-13 22:10:18 +0200712 for f in filesToAdd:
713 system("rm %s" %f)
Simon Hausmann51a26402007-04-15 09:59:56 +0200714 return
715 elif response == "a":
Simon Hausmann47a130b2007-05-20 16:33:21 +0200716 os.system(applyPatchCmd)
Simon Hausmann51a26402007-04-15 09:59:56 +0200717 if len(filesToAdd) > 0:
718 print "You may also want to call p4 add on the following files:"
719 print " ".join(filesToAdd)
720 if len(filesToDelete):
721 print "The following files should be scheduled for deletion with p4 delete:"
722 print " ".join(filesToDelete)
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -0300723 die("Please resolve and submit the conflict manually and "
724 + "continue afterwards with git-p4 submit --continue")
Simon Hausmann51a26402007-04-15 09:59:56 +0200725 elif response == "w":
726 system(diffcmd + " > patch.txt")
727 print "Patch saved to patch.txt in %s !" % self.clientPath
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -0300728 die("Please resolve and submit the conflict manually and "
729 "continue afterwards with git-p4 submit --continue")
Simon Hausmann51a26402007-04-15 09:59:56 +0200730
Simon Hausmann47a130b2007-05-20 16:33:21 +0200731 system(applyPatchCmd)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100732
733 for f in filesToAdd:
Anand Kumria87b611d2008-08-10 19:26:27 +0100734 p4_system("add \"%s\"" % f)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100735 for f in filesToDelete:
Anand Kumria87b611d2008-08-10 19:26:27 +0100736 p4_system("revert \"%s\"" % f)
737 p4_system("delete \"%s\"" % f)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100738
Chris Pettittc65b6702007-11-01 20:43:14 -0700739 # Set/clear executable bits
740 for f in filesToChangeExecBit.keys():
741 mode = filesToChangeExecBit[f]
742 setP4ExecBit(f, mode)
743
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100744 logMessage = extractLogMessageFromGitCommit(id)
Simon Hausmann0e36f2d2008-02-19 09:33:08 +0100745 logMessage = logMessage.strip()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100746
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200747 template = self.prepareSubmitTemplate()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100748
749 if self.interactive:
750 submitTemplate = self.prepareLogMessage(template, logMessage)
Shawn Bohrer67abd412008-03-12 19:03:23 -0500751 if os.environ.has_key("P4DIFF"):
752 del(os.environ["P4DIFF"])
Andrew Waters8b130262010-10-22 13:26:02 +0100753 diff = ""
754 for editedFile in editedFiles:
755 diff += p4_read_pipe("diff -du %r" % editedFile)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100756
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100757 newdiff = ""
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100758 for newFile in filesToAdd:
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100759 newdiff += "==== new file ====\n"
760 newdiff += "--- /dev/null\n"
761 newdiff += "+++ %s\n" % newFile
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100762 f = open(newFile, "r")
763 for line in f.readlines():
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100764 newdiff += "+" + line
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100765 f.close()
766
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100767 separatorLine = "######## everything below this line is just the diff #######\n"
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100768
Simon Hausmanne96e4002008-01-04 14:27:55 +0100769 [handle, fileName] = tempfile.mkstemp()
770 tmpFile = os.fdopen(handle, "w+")
Marius Storm-Olsenf3e5ae42008-03-28 15:40:40 +0100771 if self.isWindows:
772 submitTemplate = submitTemplate.replace("\n", "\r\n")
773 separatorLine = separatorLine.replace("\n", "\r\n")
774 newdiff = newdiff.replace("\n", "\r\n")
775 tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
Simon Hausmanne96e4002008-01-04 14:27:55 +0100776 tmpFile.close()
Simon Hausmanncdc7e382008-08-27 09:30:29 +0200777 mtime = os.stat(fileName).st_mtime
Shawn Bohrer82cea9f2008-03-12 19:03:24 -0500778 if os.environ.has_key("P4EDITOR"):
779 editor = os.environ.get("P4EDITOR")
780 else:
Nicolas Pitre8b187e62010-01-22 00:55:15 -0500781 editor = read_pipe("git var GIT_EDITOR").strip()
Simon Hausmanne96e4002008-01-04 14:27:55 +0100782 system(editor + " " + fileName)
Simon Hausmanncb4f1282007-05-25 22:34:30 +0200783
Simon Hausmanncdc7e382008-08-27 09:30:29 +0200784 response = "y"
785 if os.stat(fileName).st_mtime <= mtime:
786 response = "x"
787 while response != "y" and response != "n":
788 response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
789
790 if response == "y":
791 tmpFile = open(fileName, "rb")
792 message = tmpFile.read()
793 tmpFile.close()
794 submitTemplate = message[:message.index(separatorLine)]
795 if self.isWindows:
796 submitTemplate = submitTemplate.replace("\r\n", "\n")
797 p4_write_pipe("submit -i", submitTemplate)
798 else:
799 for f in editedFiles:
800 p4_system("revert \"%s\"" % f);
801 for f in filesToAdd:
802 p4_system("revert \"%s\"" % f);
803 system("rm %s" %f)
804
805 os.remove(fileName)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100806 else:
807 fileName = "submit.txt"
808 file = open(fileName, "w+")
809 file.write(self.prepareLogMessage(template, logMessage))
810 file.close()
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -0300811 print ("Perforce submit template written as %s. "
812 + "Please review/edit and then use p4 submit -i < %s to submit directly!"
813 % (fileName, fileName))
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100814
815 def run(self, args):
Simon Hausmannc9b50e62007-03-29 19:15:24 +0200816 if len(args) == 0:
817 self.master = currentGitBranch()
Simon Hausmann4280e532007-05-25 08:49:18 +0200818 if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
Simon Hausmannc9b50e62007-03-29 19:15:24 +0200819 die("Detecting current git branch failed!")
820 elif len(args) == 1:
821 self.master = args[0]
822 else:
823 return False
824
Jing Xue4c2d5d72008-06-22 14:12:39 -0400825 allowSubmit = gitConfig("git-p4.allowSubmit")
826 if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
827 die("%s is not in git-p4.allowSubmit" % self.master)
828
Simon Hausmann27d2d812007-06-12 14:31:59 +0200829 [upstream, settings] = findUpstreamBranchPoint()
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200830 self.depotPath = settings['depot-paths'][0]
Simon Hausmann27d2d812007-06-12 14:31:59 +0200831 if len(self.origin) == 0:
832 self.origin = upstream
Simon Hausmanna3fdd572007-06-07 22:54:32 +0200833
834 if self.verbose:
835 print "Origin branch is " + self.origin
Simon Hausmann95124972007-03-23 09:16:07 +0100836
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200837 if len(self.depotPath) == 0:
Simon Hausmann95124972007-03-23 09:16:07 +0100838 print "Internal error: cannot locate perforce depot path from existing branches"
839 sys.exit(128)
840
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200841 self.clientPath = p4Where(self.depotPath)
Simon Hausmann95124972007-03-23 09:16:07 +0100842
Simon Hausmann51a26402007-04-15 09:59:56 +0200843 if len(self.clientPath) == 0:
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200844 print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath
Simon Hausmann95124972007-03-23 09:16:07 +0100845 sys.exit(128)
846
Simon Hausmannea99c3a2007-08-08 17:06:55 +0200847 print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
Simon Hausmann7944f142007-05-21 11:04:26 +0200848 self.oldWorkingDirectory = os.getcwd()
Simon Hausmannc1b296b2007-05-20 16:55:05 +0200849
Robert Blum053fd0c2008-08-01 12:50:03 -0700850 chdir(self.clientPath)
Benjamin C Meyer6a012982010-03-19 00:39:10 -0400851 print "Synchronizing p4 checkout..."
Anand Kumria87b611d2008-08-10 19:26:27 +0100852 p4_system("sync ...")
Simon Hausmann95124972007-03-23 09:16:07 +0100853
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100854 self.check()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100855
Simon Hausmann4c750c02008-02-19 09:37:16 +0100856 commits = []
857 for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
858 commits.append(line.strip())
859 commits.reverse()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100860
861 while len(commits) > 0:
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100862 commit = commits[0]
863 commits = commits[1:]
Han-Wen Nienhuys7cb5cbe2007-05-23 16:55:48 -0300864 self.applyCommit(commit)
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100865 if not self.interactive:
866 break
867
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100868 if len(commits) == 0:
Simon Hausmann4c750c02008-02-19 09:37:16 +0100869 print "All changes applied!"
Robert Blum053fd0c2008-08-01 12:50:03 -0700870 chdir(self.oldWorkingDirectory)
Simon Hausmann14594f42007-08-22 09:07:15 +0200871
Simon Hausmann4c750c02008-02-19 09:37:16 +0100872 sync = P4Sync()
873 sync.run([])
Simon Hausmann14594f42007-08-22 09:07:15 +0200874
Simon Hausmann4c750c02008-02-19 09:37:16 +0100875 rebase = P4Rebase()
876 rebase.rebase()
Simon Hausmann4f5cf762007-03-19 22:25:17 +0100877
Simon Hausmannb9847332007-03-20 20:54:23 +0100878 return True
879
Simon Hausmann711544b2007-04-01 15:40:46 +0200880class P4Sync(Command):
Pete Wyckoff56c09342011-02-19 08:17:57 -0500881 delete_actions = ( "delete", "move/delete", "purge" )
882
Simon Hausmannb9847332007-03-20 20:54:23 +0100883 def __init__(self):
884 Command.__init__(self)
885 self.options = [
886 optparse.make_option("--branch", dest="branch"),
887 optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
888 optparse.make_option("--changesfile", dest="changesFile"),
889 optparse.make_option("--silent", dest="silent", action="store_true"),
Simon Hausmannef48f902007-05-17 22:17:49 +0200890 optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
Simon Hausmanna028a982007-05-23 00:03:08 +0200891 optparse.make_option("--verbose", dest="verbose", action="store_true"),
Han-Wen Nienhuysd2c6dd32007-05-23 18:49:35 -0300892 optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
893 help="Import into refs/heads/ , not refs/remotes"),
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -0300894 optparse.make_option("--max-changes", dest="maxChanges"),
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -0300895 optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +0100896 help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
897 optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
898 help="Only sync files that are included in the Perforce Client Spec")
Simon Hausmannb9847332007-03-20 20:54:23 +0100899 ]
900 self.description = """Imports from Perforce into a git repository.\n
901 example:
902 //depot/my/project/ -- to import the current head
903 //depot/my/project/@all -- to import everything
904 //depot/my/project/@1,6 -- to import only from revision 1 to 6
905
906 (a ... is not needed in the path p4 specification, it's added implicitly)"""
907
908 self.usage += " //depot/path[@revRange]"
Simon Hausmannb9847332007-03-20 20:54:23 +0100909 self.silent = False
Reilly Grant1d7367d2009-09-10 00:02:38 -0700910 self.createdBranches = set()
911 self.committedChanges = set()
Simon Hausmann569d1bd2007-03-22 21:34:16 +0100912 self.branch = ""
Simon Hausmannb9847332007-03-20 20:54:23 +0100913 self.detectBranches = False
Simon Hausmanncb53e1f2007-04-08 00:12:02 +0200914 self.detectLabels = False
Simon Hausmannb9847332007-03-20 20:54:23 +0100915 self.changesFile = ""
Simon Hausmann01265102007-05-25 10:36:10 +0200916 self.syncWithOrigin = True
Simon Hausmann4b97ffb2007-05-18 21:45:23 +0200917 self.verbose = False
Simon Hausmanna028a982007-05-23 00:03:08 +0200918 self.importIntoRemotes = True
Simon Hausmann01a9c9c2007-05-23 00:07:35 +0200919 self.maxChanges = ""
Marius Storm-Olsenc1f91972007-05-24 14:07:55 +0200920 self.isWindows = (platform.system() == "Windows")
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -0300921 self.keepRepoPath = False
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300922 self.depotPaths = None
Simon Hausmann3c699642007-06-16 13:09:21 +0200923 self.p4BranchesInGit = []
Tommy Thorn354081d2008-02-03 10:38:51 -0800924 self.cloneExclude = []
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +0100925 self.useClientSpec = False
926 self.clientSpecDirs = []
Simon Hausmannb9847332007-03-20 20:54:23 +0100927
Simon Hausmann01265102007-05-25 10:36:10 +0200928 if gitConfig("git-p4.syncFromOrigin") == "false":
929 self.syncWithOrigin = False
930
Pete Wyckoff084f6302011-02-19 08:18:00 -0500931 #
932 # P4 wildcards are not allowed in filenames. P4 complains
933 # if you simply add them, but you can force it with "-f", in
934 # which case it translates them into %xx encoding internally.
935 # Search for and fix just these four characters. Do % last so
936 # that fixing it does not inadvertently create new %-escapes.
937 #
938 def wildcard_decode(self, path):
939 # Cannot have * in a filename in windows; untested as to
940 # what p4 would do in such a case.
941 if not self.isWindows:
942 path = path.replace("%2A", "*")
943 path = path.replace("%23", "#") \
944 .replace("%40", "@") \
945 .replace("%25", "%")
946 return path
947
Simon Hausmannb9847332007-03-20 20:54:23 +0100948 def extractFilesFromCommit(self, commit):
Tommy Thorn354081d2008-02-03 10:38:51 -0800949 self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
950 for path in self.cloneExclude]
Simon Hausmannb9847332007-03-20 20:54:23 +0100951 files = []
952 fnum = 0
953 while commit.has_key("depotFile%s" % fnum):
954 path = commit["depotFile%s" % fnum]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300955
Tommy Thorn354081d2008-02-03 10:38:51 -0800956 if [p for p in self.cloneExclude
Tor Arvid Lundd53de8b2011-03-15 13:08:02 +0100957 if p4PathStartsWith(path, p)]:
Tommy Thorn354081d2008-02-03 10:38:51 -0800958 found = False
959 else:
960 found = [p for p in self.depotPaths
Tor Arvid Lundd53de8b2011-03-15 13:08:02 +0100961 if p4PathStartsWith(path, p)]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300962 if not found:
Simon Hausmannb9847332007-03-20 20:54:23 +0100963 fnum = fnum + 1
964 continue
965
966 file = {}
967 file["path"] = path
968 file["rev"] = commit["rev%s" % fnum]
969 file["action"] = commit["action%s" % fnum]
970 file["type"] = commit["type%s" % fnum]
971 files.append(file)
972 fnum = fnum + 1
973 return files
974
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300975 def stripRepoPath(self, path, prefixes):
Ian Wienand39527102011-02-11 16:33:48 -0800976 if self.useClientSpec:
977
978 # if using the client spec, we use the output directory
979 # specified in the client. For example, a view
980 # //depot/foo/branch/... //client/branch/foo/...
981 # will end up putting all foo/branch files into
982 # branch/foo/
983 for val in self.clientSpecDirs:
984 if path.startswith(val[0]):
985 # replace the depot path with the client path
986 path = path.replace(val[0], val[1][1])
987 # now strip out the client (//client/...)
988 path = re.sub("^(//[^/]+/)", '', path)
989 # the rest is all path
990 return path
991
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -0300992 if self.keepRepoPath:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300993 prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
Han-Wen Nienhuys8b41a972007-05-23 18:20:53 -0300994
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300995 for p in prefixes:
Tor Arvid Lundd53de8b2011-03-15 13:08:02 +0100996 if p4PathStartsWith(path, p):
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -0300997 path = path[len(p):]
998
999 return path
Han-Wen Nienhuys6754a292007-05-23 17:41:50 -03001000
Simon Hausmann71b112d2007-05-19 11:54:11 +02001001 def splitFilesIntoBranches(self, commit):
Simon Hausmannd5904672007-05-19 11:07:32 +02001002 branches = {}
Simon Hausmann71b112d2007-05-19 11:54:11 +02001003 fnum = 0
1004 while commit.has_key("depotFile%s" % fnum):
1005 path = commit["depotFile%s" % fnum]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001006 found = [p for p in self.depotPaths
Tor Arvid Lundd53de8b2011-03-15 13:08:02 +01001007 if p4PathStartsWith(path, p)]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001008 if not found:
Simon Hausmann71b112d2007-05-19 11:54:11 +02001009 fnum = fnum + 1
1010 continue
1011
1012 file = {}
1013 file["path"] = path
1014 file["rev"] = commit["rev%s" % fnum]
1015 file["action"] = commit["action%s" % fnum]
1016 file["type"] = commit["type%s" % fnum]
1017 fnum = fnum + 1
1018
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001019 relPath = self.stripRepoPath(path, self.depotPaths)
Simon Hausmannb9847332007-03-20 20:54:23 +01001020
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001021 for branch in self.knownBranches.keys():
Han-Wen Nienhuys6754a292007-05-23 17:41:50 -03001022
1023 # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
1024 if relPath.startswith(branch + "/"):
Simon Hausmannd5904672007-05-19 11:07:32 +02001025 if branch not in branches:
1026 branches[branch] = []
Simon Hausmann71b112d2007-05-19 11:54:11 +02001027 branches[branch].append(file)
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001028 break
Simon Hausmannb9847332007-03-20 20:54:23 +01001029
1030 return branches
1031
Luke Diamandb9327052009-07-30 00:13:46 +01001032 # output one file from the P4 stream
1033 # - helper for streamP4Files
1034
1035 def streamOneP4File(self, file, contents):
1036 if file["type"] == "apple":
1037 print "\nfile %s is a strange apple file that forks. Ignoring" % \
1038 file['depotFile']
1039 return
1040
1041 relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
Pete Wyckoff084f6302011-02-19 08:18:00 -05001042 relPath = self.wildcard_decode(relPath)
Luke Diamandb9327052009-07-30 00:13:46 +01001043 if verbose:
1044 sys.stderr.write("%s\n" % relPath)
1045
1046 mode = "644"
1047 if isP4Exec(file["type"]):
1048 mode = "755"
1049 elif file["type"] == "symlink":
1050 mode = "120000"
1051 # p4 print on a symlink contains "target\n", so strip it off
Evan Powersb39c3612010-02-16 00:44:08 -08001052 data = ''.join(contents)
1053 contents = [data[:-1]]
Luke Diamandb9327052009-07-30 00:13:46 +01001054
1055 if self.isWindows and file["type"].endswith("text"):
1056 mangled = []
1057 for data in contents:
1058 data = data.replace("\r\n", "\n")
1059 mangled.append(data)
1060 contents = mangled
1061
1062 if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
1063 contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents)
1064 elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
1065 contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents)
1066
1067 self.gitStream.write("M %s inline %s\n" % (mode, relPath))
1068
1069 # total length...
1070 length = 0
1071 for d in contents:
1072 length = length + len(d)
1073
1074 self.gitStream.write("data %d\n" % length)
1075 for d in contents:
1076 self.gitStream.write(d)
1077 self.gitStream.write("\n")
1078
1079 def streamOneP4Deletion(self, file):
1080 relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
1081 if verbose:
1082 sys.stderr.write("delete %s\n" % relPath)
1083 self.gitStream.write("D %s\n" % relPath)
1084
1085 # handle another chunk of streaming data
1086 def streamP4FilesCb(self, marshalled):
1087
1088 if marshalled.has_key('depotFile') and self.stream_have_file_info:
1089 # start of a new file - output the old one first
1090 self.streamOneP4File(self.stream_file, self.stream_contents)
1091 self.stream_file = {}
1092 self.stream_contents = []
1093 self.stream_have_file_info = False
1094
1095 # pick up the new file information... for the
1096 # 'data' field we need to append to our array
1097 for k in marshalled.keys():
1098 if k == 'data':
1099 self.stream_contents.append(marshalled['data'])
1100 else:
1101 self.stream_file[k] = marshalled[k]
1102
1103 self.stream_have_file_info = True
1104
1105 # Stream directly from "p4 files" into "git fast-import"
1106 def streamP4Files(self, files):
Simon Hausmann30b59402008-03-03 11:55:48 +01001107 filesForCommit = []
1108 filesToRead = []
Luke Diamandb9327052009-07-30 00:13:46 +01001109 filesToDelete = []
Simon Hausmann30b59402008-03-03 11:55:48 +01001110
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001111 for f in files:
Simon Hausmann30b59402008-03-03 11:55:48 +01001112 includeFile = True
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001113 for val in self.clientSpecDirs:
1114 if f['path'].startswith(val[0]):
Ian Wienand39527102011-02-11 16:33:48 -08001115 if val[1][0] <= 0:
Simon Hausmann30b59402008-03-03 11:55:48 +01001116 includeFile = False
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001117 break
1118
Simon Hausmann30b59402008-03-03 11:55:48 +01001119 if includeFile:
1120 filesForCommit.append(f)
Pete Wyckoff56c09342011-02-19 08:17:57 -05001121 if f['action'] in self.delete_actions:
Luke Diamandb9327052009-07-30 00:13:46 +01001122 filesToDelete.append(f)
Pete Wyckoff56c09342011-02-19 08:17:57 -05001123 else:
1124 filesToRead.append(f)
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001125
Luke Diamandb9327052009-07-30 00:13:46 +01001126 # deleted files...
1127 for f in filesToDelete:
1128 self.streamOneP4Deletion(f)
1129
Simon Hausmann30b59402008-03-03 11:55:48 +01001130 if len(filesToRead) > 0:
Luke Diamandb9327052009-07-30 00:13:46 +01001131 self.stream_file = {}
1132 self.stream_contents = []
1133 self.stream_have_file_info = False
1134
1135 # curry self argument
1136 def streamP4FilesCbSelf(entry):
1137 self.streamP4FilesCb(entry)
1138
1139 p4CmdList("-x - print",
1140 '\n'.join(['%s#%s' % (f['path'], f['rev'])
Simon Hausmann30b59402008-03-03 11:55:48 +01001141 for f in filesToRead]),
Luke Diamandb9327052009-07-30 00:13:46 +01001142 cb=streamP4FilesCbSelf)
Han-Wen Nienhuysf2eda792007-05-23 18:49:35 -03001143
Luke Diamandb9327052009-07-30 00:13:46 +01001144 # do the last chunk
1145 if self.stream_file.has_key('depotFile'):
1146 self.streamOneP4File(self.stream_file, self.stream_contents)
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001147
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001148 def commit(self, details, files, branch, branchPrefixes, parent = ""):
Simon Hausmannb9847332007-03-20 20:54:23 +01001149 epoch = details["time"]
1150 author = details["user"]
Luke Diamandb9327052009-07-30 00:13:46 +01001151 self.branchPrefixes = branchPrefixes
Simon Hausmannb9847332007-03-20 20:54:23 +01001152
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001153 if self.verbose:
1154 print "commit into %s" % branch
1155
Han-Wen Nienhuys96e07dd2007-05-23 18:49:35 -03001156 # start with reading files; if that fails, we should not
1157 # create a commit.
1158 new_files = []
1159 for f in files:
Tor Arvid Lundd53de8b2011-03-15 13:08:02 +01001160 if [p for p in branchPrefixes if p4PathStartsWith(f['path'], p)]:
Han-Wen Nienhuys96e07dd2007-05-23 18:49:35 -03001161 new_files.append (f)
1162 else:
1163 sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
Han-Wen Nienhuys96e07dd2007-05-23 18:49:35 -03001164
Simon Hausmannb9847332007-03-20 20:54:23 +01001165 self.gitStream.write("commit %s\n" % branch)
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001166# gitStream.write("mark :%s\n" % details["change"])
Simon Hausmannb9847332007-03-20 20:54:23 +01001167 self.committedChanges.add(int(details["change"]))
1168 committer = ""
Simon Hausmannb607e712007-05-20 10:55:54 +02001169 if author not in self.users:
1170 self.getUserMapFromPerforceServer()
Simon Hausmannb9847332007-03-20 20:54:23 +01001171 if author in self.users:
Simon Hausmann0828ab12007-03-20 20:59:30 +01001172 committer = "%s %s %s" % (self.users[author], epoch, self.tz)
Simon Hausmannb9847332007-03-20 20:54:23 +01001173 else:
Simon Hausmann0828ab12007-03-20 20:59:30 +01001174 committer = "%s <a@b> %s %s" % (author, epoch, self.tz)
Simon Hausmannb9847332007-03-20 20:54:23 +01001175
1176 self.gitStream.write("committer %s\n" % committer)
1177
1178 self.gitStream.write("data <<EOT\n")
1179 self.gitStream.write(details["desc"])
Simon Hausmann6581de02007-06-11 10:01:58 +02001180 self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s"
1181 % (','.join (branchPrefixes), details["change"]))
1182 if len(details['options']) > 0:
1183 self.gitStream.write(": options = %s" % details['options'])
1184 self.gitStream.write("]\nEOT\n\n")
Simon Hausmannb9847332007-03-20 20:54:23 +01001185
1186 if len(parent) > 0:
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001187 if self.verbose:
1188 print "parent %s" % parent
Simon Hausmannb9847332007-03-20 20:54:23 +01001189 self.gitStream.write("from %s\n" % parent)
1190
Luke Diamandb9327052009-07-30 00:13:46 +01001191 self.streamP4Files(new_files)
Simon Hausmannb9847332007-03-20 20:54:23 +01001192 self.gitStream.write("\n")
1193
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001194 change = int(details["change"])
1195
Simon Hausmann9bda3a82007-05-19 12:05:40 +02001196 if self.labels.has_key(change):
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001197 label = self.labels[change]
1198 labelDetails = label[0]
1199 labelRevisions = label[1]
Simon Hausmann71b112d2007-05-19 11:54:11 +02001200 if self.verbose:
1201 print "Change %s is labelled %s" % (change, labelDetails)
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001202
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001203 files = p4CmdList("files " + ' '.join (["%s...@%s" % (p, change)
1204 for p in branchPrefixes]))
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001205
1206 if len(files) == len(labelRevisions):
1207
1208 cleanedFiles = {}
1209 for info in files:
Pete Wyckoff56c09342011-02-19 08:17:57 -05001210 if info["action"] in self.delete_actions:
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001211 continue
1212 cleanedFiles[info["depotFile"]] = info["rev"]
1213
1214 if cleanedFiles == labelRevisions:
1215 self.gitStream.write("tag tag_%s\n" % labelDetails["label"])
1216 self.gitStream.write("from %s\n" % branch)
1217
1218 owner = labelDetails["Owner"]
1219 tagger = ""
1220 if author in self.users:
1221 tagger = "%s %s %s" % (self.users[owner], epoch, self.tz)
1222 else:
1223 tagger = "%s <a@b> %s %s" % (owner, epoch, self.tz)
1224 self.gitStream.write("tagger %s\n" % tagger)
1225 self.gitStream.write("data <<EOT\n")
1226 self.gitStream.write(labelDetails["Description"])
1227 self.gitStream.write("EOT\n\n")
1228
1229 else:
Simon Hausmanna46668f2007-03-28 17:05:38 +02001230 if not self.silent:
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001231 print ("Tag %s does not match with change %s: files do not match."
1232 % (labelDetails["label"], change))
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001233
1234 else:
Simon Hausmanna46668f2007-03-28 17:05:38 +02001235 if not self.silent:
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001236 print ("Tag %s does not match with change %s: file count is different."
1237 % (labelDetails["label"], change))
Simon Hausmannb9847332007-03-20 20:54:23 +01001238
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001239 def getUserCacheFilename(self):
Simon Hausmannb2d2d162007-07-25 09:31:38 +02001240 home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
1241 return home + "/.gitp4-usercache.txt"
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001242
Simon Hausmannb607e712007-05-20 10:55:54 +02001243 def getUserMapFromPerforceServer(self):
Simon Hausmannebd81162007-05-24 00:24:52 +02001244 if self.userMapFromPerforceServer:
1245 return
Simon Hausmannb9847332007-03-20 20:54:23 +01001246 self.users = {}
1247
1248 for output in p4CmdList("users"):
1249 if not output.has_key("User"):
1250 continue
1251 self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
1252
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001253
1254 s = ''
1255 for (key, val) in self.users.items():
Pete Wyckoff3b167392009-02-27 10:53:59 -08001256 s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001257
1258 open(self.getUserCacheFilename(), "wb").write(s)
Simon Hausmannebd81162007-05-24 00:24:52 +02001259 self.userMapFromPerforceServer = True
Simon Hausmannb607e712007-05-20 10:55:54 +02001260
1261 def loadUserMapFromCache(self):
1262 self.users = {}
Simon Hausmannebd81162007-05-24 00:24:52 +02001263 self.userMapFromPerforceServer = False
Simon Hausmannb607e712007-05-20 10:55:54 +02001264 try:
Han-Wen Nienhuys183b8ef2007-05-23 18:49:35 -03001265 cache = open(self.getUserCacheFilename(), "rb")
Simon Hausmannb607e712007-05-20 10:55:54 +02001266 lines = cache.readlines()
1267 cache.close()
1268 for line in lines:
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -03001269 entry = line.strip().split("\t")
Simon Hausmannb607e712007-05-20 10:55:54 +02001270 self.users[entry[0]] = entry[1]
1271 except IOError:
1272 self.getUserMapFromPerforceServer()
1273
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001274 def getLabels(self):
1275 self.labels = {}
1276
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001277 l = p4CmdList("labels %s..." % ' '.join (self.depotPaths))
Simon Hausmann10c32112007-04-08 10:15:47 +02001278 if len(l) > 0 and not self.silent:
Shun Kei Leung183f8432007-11-21 11:01:19 +08001279 print "Finding files belonging to labels in %s" % `self.depotPaths`
Simon Hausmann01ce1fe2007-04-07 23:46:50 +02001280
1281 for output in l:
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001282 label = output["label"]
1283 revisions = {}
1284 newestChange = 0
Simon Hausmann71b112d2007-05-19 11:54:11 +02001285 if self.verbose:
1286 print "Querying files for label %s" % label
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001287 for file in p4CmdList("files "
1288 + ' '.join (["%s...@%s" % (p, label)
1289 for p in self.depotPaths])):
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001290 revisions[file["depotFile"]] = file["rev"]
1291 change = int(file["change"])
1292 if change > newestChange:
1293 newestChange = change
1294
Simon Hausmann9bda3a82007-05-19 12:05:40 +02001295 self.labels[newestChange] = [output, revisions]
1296
1297 if self.verbose:
1298 print "Label changes: %s" % self.labels.keys()
Simon Hausmann1f4ba1c2007-03-26 22:34:34 +02001299
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001300 def guessProjectName(self):
1301 for p in self.depotPaths:
Simon Hausmann6e5295c2007-06-11 08:50:57 +02001302 if p.endswith("/"):
1303 p = p[:-1]
1304 p = p[p.strip().rfind("/") + 1:]
1305 if not p.endswith("/"):
1306 p += "/"
1307 return p
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001308
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001309 def getBranchMapping(self):
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001310 lostAndFoundBranches = set()
1311
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001312 for info in p4CmdList("branches"):
1313 details = p4Cmd("branch -o %s" % info["branch"])
1314 viewIdx = 0
1315 while details.has_key("View%s" % viewIdx):
1316 paths = details["View%s" % viewIdx].split(" ")
1317 viewIdx = viewIdx + 1
1318 # require standard //depot/foo/... //depot/bar/... mapping
1319 if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."):
1320 continue
1321 source = paths[0]
1322 destination = paths[1]
Simon Hausmann6509e192007-06-07 09:41:53 +02001323 ## HACK
Tor Arvid Lundd53de8b2011-03-15 13:08:02 +01001324 if p4PathStartsWith(source, self.depotPaths[0]) and p4PathStartsWith(destination, self.depotPaths[0]):
Simon Hausmann6509e192007-06-07 09:41:53 +02001325 source = source[len(self.depotPaths[0]):-4]
1326 destination = destination[len(self.depotPaths[0]):-4]
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001327
Simon Hausmann1a2edf42007-06-17 15:10:24 +02001328 if destination in self.knownBranches:
1329 if not self.silent:
1330 print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
1331 print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
1332 continue
1333
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001334 self.knownBranches[destination] = source
1335
1336 lostAndFoundBranches.discard(destination)
1337
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001338 if source not in self.knownBranches:
Simon Hausmann6555b2c2007-06-17 11:25:34 +02001339 lostAndFoundBranches.add(source)
1340
1341
1342 for branch in lostAndFoundBranches:
1343 self.knownBranches[branch] = branch
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001344
Simon Hausmann38f9f5e2007-11-15 10:38:45 +01001345 def getBranchMappingFromGitBranches(self):
1346 branches = p4BranchesInGit(self.importIntoRemotes)
1347 for branch in branches.keys():
1348 if branch == "master":
1349 branch = "main"
1350 else:
1351 branch = branch[len(self.projectName):]
1352 self.knownBranches[branch] = branch
1353
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001354 def listExistingP4GitBranches(self):
Simon Hausmann144ff462007-07-18 17:27:50 +02001355 # branches holds mapping from name to commit
1356 branches = p4BranchesInGit(self.importIntoRemotes)
1357 self.p4BranchesInGit = branches.keys()
1358 for branch in branches.keys():
1359 self.initialParents[self.refPrefix + branch] = branches[branch]
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001360
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001361 def updateOptionDict(self, d):
1362 option_keys = {}
1363 if self.keepRepoPath:
1364 option_keys['keepRepoPath'] = 1
1365
1366 d["options"] = ' '.join(sorted(option_keys.keys()))
1367
1368 def readOptions(self, d):
1369 self.keepRepoPath = (d.has_key('options')
1370 and ('keepRepoPath' in d['options']))
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001371
Simon Hausmann8134f692007-08-26 16:44:55 +02001372 def gitRefForBranch(self, branch):
1373 if branch == "main":
1374 return self.refPrefix + "master"
1375
1376 if len(branch) <= 0:
1377 return branch
1378
1379 return self.refPrefix + self.projectName + branch
1380
Simon Hausmann1ca3d712007-08-26 17:36:55 +02001381 def gitCommitByP4Change(self, ref, change):
1382 if self.verbose:
1383 print "looking in ref " + ref + " for change %s using bisect..." % change
1384
1385 earliestCommit = ""
1386 latestCommit = parseRevision(ref)
1387
1388 while True:
1389 if self.verbose:
1390 print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
1391 next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
1392 if len(next) == 0:
1393 if self.verbose:
1394 print "argh"
1395 return ""
1396 log = extractLogMessageFromGitCommit(next)
1397 settings = extractSettingsGitLog(log)
1398 currentChange = int(settings['change'])
1399 if self.verbose:
1400 print "current change %s" % currentChange
1401
1402 if currentChange == change:
1403 if self.verbose:
1404 print "found %s" % next
1405 return next
1406
1407 if currentChange < change:
1408 earliestCommit = "^%s" % next
1409 else:
1410 latestCommit = "%s" % next
1411
1412 return ""
1413
1414 def importNewBranch(self, branch, maxChange):
1415 # make fast-import flush all changes to disk and update the refs using the checkpoint
1416 # command so that we can try to find the branch parent in the git history
1417 self.gitStream.write("checkpoint\n\n");
1418 self.gitStream.flush();
1419 branchPrefix = self.depotPaths[0] + branch + "/"
1420 range = "@1,%s" % maxChange
1421 #print "prefix" + branchPrefix
1422 changes = p4ChangesForPaths([branchPrefix], range)
1423 if len(changes) <= 0:
1424 return False
1425 firstChange = changes[0]
1426 #print "first change in branch: %s" % firstChange
1427 sourceBranch = self.knownBranches[branch]
1428 sourceDepotPath = self.depotPaths[0] + sourceBranch
1429 sourceRef = self.gitRefForBranch(sourceBranch)
1430 #print "source " + sourceBranch
1431
1432 branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"])
1433 #print "branch parent: %s" % branchParentChange
1434 gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
1435 if len(gitParent) > 0:
1436 self.initialParents[self.gitRefForBranch(branch)] = gitParent
1437 #print "parent git commit: %s" % gitParent
1438
1439 self.importChanges(changes)
1440 return True
1441
Simon Hausmanne87f37a2007-08-26 16:00:52 +02001442 def importChanges(self, changes):
1443 cnt = 1
1444 for change in changes:
1445 description = p4Cmd("describe %s" % change)
1446 self.updateOptionDict(description)
1447
1448 if not self.silent:
1449 sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
1450 sys.stdout.flush()
1451 cnt = cnt + 1
1452
1453 try:
1454 if self.detectBranches:
1455 branches = self.splitFilesIntoBranches(description)
1456 for branch in branches.keys():
1457 ## HACK --hwn
1458 branchPrefix = self.depotPaths[0] + branch + "/"
1459
1460 parent = ""
1461
1462 filesForCommit = branches[branch]
1463
1464 if self.verbose:
1465 print "branch is %s" % branch
1466
1467 self.updatedBranches.add(branch)
1468
1469 if branch not in self.createdBranches:
1470 self.createdBranches.add(branch)
1471 parent = self.knownBranches[branch]
1472 if parent == branch:
1473 parent = ""
Simon Hausmann1ca3d712007-08-26 17:36:55 +02001474 else:
1475 fullBranch = self.projectName + branch
1476 if fullBranch not in self.p4BranchesInGit:
1477 if not self.silent:
1478 print("\n Importing new branch %s" % fullBranch);
1479 if self.importNewBranch(branch, change - 1):
1480 parent = ""
1481 self.p4BranchesInGit.append(fullBranch)
1482 if not self.silent:
1483 print("\n Resuming with change %s" % change);
1484
1485 if self.verbose:
1486 print "parent determined through known branches: %s" % parent
Simon Hausmanne87f37a2007-08-26 16:00:52 +02001487
Simon Hausmann8134f692007-08-26 16:44:55 +02001488 branch = self.gitRefForBranch(branch)
1489 parent = self.gitRefForBranch(parent)
Simon Hausmanne87f37a2007-08-26 16:00:52 +02001490
1491 if self.verbose:
1492 print "looking for initial parent for %s; current parent is %s" % (branch, parent)
1493
1494 if len(parent) == 0 and branch in self.initialParents:
1495 parent = self.initialParents[branch]
1496 del self.initialParents[branch]
1497
1498 self.commit(description, filesForCommit, branch, [branchPrefix], parent)
1499 else:
1500 files = self.extractFilesFromCommit(description)
1501 self.commit(description, files, self.branch, self.depotPaths,
1502 self.initialParent)
1503 self.initialParent = ""
1504 except IOError:
1505 print self.gitError.read()
1506 sys.exit(1)
1507
Simon Hausmannc208a242007-08-26 16:07:18 +02001508 def importHeadRevision(self, revision):
1509 print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
1510
1511 details = { "user" : "git perforce import user", "time" : int(time.time()) }
Pete Wyckoff1494fcb2011-02-19 08:17:56 -05001512 details["desc"] = ("Initial import of %s from the state at revision %s\n"
Simon Hausmannc208a242007-08-26 16:07:18 +02001513 % (' '.join(self.depotPaths), revision))
1514 details["change"] = revision
1515 newestRevision = 0
1516
1517 fileCnt = 0
1518 for info in p4CmdList("files "
1519 + ' '.join(["%s...%s"
1520 % (p, revision)
1521 for p in self.depotPaths])):
1522
Pete Wyckoff68b28592011-02-19 08:17:55 -05001523 if 'code' in info and info['code'] == 'error':
Simon Hausmannc208a242007-08-26 16:07:18 +02001524 sys.stderr.write("p4 returned an error: %s\n"
1525 % info['data'])
Pete Wyckoffd88e7072011-02-19 08:17:58 -05001526 if info['data'].find("must refer to client") >= 0:
1527 sys.stderr.write("This particular p4 error is misleading.\n")
1528 sys.stderr.write("Perhaps the depot path was misspelled.\n");
1529 sys.stderr.write("Depot path: %s\n" % " ".join(self.depotPaths))
Simon Hausmannc208a242007-08-26 16:07:18 +02001530 sys.exit(1)
Pete Wyckoff68b28592011-02-19 08:17:55 -05001531 if 'p4ExitCode' in info:
1532 sys.stderr.write("p4 exitcode: %s\n" % info['p4ExitCode'])
Simon Hausmannc208a242007-08-26 16:07:18 +02001533 sys.exit(1)
1534
1535
1536 change = int(info["change"])
1537 if change > newestRevision:
1538 newestRevision = change
1539
Pete Wyckoff56c09342011-02-19 08:17:57 -05001540 if info["action"] in self.delete_actions:
Simon Hausmannc208a242007-08-26 16:07:18 +02001541 # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
1542 #fileCnt = fileCnt + 1
1543 continue
1544
1545 for prop in ["depotFile", "rev", "action", "type" ]:
1546 details["%s%s" % (prop, fileCnt)] = info[prop]
1547
1548 fileCnt = fileCnt + 1
1549
1550 details["change"] = newestRevision
1551 self.updateOptionDict(details)
1552 try:
1553 self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
1554 except IOError:
1555 print "IO error with git fast-import. Is your git version recent enough?"
1556 print self.gitError.read()
1557
1558
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001559 def getClientSpec(self):
1560 specList = p4CmdList( "client -o" )
1561 temp = {}
1562 for entry in specList:
1563 for k,v in entry.iteritems():
1564 if k.startswith("View"):
Ian Wienand39527102011-02-11 16:33:48 -08001565
1566 # p4 has these %%1 to %%9 arguments in specs to
1567 # reorder paths; which we can't handle (yet :)
1568 if re.match('%%\d', v) != None:
1569 print "Sorry, can't handle %%n arguments in client specs"
1570 sys.exit(1)
1571
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001572 if v.startswith('"'):
1573 start = 1
1574 else:
1575 start = 0
1576 index = v.find("...")
Ian Wienand39527102011-02-11 16:33:48 -08001577
1578 # save the "client view"; i.e the RHS of the view
1579 # line that tells the client where to put the
1580 # files for this view.
1581 cv = v[index+3:].strip() # +3 to remove previous '...'
1582
1583 # if the client view doesn't end with a
1584 # ... wildcard, then we're going to mess up the
1585 # output directory, so fail gracefully.
1586 if not cv.endswith('...'):
1587 print 'Sorry, client view in "%s" needs to end with wildcard' % (k)
1588 sys.exit(1)
1589 cv=cv[:-3]
1590
1591 # now save the view; +index means included, -index
1592 # means it should be filtered out.
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001593 v = v[start:index]
1594 if v.startswith("-"):
1595 v = v[1:]
Ian Wienand39527102011-02-11 16:33:48 -08001596 include = -len(v)
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001597 else:
Ian Wienand39527102011-02-11 16:33:48 -08001598 include = len(v)
1599
1600 temp[v] = (include, cv)
1601
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001602 self.clientSpecDirs = temp.items()
Ian Wienand39527102011-02-11 16:33:48 -08001603 self.clientSpecDirs.sort( lambda x, y: abs( y[1][0] ) - abs( x[1][0] ) )
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001604
Simon Hausmannb9847332007-03-20 20:54:23 +01001605 def run(self, args):
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001606 self.depotPaths = []
Simon Hausmann179caeb2007-03-22 22:17:42 +01001607 self.changeRange = ""
1608 self.initialParent = ""
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001609 self.previousDepotPaths = []
Han-Wen Nienhuysce6f33c2007-05-23 16:46:29 -03001610
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001611 # map from branch depot path to parent branch
1612 self.knownBranches = {}
1613 self.initialParents = {}
Simon Hausmann5ca44612007-08-24 17:44:16 +02001614 self.hasOrigin = originP4BranchesExist()
Simon Hausmanna43ff002007-06-11 09:59:27 +02001615 if not self.syncWithOrigin:
1616 self.hasOrigin = False
Simon Hausmann179caeb2007-03-22 22:17:42 +01001617
Simon Hausmanna028a982007-05-23 00:03:08 +02001618 if self.importIntoRemotes:
1619 self.refPrefix = "refs/remotes/p4/"
1620 else:
Marius Storm-Olsendb775552007-06-07 15:13:59 +02001621 self.refPrefix = "refs/heads/p4/"
Simon Hausmanna028a982007-05-23 00:03:08 +02001622
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001623 if self.syncWithOrigin and self.hasOrigin:
1624 if not self.silent:
1625 print "Syncing with origin first by calling git fetch origin"
1626 system("git fetch origin")
Simon Hausmann10f880f2007-05-24 22:28:28 +02001627
Simon Hausmann569d1bd2007-03-22 21:34:16 +01001628 if len(self.branch) == 0:
Marius Storm-Olsendb775552007-06-07 15:13:59 +02001629 self.branch = self.refPrefix + "master"
Simon Hausmanna028a982007-05-23 00:03:08 +02001630 if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
Simon Hausmann48df6fd2007-05-17 21:18:53 +02001631 system("git update-ref %s refs/heads/p4" % self.branch)
Simon Hausmann48df6fd2007-05-17 21:18:53 +02001632 system("git branch -D p4");
Simon Hausmannfaf1bd22007-05-21 10:05:30 +02001633 # create it /after/ importing, when master exists
Simon Hausmann0058a332007-08-24 17:46:16 +02001634 if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
Simon Hausmanna3c55c02007-05-27 15:48:01 +02001635 system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
Simon Hausmann179caeb2007-03-22 22:17:42 +01001636
Anand Kumria3cafb7d2008-08-10 19:26:32 +01001637 if self.useClientSpec or gitConfig("git-p4.useclientspec") == "true":
Tor Arvid Lund3a70cdf2008-02-18 15:22:08 +01001638 self.getClientSpec()
1639
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001640 # TODO: should always look at previous commits,
1641 # merge with previous imports, if possible.
1642 if args == []:
Simon Hausmannd414c742007-05-25 11:36:42 +02001643 if self.hasOrigin:
Simon Hausmann5ca44612007-08-24 17:44:16 +02001644 createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent)
Simon Hausmannabcd7902007-05-24 22:25:36 +02001645 self.listExistingP4GitBranches()
1646
1647 if len(self.p4BranchesInGit) > 1:
1648 if not self.silent:
1649 print "Importing from/into multiple branches"
1650 self.detectBranches = True
Simon Hausmann967f72e2007-03-23 09:30:41 +01001651
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001652 if self.verbose:
1653 print "branches: %s" % self.p4BranchesInGit
1654
1655 p4Change = 0
1656 for branch in self.p4BranchesInGit:
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001657 logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch)
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001658
1659 settings = extractSettingsGitLog(logMsg)
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001660
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001661 self.readOptions(settings)
1662 if (settings.has_key('depot-paths')
1663 and settings.has_key ('change')):
1664 change = int(settings['change']) + 1
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001665 p4Change = max(p4Change, change)
1666
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001667 depotPaths = sorted(settings['depot-paths'])
1668 if self.previousDepotPaths == []:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001669 self.previousDepotPaths = depotPaths
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001670 else:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001671 paths = []
1672 for (prev, cur) in zip(self.previousDepotPaths, depotPaths):
Simon Hausmann583e1702007-06-07 09:37:13 +02001673 for i in range(0, min(len(cur), len(prev))):
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001674 if cur[i] <> prev[i]:
Simon Hausmann583e1702007-06-07 09:37:13 +02001675 i = i - 1
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001676 break
1677
Simon Hausmann583e1702007-06-07 09:37:13 +02001678 paths.append (cur[:i + 1])
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001679
1680 self.previousDepotPaths = paths
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001681
1682 if p4Change > 0:
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001683 self.depotPaths = sorted(self.previousDepotPaths)
Simon Hausmannd5904672007-05-19 11:07:32 +02001684 self.changeRange = "@%s,#head" % p4Change
Simon Hausmann330f53b2007-06-07 09:39:51 +02001685 if not self.detectBranches:
1686 self.initialParent = parseRevision(self.branch)
Simon Hausmann341dc1c2007-05-21 00:39:16 +02001687 if not self.silent and not self.detectBranches:
Simon Hausmann967f72e2007-03-23 09:30:41 +01001688 print "Performing incremental import into %s git branch" % self.branch
Simon Hausmann569d1bd2007-03-22 21:34:16 +01001689
Simon Hausmannf9162f62007-05-17 09:02:45 +02001690 if not self.branch.startswith("refs/"):
1691 self.branch = "refs/heads/" + self.branch
Simon Hausmann179caeb2007-03-22 22:17:42 +01001692
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001693 if len(args) == 0 and self.depotPaths:
Simon Hausmannb9847332007-03-20 20:54:23 +01001694 if not self.silent:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001695 print "Depot paths: %s" % ' '.join(self.depotPaths)
Simon Hausmannb9847332007-03-20 20:54:23 +01001696 else:
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001697 if self.depotPaths and self.depotPaths != args:
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001698 print ("previous import used depot path %s and now %s was specified. "
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001699 "This doesn't work!" % (' '.join (self.depotPaths),
1700 ' '.join (args)))
Simon Hausmannb9847332007-03-20 20:54:23 +01001701 sys.exit(1)
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001702
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001703 self.depotPaths = sorted(args)
Simon Hausmannb9847332007-03-20 20:54:23 +01001704
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001705 revision = ""
Simon Hausmannb9847332007-03-20 20:54:23 +01001706 self.users = {}
Simon Hausmannb9847332007-03-20 20:54:23 +01001707
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001708 newPaths = []
1709 for p in self.depotPaths:
1710 if p.find("@") != -1:
1711 atIdx = p.index("@")
1712 self.changeRange = p[atIdx:]
1713 if self.changeRange == "@all":
1714 self.changeRange = ""
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001715 elif ',' not in self.changeRange:
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001716 revision = self.changeRange
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001717 self.changeRange = ""
Han-Wen Nienhuys7fcff9d2007-07-23 15:56:37 -07001718 p = p[:atIdx]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001719 elif p.find("#") != -1:
1720 hashIdx = p.index("#")
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001721 revision = p[hashIdx:]
Han-Wen Nienhuys7fcff9d2007-07-23 15:56:37 -07001722 p = p[:hashIdx]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001723 elif self.previousDepotPaths == []:
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001724 revision = "#head"
Simon Hausmannb9847332007-03-20 20:54:23 +01001725
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001726 p = re.sub ("\.\.\.$", "", p)
1727 if not p.endswith("/"):
1728 p += "/"
1729
1730 newPaths.append(p)
1731
1732 self.depotPaths = newPaths
1733
Simon Hausmannb9847332007-03-20 20:54:23 +01001734
Simon Hausmannb607e712007-05-20 10:55:54 +02001735 self.loadUserMapFromCache()
Simon Hausmanncb53e1f2007-04-08 00:12:02 +02001736 self.labels = {}
1737 if self.detectLabels:
1738 self.getLabels();
Simon Hausmannb9847332007-03-20 20:54:23 +01001739
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001740 if self.detectBranches:
Simon Hausmanndf450922007-06-08 08:49:22 +02001741 ## FIXME - what's a P4 projectName ?
1742 self.projectName = self.guessProjectName()
1743
Simon Hausmann38f9f5e2007-11-15 10:38:45 +01001744 if self.hasOrigin:
1745 self.getBranchMappingFromGitBranches()
1746 else:
1747 self.getBranchMapping()
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001748 if self.verbose:
1749 print "p4-git branches: %s" % self.p4BranchesInGit
1750 print "initial parents: %s" % self.initialParents
1751 for b in self.p4BranchesInGit:
1752 if b != "master":
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001753
1754 ## FIXME
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001755 b = b[len(self.projectName):]
1756 self.createdBranches.add(b)
Simon Hausmann4b97ffb2007-05-18 21:45:23 +02001757
Simon Hausmannf291b4e2007-04-14 11:21:50 +02001758 self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
Simon Hausmannb9847332007-03-20 20:54:23 +01001759
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001760 importProcess = subprocess.Popen(["git", "fast-import"],
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001761 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1762 stderr=subprocess.PIPE);
Simon Hausmann08483582007-05-15 14:31:06 +02001763 self.gitOutput = importProcess.stdout
1764 self.gitStream = importProcess.stdin
1765 self.gitError = importProcess.stderr
Simon Hausmannb9847332007-03-20 20:54:23 +01001766
Simon Hausmann1c49fc12007-08-26 16:04:34 +02001767 if revision:
Simon Hausmannc208a242007-08-26 16:07:18 +02001768 self.importHeadRevision(revision)
Simon Hausmannb9847332007-03-20 20:54:23 +01001769 else:
1770 changes = []
1771
Simon Hausmann0828ab12007-03-20 20:59:30 +01001772 if len(self.changesFile) > 0:
Simon Hausmannb9847332007-03-20 20:54:23 +01001773 output = open(self.changesFile).readlines()
Reilly Grant1d7367d2009-09-10 00:02:38 -07001774 changeSet = set()
Simon Hausmannb9847332007-03-20 20:54:23 +01001775 for line in output:
1776 changeSet.add(int(line))
1777
1778 for change in changeSet:
1779 changes.append(change)
1780
1781 changes.sort()
1782 else:
Pete Wyckoffac34efc2011-03-12 11:23:23 -05001783 if not isinstance(self, P4Clone) and not self.p4BranchesInGit:
Pete Wyckoffe32e00d2011-02-19 08:17:59 -05001784 die("No remote p4 branches. Perhaps you never did \"git p4 clone\" in here.");
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001785 if self.verbose:
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001786 print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001787 self.changeRange)
Simon Hausmann4f6432d2007-08-26 15:56:36 +02001788 changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
Simon Hausmannb9847332007-03-20 20:54:23 +01001789
Simon Hausmann01a9c9c2007-05-23 00:07:35 +02001790 if len(self.maxChanges) > 0:
Han-Wen Nienhuys7fcff9d2007-07-23 15:56:37 -07001791 changes = changes[:min(int(self.maxChanges), len(changes))]
Simon Hausmann01a9c9c2007-05-23 00:07:35 +02001792
Simon Hausmannb9847332007-03-20 20:54:23 +01001793 if len(changes) == 0:
Simon Hausmann0828ab12007-03-20 20:59:30 +01001794 if not self.silent:
Simon Hausmann341dc1c2007-05-21 00:39:16 +02001795 print "No changes to import!"
Simon Hausmann1f52af62007-04-08 00:07:02 +02001796 return True
Simon Hausmannb9847332007-03-20 20:54:23 +01001797
Simon Hausmanna9d1a272007-06-11 23:28:03 +02001798 if not self.silent and not self.detectBranches:
1799 print "Import destination: %s" % self.branch
1800
Simon Hausmann341dc1c2007-05-21 00:39:16 +02001801 self.updatedBranches = set()
1802
Simon Hausmanne87f37a2007-08-26 16:00:52 +02001803 self.importChanges(changes)
Simon Hausmannb9847332007-03-20 20:54:23 +01001804
Simon Hausmann341dc1c2007-05-21 00:39:16 +02001805 if not self.silent:
1806 print ""
1807 if len(self.updatedBranches) > 0:
1808 sys.stdout.write("Updated branches: ")
1809 for b in self.updatedBranches:
1810 sys.stdout.write("%s " % b)
1811 sys.stdout.write("\n")
Simon Hausmannb9847332007-03-20 20:54:23 +01001812
Simon Hausmannb9847332007-03-20 20:54:23 +01001813 self.gitStream.close()
Simon Hausmann29bdbac2007-05-19 10:23:12 +02001814 if importProcess.wait() != 0:
1815 die("fast-import failed: %s" % self.gitError.read())
Simon Hausmannb9847332007-03-20 20:54:23 +01001816 self.gitOutput.close()
1817 self.gitError.close()
1818
Simon Hausmannb9847332007-03-20 20:54:23 +01001819 return True
1820
Simon Hausmann01ce1fe2007-04-07 23:46:50 +02001821class P4Rebase(Command):
1822 def __init__(self):
1823 Command.__init__(self)
Simon Hausmann01265102007-05-25 10:36:10 +02001824 self.options = [ ]
Han-Wen Nienhuyscebdf5a2007-05-23 16:53:11 -03001825 self.description = ("Fetches the latest revision from perforce and "
1826 + "rebases the current work (branch) against it")
Simon Hausmann68c42152007-06-07 12:51:03 +02001827 self.verbose = False
Simon Hausmann01ce1fe2007-04-07 23:46:50 +02001828
1829 def run(self, args):
1830 sync = P4Sync()
1831 sync.run([])
Simon Hausmannd7e38682007-06-12 14:34:46 +02001832
Simon Hausmann14594f42007-08-22 09:07:15 +02001833 return self.rebase()
1834
1835 def rebase(self):
Simon Hausmann36ee4ee2008-01-07 14:21:45 +01001836 if os.system("git update-index --refresh") != 0:
1837 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.");
1838 if len(read_pipe("git diff-index HEAD --")) > 0:
1839 die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
1840
Simon Hausmannd7e38682007-06-12 14:34:46 +02001841 [upstream, settings] = findUpstreamBranchPoint()
1842 if len(upstream) == 0:
1843 die("Cannot find upstream branchpoint for rebase")
1844
1845 # the branchpoint may be p4/foo~3, so strip off the parent
1846 upstream = re.sub("~[0-9]+$", "", upstream)
1847
1848 print "Rebasing the current branch onto %s" % upstream
Han-Wen Nienhuysb25b2062007-05-23 18:49:35 -03001849 oldHead = read_pipe("git rev-parse HEAD").strip()
Simon Hausmannd7e38682007-06-12 14:34:46 +02001850 system("git rebase %s" % upstream)
Simon Hausmann1f52af62007-04-08 00:07:02 +02001851 system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
Simon Hausmann01ce1fe2007-04-07 23:46:50 +02001852 return True
1853
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001854class P4Clone(P4Sync):
1855 def __init__(self):
1856 P4Sync.__init__(self)
1857 self.description = "Creates a new git repository and imports from Perforce into it"
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001858 self.usage = "usage: %prog [options] //depot/path[@revRange]"
Tommy Thorn354081d2008-02-03 10:38:51 -08001859 self.options += [
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001860 optparse.make_option("--destination", dest="cloneDestination",
1861 action='store', default=None,
Tommy Thorn354081d2008-02-03 10:38:51 -08001862 help="where to leave result of the clone"),
1863 optparse.make_option("-/", dest="cloneExclude",
1864 action="append", type="string",
Pete Wyckoff38200072011-02-19 08:18:01 -05001865 help="exclude depot path"),
1866 optparse.make_option("--bare", dest="cloneBare",
1867 action="store_true", default=False),
Tommy Thorn354081d2008-02-03 10:38:51 -08001868 ]
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001869 self.cloneDestination = None
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001870 self.needsGit = False
Pete Wyckoff38200072011-02-19 08:18:01 -05001871 self.cloneBare = False
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001872
Tommy Thorn354081d2008-02-03 10:38:51 -08001873 # This is required for the "append" cloneExclude action
1874 def ensure_value(self, attr, value):
1875 if not hasattr(self, attr) or getattr(self, attr) is None:
1876 setattr(self, attr, value)
1877 return getattr(self, attr)
1878
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001879 def defaultDestination(self, args):
1880 ## TODO: use common prefix of args?
1881 depotPath = args[0]
1882 depotDir = re.sub("(@[^@]*)$", "", depotPath)
1883 depotDir = re.sub("(#[^#]*)$", "", depotDir)
Toby Allsopp053d9e42008-02-05 09:41:43 +13001884 depotDir = re.sub(r"\.\.\.$", "", depotDir)
Han-Wen Nienhuys6a49f8e2007-05-23 18:49:35 -03001885 depotDir = re.sub(r"/$", "", depotDir)
1886 return os.path.split(depotDir)[1]
1887
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001888 def run(self, args):
1889 if len(args) < 1:
1890 return False
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001891
1892 if self.keepRepoPath and not self.cloneDestination:
1893 sys.stderr.write("Must specify destination for --keep-path\n")
1894 sys.exit(1)
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001895
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001896 depotPaths = args
Simon Hausmann5e100b52007-06-07 21:12:25 +02001897
1898 if not self.cloneDestination and len(depotPaths) > 1:
1899 self.cloneDestination = depotPaths[-1]
1900 depotPaths = depotPaths[:-1]
1901
Tommy Thorn354081d2008-02-03 10:38:51 -08001902 self.cloneExclude = ["/"+p for p in self.cloneExclude]
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001903 for p in depotPaths:
1904 if not p.startswith("//"):
1905 return False
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001906
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001907 if not self.cloneDestination:
Marius Storm-Olsen98ad4fa2007-06-07 15:08:33 +02001908 self.cloneDestination = self.defaultDestination(args)
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001909
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001910 print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
Pete Wyckoff38200072011-02-19 08:18:01 -05001911
Kevin Greenc3bf3f12007-06-11 16:48:07 -04001912 if not os.path.exists(self.cloneDestination):
1913 os.makedirs(self.cloneDestination)
Robert Blum053fd0c2008-08-01 12:50:03 -07001914 chdir(self.cloneDestination)
Pete Wyckoff38200072011-02-19 08:18:01 -05001915
1916 init_cmd = [ "git", "init" ]
1917 if self.cloneBare:
1918 init_cmd.append("--bare")
1919 subprocess.check_call(init_cmd)
1920
Han-Wen Nienhuys6326aa52007-05-23 18:49:35 -03001921 if not P4Sync.run(self, depotPaths):
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001922 return False
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001923 if self.branch != "master":
Tor Arvid Lunde9905012008-08-28 00:36:12 +02001924 if self.importIntoRemotes:
1925 masterbranch = "refs/remotes/p4/master"
1926 else:
1927 masterbranch = "refs/heads/p4/master"
1928 if gitBranchExists(masterbranch):
1929 system("git branch master %s" % masterbranch)
Pete Wyckoff38200072011-02-19 08:18:01 -05001930 if not self.cloneBare:
1931 system("git checkout -f")
Simon Hausmann8f9b2e02007-05-18 22:13:26 +02001932 else:
1933 print "Could not detect main branch. No checkout/master branch created."
Han-Wen Nienhuys86dff6b2007-05-23 18:49:35 -03001934
Simon Hausmannf9a3a4f2007-04-08 10:08:26 +02001935 return True
1936
Simon Hausmann09d89de2007-06-20 23:10:28 +02001937class P4Branches(Command):
1938 def __init__(self):
1939 Command.__init__(self)
1940 self.options = [ ]
1941 self.description = ("Shows the git branches that hold imports and their "
1942 + "corresponding perforce depot paths")
1943 self.verbose = False
1944
1945 def run(self, args):
Simon Hausmann5ca44612007-08-24 17:44:16 +02001946 if originP4BranchesExist():
1947 createOrUpdateBranchesFromOrigin()
1948
Simon Hausmann09d89de2007-06-20 23:10:28 +02001949 cmdline = "git rev-parse --symbolic "
1950 cmdline += " --remotes"
1951
1952 for line in read_pipe_lines(cmdline):
1953 line = line.strip()
1954
1955 if not line.startswith('p4/') or line == "p4/HEAD":
1956 continue
1957 branch = line
1958
1959 log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
1960 settings = extractSettingsGitLog(log)
1961
1962 print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
1963 return True
1964
Simon Hausmannb9847332007-03-20 20:54:23 +01001965class HelpFormatter(optparse.IndentedHelpFormatter):
1966 def __init__(self):
1967 optparse.IndentedHelpFormatter.__init__(self)
1968
1969 def format_description(self, description):
1970 if description:
1971 return description + "\n"
1972 else:
1973 return ""
Simon Hausmann4f5cf762007-03-19 22:25:17 +01001974
Simon Hausmann86949ee2007-03-19 20:59:12 +01001975def printUsage(commands):
1976 print "usage: %s <command> [options]" % sys.argv[0]
1977 print ""
1978 print "valid commands: %s" % ", ".join(commands)
1979 print ""
1980 print "Try %s <command> --help for command specific help." % sys.argv[0]
1981 print ""
1982
1983commands = {
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001984 "debug" : P4Debug,
1985 "submit" : P4Submit,
Marius Storm-Olsena9834f52007-10-09 16:16:09 +02001986 "commit" : P4Submit,
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03001987 "sync" : P4Sync,
1988 "rebase" : P4Rebase,
1989 "clone" : P4Clone,
Simon Hausmann09d89de2007-06-20 23:10:28 +02001990 "rollback" : P4RollBack,
1991 "branches" : P4Branches
Simon Hausmann86949ee2007-03-19 20:59:12 +01001992}
1993
Simon Hausmann86949ee2007-03-19 20:59:12 +01001994
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03001995def main():
1996 if len(sys.argv[1:]) == 0:
1997 printUsage(commands.keys())
1998 sys.exit(2)
Simon Hausmann86949ee2007-03-19 20:59:12 +01001999
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002000 cmd = ""
2001 cmdName = sys.argv[1]
2002 try:
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03002003 klass = commands[cmdName]
2004 cmd = klass()
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002005 except KeyError:
2006 print "unknown command %s" % cmdName
2007 print ""
2008 printUsage(commands.keys())
2009 sys.exit(2)
Simon Hausmann4f5cf762007-03-19 22:25:17 +01002010
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002011 options = cmd.options
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03002012 cmd.gitdir = os.environ.get("GIT_DIR", None)
Simon Hausmann86949ee2007-03-19 20:59:12 +01002013
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002014 args = sys.argv[2:]
Simon Hausmanne20a9e52007-03-26 00:13:51 +02002015
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002016 if len(options) > 0:
2017 options.append(optparse.make_option("--git-dir", dest="gitdir"))
Simon Hausmanne20a9e52007-03-26 00:13:51 +02002018
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002019 parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
2020 options,
2021 description = cmd.description,
2022 formatter = HelpFormatter())
Simon Hausmann86949ee2007-03-19 20:59:12 +01002023
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002024 (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
2025 global verbose
2026 verbose = cmd.verbose
2027 if cmd.needsGit:
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03002028 if cmd.gitdir == None:
2029 cmd.gitdir = os.path.abspath(".git")
2030 if not isValidGitDir(cmd.gitdir):
2031 cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
2032 if os.path.exists(cmd.gitdir):
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002033 cdup = read_pipe("git rev-parse --show-cdup").strip()
2034 if len(cdup) > 0:
Robert Blum053fd0c2008-08-01 12:50:03 -07002035 chdir(cdup);
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002036
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03002037 if not isValidGitDir(cmd.gitdir):
2038 if isValidGitDir(cmd.gitdir + "/.git"):
2039 cmd.gitdir += "/.git"
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002040 else:
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03002041 die("fatal: cannot locate git repository at %s" % cmd.gitdir)
Simon Hausmann8910ac02007-03-26 08:18:55 +02002042
Han-Wen Nienhuysb86f7372007-05-23 18:49:35 -03002043 os.environ["GIT_DIR"] = cmd.gitdir
Simon Hausmann4f5cf762007-03-19 22:25:17 +01002044
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002045 if not cmd.run(args):
2046 parser.print_help()
Simon Hausmann4f5cf762007-03-19 22:25:17 +01002047
Han-Wen Nienhuysbb6e09b2007-05-23 18:49:35 -03002048
2049if __name__ == '__main__':
2050 main()