Inspect Git Remote Refs Without Fetching
You will use git ls-remote to inspect a remote repository without creating or updating local branches, tags or objects. The guide covers the everyday branch and tag checks, glob patterns, annotated tag output, symbolic HEAD, and the exit status that tells an empty match from a failed connection. Allow about ten minutes. You need Git and a repository URL, or a configured remote name.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Check the installed Git version
- 2. List every advertised reference
- 3. Narrow the output to branches or tags
- 4. Match a particular branch or tag
- 5. Make an empty match an error
- 6. Inspect the default branch without fetching
- 7. Expand a configured URL without contacting the remote
- 8. Avoid the common traps
1. Check the installed Git version
The examples here were checked with Git 2.43.0 from the Ubuntu git-man package version 1:2.43.0-1ubuntu7.3. Confirm the command before relying on version-specific wording:
$ git --version
git version 2.43.0
$ dpkg-query -W -f='${Package} ${Version}\n' git-man
git-man 1:2.43.0-1ubuntu7.3
This command is read-only. It contacts the remote when given a normal repository URL or remote name, but it does not perform a fetch. Do not use sudo; repository access belongs to your account and its SSH or credential configuration.
2. List every advertised reference
Pass a remote name from the current repository, or replace the placeholder with a URL:
$ git ls-remote origin
f79010f9bcc3172e89339a77d5d57aadc4bc03d1 HEAD
f79010f9bcc3172e89339a77d5d57aadc4bc03d1 refs/heads/main
b9dc17ab0933892ae250fa523b62a15b2f4af2b9 refs/tags/v1.0
f79010f9bcc3172e89339a77d5d57aadc4bc03d1 refs/tags/v1.0^{}
The first field is an object ID and the second is a reference name, separated by a tab. The IDs above are from a small local test repository, so yours will differ. A normal response can include the remote's symbolic HEAD, branch references under refs/heads/, and tags under refs/tags/.
An annotated tag can produce two lines. The first names the tag object. The line ending in ^{} is the peeled object, usually the commit that the tag ultimately points to. This is why a tag count can appear to be twice what you expected.
3. Narrow the output to branches or tags
Use --heads for branches and --tags for tags. In the installed 2.43.0 manual, both can be used together, and --refs removes symbolic and peeled entries:
$ git ls-remote --heads --refs origin
f79010f9bcc3172e89339a77d5d57aadc4bc03d1 refs/heads/main
$ git ls-remote --tags --refs origin
b9dc17ab0933892ae250fa523b62a15b2f4af2b9 refs/tags/v1.0
Use the short -h and -t forms only when they make a script genuinely clearer. Current upstream documentation calls the branch option --branches and describes --heads and -h as deprecated synonyms. The local 2.43.0 manpage documents --heads, so this guide uses that form for the installed command. Check git ls-remote -h on older or newer Git releases if portability matters.
4. Match a particular branch or tag
Arguments after the repository are patterns, not revisions to resolve locally. They are globs matched against the tail of a reference. A full reference name also works:
$ git ls-remote origin refs/heads/main
f79010f9bcc3172e89339a77d5d57aadc4bc03d1 refs/heads/main
$ git ls-remote --tags origin 'v1*'
b9dc17ab0933892ae250fa523b62a15b2f4af2b9 refs/tags/v1.0
f79010f9bcc3172e89339a77d5d57aadc4bc03d1 refs/tags/v1.0^{}
Quote a pattern containing *. Without quotes, the local shell may expand it against filenames in your current directory before Git sees it. A pattern such as main matches a reference whose tail is main, but not domain. Add --refs when you need one line per stored ref rather than peeled tag information.
5. Make an empty match an error
By default, a successful conversation with the server returns status 0 even if no reference matches. That is easy to misread in a release or deployment script. Add --exit-code when the presence of the reference is part of the condition:
if git ls-remote --exit-code --heads origin 'release/*' > /tmp/release-refs.txt; then
printf '%s\n' 'At least one release branch exists'
else
status=$?
case "$status" in
2) printf '%s\n' 'The remote answered, but no release branch matched' >&2 ;;
*) printf 'Could not query the remote, status %s\n' "$status" >&2; exit "$status" ;;
esac
fi
Status 2 means that the remote was contacted but no matching ref was found. Other non-zero statuses normally indicate a transport, authentication or repository problem. Keep the output file in a temporary directory appropriate to your script, and do not treat a status 2 as proof that the server is unavailable.
6. Inspect the default branch without fetching
Use --symref when you need the symbolic target behind the remote's HEAD:
$ git ls-remote --symref origin HEAD
ref: refs/heads/main HEAD
f79010f9bcc3172e89339a77d5d57aadc4bc03d1 HEAD
The exact branch name is remote configuration, not a universal promise that it is main or master. The installed manual says that upload-pack currently exposes the HEAD symref. If you only need the commit ID, omit --symref. If you need to act on the branch, validate the returned name before inserting it into another command.
7. Expand a configured URL without contacting the remote
--get-url expands a remote URL using Git's url.<base>.insteadOf configuration and exits without talking to the server. This is useful for checking which endpoint a remote name resolves to before a network operation:
$ git ls-remote --get-url origin
ssh://[email protected]/project/repository.git
This is not a connectivity test. A URL can expand correctly while DNS, credentials, host-key verification or repository access still fails. Do not paste private URLs or access tokens into shared logs. Prefer SSH keys or the credential mechanism already approved for the repository.
8. Avoid the common traps
- Do not confuse advertised refs with downloaded objects. The command reports what the remote advertises. It does not populate your object database, so options that need local object data are not a substitute for a fetch.
- Do not parse columns by a single space. The documented separator is a tab. In a shell, use a tab-aware tool or split on whitespace only after remembering that ref names themselves cannot contain spaces.
- Do not assume a tag line names a commit. An annotated tag's first object ID can identify a tag object. Use the peeled
^{}line, or query the tag with a separate Git command after you have fetched what you need. - Do not add
--quietwhile diagnosing transport problems. It suppresses the remote URL on standard error, which can make an otherwise useful failure less obvious. Use it in automation once logging is deliberate.
No example in this guide changes repository state, so there is no undo operation. If a query fails, first run the same command without extra filtering, check the remote name with git remote -v, and then investigate SSH, credentials or network policy. Do not solve a read-only lookup failure by running as root.
Done means
- You can list a remote's refs and read the object ID plus ref name.
- You can separate branches, tags, symbolic
HEADand peeled annotated tags. - Your patterns are quoted and tested against the ref names you actually expect.
- Scripts distinguish status 2, no matching ref, from other failures.
- You know that
--get-urlchecks URL expansion, not network access.