From: Junio C Hamano Date: Tue, 27 Dec 2016 08:11:42 +0000 (-0800) Subject: Merge branch 'ld/p4-compare-dir-vs-symlink' X-Git-Tag: v2.12.0-rc0~102 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/f723df581075ae70252a93ba91dcd017f338236e?hp=-c Merge branch 'ld/p4-compare-dir-vs-symlink' "git p4" misbehaved when swapping a directory and a symbolic link. * ld/p4-compare-dir-vs-symlink: git-p4: avoid crash adding symlinked directory --- f723df581075ae70252a93ba91dcd017f338236e diff --combined git-p4.py index 73f5fce4d6,16d0b8a6ee..16e3a18e73 --- a/git-p4.py +++ b/git-p4.py @@@ -25,6 -25,7 +25,7 @@@ import sta import zipfile import zlib import ctypes + import errno try: from subprocess import CalledProcessError @@@ -78,11 -79,6 +79,11 @@@ def p4_build_cmd(cmd) if len(client) > 0: real_cmd += ["-c", client] + retries = gitConfigInt("git-p4.retries") + if retries is None: + # Perform 3 retries by default + retries = 3 + real_cmd += ["-r", str(retries)] if isinstance(cmd,basestring): real_cmd = ' '.join(real_cmd) + ' ' + cmd @@@ -90,16 -86,6 +91,16 @@@ real_cmd += cmd return real_cmd +def git_dir(path): + """ Return TRUE if the given path is a git directory (/path/to/dir/.git). + This won't automatically add ".git" to a directory. + """ + d = read_pipe(["git", "--git-dir", path, "rev-parse", "--git-dir"], True).strip() + if not d or len(d) == 0: + return None + else: + return d + def chdir(path, is_client_path=False): """Do chdir to the given path, and set the PWD environment variable for use by P4. It does not look at getcwd() output. @@@ -277,10 -263,6 +278,10 @@@ def p4_revert(f) def p4_reopen(type, f): p4_system(["reopen", "-t", type, wildcard_encode(f)]) +def p4_reopen_in_change(changelist, files): + cmd = ["reopen", "-c", str(changelist)] + files + p4_system(cmd) + def p4_move(src, dest): p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)]) @@@ -582,7 -564,10 +583,7 @@@ def currentGitBranch() return read_pipe(["git", "name-rev", "HEAD"]).split(" ")[1].strip() def isValidGitDir(path): - if (os.path.exists(path + "/HEAD") - and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")): - return True; - return False + return git_dir(path) != None def parseRevision(ref): return read_pipe("git rev-parse %s" % ref).strip() @@@ -1021,20 -1006,18 +1022,20 @@@ class LargeFileSystem(object) steps.""" if self.exceedsLargeFileThreshold(relPath, contents) or self.hasLargeFileExtension(relPath): contentTempFile = self.generateTempFile(contents) - (git_mode, contents, localLargeFile) = self.generatePointer(contentTempFile) - - # Move temp file to final location in large file system - largeFileDir = os.path.dirname(localLargeFile) - if not os.path.isdir(largeFileDir): - os.makedirs(largeFileDir) - shutil.move(contentTempFile, localLargeFile) - self.addLargeFile(relPath) - if gitConfigBool('git-p4.largeFilePush'): - self.pushFile(localLargeFile) - if verbose: - sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile)) + (pointer_git_mode, contents, localLargeFile) = self.generatePointer(contentTempFile) + if pointer_git_mode: + git_mode = pointer_git_mode + if localLargeFile: + # Move temp file to final location in large file system + largeFileDir = os.path.dirname(localLargeFile) + if not os.path.isdir(largeFileDir): + os.makedirs(largeFileDir) + shutil.move(contentTempFile, localLargeFile) + self.addLargeFile(relPath) + if gitConfigBool('git-p4.largeFilePush'): + self.pushFile(localLargeFile) + if verbose: + sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile)) return (git_mode, contents) class MockLFS(LargeFileSystem): @@@ -1074,9 -1057,6 +1075,9 @@@ class GitLFS(LargeFileSystem) the actual content. Return also the new location of the actual content. """ + if os.path.getsize(contentFile) == 0: + return (None, '', None) + pointerProcess = subprocess.Popen( ['git', 'lfs', 'pointer', '--file=' + contentFile], stdout=subprocess.PIPE @@@ -1310,12 -1290,6 +1311,12 @@@ class P4Submit(Command, P4UserMap) optparse.make_option("--conflict", dest="conflict_behavior", choices=self.conflict_behavior_choices), optparse.make_option("--branch", dest="branch"), + optparse.make_option("--shelve", dest="shelve", action="store_true", + help="Shelve instead of submit. Shelved files are reverted, " + "restoring the workspace to the state before the shelve"), + optparse.make_option("--update-shelve", dest="update_shelve", action="store", type="int", + metavar="CHANGELIST", + help="update an existing shelved changelist, implies --shelve") ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@@ -1323,8 -1297,6 +1324,8 @@@ self.detectRenames = False self.preserveUser = gitConfigBool("git-p4.preserveUser") self.dry_run = False + self.shelve = False + self.update_shelve = None self.prepare_p4_only = False self.conflict_behavior = None self.isWindows = (platform.system() == "Windows") @@@ -1493,7 -1465,7 +1494,7 @@@ return 1 return 0 - def prepareSubmitTemplate(self): + def prepareSubmitTemplate(self, changelist=None): """Run "p4 change -o" to grab a change specification template. This does not use "p4 -G", as it is nice to keep the submission template in original order, since a human might edit it. @@@ -1505,11 -1477,7 +1506,11 @@@ template = "" inFilesSection = False - for line in p4_read_pipe_lines(['change', '-o']): + args = ['change', '-o'] + if changelist: + args.append(str(changelist)) + + for line in p4_read_pipe_lines(args): if line.endswith("\r\n"): line = line[:-2] + "\n" if inFilesSection: @@@ -1571,7 -1539,7 +1572,7 @@@ if response == 'n': return False - def get_diff_description(self, editedFiles, filesToAdd): + def get_diff_description(self, editedFiles, filesToAdd, symlinks): # diff if os.environ.has_key("P4DIFF"): del(os.environ["P4DIFF"]) @@@ -1586,10 -1554,17 +1587,17 @@@ newdiff += "==== new file ====\n" newdiff += "--- /dev/null\n" newdiff += "+++ %s\n" % newFile - f = open(newFile, "r") - for line in f.readlines(): - newdiff += "+" + line - f.close() + + is_link = os.path.islink(newFile) + expect_link = newFile in symlinks + + if is_link and expect_link: + newdiff += "+%s\n" % os.readlink(newFile) + else: + f = open(newFile, "r") + for line in f.readlines(): + newdiff += "+" + line + f.close() return (diff + newdiff).replace('\r\n', '\n') @@@ -1607,15 -1582,13 +1615,16 @@@ filesToDelete = set() editedFiles = set() pureRenameCopy = set() + symlinks = set() filesToChangeExecBit = {} + all_files = list() for line in diff: diff = parseDiffTreeEntry(line) modifier = diff['status'] path = diff['src'] + all_files.append(path) + if modifier == "M": p4_edit(path) if isModeExecChanged(diff['src_mode'], diff['dst_mode']): @@@ -1626,6 -1599,11 +1635,11 @@@ filesToChangeExecBit[path] = diff['dst_mode'] if path in filesToDelete: filesToDelete.remove(path) + + dst_mode = int(diff['dst_mode'], 8) + if dst_mode == 0120000: + symlinks.add(path) + elif modifier == "D": filesToDelete.add(path) if path in filesToAdd: @@@ -1741,10 -1719,6 +1755,10 @@@ mode = filesToChangeExecBit[f] setP4ExecBit(f, mode) + if self.update_shelve: + print("all_files = %s" % str(all_files)) + p4_reopen_in_change(self.update_shelve, all_files) + # # Build p4 change description, starting with the contents # of the git commit message. @@@ -1753,7 -1727,7 +1767,7 @@@ logMessage = logMessage.strip() (logMessage, jobs) = self.separate_jobs_from_description(logMessage) - template = self.prepareSubmitTemplate() + template = self.prepareSubmitTemplate(self.update_shelve) submitTemplate = self.prepareLogMessage(template, logMessage, jobs) if self.preserveUser: @@@ -1767,7 -1741,7 +1781,7 @@@ separatorLine = "######## everything below this line is just the diff #######\n" if not self.prepare_p4_only: submitTemplate += separatorLine - submitTemplate += self.get_diff_description(editedFiles, filesToAdd) + submitTemplate += self.get_diff_description(editedFiles, filesToAdd, symlinks) (handle, fileName) = tempfile.mkstemp() tmpFile = os.fdopen(handle, "w+b") @@@ -1825,17 -1799,7 +1839,17 @@@ if self.isWindows: message = message.replace("\r\n", "\n") submitTemplate = message[:message.index(separatorLine)] - p4_write_pipe(['submit', '-i'], submitTemplate) + + if self.update_shelve: + p4_write_pipe(['shelve', '-r', '-i'], submitTemplate) + elif self.shelve: + p4_write_pipe(['shelve', '-i'], submitTemplate) + else: + p4_write_pipe(['submit', '-i'], submitTemplate) + # The rename/copy happened by applying a patch that created a + # new file. This leaves it writable, which confuses p4. + for f in pureRenameCopy: + p4_sync(f, "-f") if self.preserveUser: if p4User: @@@ -1845,20 -1809,23 +1859,20 @@@ changelist = self.lastP4Changelist() self.modifyChangelistUser(changelist, p4User) - # The rename/copy happened by applying a patch that created a - # new file. This leaves it writable, which confuses p4. - for f in pureRenameCopy: - p4_sync(f, "-f") submitted = True finally: # skip this patch - if not submitted: - print "Submission cancelled, undoing p4 changes." - for f in editedFiles: + if not submitted or self.shelve: + if self.shelve: + print ("Reverting shelved files.") + else: + print ("Submission cancelled, undoing p4 changes.") + for f in editedFiles | filesToDelete: p4_revert(f) for f in filesToAdd: p4_revert(f) os.remove(f) - for f in filesToDelete: - p4_revert(f) os.remove(fileName) return submitted @@@ -1954,9 -1921,6 +1968,9 @@@ if len(self.origin) == 0: self.origin = upstream + if self.update_shelve: + self.shelve = True + if self.preserveUser: if not self.canChangeChangelists(): die("Cannot preserve user names without p4 super-user or admin permissions") @@@ -2117,13 -2081,13 +2131,13 @@@ break chdir(self.oldWorkingDirectory) - + shelved_applied = "shelved" if self.shelve else "applied" if self.dry_run: pass elif self.prepare_p4_only: pass elif len(commits) == len(applied): - print "All commits applied!" + print ("All commits {0}!".format(shelved_applied)) sync = P4Sync() if self.branch: @@@ -2135,9 -2099,9 +2149,9 @@@ else: if len(applied) == 0: - print "No commits applied." + print ("No commits {0}.".format(shelved_applied)) else: - print "Applied only the commits marked with '*':" + print ("{0} only the commits marked with '*':".format(shelved_applied.capitalize())) for c in commits: if c in applied: star = "*" @@@ -3732,7 -3696,6 +3746,7 @@@ def main() if cmd.gitdir == None: cmd.gitdir = os.path.abspath(".git") if not isValidGitDir(cmd.gitdir): + # "rev-parse --git-dir" without arguments will try $PWD/.git cmd.gitdir = read_pipe("git rev-parse --git-dir").strip() if os.path.exists(cmd.gitdir): cdup = read_pipe("git rev-parse --show-cdup").strip() @@@ -3745,7 -3708,6 +3759,7 @@@ else: die("fatal: cannot locate git repository at %s" % cmd.gitdir) + # so git commands invoked from the P4 workspace will succeed os.environ["GIT_DIR"] = cmd.gitdir if not cmd.run(args):