diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -52,6 +52,36 @@ $commit, $path); } catch (CommandException $e) { + // The "cat-file" command may fail if the path legitimately does not + // exist, but it may also fail if the path is a submodule. This can + // produce either "Not a valid object name" or "could not get object + // info". + + // To detect if we have a submodule, use `git ls-tree`. If the path + // is a submodule, we'll get a "160000" mode mask with type "commit". + + list($sub_err, $sub_stdout) = $repository->execLocalCommand( + 'ls-tree %s -- %s', + $commit, + $path); + if (!$sub_err) { + // If the path failed "cat-file" but "ls-tree" worked, we assume it + // must be a submodule. If it is, the output will look something + // like this: + // + // 160000 commit + // + // We make sure it has the 160000 mode mask to confirm that it's + // definitely a submodule. + $mode = (int)$sub_stdout; + if ($mode & 160000) { + $submodule_reason = DiffusionBrowseResultSet::REASON_IS_SUBMODULE; + $result + ->setReasonForEmptyResultSet($submodule_reason); + return $result; + } + } + $stderr = $e->getStderr(); if (preg_match('/^fatal: Not a valid object name/', $stderr)) { // Grab two logs, since the first one is when the object was deleted. diff --git a/src/applications/diffusion/data/DiffusionBrowseResultSet.php b/src/applications/diffusion/data/DiffusionBrowseResultSet.php --- a/src/applications/diffusion/data/DiffusionBrowseResultSet.php +++ b/src/applications/diffusion/data/DiffusionBrowseResultSet.php @@ -3,6 +3,7 @@ final class DiffusionBrowseResultSet extends Phobject { const REASON_IS_FILE = 'is-file'; + const REASON_IS_SUBMODULE = 'is-submodule'; const REASON_IS_DELETED = 'is-deleted'; const REASON_IS_NONEXISTENT = 'nonexistent'; const REASON_BAD_COMMIT = 'bad-commit'; diff --git a/src/applications/diffusion/view/DiffusionEmptyResultView.php b/src/applications/diffusion/view/DiffusionEmptyResultView.php --- a/src/applications/diffusion/view/DiffusionEmptyResultView.php +++ b/src/applications/diffusion/view/DiffusionEmptyResultView.php @@ -40,6 +40,13 @@ $body = pht('This path was an empty directory at %s.', $commit); $severity = PHUIInfoView::SEVERITY_NOTICE; break; + case DiffusionBrowseResultSet::REASON_IS_SUBMODULE: + $title = pht('Submodule'); + // TODO: We could improve this, but it is normally difficult to + // reach this page for a submodule. + $body = pht('This path was a submodule at %s.', $commit); + $severity = PHUIInfoView::SEVERITY_NOTICE; + break; case DiffusionBrowseResultSet::REASON_IS_DELETED: $deleted = $this->browseResultSet->getDeletedAtCommit(); $existed = $this->browseResultSet->getExistedAtCommit();