diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 6112b1bdc1..de7a69bca1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -150,6 +150,7 @@ phutil_register_library_map(array( 'DiffusionBrowseFileController' => 'applications/diffusion/controller/file', 'DiffusionBrowseQuery' => 'applications/diffusion/query/browse/base', 'DiffusionBrowseTableView' => 'applications/diffusion/view/browsetable', + 'DiffusionChangeController' => 'applications/diffusion/controller/change', 'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable', 'DiffusionCommitController' => 'applications/diffusion/controller/commit', 'DiffusionController' => 'applications/diffusion/controller/base', @@ -170,6 +171,7 @@ phutil_register_library_map(array( 'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath', 'DiffusionRequest' => 'applications/diffusion/request/base', 'DiffusionSvnBrowseQuery' => 'applications/diffusion/query/browse/svn', + 'DiffusionSvnFileContentQuery' => 'applications/diffusion/query/filecontent/svn', 'DiffusionSvnHistoryQuery' => 'applications/diffusion/query/history/svn', 'DiffusionView' => 'applications/diffusion/view/base', 'Javelin' => 'infrastructure/javelin/api', @@ -474,6 +476,7 @@ phutil_register_library_map(array( 'DiffusionBrowseController' => 'DiffusionController', 'DiffusionBrowseFileController' => 'DiffusionController', 'DiffusionBrowseTableView' => 'DiffusionView', + 'DiffusionChangeController' => 'DiffusionController', 'DiffusionCommitChangeTableView' => 'DiffusionView', 'DiffusionCommitController' => 'DiffusionController', 'DiffusionController' => 'PhabricatorController', @@ -487,6 +490,7 @@ phutil_register_library_map(array( 'DiffusionHomeController' => 'DiffusionController', 'DiffusionRepositoryController' => 'DiffusionController', 'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery', + 'DiffusionSvnFileContentQuery' => 'DiffusionFileContentQuery', 'DiffusionSvnHistoryQuery' => 'DiffusionHistoryQuery', 'DiffusionView' => 'AphrontView', 'ManiphestController' => 'PhabricatorController', diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index 62468c8d98..10d9924c40 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -189,6 +189,11 @@ class AphrontDefaultApplicationConfiguration '$' => 'DiffusionHomeController', '(?P[A-Z]+)/' => array( '$' => 'DiffusionRepositoryController', + 'change/'. + '(?P.*?)'. + '(?:[;](?P[a-z0-9]+))?'. + '$' + => 'DiffusionChangeController', 'history/'. '(?P.*?)'. '(?:[;](?P[a-z0-9]+))?'. diff --git a/src/applications/diffusion/controller/base/DiffusionController.php b/src/applications/diffusion/controller/base/DiffusionController.php index addf89fe5d..4b227b10a4 100644 --- a/src/applications/diffusion/controller/base/DiffusionController.php +++ b/src/applications/diffusion/controller/base/DiffusionController.php @@ -151,6 +151,10 @@ abstract class DiffusionController extends PhabricatorController { case 'browse': $view_name = 'Browse'; break; + case 'change': + $crumb_list[] = 'TODO CHANGE'; + $crumbs->setCrumbs($crumb_list); + return $crumbs; } $path = null; diff --git a/src/applications/diffusion/controller/browse/DiffusionBrowseController.php b/src/applications/diffusion/controller/browse/DiffusionBrowseController.php index 868aaa5cb6..44074b0561 100644 --- a/src/applications/diffusion/controller/browse/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/browse/DiffusionBrowseController.php @@ -49,6 +49,16 @@ class DiffusionBrowseController extends DiffusionController { $deleted = $browse_query->getDeletedAtCommit(); $existed = $browse_query->getExistedAtCommit(); + $callsign = $drequest->getRepository()->getCallsign(); + + if ($commit) { + $commit = "r{$callsign}{$commit}"; + } else { + $commit = 'HEAD'; + } + $deleted = "r{$callsign}{$deleted}"; + $existed = "r{$callsign}{$existed}"; + $title = 'Path Was Deleted'; $body = "This path does not exist at {$commit}. It was deleted in ". "{$deleted} and last existed at {$existed}."; diff --git a/src/applications/diffusion/controller/change/DiffusionChangeController.php b/src/applications/diffusion/controller/change/DiffusionChangeController.php new file mode 100644 index 0000000000..33ec3bd07e --- /dev/null +++ b/src/applications/diffusion/controller/change/DiffusionChangeController.php @@ -0,0 +1,96 @@ +diffusionRequest; + +// $browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest); +// $results = $browse_query->loadPaths(); + + $content = array(); + + $content[] = $this->buildCrumbs( + array( + 'branch' => true, + 'path' => true, + 'view' => 'change', + )); + +/* + 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->appendChild($browse_table); + + $content[] = $browse_panel; + } +*/ + + $nav = $this->buildSideNav('change', true); + $nav->appendChild($content); + + return $this->buildStandardPageResponse( + $nav, + array( + 'title' => 'Change', + )); + } + +} diff --git a/src/applications/diffusion/controller/change/__init__.php b/src/applications/diffusion/controller/change/__init__.php new file mode 100644 index 0000000000..5ee7801e41 --- /dev/null +++ b/src/applications/diffusion/controller/change/__init__.php @@ -0,0 +1,12 @@ +reason = self::REASON_IS_NONEXISTENT; + return array(); + } if ($commit) { $slice_clause = 'AND svnCommit <= '.(int)$commit; @@ -54,8 +59,38 @@ final class DiffusionSvnBrowseQuery extends DiffusionBrowseQuery { $slice_clause); if (!$index) { - // TODO: ! - return false; + if ($this->path == '/') { + $this->reason = self::REASON_IS_EMPTY; + } else { + $reasons = queryfx_all( + $conn_r, + 'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d + %Q ORDER BY svnCommit DESC LIMIT 2', + PhabricatorRepository::TABLE_FILESYSTEM, + $repository->getID(), + $path_id, + $slice_clause); + + $reason = reset($reasons); + + if (!$reason) { + $this->reason = self::REASON_IS_NONEXISTENT; + } else { + $file_type = $reason['fileType']; + if (empty($reason['existed'])) { + $this->reason = self::REASON_IS_DELETED; + $this->deletedAtCommit = $reason['svnCommit']; + if (!empty($reasons[1])) { + $this->existedAtCommit = $reasons[1]['svnCommit']; + } + } else if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { + $this->reason = self::REASON_IS_EMPTY; + } else { + $this->reason = self::REASON_IS_FILE; + } + } + } + return array(); } $sql = array(); diff --git a/src/applications/diffusion/query/browse/svn/__init__.php b/src/applications/diffusion/query/browse/svn/__init__.php index 2f3932f527..5d529ccb9d 100644 --- a/src/applications/diffusion/query/browse/svn/__init__.php +++ b/src/applications/diffusion/query/browse/svn/__init__.php @@ -6,6 +6,7 @@ +phutil_require_module('phabricator', 'applications/differential/constants/changetype'); phutil_require_module('phabricator', 'applications/diffusion/data/repositorypath'); phutil_require_module('phabricator', 'applications/diffusion/query/browse/base'); phutil_require_module('phabricator', 'applications/repository/storage/repository'); diff --git a/src/applications/diffusion/query/filecontent/base/DiffusionFileContentQuery.php b/src/applications/diffusion/query/filecontent/base/DiffusionFileContentQuery.php index efab915a49..62dc310b34 100644 --- a/src/applications/diffusion/query/filecontent/base/DiffusionFileContentQuery.php +++ b/src/applications/diffusion/query/filecontent/base/DiffusionFileContentQuery.php @@ -30,9 +30,12 @@ abstract class DiffusionFileContentQuery { $repository = $request->getRepository(); switch ($repository->getVersionControlSystem()) { - case 'git': + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $class = 'DiffusionGitFileContentQuery'; break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + $class = 'DiffusionSvnFileContentQuery'; + break; default: throw new Exception("Unsupported VCS!"); } diff --git a/src/applications/diffusion/query/filecontent/base/__init__.php b/src/applications/diffusion/query/filecontent/base/__init__.php index 78aec40d93..f964d49824 100644 --- a/src/applications/diffusion/query/filecontent/base/__init__.php +++ b/src/applications/diffusion/query/filecontent/base/__init__.php @@ -6,6 +6,8 @@ +phutil_require_module('phabricator', 'applications/repository/constants/repositorytype'); + phutil_require_module('phutil', 'symbols'); diff --git a/src/applications/diffusion/query/filecontent/svn/DiffusionSvnFileContentQuery.php b/src/applications/diffusion/query/filecontent/svn/DiffusionSvnFileContentQuery.php new file mode 100644 index 0000000000..6105b1498e --- /dev/null +++ b/src/applications/diffusion/query/filecontent/svn/DiffusionSvnFileContentQuery.php @@ -0,0 +1,42 @@ +getRequest(); + + $repository = $drequest->getRepository(); + $path = $drequest->getPath(); + $commit = $drequest->getCommit(); + + $remote_uri = $repository->getDetail('remote-uri'); + + list($corpus) = execx( + 'svn --non-interactive cat %s%s@%s', + $remote_uri, + $path, + $commit); + + $file_content = new DiffusionFileContent(); + $file_content->setCorpus($corpus); + + return $file_content; + } + +} diff --git a/src/applications/diffusion/query/filecontent/svn/__init__.php b/src/applications/diffusion/query/filecontent/svn/__init__.php new file mode 100644 index 0000000000..3b9b44f8f9 --- /dev/null +++ b/src/applications/diffusion/query/filecontent/svn/__init__.php @@ -0,0 +1,15 @@ +getCommit(); $local_path = $repository->getDetail('local-path'); - $git = $drequest->getPathToGitBinary(); list($stdout) = execx( - '(cd %s && %s log '. + '(cd %s && git log '. '--skip=%d '. '-n %d '. - '-t '. '--abbrev=40 '. '--pretty=format:%%H '. '%s -- %s)', $local_path, - $git, $this->getOffset(), $this->getLimit(), $commit_hash, @@ -48,6 +45,9 @@ final class DiffusionGitHistoryQuery extends DiffusionHistoryQuery { $commits = array(); $commit_data = array(); + $path_changes = array(); + + $conn_r = $repository->establishConnection('r'); if ($hashes) { $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( @@ -61,8 +61,28 @@ final class DiffusionGitHistoryQuery extends DiffusionHistoryQuery { mpull($commits, 'getID')); $commit_data = mpull($commit_data, null, 'getCommitID'); } + + if ($commits) { + $path_normal = '/'.trim($path, '/'); + $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 = idx($paths, $path_normal); + + $path_changes = queryfx_all( + $conn_r, + 'SELECT * FROM %T WHERE commitID IN (%Ld) AND pathID = %d', + PhabricatorRepository::TABLE_PATHCHANGE, + mpull($commits, 'getID'), + $path_id); + $path_changes = ipull($path_changes, null, 'commitID'); + } } + $history = array(); foreach ($hashes as $hash) { $item = new DiffusionPathChange(); @@ -74,6 +94,11 @@ final class DiffusionGitHistoryQuery extends DiffusionHistoryQuery { if ($data) { $item->setCommitData($data); } + $change = idx($path_changes, $commit->getID()); + if ($change) { + $item->setChangeType($change['changeType']); + $item->setFileType($change['fileType']); + } } $history[] = $item; } diff --git a/src/applications/diffusion/query/history/svn/DiffusionSvnHistoryQuery.php b/src/applications/diffusion/query/history/svn/DiffusionSvnHistoryQuery.php index 92106793c1..cb5ea6964f 100644 --- a/src/applications/diffusion/query/history/svn/DiffusionSvnHistoryQuery.php +++ b/src/applications/diffusion/query/history/svn/DiffusionSvnHistoryQuery.php @@ -35,6 +35,8 @@ final class DiffusionSvnHistoryQuery extends DiffusionHistoryQuery { $paths = ipull($paths, 'id', 'path'); $path_id = $paths['/'.trim($path, '/')]; + // TODO: isDirect junk + $history_data = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d @@ -63,8 +65,6 @@ final class DiffusionSvnHistoryQuery extends DiffusionHistoryQuery { } } - - $history = array(); foreach ($history_data as $row) { $item = new DiffusionPathChange(); @@ -77,6 +77,11 @@ final class DiffusionSvnHistoryQuery extends DiffusionHistoryQuery { $item->setCommitData($data); } } + + $item->setChangeType($row['changeType']); + $item->setFileType($row['fileType']); + + $history[] = $item; } diff --git a/src/applications/diffusion/view/base/DiffusionView.php b/src/applications/diffusion/view/base/DiffusionView.php index 72783cccf6..13020d6a0b 100644 --- a/src/applications/diffusion/view/base/DiffusionView.php +++ b/src/applications/diffusion/view/base/DiffusionView.php @@ -29,6 +29,39 @@ abstract class DiffusionView extends AphrontView { return $this->diffusionRequest; } + final public function linkChange($change_type, $file_type) { + + $text = DifferentialChangeType::getFullNameForChangeType($change_type); + if ($change_type == DifferentialChangeType::TYPE_CHILD) { + // TODO: Don't link COPY_AWAY without a direct change. + return $text; + } + if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { + return $text; + } + + $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.$drequest->getPath(); + + return phutil_render_tag( + 'a', + array( + 'href' => "/diffusion/{$callsign}/change/{$path}{$commit}", + ), + $text); + } + final public function linkHistory($path) { $drequest = $this->getDiffusionRequest(); diff --git a/src/applications/diffusion/view/commitchangetable/DiffusionCommitChangeTableView.php b/src/applications/diffusion/view/commitchangetable/DiffusionCommitChangeTableView.php index 4478a49572..6b8d90c8e8 100644 --- a/src/applications/diffusion/view/commitchangetable/DiffusionCommitChangeTableView.php +++ b/src/applications/diffusion/view/commitchangetable/DiffusionCommitChangeTableView.php @@ -28,10 +28,17 @@ final class DiffusionCommitChangeTableView extends DiffusionView { public function render() { $rows = array(); foreach ($this->pathChanges as $change) { + $change_verb = DifferentialChangeType::getFullNameForChangeType( + $change->getChangeType()); + $rows[] = array( $this->linkHistory($change->getPath()), $this->linkBrowse($change->getPath()), - '-', + $this->linkChange( + $change->getPath(), + array( + 'text' => $change_verb, + )), phutil_escape_html($change->getPath()), ); } diff --git a/src/applications/diffusion/view/commitchangetable/__init__.php b/src/applications/diffusion/view/commitchangetable/__init__.php index ec22041ef3..bc6bcf3ec8 100644 --- a/src/applications/diffusion/view/commitchangetable/__init__.php +++ b/src/applications/diffusion/view/commitchangetable/__init__.php @@ -6,6 +6,7 @@ +phutil_require_module('phabricator', 'applications/differential/constants/changetype'); phutil_require_module('phabricator', 'applications/diffusion/view/base'); phutil_require_module('phabricator', 'view/control/table'); diff --git a/src/applications/diffusion/view/historytable/DiffusionHistoryTableView.php b/src/applications/diffusion/view/historytable/DiffusionHistoryTableView.php index bf31331419..f9c241265e 100644 --- a/src/applications/diffusion/view/historytable/DiffusionHistoryTableView.php +++ b/src/applications/diffusion/view/historytable/DiffusionHistoryTableView.php @@ -49,7 +49,9 @@ final class DiffusionHistoryTableView extends DiffusionView { self::linkCommit( $drequest->getRepository(), $history->getCommitIdentifier()), - '-', + $this->linkChange( + $history->getChangeType(), + $history->getFileType()), $date, $time, phutil_escape_html($history->getAuthorName()), diff --git a/src/applications/repository/worker/commitchangeparser/git/PhabricatorRepositoryGitCommitChangeParserWorker.php b/src/applications/repository/worker/commitchangeparser/git/PhabricatorRepositoryGitCommitChangeParserWorker.php index 02ce1630c3..8baa3d5f6d 100644 --- a/src/applications/repository/worker/commitchangeparser/git/PhabricatorRepositoryGitCommitChangeParserWorker.php +++ b/src/applications/repository/worker/commitchangeparser/git/PhabricatorRepositoryGitCommitChangeParserWorker.php @@ -73,6 +73,7 @@ class PhabricatorRepositoryGitCommitChangeParserWorker $change_type = null; $change_path = $src_path; $change_target = null; + $is_direct = true; switch ($action[0]) { case 'A': @@ -94,7 +95,12 @@ class PhabricatorRepositoryGitCommitChangeParserWorker $move_away[$change_target][] = $change_path; break; case 'M': - $change_type = DifferentialChangeType::TYPE_CHANGE; + if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { + $change_type = DifferentialChangeType::TYPE_CHILD; + $is_direct = false; + } else { + $change_type = DifferentialChangeType::TYPE_CHANGE; + } break; default: throw new Exception("Failed to parse line '{$line}'."); @@ -107,7 +113,7 @@ class PhabricatorRepositoryGitCommitChangeParserWorker 'path' => $change_path, 'changeType' => $change_type, 'fileType' => $file_type, - 'isDirect' => true, + 'isDirect' => $is_direct, 'commitSequence' => $commit->getEpoch(), 'targetPath' => $change_target, @@ -115,6 +121,21 @@ class PhabricatorRepositoryGitCommitChangeParserWorker ); } + // Add a change to '/' since git doesn't mention it. + $changes['/'] = array( + 'repositoryID' => $repository->getID(), + 'commitID' => $commit->getID(), + + 'path' => '/', + 'changeType' => DifferentialChangeType::TYPE_CHILD, + 'fileType' => DifferentialChangeType::FILE_DIRECTORY, + 'isDirect' => false, + 'commitSequence' => $commit->getEpoch(), + + 'targetPath' => null, + 'targetCommitID' => null, + ); + foreach ($copy_away as $change_path => $destinations) { if (isset($move_away[$change_path])) { $change_type = DifferentialChangeType::TYPE_MULTICOPY; diff --git a/webroot/rsrc/css/aphront/side-nav-view.css b/webroot/rsrc/css/aphront/side-nav-view.css index fd4080e027..bee51c44c2 100644 --- a/webroot/rsrc/css/aphront/side-nav-view.css +++ b/webroot/rsrc/css/aphront/side-nav-view.css @@ -13,6 +13,7 @@ td.aphront-side-nav-content { th.aphront-side-nav-navigation { border-right: 1px solid #bbbbbb; + padding-bottom: 8em; } th.aphront-side-nav-navigation a, diff --git a/webroot/rsrc/css/application/diffusion/commit-view.css b/webroot/rsrc/css/application/diffusion/commit-view.css index e065c61e0b..495cdc9f82 100644 --- a/webroot/rsrc/css/application/diffusion/commit-view.css +++ b/webroot/rsrc/css/application/diffusion/commit-view.css @@ -24,8 +24,7 @@ color: #444444; text-align: right; white-space: nowrap; - padding: 2px 4px; - width: 100px; + padding: 2px 4px 2px 24px; } .diffusion-commit-properties td {