Skip to content
GitmoapdocsBeta
Guide· How do I…?

Search

Overview

Type what you remember and get back everything you can read that matches it: work items, merge requests, comments, releases, repositories, groups, services, specification documents and wiki pages, ranked together in one list rather than nine lists you have to visit in turn. You never have to decide first whether the thing you are looking for was a work item or a merge request — that is the search's job, not yours.

You can point a search at everything you can see, at one group and everything beneath it, or at a single repository. Whichever you choose, a result only appears if you could already open it, so a search is a safe thing to run in a group you share with people who cannot see everything you can.

Before you start

  • Nothing to switch on.Search reads what is already there. A work item is findable the moment it is saved, and so is an edit to it.
  • You find what you can already read, and nothing else.A public repository's contents answer anyone, an internal one answers anyone signed in, and a private one answers its members and the members of the groups above it. Signed out, the search across everything still works and returns only public results.
  • The right to open the container you scope to.A group or repository search authorizes that group or repository before it matches anything. A public one lets anybody in, an internal one lets in anybody signed in, and a private one needs Reporter access — a membership on it, or on a group above it. Without that the call is refused before any matching happens: 404 when you are signed in and cannot see it, 401 when you are not signed in at all.
  • A token with search:read, if you are calling the API with a personal or service-account access token. Search has its own scope rather than borrowing the ones belonging to the domains it spans, so a token carrying repo:read and issue:read still cannot search. A signed-in browser session and a CLI signed in with gitmoap login carry your own access and need nothing extra. A token bound to one repository or group is refused with 403 when a group or repository search is aimed outside that binding.
  • A service account searches with its own access.A service-account token's search finds what the account's grants let it read — the same answer the account's own listings give.
  • An id, or a path.Over the API the group or repository you scope to is named by its id. The CLI takes either an id or the path you see in the address bar, and resolves the path for you.

How it works

One ranked list, not eight

Every kind of result is scored against your query and the scores are compared with each other, so the most relevant merge request can outrank the least relevant work item and land above it in the same list. Each hit carries a rank, which is the score that put it where it is; the list is ordered by that, and by id where two hits tie, so the same query gives the same order twice.

A hit is a signpost, not a copy of the thing it points at. You get its kind, its id, a title, a short excerpt and when it last changed, plus the scope it sits in and its number for the kinds that have them — enough to render a row and follow the link. A hit with nothing to excerpt, a repository with no description say, arrives without one. Everything else lives at the resource's own address.

A comment's scope and number are those of the work item or merge request it was written on, and its on says which of the two it is, so every comment leads back to its thread. A service's scope is the group it belongs to. A specification document carries its file's path in the repository, and a wiki page carries the page's current path, which is what reads it.

What each kind matches

KindWhat is searchedThe hit's titleThe hit's id
issuetitle and descriptionthe work item's titleits own id
merge_requesttitle and descriptionthe merge request's titlethe repository id and the merge request number, joined by /
commentthe comment bodythe first 120 characters of the bodythe comment's id
releasename and release notesthe release namethe repository id and the tag, joined by /
repositorydisplay name and description as words; the full path as a fragmentthe full paththe repository id
groupdisplay name and description as words; the full path as a fragmentthe full paththe group id
serviceslug, display name and description as words; the slug as a fragmentthe slugthe service id
spectitle, summary and document id as words; the document id as a fragmentthe document titlethe repository id and the document id, joined by /
wikititle, summary and page id as words; the page id as a fragmentthe page titlethe wiki id and the page id, joined by /

Two different kinds of matching are at work in that table, and the difference is the thing people trip over. Titles, descriptions and bodies match whole words, with English stemming — deploying finds deployed and deploys, but deplo finds nothing. Paths, slugs and document ids also match fragments, because they are identifiers and no amount of stemming makes check find checkout-service. So a half-typed word finds a repository and does not find a work item.

Comments are searched as themselves rather than as part of the thing they are attached to, which is why a discussion that repeats a term ten times produces ten hits rather than one very relevant work item. Specification documents are matched on their title, summary and document id — not their full body.

Writing a query

The box takes the syntax a search box has trained you to expect:

You typeYou get
flaky login testresults matching all three words, in any order and any position
"connection pool"results containing that exact phrase, in that order
timeout -dockerresults matching timeout with everything mentioning docker removed
retry or backoffresults matching either word

