Re: [PATCH 1/1] git-p4: add unshelve command
From: Luke Diamand <hidden>
Date: 2018-05-16 20:48:56
On 13 May 2018 at 14:52, Merland Romain [off-list ref] wrote:
Hello Luke, Very interseting This is indeed an option we are waiting since the introduction of option --shelve for git p4 submit What I like most in your approach is the preservation of link to p4/master inducing small changes of git-p4 existing functions already heavily tested. Also I like the dedicated branch you create, it is cleaner and then we can cherry-pick in other git branches.
Thanks, I'd be interested to know how you get on with it!
We made some basic tries on our side, just adding an option --unshelve to P4Submit (for simplicity, but I like much more your P4Unshelve class) and trying to use the diff of p4 to generate a patch when possible, then apply it in the current git branch on top of HEAD. Here it is, hope it can help a bit. Note it also uses p4 print -o for binary files.
I did try something like this earlier this year (if you look in the archives you'll find it) but I found that it was getting quite complicated trying to construct a sensible looking patch file from the output of p4 describe. Better to let git's existing tools do that, as they're going to be better than any half-baked attempt I might manage in Python! Thanks! Luke
quoted hunk
diff --git a/git-p4.py b/git-p4.py index f4a6f3b4c..b466b46e1 100755 --- a/git-p4.py +++ b/git-p4.py@@ -1353,6 +1353,8 @@ class P4Submit(Command, P4UserMap): metavar="CHANGELIST", help="update an existing shelved changelist, implies --shelve, " "repeat in-order for multiple shelved changelists"), + optparse.make_option("--unshelve", dest="unshelve", + help="unshelve speficied ChangeList into current BRANCH."), optparse.make_option("--commit", dest="commit", metavar="COMMIT", help="submit only the specified commit(s), one commit or xxx..xxx"), optparse.make_option("--disable-rebase", dest="disable_rebase", action="store_true",@@ -1367,6 +1369,7 @@ class P4Submit(Command, P4UserMap): self.dry_run = False self.shelve = False self.update_shelve = list() + self.unshelve = "" self.commit = "" self.disable_rebase = False self.prepare_p4_only = False@@ -2083,6 +2086,66 @@ class P4Submit(Command, P4UserMap): if self.clientPath == "": die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath) + # special case of unshelving + # todo: put this code in a class like P4Sync or P4Rebase + if self.unshelve != "": + git_dir = os.getcwd() + '/' + print "Importing shelved CL %s into current git branch %s" % (self.unshelve, self.master) + description = p4_describe(self.unshelve) + + # get changed files + files = p4CmdList(['files', "@=%s" % self.unshelve]) + editedFiles = [] + filesToAdd = [] + filesToDelete = [] + binaryFiles = [] + something_to_commit = False + for f in files: + if not f["depotFile"].startswith(self.depotPath): + print "WARNING: file %s not in this p4 depot - skipping" % f["depotFile"] + continue + + elif f["action"] == 'delete': + filesToDelete.append(f) + something_to_commit = True + elif f["action"] == 'add': + filesToAdd.append(f) + something_to_commit = True + elif f["type"] == 'binary': + binaryFiles.append(f) + something_to_commit = True + elif f["action"] == 'edit': + editedFiles.append(f) + something_to_commit = True + + f["clientFile"] = f["depotFile"].replace(self.depotPath,self.clientPath) + f["gitFile"] = f["depotFile"].replace(self.depotPath,git_dir) + + if not something_to_commit: + print "Nothing to commit. Exiting" + return True + + # get the diff and copy to diff directory + for f in editedFiles: + p4diff = p4_read_pipe(['diff2', '-du', f["depotFile"]+'#'+f["rev"], f["depotFile"]+'@='+self.unshelve]) + p4diff = "\n".join(p4diff.split("\n")[1:]) + p4diff = '--- '+f["gitFile"]+'\n' + '+++ '+f["gitFile"]+'\n' + p4diff + write_pipe(['patch', '-d/', '-p0'], p4diff) + write_pipe(['git', 'add', '-f', f["gitFile"]], "") + for f in filesToAdd: + p4_write_pipe(['print', '-o', f["gitFile"], f["depotFile"]+'@='+self.unshelve], "") + write_pipe(['git', 'add', '-f', f["gitFile"]], "") + for f in filesToDelete: + os.remove(f["gitFile"]) + write_pipe(['git', 'rm', f["gitFile"]], "") + for f in binaryFiles: + p4_write_pipe(['print', '-o', f["gitFile"], f["depotFile"]+'@='+self.unshelve], "") + write_pipe(['git', 'add', '-f', f["gitFile"]], "") + + # finalize: commit in git + write_pipe(['git', 'commit', '-m', description["desc"]], "") + return True + print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd()Romain Le samedi 12 mai 2018 à 23:24:48 UTC+2, Luke Diamand [off-list ref] a écrit : This can be used to "unshelve" a shelved P4 commit into a git commit. For example: $ git p4 unshelve 12345 The resulting commit ends up in the branch: refs/remotes/p4/unshelved/12345 If that branch already exists, it is renamed - for example the above branch would be saved as p4/unshelved/12345.1. Caveat: The unshelving is done against the current "p4/master" branch; git-p4 uses "p4 print" to get the file contents at the requested revision, and then fast-import creates a commit relative to p4/master. Ideally what you would want is for fast-import to create the commit based on the Perforce "revision" prior to the shelved commit, but Perforce doesn't have such a concept - to do this, git-p4 would need to figure out the revisions of the individual files before the shelved changelist, and then construct a temporary git branch which matched this. It's possible to do this, but doing so makes this change a lot more complicated. This limitation means that if you unshelve a change where some of the changed files were not based on p4/master, you will get an amalgam of the change you wanted, and these other changes. The reference branch can be changed manually with the "--origin" option. The change adds a new Unshelve command class. This just runs the existing P4Sync code tweaked to handle a shelved changelist. Signed-off-by: Luke Diamand <redacted>