Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for detached work trees #397

Closed
psych3r opened this issue Oct 31, 2021 · 15 comments · Fixed by #600
Closed

Support for detached work trees #397

psych3r opened this issue Oct 31, 2021 · 15 comments · Fixed by #600
Labels
enhancement New feature or request

Comments

@psych3r
Copy link

psych3r commented Oct 31, 2021

Is your feature request related to a problem? Please describe.
I manage my dotfiles with a bare git repo setting --work-tree to $HOME but --git-dir to a different directory.
I interact with the repo with this command:
git --git-dir=$HOME/.cfg/ --work-tree=$HOME
Gitsigns does not work on my dotfiles ( it doesn't detect that $HOME is git repository).

Describe the solution you'd like
Expose a variable that allows providing extra arguments to the git command used by gitsigns.
Gitgutter does this, and I'm able to do something like:

if getcwd() == $HOME
    let g:gitgutter_git_args = '--git-dir=$HOME/.cfg/ --work-tree=$HOME'
else
    let g:gitgutter_git_args = ''
endif
@psych3r psych3r added the enhancement New feature or request label Oct 31, 2021
@lewis6991
Copy link
Owner

I don't think customizing arguments passed to git is the way to go. Gitsigns works quite differently to gitgutter and issues several git commands, one of them is to determine the gitdir which is then use for all subsequent git commands. We just need to improve that to accommodate what is essentially "detached gitdirs".

Proposal 1

Modify config.on_attach to allow pre-polulation of gitsigns buffer state:

local home = vim.fn.expand('$HOME')
require('gitsigns').setup{
  ...
  on_attach = function()
    if vim.fn.getcwd() == home then
      return {
        gitdir = home..'/.cfg'
      }
    end
  end
}

Proposal 2

Add config.detached_trees (or similar), to specify additional git checkouts to look at when attaching to a buffer. This could be a map of working tree to gitdir:

local home = vim.fn.expand('$HOME')
require('gitsigns').setup{
  ...
  detached_trees = {
    -- working tree -> gitdir
    [home] = home..'/.cfg'
  }
}

Personally I think proposal 2 is the way to go, but we may end up doing proposal 1 if we ever need to prepopulate other fields.

@psych3r
Copy link
Author

psych3r commented Nov 3, 2021

Both proposals seem reasonable to me.
Regarding the second proposal, can't we support other fields this way?

    -- working tree -> {  gitdir, otherFields }
    [home] = { gitdir = home..'/.cfg' , possiblefield = '' , ... }

I'm fairly new to Lua so excuse me if this is a dumb question...

@lewis6991
Copy link
Owner

lewis6991 commented Nov 3, 2021

Yes we could do that. What kind of fields could we possibly have? Also, it is possible to support both in a polymorphic way.

With that said I like the specific gitdir field more.

@psych3r
Copy link
Author

psych3r commented Nov 3, 2021

I don't have anything specific on mind regarding other fields.
My point was that should the need for other fields arise, it would be easy to support them with the second proposal.

@lewis6991
Copy link
Owner

Ok sure, we can go with that then.

@lewis6991 lewis6991 changed the title Provide a way to customize the arguments passed to git by gitsigns Support for detached work trees Nov 10, 2021
@SPFabGerman
Copy link

SPFabGerman commented Jan 1, 2022

Hi,

I too manage my dotfiles with a bare git repository and was looking to implement this with gitsigns.
I however found a very easy solution to the problem, using the GIT_* enviroment variables:

local gs = require("gitsigns")

local jid = vim.fn.jobstart({ "git", "rev-parse", "--git-dir" })
local ret = vim.fn.jobwait({jid})[1]
if ret > 0 then
    vim.env.GIT_DIR = vim.fn.expand("~/.dotfiles.git")
    vim.env.GIT_WORK_TREE = vim.fn.expand("~")
end

gs.setup()

I first check if git finds a git repository on it's own and if not, I set the enviroment variables. These variables get later used when gitsigns spawns it's git subprocesses. (I haven't tried if we can change the enviroment variables after setting up gitsigns, but this way is probably safer.) I used the "old" neovim job controll feature, instead of the "newer" event loop, since it is easier to work with in this case. (Mainly because the event loop doesn't have a jobwait equivalent.)

On another note: if you still want to implement a feature to do this, I think your first proposal would be preferable. This is because, the second proposal would not work with subdirectories. (E.g. when I edit a file like ~/foo/bar, but my cwd is not ~ but instead ~/foo.) Or at the very least it would be very hard to implement.

Anyway, I hope my workaround is helpful to you all!

@lewis6991
Copy link
Owner

Thanks this is really useful to know.

lewis6991 added a commit that referenced this issue Aug 9, 2022
Added config.worktrees.

Array of tables with the keys 'gitdir' and 'toplevel'.

If attaching normally fails, then each entry in the table is attempted.

Example:

  worktrees = {
    {
      toplevel = vim.env.HOME,
      gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
    }
  }

Resolves #397
lewis6991 added a commit that referenced this issue Aug 9, 2022
Added config.worktrees.

Array of tables with the keys 'gitdir' and 'toplevel'.

If attaching normally fails, then each entry in the table is attempted.

Example:

  worktrees = {
    {
      toplevel = vim.env.HOME,
      gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
    }
  }

Resolves #397
lewis6991 added a commit that referenced this issue Aug 9, 2022
Added config.worktrees.

Array of tables with the keys 'gitdir' and 'toplevel'.

If attaching normally fails, then each entry in the table is attempted.

Example:

  worktrees = {
    {
      toplevel = vim.env.HOME,
      gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
    }
  }

Resolves #397
@lewis6991
Copy link
Owner

Implemented a form of proposal 2.

Feedback welcome 😄

lewis6991 added a commit that referenced this issue Aug 9, 2022
Added config.worktrees.

Array of tables with the keys 'gitdir' and 'toplevel'.

If attaching normally fails, then each entry in the table is attempted.

Example:

  worktrees = {
    {
      toplevel = vim.env.HOME,
      gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
    }
  }

Resolves #397
@marwing
Copy link

marwing commented Aug 9, 2022

Hi,
I came across this issue a short while ago and have the same dotfiles in bare repo problem.
I have tested your pr and have some feedback on it.
Currently, the changes don't work correctly for object information (no signs):

run_job: git --no-pager --literal-pathspecs --git-dir /home/marwin/.dotfiles --work-tree /home/marwin rev-parse --show-toplevel --absolute-git-dir --abbrev-ref HEAD
run_job: git --no-pager --literal-pathspecs --git-dir=/home/marwin/.dotfiles -c core.quotepath=off ls-files --stage --others --exclude-standard --eol /home/marwin/.config/nvim/lua/user/setup/gitsigns.lua
attach(1): Empty git obj

You can see in the debug log above that you currently don't pass --work-tree to this command but you do to in the one above when testing the detached work tree. Passing the toplevel as cwd instead of the argument doesn't appear to work for me even when running manually in the shell. I was able to locally fix this by setting the --work-tree option instead of setting the cwd in git.lua (I don't have teal but the change should translate).

@marwing
Copy link

marwing commented Aug 9, 2022

Whoops, my local fix has the side effect that the every file I open outside the toplevel dir now shows up as all new lines (probably because git's cwd is toplevel and the filename passed to git is relative to neovim's cwd. Also files from other git repos (for example gitsigns.nvim appear to also be diffed against the toplevel dotfiles entry instead of the current git repo.

@lewis6991
Copy link
Owner

Well from my testing this works.

run_job: git --no-pager --literal-pathspecs --git-dir /Users/lewrus01/projects/dotfiles/.git --work-tree /Users/lewrus01 rev-parse --show-toplevel --absolute-git-dir --abbrev-ref HEAD
run_job: git --no-pager --literal-pathspecs --git-dir=/Users/lewrus01/projects/dotfiles/.git -c core.quotepath=off ls-files --stage --others --exclude-standard --eol /Users/lewrus01/.bashrc
try_worktrees(1): Using worktree {
  gitdir = "/Users/lewrus01/projects/dotfiles/.git",
  toplevel = "/Users/lewrus01"
}

Adding --work-tree to ls-files doesn't seem to be needed:

> git -C $HOME --git-dir=$HOME/projects/dotfiles/.git ls-files --stage --others --exclude-standard --eol $HOME/.bashrc
100644 163042adb51c8f7544f8785a0d886d424ed9ccaf 0       i/lf    w/lf    attr/                   .bashrc

Also, just to check is /home/marwin/.dotfiles your actually gitdir, or should it be /home/marwin/.dotfiles/.git?

@marwing
Copy link

marwing commented Aug 10, 2022

If I run the ls-files command logged by gitsignes.nvim (without the --work-tree set) I get the following output:

~/.config/nvim/lua/user/setup > git --no-pager --literal-pathspecs --git-dir=$HOME/.dotfiles -c core.quotepath=off ls-files --stage --others --exclude-standard --eol gitsigns.lua
fatal: this operation must be run in a work tree

The same happens when I pass -C $HOME.
If I add --work-tree I get the expected output:

~/.config/nvim/lua/user/setup > git --no-pager --literal-pathspecs --git-dir=$HOME/.dotfiles --work-tree=$HOME -c core.quotepath=off ls-files --stage --others --exclude-standard --eol gitsigns.lua
100644 53d1a1ecbbad8ca2bc885bbeed5e1a10f0c98b77 0       i/lf    w/lf    attr/                   gitsigns.lua

And $HOME/.dotfiles is in fact my actual git dir as this is initialized as a bare repository that I manage with a dotfiles shell alias:

~/.config/nvim/lua/user/setup >  which dotfiles
dotfiles: aliased to git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME

From this alias I know that all git operations work as expected when I add --git-dir and --work-tree to absolutely every command and don't change cwd for git manually.
I don't know if this difference comes from git working differently under macos (apple patches?) or maybe this just comes from a version mismatch of git (I'm running version 2.37.1) but this command doesn't work for me.

@lewis6991
Copy link
Owner

Hmm, I've gone ahead and added the --work-tree argument to all commands. Hopefully that works.

lewis6991 added a commit that referenced this issue Aug 10, 2022
Added config.worktrees.

Array of tables with the keys 'gitdir' and 'toplevel'.

If attaching normally fails, then each entry in the table is attempted.

Example:

  worktrees = {
    {
      toplevel = vim.env.HOME,
      gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
    }
  }

Resolves #397
@marwing
Copy link

marwing commented Aug 10, 2022

With 9b09c55 (the latest) I now get an error when launching neovim in a situation in which a worktree is tried (e.g. outside a 'normal' git repository):

Error executing luv callback:
...e/pack/packer/start/gitsigns.nvim/lua/gitsigns/async.lua:67: The coroutine failed with this message: ...ite/pack/packer/start/gitsigns.nvim/lua/gitsigns/git.lua:293: attempt to concatenate field 'toplevel' (a nil value)
stack traceback:
        ...ite/pack/packer/start/gitsigns.nvim/lua/gitsigns/git.lua: in function 'get_repo_info'
        ...ite/pack/packer/start/gitsigns.nvim/lua/gitsigns/git.lua:368: in function 'new'
        ...ite/pack/packer/start/gitsigns.nvim/lua/gitsigns/git.lua:594: in function 'new'
        ...im/site/pack/packer/start/gitsigns.nvim/lua/gitsigns.lua:235: in function 'fn'
        ...ack/packer/start/gitsigns.nvim/lua/gitsigns/debounce.lua:76: in function 'attach_throttled'
        ...im/site/pack/packer/start/gitsigns.nvim/lua/gitsigns.lua:316: in function 'attach'
        ...im/site/pack/packer/start/gitsigns.nvim/lua/gitsigns.lua:474: in function <...im/site/pack/packer/start/gitsigns.nvim/lua/gitsigns.lua:433>
stack traceback:
        [C]: in function 'error'
        ...e/pack/packer/start/gitsigns.nvim/lua/gitsigns/async.lua:67: in function 'callback'
        ...ite/pack/packer/start/gitsigns.nvim/lua/gitsigns/git.lua:195: in function 'callback'
        ...k/packer/start/gitsigns.nvim/lua/gitsigns/subprocess.lua:66: in function <...k/packer/start/gitsigns.nvim/lua/gitsigns/subprocess.lua:52>
Press ENTER or type command to continue

with this debug_log (after defining signs):

attach(1): Attaching (trigger=setup)
run_job: git --no-pager --literal-pathspecs config user.name
run_job: git --no-pager --literal-pathspecs rev-parse --show-toplevel --absolute-git-dir --abbrev-ref HEAD
Press ENTER or type command to continue

lewis6991 added a commit that referenced this issue Aug 10, 2022
Added config.worktrees.

Array of tables with the keys 'gitdir' and 'toplevel'.

If attaching normally fails, then each entry in the table is attempted.

Example:

  worktrees = {
    {
      toplevel = vim.env.HOME,
      gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
    }
  }

Resolves #397
@lewis6991
Copy link
Owner

Yes, probably best to wait for a CI pass before testing yourself. Should be ok now. Maybe best move this discussion to the PR as well.

lewis6991 added a commit that referenced this issue Aug 10, 2022
Added config.worktrees.

Array of tables with the keys 'gitdir' and 'toplevel'.

If attaching normally fails, then each entry in the table is attempted.

Example:

  worktrees = {
    {
      toplevel = vim.env.HOME,
      gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
    }
  }

Resolves #397
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
4 participants