Casing never matters. Punctuation is dropped from word matching, but it counts in a path, a slug or a document id — which is what makes platform/pay match the path it looks like. The operators shape word matching only: a fragment is compared against the whole of what you typed, so quoting a phrase or excluding a word does not change which repositories, groups or services a fragment finds.

A query of nothing but spaces is refused rather than treated as "everything", because a search that returns everything you can read is never what anybody meant. So is a query made only of very common words, in effect — the and and are dropped from word matching and leave nothing to match on.

Where the search runs

ScopeWhat it coversWhat it can return
Everything you can seeevery group you can read — the ones you belong to, everything public, and everything internal once you are signed inall nine kinds
A groupthat group, its subgroups, and every repository under any of themwork items, merge requests, comments, releases, services, specification documents and wiki pages
A repositorythat one repositorywork items, merge requests, comments, releases, specification documents and wiki pages

A scoped search returns the things filed inside the scope. Repositories and groups are containers rather than things filed in one, so a group or repository search never returns them — to find one, search across everything and narrow with types. A service belongs to a group, so a search in that group or one above it finds it, and a repository search does not.

Paging

A page holds up to 50 hits and defaults to 20. A full page comes back with a next_token; pass it on the next call to get the page after it, and keep going until a response arrives without one. That is the only reliable stopping condition — never stop because a page looked short, and never pass a cursor from a listing to a search or the other way round. A search cursor is a position in relevance order, a listing cursor is a position in most-recently-updated order, and the two mean different things.

How to

Search everything you can see

The broadest search: every group you can read, every kind of result, ranked together. It is the right starting point when you know the words but not where the thing lives.

Choose the search box in the top bar, or press / from anywhere. Suggestions appear as you type; press Enter for the full results page. That page shows one ranked list with a label on each row saying what the row is, and chips for narrowing it by kind.

Terminalbash
gitmoap search search-global --q <q>
curlbash
curl -X GET https://api.gitmoap.example/search \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Signed out, this call still works and answers with public results only. Signed in, it spans everything your memberships reach — which can be surprisingly wide, so scope the search when you already know where you are looking.

Search a group and everything below it

A group search covers the group, every subgroup underneath it and every repository in any of them. It is the search to use when a term means one thing in your team's group and something else across the company.

Open the group and choose Search in the side navigation. If you start typing in the top bar while you are already on a group's page, the first option offered is in this group — choosing it scopes the query without leaving the page.

Terminalbash
gitmoap search search-group --group-id <id> --q <q>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id}/search \
-H "Authorization: Bearer $GITMOAP_TOKEN"

You need to be able to see the group. A private group you are not a member of answers 404 rather than telling you it exists, and answers 401 if you are not signed in at all.

Search one repository

The narrowest scope, and the fastest to read: everything filed against one repository, with no results from anywhere else competing for the top of the list.

Open the repository and choose Search in the side navigation, or press / while a repository page is open — the box scopes to that repository by default and offers search everywhere as the alternative.

Terminalbash
gitmoap search search-repo --repo-id <id> --q <q>
curlbash
curl -X GET https://api.gitmoap.example/repositories/{repo_id}/search \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Narrow a search to one kind of thing

types takes a comma-separated list of the kind names from the table above — issue, merge_request, repository, group, release, comment, service, spec. Leave it out and every kind is searched.

The chips above the results — All, Work items, Merge requests, Comments, Releases, Repositories, Groups, Services, Specs — set this. They are multi-select: choosing two shows both, ranked against each other.

Terminalbash
gitmoap search search-global --q <q>
curlbash
curl -X GET https://api.gitmoap.example/search \
-H "Authorization: Bearer $GITMOAP_TOKEN"

A name that is not on the list is refused with 400, rather than being ignored. Asking for ?types=issues and quietly getting merge requests back would be an answer about a search you did not run, so the refusal names every value that would have worked.

Write a query that finds one specific thing

When a plain query returns too much, the three operators in the table above cut it down fast, and they compose. "connection pool" -test finds the design discussion and drops the test that mentions it in passing. timeout or deadline widens a term whose spelling nobody agreed on.

