Giter Site home page Giter Site logo

moyiz / git-dev.nvim Goto Github PK

View Code? Open in Web Editor NEW
68.0 2.0 0.0 49 KB

Open remote git repositories in the comfort of Neovim.

License: BSD 3-Clause "New" or "Revised" License

Lua 94.75% Makefile 5.25%
git neovim neovim-plugin nvim nvim-lua nvim-plugin

git-dev.nvim's Introduction

               / /           |                         /
          ___   (___ ___  ___| ___           ___         _ _
         |   )| |        |   )|___) \  )    |   ) \  )| | | )
         |__/ | |__      |__/ |__    \/     |  /   \/ | |  /
         __/                            -

Open remote git repositories in the comfort of Neovim.

A plugin to open remote Git repositories inside Neovim by managing ephemeral shallow clones automatically. It aims to provide a similar experience to GitHub.dev but directly within Neovim.

๐Ÿ“น Demo

git-dev-2024-04-14_13.39.17.webm

Table of Contents

๐ŸŽจ Features

  • Open remote Git repositories inside Neovim at branch, tag or commit.
  • Supports most URLs from GitHub, GitLab, Gitea and Codeberg.
  • Seamless integration with your workflow (e.g. LSP and tree-sitter).
  • Ephemeral repositories - cleanup when Neovim exits.

๐Ÿ”จ Installation

Use your favorite plugin manager:

Lazy

{
  "moyiz/git-dev.nvim",
  event = "VeryLazy",
  opts = {},
}

Lazier (documentation will not be available until first use):

{
  "moyiz/git-dev.nvim",
  lazy = true,
  cmd = { "GitDevOpen", "GitDevCleanAll" },
  opts = {},
}

See Options.

๐Ÿ“˜ Usage

๐Ÿ“‚ Open

API: require("git-dev").open(repo, ref, opts)

Command: GitDevOpen

Open the repository in Neovim.

Parameters

  • repo - string - A partial or full Git URI.
  • ref - table - Target reference to checkout (default: nil). Empty ref will checkout the default branch. Examples: { branch = "..." }|{ tag = "..." }|{ commit = "..." }. If more than one is specified, the priority is: commit > tag > branch.
  • opts - table - Override plugin configuration for this call (default: nil). See Options below.

Examples

-- :GitDevOpen moyiz/git-dev.nvim
require("git-dev").open("moyiz/git-dev.nvim")

-- :GitDevOpen derailed/k9s '{ tag = "v0.32.4" }'
require("git-dev").open("derailed/k9s", { tag = "v0.32.4" })

-- :GitDevOpen echasnovski/mini.nvim '{ branch = "stable" }' '{ ephemeral = false }'
require("git-dev").open("echasnovski/mini.nvim", { branch = "stable "}, { ephemeral = false })

-- :GitDevOpen https://git.savannah.gnu.org/git/bash.git '{}' '{ read_only = false }'
require("git-dev").open("https://git.savannah.gnu.org/git/bash.git", {}, { read_only = false })

๐Ÿงน Clean All

Clean all cached local repositories.

Caution: It will delete the repositories directory itself. If you changed the default value, make sure that the new directory is being used only for this purpose.

By either using the lua function require("git-dev").clean_all() or the command GitDevCleanAll.

๐Ÿ‘“ Parse

Parses a Git URL. See URL Parsing.

โš™๏ธ Options

M.config = {
  -- Whether to delete an opened repository when nvim exits.
  -- If `true`, it will create an auto command for opened repositories
  -- to delete the local directory when nvim exists.
  ephemeral = true,
  -- Set buffers of opened repositories to be read-only and unmodifiable.
  read_only = true,
  -- Whether / how to CD into opened repository.
  -- Options: global|tab|window|none
  cd_type = "global",
  -- The actual `open` behavior.
  ---@param dir string The path to the local repository.
  ---@param repo_uri string The URI that was used to clone this repository.
  ---@param selected_path? string A relative path to a file in this repository.
  opener = function(dir, repo_uri, selected_path)
    vim.print("Opening " .. repo_uri)
    local dest =
      vim.fn.fnameescape(selected_path and dir .. "/" .. selected_path or dir)
    vim.cmd("edit " .. dest)
  end,
  -- Location of cloned repositories. Should be dedicated for this purpose.
  repositories_dir = vim.fn.stdpath "cache" .. "/git-dev",
  -- Extend the builtin URL parsers.
  -- Should map domains to parse functions. See |parser.lua|.
  extra_domain_to_parser = nil,
  git = {
    -- Name / path of `git` command.
    command = "git",
    -- Default organization if none is specified.
    -- If given repository name does not contain '/' and `default_org` is
    -- not `nil` nor empty, it will be prepended to the given name.
    default_org = nil,
    -- Base URI to use when given repository name is scheme-less.
    base_uri_format = "https://github.com/%s.git",
    -- Arguments for `git clone`.
    -- Triggered when repository does not exist locally.
    -- It will clone submodules too, disable it if it is too slow.
    clone_args = "--jobs=2 --single-branch --recurse-submodules "
      .. "--shallow-submodules",
    -- Arguments for `git fetch`.
    -- Triggered when repository is already exists locally to refresh the local
    -- copy.
    fetch_args = "--jobs=2 --no-all --update-shallow -f --prune --no-tags",
    -- Arguments for `git checkout`.
    -- Triggered when a branch, tag or commit is given.
    checkout_args = "-f --recurse-submodules",
  },
  -- Print command outputs.
  verbose = false,
}

