# 2007 Trolltech ASA
# License: MIT <http://www.opensource.org/licenses/mit-license.php>
#
-
-import optparse, sys, os, marshal, subprocess, shelve
-import tempfile, getopt, os.path, time, platform
-import re, shutil
+import sys
+if sys.hexversion < 0x02040000:
+ # The limiter is the subprocess module
+ sys.stderr.write("git-p4: requires Python 2.4 or later.\n")
+ sys.exit(1)
+import os
+import optparse
+import marshal
+import subprocess
+import tempfile
+import time
+import platform
+import re
+import shutil
verbose = False
expand = isinstance(real_cmd, basestring)
subprocess.check_call(real_cmd, shell=expand)
+_p4_version_string = None
+def p4_version_string():
+ """Read the version string, showing just the last line, which
+ hopefully is the interesting version bit.
+
+ $ p4 -V
+ Perforce - The Fast Software Configuration Management System.
+ Copyright 1995-2011 Perforce Software. All rights reserved.
+ Rev. P4/NTX86/2011.1/393975 (2011/12/16).
+ """
+ global _p4_version_string
+ if not _p4_version_string:
+ a = p4_read_pipe_lines(["-V"])
+ _p4_version_string = a[-1].rstrip()
+ return _p4_version_string
+
def p4_integrate(src, dest):
p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)])
return branches
+def branch_exists(branch):
+ """Make sure that the given ref name really exists."""
+
+ cmd = [ "git", "rev-parse", "--symbolic", "--verify", branch ]
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, _ = p.communicate()
+ if p.returncode:
+ return False
+ # expect exactly one line of output: the branch name
+ return out.rstrip() == branch
+
def findUpstreamBranchPoint(head = "HEAD"):
branches = p4BranchesInGit()
# map from depot-path to branch name
optparse.make_option("--dry-run", "-n", dest="dry_run", action="store_true"),
optparse.make_option("--prepare-p4-only", dest="prepare_p4_only", action="store_true"),
optparse.make_option("--conflict", dest="conflict_behavior",
- choices=self.conflict_behavior_choices)
+ choices=self.conflict_behavior_choices),
+ optparse.make_option("--branch", dest="branch"),
]
self.description = "Submit changes from git to the perforce depot."
self.usage += " [name of git branch to submit into perforce depot]"
self.isWindows = (platform.system() == "Windows")
self.exportLabels = False
self.p4HasMoveCommand = p4_has_move_command()
+ self.branch = None
def check(self):
if len(p4CmdList("opened ...")) > 0:
print "All commits applied!"
sync = P4Sync()
+ if self.branch:
+ sync.branch = self.branch
sync.run([])
rebase = P4Rebase()
self.syncWithOrigin = True
self.importIntoRemotes = True
self.maxChanges = ""
- self.isWindows = (platform.system() == "Windows")
self.keepRepoPath = False
self.depotPaths = None
self.p4BranchesInGit = []
# operations. utf16 is converted to ascii or utf8, perhaps.
# But ascii text saved as -t utf16 is completely mangled.
# Invoke print -o to get the real contents.
+ #
+ # On windows, the newlines will always be mangled by print, so put
+ # them back too. This is not needed to the cygwin windows version,
+ # just the native "NT" type.
+ #
text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']])
+ if p4_version_string().find("/NT") >= 0:
+ text = text.replace("\r\n", "\n")
contents = [ text ]
if type_base == "apple":
print "\nIgnoring apple filetype file %s" % file['depotFile']
return
- # Perhaps windows wants unicode, utf16 newlines translated too;
- # but this is not doing it.
- if self.isWindows and type_base == "text":
- mangled = []
- for data in contents:
- data = data.replace("\r\n", "\n")
- mangled.append(data)
- contents = mangled
-
# Note that we do not try to de-mangle keywords on utf16 files,
# even though in theory somebody may want that.
pattern = p4_keywords_regexp_for_type(type_base, type_mods)
blob = None
if len(parent) > 0:
- tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change))
+ tempBranch = "%s/%d" % (self.tempBranchLocation, change)
if self.verbose:
print "Creating temporary branch: " + tempBranch
self.commit(description, filesForCommit, tempBranch)
files = self.extractFilesFromCommit(description)
self.commit(description, files, self.branch,
self.initialParent)
+ # only needed once, to connect to the previous commit
self.initialParent = ""
except IOError:
print self.gitError.read()
def run(self, args):
self.depotPaths = []
self.changeRange = ""
- self.initialParent = ""
self.previousDepotPaths = []
self.hasOrigin = False
print 'Syncing with origin first, using "git fetch origin"'
system("git fetch origin")
+ branch_arg_given = bool(self.branch)
if len(self.branch) == 0:
self.branch = self.refPrefix + "master"
if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
# branches holds mapping from branch name to sha1
branches = p4BranchesInGit(self.importIntoRemotes)
- self.p4BranchesInGit = branches.keys()
- for branch in branches.keys():
- self.initialParents[self.refPrefix + branch] = branches[branch]
+
+ # restrict to just this one, disabling detect-branches
+ if branch_arg_given:
+ short = self.branch.split("/")[-1]
+ if short in branches:
+ self.p4BranchesInGit = [ short ]
+ else:
+ self.p4BranchesInGit = branches.keys()
if len(self.p4BranchesInGit) > 1:
if not self.silent:
print "Importing from/into multiple branches"
self.detectBranches = True
+ for branch in branches.keys():
+ self.initialParents[self.refPrefix + branch] = \
+ branches[branch]
if self.verbose:
print "branches: %s" % self.p4BranchesInGit
if p4Change > 0:
self.depotPaths = sorted(self.previousDepotPaths)
self.changeRange = "@%s,#head" % p4Change
- if not self.detectBranches:
- self.initialParent = parseRevision(self.branch)
if not self.silent and not self.detectBranches:
print "Performing incremental import into %s git branch" % self.branch
else:
# catch "git p4 sync" with no new branches, in a repo that
# does not have any existing p4 branches
- if len(args) == 0 and not self.p4BranchesInGit:
- die("No remote p4 branches. Perhaps you never did \"git p4 clone\" in here.");
+ if len(args) == 0:
+ if not self.p4BranchesInGit:
+ die("No remote p4 branches. Perhaps you never did \"git p4 clone\" in here.")
+
+ # The default branch is master, unless --branch is used to
+ # specify something else. Make sure it exists, or complain
+ # nicely about how to use --branch.
+ if not self.detectBranches:
+ if not branch_exists(self.branch):
+ if branch_arg_given:
+ die("Error: branch %s does not exist." % self.branch)
+ else:
+ die("Error: no branch %s; perhaps specify one with --branch." %
+ self.branch)
+
if self.verbose:
print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
self.changeRange)
self.updatedBranches = set()
+ if not self.detectBranches:
+ if args:
+ # start a new branch
+ self.initialParent = ""
+ else:
+ # build on a previous revision
+ self.initialParent = parseRevision(self.branch)
+
self.importChanges(changes)
if not self.silent:
self.cloneExclude = ["/"+p for p in self.cloneExclude]
for p in depotPaths:
if not p.startswith("//"):
+ sys.stderr.write('Depot paths must start with "//": %s\n' % p)
return False
if not self.cloneDestination: