Skip to content
dpassenPublic

About

CLI for generating shareable URLs to files in hosted git repositories

Resources

Stars

16 stars

Watchers

1 watching

Forks

Latest commit

 

History

131 Commits

Folders and files

Repository files navigation

forgelink

forgelink banner

crates.io test lint format

Generate shareable URLs to files and line ranges in hosted git repositories, from the command line.

Inspired by the Emacs package git-link.

Supported Forges

Installation

cargo install forgelink-cli

This installs a binary named forgelink.

Usage

Use print to generate a stable URL pinned to the current commit SHA:

$ forgelink print src/main.rs
https://github.com/user/repo/blob/abc123def.../src/main.rs

Append :N for a single line or :N-M for a range:

$ forgelink print src/main.rs:42
https://github.com/user/repo/blob/abc123def.../src/main.rs#L42

$ forgelink print src/main.rs:42-55
https://github.com/user/repo/blob/abc123def.../src/main.rs#L42-L55

Target a remote other than origin with --remote:

$ forgelink print --remote upstream src/main.rs
https://github.com/upstream-owner/repo/blob/abc123def.../src/main.rs

Use the current branch name instead of the commit SHA with --branch:

$ forgelink print --branch src/main.rs
https://github.com/user/repo/blob/main/src/main.rs

--branch requires HEAD to be on a named branch. It errors on a detached HEAD, which includes Jujutsu (jj) working copies. Use the default commit-pinned link in that case.

Copy the URL to the clipboard with copy:

forgelink copy src/main.rs

Open the URL in your default browser with open:

forgelink open src/main.rs

copy and open do not print the URL. Use print when you want stdout. When available, all subcommands support the same --remote and --branch options.

Clipboard and browser support are default-on cargo features (clipboard and browser). Build with --no-default-features to drop the arboard and open dependencies, which also removes the copy and open subcommands. On Linux under X11 the clipboard is owned by the running process, so the copied URL may not persist after forgelink exits. macOS and Windows are unaffected.

It works from any subdirectory inside the repository:

$ cd src && forgelink print main.rs
https://github.com/user/repo/blob/abc123def.../src/main.rs

You can also pass an absolute path to link to a file in any repository, regardless of your current directory:

$ forgelink print ~/Developer/other-repo/src/main.rs
https://github.com/user/other-repo/blob/abc123def.../src/main.rs

Host configuration

forgelink applies Git's url.<base>.insteadOf rules to the remote URL before detecting the forge. Aliases rewritten to a supported forge do not need a host entry. Add one for other SSH aliases and forges on custom domains.

# git@gh-work:owner/repo.git
[[hosts]]
host = "gh-work"
base-url = "https://github.com"
forge = "github"

# git@git.company.tld:group/repo.git
[[hosts]]
host = "git.company.tld"
base-url = "https://git.company.tld"
forge = "gitlab"

Host entries require all three fields:

  • host is the hostname after Git URL rewrites are applied.
  • base-url is the HTTP or HTTPS URL used to build links. It may include a custom port or path prefix, but not credentials, a query, or a fragment.
  • forge is one of github, gitlab, sourcehut, bitbucket, codeberg, tangled, or azure-devops.

Host matching is exact and case-insensitive. A matching entry skips automatic forge detection. Wildcards and chained mappings are not supported.

Use --config to pick a file:

forgelink --config path/to/config.toml print src/main.rs

Otherwise, forgelink checks these locations in order:

  1. $XDG_CONFIG_HOME/forgelink/config.toml, when XDG_CONFIG_HOME is an absolute path.
  2. The native platform configuration directory:
    • Linux: ~/.config/forgelink/config.toml
    • macOS: ~/Library/Application Support/forgelink/config.toml
    • Windows: %APPDATA%\forgelink\config.toml

forgelink uses the first applicable location in the order shown. It does not merge configuration files. A missing default file is ignored, while a missing file passed with --config is an error. forgelink does not read ~/.ssh/config; add each SSH alias as a host entry.

Editor integration

The file argument accepts path:line syntax, so any editor that can run a shell command with the current buffer path and cursor line can bind it with no plugin required. Use copy to copy directly, or print to capture stdout yourself.

Helix

In ~/.config/helix/config.toml:

[keys.normal.space.u]
c = ':sh forgelink copy "%{buffer_name}:%{selection_line_start}-%{selection_line_end}"'
o = ':sh forgelink open "%{buffer_name}:%{selection_line_start}-%{selection_line_end}"'
p = ':sh forgelink print "%{buffer_name}:%{selection_line_start}-%{selection_line_end}"'

[keys.select.space.u]
c = ':sh forgelink copy "%{buffer_name}:%{selection_line_start}-%{selection_line_end}"'
o = ':sh forgelink open "%{buffer_name}:%{selection_line_start}-%{selection_line_end}"'
p = ':sh forgelink print "%{buffer_name}:%{selection_line_start}-%{selection_line_end}"'

Kakoune

In ~/.config/kak/kakrc:

declare-user-mode forgelink
map global user u ':enter-user-mode forgelink<ret>' -docstring "forgelink"
map global forgelink c ':forgelink copy<ret>' -docstring "copy link"
map global forgelink o ':forgelink open<ret>' -docstring "open link"
map global forgelink p ':forgelink print<ret>' -docstring "print link"

define-command forgelink -params 1 -hidden %{
  echo %sh{
    anchor=${kak_selection_desc%%.*}
    cursor=${kak_selection_desc#*,}
    cursor=${cursor%%.*}
    if [ "$anchor" -gt "$cursor" ]; then
      start=$cursor end=$anchor
    else
      start=$anchor end=$cursor
    fi
    forgelink "$1" "$kak_buffile:$start-$end" 2>&1
  }
}

Neovim

The following is a Lua implementation for using forgelink in Neovim. It copies a link to the file path in normal mode and a link to the line(s) selected in visual mode to the system clipboard.

if vim.fn.exepath('forgelink') ~= '' then
    local remotes = { "upstream", "private", "origin" }
    
    -- Tries executing forgelink across remotes until one succeeds.
    local function copy_forge_url(target)
        for _, remote in ipairs(remotes) do
            local output = vim.fn.system({ 'forgelink', 'print', '--remote', remote, target })
    
            if vim.v.shell_error == 0 then
                vim.fn.setreg('+', vim.trim(output))
                vim.notify("Copied forge URL (" .. remote .. "): \n" .. vim.trim(output), vim.log.levels.INFO)
                return
            end
        end
    
        vim.notify("Failed to generate forge URL for any candidate branch", vim.log.levels.ERROR)
    end
    
    -- Normal mode: file URL
    vim.keymap.set('n', '<leader>cf', function()
        local curFile = vim.api.nvim_buf_get_name(0)
        copy_forge_url(curFile)
    end, { desc = "Copy URL to Git forge for current file to clipboard" })
    
    -- Visual mode: file URL with line range
    vim.keymap.set("v", "<leader>cf", function()
        -- Exit visual mode to update '<, '> marks
        vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "x", false)
    
        local curFile = vim.api.nvim_buf_get_name(0)
        local startLine = vim.fn.line("'<")
        local endLine = vim.fn.line("'>")
        local lineRef = (startLine == endLine) and tostring(startLine) or (startLine .. "-" .. endLine)
    
        copy_forge_url(curFile .. ":" .. lineRef)
    end, { desc = "Copy URL to Git forge for current file with selected line numbers to clipboard" })
end

License

Licensed under either of MIT or Apache-2.0 at your option.

About

CLI for generating shareable URLs to files in hosted git repositories

Resources

Stars

16 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages