diff --git a/resources/sql/patches/006.repository.sql b/resources/sql/patches/006.repository.sql index fdca9e5517..6ea9ee3f5f 100644 --- a/resources/sql/patches/006.repository.sql +++ b/resources/sql/patches/006.repository.sql @@ -1,42 +1,44 @@ create table phabricator_repository.repository_commitdata ( id int unsigned not null auto_increment primary key, commitID int unsigned not null, authorName varchar(255) not null, commitMessage longblob not null, unique key (commitID), key (authorName) ); ALTER TABLE phabricator_worker.worker_task drop priority; ALTER TABLE phabricator_worker.worker_task drop key leaseOwner; ALTER TABLE phabricator_worker.worker_task drop key (leaseOwner(16)); create table phabricator_repository.repository_path ( id int unsigned not null auto_increment primary key, path varchar(512) binary not null, unique key (path) ); create table phabricator_repository.repository_pathchange ( repositoryID int unsigned NOT NULL, pathID int unsigned NOT NULL, commitID int unsigned NOT NULL, targetPathID int unsigned, targetCommitID int unsigned, changeType int unsigned NOT NULL, fileType int unsigned NOT NULL, isDirect bool NOT NULL, commitSequence int unsigned NOT NULL, primary key (commitID, pathID), key (repositoryID, pathID, commitSequence) ); create table phabricator_repository.repository_filesystem ( repositoryID int unsigned not null, parentID int unsigned not null, svnCommit int unsigned not null, pathID int unsigned not null, existed bool not null, fileType int unsigned not null, primary key (repositoryID, parentID, svnCommit, pathID) ); + +alter table repository_filesystem add key (repositoryID, svnCommit); \ No newline at end of file diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 3cbf65f1a8..1d7f4eb5f8 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -1,559 +1,568 @@ + array( + 'uri' => '/res/4408ef5f/rsrc/css/aphront/crumbs-view.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/aphront/crumbs-view.css', + ), 'aphront-dark-console-css' => array( 'uri' => '/res/056b0c12/rsrc/css/aphront/dark-console.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/dark-console.css', ), 'aphront-dialog-view-css' => array( 'uri' => '/res/7101ab69/rsrc/css/aphront/dialog-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/dialog-view.css', ), 'aphront-error-view-css' => array( 'uri' => '/res/19b27527/rsrc/css/aphront/error-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/error-view.css', ), 'aphront-form-view-css' => array( 'uri' => '/res/8aaef437/rsrc/css/aphront/form-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/form-view.css', ), 'aphront-headsup-action-list-view-css' => array( 'uri' => '/res/8fd91c1d/rsrc/css/aphront/headsup-action-list-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/headsup-action-list-view.css', ), 'aphront-panel-view-css' => array( 'uri' => '/res/63672373/rsrc/css/aphront/panel-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/panel-view.css', ), 'aphront-request-failure-view-css' => array( 'uri' => '/res/97b8337a/rsrc/css/aphront/request-failure-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/request-failure-view.css', ), 'aphront-side-nav-view-css' => array( 'uri' => '/res/09b7eb85/rsrc/css/aphront/side-nav-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/side-nav-view.css', ), 'aphront-table-view-css' => array( 'uri' => '/res/6a70f0f0/rsrc/css/aphront/table-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/table-view.css', ), 'aphront-tokenizer-control-css' => array( 'uri' => '/res/a3d23074/rsrc/css/aphront/tokenizer.css', 'type' => 'css', 'requires' => array( 0 => 'aphront-typeahead-control-css', ), 'disk' => '/rsrc/css/aphront/tokenizer.css', ), 'aphront-typeahead-control-css' => array( 'uri' => '/res/928df9f0/rsrc/css/aphront/typeahead.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/typeahead.css', ), 'phabricator-standard-page-view' => array( 'uri' => '/res/0d41ea7c/rsrc/css/application/base/standard-page-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/base/standard-page-view.css', ), 'differential-revision-add-comment-css' => array( 'uri' => '/res/aaae14d3/rsrc/css/application/differential/add-comment.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/add-comment.css', ), 'differential-changeset-view-css' => array( 'uri' => '/res/f26ca6f9/rsrc/css/application/differential/changeset-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/changeset-view.css', ), 'differential-core-view-css' => array( 'uri' => '/res/525d1a12/rsrc/css/application/differential/core.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/core.css', ), 'differential-revision-comment-list-css' => array( 'uri' => '/res/10b9a829/rsrc/css/application/differential/revision-comment-list.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/revision-comment-list.css', ), 'differential-revision-comment-css' => array( 'uri' => '/res/b271baaf/rsrc/css/application/differential/revision-comment.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/revision-comment.css', ), 'differential-revision-detail-css' => array( 'uri' => '/res/623e3946/rsrc/css/application/differential/revision-detail.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/revision-detail.css', ), 'differential-revision-history-css' => array( 'uri' => '/res/755f3da3/rsrc/css/application/differential/revision-history.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/revision-history.css', ), 'differential-table-of-contents-css' => array( 'uri' => '/res/e68f6f05/rsrc/css/application/differential/table-of-contents.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/table-of-contents.css', ), 'phabricator-directory-css' => array( 'uri' => '/res/6a000601/rsrc/css/application/directory/phabricator-directory.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/directory/phabricator-directory.css', ), 'mainphest-task-detail-css' => array( 'uri' => '/res/e5f3beca/rsrc/css/application/maniphest/task-detail.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/maniphest/task-detail.css', ), 'maniphest-task-summary-css' => array( 'uri' => '/res/94d01e6f/rsrc/css/application/maniphest/task-summary.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/maniphest/task-summary.css', ), 'maniphest-transaction-detail-css' => array( - 'uri' => '/res/658912c5/rsrc/css/application/maniphest/transaction-detail.css', + 'uri' => '/res/9418efc9/rsrc/css/application/maniphest/transaction-detail.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/maniphest/transaction-detail.css', ), 'phabricator-object-selector-css' => array( 'uri' => '/res/52a7e289/rsrc/css/application/objectselector/object-selector.css', 'type' => 'css', 'requires' => array( 0 => 'aphront-dialog-view-css', ), 'disk' => '/rsrc/css/application/objectselector/object-selector.css', ), 'phabricator-profile-css' => array( 'uri' => '/res/259ad37f/rsrc/css/application/people/profile.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/people/profile.css', ), 'phabricator-core-buttons-css' => array( 'uri' => '/res/53b4f712/rsrc/css/core/buttons.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/buttons.css', ), 'phabricator-core-css' => array( 'uri' => '/res/6eebb99b/rsrc/css/core/core.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/core.css', ), 'phabricator-remarkup-css' => array( 'uri' => '/res/786989c3/rsrc/css/core/remarkup.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/remarkup.css', ), 'syntax-highlighting-css' => array( 'uri' => '/res/fb673ece/rsrc/css/core/syntax.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/syntax.css', ), 'javelin-behavior-dark-console' => array( 'uri' => '/res/020b0265/rsrc/js/application/core/behavior-dark-console.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/application/core/behavior-dark-console.js', ), 'javelin-behavior-phabricator-object-selector' => array( 'uri' => '/res/4fe735af/rsrc/js/application/core/behavior-object-selector.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/core/behavior-object-selector.js', ), 'javelin-behavior-aphront-basic-tokenizer' => array( 'uri' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/core/behavior-tokenizer.js', ), 'javelin-behavior-workflow' => array( 'uri' => '/res/15446e7e/rsrc/js/application/core/behavior-workflow.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/core/behavior-workflow.js', ), 'javelin-behavior-differential-add-reviewers' => array( 'uri' => '/res/330154e4/rsrc/js/application/differential/behavior-add-reviewers.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-add-reviewers.js', ), 'javelin-behavior-differential-feedback-preview' => array( 'uri' => '/res/8695d8b8/rsrc/js/application/differential/behavior-comment-preview.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-comment-preview.js', ), 'javelin-behavior-differential-diff-radios' => array( 'uri' => '/res/fdeb3823/rsrc/js/application/differential/behavior-diff-radios.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-diff-radios.js', ), 'javelin-behavior-differential-edit-inline-comments' => array( 'uri' => '/res/74747b2e/rsrc/js/application/differential/behavior-edit-inline-comments.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-edit-inline-comments.js', ), 'javelin-behavior-differential-populate' => array( 'uri' => '/res/a13dcd7e/rsrc/js/application/differential/behavior-populate.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-populate.js', ), 'javelin-behavior-differential-show-all-comments' => array( 'uri' => '/res/2a3592b8/rsrc/js/application/differential/behavior-show-all-comments.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-show-all-comments.js', ), 'javelin-behavior-differential-show-more' => array( 'uri' => '/res/ea998002/rsrc/js/application/differential/behavior-show-more.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-show-more.js', ), 'javelin-behavior-maniphest-transaction-controls' => array( 'uri' => '/res/fc6a8722/rsrc/js/application/maniphest/behavior-transaction-controls.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js', ), 'javelin-magical-init' => array( 'uri' => '/res/76614f84/rsrc/js/javelin/init.dev.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/init.dev.js', ), 'javelin-init-prod' => array( 'uri' => '/res/1267c868/rsrc/js/javelin/init.min.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/init.min.js', ), 'javelin-lib-dev' => array( 'uri' => '/res/a0e7a5e9/rsrc/js/javelin/javelin.dev.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/javelin.dev.js', ), 'javelin-lib-prod' => array( 'uri' => '/res/2f2b3b2e/rsrc/js/javelin/javelin.min.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/javelin.min.js', ), 'javelin-typeahead-dev' => array( 'uri' => '/res/6de6ae59/rsrc/js/javelin/typeahead.dev.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/typeahead.dev.js', ), 'javelin-typeahead-prod' => array( 'uri' => '/res/69d5fad1/rsrc/js/javelin/typeahead.min.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/typeahead.min.js', ), 'javelin-workflow-dev' => array( 'uri' => '/res/c6b17f93/rsrc/js/javelin/workflow.dev.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/workflow.dev.js', ), 'javelin-workflow-prod' => array( 'uri' => '/res/b758e0a0/rsrc/js/javelin/workflow.min.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/workflow.min.js', ), ), array ( 'packages' => array ( '04b474ba' => array ( 'name' => 'core.pkg.css', 'symbols' => array ( 0 => 'phabricator-core-css', 1 => 'phabricator-core-buttons-css', 2 => 'phabricator-standard-page-view', 3 => 'aphront-dialog-view-css', 4 => 'aphront-form-view-css', 5 => 'aphront-panel-view-css', 6 => 'aphront-side-nav-view-css', 7 => 'aphront-table-view-css', 8 => 'aphront-tokenizer-control-css', 9 => 'aphront-typeahead-control-css', 10 => 'phabricator-directory-css', 11 => 'phabricator-remarkup-css', 12 => 'syntax-highlighting-css', ), 'uri' => '/res/pkg/04b474ba/core.pkg.css', 'type' => 'css', ), '76f3c1f8' => array ( 'name' => 'differential.pkg.css', 'symbols' => array ( 0 => 'differential-core-view-css', 1 => 'differential-changeset-view-css', 2 => 'differential-revision-detail-css', 3 => 'differential-revision-history-css', 4 => 'differential-table-of-contents-css', 5 => 'differential-revision-comment-css', 6 => 'differential-revision-add-comment-css', 7 => 'differential-revision-comment-list-css', ), 'uri' => '/res/pkg/76f3c1f8/differential.pkg.css', 'type' => 'css', ), '30d594cf' => array ( 'name' => 'differential.pkg.js', 'symbols' => array ( 0 => 'javelin-behavior-differential-feedback-preview', 1 => 'javelin-behavior-differential-edit-inline-comments', 2 => 'javelin-behavior-differential-populate', 3 => 'javelin-behavior-differential-show-more', 4 => 'javelin-behavior-differential-diff-radios', ), 'uri' => '/res/pkg/30d594cf/differential.pkg.js', 'type' => 'js', ), ), 'reverse' => array ( 'phabricator-core-css' => '04b474ba', 'phabricator-core-buttons-css' => '04b474ba', 'phabricator-standard-page-view' => '04b474ba', 'aphront-dialog-view-css' => '04b474ba', 'aphront-form-view-css' => '04b474ba', 'aphront-panel-view-css' => '04b474ba', 'aphront-side-nav-view-css' => '04b474ba', 'aphront-table-view-css' => '04b474ba', 'aphront-tokenizer-control-css' => '04b474ba', 'aphront-typeahead-control-css' => '04b474ba', 'phabricator-directory-css' => '04b474ba', 'phabricator-remarkup-css' => '04b474ba', 'syntax-highlighting-css' => '04b474ba', 'differential-core-view-css' => '76f3c1f8', 'differential-changeset-view-css' => '76f3c1f8', 'differential-revision-detail-css' => '76f3c1f8', 'differential-revision-history-css' => '76f3c1f8', 'differential-table-of-contents-css' => '76f3c1f8', 'differential-revision-comment-css' => '76f3c1f8', 'differential-revision-add-comment-css' => '76f3c1f8', 'differential-revision-comment-list-css' => '76f3c1f8', 'javelin-behavior-differential-feedback-preview' => '30d594cf', 'javelin-behavior-differential-edit-inline-comments' => '30d594cf', 'javelin-behavior-differential-populate' => '30d594cf', 'javelin-behavior-differential-show-more' => '30d594cf', 'javelin-behavior-differential-diff-radios' => '30d594cf', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f130884b9b..9b91bedb95 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,630 +1,646 @@ array( 'Aphront400Response' => 'aphront/response/400', 'Aphront404Response' => 'aphront/response/404', 'AphrontAjaxResponse' => 'aphront/response/ajax', 'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration', 'AphrontController' => 'aphront/controller', + 'AphrontCrumbsView' => 'view/layout/crumbs', 'AphrontDatabaseConnection' => 'storage/connection/base', 'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration', 'AphrontDefaultApplicationController' => 'aphront/default/controller', 'AphrontDialogResponse' => 'aphront/response/dialog', 'AphrontDialogView' => 'view/dialog', 'AphrontErrorView' => 'view/form/error', 'AphrontException' => 'aphront/exception/base', 'AphrontFileResponse' => 'aphront/response/file', 'AphrontFormCheckboxControl' => 'view/form/control/checkbox', 'AphrontFormControl' => 'view/form/control/base', 'AphrontFormDividerControl' => 'view/form/control/divider', 'AphrontFormFileControl' => 'view/form/control/file', 'AphrontFormMarkupControl' => 'view/form/control/markup', 'AphrontFormPasswordControl' => 'view/form/control/password', 'AphrontFormRecaptchaControl' => 'view/form/control/recaptcha', 'AphrontFormSelectControl' => 'view/form/control/select', 'AphrontFormStaticControl' => 'view/form/control/static', 'AphrontFormSubmitControl' => 'view/form/control/submit', 'AphrontFormTextAreaControl' => 'view/form/control/textarea', 'AphrontFormTextControl' => 'view/form/control/text', 'AphrontFormTokenizerControl' => 'view/form/control/tokenizer', 'AphrontFormView' => 'view/form/base', 'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist', 'AphrontHeadsupActionView' => 'view/layout/headsup/action', 'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql', 'AphrontNullView' => 'view/null', 'AphrontPageView' => 'view/page/base', 'AphrontPanelView' => 'view/layout/panel', 'AphrontQueryConnectionException' => 'storage/exception/connection', 'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost', 'AphrontQueryCountException' => 'storage/exception/count', 'AphrontQueryDuplicateKeyException' => 'storage/exception/duplicatekey', 'AphrontQueryException' => 'storage/exception/base', 'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing', 'AphrontQueryParameterException' => 'storage/exception/parameter', 'AphrontQueryRecoverableException' => 'storage/exception/recoverable', 'AphrontRedirectException' => 'aphront/exception/redirect', 'AphrontRedirectResponse' => 'aphront/response/redirect', 'AphrontRequest' => 'aphront/request', 'AphrontRequestFailureView' => 'view/page/failure', 'AphrontResponse' => 'aphront/response/base', 'AphrontSideNavView' => 'view/layout/sidenav', 'AphrontTableView' => 'view/control/table', 'AphrontURIMapper' => 'aphront/mapper', 'AphrontView' => 'view/base', 'AphrontWebpageResponse' => 'aphront/response/webpage', 'CelerityAPI' => 'infrastructure/celerity/api', 'CelerityResourceController' => 'infrastructure/celerity/controller', 'CelerityResourceMap' => 'infrastructure/celerity/map', 'CelerityStaticResourceResponse' => 'infrastructure/celerity/response', 'ConduitAPIMethod' => 'applications/conduit/method/base', 'ConduitAPIRequest' => 'applications/conduit/protocol/request', 'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect', 'ConduitAPI_conduit_ping_Method' => 'applications/conduit/method/conduit/ping', 'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff', 'ConduitAPI_differential_createrevision_Method' => 'applications/conduit/method/differential/createrevision', 'ConduitAPI_differential_find_Method' => 'applications/conduit/method/differential/find', 'ConduitAPI_differential_getcommitmessage_Method' => 'applications/conduit/method/differential/getcommitmessage', 'ConduitAPI_differential_getcommitpaths_Method' => 'applications/conduit/method/differential/getcommitpaths', 'ConduitAPI_differential_getdiff_Method' => 'applications/conduit/method/differential/getdiff', 'ConduitAPI_differential_markcommitted_Method' => 'applications/conduit/method/differential/markcommitted', 'ConduitAPI_differential_parsecommitmessage_Method' => 'applications/conduit/method/differential/parsecommitmessage', 'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty', 'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision', 'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload', 'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find', 'ConduitException' => 'applications/conduit/protocol/exception', 'DarkConsole' => 'aphront/console/api', 'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config', 'DarkConsoleController' => 'aphront/console/controller', 'DarkConsoleCore' => 'aphront/console/core', 'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog', 'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/api', 'DarkConsolePlugin' => 'aphront/console/plugin/base', 'DarkConsoleRequestPlugin' => 'aphront/console/plugin/request', 'DarkConsoleServicesPlugin' => 'aphront/console/plugin/services', 'DarkConsoleServicesPluginAPI' => 'aphront/console/plugin/services/api', 'DarkConsoleXHProfPlugin' => 'aphront/console/plugin/xhprof', 'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api', 'DifferentialAction' => 'applications/differential/constants/action', 'DifferentialAddCommentView' => 'applications/differential/view/addcomment', 'DifferentialAttachController' => 'applications/differential/controller/attach', 'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome', 'DifferentialChangeType' => 'applications/differential/constants/changetype', 'DifferentialChangeset' => 'applications/differential/storage/changeset', 'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview', 'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview', 'DifferentialChangesetParser' => 'applications/differential/parser/changeset', 'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview', 'DifferentialComment' => 'applications/differential/storage/comment', 'DifferentialCommentEditor' => 'applications/differential/editor/comment', 'DifferentialCommentMail' => 'applications/differential/mail/comment', 'DifferentialCommentPreviewController' => 'applications/differential/controller/commentpreview', 'DifferentialCommentSaveController' => 'applications/differential/controller/commentsave', 'DifferentialCommitMessage' => 'applications/differential/parser/commitmessage', 'DifferentialCommitMessageData' => 'applications/differential/data/commitmessage', 'DifferentialCommitMessageParserException' => 'applications/differential/parser/commitmessage/exception', 'DifferentialController' => 'applications/differential/controller/base', 'DifferentialDAO' => 'applications/differential/storage/base', 'DifferentialDiff' => 'applications/differential/storage/diff', 'DifferentialDiffContentMail' => 'applications/differential/mail/diffcontent', 'DifferentialDiffCreateController' => 'applications/differential/controller/diffcreate', 'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty', 'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents', 'DifferentialDiffViewController' => 'applications/differential/controller/diffview', 'DifferentialHunk' => 'applications/differential/storage/hunk', 'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment', 'DifferentialInlineCommentEditController' => 'applications/differential/controller/inlinecommentedit', 'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/inlinecommentpreview', 'DifferentialInlineCommentView' => 'applications/differential/view/inlinecomment', 'DifferentialLintStatus' => 'applications/differential/constants/lintstatus', 'DifferentialMail' => 'applications/differential/mail/base', 'DifferentialMarkupEngineFactory' => 'applications/differential/parser/markup', 'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff', 'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest', 'DifferentialRevision' => 'applications/differential/storage/revision', 'DifferentialRevisionCommentListView' => 'applications/differential/view/revisioncommentlist', 'DifferentialRevisionCommentView' => 'applications/differential/view/revisioncomment', 'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem', 'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail', 'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit', 'DifferentialRevisionEditor' => 'applications/differential/editor/revision', 'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist', 'DifferentialRevisionListData' => 'applications/differential/data/revisionlist', 'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus', 'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory', 'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview', 'DifferentialSubscribeController' => 'applications/differential/controller/subscribe', 'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus', + 'DiffusionBranchInformation' => 'applications/diffusion/data/branch', + 'DiffusionBranchQuery' => 'applications/diffusion/query/branch/base', + 'DiffusionBranchTableView' => 'applications/diffusion/view/branchtable', 'DiffusionBrowseController' => 'applications/diffusion/controller/browse', 'DiffusionBrowseFileController' => 'applications/diffusion/controller/file', 'DiffusionBrowseQuery' => 'applications/diffusion/query/browse/base', 'DiffusionBrowseTableView' => 'applications/diffusion/view/browsetable', 'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable', 'DiffusionCommitController' => 'applications/diffusion/controller/commit', 'DiffusionController' => 'applications/diffusion/controller/base', 'DiffusionFileContent' => 'applications/diffusion/data/filecontent', 'DiffusionFileContentQuery' => 'applications/diffusion/query/filecontent/base', + 'DiffusionGitBranchQuery' => 'applications/diffusion/query/branch/git', 'DiffusionGitBrowseQuery' => 'applications/diffusion/query/browse/git', 'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/git', 'DiffusionGitHistoryQuery' => 'applications/diffusion/query/history/git', 'DiffusionGitRequest' => 'applications/diffusion/request/git', 'DiffusionHistoryController' => 'applications/diffusion/controller/history', 'DiffusionHistoryQuery' => 'applications/diffusion/query/history/base', 'DiffusionHistoryTableView' => 'applications/diffusion/view/historytable', 'DiffusionHomeController' => 'applications/diffusion/controller/home', 'DiffusionPathChange' => 'applications/diffusion/data/pathchange', + 'DiffusionRepositoryController' => 'applications/diffusion/controller/repository', 'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath', 'DiffusionRequest' => 'applications/diffusion/request/base', + 'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/svn', + 'DiffusionSvnHistoryQuery' => 'applications/diffusion/query/history/svn', + 'DiffusionView' => 'applications/diffusion/view/base', 'Javelin' => 'infrastructure/javelin/api', 'LiskDAO' => 'storage/lisk/dao', 'ManiphestController' => 'applications/maniphest/controller/base', 'ManiphestDAO' => 'applications/maniphest/storage/base', 'ManiphestTask' => 'applications/maniphest/storage/task', 'ManiphestTaskDetailController' => 'applications/maniphest/controller/taskdetail', 'ManiphestTaskEditController' => 'applications/maniphest/controller/taskedit', 'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist', 'ManiphestTaskListView' => 'applications/maniphest/view/tasklist', 'ManiphestTaskPriority' => 'applications/maniphest/constants/priority', 'ManiphestTaskSelectorSearchController' => 'applications/maniphest/controller/taskselectorsearch', 'ManiphestTaskStatus' => 'applications/maniphest/constants/status', 'ManiphestTaskSummaryView' => 'applications/maniphest/view/tasksummary', 'ManiphestTransaction' => 'applications/maniphest/storage/transaction', 'ManiphestTransactionDetailView' => 'applications/maniphest/view/transactiondetail', 'ManiphestTransactionEditor' => 'applications/maniphest/editor/transaction', 'ManiphestTransactionListView' => 'applications/maniphest/view/transactionlist', 'ManiphestTransactionSaveController' => 'applications/maniphest/controller/transactionsave', 'ManiphestTransactionType' => 'applications/maniphest/constants/transactiontype', 'Phabricator404Controller' => 'applications/base/controller/404', 'PhabricatorAuthController' => 'applications/auth/controller/base', 'PhabricatorConduitAPIController' => 'applications/conduit/controller/api', 'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog', 'PhabricatorConduitConsoleController' => 'applications/conduit/controller/console', 'PhabricatorConduitController' => 'applications/conduit/controller/base', 'PhabricatorConduitDAO' => 'applications/conduit/storage/base', 'PhabricatorConduitLogController' => 'applications/conduit/controller/log', 'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog', 'PhabricatorController' => 'applications/base/controller/base', 'PhabricatorDaemon' => 'infrastructure/daemon/base', 'PhabricatorDaemonConsoleController' => 'applications/daemon/controller/console', 'PhabricatorDaemonController' => 'applications/daemon/controller/base', 'PhabricatorDaemonTimelineConsoleController' => 'applications/daemon/controller/timeline', 'PhabricatorDaemonTimelineEventController' => 'applications/daemon/controller/timelineevent', 'PhabricatorDirectoryCategory' => 'applications/directory/storage/category', 'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete', 'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit', 'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist', 'PhabricatorDirectoryController' => 'applications/directory/controller/base', 'PhabricatorDirectoryDAO' => 'applications/directory/storage/base', 'PhabricatorDirectoryItem' => 'applications/directory/storage/item', 'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete', 'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit', 'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist', 'PhabricatorDirectoryMainController' => 'applications/directory/controller/main', 'PhabricatorDraft' => 'applications/draft/storage/draft', 'PhabricatorDraftDAO' => 'applications/draft/storage/base', 'PhabricatorEmailLoginController' => 'applications/auth/controller/email', 'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken', 'PhabricatorEnv' => 'infrastructure/env', 'PhabricatorFile' => 'applications/files/storage/file', 'PhabricatorFileController' => 'applications/files/controller/base', 'PhabricatorFileDAO' => 'applications/files/storage/base', 'PhabricatorFileListController' => 'applications/files/controller/list', 'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob', 'PhabricatorFileURI' => 'applications/files/uri', 'PhabricatorFileUploadController' => 'applications/files/controller/upload', 'PhabricatorFileViewController' => 'applications/files/controller/view', 'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing', 'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector', 'PhabricatorLiskDAO' => 'applications/base/storage/lisk', 'PhabricatorLoginController' => 'applications/auth/controller/login', 'PhabricatorLogoutController' => 'applications/auth/controller/logout', 'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base', 'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/amazonses', 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite', 'PhabricatorMetaMTAController' => 'applications/metamta/controller/base', 'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base', 'PhabricatorMetaMTADaemon' => 'applications/metamta/daemon/mta', 'PhabricatorMetaMTAListController' => 'applications/metamta/controller/list', 'PhabricatorMetaMTAMail' => 'applications/metamta/storage/mail', 'PhabricatorMetaMTAMailingList' => 'applications/metamta/storage/mailinglist', 'PhabricatorMetaMTAMailingListEditController' => 'applications/metamta/controller/mailinglistedit', 'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists', 'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send', 'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view', 'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default', 'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics', 'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure', 'PhabricatorOAuthLoginController' => 'applications/auth/controller/oauth', 'PhabricatorOAuthProvider' => 'applications/auth/oauth/provider/base', 'PhabricatorOAuthProviderFacebook' => 'applications/auth/oauth/provider/facebook', 'PhabricatorOAuthProviderGithub' => 'applications/auth/oauth/provider/github', 'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base', 'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink', 'PhabricatorObjectHandle' => 'applications/phid/handle', 'PhabricatorObjectHandleData' => 'applications/phid/handle/data', 'PhabricatorObjectSelectorDialog' => 'view/control/objectselector', 'PhabricatorPHID' => 'applications/phid/storage/phid', 'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate', 'PhabricatorPHIDConstants' => 'applications/phid/constants', 'PhabricatorPHIDController' => 'applications/phid/controller/base', 'PhabricatorPHIDDAO' => 'applications/phid/storage/base', 'PhabricatorPHIDListController' => 'applications/phid/controller/list', 'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup', 'PhabricatorPHIDType' => 'applications/phid/storage/type', 'PhabricatorPHIDTypeEditController' => 'applications/phid/controller/typeedit', 'PhabricatorPHIDTypeListController' => 'applications/phid/controller/typelist', 'PhabricatorPeopleController' => 'applications/people/controller/base', 'PhabricatorPeopleEditController' => 'applications/people/controller/edit', 'PhabricatorPeopleListController' => 'applications/people/controller/list', 'PhabricatorPeopleProfileController' => 'applications/people/controller/profile', 'PhabricatorPeopleProfileEditController' => 'applications/people/controller/profileedit', 'PhabricatorProject' => 'applications/project/storage/project', 'PhabricatorProjectAffiliation' => 'applications/project/storage/affiliation', 'PhabricatorProjectAffiliationEditController' => 'applications/project/controller/editaffiliation', 'PhabricatorProjectController' => 'applications/project/controller/base', 'PhabricatorProjectDAO' => 'applications/project/storage/base', 'PhabricatorProjectEditController' => 'applications/project/controller/edit', 'PhabricatorProjectListController' => 'applications/project/controller/list', 'PhabricatorProjectProfile' => 'applications/project/storage/profile', 'PhabricatorProjectProfileController' => 'applications/project/controller/profile', 'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential', 'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest', 'PhabricatorRepository' => 'applications/repository/storage/repository', 'PhabricatorRepositoryCommit' => 'applications/repository/storage/commit', 'PhabricatorRepositoryCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/base', 'PhabricatorRepositoryCommitData' => 'applications/repository/storage/commitdata', 'PhabricatorRepositoryCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/base', 'PhabricatorRepositoryCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/base', 'PhabricatorRepositoryCommitParserWorker' => 'applications/repository/worker/base', 'PhabricatorRepositoryCommitTaskDaemon' => 'applications/repository/daemon/committask', 'PhabricatorRepositoryController' => 'applications/repository/controller/base', 'PhabricatorRepositoryCreateController' => 'applications/repository/controller/create', 'PhabricatorRepositoryDAO' => 'applications/repository/storage/base', 'PhabricatorRepositoryDaemon' => 'applications/repository/daemon/base', 'PhabricatorRepositoryEditController' => 'applications/repository/controller/edit', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/git', 'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/git', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git', 'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification', 'PhabricatorRepositoryGitHubPostReceiveController' => 'applications/repository/controller/github-post-receive', 'PhabricatorRepositoryGitPullDaemon' => 'applications/repository/daemon/gitpull', 'PhabricatorRepositoryListController' => 'applications/repository/controller/list', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn', 'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn', 'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype', 'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument', 'PhabricatorSearchBaseController' => 'applications/search/controller/base', 'PhabricatorSearchController' => 'applications/search/controller/search', 'PhabricatorSearchDAO' => 'applications/search/storage/base', 'PhabricatorSearchDifferentialIndexer' => 'applications/search/index/indexer/differential', 'PhabricatorSearchDocument' => 'applications/search/storage/document/document', 'PhabricatorSearchDocumentField' => 'applications/search/storage/document/field', 'PhabricatorSearchDocumentIndexer' => 'applications/search/index/indexer/base', 'PhabricatorSearchDocumentRelationship' => 'applications/search/storage/document/relationship', 'PhabricatorSearchExecutor' => 'applications/search/execute/base', 'PhabricatorSearchField' => 'applications/search/constants/field', 'PhabricatorSearchManiphestIndexer' => 'applications/search/index/indexer/maniphest', 'PhabricatorSearchMySQLExecutor' => 'applications/search/execute/mysql', 'PhabricatorSearchQuery' => 'applications/search/storage/query', 'PhabricatorSearchRelationship' => 'applications/search/constants/relationship', 'PhabricatorStandardPageView' => 'view/page/standard', 'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster', 'PhabricatorTimelineCursor' => 'infrastructure/daemon/timeline/storage/cursor', 'PhabricatorTimelineDAO' => 'infrastructure/daemon/timeline/storage/base', 'PhabricatorTimelineEvent' => 'infrastructure/daemon/timeline/storage/event', 'PhabricatorTimelineEventData' => 'infrastructure/daemon/timeline/storage/eventdata', 'PhabricatorTimelineIterator' => 'infrastructure/daemon/timeline/cursor/iterator', 'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common', 'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base', 'PhabricatorUser' => 'applications/people/storage/user', 'PhabricatorUserDAO' => 'applications/people/storage/base', 'PhabricatorUserOAuthInfo' => 'applications/people/storage/useroauthinfo', 'PhabricatorUserProfile' => 'applications/people/storage/profile', 'PhabricatorUserSettingsController' => 'applications/people/controller/settings', 'PhabricatorWorker' => 'infrastructure/daemon/workers/worker', 'PhabricatorWorkerDAO' => 'infrastructure/daemon/workers/storage/base', 'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/task', 'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/taskdata', 'PhabricatorXHProfController' => 'applications/xhprof/controller/base', 'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile', 'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol', 'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel', ), 'function' => array( '_qsprintf_check_scalar_type' => 'storage/qsprintf', '_qsprintf_check_type' => 'storage/qsprintf', 'celerity_generate_unique_node_id' => 'infrastructure/celerity/api', 'celerity_register_resource_map' => 'infrastructure/celerity/map', 'javelin_render_tag' => 'infrastructure/javelin/markup', 'phabricator_format_relative_time' => 'view/utils', 'phabricator_format_timestamp' => 'view/utils', 'phabricator_format_units_generic' => 'view/utils', 'phabricator_render_form' => 'infrastructure/javelin/markup', 'qsprintf' => 'storage/qsprintf', 'queryfx' => 'storage/queryfx', 'queryfx_all' => 'storage/queryfx', 'queryfx_one' => 'storage/queryfx', 'require_celerity_resource' => 'infrastructure/celerity/api', 'vqsprintf' => 'storage/qsprintf', 'vqueryfx' => 'storage/queryfx', 'vqueryfx_all' => 'storage/queryfx', 'xsprintf_query' => 'storage/qsprintf', ), 'requires_class' => array( 'Aphront400Response' => 'AphrontResponse', 'Aphront404Response' => 'AphrontResponse', 'AphrontAjaxResponse' => 'AphrontResponse', + 'AphrontCrumbsView' => 'AphrontView', 'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration', 'AphrontDefaultApplicationController' => 'AphrontController', 'AphrontDialogResponse' => 'AphrontResponse', 'AphrontDialogView' => 'AphrontView', 'AphrontErrorView' => 'AphrontView', 'AphrontFileResponse' => 'AphrontResponse', 'AphrontFormCheckboxControl' => 'AphrontFormControl', 'AphrontFormControl' => 'AphrontView', 'AphrontFormDividerControl' => 'AphrontFormControl', 'AphrontFormFileControl' => 'AphrontFormControl', 'AphrontFormMarkupControl' => 'AphrontFormControl', 'AphrontFormPasswordControl' => 'AphrontFormControl', 'AphrontFormRecaptchaControl' => 'AphrontFormControl', 'AphrontFormSelectControl' => 'AphrontFormControl', 'AphrontFormStaticControl' => 'AphrontFormControl', 'AphrontFormSubmitControl' => 'AphrontFormControl', 'AphrontFormTextAreaControl' => 'AphrontFormControl', 'AphrontFormTextControl' => 'AphrontFormControl', 'AphrontFormTokenizerControl' => 'AphrontFormControl', 'AphrontFormView' => 'AphrontView', 'AphrontHeadsupActionListView' => 'AphrontView', 'AphrontHeadsupActionView' => 'AphrontView', 'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontNullView' => 'AphrontView', 'AphrontPageView' => 'AphrontView', 'AphrontPanelView' => 'AphrontView', 'AphrontQueryConnectionException' => 'AphrontQueryException', 'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException', 'AphrontQueryCountException' => 'AphrontQueryException', 'AphrontQueryDuplicateKeyException' => 'AphrontQueryException', 'AphrontQueryObjectMissingException' => 'AphrontQueryException', 'AphrontQueryParameterException' => 'AphrontQueryException', 'AphrontQueryRecoverableException' => 'AphrontQueryException', 'AphrontRedirectException' => 'AphrontException', 'AphrontRedirectResponse' => 'AphrontResponse', 'AphrontRequestFailureView' => 'AphrontView', 'AphrontSideNavView' => 'AphrontView', 'AphrontTableView' => 'AphrontView', 'AphrontWebpageResponse' => 'AphrontResponse', 'CelerityResourceController' => 'AphrontController', 'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod', 'ConduitAPI_conduit_ping_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_createrevision_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_find_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getcommitmessage_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getcommitpaths_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_getdiff_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_markcommitted_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_parsecommitmessage_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod', 'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod', 'ConduitAPI_user_find_Method' => 'ConduitAPIMethod', 'DarkConsoleConfigPlugin' => 'DarkConsolePlugin', 'DarkConsoleController' => 'PhabricatorController', 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', 'DarkConsoleRequestPlugin' => 'DarkConsolePlugin', 'DarkConsoleServicesPlugin' => 'DarkConsolePlugin', 'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin', 'DifferentialAddCommentView' => 'AphrontView', 'DifferentialAttachController' => 'DifferentialController', 'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail', 'DifferentialChangeset' => 'DifferentialDAO', 'DifferentialChangesetDetailView' => 'AphrontView', 'DifferentialChangesetListView' => 'AphrontView', 'DifferentialChangesetViewController' => 'DifferentialController', 'DifferentialComment' => 'DifferentialDAO', 'DifferentialCommentMail' => 'DifferentialMail', 'DifferentialCommentPreviewController' => 'DifferentialController', 'DifferentialCommentSaveController' => 'DifferentialController', 'DifferentialController' => 'PhabricatorController', 'DifferentialDAO' => 'PhabricatorLiskDAO', 'DifferentialDiff' => 'DifferentialDAO', 'DifferentialDiffContentMail' => 'DifferentialMail', 'DifferentialDiffCreateController' => 'DifferentialController', 'DifferentialDiffProperty' => 'DifferentialDAO', 'DifferentialDiffTableOfContentsView' => 'AphrontView', 'DifferentialDiffViewController' => 'DifferentialController', 'DifferentialHunk' => 'DifferentialDAO', 'DifferentialInlineComment' => 'DifferentialDAO', 'DifferentialInlineCommentEditController' => 'DifferentialController', 'DifferentialInlineCommentPreviewController' => 'DifferentialController', 'DifferentialInlineCommentView' => 'AphrontView', 'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail', 'DifferentialReviewRequestMail' => 'DifferentialMail', 'DifferentialRevision' => 'DifferentialDAO', 'DifferentialRevisionCommentListView' => 'AphrontView', 'DifferentialRevisionCommentView' => 'AphrontView', 'DifferentialRevisionDetailView' => 'AphrontView', 'DifferentialRevisionEditController' => 'DifferentialController', 'DifferentialRevisionListController' => 'DifferentialController', 'DifferentialRevisionUpdateHistoryView' => 'AphrontView', 'DifferentialRevisionViewController' => 'DifferentialController', 'DifferentialSubscribeController' => 'DifferentialController', + 'DiffusionBranchTableView' => 'DiffusionView', 'DiffusionBrowseController' => 'DiffusionController', 'DiffusionBrowseFileController' => 'DiffusionController', - 'DiffusionBrowseTableView' => 'AphrontView', - 'DiffusionCommitChangeTableView' => 'AphrontView', + 'DiffusionBrowseTableView' => 'DiffusionView', + 'DiffusionCommitChangeTableView' => 'DiffusionView', 'DiffusionCommitController' => 'DiffusionController', 'DiffusionController' => 'PhabricatorController', + 'DiffusionGitBranchQuery' => 'DiffusionBranchQuery', 'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery', 'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery', 'DiffusionGitHistoryQuery' => 'DiffusionHistoryQuery', 'DiffusionGitRequest' => 'DiffusionRequest', 'DiffusionHistoryController' => 'DiffusionController', - 'DiffusionHistoryTableView' => 'AphrontView', + 'DiffusionHistoryTableView' => 'DiffusionView', 'DiffusionHomeController' => 'DiffusionController', + 'DiffusionRepositoryController' => 'DiffusionController', + 'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery', + 'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery', + 'DiffusionView' => 'AphrontView', 'ManiphestController' => 'PhabricatorController', 'ManiphestDAO' => 'PhabricatorLiskDAO', 'ManiphestTask' => 'ManiphestDAO', 'ManiphestTaskDetailController' => 'ManiphestController', 'ManiphestTaskEditController' => 'ManiphestController', 'ManiphestTaskListController' => 'ManiphestController', 'ManiphestTaskListView' => 'AphrontView', 'ManiphestTaskSelectorSearchController' => 'ManiphestController', 'ManiphestTaskSummaryView' => 'AphrontView', 'ManiphestTransaction' => 'ManiphestDAO', 'ManiphestTransactionDetailView' => 'AphrontView', 'ManiphestTransactionListView' => 'AphrontView', 'ManiphestTransactionSaveController' => 'ManiphestController', 'Phabricator404Controller' => 'PhabricatorController', 'PhabricatorAuthController' => 'PhabricatorController', 'PhabricatorConduitAPIController' => 'PhabricatorConduitController', 'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO', 'PhabricatorConduitConsoleController' => 'PhabricatorConduitController', 'PhabricatorConduitController' => 'PhabricatorController', 'PhabricatorConduitDAO' => 'PhabricatorLiskDAO', 'PhabricatorConduitLogController' => 'PhabricatorConduitController', 'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO', 'PhabricatorController' => 'AphrontController', 'PhabricatorDaemon' => 'PhutilDaemon', 'PhabricatorDaemonConsoleController' => 'PhabricatorDaemonController', 'PhabricatorDaemonController' => 'PhabricatorController', 'PhabricatorDaemonTimelineConsoleController' => 'PhabricatorDaemonController', 'PhabricatorDaemonTimelineEventController' => 'PhabricatorDaemonController', 'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryController' => 'PhabricatorController', 'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController', 'PhabricatorDraft' => 'PhabricatorDraftDAO', 'PhabricatorDraftDAO' => 'PhabricatorLiskDAO', 'PhabricatorEmailLoginController' => 'PhabricatorAuthController', 'PhabricatorEmailTokenController' => 'PhabricatorAuthController', 'PhabricatorFile' => 'PhabricatorFileDAO', 'PhabricatorFileController' => 'PhabricatorController', 'PhabricatorFileDAO' => 'PhabricatorLiskDAO', 'PhabricatorFileListController' => 'PhabricatorFileController', 'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO', 'PhabricatorFileUploadController' => 'PhabricatorFileController', 'PhabricatorFileViewController' => 'PhabricatorFileController', 'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker', 'PhabricatorLiskDAO' => 'LiskDAO', 'PhabricatorLoginController' => 'PhabricatorAuthController', 'PhabricatorLogoutController' => 'PhabricatorAuthController', 'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter', 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter', 'PhabricatorMetaMTAController' => 'PhabricatorController', 'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO', 'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO', 'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO', 'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController', 'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController', 'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController', 'PhabricatorOAuthFailureView' => 'AphrontView', 'PhabricatorOAuthLoginController' => 'PhabricatorAuthController', 'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider', 'PhabricatorOAuthProviderGithub' => 'PhabricatorOAuthProvider', 'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController', 'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController', 'PhabricatorPHID' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController', 'PhabricatorPHIDController' => 'PhabricatorController', 'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO', 'PhabricatorPHIDListController' => 'PhabricatorPHIDController', 'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController', 'PhabricatorPHIDType' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDTypeEditController' => 'PhabricatorPHIDController', 'PhabricatorPHIDTypeListController' => 'PhabricatorPHIDController', 'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', 'PhabricatorPeopleListController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleController', 'PhabricatorProject' => 'PhabricatorProjectDAO', 'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO', 'PhabricatorProjectAffiliationEditController' => 'PhabricatorProjectController', 'PhabricatorProjectController' => 'PhabricatorController', 'PhabricatorProjectDAO' => 'PhabricatorLiskDAO', 'PhabricatorProjectEditController' => 'PhabricatorProjectController', 'PhabricatorProjectListController' => 'PhabricatorProjectController', 'PhabricatorProjectProfile' => 'PhabricatorProjectDAO', 'PhabricatorProjectProfileController' => 'PhabricatorProjectController', 'PhabricatorRemarkupRuleDifferential' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleManiphest' => 'PhutilRemarkupRule', 'PhabricatorRepository' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryCommit' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker', 'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryCommitDiscoveryDaemon' => 'PhabricatorRepositoryDaemon', 'PhabricatorRepositoryCommitMessageParserWorker' => 'PhabricatorRepositoryCommitParserWorker', 'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker', 'PhabricatorRepositoryCommitTaskDaemon' => 'PhabricatorRepositoryDaemon', 'PhabricatorRepositoryController' => 'PhabricatorController', 'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorRepositoryDaemon' => 'PhabricatorDaemon', 'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryGitHubPostReceiveController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryGitPullDaemon' => 'PhabricatorRepositoryDaemon', 'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 'PhabricatorSearchBaseController' => 'PhabricatorController', 'PhabricatorSearchController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchDAO' => 'PhabricatorLiskDAO', 'PhabricatorSearchDifferentialIndexer' => 'PhabricatorSearchDocumentIndexer', 'PhabricatorSearchDocument' => 'PhabricatorSearchDAO', 'PhabricatorSearchDocumentField' => 'PhabricatorSearchDAO', 'PhabricatorSearchDocumentRelationship' => 'PhabricatorSearchDAO', 'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer', 'PhabricatorSearchMySQLExecutor' => 'PhabricatorSearchExecutor', 'PhabricatorSearchQuery' => 'PhabricatorSearchDAO', 'PhabricatorStandardPageView' => 'AphrontPageView', 'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon', 'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO', 'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO', 'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO', 'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO', 'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController', 'PhabricatorUser' => 'PhabricatorUserDAO', 'PhabricatorUserDAO' => 'PhabricatorLiskDAO', 'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO', 'PhabricatorUserProfile' => 'PhabricatorUserDAO', 'PhabricatorUserSettingsController' => 'PhabricatorPeopleController', 'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO', 'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO', 'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO', 'PhabricatorXHProfController' => 'PhabricatorController', 'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController', 'PhabricatorXHProfProfileSymbolView' => 'AphrontView', 'PhabricatorXHProfProfileTopLevelView' => 'AphrontView', ), 'requires_interface' => array( ), )); diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index aa07c7cbe1..62468c8d98 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -1,314 +1,315 @@ getResourceURIMapRules() + array( '/' => array( '$' => 'PhabricatorDirectoryMainController', ), '/directory/' => array( 'item/$' => 'PhabricatorDirectoryItemListController', 'item/edit/(?:(?P\d+)/)?$' => 'PhabricatorDirectoryItemEditController', 'item/delete/(?P\d+)/' => 'PhabricatorDirectoryItemDeleteController', 'category/$' => 'PhabricatorDirectoryCategoryListController', 'category/edit/(?:(?P\d+)/)?$' => 'PhabricatorDirectoryCategoryEditController', 'category/delete/(?P\d+)/' => 'PhabricatorDirectoryCategoryDeleteController', ), '/file/' => array( '$' => 'PhabricatorFileListController', 'upload/$' => 'PhabricatorFileUploadController', '(?Pinfo)/(?P[^/]+)/' => 'PhabricatorFileViewController', '(?Pview)/(?P[^/]+)/' => 'PhabricatorFileViewController', '(?Pdownload)/(?P[^/]+)/' => 'PhabricatorFileViewController', ), '/phid/' => array( '$' => 'PhabricatorPHIDLookupController', 'list/$' => 'PhabricatorPHIDListController', 'type/$' => 'PhabricatorPHIDTypeListController', 'type/edit/(?:(?P\d+)/)?$' => 'PhabricatorPHIDTypeEditController', 'new/$' => 'PhabricatorPHIDAllocateController', ), '/people/' => array( '$' => 'PhabricatorPeopleListController', 'edit/(?:(?P\w+)/)?$' => 'PhabricatorPeopleEditController', ), '/p/(?P\w+)/$' => 'PhabricatorPeopleProfileController', '/profile/' => array( 'edit/$' => 'PhabricatorPeopleProfileEditController', ), '/conduit/' => array( '$' => 'PhabricatorConduitConsoleController', 'method/(?P[^/]+)$' => 'PhabricatorConduitConsoleController', 'log/$' => 'PhabricatorConduitLogController', ), '/api/(?P[^/]+)$' => 'PhabricatorConduitAPIController', '/D(?P\d+)' => 'DifferentialRevisionViewController', '/differential/' => array( '$' => 'DifferentialRevisionListController', 'filter/(?P\w+)/$' => 'DifferentialRevisionListController', 'diff/' => array( '(?P\d+)/$' => 'DifferentialDiffViewController', 'create/$' => 'DifferentialDiffCreateController', ), 'changeset/$' => 'DifferentialChangesetViewController', 'revision/edit/(?:(?P\d+)/)?$' => 'DifferentialRevisionEditController', 'comment/' => array( 'preview/(?P\d+)/$' => 'DifferentialCommentPreviewController', 'save/$' => 'DifferentialCommentSaveController', 'inline/' => array( 'preview/(?P\d+)/$' => 'DifferentialInlineCommentPreviewController', 'edit/(?P\d+)/$' => 'DifferentialInlineCommentEditController', ), ), 'attach/(?P\d+)/(?P\w+)/$' => 'DifferentialAttachController', 'subscribe/(?Padd|rem)/(?P\d+)/$' => 'DifferentialSubscribeController', ), '/typeahead/' => array( 'common/(?P\w+)/$' => 'PhabricatorTypeaheadCommonDatasourceController', ), '/mail/' => array( '$' => 'PhabricatorMetaMTAListController', 'send/$' => 'PhabricatorMetaMTASendController', 'view/(?P\d+)/$' => 'PhabricatorMetaMTAViewController', 'lists/$' => 'PhabricatorMetaMTAMailingListsController', 'lists/edit/(?:(?P\d+)/)?$' => 'PhabricatorMetaMTAMailingListEditController', ), '/login/' => array( '$' => 'PhabricatorLoginController', 'email/$' => 'PhabricatorEmailLoginController', 'etoken/(?P\w+)/$' => 'PhabricatorEmailTokenController', ), '/logout/$' => 'PhabricatorLogoutController', '/oauth/' => array( '(?Pgithub|facebook)/' => array( 'login/$' => 'PhabricatorOAuthLoginController', 'diagnose/$' => 'PhabricatorOAuthDiagnosticsController', 'unlink/$' => 'PhabricatorOAuthUnlinkController', ), ), '/xhprof/' => array( 'profile/(?P[^/]+)/$' => 'PhabricatorXHProfProfileController', ), '/~/' => 'DarkConsoleController', '/settings/' => array( '(?:page/(?P[^/]+)/)?$' => 'PhabricatorUserSettingsController', ), '/maniphest/' => array( '$' => 'ManiphestTaskListController', 'view/(?P\w+)/$' => 'ManiphestTaskListController', 'task/' => array( 'create/$' => 'ManiphestTaskEditController', 'edit/(?P\d+)/$' => 'ManiphestTaskEditController', ), 'transaction/' => array( 'save/' => 'ManiphestTransactionSaveController', ), 'select/search/$' => 'ManiphestTaskSelectorSearchController', ), '/T(?P\d+)$' => 'ManiphestTaskDetailController', '/github-post-receive/(?P\d+)/(?P[^/]+)/$' => 'PhabricatorRepositoryGitHubPostReceiveController', '/repository/' => array( '$' => 'PhabricatorRepositoryListController', 'create/$' => 'PhabricatorRepositoryCreateController', 'edit/(?P\d+)/(?:(?P\w+)?/)?$' => 'PhabricatorRepositoryEditController', 'delete/(?P\d+)/$' => 'PhabricatorRepositoryDeleteController', ), '/search/' => array( '$' => 'PhabricatorSearchController', '(?P\d+)/$' => 'PhabricatorSearchController', ), '/project/' => array( '$' => 'PhabricatorProjectListController', 'edit/(?:(?P\d+)/)?$' => 'PhabricatorProjectEditController', 'view/(?P\d+)/$' => 'PhabricatorProjectProfileController', 'affiliation/(?P\d+)/$' => 'PhabricatorProjectAffiliationEditController', ), '/r(?P[A-Z]+)(?P[a-z0-9]+)$' => 'DiffusionCommitController', '/diffusion/' => array( '$' => 'DiffusionHomeController', - '(?P[A-Z]+)' => array( - '/history/'. + '(?P[A-Z]+)/' => array( + '$' => 'DiffusionRepositoryController', + 'history/'. '(?P.*?)'. '(?:[;](?P[a-z0-9]+))?'. '$' => 'DiffusionHistoryController', - '/browse/'. + 'browse/'. '(?P.*?)'. '(?:[;](?P[a-z0-9]+))?'. '(?:[$](?P\d+))?'. '$' => 'DiffusionBrowseController', ), ), '/daemon/' => array( 'timeline/$' => 'PhabricatorDaemonTimelineConsoleController', 'timeline/(?P\d+)/$' => 'PhabricatorDaemonTimelineEventController', '$' => 'PhabricatorDaemonConsoleController', ), ); } protected function getResourceURIMapRules() { return array( '/res/' => array( '(?Ppkg/)?(?P[a-f0-9]{8})/(?P.+\.(?:css|js))$' => 'CelerityResourceController', ), ); } public function buildRequest() { $request = new AphrontRequest($this->getHost(), $this->getPath()); $request->setRequestData($_GET + $_POST); $request->setApplicationConfiguration($this); return $request; } public function handleException(Exception $ex) { $class = phutil_escape_html(get_class($ex)); $message = phutil_escape_html($ex->getMessage()); $content = '
'. '

Unhandled Exception "'.$class.'": '.$message.'

'. ''.phutil_escape_html((string)$ex).''. '
'; $user = $this->getRequest()->getUser(); if (!$user) { // If we hit an exception very early, we won't have a user. $user = new PhabricatorUser(); } $dialog = new AphrontDialogView(); $dialog ->setTitle('Exception!') ->setClass('aphront-exception-dialog') ->setUser($user) ->appendChild($content) ->addCancelButton('/'); $response = new AphrontDialogResponse(); $response->setDialog($dialog); return $response; } public function willSendResponse(AphrontResponse $response) { $request = $this->getRequest(); if ($response instanceof AphrontDialogResponse) { if (!$request->isAjax()) { $view = new PhabricatorStandardPageView(); $view->setRequest($request); $view->appendChild( '
'. $response->buildResponseString(). '
'); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } else { return id(new AphrontAjaxResponse()) ->setContent(array( 'dialog' => $response->buildResponseString(), )); } } else if ($response instanceof AphrontRedirectResponse) { if ($request->isAjax()) { return id(new AphrontAjaxResponse()) ->setContent( array( 'redirect' => $response->getURI(), )); } } else if ($response instanceof Aphront404Response) { $failure = new AphrontRequestFailureView(); $failure->setHeader('404 Not Found'); $failure->appendChild( '

The page you requested was not found.

'); $view = new PhabricatorStandardPageView(); $view->setTitle('404 Not Found'); $view->setRequest($this->getRequest()); $view->appendChild($failure); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); $response->setHTTPResponseCode(404); return $response; } return $response; } public function build404Controller() { return array(new Phabricator404Controller($this->getRequest()), array()); } } diff --git a/src/applications/diffusion/controller/base/DiffusionController.php b/src/applications/diffusion/controller/base/DiffusionController.php index 5f6bc85bdb..43cd81b269 100644 --- a/src/applications/diffusion/controller/base/DiffusionController.php +++ b/src/applications/diffusion/controller/base/DiffusionController.php @@ -1,47 +1,230 @@ diffusionRequest = DiffusionRequest::newFromAphrontRequestDictionary( $data); } public function setDiffusionRequest(DiffusionRequest $request) { $this->diffusionRequest = $request; return $this; } + protected function getDiffusionRequest() { + return $this->diffusionRequest; + } + public function buildStandardPageResponse($view, array $data) { $page = $this->buildStandardPageView(); $page->setApplicationName('Diffusion'); $page->setBaseURI('/diffusion/'); $page->setTitle(idx($data, 'title')); $page->setGlyph("\xE2\x89\x88"); $page->appendChild($view); $response = new AphrontWebpageResponse(); return $response->setContent($page->render()); } + final protected function buildSideNav($selected, $has_change_view) { + $nav = new AphrontSideNavView(); + + $navs = array( + 'history' => 'History View', + 'browse' => 'Browse View', + 'change' => 'Change View', + ); + + if (!$has_change_view) { + unset($navs['change']); + } + + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $callsign = $repository->getCallsign(); + + $branch_uri = $drequest->getBranchURIComponent($drequest->getBranch()); + $path_uri = $branch_uri.$drequest->getPath(); + + $commit_uri = null; + $raw_commit = $drequest->getRawCommit(); + if ($raw_commit) { + $commit_uri = ';'.$drequest->getCommitURIComponent($raw_commit); + } + + foreach ($navs as $uri => $name) { + $nav->addNavItem( + phutil_render_tag( + 'a', + array( + 'href' => "/diffusion/{$callsign}/{$uri}/{$path_uri}{$commit_uri}", + 'class' => + ($uri == $selected + ? 'aphront-side-nav-selected' + : null), + ), + $name)); + } + + return $nav; + } + + public function buildCrumbs(array $spec = array()) { + $drequest = $this->diffusionRequest; + + $crumbs = new AphrontCrumbsView(); + + $crumb_list = array(); + + $repository = $drequest->getRepository(); + if ($repository) { + $crumb_list[] = phutil_render_tag( + 'a', + array( + 'href' => '/diffusion/', + ), + 'Diffusion'); + } else { + $crumb_list[] = 'Diffusion'; + $crumbs->setCrumbs($crumb_list); + return $crumbs; + } + + $callsign = $repository->getCallsign(); + $repository_name = phutil_escape_html($repository->getName()).' Repository'; + + $branch_name = $drequest->getBranch(); + if ($branch_name) { + $repository_name .= ' ('.phutil_escape_html($branch_name).')'; + } + + $branch_uri = $drequest->getBranchURIComponent($drequest->getBranch()); + + if (empty($spec['view'])) { + $crumb_list[] = $repository_name; + $crumbs->setCrumbs($crumb_list); + return $crumbs; + } + + $crumb_list[] = phutil_render_tag( + 'a', + array( + 'href' => "/diffusion/{$callsign}/", + ), + $repository_name); + + + if (empty($spec['view'])) { + $crumbs->setCrumbs($crumb_list); + return $crumbs; + } + + $view = $spec['view']; + + switch ($view) { + case 'history': + $view_name = 'History'; + break; + case 'browse': + $view_name = 'Browse'; + break; + } + + $path = null; + if (isset($spec['path'])) { + $path = $drequest->getPath(); + } + + $view_root_uri = "/diffusion/{$callsign}/{$view}/{$branch_uri}"; + $jump_href = $view_root_uri; + + $view_tail_uri = null; + $raw_commit = $drequest->getRawCommit(); + if ($raw_commit) { + $view_tail_uri = ';'.$drequest->getCommitURIComponent($raw_commit); + } + + if (!strlen($path)) { + $crumb_list[] = $view_name; + } else { + + $crumb_list[] = phutil_render_tag( + 'a', + array( + 'href' => $view_root_uri.$view_tail_uri, + ), + $view_name); + + $path_parts = explode('/', $path); + do { + $last = array_pop($path_parts); + } while ($last == ''); + + $path_sections = array(); + $thus_far = ''; + foreach ($path_parts as $path_part) { + $thus_far .= $path_part.'/'; + $path_sections[] = phutil_render_tag( + 'a', + array( + 'href' => $view_root_uri.$thus_far.$view_tail_uri, + ), + phutil_escape_html($path_part)); + } + + $path_sections[] = phutil_escape_html($last); + $path_sections = '/'.implode('/', $path_sections); + + $jump_href = $view_root_uri.$thus_far.$last; + + $crumb_list[] = $path_sections; + } + + $last_crumb = array_pop($crumb_list); + + if ($raw_commit) { + $commit_link = DiffusionView::linkCommit( + $repository, + $raw_commit); + $jump_link = phutil_render_tag( + 'a', + array( + 'href' => $jump_href, + ), + 'Jump to HEAD'); + $last_crumb .= " @ {$commit_link} ({$jump_link})"; + } else { + $last_crumb .= " @ HEAD"; + } + + $crumb_list[] = $last_crumb; + + + $crumbs->setCrumbs($crumb_list); + + return $crumbs; + } + } diff --git a/src/applications/diffusion/controller/base/__init__.php b/src/applications/diffusion/controller/base/__init__.php index c68cb98500..d3298a471f 100644 --- a/src/applications/diffusion/controller/base/__init__.php +++ b/src/applications/diffusion/controller/base/__init__.php @@ -1,16 +1,20 @@ diffusionRequest; $browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest); $results = $browse_query->loadPaths(); $content = array(); + $content[] = $this->buildCrumbs( + array( + 'branch' => true, + 'path' => true, + 'view' => 'browse', + )); + if (!$results) { switch ($browse_query->getReasonForEmptyResultSet()) { case DiffusionBrowseQuery::REASON_IS_NONEXISTENT: $title = 'Path Does Not Exist'; // TODO: Under git, this error message should be more specific. It // may exist on some other branch. $body = "This path does not exist anywhere."; $severity = AphrontErrorView::SEVERITY_ERROR; break; case DiffusionBrowseQuery::REASON_IS_DELETED: // TODO: Format all these commits into nice VCS-agnostic links. $commit = $drequest->getCommit(); $deleted = $browse_query->getDeletedAtCommit(); $existed = $browse_query->getExistedAtCommit(); $title = 'Path Was Deleted'; $body = "This path does not exist at {$commit}. It was deleted in ". "{$deleted} and last existed at {$existed}."; $severity = AphrontErrorView::SEVERITY_WARNING; break; case DiffusionBrowseQuery::REASON_IS_FILE: $controller = new DiffusionBrowseFileController($this->getRequest()); $controller->setDiffusionRequest($drequest); return $this->delegateToController($controller); break; default: throw new Exception("Unknown failure reason!"); } $error_view = new AphrontErrorView(); $error_view->setSeverity($severity); $error_view->setTitle($title); $error_view->appendChild('

'.$body.'

'); $content[] = $error_view; } else { $browse_table = new DiffusionBrowseTableView(); $browse_table->setDiffusionRequest($drequest); $browse_table->setPaths($results); $browse_panel = new AphrontPanelView(); - $browse_panel->setHeader($drequest->getPath()); $browse_panel->appendChild($browse_table); $content[] = $browse_panel; - - // TODO: Branch table } - // TODO: Crumbs - // TODO: Side nav + $nav = $this->buildSideNav('browse', false); + $nav->appendChild($content); return $this->buildStandardPageResponse( - $content, + $nav, array( 'title' => basename($drequest->getPath()), )); } } diff --git a/src/applications/diffusion/controller/file/DiffusionBrowseFileController.php b/src/applications/diffusion/controller/file/DiffusionBrowseFileController.php index 7dec1a0bae..148766f182 100644 --- a/src/applications/diffusion/controller/file/DiffusionBrowseFileController.php +++ b/src/applications/diffusion/controller/file/DiffusionBrowseFileController.php @@ -1,40 +1,55 @@ buildCrumbs( + array( + 'branch' => true, + 'path' => true, + 'view' => 'browse', + )); + $file_query = DiffusionFileContentQuery::newFromDiffusionRequest( $this->diffusionRequest); $file_content = $file_query->loadFileContent(); $corpus = phutil_render_tag( 'textarea', array( + 'style' => 'margin: 1em 2em; width: 90%; height: 80em;', ), phutil_escape_html($file_content->getCorpus())); + $content[] = $corpus; + // TODO: blame, color, line numbers, highlighting, etc etc + $nav = $this->buildSideNav('browse', true); + $nav->appendChild($content); + return $this->buildStandardPageResponse( - $corpus, + $nav, array( 'title' => 'Browse', )); } } diff --git a/src/applications/diffusion/controller/history/DiffusionHistoryController.php b/src/applications/diffusion/controller/history/DiffusionHistoryController.php index 3f5ee68690..f64e3e97e3 100644 --- a/src/applications/diffusion/controller/history/DiffusionHistoryController.php +++ b/src/applications/diffusion/controller/history/DiffusionHistoryController.php @@ -1,51 +1,60 @@ diffusionRequest; $history_query = DiffusionHistoryQuery::newFromDiffusionRequest( $drequest); $history = $history_query->loadHistory(); $content = array(); + $content[] = $this->buildCrumbs( + array( + 'branch' => true, + 'path' => true, + 'view' => 'history', + )); + $history_table = new DiffusionHistoryTableView(); $history_table->setDiffusionRequest($drequest); $history_table->setHistory($history); $history_panel = new AphrontPanelView(); - $history_panel->setHeader($drequest->getPath()); $history_panel->appendChild($history_table); $content[] = $history_panel; - // TODO: Crumbs - // TODO: Side nav + // TODO: Sometimes we do have a change view, we need to look at the most + // recent history entry to figure it out. + + $nav = $this->buildSideNav('history', false); + $nav->appendChild($content); return $this->buildStandardPageResponse( - $content, + $nav, array( 'title' => 'history', )); } } diff --git a/src/applications/diffusion/controller/home/DiffusionHomeController.php b/src/applications/diffusion/controller/home/DiffusionHomeController.php index 306719b2d9..9989cb3b66 100644 --- a/src/applications/diffusion/controller/home/DiffusionHomeController.php +++ b/src/applications/diffusion/controller/home/DiffusionHomeController.php @@ -1,91 +1,96 @@ loadAll(); $commit = new PhabricatorRepositoryCommit(); $conn_r = $commit->establishConnection('r'); // TODO: Both these queries are basically bogus and have total trash for // query plans, and don't return the right results. Build a cache instead. // These are just pulling data with approximately the right look to it. $commits = $commit->loadAllWhere( '1 = 1 GROUP BY repositoryPHID'); $commits = mpull($commits, null, 'getRepositoryPHID'); $commit_counts = queryfx_all( $conn_r, 'SELECT repositoryPHID, count(*) N FROM %T GROUP BY repositoryPHID', $commit->getTableName()); $commit_counts = ipull($commit_counts, 'N', 'repositoryPHID'); $rows = array(); foreach ($repositories as $repository) { $phid = $repository->getPHID(); $commit = idx($commits, $phid); $rows[] = array( phutil_render_tag( 'a', array( - 'href' => '#', // TODO: Link + 'href' => '/diffusion/'.$repository->getCallsign().'/', ), phutil_escape_html($repository->getName())), $repository->getVersionControlSystem(), idx($commit_counts, $phid, 0), $commit ? $commit->getCommitIdentifier() : null, // TODO: Link/format $commit ? phabricator_format_timestamp($commit->getEpoch()) : null, ); } $table = new AphrontTableView($rows); $table->setHeaders( array( 'Repository', 'VCS', 'Size', 'Last', 'Committed', )); $table->setColumnClasses( array( 'wide', )); $panel = new AphrontPanelView(); $panel->setHeader('Browse Repositories'); $panel->appendChild($table); + $crumbs = $this->buildCrumbs(); + return $this->buildStandardPageResponse( - $panel, + array( + $crumbs, + $panel, + ), array( 'title' => 'Diffusion', )); } } diff --git a/src/applications/diffusion/controller/repository/DiffusionRepositoryController.php b/src/applications/diffusion/controller/repository/DiffusionRepositoryController.php new file mode 100644 index 0000000000..7b14ccdd24 --- /dev/null +++ b/src/applications/diffusion/controller/repository/DiffusionRepositoryController.php @@ -0,0 +1,79 @@ +diffusionRequest; + + $content = array(); + + $crumbs = $this->buildCrumbs(); + $content[] = $crumbs; + + $history_query = DiffusionHistoryQuery::newFromDiffusionRequest( + $drequest); + $history_query->setLimit(15); + + $history = $history_query->loadHistory(); + $history_table = new DiffusionHistoryTableView(); + $history_table->setDiffusionRequest($drequest); + $history_table->setHistory($history); + + $panel = new AphrontPanelView(); + $panel->setHeader('Recent Commits'); + $panel->appendChild($history_table); + + $content[] = $panel; + + $browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest); + $results = $browse_query->loadPaths(); + + $browse_table = new DiffusionBrowseTableView(); + $browse_table->setDiffusionRequest($drequest); + $browse_table->setPaths($results); + + $browse_panel = new AphrontPanelView(); + $browse_panel->setHeader('Browse Repository'); + $browse_panel->appendChild($browse_table); + + $content[] = $browse_panel; + + if ($drequest->getBranch() !== null) { + $branch_query = DiffusionBranchQuery::newFromDiffusionRequest($drequest); + $branches = $branch_query->loadBranches(); + + $branch_table = new DiffusionBranchTableView(); + $branch_table->setDiffusionRequest($drequest); + $branch_table->setBranches($branches); + + $branch_panel = new AphrontPanelView(); + $branch_panel->setHeader('Branches'); + $branch_panel->appendChild($branch_table); + + $content[] = $branch_panel; + } + + return $this->buildStandardPageResponse( + $content, + array( + 'title' => 'Diffusion', + )); + } + +} diff --git a/src/applications/diffusion/controller/repository/__init__.php b/src/applications/diffusion/controller/repository/__init__.php new file mode 100644 index 0000000000..6f5e15daaa --- /dev/null +++ b/src/applications/diffusion/controller/repository/__init__.php @@ -0,0 +1,19 @@ +commit = $commit; + public function setName($name) { + $this->name = $name; return $this; } - final public function getCommit() { - return $this->commit; + public function getName() { + return $this->name; } + public function setHeadCommitIdentifier($head_commit_identifier) { + $this->headCommitIdentifier = $head_commit_identifier; + return $this; + } + + public function getHeadCommitIdentifier() { + return $this->headCommitIdentifier; + } } diff --git a/src/applications/diffusion/query/browse/base/__init__.php b/src/applications/diffusion/data/branch/__init__.php similarity index 52% copy from src/applications/diffusion/query/browse/base/__init__.php copy to src/applications/diffusion/data/branch/__init__.php index 9bc1b90505..43da675b4a 100644 --- a/src/applications/diffusion/query/browse/base/__init__.php +++ b/src/applications/diffusion/data/branch/__init__.php @@ -1,12 +1,10 @@ commit = $commit; + final public function setCommitIdentifier($commit) { + $this->commitIdentifier = $commit; return $this; } - final public function getCommit() { - return $this->commit; + final public function getCommitIdentifier() { + return $this->commitIdentifier; } } diff --git a/src/applications/diffusion/query/history/base/DiffusionHistoryQuery.php b/src/applications/diffusion/query/branch/base/DiffusionBranchQuery.php similarity index 87% copy from src/applications/diffusion/query/history/base/DiffusionHistoryQuery.php copy to src/applications/diffusion/query/branch/base/DiffusionBranchQuery.php index ae20419d7a..811916f0cd 100644 --- a/src/applications/diffusion/query/history/base/DiffusionHistoryQuery.php +++ b/src/applications/diffusion/query/branch/base/DiffusionBranchQuery.php @@ -1,57 +1,57 @@ } final public static function newFromDiffusionRequest( DiffusionRequest $request) { $repository = $request->getRepository(); switch ($repository->getVersionControlSystem()) { - case 'git': - $class = 'DiffusionGitHistoryQuery'; + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + $class = 'DiffusionGitBranchQuery'; break; default: throw new Exception("Unsupported VCS!"); } PhutilSymbolLoader::loadClass($class); $query = new $class(); $query->request = $request; return $query; } final protected function getRequest() { return $this->request; } - final public function loadHistory() { + final public function loadBranches() { return $this->executeQuery(); } abstract protected function executeQuery(); } diff --git a/src/applications/diffusion/query/browse/base/__init__.php b/src/applications/diffusion/query/branch/base/__init__.php similarity index 51% copy from src/applications/diffusion/query/browse/base/__init__.php copy to src/applications/diffusion/query/branch/base/__init__.php index 9bc1b90505..a03fa7e6a1 100644 --- a/src/applications/diffusion/query/browse/base/__init__.php +++ b/src/applications/diffusion/query/branch/base/__init__.php @@ -1,12 +1,14 @@ getRequest(); - $repository = $drequest->getRepository(); + $path = $drequest->getPath(); $commit = $drequest->getCommit(); $local_path = $repository->getDetail('local-path'); - $git = $drequest->getPathToGitBinary(); list($stdout) = execx( - '(cd %s && %s log '. - '-n %d '. - '--skip=%d '. - '-M '. - '-C '. - '-B '. - '--find-copies-harder '. - '--raw '. - '-t '. - '--abbrev=40 '. - '--pretty=format:%%x1c%%H%%x1d '. - '%s -- %s)', - $local_path, - $git, - $limit = 100, - $offset = 0, - $commit, - $path); - - $commits = explode("\x1c", $stdout); - array_shift($commits); // \x1c character is first, remove empty record - - $history = array(); - foreach ($commits as $commit) { - list($hash, $raw) = explode("\x1d", $commit); - - $item = new DiffusionPathChange(); - $item->setCommit($hash); - $history[] = $item; + '(cd %s && git branch --verbose --no-abbrev)', + $local_path); + + $branches = array(); + + $lines = array_filter(explode("\n", $stdout)); + foreach ($lines as $line) { + $matches = null; + if (!preg_match('/^[ *] (\S+)\s+([a-z0-9]{40}) /', $line, $matches)) { + throw new Exception("Failed to parse {$line}!"); + } + $branch = new DiffusionBranchInformation(); + $branch->setName($matches[1]); + $branch->setHeadCommitIdentifier($matches[2]); + + $branches[] = $branch; } - return $history; + return $branches; } } diff --git a/src/applications/diffusion/query/branch/git/__init__.php b/src/applications/diffusion/query/branch/git/__init__.php new file mode 100644 index 0000000000..025d906a8b --- /dev/null +++ b/src/applications/diffusion/query/branch/git/__init__.php @@ -0,0 +1,15 @@ + } final public static function newFromDiffusionRequest( DiffusionRequest $request) { $repository = $request->getRepository(); switch ($repository->getVersionControlSystem()) { - case 'git': + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: // TODO: Verify local-path? $class = 'DiffusionGitBrowseQuery'; break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + $class = 'DiffusionSvnBrowseQuery'; + break; default: throw new Exception("Unsupported VCS!"); } PhutilSymbolLoader::loadClass($class); $query = new $class(); $query->request = $request; return $query; } final protected function getRequest() { return $this->request; } final public function getReasonForEmptyResultSet() { return $this->reason; } final public function getExistedAtCommit() { return $this->existedAtCommit; } final public function getDeletedAtCommit() { return $this->deletedAtCommit; } final public function loadPaths() { return $this->executeQuery(); } abstract protected function executeQuery(); } diff --git a/src/applications/diffusion/query/browse/base/__init__.php b/src/applications/diffusion/query/browse/base/__init__.php index 9bc1b90505..3e2ddb571f 100644 --- a/src/applications/diffusion/query/browse/base/__init__.php +++ b/src/applications/diffusion/query/browse/base/__init__.php @@ -1,12 +1,14 @@ getRequest(); + $repository = $drequest->getRepository(); + + $path = $drequest->getPath(); + $commit = $drequest->getCommit(); + + $path_normal = '/'.trim($path, '/'); + + $conn_r = $repository->establishConnection('r'); + + $paths = queryfx_all( + $conn_r, + 'SELECT id, path FROM %T WHERE path IN (%Ls)', + PhabricatorRepository::TABLE_PATH, + array($path_normal)); + $paths = ipull($paths, 'id', 'path'); + $path_id = $paths[$path_normal]; + + $index = queryfx_all( + $conn_r, + 'SELECT pathID, max(svnCommit) maxCommit FROM %T WHERE + repositoryID = %d AND parentID = %d + %Q GROUP BY pathID', + PhabricatorRepository::TABLE_FILESYSTEM, + $repository->getID(), + $path_id, + ''); + + if (!$index) { + // TODO: ! + return false; + } + + $sql = array(); + foreach ($index as $row) { + $sql[] = '('.(int)$row['pathID'].', '.(int)$row['maxCommit'].')'; + } + + $browse = queryfx_all( + $conn_r, + 'SELECT *, p.path pathName + FROM %T f JOIN %T p ON f.pathID = p.id + WHERE repositoryID = %d + AND parentID = %d + AND existed = 1 + AND (pathID, svnCommit) in (%Q) + ORDER BY pathName', + PhabricatorRepository::TABLE_FILESYSTEM, + PhabricatorRepository::TABLE_PATH, + $repository->getID(), + $path_id, + implode(', ', $sql)); + + $results = array(); + foreach ($browse as $file) { + + $file_path = $file['pathName']; + $file_path = ltrim(substr($file_path, strlen($path_normal)), '/'); + + $result = new DiffusionRepositoryPath(); + $result->setPath($file_path); +// $result->setHash($hash); + $result->setFileType($file['fileType']); +// $result->setFileSize($size); + + $results[] = $result; + } + + return $results; + } + +} diff --git a/src/applications/diffusion/query/browse/svn/__init__.php b/src/applications/diffusion/query/browse/svn/__init__.php new file mode 100644 index 0000000000..2f3932f527 --- /dev/null +++ b/src/applications/diffusion/query/browse/svn/__init__.php @@ -0,0 +1,17 @@ + } final public static function newFromDiffusionRequest( DiffusionRequest $request) { $repository = $request->getRepository(); switch ($repository->getVersionControlSystem()) { - case 'git': + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $class = 'DiffusionGitHistoryQuery'; break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + $class = 'DiffusionSvnHistoryQuery'; + break; default: throw new Exception("Unsupported VCS!"); } PhutilSymbolLoader::loadClass($class); $query = new $class(); $query->request = $request; return $query; } final protected function getRequest() { return $this->request; } final public function loadHistory() { return $this->executeQuery(); } + final public function setLimit($limit) { + $this->limit = $limit; + return $this; + } + + final public function getLimit() { + return $this->limit; + } + abstract protected function executeQuery(); } diff --git a/src/applications/diffusion/query/history/base/__init__.php b/src/applications/diffusion/query/history/base/__init__.php index df26090017..010bb2a494 100644 --- a/src/applications/diffusion/query/history/base/__init__.php +++ b/src/applications/diffusion/query/history/base/__init__.php @@ -1,12 +1,14 @@ getRequest(); $repository = $drequest->getRepository(); $path = $drequest->getPath(); $commit = $drequest->getCommit(); $local_path = $repository->getDetail('local-path'); $git = $drequest->getPathToGitBinary(); list($stdout) = execx( '(cd %s && %s log '. - '-n %d '. '--skip=%d '. + '-n %d '. '-M '. '-C '. '-B '. '--find-copies-harder '. '--raw '. '-t '. '--abbrev=40 '. '--pretty=format:%%x1c%%H%%x1d '. '%s -- %s)', $local_path, $git, - $limit = 100, $offset = 0, + $this->getLimit(), $commit, $path); $commits = explode("\x1c", $stdout); array_shift($commits); // \x1c character is first, remove empty record $history = array(); foreach ($commits as $commit) { list($hash, $raw) = explode("\x1d", $commit); $item = new DiffusionPathChange(); - $item->setCommit($hash); + $item->setCommitIdentifier($hash); $history[] = $item; } return $history; } } diff --git a/src/applications/diffusion/query/history/git/DiffusionGitHistoryQuery.php b/src/applications/diffusion/query/history/svn/DiffusionSvnHistoryQuery.php similarity index 53% copy from src/applications/diffusion/query/history/git/DiffusionGitHistoryQuery.php copy to src/applications/diffusion/query/history/svn/DiffusionSvnHistoryQuery.php index 7e6cbd7e8a..a95b588c8a 100644 --- a/src/applications/diffusion/query/history/git/DiffusionGitHistoryQuery.php +++ b/src/applications/diffusion/query/history/svn/DiffusionSvnHistoryQuery.php @@ -1,66 +1,60 @@ getRequest(); $repository = $drequest->getRepository(); $path = $drequest->getPath(); $commit = $drequest->getCommit(); - $local_path = $repository->getDetail('local-path'); - $git = $drequest->getPathToGitBinary(); - - list($stdout) = execx( - '(cd %s && %s log '. - '-n %d '. - '--skip=%d '. - '-M '. - '-C '. - '-B '. - '--find-copies-harder '. - '--raw '. - '-t '. - '--abbrev=40 '. - '--pretty=format:%%x1c%%H%%x1d '. - '%s -- %s)', - $local_path, - $git, - $limit = 100, - $offset = 0, - $commit, - $path); - - $commits = explode("\x1c", $stdout); - array_shift($commits); // \x1c character is first, remove empty record + $conn_r = $repository->establishConnection('r'); + + $paths = queryfx_all( + $conn_r, + 'SELECT id, path FROM %T WHERE path IN (%Ls)', + PhabricatorRepository::TABLE_PATH, + array('/'.trim($path, '/'))); + $paths = ipull($paths, 'id', 'path'); + $path_id = $paths['/'.trim($path, '/')]; + + $history_data = queryfx_all( + $conn_r, + 'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d + AND commitSequence <= %d + ORDER BY commitSequence DESC + LIMIT %d', + PhabricatorRepository::TABLE_PATHCHANGE, + $repository->getID(), + $path_id, + $commit ? $commit : 0x7FFFFFFF, + $this->getLimit()); $history = array(); - foreach ($commits as $commit) { - list($hash, $raw) = explode("\x1d", $commit); - + foreach ($history_data as $row) { $item = new DiffusionPathChange(); - $item->setCommit($hash); + $item->setCommitIdentifier($row['commitID']); $history[] = $item; } return $history; } } diff --git a/src/applications/diffusion/query/history/svn/__init__.php b/src/applications/diffusion/query/history/svn/__init__.php new file mode 100644 index 0000000000..c82a795dc9 --- /dev/null +++ b/src/applications/diffusion/query/history/svn/__init__.php @@ -0,0 +1,17 @@ + } final public static function newFromAphrontRequestDictionary(array $data) { $vcs = null; $repository = null; $callsign = idx($data, 'callsign'); if ($callsign) { $repository = id(new PhabricatorRepository())->loadOneWhere( 'callsign = %s', $callsign); if (!$repository) { throw new Exception("No such repository '{$callsign}'."); } $vcs = $repository->getVersionControlSystem(); } switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $class = 'DiffusionGitRequest'; break; default: $class = 'DiffusionRequest'; break; } $object = new $class(); $object->callsign = $callsign; $object->repository = $repository; $object->line = idx($data, 'line'); $object->commit = idx($data, 'commit'); $object->path = idx($data, 'path'); $object->initializeFromAphrontRequestDictionary(); return $object; } protected function initializeFromAphrontRequestDictionary() { } protected function parsePath($path) { $this->path = $path; } public function getRepository() { return $this->repository; } public function getCallsign() { return $this->callsign; } public function getPath() { return $this->path; } public function getLine() { return $this->line; } public function getCommit() { return $this->commit; } public function getBranch() { return $this->branch; } + final public function getRawCommit() { + return $this->commit; + } + + public function getCommitURIComponent($commit) { + return $commit; + } + + public function getBranchURIComponent($branch) { + return $branch; + } + } diff --git a/src/applications/diffusion/request/git/DiffusionGitRequest.php b/src/applications/diffusion/request/git/DiffusionGitRequest.php index ab00541dc0..f4c314eb94 100644 --- a/src/applications/diffusion/request/git/DiffusionGitRequest.php +++ b/src/applications/diffusion/request/git/DiffusionGitRequest.php @@ -1,107 +1,119 @@ path; $parts = explode('/', $path); $branch = array_shift($parts); $this->branch = $this->decodeBranchName($branch); + foreach ($parts as $key => $part) { + // Prevent any hyjinx since we're ultimately shipping this to the + // filesystem under a lot of git workflows. + if ($part == '..') { + unset($parts[$key]); + } + } + $this->path = implode('/', $parts); if ($this->repository) { $local_path = $this->repository->getDetail('local-path'); $git = $this->getPathToGitBinary(); // TODO: This is not terribly efficient and does not produce terribly // good error messages, but it seems better to put error handling code // here than to try to do it in every query. $branch = $this->getBranch(); execx( '(cd %s && %s rev-parse --verify %s)', $local_path, $git, $branch); if ($this->commit) { execx( '(cd %s && %s rev-parse --verify %s)', $local_path, $git, $this->commit); list($contains) = execx( '(cd %s && %s branch --contains %s)', $local_path, $git, $this->commit); $contains = array_filter(explode("\n", $contains)); $found = false; foreach ($contains as $containing_branch) { $containing_branch = trim($containing_branch, "* \n"); if ($containing_branch == $branch) { $found = true; break; } } if (!$found) { throw new Exception( "Commit does not exist on this branch!"); } } } } public function getPathToGitBinary() { return PhabricatorEnv::getEnvConfig('git.path'); } public function getBranch() { if ($this->branch) { return $this->branch; } if ($this->repository) { return $this->repository->getDetail('default-branch', 'master'); } throw new Exception("Unable to determine branch!"); } public function getCommit() { if ($this->commit) { return $this->commit; } return $this->getBranch(); } + public function getBranchURIComponent($branch) { + return $this->encodeBranchName($branch).'/'; + } + private function decodeBranchName($branch) { return str_replace(':', '/', $branch); } private function encodeBranchName($branch) { return str_replace('/', ':', $branch); } } diff --git a/src/applications/diffusion/view/base/DiffusionView.php b/src/applications/diffusion/view/base/DiffusionView.php new file mode 100644 index 0000000000..72783cccf6 --- /dev/null +++ b/src/applications/diffusion/view/base/DiffusionView.php @@ -0,0 +1,109 @@ +diffusionRequest = $request; + return $this; + } + + final public function getDiffusionRequest() { + return $this->diffusionRequest; + } + + final public function linkHistory($path) { + $drequest = $this->getDiffusionRequest(); + + if ($drequest->getRawCommit()) { + $commit = ';'.$drequest->getCommitURIComponent($drequest->getRawCommit()); + } else { + $commit = null; + } + + $repository = $drequest->getRepository(); + $callsign = $repository->getCallsign(); + + $branch = $drequest->getBranchURIComponent($drequest->getBranch()); + $path = $branch.$path; + + $text = 'History'; + + return phutil_render_tag( + 'a', + array( + 'href' => "/diffusion/{$callsign}/history/{$path}{$commit}", + ), + $text); + } + + final public function linkBrowse($path, array $details = array()) { + $drequest = $this->getDiffusionRequest(); + + $raw_commit = idx($details, 'commit', $drequest->getRawCommit()); + if ($raw_commit) { + $commit = ';'.$drequest->getCommitURIComponent($raw_commit); + } else { + $commit = null; + } + + $repository = $drequest->getRepository(); + $callsign = $repository->getCallsign(); + + $branch = $drequest->getBranchURIComponent($drequest->getBranch()); + $path = $branch.$path; + + if (isset($details['text'])) { + $text = phutil_escape_html($details['text']); + } else { + $text = 'Browse'; + } + + return phutil_render_tag( + 'a', + array( + 'href' => "/diffusion/{$callsign}/browse/{$path}{$commit}", + ), + $text); + } + + final public static function linkCommit($repository, $commit) { + + switch ($repository->getVersionControlSystem()) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + $commit_name = substr($commit, 0, 7); + break; + default: + $commit_name = $commit; + break; + } + + $callsign = $repository->getCallsign(); + $commit_name = "r{$callsign}{$commit_name}"; + + return phutil_render_tag( + 'a', + array( + 'href' => "/r{$callsign}{$commit}", + ), + $commit_name); + } + +} diff --git a/src/applications/diffusion/view/historytable/__init__.php b/src/applications/diffusion/view/base/__init__.php similarity index 53% copy from src/applications/diffusion/view/historytable/__init__.php copy to src/applications/diffusion/view/base/__init__.php index e53ae0b476..4a1c56d3a7 100644 --- a/src/applications/diffusion/view/historytable/__init__.php +++ b/src/applications/diffusion/view/base/__init__.php @@ -1,15 +1,16 @@ request = $request; - return $this; - } - - public function setPathChanges(array $path_changes) { - $this->pathChanges = $path_changes; + public function setBranches(array $branches) { + $this->branches = $branches; return $this; } public function render() { + $drequest = $this->getDiffusionRequest(); + $current_branch = $drequest->getBranch(); + $rows = array(); - foreach ($this->pathChanges as $change) { + $rowc = array(); + foreach ($this->branches as $branch) { $rows[] = array( - 'browse', - 'whatever', - $change->getPath(), // TODO: link + phutil_escape_html($branch->getName()), // TODO: link + self::linkCommit( + $drequest->getRepository(), + $branch->getHeadCommitIdentifier()), // TODO: etc etc ); + if ($branch->getName() == $current_branch) { + $rowc[] = 'highlighted'; + } else { + $rowc[] = null; + } } $view = new AphrontTableView($rows); $view->setHeaders( array( - 'History', - 'Change', - 'Path', + 'Branch', + 'Head', )); $view->setColumnClasses( array( - '', - '', 'wide', )); - $view->setNoDataString('This change has not been fully parsed yet.'); - + $view->setRowClasses($rowc); return $view->render(); } } diff --git a/src/applications/diffusion/view/historytable/__init__.php b/src/applications/diffusion/view/branchtable/__init__.php similarity index 61% copy from src/applications/diffusion/view/historytable/__init__.php copy to src/applications/diffusion/view/branchtable/__init__.php index e53ae0b476..0fa1d5c6e5 100644 --- a/src/applications/diffusion/view/historytable/__init__.php +++ b/src/applications/diffusion/view/branchtable/__init__.php @@ -1,15 +1,15 @@ request = $request; - return $this; - } - public function setPaths(array $paths) { $this->paths = $paths; return $this; } public function render() { + $request = $this->getDiffusionRequest(); + + $base_path = trim($request->getPath(), '/'); + if ($base_path) { + $base_path = $base_path.'/'; + } + $rows = array(); foreach ($this->paths as $path) { + + if ($path->getFileType() == DifferentialChangeType::FILE_DIRECTORY) { + $browse_text = $path->getPath().'/'; + $dir_slash = '/'; + } else { + $browse_text = $path->getPath(); + $dir_slash = null; + } + $rows[] = array( - phutil_escape_html($path->getPath()), // TODO: link - // TODO: etc etc + $this->linkHistory($base_path.$path->getPath().$dir_slash), + $this->linkBrowse( + $base_path.$path->getPath().$dir_slash, + array( + 'text' => $browse_text, + )), ); } $view = new AphrontTableView($rows); $view->setHeaders( array( + 'History', 'Path', )); + $view->setColumnClasses( + array( + '', + 'wide pri', + )); return $view->render(); } } diff --git a/src/applications/diffusion/view/browsetable/__init__.php b/src/applications/diffusion/view/browsetable/__init__.php index e6361bcfc8..12e1f538ef 100644 --- a/src/applications/diffusion/view/browsetable/__init__.php +++ b/src/applications/diffusion/view/browsetable/__init__.php @@ -1,15 +1,14 @@ request = $request; - return $this; - } - public function setPathChanges(array $path_changes) { $this->pathChanges = $path_changes; return $this; } public function render() { $rows = array(); foreach ($this->pathChanges as $change) { $rows[] = array( 'browse', 'whatever', $change->getPath(), // TODO: link // TODO: etc etc ); } $view = new AphrontTableView($rows); $view->setHeaders( array( 'History', 'Change', 'Path', )); $view->setColumnClasses( array( '', '', 'wide', )); $view->setNoDataString('This change has not been fully parsed yet.'); return $view->render(); } } diff --git a/src/applications/diffusion/view/commitchangetable/__init__.php b/src/applications/diffusion/view/commitchangetable/__init__.php index 38dfa02082..a8d32cb9ac 100644 --- a/src/applications/diffusion/view/commitchangetable/__init__.php +++ b/src/applications/diffusion/view/commitchangetable/__init__.php @@ -1,13 +1,13 @@ request = $request; - return $this; - } - public function setHistory(array $history) { $this->history = $history; return $this; } public function render() { + $drequest = $this->getDiffusionRequest(); + $rows = array(); foreach ($this->history as $history) { $rows[] = array( - phutil_escape_html($history->getCommit()), // TODO: link + $this->linkBrowse( + $drequest->getPath(), + array( + 'commit' => $history->getCommitIdentifier(), + )), + self::linkCommit( + $drequest->getRepository(), + $history->getCommitIdentifier()), + '?', + '?', + '', + '', // TODO: etc etc ); } $view = new AphrontTableView($rows); $view->setHeaders( array( + 'Browse', 'Commit', + 'Change', + 'Date', + 'Author', + 'Details', + )); + $view->setColumnClasses( + array( + '', + 'n', + '', + '', + '', + 'wide wrap', )); return $view->render(); } } diff --git a/src/applications/diffusion/view/historytable/__init__.php b/src/applications/diffusion/view/historytable/__init__.php index e53ae0b476..f6e0c07164 100644 --- a/src/applications/diffusion/view/historytable/__init__.php +++ b/src/applications/diffusion/view/historytable/__init__.php @@ -1,15 +1,13 @@ getDetail('remote-uri'); $svn_commit = $commit->getCommitIdentifier(); $callsign = $repository->getCallsign(); echo "Parsing r{$callsign}{$svn_commit}...\n"; // Pull the top-level path changes out of "svn log". This is pretty // straightforward; just parse the XML log. list($xml) = execx( 'svn log --verbose --xml --limit 1 --non-interactive %s@%d', $uri, $svn_commit); $log = new SimpleXMLElement($xml); $entry = $log->logentry[0]; if (!$entry->paths) { // TODO: Explicitly mark this commit as broken elsewhere? This isn't // supposed to happen but we have some cases like rE27 and rG935 in the // Facebook repositories where things got all clowned up. return; } $raw_paths = array(); foreach ($entry->paths->path as $path) { $name = trim((string)$path); $raw_paths[$name] = array( 'rawPath' => $name, 'rawTargetPath' => (string)$path['copyfrom-path'], 'rawChangeType' => (string)$path['action'], 'rawTargetCommit' => (string)$path['copyfrom-rev'], ); } $copied_or_moved_map = array(); $deleted_paths = array(); $add_paths = array(); foreach ($raw_paths as $path => $raw_info) { if ($raw_info['rawTargetPath']) { $copied_or_moved_map[$raw_info['rawTargetPath']][] = $raw_info; } switch ($raw_info['rawChangeType']) { case 'D': $deleted_paths[$path] = $raw_info; break; case 'A': $add_paths[$path] = $raw_info; break; } } // If a path was deleted, we need to look in the repository history to // figure out where the former valid location for it is so we can figure out // if it was a directory or not, among other things. $lookup_here = array(); foreach ($raw_paths as $path => $raw_info) { if ($raw_info['rawChangeType'] != 'D') { continue; } // If a change copies a directory and then deletes something from it, // we need to look at the old location for information about the path, not // the new location. This workflow is pretty ridiculous -- so much so that // Trac gets it wrong. See Facebook rO6 for an example, if you happen to // work at Facebook. $parents = $this->expandAllParentPaths($path, $include_self = true); foreach ($parents as $parent) { if (isset($add_paths[$parent])) { $relative_path = substr($path, strlen($parent)); $lookup_here[$path] = array( 'rawPath' => $add_paths[$parent]['rawTargetPath'].$relative_path, 'rawCommit' => $add_paths[$parent]['rawTargetCommit'], ); continue 2; } } // Otherwise we can just look at the previous revision. $lookup_here[$path] = array( 'rawPath' => $path, 'rawCommit' => $svn_commit - 1, ); } $lookup = array(); foreach ($raw_paths as $path => $raw_info) { if ($raw_info['rawChangeType'] == 'D') { $lookup[$path] = $lookup_here[$path]; } else { // For everything that wasn't deleted, we can just look it up directly. $lookup[$path] = array( 'rawPath' => $path, 'rawCommit' => $svn_commit, ); } } $path_file_types = $this->lookupPathFileTypes($repository, $lookup); $effects = array(); $resolved_types = array(); $supplemental = array(); foreach ($raw_paths as $path => $raw_info) { if (isset($resolved_types[$path])) { $type = $resolved_types[$path]; } else { switch ($raw_info['rawChangeType']) { case 'D': if (isset($copied_or_moved_map[$path])) { if (count($copied_or_moved_map[$path]) > 1) { $type = DifferentialChangeType::TYPE_MULTICOPY; } else { $type = DifferentialChangeType::TYPE_MOVE_AWAY; } } else { $type = DifferentialChangeType::TYPE_DELETE; $file_type = $path_file_types[$path]; if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { // Bad. Child paths aren't enumerated in "svn log" so we need // to go fishing. $list = $this->lookupRecursiveFileList( $repository, $lookup[$path]); foreach ($list as $deleted_path => $path_file_type) { $deleted_path = rtrim($path.'/'.$deleted_path, '/'); if (!empty($raw_paths[$deleted_path])) { // We somehow learned about this deletion explicitly? // TODO: Unclear how this is possible. continue; } $effects[$deleted_path] = array( 'rawPath' => $deleted_path, 'rawTargetPath' => null, 'rawTargetCommit' => null, 'rawDirect' => true, 'changeType' => $type, 'fileType' => $path_file_type, ); } } } break; case 'A': $copy_from = $raw_info['rawTargetPath']; $copy_rev = $raw_info['rawTargetCommit']; if (!strlen($copy_from)) { $type = DifferentialChangeType::TYPE_ADD; } else { if (isset($deleted_paths[$copy_from])) { $type = DifferentialChangeType::TYPE_MOVE_HERE; $other_type = DifferentialChangeType::TYPE_MOVE_AWAY; } else { $type = DifferentialChangeType::TYPE_COPY_HERE; $other_type = DifferentialChangeType::TYPE_COPY_AWAY; } $source_file_type = $this->lookupPathFileType( $repository, $path, array( 'rawPath' => $copy_from, 'rawCommit' => $copy_rev, )); if ($source_file_type != DifferentialChangeType::FILE_DIRECTORY) { if (isset($raw_paths[$copy_from])) { break; } $effects[$copy_from] = array( 'rawPath' => $copy_from, 'rawTargetPath' => null, 'rawTargetCommit' => null, 'rawDirect' => false, 'changeType' => $other_type, 'fileType' => $source_file_type, ); } else { // ULTRADISASTER. We've added a directory which was copied // or moved from somewhere else. This is the most complex and // ridiculous case. $list = $this->lookupRecursiveFileList( $repository, array( 'rawPath' => $copy_from, 'rawCommit' => $copy_rev, )); foreach ($list as $from_path => $from_file_type) { $full_from = rtrim($copy_from.'/'.$from_path, '/'); $full_to = rtrim($path.'/'.$from_path, '/'); if (empty($raw_paths[$full_to])) { $effects[$full_to] = array( 'rawPath' => $full_to, 'rawTargetPath' => $full_from, 'rawTargetCommit' => $copy_rev, 'rawDirect' => true, 'changeType' => $type, 'fileType' => $from_file_type, ); } else { // This means we picked the file up explicitly elsewhere. // If the file as modified, SVN will drop the copy // information. We need to restore it. $supplemental[$full_to]['rawTargetPath'] = $full_from; $supplemental[$full_to]['rawTargetCommit'] = $copy_rev; if ($raw_paths[$full_to]['rawChangeType'] == 'M') { $resolved_types[$full_to] = $type; } } if (empty($raw_paths[$full_from])) { if ($other_type == DifferentialChangeType::TYPE_COPY_AWAY) { $effects[$full_from] = array( 'rawPath' => $full_from, 'rawTargetPath' => null, 'rawTargetCommit' => null, 'rawDirect' => false, 'changeType' => $other_type, 'fileType' => $from_file_type, ); } } } } } break; // This is "replaced", caused by "svn rm"-ing a file, putting another // in its place, and then "svn add"-ing it. We do not distinguish // between this and "M". case 'R': case 'M': if (isset($copied_or_moved_map[$path])) { $type = DifferentialChangeType::TYPE_COPY_AWAY; } else { $type = DifferentialChangeType::TYPE_CHANGE; } break; } } $resolved_types[$path] = $type; } foreach ($raw_paths as $path => $raw_info) { $raw_paths[$path]['changeType'] = $resolved_types[$path]; if (isset($supplemental[$path])) { foreach ($supplemental[$path] as $key => $value) { $raw_paths[$path][$key] = $value; } } } foreach ($raw_paths as $path => $raw_info) { $effects[$path] = array( 'rawPath' => $path, 'rawTargetPath' => $raw_info['rawTargetPath'], 'rawTargetCommit' => $raw_info['rawTargetCommit'], 'rawDirect' => true, 'changeType' => $raw_info['changeType'], 'fileType' => $path_file_types[$path], ); } $parents = array(); foreach ($effects as $path => $effect) { foreach ($this->expandAllParentPaths($path) as $parent_path) { $parents[$parent_path] = true; } } $parents = array_keys($parents); foreach ($parents as $parent) { if (isset($effects[$parent])) { continue; } $effects[$parent] = array( 'rawPath' => $parent, 'rawTargetPath' => null, 'rawTargetCommit' => null, 'rawDirect' => false, 'changeType' => DifferentialChangeType::TYPE_CHILD, 'fileType' => DifferentialChangeType::FILE_DIRECTORY, ); } $lookup_paths = array(); foreach ($effects as $effect) { $lookup_paths[$effect['rawPath']] = true; if ($effect['rawTargetPath']) { $lookup_paths[$effect['rawTargetPath']] = true; } } $lookup_paths = array_keys($lookup_paths); $lookup_commits = array(); foreach ($effects as $effect) { if ($effect['rawTargetCommit']) { $lookup_commits[$effect['rawTargetCommit']] = true; } } $lookup_commits = array_keys($lookup_commits); $path_map = $this->lookupOrCreatePaths($lookup_paths); $commit_map = $this->lookupSvnCommits($repository, $lookup_commits); $this->writeChanges($repository, $commit, $effects, $path_map, $commit_map); $this->writeBrowse($repository, $commit, $effects, $path_map); } private function writeChanges( PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, array $effects, array $path_map, array $commit_map) { $conn_w = $repository->establishConnection('w'); $sql = array(); foreach ($effects as $effect) { $sql[] = qsprintf( $conn_w, '(%d, %d, %d, %nd, %nd, %d, %d, %d, %d)', $repository->getID(), $path_map[$effect['rawPath']], $commit->getID(), $effect['rawTargetPath'] ? $path_map[$effect['rawTargetPath']] : null, $effect['rawTargetCommit'] ? $commit_map[$effect['rawTargetCommit']] : null, $effect['changeType'], $effect['fileType'], $effect['rawDirect'] ? 1 : 0, $commit->getCommitIdentifier()); } queryfx( $conn_w, 'DELETE FROM %T WHERE commitID = %d', PhabricatorRepository::TABLE_PATHCHANGE, $commit->getID()); foreach (array_chunk($sql, 512) as $sql_chunk) { queryfx( $conn_w, 'INSERT INTO %T (repositoryID, pathID, commitID, targetPathID, targetCommitID, changeType, fileType, isDirect, commitSequence) VALUES %Q', PhabricatorRepository::TABLE_PATHCHANGE, implode(', ', $sql_chunk)); } } private function writeBrowse( PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, array $effects, array $path_map) { $conn_w = $repository->establishConnection('w'); $sql = array(); foreach ($effects as $effect) { $type = $effect['changeType']; // Don't write COPY_AWAY to the filesystem table if it isn't a direct // event. We do write CHILD. if (!$effect['rawDirect']) { if ($type == DifferentialChangeType::TYPE_COPY_AWAY) { continue; } } + if ($effect['rawPath'] == '/') { + // Don't bother writing the CHILD events on '/' to the filesystem + // table; in particular, it doesn't have a meaningful parentID. + continue; + } + $existed = !DifferentialChangeType::isDeleteChangeType($type); $sql[] = qsprintf( $conn_w, '(%d, %d, %d, %d, %d, %d)', $repository->getID(), $path_map[$this->getParentPath($effect['rawPath'])], $commit->getCommitIdentifier(), $path_map[$effect['rawPath']], $existed ? 1 : 0, $effect['fileType']); } queryfx( $conn_w, 'DELETE FROM %T WHERE repositoryID = %d AND svnCommit = %d', PhabricatorRepository::TABLE_FILESYSTEM, $repository->getID(), $commit->getCommitIdentifier()); foreach (array_chunk($sql, 512) as $sql_chunk) { queryfx( $conn_w, 'INSERT INTO %T (repositoryID, parentID, svnCommit, pathID, existed, fileType) VALUES %Q', PhabricatorRepository::TABLE_FILESYSTEM, implode(', ', $sql_chunk)); } } private function lookupSvnCommits( PhabricatorRepository $repository, array $commits) { if (!$commits) { return array(); } $commit_table = new PhabricatorRepositoryCommit(); $commit_data = queryfx_all( $commit_table->establishConnection('w'), 'SELECT id, commitIdentifier FROM %T WHERE commitIdentifier in (%Ld)', $commit_table->getTableName(), $commits); return ipull($commit_data, 'id', 'commitIdentifier'); } private function lookupPathFileType( PhabricatorRepository $repository, $path, array $path_info) { $result = $this->lookupPathFileTypes( $repository, array( $path => $path_info, )); return $result[$path]; } private function lookupPathFileTypes( PhabricatorRepository $repository, array $paths) { $repository_uri = $repository->getDetail('remote-uri'); $parents = array(); $path_mapping = array(); foreach ($paths as $path => $lookup) { $parent = dirname($lookup['rawPath']); $parent = ltrim($parent, '/'); $parent = $this->encodeSVNPath($parent); $parent = $repository_uri.$parent.'@'.$lookup['rawCommit']; $parent = escapeshellarg($parent); $parents[$parent] = true; $path_mapping[$parent][] = $path; } $result_map = array(); // Reverse this list so we can pop $path_mapping, as that's more efficient // than shifting it. We need to associate these maps positionally because // a change can copy the same source path from multiple revisions via // "svn cp path@1 a; svn cp path@2 b;" and the XML output gives us no way // to distinguish which revision we're looking at except based on its // position in the document. $all_paths = array_reverse(array_keys($parents)); foreach (array_chunk($all_paths, 64) as $path_chunk) { list($raw_xml) = execx( 'svn --non-interactive --xml ls %C', implode(' ', $path_chunk)); $xml = new SimpleXMLElement($raw_xml); foreach ($xml->list as $list) { $list_path = (string)$list['path']; // SVN is a big mess. See Facebook rG8 (a revision which adds files // with spaces in their names) for an example. $list_path = rawurldecode($list_path); if ($list_path == $repository_uri) { $base = '/'; } else { $base = substr($list_path, strlen($repository_uri)); } $mapping = array_pop($path_mapping); foreach ($list->entry as $entry) { $val = $this->getFileTypeFromSVNKind($entry['kind']); foreach ($mapping as $base_path) { // rtrim() causes us to handle top-level directories correctly. $key = rtrim($base_path, '/').'/'.$entry->name; $result_map[$key] = $val; } } } } foreach ($paths as $path => $lookup) { if (empty($result_map[$path])) { $result_map[$path] = DifferentialChangeType::FILE_DELETED; } } return $result_map; } private function encodeSVNPath($path) { $path = rawurlencode($path); $path = str_replace('%2F', '/', $path); return $path; } private function getFileTypeFromSVNKind($kind) { $kind = (string)$kind; switch ($kind) { case 'dir': return DifferentialChangeType::FILE_DIRECTORY; case 'file': return DifferentialChangeType::FILE_NORMAL; default: throw new Exception("Unknown SVN file kind '{$kind}'."); } } private function lookupRecursiveFileList( PhabricatorRepository $repository, array $info) { $path = $info['rawPath']; $rev = $info['rawCommit']; $path = $this->encodeSVNPath($path); // TODO: This is a scalability nightmare. list($raw_xml) = execx( 'svn --non-interactive --xml ls -R %s%s@%d', $repository->getDetail('remote-uri'), $path, $rev); $map = array(); $xml = new SimpleXMLElement($raw_xml); foreach ($xml->list[0] as $entry) { $key = (string)$entry->name; $file_type = $this->getFileTypeFromSVNKind($entry['kind']); $map[$key] = $file_type; } return $map; } private function getParentPath($path) { - $path = rtrim('/', $path); + $path = rtrim($path, '/'); $path = dirname($path); - $path = rtrim('/', $path); if (!$path) { $path = '/'; } return $path; } private function expandAllParentPaths($path, $include_self = false) { $parents = array(); if ($include_self) { $parents[] = '/'.rtrim($path, '/'); } $parts = explode('/', trim($path, '/')); while (count($parts) >= 1) { array_pop($parts); $parents[] = '/'.implode('/', $parts); } return $parents; } } diff --git a/src/view/base/AphrontView.php b/src/view/base/AphrontView.php index 263f56b98d..99196f28e1 100755 --- a/src/view/base/AphrontView.php +++ b/src/view/base/AphrontView.php @@ -1,52 +1,52 @@ children[] = $child; return $this; } final protected function renderChildren() { $out = array(); foreach ($this->children as $child) { - $out[] = $this->renderChild($child); + $out[] = $this->renderSingleView($child); } return implode('', $out); } - private function renderChild($child) { + final protected function renderSingleView($child) { if ($child instanceof AphrontView) { return $child->render(); } else if (is_array($child)) { $out = array(); foreach ($child as $element) { - $out[] = $this->renderChild($element); + $out[] = $this->renderSingleView($element); } return implode('', $out); } else { return $child; } } abstract public function render(); } diff --git a/src/applications/diffusion/view/browsetable/DiffusionBrowseTableView.php b/src/view/layout/crumbs/AphrontCrumbsView.php similarity index 51% copy from src/applications/diffusion/view/browsetable/DiffusionBrowseTableView.php copy to src/view/layout/crumbs/AphrontCrumbsView.php index 389ac421af..7085651519 100644 --- a/src/applications/diffusion/view/browsetable/DiffusionBrowseTableView.php +++ b/src/view/layout/crumbs/AphrontCrumbsView.php @@ -1,51 +1,50 @@ request = $request; - return $this; - } - - public function setPaths(array $paths) { - $this->paths = $paths; - return $this; + public function setCrumbs(array $crumbs) { + $this->crumbs = $crumbs; + return; } public function render() { - $rows = array(); - foreach ($this->paths as $path) { - $rows[] = array( - phutil_escape_html($path->getPath()), // TODO: link - // TODO: etc etc - ); - } - $view = new AphrontTableView($rows); - $view->setHeaders( - array( - 'Path', - )); - return $view->render(); + require_celerity_resource('aphront-crumbs-view-css'); + + $out = array(); + foreach ($this->crumbs as $crumb) { + $out[] = $this->renderSingleView($crumb); + } + $out = implode( + ''. + "\xC2\xBB". + '', + $out); + + return + '
'. + '
'. + $out. + '
'. + '
'; } } diff --git a/src/applications/diffusion/view/commitchangetable/__init__.php b/src/view/layout/crumbs/__init__.php similarity index 56% copy from src/applications/diffusion/view/commitchangetable/__init__.php copy to src/view/layout/crumbs/__init__.php index 38dfa02082..53d523045d 100644 --- a/src/applications/diffusion/view/commitchangetable/__init__.php +++ b/src/view/layout/crumbs/__init__.php @@ -1,13 +1,13 @@