{"action":"created","comment":{"url":"https://api.github.com/repos/opf/openproject/pulls/comments/33331989","id":33331989,"diff_hunk":"@@ -0,0 +1,289 @@\n+#-- encoding: UTF-8\n+#-- copyright\n+# OpenProject is a project management system.\n+# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)\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 version 3.\n+#\n+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:\n+# Copyright (C) 2006-2013 Jean-Philippe Lang\n+# Copyright (C) 2010-2013 the ChiliProject Team\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\n+# as published by the Free Software Foundation; either version 2\n+# of 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+#\n+# See doc/COPYRIGHT.rdoc for more details.\n+#++\n+\n+require 'uri'\n+\n+module OpenProject\n+  module Scm\n+    module Adapters\n+      class Subversion < Base\n+        include LocalClient\n+\n+        # SVN executable name\n+        SVN_BIN = OpenProject::Configuration['scm_subversion_command'] || 'svn'\n+\n+        def client_command\n+          SVN_BIN\n+        end\n+\n+        def sq_bin\n+          @sq_bin ||= shell_quote(SVN_BIN)\n+        end\n+\n+        def client_version\n+          @client_version ||= (svn_binary_version || [])\n+        end\n+\n+        def svn_binary_version\n+          scm_version = scm_version_from_command_line.dup\n+          m = scm_version.match(%r{\\A(.*?)((\\d+\\.)+\\d+)})\n+          if m\n+            m[2].scan(%r{\\d+}).map(&:to_i)\n+          end\n+        end\n+\n+        def scm_version_from_command_line\n+          capture_out('--version')\n+        end\n+\n+        def initialize(url, root_url = nil, login = nil, password = nil, _path_encoding = nil)\n+          super(url, root_url)\n+          @login = login\n+          @password = password\n+        end\n+\n+        # Get info about the svn repository\n+        def info\n+          cmd = build_svn_cmd(['info', '--xml', target])\n+          xml_capture(cmd, force_encoding: true) do |doc|\n+            Info.new(\n+              root_url: doc.xpath('/info/entry/repository/root').text,\n+              lastrev: extract_revision(doc.at_xpath('/info/entry/commit'))\n+            )\n+          end\n+        end\n+\n+        def entries(path = nil, identifier = nil)\n+          path ||= ''\n+          identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : 'HEAD'\n+          entries = Entries.new\n+          cmd = ['list', '--xml', \"#{target(path)}@#{identifier}\"]\n+          xml_capture(cmd, force_encoding: true) do |doc|\n+            doc.xpath('/lists/list/entry').each { |list| entries << extract_entry(list, path) }\n+          end\n+          entries.sort_by_name\n+        end\n+\n+        def properties(path, identifier = nil)\n+          # proplist xml output supported in svn 1.5.0 and higher\n+          return nil unless client_version_above?([1, 5, 0])\n+\n+          identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : 'HEAD'\n+          cmd = ['proplist', '--verbose', '--xml', \"#{target(path)}@#{identifier}\"]\n+          properties = {}\n+          xml_capture(cmd, force_encoding: true) do |doc|\n+            doc.xpath('/properties/target/property').each do |prop|\n+              properties[prop['name']] = prop.text\n+            end\n+          end\n+\n+          properties\n+        end\n+\n+        def revisions(path = nil, identifier_from = nil, identifier_to = nil, options = {})\n+          revisions = Revisions.new\n+          fetch_revision_entries(identifier_from, identifier_to, options, path) do |logentry|\n+            paths = logentry.xpath('paths/path').map { |entry| build_path(entry) }\n+            paths.sort! { |x, y| x[:path] <=> y[:path] }\n+\n+            r = extract_revision(logentry)\n+            r.paths = paths\n+\n+            revisions << r\n+          end\n+          revisions\n+        end\n+\n+        def diff(path, identifier_from, identifier_to = nil, _type = 'inline')\n+          path ||= ''\n+\n+          identifier_from = numeric_identifier(identifier_from)\n+          identifier_to = numeric_identifier(identifier_to, identifier_from - 1)\n+\n+          cmd = ['diff', '-r', \"#{identifier_to}:#{identifier_from}\",\n+                 \"#{target(path)}@#{identifier_from}\"]\n+          capture_svn(cmd).lines.map(&:chomp)\n+        end\n+\n+        def numeric_identifier(identifier, default = '')\n+          if identifier && identifier.to_i > 0\n+            identifier.to_i\n+          else\n+            default\n+          end\n+        end\n+\n+        def cat(path, identifier = nil)\n+          identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : 'HEAD'\n+          cmd = ['cat', \"#{target(path)}@#{identifier}\"]\n+          capture_svn(cmd)\n+        end\n+\n+        def annotate(path, identifier = nil)\n+          identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : 'HEAD'\n+          cmd = ['blame', \"#{target(path)}@#{identifier}\"]\n+          blame = Annotate.new\n+          popen3(cmd) do |io|\n+            io.each_line do |line|\n+              next unless line =~ %r{^\\s*(\\d+)\\s*(\\S+)\\s(.*)$}\n+              blame.add_line($3.rstrip, Revision.new(identifier: $1.to_i, author: $2.strip))\n+            end\n+          end\n+          blame\n+        end\n+\n+        private\n+\n+        ##\n+        # Builds the SVM command arguments around the given parameters\n+        # Appends to the parameter:\n+        # --username, --password    if specified for this repository\n+        # --no-auth-cache           force re-authentication\n+        # --non-interactive         avoid prompts\n+        def build_svn_cmd(args)\n+          if @login.present?\n+            args.push('--username', shell_quote(@login))\n+            args.push('--password', shell_quote(@password)) if @password.present?\n+          end\n+\n+          args.push('--no-auth-cache', '--non-interactive')\n+        end\n+\n+        def xml_capture(cmd, opts = {})\n+          output = capture_svn(cmd, opts)\n+          doc = Nokogiri::XML(output)\n+\n+          # Yield helper methods instead of doc\n+          yield doc\n+        end\n+\n+        def extract_entry(entry, path)\n+          revision = extract_revision(entry.at_xpath('commit'))\n+          kind, size, name = parse_entry(entry)\n+\n+          # Skip directory if there is no commit date (usually that\n+          # means that we don't have read access to it)\n+          return if kind == 'dir' && revision.time.nil?\n+\n+          Entry.new(\n+            name: URI.unescape(name),\n+            path: ((path.empty? ? '' : \"#{path}/\") + name),\n+            kind: kind,\n+            size: size.nil? ? nil : size.to_i,\n+            lastrev: revision\n+          )\n+        end\n+\n+        def parse_entry(entry)\n+          kind = entry['kind']\n+          size = entry.xpath('size').text\n+          name = entry.xpath('name').text\n+\n+          [kind, size, name]\n+        end\n+\n+        def build_path(entry)\n+          {\n+            action: entry['action'],\n+            path: entry.text,\n+            from_path: entry['copyfrom-path'],\n+            from_revision: entry['copyfrom-rev']","path":"lib/open_project/scm/adapters/subversion.rb","position":216,"original_position":216,"commit_id":"2149109b6fe22e512495af208f4fb6842490c125","original_commit_id":"2149109b6fe22e512495af208f4fb6842490c125","user":{"login":"houndci","id":6697940,"avatar_url":"https://avatars.githubusercontent.com/u/6697940?v=3","gravatar_id":"","url":"https://api.github.com/users/houndci","html_url":"https://github.com/houndci","followers_url":"https://api.github.com/users/houndci/followers","following_url":"https://api.github.com/users/houndci/following{/other_user}","gists_url":"https://api.github.com/users/houndci/gists{/gist_id}","starred_url":"https://api.github.com/users/houndci/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/houndci/subscriptions","organizations_url":"https://api.github.com/users/houndci/orgs","repos_url":"https://api.github.com/users/houndci/repos","events_url":"https://api.github.com/users/houndci/events{/privacy}","received_events_url":"https://api.github.com/users/houndci/received_events","type":"User","site_admin":false},"body":"Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.","created_at":"2015-06-26T06:41:49Z","updated_at":"2015-06-26T06:41:49Z","html_url":"https://github.com/opf/openproject/pull/3149#discussion_r33331989","pull_request_url":"https://api.github.com/repos/opf/openproject/pulls/3149","_links":{"self":{"href":"https://api.github.com/repos/opf/openproject/pulls/comments/33331989"},"html":{"href":"https://github.com/opf/openproject/pull/3149#discussion_r33331989"},"pull_request":{"href":"https://api.github.com/repos/opf/openproject/pulls/3149"}}},"pull_request":{"url":"https://api.github.com/repos/opf/openproject/pulls/3149","id":38015554,"html_url":"https://github.com/opf/openproject/pull/3149","diff_url":"https://github.com/opf/openproject/pull/3149.diff","patch_url":"https://github.com/opf/openproject/pull/3149.patch","issue_url":"https://api.github.com/repos/opf/openproject/issues/3149","number":3149,"state":"open","locked":false,"title":"Repository Management - Refactoring and Preparation","user":{"login":"oliverguenther","id":459462,"avatar_url":"https://avatars.githubusercontent.com/u/459462?v=3","gravatar_id":"","url":"https://api.github.com/users/oliverguenther","html_url":"https://github.com/oliverguenther","followers_url":"https://api.github.com/users/oliverguenther/followers","following_url":"https://api.github.com/users/oliverguenther/following{/other_user}","gists_url":"https://api.github.com/users/oliverguenther/gists{/gist_id}","starred_url":"https://api.github.com/users/oliverguenther/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliverguenther/subscriptions","organizations_url":"https://api.github.com/users/oliverguenther/orgs","repos_url":"https://api.github.com/users/oliverguenther/repos","events_url":"https://api.github.com/users/oliverguenther/events{/privacy}","received_events_url":"https://api.github.com/users/oliverguenther/received_events","type":"User","site_admin":false},"body":"Related work package: [#20415](https://community.openproject.org/work_packages/20415).\r\n\r\nThis is a combined refactoring and extraction effort of local SCM functionality for the {SVM, Git, FileSystem} adapters in preparation for the explicit lifetime management of repositories in #20416 and implementation of (local) repository storage quota in #18393.\r\n\r\nThis PR is still heavily WIP and for the time being, depends on the **legacy specs** for testing.\r\nTo run the tests, execute the following steps:\r\n\r\n~~~\r\nrake test:scm:setup:all\r\nrake spec:legacy\r\n~~~\r\n\r\nI'm open for ideas on how we integrate scm adapters tests into current specs.\r\n\r\n---\r\n\r\n- [x] Exchange `IO::Open` for `Open3` to avoid countless manual exitcode checks\r\n- [x] Extract local functionality in its own Module\r\n  -  [ ] Extend `LocalClient` module with links to create/delete services for #20415\r\n- [x] Break down unbearable local parse routines (Hound will be over the place until fixed - but down from 260 to < 20)\r\n  - [x] `entries`\r\n  - [x] `revisions`\r\n- [ ] local repository factory\r\n- [ ] Specs\r\n  - [ ] Fix / Modernize\r\n  - [ ] More adapter-related specs","created_at":"2015-06-18T14:55:33Z","updated_at":"2015-06-26T06:41:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0f50628cc227b825311f48802a5f2312c8e303a7","assignee":{"login":"oliverguenther","id":459462,"avatar_url":"https://avatars.githubusercontent.com/u/459462?v=3","gravatar_id":"","url":"https://api.github.com/users/oliverguenther","html_url":"https://github.com/oliverguenther","followers_url":"https://api.github.com/users/oliverguenther/followers","following_url":"https://api.github.com/users/oliverguenther/following{/other_user}","gists_url":"https://api.github.com/users/oliverguenther/gists{/gist_id}","starred_url":"https://api.github.com/users/oliverguenther/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliverguenther/subscriptions","organizations_url":"https://api.github.com/users/oliverguenther/orgs","repos_url":"https://api.github.com/users/oliverguenther/repos","events_url":"https://api.github.com/users/oliverguenther/events{/privacy}","received_events_url":"https://api.github.com/users/oliverguenther/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/opf/openproject/milestones/3","html_url":"https://github.com/opf/openproject/milestones/4.3","labels_url":"https://api.github.com/repos/opf/openproject/milestones/3/labels","id":1104474,"number":3,"title":"4.3","description":"","creator":{"login":"myabc","id":755,"avatar_url":"https://avatars.githubusercontent.com/u/755?v=3","gravatar_id":"","url":"https://api.github.com/users/myabc","html_url":"https://github.com/myabc","followers_url":"https://api.github.com/users/myabc/followers","following_url":"https://api.github.com/users/myabc/following{/other_user}","gists_url":"https://api.github.com/users/myabc/gists{/gist_id}","starred_url":"https://api.github.com/users/myabc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/myabc/subscriptions","organizations_url":"https://api.github.com/users/myabc/orgs","repos_url":"https://api.github.com/users/myabc/repos","events_url":"https://api.github.com/users/myabc/events{/privacy}","received_events_url":"https://api.github.com/users/myabc/received_events","type":"User","site_admin":false},"open_issues":1,"closed_issues":0,"state":"open","created_at":"2015-05-11T10:04:28Z","updated_at":"2015-06-18T15:00:09Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/opf/openproject/pulls/3149/commits","review_comments_url":"https://api.github.com/repos/opf/openproject/pulls/3149/comments","review_comment_url":"https://api.github.com/repos/opf/openproject/pulls/comments{/number}","comments_url":"https://api.github.com/repos/opf/openproject/issues/3149/comments","statuses_url":"https://api.github.com/repos/opf/openproject/statuses/2149109b6fe22e512495af208f4fb6842490c125","head":{"label":"oliverguenther:feature/repository_interfaces","ref":"feature/repository_interfaces","sha":"2149109b6fe22e512495af208f4fb6842490c125","user":{"login":"oliverguenther","id":459462,"avatar_url":"https://avatars.githubusercontent.com/u/459462?v=3","gravatar_id":"","url":"https://api.github.com/users/oliverguenther","html_url":"https://github.com/oliverguenther","followers_url":"https://api.github.com/users/oliverguenther/followers","following_url":"https://api.github.com/users/oliverguenther/following{/other_user}","gists_url":"https://api.github.com/users/oliverguenther/gists{/gist_id}","starred_url":"https://api.github.com/users/oliverguenther/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliverguenther/subscriptions","organizations_url":"https://api.github.com/users/oliverguenther/orgs","repos_url":"https://api.github.com/users/oliverguenther/repos","events_url":"https://api.github.com/users/oliverguenther/events{/privacy}","received_events_url":"https://api.github.com/users/oliverguenther/received_events","type":"User","site_admin":false},"repo":{"id":33988921,"name":"openproject","full_name":"oliverguenther/openproject","owner":{"login":"oliverguenther","id":459462,"avatar_url":"https://avatars.githubusercontent.com/u/459462?v=3","gravatar_id":"","url":"https://api.github.com/users/oliverguenther","html_url":"https://github.com/oliverguenther","followers_url":"https://api.github.com/users/oliverguenther/followers","following_url":"https://api.github.com/users/oliverguenther/following{/other_user}","gists_url":"https://api.github.com/users/oliverguenther/gists{/gist_id}","starred_url":"https://api.github.com/users/oliverguenther/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oliverguenther/subscriptions","organizations_url":"https://api.github.com/users/oliverguenther/orgs","repos_url":"https://api.github.com/users/oliverguenther/repos","events_url":"https://api.github.com/users/oliverguenther/events{/privacy}","received_events_url":"https://api.github.com/users/oliverguenther/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/oliverguenther/openproject","description":"OpenProject is a web-based project management system built on Ruby on Rails","fork":true,"url":"https://api.github.com/repos/oliverguenther/openproject","forks_url":"https://api.github.com/repos/oliverguenther/openproject/forks","keys_url":"https://api.github.com/repos/oliverguenther/openproject/keys{/key_id}","collaborators_url":"https://api.github.com/repos/oliverguenther/openproject/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/oliverguenther/openproject/teams","hooks_url":"https://api.github.com/repos/oliverguenther/openproject/hooks","issue_events_url":"https://api.github.com/repos/oliverguenther/openproject/issues/events{/number}","events_url":"https://api.github.com/repos/oliverguenther/openproject/events","assignees_url":"https://api.github.com/repos/oliverguenther/openproject/assignees{/user}","branches_url":"https://api.github.com/repos/oliverguenther/openproject/branches{/branch}","tags_url":"https://api.github.com/repos/oliverguenther/openproject/tags","blobs_url":"https://api.github.com/repos/oliverguenther/openproject/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/oliverguenther/openproject/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/oliverguenther/openproject/git/refs{/sha}","trees_url":"https://api.github.com/repos/oliverguenther/openproject/git/trees{/sha}","statuses_url":"https://api.github.com/repos/oliverguenther/openproject/statuses/{sha}","languages_url":"https://api.github.com/repos/oliverguenther/openproject/languages","stargazers_url":"https://api.github.com/repos/oliverguenther/openproject/stargazers","contributors_url":"https://api.github.com/repos/oliverguenther/openproject/contributors","subscribers_url":"https://api.github.com/repos/oliverguenther/openproject/subscribers","subscription_url":"https://api.github.com/repos/oliverguenther/openproject/subscription","commits_url":"https://api.github.com/repos/oliverguenther/openproject/commits{/sha}","git_commits_url":"https://api.github.com/repos/oliverguenther/openproject/git/commits{/sha}","comments_url":"https://api.github.com/repos/oliverguenther/openproject/comments{/number}","issue_comment_url":"https://api.github.com/repos/oliverguenther/openproject/issues/comments{/number}","contents_url":"https://api.github.com/repos/oliverguenther/openproject/contents/{+path}","compare_url":"https://api.github.com/repos/oliverguenther/openproject/compare/{base}...{head}","merges_url":"https://api.github.com/repos/oliverguenther/openproject/merges","archive_url":"https://api.github.com/repos/oliverguenther/openproject/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/oliverguenther/openproject/downloads","issues_url":"https://api.github.com/repos/oliverguenther/openproject/issues{/number}","pulls_url":"https://api.github.com/repos/oliverguenther/openproject/pulls{/number}","milestones_url":"https://api.github.com/repos/oliverguenther/openproject/milestones{/number}","notifications_url":"https://api.github.com/repos/oliverguenther/openproject/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/oliverguenther/openproject/labels{/name}","releases_url":"https://api.github.com/repos/oliverguenther/openproject/releases{/id}","created_at":"2015-04-15T11:02:49Z","updated_at":"2015-04-15T11:02:57Z","pushed_at":"2015-06-26T06:41:38Z","git_url":"git://github.com/oliverguenther/openproject.git","ssh_url":"git@github.com:oliverguenther/openproject.git","clone_url":"https://github.com/oliverguenther/openproject.git","svn_url":"https://github.com/oliverguenther/openproject","homepage":"https://www.openproject.org","size":64969,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"dev"}},"base":{"label":"opf:dev","ref":"dev","sha":"b8e030530812d5ef670b95778528d6bee6f75f55","user":{"login":"opf","id":1756674,"avatar_url":"https://avatars.githubusercontent.com/u/1756674?v=3","gravatar_id":"","url":"https://api.github.com/users/opf","html_url":"https://github.com/opf","followers_url":"https://api.github.com/users/opf/followers","following_url":"https://api.github.com/users/opf/following{/other_user}","gists_url":"https://api.github.com/users/opf/gists{/gist_id}","starred_url":"https://api.github.com/users/opf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opf/subscriptions","organizations_url":"https://api.github.com/users/opf/orgs","repos_url":"https://api.github.com/users/opf/repos","events_url":"https://api.github.com/users/opf/events{/privacy}","received_events_url":"https://api.github.com/users/opf/received_events","type":"Organization","site_admin":false},"repo":{"id":6899875,"name":"openproject","full_name":"opf/openproject","owner":{"login":"opf","id":1756674,"avatar_url":"https://avatars.githubusercontent.com/u/1756674?v=3","gravatar_id":"","url":"https://api.github.com/users/opf","html_url":"https://github.com/opf","followers_url":"https://api.github.com/users/opf/followers","following_url":"https://api.github.com/users/opf/following{/other_user}","gists_url":"https://api.github.com/users/opf/gists{/gist_id}","starred_url":"https://api.github.com/users/opf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opf/subscriptions","organizations_url":"https://api.github.com/users/opf/orgs","repos_url":"https://api.github.com/users/opf/repos","events_url":"https://api.github.com/users/opf/events{/privacy}","received_events_url":"https://api.github.com/users/opf/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/opf/openproject","description":"OpenProject is a web-based project management system built on Ruby on Rails","fork":false,"url":"https://api.github.com/repos/opf/openproject","forks_url":"https://api.github.com/repos/opf/openproject/forks","keys_url":"https://api.github.com/repos/opf/openproject/keys{/key_id}","collaborators_url":"https://api.github.com/repos/opf/openproject/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/opf/openproject/teams","hooks_url":"https://api.github.com/repos/opf/openproject/hooks","issue_events_url":"https://api.github.com/repos/opf/openproject/issues/events{/number}","events_url":"https://api.github.com/repos/opf/openproject/events","assignees_url":"https://api.github.com/repos/opf/openproject/assignees{/user}","branches_url":"https://api.github.com/repos/opf/openproject/branches{/branch}","tags_url":"https://api.github.com/repos/opf/openproject/tags","blobs_url":"https://api.github.com/repos/opf/openproject/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/opf/openproject/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/opf/openproject/git/refs{/sha}","trees_url":"https://api.github.com/repos/opf/openproject/git/trees{/sha}","statuses_url":"https://api.github.com/repos/opf/openproject/statuses/{sha}","languages_url":"https://api.github.com/repos/opf/openproject/languages","stargazers_url":"https://api.github.com/repos/opf/openproject/stargazers","contributors_url":"https://api.github.com/repos/opf/openproject/contributors","subscribers_url":"https://api.github.com/repos/opf/openproject/subscribers","subscription_url":"https://api.github.com/repos/opf/openproject/subscription","commits_url":"https://api.github.com/repos/opf/openproject/commits{/sha}","git_commits_url":"https://api.github.com/repos/opf/openproject/git/commits{/sha}","comments_url":"https://api.github.com/repos/opf/openproject/comments{/number}","issue_comment_url":"https://api.github.com/repos/opf/openproject/issues/comments{/number}","contents_url":"https://api.github.com/repos/opf/openproject/contents/{+path}","compare_url":"https://api.github.com/repos/opf/openproject/compare/{base}...{head}","merges_url":"https://api.github.com/repos/opf/openproject/merges","archive_url":"https://api.github.com/repos/opf/openproject/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/opf/openproject/downloads","issues_url":"https://api.github.com/repos/opf/openproject/issues{/number}","pulls_url":"https://api.github.com/repos/opf/openproject/pulls{/number}","milestones_url":"https://api.github.com/repos/opf/openproject/milestones{/number}","notifications_url":"https://api.github.com/repos/opf/openproject/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/opf/openproject/labels{/name}","releases_url":"https://api.github.com/repos/opf/openproject/releases{/id}","created_at":"2012-11-28T09:59:11Z","updated_at":"2015-06-25T11:22:31Z","pushed_at":"2015-06-26T06:41:39Z","git_url":"git://github.com/opf/openproject.git","ssh_url":"git@github.com:opf/openproject.git","clone_url":"https://github.com/opf/openproject.git","svn_url":"https://github.com/opf/openproject","homepage":"https://www.openproject.org","size":199263,"stargazers_count":644,"watchers_count":644,"language":"Ruby","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":280,"mirror_url":null,"open_issues_count":69,"forks":280,"open_issues":69,"watchers":644,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/opf/openproject/pulls/3149"},"html":{"href":"https://github.com/opf/openproject/pull/3149"},"issue":{"href":"https://api.github.com/repos/opf/openproject/issues/3149"},"comments":{"href":"https://api.github.com/repos/opf/openproject/issues/3149/comments"},"review_comments":{"href":"https://api.github.com/repos/opf/openproject/pulls/3149/comments"},"review_comment":{"href":"https://api.github.com/repos/opf/openproject/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/opf/openproject/pulls/3149/commits"},"statuses":{"href":"https://api.github.com/repos/opf/openproject/statuses/2149109b6fe22e512495af208f4fb6842490c125"}}}}