Type the operators straight into the box — quotes for a phrase, a leading - to exclude a word, or between alternatives. They compose, so a quoted phrase and an exclusion can go in the same query.

Terminalbash
gitmoap search search-global --q <q>
curlbash
curl -X GET https://api.gitmoap.example/search \
-H "Authorization: Bearer $GITMOAP_TOKEN"

A query may be up to 256 characters. If you are pasting a stack trace, quote the one distinctive line from it rather than the whole thing.

Page through a long result set

Ask for up to 50 hits at a time, then follow the cursor. Every response that fills its page carries next_token; a response without one is the end.

Results load a page at a time as you scroll — the next page is fetched for you as you reach the bottom of the list, so there is no cursor to carry by hand.

Terminalbash
gitmoap search search-group --group-id <id> --q <q>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id}/search \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Asking for more than 50 is not an error — you get 50. Asking for a page size that is not a number is refused with 400.

Find a repository, group or service by name or path

These three kinds match on a fragment of their path or slug as well as on their description, which makes search a fast way to jump to somewhere you can half-remember the name of. platform/pay finds platform/payments-api, and check finds the checkout service.

Type the fragment into the top bar and look for the repository, group or service rows among the suggestions. Pressing Enter opens the full results page, where the Repositories, Groups and Services chips leave only those.

Terminalbash
gitmoap search search-global --q <q>
curlbash
curl -X GET https://api.gitmoap.example/search \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Search these from the top-level scope. A group or repository search cannot return them.

Find a specification document

Indexed specification documents are searchable by title, summary and document id, so you can find the document that owns a decision without knowing which repository it was written in.

Open the repository, choose Search, and pick the Specs chip. Each hit shows the document's title and its summary line, and links to the document as it was last indexed.

Terminalbash
gitmoap search search-repo --repo-id <id> --q <q>
curlbash
curl -X GET https://api.gitmoap.example/repositories/{repo_id}/search \
-H "Authorization: Bearer $GITMOAP_TOKEN"

A document becomes searchable once it has been indexed on the repository's default branch. On any other branch the corpus is checked but not published, so a document that exists only on a working branch does not appear until the change lands — and a document deleted on the default branch stops appearing once that change is indexed.

Reference

3 endpoints

GET/searchsearch:read

Search everything the caller can see

search_globalGET /searchOpen in the API reference

Token scope

search:read

Send

Query
  • qstringFree text. Quote a phrase with "…", and exclude a word with a leading -
  • typesstringoptionalComma-separated kinds to search — issue, merge_request, repository, group, release, comment. Omit for all
  • limitint32optionalPage size (default 20, max 50)
  • next_tokenstringoptionalPagination cursor — a position in rank order, not recency order

Refuses

  • 400Missing or invalid q/types/limit/cursor
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient

Receive

  • items[]object10 fields
    • excerptobjectoptionalA short excerpt of the matching body, when the kind has one.
    • idstringThe resource's own identifier, as its domain names it (an issue uid, a repository id, a group id, a release tag_name).
    • iidint64optionalThe per-scope sequential number, for the kinds that have one. A comment carries the number of the work item or merge request it was written on.
    • kindcommentgroupissuemerge_requestreleaserepositoryservicespecwikiWhat kind of thing a search result is.
    • onobjectoptional
    • pathobjectoptionalFor a spec: the document's file in its repository. For a wiki page: the page's path, which is what reads it.
    • rankfloatHow well the result matches the query; results are ordered by it, highest first.
    • scopeobjectoptional
    • titlestring
    • updated_atstring
  • next_tokenobjectoptional
GET/groups/{group_id}/searchsearch:read

Search a group and everything beneath it

search_groupGET /groups/{group_id}/searchOpen in the API reference

Token scope

search:read

Send

Path
  • group_iduuidGroup ID
Query
  • qstringFree text
  • typesstringoptionalComma-separated kinds to search
  • limitint32optionalPage size (default 20, max 50)
  • next_tokenstringoptionalPagination cursor

Refuses

  • 400Missing or invalid q/types/limit/cursor
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found, or private with no access

Receive

The same shape search_global returns — 2 fields. Go to it

