Source capability tiers

Not every source can answer every question, and the honest version of that is worth reading before a proof of concept rather than during one.

If you can put it on a disk, we'll index it. Search, symbols, navigation, the dependency index, findings and complexity work over a plain directory with no version control in it at all. What a directory cannot give you is anything computed from commits — and that is stated up front, reported as a gap at query time, and never dressed up as an empty result.

The table

Capability What it answers Native git Git bridge fullHistory: false Directory
search Search, symbols, code navigation Yes Yes Yes Yes
dependencies Dependency index, resolved versions, "who uses this" Yes Yes Yes Yes
findings Security findings, code quality, complexity Yes Yes Yes Yes
history Commit log for a file or a repository Yes Yes No No
blame Who wrote this line, and when Yes Yes No No
coupling Hotspots and change coupling Yes Yes No No
commit_sha Which exact commit an answer came from Yes Yes Yes No

The column names are the four things you can actually configure. Git bridge is not a separate code path — git-p4, git-svn and hg-git produce a git repository, and the product cannot tell it apart from any other. That is the point: run a bridge and you get the full feature set.

Every repository reports its own row of this table on /api/v1/repos, keyed by these exact names. Read it before asking a history question rather than inferring it from the source type.

Why a gap is not an empty result

Standing rule: coverage gaps are reported as gaps, never as empty results.

"No coupling data available: source type directory has no history" is a different sentence from "this file has no coupling", and an agent that cannot tell them apart will draw the false conclusion and state it confidently — and so will the human reading its output.

So every response that can be partially answered carries a gaps array. It is never null: an empty results with an empty gaps means nothing matched; with a populated one it means we could not answer. Both halves matter, and the distinction is what the array exists for.

Native git

The default and the full set. Clone URL, optional credential, and the refresh loop is git fetch on an interval.

repositories:
  - url: https://github.example.com/platform/api
    groups: [platform]
    credential: github-ssh

Git bridge — Perforce, Subversion, Mercurial

This product does not natively speak Perforce, Subversion or Mercurial, and this page will not imply that it does. Advertising the product as Perforce-native would imply changelist awareness, and somebody would be disappointed in a proof of concept.

What is true is better than it sounds: the bridges are mature, widely deployed, and a customer running one gets every capability in the table above, including blame and change coupling.

Your system Bridge
Perforce Helix git-p4
Subversion git-svn
Mercurial hg-git

Run the bridge on a schedule into a git repository, point searchcode at that, and nothing downstream knows the difference. If you already mirror to git for CI — most Perforce shops do — you are done.

If you would rather not run a bridge, a directory source over a periodic export works and costs you the bottom three rows of the table.

fullHistory: false

The escape hatch for a repository whose history is the expensive part — fifteen years of binary assets, say.

repositories:
  - url: https://github.example.com/platform/monolith
    groups: [platform]
    fullHistory: false
    depth: 10

It is a per repository setting, so the deep-history repository that is costing you the disk can be shallow while everything else stays full. What it costs is exactly the history, blame and coupling rows, for that repository only — reported as gaps.

The commit SHA survives, so provenance is intact: an answer still names the commit it came from.

See Sizing for what full history actually costs, measured.

Directory sources

A path this process can read. A dropped tarball, a Perforce export, an NFS mount populated by your own tooling, a mainframe extract.

directorySources:
  allowedRoots: ["/mnt/depot"]
  denyPaths: ["/mnt/depot/.creds"]
  sources:
    - path: /mnt/depot/cobol
      groups: [mainframe]
      unitDetection: marker
      changeDetection: mtime

allowedRoots is a hard allowlist: every source path must resolve inside one of its entries, symlinks included. instance.dataDir, the configuration file's own directory and /run/secrets are denied implicitly whether or not you list them. A relative path is refused rather than resolved against whatever directory the process happened to start in.

unitDetection decides how many repositories you have, which makes it a metering input as well as a display one:

Mode One drop becomes
single One unit
subdirectories One unit per immediate subdirectory
marker (default) One unit per directory containing .git, go.mod, pom.xml, package.json, Cargo.toml or a marker you name

A Perforce-style depot drop of 2,000 projects under one path is 2,000 repositories under subdirectories and one under single. Choose deliberately.

changeDetection is mtime by default. Use hash when your export tooling rewrites every modification time on every run — mainframe and some Perforce export scripts do — at the cost of a full content read per scan.

Generated from the documentation that ships with searchcode enterprise. Questions: ben@boyter.org