๐Ÿ•ธ๏ธ URL Parsing

It is reasonable to assume that browsing arbitrary Git repositories will probably begin in a web browser. The main purpose of this feature is to allow quicker transition from the currently viewed branch / tag / commit / file to Neovim.

This plugin supports multiple types and flavors of URLs. It will accept most GitHub, GitLab, Gitea and Codeberg URLs, and will try to extract the actual git repository URL, selected branch / tag / commit and selected file.

If such extraction was successful, opener will be provided with selected_path, which is a relative path of a file in the repository. Its main use-case is to auto-open currently viewed file.

Nested branches (contain slashes) are supported.

Notice that passing explicit ref to GitDevOpen will take precedence on parsed fields.

Supported URLs

  • GitHub
    • https://github.com/<repo>
    • https://github.com/<repo>.git
    • https://github.com/<repo>/tree/<branch>
    • https://github.com/<repo>/tree/<tag>
    • https://github.com/<repo>/blob/<branch>
    • https://github.com/<repo>/blob/<branch>/<file_path>
    • https://github.com/<repo>/blob/<tag>
    • https://github.com/<repo>/blob/<tag>/<file_path>
  • GitLab
    • https://gitlab.com/<repo>
    • https://gitlab.com/<repo>.git
    • https://gitlab.com/<repo>/-/tree/<branch>
    • https://gitlab.com/<repo>/-/tree/<tag>
    • https://gitlab.com/<repo>/-/blob/<branch>
    • https://gitlab.com/<repo>/-/blob/<branch>/<file_path>
    • https://gitlab.com/<repo>/-/blob/<tag>
    • https://gitlab.com/<repo>/-/blob/<tag>/<file_path>
  • Gitea
    • https://gitea.com/<repo>
    • https://gitea.com/<repo>.git
    • https://gitea.com/<repo>/(src|raw)/tag/<tag>
    • https://gitea.com/<repo>/(src|raw)/tag/<tag>/<file_path>
    • https://gitea.com/<repo>/(src|raw)/branch/<branch>
    • https://gitea.com/<repo>/(src|raw)/branch/<branch>/<file_path>
    • https://gitea.com/<repo>/(src|raw)/commit/<commit_id>
    • https://gitea.com/<repo>/(src|raw)/commit/<commit_id>/<file_path>
  • Codeberg - Same as Gitea.

Examples

Open README.md in main branch:

require("git-dev").open("https://github.com/echasnovski/mini.nvim/blob/main/README.md")

Parser output:

{
  branch = "main",
  repo_url = "https://github.com/echasnovski/mini.nvim.git",
  selected_path = "README.md",
  type = "http"
}

Open cmd/scan/main.go in acook/generic_docker_source_entry branch:

require("git-dev").open("https://gitlab.com/gitlab-org/code-creation/repository-x-ray/-/blob/acook/generic_docker_source_entry/cmd/scan/main.go")

Parser output:

{
  branch = "acook/generic_docker_source_entry",
  repo_url = "https://gitlab.com/gitlab-org/code-creation/repository-x-ray.git",
  selected_path = "cmd/scan/main.go",
  type = "http"
}

See lua/git-dev/parser_spec.lua for more examples.

