{"thread":{"id":"47476","subject":"[PATCH 0/1] git-p4: update multiple shelved change lists","startedAt":"2017-12-21T11:11:55Z","lastAt":"2017-12-21T11:11:58Z","messageCount":2,"participants":["Luke Diamand"],"isPatch":true,"patchVersion":1,"patchTotal":1},"messages":[{"id":"335121","messageId":"20171221110614.29300-1-luke@diamand.org","threadId":"47476","inReplyTo":null,"subject":"[PATCH 0/1] git-p4: update multiple shelved change lists","fromName":"Luke Diamand","fromEmail":"luke@diamand.org","sentAt":"2017-12-21T11:06:13Z","receivedAt":"2017-12-21T11:11:55Z","isPatch":true,"sender":{"key":"luke@diamand.org","avatar":"https://avatars.githubusercontent.com/u/5330967?v=4"},"body":"This change lets you update several P4 changelists in sequence. Say\nyou have several git commits which are all somehow related. You would\nstart by shelving them (e.g. for a review), something like this:\n\n git p4 submit --origin HEAD^2 --shelve\n\nYou then make changes to these commits (in git) and now need to re-shelve\nthem. Before this change you would need to cherry-pick each change onto\na clean branch and do \"git p4 --update-shelve\", then remove the tip and\nrepeat.\n\nWith this change, you can just do:\n\n git p4 submit --origin HEAD^2 --update-shelve $CL1 --update-shelve $CL2\n\nIf the shelved changelists overlap (one changelist touches the same line\nas another) then this won't work, but that problem already exists with\nthe --shelve option. Solving that is pretty hard to do as P4 really\nonly understands files, not changes. Despite this shortcoming, it's\nvery useful to be able to do update shelved changelists like this.\n\nLuke Diamand (1):\n  git-p4: update multiple shelved change lists\n\n Documentation/git-p4.txt |  8 +++++++-\n git-p4.py                | 41 ++++++++++++++++++++++++++---------------\n t/t9807-git-p4-submit.sh | 24 ++++++++++++++----------\n 3 files changed, 47 insertions(+), 26 deletions(-)\n\n-- \n2.15.0.276.g89ea799.dirty\n\n"},{"id":"335122","messageId":"20171221110614.29300-2-luke@diamand.org","threadId":"47476","inReplyTo":"20171221110614.29300-1-luke@diamand.org","subject":"[PATCH 1/1] git-p4: update multiple shelved change lists","fromName":"Luke Diamand","fromEmail":"luke@diamand.org","sentAt":"2017-12-21T11:06:14Z","receivedAt":"2017-12-21T11:11:58Z","isPatch":true,"sender":{"key":"luke@diamand.org","avatar":"https://avatars.githubusercontent.com/u/5330967?v=4"},"body":"--update-shelve can now be specified multiple times on the\ncommand-line, to update multiple shelved changelists in a single\nsubmit.\n\nThis then means that a git patch series can be mirrored to a\nsequence of shelved changelists, and (relatively easily) kept in\nsync as changes are made in git.\n\nNote that Perforce does not really support overlapping shelved\nchangelists where one change touches the files modified by\nanother. Trying to do this will result in merge conflicts.\n\nSigned-off-by: Luke Diamand <luke@diamand.org>\n---\n Documentation/git-p4.txt |  8 +++++++-\n git-p4.py                | 41 ++++++++++++++++++++++++++---------------\n t/t9807-git-p4-submit.sh | 24 ++++++++++++++----------\n 3 files changed, 47 insertions(+), 26 deletions(-)\n\ndiff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt\nindex 7436c64a9..d8c8f11c9 100644\n--- a/Documentation/git-p4.txt\n+++ b/Documentation/git-p4.txt\n@@ -157,6 +157,12 @@ The p4 changes will be created as the user invoking 'git p4 submit'. The\n according to the author of the Git commit.  This option requires admin\n privileges in p4, which can be granted using 'p4 protect'.\n \n+To shelve changes instead of submitting, use `--shelve` and `--update-shelve`:\n+\n+----\n+$ git p4 submit --shelve\n+$ git p4 submit --update-shelve 1234 --update-shelve 2345\n+----\n \n OPTIONS\n -------\n@@ -310,7 +316,7 @@ These options can be used to modify 'git p4 submit' behavior.\n \n --update-shelve CHANGELIST::\n \tUpdate an existing shelved changelist with this commit. Implies\n-\t--shelve.\n+\t--shelve. Repeat for multiple shelved changelists.\n \n --conflict=(ask|skip|quit)::\n \tConflicts can occur when applying a commit to p4.  When this\ndiff --git a/git-p4.py b/git-p4.py\nindex 76859b453..7bb9cadc6 100755\n--- a/git-p4.py\n+++ b/git-p4.py\n@@ -1178,6 +1178,12 @@ class Command:\n         self.needsGit = True\n         self.verbose = False\n \n+    # This is required for the \"append\" cloneExclude action\n+    def ensure_value(self, attr, value):\n+        if not hasattr(self, attr) or getattr(self, attr) is None:\n+            setattr(self, attr, value)\n+        return getattr(self, attr)\n+\n class P4UserMap:\n     def __init__(self):\n         self.userMapFromPerforceServer = False\n@@ -1343,9 +1349,10 @@ class P4Submit(Command, P4UserMap):\n                 optparse.make_option(\"--shelve\", dest=\"shelve\", action=\"store_true\",\n                                      help=\"Shelve instead of submit. Shelved files are reverted, \"\n                                      \"restoring the workspace to the state before the shelve\"),\n-                optparse.make_option(\"--update-shelve\", dest=\"update_shelve\", action=\"store\", type=\"int\",\n+                optparse.make_option(\"--update-shelve\", dest=\"update_shelve\", action=\"append\", type=\"int\",\n                                      metavar=\"CHANGELIST\",\n-                                     help=\"update an existing shelved changelist, implies --shelve\")\n+                                     help=\"update an existing shelved changelist, implies --shelve, \"\n+                                           \"repeat in-order for multiple shelved changelists\")\n         ]\n         self.description = \"Submit changes from git to the perforce depot.\"\n         self.usage += \" [name of git branch to submit into perforce depot]\"\n@@ -1354,7 +1361,7 @@ class P4Submit(Command, P4UserMap):\n         self.preserveUser = gitConfigBool(\"git-p4.preserveUser\")\n         self.dry_run = False\n         self.shelve = False\n-        self.update_shelve = None\n+        self.update_shelve = list()\n         self.prepare_p4_only = False\n         self.conflict_behavior = None\n         self.isWindows = (platform.system() == \"Windows\")\n@@ -1809,9 +1816,10 @@ class P4Submit(Command, P4UserMap):\n             mode = filesToChangeExecBit[f]\n             setP4ExecBit(f, mode)\n \n-        if self.update_shelve:\n-            print(\"all_files = %s\" % str(all_files))\n-            p4_reopen_in_change(self.update_shelve, all_files)\n+        update_shelve = 0\n+        if len(self.update_shelve) > 0:\n+            update_shelve = self.update_shelve.pop(0)\n+            p4_reopen_in_change(update_shelve, all_files)\n \n         #\n         # Build p4 change description, starting with the contents\n@@ -1821,7 +1829,7 @@ class P4Submit(Command, P4UserMap):\n         logMessage = logMessage.strip()\n         (logMessage, jobs) = self.separate_jobs_from_description(logMessage)\n \n-        template = self.prepareSubmitTemplate(self.update_shelve)\n+        template = self.prepareSubmitTemplate(update_shelve)\n         submitTemplate = self.prepareLogMessage(template, logMessage, jobs)\n \n         if self.preserveUser:\n@@ -1894,7 +1902,7 @@ class P4Submit(Command, P4UserMap):\n                     message = message.replace(\"\\r\\n\", \"\\n\")\n                 submitTemplate = message[:message.index(separatorLine)]\n \n-                if self.update_shelve:\n+                if update_shelve:\n                     p4_write_pipe(['shelve', '-r', '-i'], submitTemplate)\n                 elif self.shelve:\n                     p4_write_pipe(['shelve', '-i'], submitTemplate)\n@@ -2012,6 +2020,10 @@ class P4Submit(Command, P4UserMap):\n         else:\n             return False\n \n+        for i in self.update_shelve:\n+            if i <= 0:\n+                sys.exit(\"invalid changelist %d\" % i)\n+\n         if self.master:\n             allowSubmit = gitConfig(\"git-p4.allowSubmit\")\n             if len(allowSubmit) > 0 and not self.master in allowSubmit.split(\",\"):\n@@ -2022,7 +2034,7 @@ class P4Submit(Command, P4UserMap):\n         if len(self.origin) == 0:\n             self.origin = upstream\n \n-        if self.update_shelve:\n+        if len(self.update_shelve) > 0:\n             self.shelve = True\n \n         if self.preserveUser:\n@@ -2134,6 +2146,11 @@ class P4Submit(Command, P4UserMap):\n         if gitConfigBool(\"git-p4.detectCopiesHarder\"):\n             self.diffOpts += \" --find-copies-harder\"\n \n+        num_shelves = len(self.update_shelve)\n+        if num_shelves > 0 and num_shelves != len(commits):\n+            sys.exit(\"number of commits (%d) must match number of shelved changelist (%d)\" %\n+                     (len(commits), num_shelves))\n+\n         #\n         # Apply the commits, one at a time.  On failure, ask if should\n         # continue to try the rest of the patches, or quit.\n@@ -2404,12 +2421,6 @@ class P4Sync(Command, P4UserMap):\n         if gitConfig(\"git-p4.syncFromOrigin\") == \"false\":\n             self.syncWithOrigin = False\n \n-    # This is required for the \"append\" cloneExclude action\n-    def ensure_value(self, attr, value):\n-        if not hasattr(self, attr) or getattr(self, attr) is None:\n-            setattr(self, attr, value)\n-        return getattr(self, attr)\n-\n     # Force a checkpoint in fast-import and wait for it to finish\n     def checkpoint(self):\n         self.gitStream.write(\"checkpoint\\n\\n\")\ndiff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh\nindex 3457d5db6..71cae2874 100755\n--- a/t/t9807-git-p4-submit.sh\n+++ b/t/t9807-git-p4-submit.sh\n@@ -460,7 +460,13 @@ test_expect_success 'submit --shelve' '\n \t)\n '\n \n-# Update an existing shelved changelist\n+make_shelved_cl() {\n+\ttest_commit \"$1\" >/dev/null &&\n+\tgit p4 submit --origin HEAD^ --shelve >/dev/null &&\n+\tp4 -G changes -s shelved -m 1 | marshal_dump change\n+}\n+\n+# Update existing shelved changelists\n \n test_expect_success 'submit --update-shelve' '\n \ttest_when_finished cleanup_git &&\n@@ -470,21 +476,19 @@ test_expect_success 'submit --update-shelve' '\n \t\tp4 revert ... &&\n \t\tcd \"$git\" &&\n \t\tgit config git-p4.skipSubmitEdit true &&\n-\t\ttest_commit \"test-update-shelved-change\" &&\n-\t\tgit p4 submit --origin=HEAD^ --shelve &&\n+\t\tshelved_cl0=$(make_shelved_cl \"shelved-change-0\") &&\n+\t\techo shelved_cl0=$shelved_cl0 &&\n+\t\tshelved_cl1=$(make_shelved_cl \"shelved-change-1\") &&\n \n-\t\tshelf_cl=$(p4 -G changes -s shelved -m 1 |\\\n-\t\t\tmarshal_dump change) &&\n-\t\ttest -n $shelf_cl &&\n-\t\techo \"updating shelved change list $shelf_cl\" &&\n+\t\techo \"updating shelved change lists $shelved_cl0 and $shelved_cl1\" &&\n \n \t\techo \"updated-line\" >>shelf.t &&\n \t\techo added-file.t >added-file.t &&\n \t\tgit add shelf.t added-file.t &&\n-\t\tgit rm -f test-update-shelved-change.t &&\n+\t\tgit rm -f shelved-change-1.t &&\n \t\tgit commit --amend -C HEAD &&\n \t\tgit show --stat HEAD &&\n-\t\tgit p4 submit -v --origin HEAD^ --update-shelve $shelf_cl &&\n+\t\tgit p4 submit -v --origin HEAD~2 --update-shelve $shelved_cl0 --update-shelve $shelved_cl1 &&\n \t\techo \"done git p4 submit\"\n \t) &&\n \t(\n@@ -494,7 +498,7 @@ test_expect_success 'submit --update-shelve' '\n \t\tp4 unshelve -c $change -s $change &&\n \t\tgrep -q updated-line shelf.t &&\n \t\tp4 describe -S $change | grep added-file.t &&\n-\t\ttest_path_is_missing test-update-shelved-change.t\n+\t\ttest_path_is_missing shelved-change-1.t\n \t)\n '\n \n-- \n2.15.0.276.g89ea799.dirty\n\n"}]}