Remove ?ref_type=heads in Gitlab file/dir link to reduce verbosity
Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.
Maybe since mid Aug 2023, Gitlab links to file or directory now always has a query string parameter ?ref_type=heads. Example:
https://<myproject_path>/-/blob/master/.gitlab-ci.yml?ref_type=headsWe often paste the Gitlab URL in our Slack/Teams messages and internal documentation. The ?ref_type=heads parameter is just noise adding no value to the link.
Gitlab support had indicated that the ?ref_type=heads was added to address issue 20526 Impossible to browse a branch if a tag exists with the same name
Would it be possible to have a user/group config somewhere to disable the ?ref_type=heads workaround? As it never occurred to us to give an identical name to a branch and a tag. Also confirmed by a comment on that same issue 20526 that it might not be a good idea:
using branches and tags of the same name isn't a pattern that works well. If you do the same thing locally, you'll end up with warnings like
warning: refname 'v0.0.1' is ambiguous
On github, there is no equivalent of ?ref_type=heads.
Proposal
Only include ref_type in a URL when the ref is genuinely ambiguous — i.e. a
branch and a tag share the same name, or the name is itself symbolic (e.g.
refs/heads/...). For the ~99% of refs that are unambiguous, links stay clean
(/-/blob/master/file.rb). This implements the conditional ref_type approach
described in
this comment.
Goal
ref_type is present in a URL only when required to disambiguate. Unambiguous
refs produce no ?ref_type= query parameter in repository navigation, while the
frontend still knows which type of ref is being viewed.
Why we can avoid forcing ref_type into the URL
- Unambiguous refs:
ExtractsRef::RefExtractor#ref_typereturnsnilwhen noref_typeparam is supplied, and Rails omitsnilquery params, so link helpers that passref_type: @ref_typealready emit nothing for the common case. - Ambiguity is computed once per request, not per link:
set_is_ambiguous_ref(app/controllers/projects/application_controller.rb) short-circuits tofalsewhen@ref_typeis present, and otherwise callsExtractsRef::VerifiedRefExtractor#ambiguous_ref?a single time for the ref being viewed.
Scope of changes
1. Provide the ref type to the frontend without putting it in the URL
The ref selector dropdown (app/assets/javascripts/ref/components/ref_selector.vue)
currently derives the branch/tag icon (dropdownIcon) from a symbolic ref string,
which is only available when ref_type is present in the URL (via
useSymbolicRefNames). Removing ref_type from the URL would therefore break the
icon for unambiguous refs.
To fix this we need a system for initializing @ref_type even when
@is_ambiguous_ref is false, so the frontend always knows the type of ref being
viewed regardless of whether ref_type is in the URL.
ExtractsPath#ref_type (lib/extracts_path.rb) already resolves the type via
VerifiedRefExtractor.ref_type, which detects heads/tags for unambiguous refs
and returns nil only when the ref is ambiguous. The work is to:
- Ensure the blob/tree views that mount the ref selector expose this detected
@ref_typeto the frontend as data, separately from the URL query param. - Update
ref_selector.vueso the icon (and any other ref-type-dependent display) is driven by this passed-in value rather than by parsing a symbolic ref out of the URL — e.g. matching onref_type.present? ? [ref_type, ref_name] : ref_name.
2. Single-ref navigation links — conditional propagation
Tree, blob, blame, and find_file views propagate @ref_type through their navigation
links. Because @ref_type is nil in the URL-param sense for unambiguous refs, these
links are clean as long as we do not force the detected type into the URL. This item
is verification plus removing any literal unconditional ref_type: from these links,
keeping the detected type available to the frontend (item 1) without serializing it
into the URL.
3. Ambiguous-ref entry prompt
When a user navigates to an ambiguous ref without a ref_type (e.g. a shared
clean URL that happens to be ambiguous), the existing ambiguous-ref modal
(app/assets/javascripts/ref/init_ambiguous_ref_modal.js) asks the user to choose
branch or tag and sets ref_type. This flow remains the single point where
ref_type is (re-)introduced into the URL. Confirm the modal is mounted on all
relevant views (tree, blob, commits, project show).
Implementation note: caching ambiguity / ref-type lookups
Detecting the ref type for an unambiguous ref still relies on
VerifiedRefExtractor (tag_exists? / branch_exists?), which are Gitaly calls.
Resolving @ref_type for the frontend on views that previously relied on the URL
param may therefore add lookups on those requests. We have not confirmed the
performance impact. The implementing engineer should evaluate whether these
lookups need caching (e.g. memoization per request, or a cheaper repo-level
ambiguity check) before rollout, and measure rather than assume.
Out of scope
- List pages (
branches,tags): these render a single, known ref type per page, so passingref_type: :heads/:tagsthere is correct and intentional — not the verbose-URL problem this issue targets. No change. - Putting fully-qualified refs (
refs/heads/...) directly in URL paths — rejected becauseRefExtractor#extract_refre-splits the URLidagainst short ref names and would mis-parse a qualified path.
Acceptance criteria
- Navigating to / sharing an unambiguous ref produces no
?ref_type=in tree, blob, blame, find_file, raw, download, and ref-switcher URLs. - The ref-selector dropdown shows the correct branch/tag icon for the current
ref without relying on
ref_typein the URL. - Ambiguous refs continue to resolve to the correct content (branch vs tag).
- The ambiguous-ref modal still appears when navigating to an ambiguous ref
without
ref_type, and is the mechanism that setsref_type. - Any added ref-type lookups for unambiguous refs have been assessed for performance, with caching applied if measurement shows it is needed.