get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/203517/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 203517,
    "url": "http://patchwork.ozlabs.org/api/patches/203517/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/uboot/patch/1354581799-775-3-git-send-email-dianders@chromium.org/",
    "project": {
        "id": 18,
        "url": "http://patchwork.ozlabs.org/api/projects/18/?format=api",
        "name": "U-Boot",
        "link_name": "uboot",
        "list_id": "u-boot.lists.denx.de",
        "list_email": "u-boot@lists.denx.de",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<1354581799-775-3-git-send-email-dianders@chromium.org>",
    "list_archive_url": null,
    "date": "2012-12-04T00:43:18",
    "name": "[U-Boot,v2,3/4] patman: Add the concept of multiple projects",
    "commit_ref": "a1dcee84c993232a6c5a1f3b4e54952b587cf1d1",
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "85f930e647817c1b5891d6aa7de20b22d93d3ac3",
    "submitter": {
        "id": 9763,
        "url": "http://patchwork.ozlabs.org/api/people/9763/?format=api",
        "name": "Douglas Anderson",
        "email": "dianders@chromium.org"
    },
    "delegate": {
        "id": 3184,
        "url": "http://patchwork.ozlabs.org/api/users/3184/?format=api",
        "username": "sjg",
        "first_name": "Simon",
        "last_name": "Glass",
        "email": "sjg@chromium.org"
    },
    "mbox": "http://patchwork.ozlabs.org/project/uboot/patch/1354581799-775-3-git-send-email-dianders@chromium.org/mbox/",
    "series": [],
    "comments": "http://patchwork.ozlabs.org/api/patches/203517/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/203517/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<u-boot-bounces@lists.denx.de>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org",
        "Received": [
            "from theia.denx.de (theia.denx.de [85.214.87.163])\n\tby ozlabs.org (Postfix) with ESMTP id A37BE2C008F\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue,  4 Dec 2012 11:43:35 +1100 (EST)",
            "from localhost (localhost [127.0.0.1])\n\tby theia.denx.de (Postfix) with ESMTP id 28DAB4A170;\n\tTue,  4 Dec 2012 01:43:34 +0100 (CET)",
            "from theia.denx.de ([127.0.0.1])\n\tby localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id EObkAcZq4SxP; Tue,  4 Dec 2012 01:43:33 +0100 (CET)",
            "from theia.denx.de (localhost [127.0.0.1])\n\tby theia.denx.de (Postfix) with ESMTP id C43344A152;\n\tTue,  4 Dec 2012 01:43:30 +0100 (CET)",
            "from localhost (localhost [127.0.0.1])\n\tby theia.denx.de (Postfix) with ESMTP id 3DC834A152\n\tfor <u-boot@lists.denx.de>; Tue,  4 Dec 2012 01:43:28 +0100 (CET)",
            "from theia.denx.de ([127.0.0.1])\n\tby localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id sLPWSE9Lo63a for <u-boot@lists.denx.de>;\n\tTue,  4 Dec 2012 01:43:27 +0100 (CET)",
            "from mail-wg0-f74.google.com (mail-wg0-f74.google.com\n\t[74.125.82.74])\n\tby theia.denx.de (Postfix) with ESMTPS id 02B304A132\n\tfor <u-boot@lists.denx.de>; Tue,  4 Dec 2012 01:43:25 +0100 (CET)",
            "by mail-wg0-f74.google.com with SMTP id dt14so407281wgb.3\n\tfor <u-boot@lists.denx.de>; Mon, 03 Dec 2012 16:43:25 -0800 (PST)",
            "by 10.14.216.197 with SMTP id g45mr12253000eep.3.1354581805274;\n\tMon, 03 Dec 2012 16:43:25 -0800 (PST)",
            "from hpza10.eem.corp.google.com ([74.125.121.33])\n\tby gmr-mx.google.com with ESMTPS id\n\tu8si3560874een.1.2012.12.03.16.43.25\n\t(version=TLSv1/SSLv3 cipher=AES128-SHA);\n\tMon, 03 Dec 2012 16:43:25 -0800 (PST)",
            "from tictac.mtv.corp.google.com (tictac.mtv.corp.google.com\n\t[172.22.73.80])\n\tby hpza10.eem.corp.google.com (Postfix) with ESMTP id B049A20005C;\n\tMon,  3 Dec 2012 16:43:24 -0800 (PST)",
            "by tictac.mtv.corp.google.com (Postfix, from userid 121310)\n\tid 10E2C819C4; Mon,  3 Dec 2012 16:43:23 -0800 (PST)"
        ],
        "X-Virus-Scanned": [
            "Debian amavisd-new at theia.denx.de",
            "Debian amavisd-new at theia.denx.de"
        ],
        "X-policyd-weight": "NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5\n\tNOT_IN_BL_NJABL=-1.5 (only DNSBL check requested)",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=google.com; s=20120113;\n\th=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references\n\t:x-gm-message-state;\n\tbh=Jhge/7jnwZmPPv+MSEH0yAqfyM390LZMgF5WDqgLIRM=;\n\tb=EHmd7Jqw6yln05fLqgTqaVncmFVo0wNbkASVt75OeeEtGJx7jaHG10VQucEA4O3j1F\n\tR+xBUJwJK0oiLtBUT9VSWbiVkg1Ny2hVzIxX+DMWtbg/1SgEUIEzvciKrLGy+VpFpIL1\n\t5S6+yyBFU1oXeow0N7EmNAfq9JgKNAj5coob+eZXyIr7qflRfTCq2e6iTYoMsAI4AP0x\n\tjfi6UGw7UnC10mRBMTWL2Y5ido74yFQJaQfUawJ+03k8utriqT9fDgllG6klBLOhui+/\n\tstTTljOJW5shcZyvPn4fq1S7qDxirg9TGF0aGSRqwDSCA8ezk/Eu2nEwFrcqRzjcwHJK\n\tzv2Q==",
        "From": "Doug Anderson <dianders@chromium.org>",
        "To": "Simon Glass <sjg@chromium.org>",
        "Date": "Mon,  3 Dec 2012 16:43:18 -0800",
        "Message-Id": "<1354581799-775-3-git-send-email-dianders@chromium.org>",
        "X-Mailer": "git-send-email 1.7.7.3",
        "In-Reply-To": "<1354581799-775-1-git-send-email-dianders@chromium.org>",
        "References": "<1354321745-1359-1-git-send-email-dianders@chromium.org>\n\t<1354581799-775-1-git-send-email-dianders@chromium.org>",
        "X-Gm-Message-State": "ALoCoQkfqW5ZJz/2wjlDaM5B3kV0aVSUASHpjexvv8/qutAFvvNwebfILawJk8NcSD89+FuiKfhDdZyAXeMZgXg/PV3lOPa97G89rKu2ROIYJ7jOsXXAmRi0O8/GPXSFlHQPqeb9Uh/v67U2+E8ErstnYup8GSH+q7j3PTw3c2RljbYCPrmTDpt8CG0SZWQZJrXMcVGjPWMY",
        "Cc": "u-boot@lists.denx.de",
        "Subject": "[U-Boot] [PATCH v2 3/4] patman: Add the concept of multiple projects",
        "X-BeenThere": "u-boot@lists.denx.de",
        "X-Mailman-Version": "2.1.11",
        "Precedence": "list",
        "List-Id": "U-Boot discussion <u-boot.lists.denx.de>",
        "List-Unsubscribe": "<http://lists.denx.de/mailman/options/u-boot>,\n\t<mailto:u-boot-request@lists.denx.de?subject=unsubscribe>",
        "List-Archive": "<http://lists.denx.de/pipermail/u-boot>",
        "List-Post": "<mailto:u-boot@lists.denx.de>",
        "List-Help": "<mailto:u-boot-request@lists.denx.de?subject=help>",
        "List-Subscribe": "<http://lists.denx.de/mailman/listinfo/u-boot>,\n\t<mailto:u-boot-request@lists.denx.de?subject=subscribe>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Sender": "u-boot-bounces@lists.denx.de",
        "Errors-To": "u-boot-bounces@lists.denx.de"
    },
    "content": "There are cases that we want to support different settings (or maybe\neven different aliases) for different projects.  Add support for this\nby:\n* Adding detection for two big projects: U-Boot and Linux.\n* Adding default settings for Linux (U-Boot is already good with the\n  standard patman defaults).\n* Extend the new \"settings\" feature in .patman to specify per-project\n  settings.\n\nSigned-off-by: Doug Anderson <dianders@chromium.org>\n---\nChanges in v2:\n- Added requested short option: '-p'.\n\n tools/patman/README      |   13 ++++\n tools/patman/patman.py   |    9 +++-\n tools/patman/project.py  |   43 +++++++++++++\n tools/patman/settings.py |  147 +++++++++++++++++++++++++++++++++++++++++++++-\n 4 files changed, 208 insertions(+), 4 deletions(-)\n create mode 100644 tools/patman/project.py",
    "diff": "diff --git a/tools/patman/README b/tools/patman/README\nindex 2743da9..1832ebd 100644\n--- a/tools/patman/README\n+++ b/tools/patman/README\n@@ -114,6 +114,19 @@ verbose: True\n <<<\n \n \n+If you want to adjust settings (or aliases) that affect just a single\n+project you can add a section that looks like [project_settings] or\n+[project_alias].  If you want to use tags for your linux work, you could\n+do:\n+\n+>>>\n+\n+[linux_settings]\n+process_tags: True\n+\n+<<<\n+\n+\n How to run it\n =============\n \ndiff --git a/tools/patman/patman.py b/tools/patman/patman.py\nindex b327c67..2e9e5dc 100755\n--- a/tools/patman/patman.py\n+++ b/tools/patman/patman.py\n@@ -34,6 +34,7 @@ import checkpatch\n import command\n import gitutil\n import patchstream\n+import project\n import settings\n import terminal\n import test\n@@ -59,6 +60,9 @@ parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store',\n        default=None, help='Output cc list for patch file (used by git)')\n parser.add_option('--no-tags', action='store_false', dest='process_tags',\n                   default=True, help=\"Don't process subject tags as aliaes\")\n+parser.add_option('-p', '--project', default=project.DetectProject(),\n+                  help=\"Project name; affects default option values and \"\n+                  \"aliases [default: %default]\")\n \n parser.usage = \"\"\"patman [options]\n \n@@ -66,7 +70,10 @@ Create patches from commits in a branch, check them and email them as\n specified by tags you place in the commits. Use -n to \"\"\"\n \n \n-settings.Setup(parser, '')\n+# Parse options twice: first to get the project and second to handle\n+# defaults properly (which depends on project).\n+(options, args) = parser.parse_args()\n+settings.Setup(parser, options.project, '')\n (options, args) = parser.parse_args()\n \n # Run our meagre tests\ndiff --git a/tools/patman/project.py b/tools/patman/project.py\nnew file mode 100644\nindex 0000000..4f7b2b3\n--- /dev/null\n+++ b/tools/patman/project.py\n@@ -0,0 +1,43 @@\n+# Copyright (c) 2012 The Chromium OS Authors.\n+#\n+# See file CREDITS for list of people who contributed to this\n+# project.\n+#\n+# This program is free software; you can redistribute it and/or\n+# modify it under the terms of the GNU General Public License as\n+# published by the Free Software Foundation; either version 2 of\n+# the License, or (at your option) any later version.\n+#\n+# This program is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with this program; if not, write to the Free Software\n+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,\n+# MA 02111-1307 USA\n+#\n+\n+import os.path\n+\n+import gitutil\n+\n+def DetectProject():\n+    \"\"\"Autodetect the name of the current project.\n+\n+    This looks for signature files/directories that are unlikely to exist except\n+    in the given project.\n+\n+    Returns:\n+        The name of the project, like \"linux\" or \"u-boot\".  Returns \"unknown\"\n+        if we can't detect the project.\n+    \"\"\"\n+    top_level = gitutil.GetTopLevel()\n+\n+    if os.path.exists(os.path.join(top_level, \"include\", \"u-boot\")):\n+        return \"u-boot\"\n+    elif os.path.exists(os.path.join(top_level, \"kernel\")):\n+        return \"linux\"\n+\n+    return \"unknown\"\ndiff --git a/tools/patman/settings.py b/tools/patman/settings.py\nindex 5208f7d..084d1b8 100644\n--- a/tools/patman/settings.py\n+++ b/tools/patman/settings.py\n@@ -26,6 +26,140 @@ import re\n import command\n import gitutil\n \n+\"\"\"Default settings per-project.\n+\n+These are used by _ProjectConfigParser.  Settings names should match\n+the \"dest\" of the option parser from patman.py.\n+\"\"\"\n+_default_settings = {\n+    \"u-boot\": {},\n+    \"linux\": {\n+        \"process_tags\": \"False\",\n+    }\n+}\n+\n+class _ProjectConfigParser(ConfigParser.SafeConfigParser):\n+    \"\"\"ConfigParser that handles projects.\n+\n+    There are two main goals of this class:\n+    - Load project-specific default settings.\n+    - Merge general default settings/aliases with project-specific ones.\n+\n+    # Sample config used for tests below...\n+    >>> import StringIO\n+    >>> sample_config = '''\n+    ... [alias]\n+    ... me: Peter P. <likesspiders@example.com>\n+    ... enemies: Evil <evil@example.com>\n+    ...\n+    ... [sm_alias]\n+    ... enemies: Green G. <ugly@example.com>\n+    ...\n+    ... [sm2_alias]\n+    ... enemies: Doc O. <pus@example.com>\n+    ...\n+    ... [settings]\n+    ... am_hero: True\n+    ... '''\n+\n+    # Check to make sure that bogus project gets general alias.\n+    >>> config = _ProjectConfigParser(\"zzz\")\n+    >>> config.readfp(StringIO.StringIO(sample_config))\n+    >>> config.get(\"alias\", \"enemies\")\n+    'Evil <evil@example.com>'\n+\n+    # Check to make sure that alias gets overridden by project.\n+    >>> config = _ProjectConfigParser(\"sm\")\n+    >>> config.readfp(StringIO.StringIO(sample_config))\n+    >>> config.get(\"alias\", \"enemies\")\n+    'Green G. <ugly@example.com>'\n+\n+    # Check to make sure that settings get merged with project.\n+    >>> config = _ProjectConfigParser(\"linux\")\n+    >>> config.readfp(StringIO.StringIO(sample_config))\n+    >>> sorted(config.items(\"settings\"))\n+    [('am_hero', 'True'), ('process_tags', 'False')]\n+\n+    # Check to make sure that settings works with unknown project.\n+    >>> config = _ProjectConfigParser(\"unknown\")\n+    >>> config.readfp(StringIO.StringIO(sample_config))\n+    >>> sorted(config.items(\"settings\"))\n+    [('am_hero', 'True')]\n+    \"\"\"\n+    def __init__(self, project_name):\n+        \"\"\"Construct _ProjectConfigParser.\n+\n+        In addition to standard SafeConfigParser initialization, this also loads\n+        project defaults.\n+\n+        Args:\n+            project_name: The name of the project.\n+        \"\"\"\n+        self._project_name = project_name\n+        ConfigParser.SafeConfigParser.__init__(self)\n+\n+        # Update the project settings in the config based on\n+        # the _default_settings global.\n+        project_settings = \"%s_settings\" % project_name\n+        if not self.has_section(project_settings):\n+            self.add_section(project_settings)\n+        project_defaults = _default_settings.get(project_name, {})\n+        for setting_name, setting_value in project_defaults.iteritems():\n+            self.set(project_settings, setting_name, setting_value)\n+\n+    def get(self, section, option, *args, **kwargs):\n+        \"\"\"Extend SafeConfigParser to try project_section before section.\n+\n+        Args:\n+            See SafeConfigParser.\n+        Returns:\n+            See SafeConfigParser.\n+        \"\"\"\n+        try:\n+            return ConfigParser.SafeConfigParser.get(\n+                self, \"%s_%s\" % (self._project_name, section), option,\n+                *args, **kwargs\n+            )\n+        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):\n+            return ConfigParser.SafeConfigParser.get(\n+                self, section, option, *args, **kwargs\n+            )\n+\n+    def items(self, section, *args, **kwargs):\n+        \"\"\"Extend SafeConfigParser to add project_section to section.\n+\n+        Args:\n+            See SafeConfigParser.\n+        Returns:\n+            See SafeConfigParser.\n+        \"\"\"\n+        project_items = []\n+        has_project_section = False\n+        top_items = []\n+\n+        # Get items from the project section\n+        try:\n+            project_items = ConfigParser.SafeConfigParser.items(\n+                self, \"%s_%s\" % (self._project_name, section), *args, **kwargs\n+            )\n+            has_project_section = True\n+        except ConfigParser.NoSectionError:\n+            pass\n+\n+        # Get top-level items\n+        try:\n+            top_items = ConfigParser.SafeConfigParser.items(\n+                self, section, *args, **kwargs\n+            )\n+        except ConfigParser.NoSectionError:\n+            # If neither section exists raise the error on...\n+            if not has_project_section:\n+                raise\n+\n+        item_dict = dict(top_items)\n+        item_dict.update(project_items)\n+        return item_dict.items()\n+\n def ReadGitAliases(fname):\n     \"\"\"Read a git alias file. This is in the form used by git:\n \n@@ -102,7 +236,7 @@ def _UpdateDefaults(parser, config):\n     Args:\n         parser: An instance of an OptionParser whose defaults will be\n             updated.\n-        config: An instance of SafeConfigParser that we will query\n+        config: An instance of _ProjectConfigParser that we will query\n             for settings.\n     \"\"\"\n     defaults = parser.get_default_values()\n@@ -117,14 +251,16 @@ def _UpdateDefaults(parser, config):\n         else:\n             print \"WARNING: Unknown setting %s\" % name\n \n-def Setup(parser, config_fname=''):\n+def Setup(parser, project_name, config_fname=''):\n     \"\"\"Set up the settings module by reading config files.\n \n     Args:\n         parser:         The parser to update\n+        project_name:   Name of project that we're working on; we'll look\n+            for sections named \"project_section\" as well.\n         config_fname:   Config filename to read ('' for default)\n     \"\"\"\n-    config = ConfigParser.SafeConfigParser()\n+    config = _ProjectConfigParser(project_name)\n     if config_fname == '':\n         config_fname = '%s/.patman' % os.getenv('HOME')\n \n@@ -141,3 +277,8 @@ def Setup(parser, config_fname=''):\n \n # These are the aliases we understand, indexed by alias. Each member is a list.\n alias = {}\n+\n+if __name__ == \"__main__\":\n+    import doctest\n+\n+    doctest.testmod()\n",
    "prefixes": [
        "U-Boot",
        "v2",
        "3/4"
    ]
}