Show it here
  • items[]object10 fields
    • excerptobjectoptionalA short excerpt of the matching body, when the kind has one.
    • idstringThe resource's own identifier, as its domain names it (an issue uid, a repository id, a group id, a release tag_name).
    • iidint64optionalThe per-scope sequential number, for the kinds that have one. A comment carries the number of the work item or merge request it was written on.
    • kindcommentgroupissuemerge_requestreleaserepositoryservicespecwikiWhat kind of thing a search result is.
    • onobjectoptional
    • pathobjectoptionalFor a spec: the document's file in its repository. For a wiki page: the page's path, which is what reads it.
    • rankfloatHow well the result matches the query; results are ordered by it, highest first.
    • scopeobjectoptional
    • titlestring
    • updated_atstring
  • next_tokenobjectoptional
GET/repositories/{repo_id}/searchsearch:read

Search one repository

search_repoGET /repositories/{repo_id}/searchOpen in the API reference

Token scope

search:read

Send

Path
  • repo_iduuidRepository ID
Query
  • qstringFree text
  • typesstringoptionalComma-separated kinds to search
  • limitint32optionalPage size (default 20, max 50)
  • next_tokenstringoptionalPagination cursor

Refuses

  • 400Missing or invalid q/types/limit/cursor
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Repository not found, or private with no access

Receive

The same shape search_global returns — 2 fields. Go to it

Show it here
  • items[]object10 fields
    • excerptobjectoptionalA short excerpt of the matching body, when the kind has one.
    • idstringThe resource's own identifier, as its domain names it (an issue uid, a repository id, a group id, a release tag_name).
    • iidint64optionalThe per-scope sequential number, for the kinds that have one. A comment carries the number of the work item or merge request it was written on.
    • kindcommentgroupissuemerge_requestreleaserepositoryservicespecwikiWhat kind of thing a search result is.
    • onobjectoptional
    • pathobjectoptionalFor a spec: the document's file in its repository. For a wiki page: the page's path, which is what reads it.
    • rankfloatHow well the result matches the query; results are ordered by it, highest first.
    • scopeobjectoptional
    • titlestring
    • updated_atstring
  • next_tokenobjectoptional

Limits

WhatValueWhat happens at it
Query length256 charactersLonger is refused with 400 and nothing is searched
Page size20 by default, 50 maximumA larger number is reduced to 50 rather than refused; zero or a negative number becomes 1
Excerpt length200 charactersThe excerpt is the opening of the description, body or summary, cut at that point
Comment hit title120 charactersA comment has no title, so the opening of its body stands in for one
Kindsthe eight in the table aboveAn unrecognised name in types is refused with 400

Searching creates nothing, so it consumes none of your namespace's creation limits, and the size of a result page is the only bound on what one call returns. Nothing here differs by plan.

Troubleshooting

