diff options
Diffstat (limited to 'git-p4.py')
-rwxr-xr-x | git-p4.py | 106 |
1 files changed, 68 insertions, 38 deletions
@@ -1306,6 +1306,9 @@ class GitLFS(LargeFileSystem): return LargeFileSystem.processContent(self, git_mode, relPath, contents) class Command: + delete_actions = ( "delete", "move/delete", "purge" ) + add_actions = ( "add", "move/add" ) + def __init__(self): self.usage = "usage: %prog [options]" self.needsGit = True @@ -1494,7 +1497,13 @@ class P4Submit(Command, P4UserMap): optparse.make_option("--disable-p4sync", dest="disable_p4sync", action="store_true", help="Skip Perforce sync of p4/master after submit or shelve"), ] - self.description = "Submit changes from git to the perforce depot." + self.description = """Submit changes from git to the perforce depot.\n + The `p4-pre-submit` hook is executed if it exists and is executable. + The hook takes no parameters and nothing from standard input. Exiting with + non-zero status from this script prevents `git-p4 submit` from launching. + + One usage scenario is to run unit tests in the hook.""" + self.usage += " [name of git branch to submit into perforce depot]" self.origin = "" self.detectRenames = False @@ -2303,6 +2312,14 @@ class P4Submit(Command, P4UserMap): sys.exit("number of commits (%d) must match number of shelved changelist (%d)" % (len(commits), num_shelves)) + hooks_path = gitConfig("core.hooksPath") + if len(hooks_path) <= 0: + hooks_path = os.path.join(os.environ.get("GIT_DIR", ".git"), "hooks") + + hook_file = os.path.join(hooks_path, "p4-pre-submit") + if os.path.isfile(hook_file) and os.access(hook_file, os.X_OK) and subprocess.call([hook_file]) != 0: + sys.exit(1) + # # Apply the commits, one at a time. On failure, ask if should # continue to try the rest of the patches, or quit. @@ -2510,7 +2527,6 @@ class View(object): return "" class P4Sync(Command, P4UserMap): - delete_actions = ( "delete", "move/delete", "purge" ) def __init__(self): Command.__init__(self) @@ -2598,20 +2614,7 @@ class P4Sync(Command, P4UserMap): if self.verbose: print("checkpoint finished: " + out) - def cmp_shelved(self, path, filerev, revision): - """ Determine if a path at revision #filerev is the same as the file - at revision @revision for a shelved changelist. If they don't match, - unshelving won't be safe (we will get other changes mixed in). - - This is comparing the revision that the shelved changelist is *based* on, not - the shelved changelist itself. - """ - ret = p4Cmd(["diff2", "{0}#{1}".format(path, filerev), "{0}@{1}".format(path, revision)]) - if verbose: - print("p4 diff2 path %s filerev %s revision %s => %s" % (path, filerev, revision, ret)) - return ret["status"] == "identical" - - def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0, origin_revision = 0): + def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0): self.cloneExclude = [re.sub(r"\.\.\.$", "", path) for path in self.cloneExclude] files = [] @@ -2636,17 +2639,6 @@ class P4Sync(Command, P4UserMap): file["type"] = commit["type%s" % fnum] if shelved: file["shelved_cl"] = int(shelved_cl) - - # For shelved changelists, check that the revision of each file that the - # shelve was based on matches the revision that we are using for the - # starting point for git-fast-import (self.initialParent). Otherwise - # the resulting diff will contain deltas from multiple commits. - - if file["action"] != "add" and \ - not self.cmp_shelved(path, file["rev"], origin_revision): - sys.exit("change {0} not based on {1} for {2}, cannot unshelve".format( - commit["change"], self.initialParent, path)) - files.append(file) fnum = fnum + 1 return files @@ -2761,7 +2753,10 @@ class P4Sync(Command, P4UserMap): relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes) relPath = self.encodeWithUTF8(relPath) if verbose: - size = int(self.stream_file['fileSize']) + if 'fileSize' in self.stream_file: + size = int(self.stream_file['fileSize']) + else: + size = 0 # deleted files don't get a fileSize apparently sys.stdout.write('\r%s --> %s (%i MB)\n' % (file['depotFile'], relPath, size/1024/1024)) sys.stdout.flush() @@ -3015,7 +3010,7 @@ class P4Sync(Command, P4UserMap): print('Ignoring file outside of prefix: {0}'.format(path)) return hasPrefix - def commit(self, details, files, branch, parent = ""): + def commit(self, details, files, branch, parent = "", allow_empty=False): epoch = details["time"] author = details["user"] jobs = self.extractJobsFromCommit(details) @@ -3029,7 +3024,10 @@ class P4Sync(Command, P4UserMap): files = [f for f in files if self.inClientSpec(f['path']) and self.hasBranchPrefix(f['path'])] - if not files and not gitConfigBool('git-p4.keepEmptyCommits'): + if gitConfigBool('git-p4.keepEmptyCommits'): + allow_empty = True + + if not files and not allow_empty: print('Ignoring revision {0} as it would produce an empty commit.' .format(details['change'])) return @@ -3370,10 +3368,10 @@ class P4Sync(Command, P4UserMap): else: return None - def importChanges(self, changes, shelved=False, origin_revision=0): + def importChanges(self, changes, origin_revision=0): cnt = 1 for change in changes: - description = p4_describe(change, shelved) + description = p4_describe(change) self.updateOptionDict(description) if not self.silent: @@ -3443,7 +3441,7 @@ class P4Sync(Command, P4UserMap): print("Parent of %s not found. Committing into head of %s" % (branch, parent)) self.commit(description, filesForCommit, branch, parent) else: - files = self.extractFilesFromCommit(description, shelved, change, origin_revision) + files = self.extractFilesFromCommit(description) self.commit(description, files, self.branch, self.initialParent) # only needed once, to connect to the previous commit @@ -3939,7 +3937,7 @@ class P4Unshelve(Command): ] self.verbose = False self.noCommit = False - self.destbranch = "refs/remotes/p4/unshelved" + self.destbranch = "refs/remotes/p4-unshelved" def renameBranch(self, branch_name): """ Rename the existing branch to branch_name.N @@ -3971,6 +3969,32 @@ class P4Unshelve(Command): sys.exit("could not find git-p4 commits in {0}".format(self.origin)) + def createShelveParent(self, change, branch_name, sync, origin): + """ Create a commit matching the parent of the shelved changelist 'change' + """ + parent_description = p4_describe(change, shelved=True) + parent_description['desc'] = 'parent for shelved changelist {}\n'.format(change) + files = sync.extractFilesFromCommit(parent_description, shelved=False, shelved_cl=change) + + parent_files = [] + for f in files: + # if it was added in the shelved changelist, it won't exist in the parent + if f['action'] in self.add_actions: + continue + + # if it was deleted in the shelved changelist it must not be deleted + # in the parent - we might even need to create it if the origin branch + # does not have it + if f['action'] in self.delete_actions: + f['action'] = 'add' + + parent_files.append(f) + + sync.commit(parent_description, parent_files, branch_name, + parent=origin, allow_empty=True) + print("created parent commit for {0} based on {1} in {2}".format( + change, self.origin, branch_name)) + def run(self, args): if len(args) != 1: return False @@ -3980,9 +4004,8 @@ class P4Unshelve(Command): sync = P4Sync() changes = args - sync.initialParent = self.origin - # use the first change in the list to construct the branch to unshelve into + # only one change at a time change = changes[0] # if the target branch already exists, rename it @@ -3995,14 +4018,21 @@ class P4Unshelve(Command): sync.suppress_meta_comment = True settings = self.findLastP4Revision(self.origin) - origin_revision = settings['change'] sync.depotPaths = settings['depot-paths'] sync.branchPrefixes = sync.depotPaths sync.openStreams() sync.loadUserMapFromCache() sync.silent = True - sync.importChanges(changes, shelved=True, origin_revision=origin_revision) + + # create a commit for the parent of the shelved changelist + self.createShelveParent(change, branch_name, sync, self.origin) + + # create the commit for the shelved changelist itself + description = p4_describe(change, True) + files = sync.extractFilesFromCommit(description, True, change) + + sync.commit(description, files, branch_name, "") sync.closeStreams() print("unshelved changelist {0} into {1}".format(change, branch_name)) |