{"thread":{"id":"10581","subject":"[PATCH] git-p4: Add a helper function to parse the full git diff-tree output.","startedAt":"2007-11-02T03:43:13Z","lastAt":"2007-11-02T03:43:14Z","messageCount":2,"participants":["Chris Pettitt"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"57919","messageId":"1193974994-19211-1-git-send-email-cpettitt@gmail.com","threadId":"10581","inReplyTo":null,"subject":"[PATCH] git-p4: Add a helper function to parse the full git diff-tree output.","fromName":"Chris Pettitt","fromEmail":"cpettitt@gmail.com","sentAt":"2007-11-02T03:43:13Z","receivedAt":"2007-11-02T03:43:13Z","isPatch":true,"sender":{"key":"cpettitt@gmail.com","avatar":null},"body":"\nSigned-off-by: Chris Pettitt <cpettitt@gmail.com>\n---\n contrib/fast-import/git-p4 |   49 ++++++++++++++++++++++++++++++++++++++++---\n 1 files changed, 45 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex bf33f74..c7fc564 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -71,6 +71,46 @@ def isP4Exec(kind):\n     a plus sign, it is also executable\"\"\"\n     return (re.search(r\"(^[cku]?x)|\\+.*x\", kind) != None)\n \n+def diffTreePattern():\n+    # This is a simple generator for the diff tree regex pattern. This could be\n+    # a class variable if this and parseDiffTreeEntry were a part of a class.\n+    pattern = re.compile(':(\\d+) (\\d+) (\\w+) (\\w+) ([A-Z])(\\d+)?\\t(.*?)((\\t(.*))|$)')\n+    while True:\n+        yield pattern\n+\n+def parseDiffTreeEntry(entry):\n+    \"\"\"Parses a single diff tree entry into its component elements.\n+\n+    See git-diff-tree(1) manpage for details about the format of the diff\n+    output. This method returns a dictionary with the following elements:\n+\n+    src_mode - The mode of the source file\n+    dst_mode - The mode of the destination file\n+    src_sha1 - The sha1 for the source file\n+    dst_sha1 - The sha1 fr the destination file\n+    status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc)\n+    status_score - The score for the status (applicable for 'C' and 'R'\n+                   statuses). This is None if there is no score.\n+    src - The path for the source file.\n+    dst - The path for the destination file. This is only present for\n+          copy or renames. If it is not present, this is None.\n+\n+    If the pattern is not matched, None is returned.\"\"\"\n+\n+    match = diffTreePattern().next().match(entry)\n+    if match:\n+        return {\n+            'src_mode': match.group(1),\n+            'dst_mode': match.group(2),\n+            'src_sha1': match.group(3),\n+            'dst_sha1': match.group(4),\n+            'status': match.group(5),\n+            'status_score': match.group(6),\n+            'src': match.group(7),\n+            'dst': match.group(10)\n+        }\n+    return None\n+\n def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):\n     cmd = \"p4 -G %s\" % cmd\n     if verbose:\n@@ -494,13 +534,14 @@ class P4Submit(Command):\n         else:\n             print \"Applying %s\" % (read_pipe(\"git log --max-count=1 --pretty=oneline %s\" % id))\n             diffOpts = (\"\", \"-M\")[self.detectRename]\n-            diff = read_pipe_lines(\"git diff-tree -r --name-status %s \\\"%s^\\\" \\\"%s\\\"\" % (diffOpts, id, id))\n+            diff = read_pipe_lines(\"git diff-tree -r %s \\\"%s^\\\" \\\"%s\\\"\" % (diffOpts, id, id))\n         filesToAdd = set()\n         filesToDelete = set()\n         editedFiles = set()\n         for line in diff:\n-            modifier = line[0]\n-            path = line[1:].strip()\n+            diff = parseDiffTreeEntry(line)\n+            modifier = diff['status']\n+            path = diff['src']\n             if modifier == \"M\":\n                 system(\"p4 edit \\\"%s\\\"\" % path)\n                 editedFiles.add(path)\n@@ -513,7 +554,7 @@ class P4Submit(Command):\n                 if path in filesToAdd:\n                     filesToAdd.remove(path)\n             elif modifier == \"R\":\n-                src, dest = line.strip().split(\"\\t\")[1:3]\n+                src, dest = diff['src'], diff['dst']\n                 system(\"p4 integrate -Dt \\\"%s\\\" \\\"%s\\\"\" % (src, dest))\n                 system(\"p4 edit \\\"%s\\\"\" % (dest))\n                 os.unlink(dest)\n-- \n1.5.3.4.498.g9c514\n"},{"id":"57920","messageId":"1193974994-19211-2-git-send-email-cpettitt@gmail.com","threadId":"10581","inReplyTo":"1193974994-19211-1-git-send-email-cpettitt@gmail.com","subject":"[PATCH] git-p4: Detect changes to executable bit and include them in p4 submit.","fromName":"Chris Pettitt","fromEmail":"cpettitt@gmail.com","sentAt":"2007-11-02T03:43:14Z","receivedAt":"2007-11-02T03:43:14Z","isPatch":true,"sender":{"key":"cpettitt@gmail.com","avatar":null},"body":"This changeset takes advantage of the new parseDiffTreeEntry(...) function to\ndetect changes to the execute bit in the git repository.  During submit, git-p4\nnow looks for changes to the executable bit and if it finds them it \"reopens\"\nthe file in perforce, which allows it to change the file type.\n\nThe logic for adding the executable bit in perforce is straightforward: the +x\nmodifier can be used. Removing the executable bit in perforce requires that the\nentire filetype be redefined (there is no way to join remove the bit with a -x\nmodifier, for example). This changeset includes logic to remove the executable\nbit from the full file type while preserving the base file type and other\nmodifiers.\n\nSigned-off-by: Chris Pettitt <cpettitt@gmail.com>\n---\n contrib/fast-import/git-p4 |   44 ++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 44 insertions(+), 0 deletions(-)\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex c7fc564..c148b5a 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -71,6 +71,31 @@ def isP4Exec(kind):\n     a plus sign, it is also executable\"\"\"\n     return (re.search(r\"(^[cku]?x)|\\+.*x\", kind) != None)\n \n+def setP4ExecBit(file, mode):\n+    # Reopens an already open file and changes the execute bit to match\n+    # the execute bit setting in the passed in mode.\n+\n+    p4Type = \"+x\"\n+\n+    if not isModeExec(mode):\n+        p4Type = getP4OpenedType(file)\n+        p4Type = re.sub('^([cku]?)x(.*)', '\\\\1\\\\2', p4Type)\n+        p4Type = re.sub('(.*?\\+.*?)x(.*?)', '\\\\1\\\\2', p4Type)\n+        if p4Type[-1] == \"+\":\n+            p4Type = p4Type[0:-1]\n+\n+    system(\"p4 reopen -t %s %s\" % (p4Type, file))\n+\n+def getP4OpenedType(file):\n+    # Returns the perforce file type for the given file.\n+\n+    result = read_pipe(\"p4 opened %s\" % file)\n+    match = re.match(\".*\\((.+)\\)$\", result)\n+    if match:\n+        return match.group(1)\n+    else:\n+        die(\"Could not determine file type for %s\" % file)\n+\n def diffTreePattern():\n     # This is a simple generator for the diff tree regex pattern. This could be\n     # a class variable if this and parseDiffTreeEntry were a part of a class.\n@@ -111,6 +136,14 @@ def parseDiffTreeEntry(entry):\n         }\n     return None\n \n+def isModeExec(mode):\n+    # Returns True if the given git mode represents an executable file,\n+    # otherwise False.\n+    return mode[-3:] == \"755\"\n+\n+def isModeExecChanged(src_mode, dst_mode):\n+    return isModeExec(src_mode) != isModeExec(dst_mode)\n+\n def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):\n     cmd = \"p4 -G %s\" % cmd\n     if verbose:\n@@ -538,15 +571,19 @@ class P4Submit(Command):\n         filesToAdd = set()\n         filesToDelete = set()\n         editedFiles = set()\n+        filesToChangeExecBit = {}\n         for line in diff:\n             diff = parseDiffTreeEntry(line)\n             modifier = diff['status']\n             path = diff['src']\n             if modifier == \"M\":\n                 system(\"p4 edit \\\"%s\\\"\" % path)\n+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):\n+                    filesToChangeExecBit[path] = diff['dst_mode']\n                 editedFiles.add(path)\n             elif modifier == \"A\":\n                 filesToAdd.add(path)\n+                filesToChangeExecBit[path] = diff['dst_mode']\n                 if path in filesToDelete:\n                     filesToDelete.remove(path)\n             elif modifier == \"D\":\n@@ -557,6 +594,8 @@ class P4Submit(Command):\n                 src, dest = diff['src'], diff['dst']\n                 system(\"p4 integrate -Dt \\\"%s\\\" \\\"%s\\\"\" % (src, dest))\n                 system(\"p4 edit \\\"%s\\\"\" % (dest))\n+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):\n+                    filesToChangeExecBit[dest] = diff['dst_mode']\n                 os.unlink(dest)\n                 editedFiles.add(dest)\n                 filesToDelete.add(src)\n@@ -609,6 +648,11 @@ class P4Submit(Command):\n             system(\"p4 revert \\\"%s\\\"\" % f)\n             system(\"p4 delete \\\"%s\\\"\" % f)\n \n+        # Set/clear executable bits\n+        for f in filesToChangeExecBit.keys():\n+            mode = filesToChangeExecBit[f]\n+            setP4ExecBit(f, mode)\n+\n         logMessage = \"\"\n         if not self.directSubmit:\n             logMessage = extractLogMessageFromGitCommit(id)\n-- \n1.5.3.4.498.g9c514\n"}]}