400 BAD_REQUEST, q is required
Cause
The call went out with no query parameter at all
What to do
Send q. There is no "match everything" mode — to list a whole domain, use that domain's own listing
400 BAD_REQUEST, q must not be empty
Cause
q was present but blank, or only whitespace
What to do
Type at least one word. A blank query is refused rather than treated as a request for everything
400 BAD_REQUEST, q must be at most 256 characters
Cause
The query is longer than the limit — usually a pasted log line or stack trace
What to do
Quote the one distinctive phrase from it instead
400 BAD_REQUEST, unknown search type 'issues'
Cause
A value in types is not one of the eight names — plural forms are the usual cause
What to do
Use the singular names. The message lists every value that would have worked
400 BAD_REQUEST, invalid limit
Cause
The page size is not a whole number
What to do
Send an integer. Anything above 50 is reduced to 50, so send 50 for the biggest page
400 BAD_REQUEST, invalid search cursor
Cause
The cursor is damaged, or it came from somewhere else — a listing's cursor is not a search cursor
What to do
Start again from the first page and follow only the next_token a search returned
400 BAD_REQUESTmentioning group_id or repo_id
Cause
A path was pasted where an id belongs
What to do
Use the id, or run the search from the CLI, which accepts a path and resolves it for you
401 UNAUTHORIZEDon a group or repository search while signed out
Cause
The group or repository is internal or private, and a scoped search must first see its container
What to do
Sign in. The search across everything works signed out and returns public results
401 UNAUTHORIZEDwith a token attached
Cause
The token has expired, been revoked, or was truncated in transit
What to do
Mint a fresh token and retry. A revoked token cannot be revived
403 INSUFFICIENT_SCOPE
Cause
The token is valid but was minted without search:read — the scopes of the domains being searched do not stand in for it
What to do
Mint a new token carrying search:read. Scopes are fixed when a token is created and cannot be added afterwards
403 FORBIDDEN
Cause
The token is bound to one repository or group, and a group or repository search was aimed outside that binding
What to do
Aim the search at the bound resource, or use a token without a resource binding
404 NOT_FOUNDon a group or repository you know exists
Cause
It is private and you are not a member of it or of any group above it — or the id is wrong
What to do
A private resource answers 404 rather than 403 so its existence stays private. Check your membership, then check the id
Nothing found for a word you can see on the screen
Cause
You typed part of a word. Titles, descriptions and bodies match whole words with stemming
What to do
Type the whole word. Only paths, service slugs and specification document ids match on a fragment
A repository, group or service is missing from a group or repository search
Cause
Those three are containers rather than things filed inside a scope, so a scoped search cannot return them
What to do
Search across everything and narrow with types=repository, group or service
A comment on a merge request is missing from a repository search
Cause
In a scoped search, comment hits are the ones on work items
What to do
Search across everything with types=merge_request,comment, or open the merge request and read its discussion
Results seem to stop early
Cause
You stopped paging because a page looked short. A full page always carries a cursor and a short one is genuinely the end
What to do
Page until a response arrives with no next_token, never until a page is smaller than you asked for
A service-account token's search misses a repository you can see yourself
Cause
Search answers as the token's owner, and the account holds no grant on that repository
What to do
Grant the account access to the repository, or run the search with your own token or signed-in session
A common word finds nothing
Cause
Words like the, and and is are dropped from word matching, so a query made only of them has nothing left to match
What to do
Search on the distinctive words in the phrase, and quote them together when their order matters
A specification document that exists in the repository is not found
Cause
It has not been indexed on the default branch — a working branch is checked but not published
What to do
Land the change on the default branch and retry once the indexing run for that push has finished
The excerpt does not contain the words you searched for
Cause
The excerpt is the opening of the body, not a highlight of the match
What to do
Open the result. The match may be further down the body, or in the title
A file's contents are not found
Cause
Search covers the eight kinds above, and file contents are not one of them
What to do
Clone the repository and use git grep, or find the merge request that changed the file

FAQ

Why is this separate from the filter box on the work item list?

They answer different questions. A listing's filter narrows a list that stays in most-recently-updated order, so you can page through it and know where you are. Search reorders by relevance, which is what you want when you are hunting and the wrong thing when you are reviewing a queue. Both accept free text; use the listing filter when you care about recency and search when you care about the best match.

Does a search ever show me something I cannot open?

No. Visibility is applied while the results are being selected, not afterwards, so a page is never quietly shortened by hidden rows and a hit you can see is a hit you can follow.

Can I search inside files?

The eight kinds in the table are what search covers, and file contents are not among them. Clone the repository and use git grep for a string in the code.

Can I search a pipeline run's logs?

Not from here. A step's log output has its own search on the run itself, which matches your term case-insensitively and hands back the lines it appears on — a ranked hit would lose exactly the line numbers you were after. See Pipelines.

Can I search published packages?

Yes, through the registry protocols rather than here — the npm and Cargo endpoints answer their own clients' search requests, so npm search and a Cargo registry search work against your own registries unchanged. See Package registries.

Why does a comment result show a wall of text where a title should be?

A comment has no title, so the first 120 characters of its body stand in for one. The excerpt below it is the first 200.

  • Issuesthe work items search finds, and the listing filter that keeps them in recency order
  • Merge requeststhe reviews and discussions on the other side of a hit
  • Repositoriesvisibility, which decides what your search can see at all
  • Groupsnesting, which decides how far a group-scoped search reaches
  • Service catalogwhat a service is, and why its slug matches on a fragment
  • Spec indexhow a repository's specification corpus gets indexed and when
  • Access tokensminting a token that carries search:read
  • Service accountswhat a service account can read, and so what its search finds
  • Pipelinessearching a run's logs, which is its own surface
  • Package registriesthe separate search each registry protocol provides