(Or: GitDevOpen https://github.com/moyiz/git-dev/blob/master/lua/git-dev/parser_spec.lua)

Limitations

Notice this feature is quite experimental. If you encounter any issues or have any questions or requests, feel free to reach out.

๐Ÿ““ Recipes

โ” Interactive Opening

The sky is the limit. I have settled for this key binding at the moment (set via lazy.nvim):

{
  "moyiz/git-dev.nvim",
  ...
  keys = {
    {
      "<leader>go",
      function()
        local repo = vim.fn.input "Repository name / URI: "
        if repo ~= "" then
          require("git-dev").open(repo)
        end
      end,
      desc = "[O]pen a remote git repository",
    }
  }
  ...
}

๐ŸŒฒ nvim-tree

To open with nvim-tree:

opts = {
  opener = function(dir, _, selected_path)
    -- vim.cmd("Oil " .. vim.fn.fnameescape(dir))
    vim.cmd("NvimTreeOpen " .. vim.fn.fnameescape(dir))
    if selected_path then
      vim.cmd("edit " .. selected_path)
    end
  end
}

๐ŸŒฒ neo-tree

opts = {
  opener = function(dir, _, selected_path)
    vim.cmd("Neotree " .. dir)
    if selected_path then
      vim.cmd("edit " .. selected_path)
    end
  end
}

๐Ÿ“‘ New tab

Recommended. Repositories will be opened in a new tab and its CWD will be set.

opts = {
  cd_type = "tab",
  opener = function(dir, _, selected_path)
    vim.cmd "tabnew"
    vim.cmd("Neotree " .. dir)
    if selected_path then
      vim.cmd("edit " .. selected_path)
    end
  end
}

๐ŸฆŠ Web browser

It does not make much sense on its own, but a showcase for getting both the repository URL and the local directory.

opts = {
  cd_type = "none",
  opener = function(_, repo_url)
     -- vim.cmd("!librewolf " .. repo_url)
     vim.cmd("!firefox " .. repo_url)
  end
}

๐Ÿ“ Customizing Default URL

By default, this plugin accepts partial repository URI (e.g. org/repo) by applying it onto a format string. This behavior can be customized by setting git.base_uri_format to change the URI, or git.default_org to prepend a default organization name if the given repository name does not contain /.

-- Change default URI
opts = {
  git = {
    base_uri_format = "https://git.home.arpa/%s.git",
  }
}

-- Open my own repositories by name with SSH.
-- E.g. "git-dev.nvim" rather than "moyiz/git-dev.nvim"
opts = {
  git = {
    default_org = "moyiz",
    base_uri_format = "[email protected]:%s.git",
  }
}

-- Enforce only full URIs (do not accept partial names).
opts = {
  git = {
    base_uri_format = "%s"
  }
}

๐Ÿก Private Git Instance

All repositories in my home Gitea service are private. Cloning such repositories using HTTP URLs will require inserting user and password. Since my SSH keys are already set, a custom parser can workaround it by leveraging the domain parameter of the parser function.

opts = {
  extra_domain_to_parser = {
    ["git.home.arpa"] = function(parser, text, _)
      text = text:gsub("https://([^/]+)/(.*)$", "ssh://git@%1:2222/%2")
      return parser:parse_gitea_like_url(text, "ssh://[email protected]:2222")
    end,
  },
}

Notice that my Gitea service listens on port 2222 for SSH. This custom parser tricks parse_gitea_like_url by converting a HTTP URL to SSH like URL (which is not a valid git URI). I.e.

https://git.home.arpa/homelab/k8s/src/commit/ef3fec4973042f0e0357a136d927fe2839350170/apps/gitea/kustomization.yaml

To:

ssh://[email protected]:2222/homelab/k8s/src/commit/ef3fec4973042f0e0357a136d927fe2839350170/apps/gitea/kustomization.yaml

Then, the parser trims the "domain" and proceeds as usual. Output:

{
  commit = "ef3fec4973042f0e0357a136d927fe2839350170",
  repo_url = "ssh://[email protected]:2222/homelab/k8s.git",
  selected_path = "apps/gitea/kustomization.yaml",
  type = "http"
}

๐Ÿ”ญ Telescope

TBD

๐Ÿ”ฎ Future Plans / Thoughts

  • Telescope extension to view, open and manage cloned repositories (will require ephemeral = false).
  • Open repository in visual selection / current "word".
  • Asynchronous command invocation.

๐Ÿ“œ License

See License.

git-dev.nvim's People

Contributors

moyiz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

git-dev.nvim's Issues

Feature Idea: Advanced Git Parsing

It would be useful to have advance git parsing.

Could possibly use https://github.com/linrongbin16/giturlparser.lua

This would allow for some neat features:

  • Opening directly to a file:

https://github.com/moyiz/git-dev.nvim/blob/5808fe220441a7ed70a2882d91912353337c6035/README.md

  • Opening up with a specific branch checked out:

https://github.com/neovim/neovim/tree/release-0.9

Happy to help with a PR.

Thanks for the awesome plugin!

Feature: GitDevClean command

It would be nice to have a GitDevClean command to clean the current repo if no args are passed, which would close all buffers that are open containing files from the repo and then delete the repo directory.

The command could also support an argument to clean a specific repo, e.g. GitDevClean moyiz/git-dev.nvim would only clean the given repo.

Additionally, it would be nice if the repo name was tab-completable from the list of local cached repos.

Bug: Parser fails with GitLab URL

The following URL fails: https://gitlab.com/infai/rdf-diagram-framework/-/blob/typeScriptRewrite/rdf-diagram-drawio/src/plugins/rdf-diagram.ts

Feature: Promote an ephemeral repo into a persisted repo

Cool plugin! It would be cool if there were a command which would make an ephemeral cloned repo into a persistent, editable repo. For example, :GitDevPersist or :GitDevSave. Maybe with an optional argument to move the repo's root directory to a new path.

Feature request: Open file in local repo if it exists

I am using this function in tridactyl to run git-dev directly from the browser:

command browse_repo composite get_current_url | js -p tri.excmds.exclaim("alacritty -e zsh -c \"vi -c \'GitDevOpen " + JS_ARG + "\'\"")

But now there are two different kinds of repos:

  1. ephemeral ones for git-dev
  2. the local Repos I am working on

I do not want to just set the repositories_dir var to my local repos. (like you advised)

Could git-dev then use two repositories dirs one for caching and one for only for opening.

So that then GitDevOpen foo does the following:

  1. Check if the repo already exists in the local folder
    2a. If it exists: Open the file from local
    2b. If it does not exist, do the shallow clone in ephemeral...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.