Neovim plugin to automatically adjust git env vars when syncing dotfiles using the "bare git repo" method

Initial commit

ejrichards 20fd4ef1

+1
.gitignore
···
+
.mise.local.toml
+21
LICENSE
···
+
MIT License
+
+
Copyright (c) 2024 ejrichards
+
+
Permission is hereby granted, free of charge, to any person obtaining a copy
+
of this software and associated documentation files (the "Software"), to deal
+
in the Software without restriction, including without limitation the rights
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+
copies of the Software, and to permit persons to whom the Software is
+
furnished to do so, subject to the following conditions:
+
+
The above copyright notice and this permission notice shall be included in all
+
copies or substantial portions of the Software.
+
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+
SOFTWARE.
+50
README.md
···
+
# Baredot
+
+
This is a Neovim plugin to automatically adjust `git` env vars when syncing dotfiles using the "bare git repo" method:
+
+
[Dotfiles: Best way to store in a bare git repository](https://www.atlassian.com/git/tutorials/dotfiles)
+
+
## Details
+
+
When launching and when changing directory (eg. `:cd`), the plugin will detect if the current
+
directory is in a git repo by searching for a `.git` folder.
+
+
- If a `.git` folder is found, the env vars are cleared and `git` will work as normal.
+
- If a `.git` folder is not found, and we are in `$HOME`, the env vars will be adjusted to use the
+
bare git repo. This will let other git plugins function using that repo.
+
+
## Setup
+
+
lazy.nvim
+
```lua
+
{
+
"ejrichards/baredot.nvim",
+
opts = {
+
git_dir = "~/.cfg" -- Change this path
+
}
+
}
+
```
+
+
## Configuration
+
+
```lua
+
{
+
-- These two options set the GIT_DIR and GIT_WORK_TREE env vars
+
-- They are expanded using "vim.fn.expand"
+
git_dir = "~/.cfg",
+
git_work_tree = "~",
+
-- Filename pattern to find that will disable Baredot
+
disable_pattern = "%.git"
+
}
+
```
+
+
## Commands
+
+
- `:BaredotInfo` - Print current status
+
- `:BaredotToggle` - Manually toggle the env vars on / off
+
+
## Functions
+
+
- `require("baredot").info()` - Print current status
+
- `require("baredot").toggle()` - Manually toggle the env vars on / off
+
- `require("baredot").set(boolean)` - Set the env vars on / off
+44
lua/baredot/commands.lua
···
+
local Commands = {}
+
+
---@type BaredotConfig
+
local options
+
+
function Commands.set(enable)
+
if enable then
+
vim.env.GIT_WORK_TREE = options.git_work_tree
+
vim.env.GIT_DIR = options.git_dir
+
else
+
vim.env.GIT_WORK_TREE = nil
+
vim.env.GIT_DIR = nil
+
end
+
end
+
+
function Commands.toggle()
+
if vim.env.GIT_DIR == nil or vim.env.GIT_WORK_TREE == nil then
+
Commands.set(true)
+
else
+
Commands.set(false)
+
end
+
Commands.info()
+
end
+
+
function Commands.info()
+
if vim.env.GIT_DIR == nil or vim.env.GIT_WORK_TREE == nil then
+
vim.notify("Baredot mode off", vim.log.levels.INFO, { title = "baredot.nvim" });
+
else
+
vim.notify(
+
"Baredot mode on: GIT_DIR=" .. vim.env.GIT_DIR .. " GIT_WORK_TREE=" .. vim.env.GIT_WORK_TREE,
+
vim.log.levels.INFO,
+
{ title = "baredot.nvim" }
+
);
+
end
+
end
+
+
function Commands.setup(opt)
+
options = opt
+
+
vim.api.nvim_create_user_command("BaredotInfo", Commands.info, { desc = "BaredotInfo" })
+
vim.api.nvim_create_user_command("BaredotToggle", Commands.toggle, { desc = "BaredotToggle" })
+
end
+
+
return Commands
+51
lua/baredot/init.lua
···
+
local commands = require("baredot.commands")
+
local scan = require("baredot.scan")
+
+
local Baredot = {}
+
+
---@class BaredotConfig
+
local defaults = {
+
git_dir = "~/.cfg",
+
git_work_tree = "~",
+
disable_pattern = "%.git"
+
}
+
+
---@param opt? BaredotConfig
+
function Baredot.setup(opt)
+
---@type BaredotConfig
+
local options = vim.tbl_deep_extend("force", {}, defaults, opt or {})
+
+
options.git_dir = vim.fn.expand(options.git_dir)
+
options.git_work_tree = vim.fn.expand(options.git_work_tree)
+
+
commands.setup(options)
+
scan.setup(options)
+
+
if scan.in_work_tree_no_dot_git() then
+
commands.set(true)
+
end
+
+
local group = vim.api.nvim_create_augroup("baredot", { clear = true })
+
vim.api.nvim_create_autocmd("DirChanged", {
+
group = group,
+
desc = "Baredot: scan for .git",
+
callback = function()
+
if vim.v.event.scope == "global" then
+
commands.set(scan.in_work_tree_no_dot_git())
+
end
+
end,
+
})
+
end
+
+
function Baredot.info()
+
return commands.info()
+
end
+
---@param enable boolean
+
function Baredot.set(enable)
+
return commands.set(enable)
+
end
+
function Baredot.toggle()
+
return commands.toggle()
+
end
+
+
return Baredot
+72
lua/baredot/scan.lua
···
+
local Scan = {}
+
+
---@type BaredotConfig
+
local options
+
+
-- Modified from ahmedkhalf/project.nvim
+
local function get_parent(path)
+
path = path:match("^(.*)/")
+
if path == "" then
+
path = "/"
+
end
+
return path
+
end
+
+
local function get_files(file_dir)
+
local files = {}
+
local dir = vim.loop.fs_scandir(file_dir)
+
if dir == nil then
+
return files
+
end
+
+
while true do
+
local file = vim.loop.fs_scandir_next(dir)
+
if file == nil then
+
return files
+
end
+
+
table.insert(files, file)
+
end
+
end
+
+
local function has(dir, pattern)
+
for _, file in ipairs(get_files(dir)) do
+
if file:match(pattern) ~= nil then
+
return true
+
end
+
end
+
return false
+
end
+
+
function Scan.in_work_tree_no_dot_git()
+
local search_dir = vim.fn.getcwd()
+
local work_tree_root = options.git_work_tree
+
if vim.fn.has("win32") > 0 then
+
search_dir = search_dir:gsub("\\", "/")
+
work_tree_root = work_tree_root:gsub("\\", "/")
+
end
+
+
while true do
+
if search_dir == work_tree_root then
+
return true
+
end
+
+
local pattern = options.disable_pattern
+
if has(search_dir, pattern) then
+
return false
+
end
+
+
local parent = get_parent(search_dir)
+
if parent == nil or parent == search_dir then
+
return false
+
end
+
+
search_dir = parent
+
end
+
end
+
+
function Scan.setup(opt)
+
options = opt
+
end
+
+
return Scan