Some checks failed
		
		
	
	Detach Plugins / check (FlyGrep.vim) (push) Has been cancelled
				
			Detach Plugins / check (GitHub.vim) (push) Has been cancelled
				
			Detach Plugins / check (JavaUnit.vim) (push) Has been cancelled
				
			Detach Plugins / check (SourceCounter.vim) (push) Has been cancelled
				
			Detach Plugins / check (cpicker.nvim) (push) Has been cancelled
				
			Detach Plugins / check (dein-ui.vim) (push) Has been cancelled
				
			Detach Plugins / check (git.vim) (push) Has been cancelled
				
			Detach Plugins / check (iedit.vim) (push) Has been cancelled
				
			Detach Plugins / check (scrollbar.vim) (push) Has been cancelled
				
			Detach Plugins / check (vim-chat) (push) Has been cancelled
				
			Detach Plugins / check (vim-cheat) (push) Has been cancelled
				
			Detach Plugins / check (vim-todo) (push) Has been cancelled
				
			Detach Plugins / check (xmake.vim) (push) Has been cancelled
				
			test / Linux (nvim, nightly) (push) Has been cancelled
				
			test / Linux (nvim, v0.3.8) (push) Has been cancelled
				
			test / Linux (nvim, v0.4.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.4.2) (push) Has been cancelled
				
			test / Linux (nvim, v0.4.3) (push) Has been cancelled
				
			test / Linux (nvim, v0.4.4) (push) Has been cancelled
				
			test / Linux (nvim, v0.5.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.5.1) (push) Has been cancelled
				
			test / Linux (nvim, v0.6.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.6.1) (push) Has been cancelled
				
			test / Linux (nvim, v0.7.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.7.2) (push) Has been cancelled
				
			test / Linux (nvim, v0.8.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.8.1) (push) Has been cancelled
				
			test / Linux (nvim, v0.8.2) (push) Has been cancelled
				
			test / Linux (nvim, v0.8.3) (push) Has been cancelled
				
			test / Linux (nvim, v0.9.0) (push) Has been cancelled
				
			test / Linux (nvim, v0.9.1) (push) Has been cancelled
				
			test / Linux (true, vim, v7.4.052) (push) Has been cancelled
				
			test / Linux (true, vim, v7.4.1689) (push) Has been cancelled
				
			test / Linux (true, vim, v7.4.629) (push) Has been cancelled
				
			test / Linux (true, vim, v8.0.0027) (push) Has been cancelled
				
			test / Linux (true, vim, v8.0.0183) (push) Has been cancelled
				
			test / Linux (vim, nightly) (push) Has been cancelled
				
			test / Linux (vim, v8.0.0184) (push) Has been cancelled
				
			test / Linux (vim, v8.0.1453) (push) Has been cancelled
				
			test / Linux (vim, v8.1.2269) (push) Has been cancelled
				
			test / Linux (vim, v8.2.2434) (push) Has been cancelled
				
			test / Linux (vim, v8.2.3995) (push) Has been cancelled
				
			test / Windows (nvim, nightly) (push) Has been cancelled
				
			test / Windows (nvim, v0.3.8) (push) Has been cancelled
				
			test / Windows (nvim, v0.4.2) (push) Has been cancelled
				
			test / Windows (nvim, v0.4.3) (push) Has been cancelled
				
			test / Windows (nvim, v0.4.4) (push) Has been cancelled
				
			test / Windows (nvim, v0.5.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.5.1) (push) Has been cancelled
				
			test / Windows (nvim, v0.6.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.6.1) (push) Has been cancelled
				
			test / Windows (nvim, v0.7.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.7.2) (push) Has been cancelled
				
			test / Windows (nvim, v0.8.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.8.1) (push) Has been cancelled
				
			test / Windows (nvim, v0.8.2) (push) Has been cancelled
				
			test / Windows (nvim, v0.8.3) (push) Has been cancelled
				
			test / Windows (nvim, v0.9.0) (push) Has been cancelled
				
			test / Windows (nvim, v0.9.1) (push) Has been cancelled
				
			test / Windows (vim, nightly) (push) Has been cancelled
				
			test / Windows (vim, v7.4.1185) (push) Has been cancelled
				
			test / Windows (vim, v7.4.1689) (push) Has been cancelled
				
			test / Windows (vim, v8.0.0027) (push) Has been cancelled
				
			test / Windows (vim, v8.0.1453) (push) Has been cancelled
				
			test / Windows (vim, v8.1.2269) (push) Has been cancelled
				
			test / Windows (vim, v8.2.2434) (push) Has been cancelled
				
			test / Windows (vim, v8.2.3995) (push) Has been cancelled
				
			docker / docker (push) Has been cancelled
				
			mirror / check (coding) (push) Has been cancelled
				
			mirror / check (gitee) (push) Has been cancelled
				
			mirror / check (gitlab) (push) Has been cancelled
				
			
		
			
				
	
	
		
			340 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			340 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local utils = require("neo-tree.utils")
 | 
						|
local events = require("neo-tree.events")
 | 
						|
local Job = require("plenary.job")
 | 
						|
local log = require("neo-tree.log")
 | 
						|
local git_utils = require("neo-tree.git.utils")
 | 
						|
 | 
						|
local M = {}
 | 
						|
 | 
						|
local function get_simple_git_status_code(status)
 | 
						|
  -- Prioritze M then A over all others
 | 
						|
  if status:match("U") or status == "AA" or status == "DD" then
 | 
						|
    return "U"
 | 
						|
  elseif status:match("M") then
 | 
						|
    return "M"
 | 
						|
  elseif status:match("[ACR]") then
 | 
						|
    return "A"
 | 
						|
  elseif status:match("!$") then
 | 
						|
    return "!"
 | 
						|
  elseif status:match("?$") then
 | 
						|
    return "?"
 | 
						|
  else
 | 
						|
    local len = #status
 | 
						|
    while len > 0 do
 | 
						|
      local char = status:sub(len, len)
 | 
						|
      if char ~= " " then
 | 
						|
        return char
 | 
						|
      end
 | 
						|
      len = len - 1
 | 
						|
    end
 | 
						|
    return status
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
local function get_priority_git_status_code(status, other_status)
 | 
						|
  if not status then
 | 
						|
    return other_status
 | 
						|
  elseif not other_status then
 | 
						|
    return status
 | 
						|
  elseif status == "U" or other_status == "U" then
 | 
						|
    return "U"
 | 
						|
  elseif status == "?" or other_status == "?" then
 | 
						|
    return "?"
 | 
						|
  elseif status == "M" or other_status == "M" then
 | 
						|
    return "M"
 | 
						|
  elseif status == "A" or other_status == "A" then
 | 
						|
    return "A"
 | 
						|
  else
 | 
						|
    return status
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
local parse_git_status_line = function(context, line)
 | 
						|
  context.lines_parsed = context.lines_parsed + 1
 | 
						|
  if type(line) ~= "string" then
 | 
						|
    return
 | 
						|
  end
 | 
						|
  if #line < 4 then
 | 
						|
    return
 | 
						|
  end
 | 
						|
  local git_root = context.git_root
 | 
						|
  local git_status = context.git_status
 | 
						|
  local exclude_directories = context.exclude_directories
 | 
						|
 | 
						|
  local line_parts = vim.split(line, "	")
 | 
						|
  if #line_parts < 2 then
 | 
						|
    return
 | 
						|
  end
 | 
						|
  local status = line_parts[1]
 | 
						|
  local relative_path = line_parts[2]
 | 
						|
 | 
						|
  -- rename output is `R000 from/filename to/filename`
 | 
						|
  if status:match("^R") then
 | 
						|
    relative_path = line_parts[3]
 | 
						|
  end
 | 
						|
 | 
						|
  -- remove any " due to whitespace or utf-8 in the path
 | 
						|
  relative_path = relative_path:gsub('^"', ""):gsub('"$', "")
 | 
						|
  -- convert octal encoded lines to utf-8
 | 
						|
  relative_path = git_utils.octal_to_utf8(relative_path)
 | 
						|
 | 
						|
  if utils.is_windows == true then
 | 
						|
    relative_path = utils.windowize_path(relative_path)
 | 
						|
  end
 | 
						|
  local absolute_path = utils.path_join(git_root, relative_path)
 | 
						|
  -- merge status result if there are results from multiple passes
 | 
						|
  local existing_status = git_status[absolute_path]
 | 
						|
  if existing_status then
 | 
						|
    local merged = ""
 | 
						|
    local i = 0
 | 
						|
    while i < 2 do
 | 
						|
      i = i + 1
 | 
						|
      local existing_char = #existing_status >= i and existing_status:sub(i, i) or ""
 | 
						|
      local new_char = #status >= i and status:sub(i, i) or ""
 | 
						|
      local merged_char = get_priority_git_status_code(existing_char, new_char)
 | 
						|
      merged = merged .. merged_char
 | 
						|
    end
 | 
						|
    status = merged
 | 
						|
  end
 | 
						|
  git_status[absolute_path] = status
 | 
						|
 | 
						|
  if not exclude_directories then
 | 
						|
    -- Now bubble this status up to the parent directories
 | 
						|
    local parts = utils.split(absolute_path, utils.path_separator)
 | 
						|
    table.remove(parts) -- pop the last part so we don't override the file's status
 | 
						|
    utils.reduce(parts, "", function(acc, part)
 | 
						|
      local path = acc .. utils.path_separator .. part
 | 
						|
      if utils.is_windows == true then
 | 
						|
        path = path:gsub("^" .. utils.path_separator, "")
 | 
						|
      end
 | 
						|
      local path_status = git_status[path]
 | 
						|
      local file_status = get_simple_git_status_code(status)
 | 
						|
      git_status[path] = get_priority_git_status_code(path_status, file_status)
 | 
						|
      return path
 | 
						|
    end)
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
---Parse "git status" output for the current working directory.
 | 
						|
---@base git ref base
 | 
						|
---@exclude_directories boolean Whether to skip bubling up status to directories
 | 
						|
---@path string Path to run the git status command in, defaults to cwd.
 | 
						|
---@return table table Table with the path as key and the status as value.
 | 
						|
---@return table, string|nil The git root for the specified path.
 | 
						|
M.status = function(base, exclude_directories, path)
 | 
						|
  local git_root = git_utils.get_repository_root(path)
 | 
						|
  if not utils.truthy(git_root) then
 | 
						|
    return {}
 | 
						|
  end
 | 
						|
 | 
						|
  local C = git_root
 | 
						|
  local staged_cmd = { "git", "-C", C, "diff", "--staged", "--name-status", base, "--" }
 | 
						|
  local staged_ok, staged_result = utils.execute_command(staged_cmd)
 | 
						|
  if not staged_ok then
 | 
						|
    return {}
 | 
						|
  end
 | 
						|
  local unstaged_cmd = { "git", "-C", C, "diff", "--name-status" }
 | 
						|
  local unstaged_ok, unstaged_result = utils.execute_command(unstaged_cmd)
 | 
						|
  if not unstaged_ok then
 | 
						|
    return {}
 | 
						|
  end
 | 
						|
  local untracked_cmd = { "git", "-C", C, "ls-files", "--exclude-standard", "--others" }
 | 
						|
  local untracked_ok, untracked_result = utils.execute_command(untracked_cmd)
 | 
						|
  if not untracked_ok then
 | 
						|
    return {}
 | 
						|
  end
 | 
						|
 | 
						|
  local context = {
 | 
						|
    git_root = git_root,
 | 
						|
    git_status = {},
 | 
						|
    exclude_directories = exclude_directories,
 | 
						|
    lines_parsed = 0,
 | 
						|
  }
 | 
						|
 | 
						|
  for _, line in ipairs(staged_result) do
 | 
						|
    parse_git_status_line(context, line)
 | 
						|
  end
 | 
						|
  for _, line in ipairs(unstaged_result) do
 | 
						|
    if line then
 | 
						|
      line = " " .. line
 | 
						|
    end
 | 
						|
    parse_git_status_line(context, line)
 | 
						|
  end
 | 
						|
  for _, line in ipairs(untracked_result) do
 | 
						|
    if line then
 | 
						|
      line = "?	" .. line
 | 
						|
    end
 | 
						|
    parse_git_status_line(context, line)
 | 
						|
  end
 | 
						|
 | 
						|
  return context.git_status, git_root
 | 
						|
end
 | 
						|
 | 
						|
local function parse_lines_batch(context, job_complete_callback)
 | 
						|
  local i, batch_size = 0, context.batch_size
 | 
						|
 | 
						|
  if context.lines_total == nil then
 | 
						|
    -- first time through, get the total number of lines
 | 
						|
    context.lines_total = math.min(context.max_lines, #context.lines)
 | 
						|
    context.lines_parsed = 0
 | 
						|
    if context.lines_total == 0 then
 | 
						|
      if type(job_complete_callback) == "function" then
 | 
						|
        job_complete_callback()
 | 
						|
      end
 | 
						|
      return
 | 
						|
    end
 | 
						|
  end
 | 
						|
  batch_size = math.min(context.batch_size, context.lines_total - context.lines_parsed)
 | 
						|
 | 
						|
  while i < batch_size do
 | 
						|
    i = i + 1
 | 
						|
    parse_git_status_line(context, context.lines[context.lines_parsed + 1])
 | 
						|
  end
 | 
						|
 | 
						|
  if context.lines_parsed >= context.lines_total then
 | 
						|
    if type(job_complete_callback) == "function" then
 | 
						|
      job_complete_callback()
 | 
						|
    end
 | 
						|
  else
 | 
						|
    -- add small delay so other work can happen
 | 
						|
    vim.defer_fn(function()
 | 
						|
      parse_lines_batch(context, job_complete_callback)
 | 
						|
    end, context.batch_delay)
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
M.status_async = function(path, base, opts)
 | 
						|
  git_utils.get_repository_root(path, function(git_root)
 | 
						|
    if utils.truthy(git_root) then
 | 
						|
      log.trace("git.status.status_async called")
 | 
						|
    else
 | 
						|
      log.trace("status_async: not a git folder: ", path)
 | 
						|
      return false
 | 
						|
    end
 | 
						|
 | 
						|
    local event_id = "git_status_" .. git_root
 | 
						|
    local context = {
 | 
						|
      git_root = git_root,
 | 
						|
      git_status = {},
 | 
						|
      exclude_directories = false,
 | 
						|
      lines = {},
 | 
						|
      lines_parsed = 0,
 | 
						|
      batch_size = opts.batch_size or 1000,
 | 
						|
      batch_delay = opts.batch_delay or 10,
 | 
						|
      max_lines = opts.max_lines or 100000,
 | 
						|
    }
 | 
						|
 | 
						|
    local should_process = function(err, line, job, err_msg)
 | 
						|
      if vim.v.dying > 0 or vim.v.exiting ~= vim.NIL then
 | 
						|
        job:shutdown()
 | 
						|
        return false
 | 
						|
      end
 | 
						|
      if err and err > 0 then
 | 
						|
        log.error(err_msg, err, line)
 | 
						|
        return false
 | 
						|
      end
 | 
						|
      return true
 | 
						|
    end
 | 
						|
 | 
						|
    local job_complete_callback = function()
 | 
						|
      utils.debounce(event_id, nil, nil, nil, utils.debounce_action.COMPLETE_ASYNC_JOB)
 | 
						|
      vim.schedule(function()
 | 
						|
        events.fire_event(events.GIT_STATUS_CHANGED, {
 | 
						|
          git_root = context.git_root,
 | 
						|
          git_status = context.git_status,
 | 
						|
        })
 | 
						|
      end)
 | 
						|
    end
 | 
						|
 | 
						|
    local parse_lines = vim.schedule_wrap(function()
 | 
						|
      parse_lines_batch(context, job_complete_callback)
 | 
						|
    end)
 | 
						|
 | 
						|
    utils.debounce(event_id, function()
 | 
						|
      local staged_job = Job:new({
 | 
						|
        command = "git",
 | 
						|
        args = { "-C", git_root, "diff", "--staged", "--name-status", base, "--" },
 | 
						|
        enable_recording = false,
 | 
						|
        maximium_results = context.max_lines,
 | 
						|
        on_stdout = vim.schedule_wrap(function(err, line, job)
 | 
						|
          if should_process(err, line, job, "status_async staged error:") then
 | 
						|
            table.insert(context.lines, line)
 | 
						|
          end
 | 
						|
        end),
 | 
						|
        on_stderr = function(err, line)
 | 
						|
          if err and err > 0 then
 | 
						|
            log.error("status_async staged error: ", err, line)
 | 
						|
          end
 | 
						|
        end,
 | 
						|
      })
 | 
						|
 | 
						|
      local unstaged_job = Job:new({
 | 
						|
        command = "git",
 | 
						|
        args = { "-C", git_root, "diff", "--name-status" },
 | 
						|
        enable_recording = false,
 | 
						|
        maximium_results = context.max_lines,
 | 
						|
        on_stdout = vim.schedule_wrap(function(err, line, job)
 | 
						|
          if should_process(err, line, job, "status_async unstaged error:") then
 | 
						|
            if line then
 | 
						|
              line = " " .. line
 | 
						|
            end
 | 
						|
            table.insert(context.lines, line)
 | 
						|
          end
 | 
						|
        end),
 | 
						|
        on_stderr = function(err, line)
 | 
						|
          if err and err > 0 then
 | 
						|
            log.error("status_async unstaged error: ", err, line)
 | 
						|
          end
 | 
						|
        end,
 | 
						|
      })
 | 
						|
 | 
						|
      local untracked_job = Job:new({
 | 
						|
        command = "git",
 | 
						|
        args = { "-C", git_root, "ls-files", "--exclude-standard", "--others" },
 | 
						|
        enable_recording = false,
 | 
						|
        maximium_results = context.max_lines,
 | 
						|
        on_stdout = vim.schedule_wrap(function(err, line, job)
 | 
						|
          if should_process(err, line, job, "status_async untracked error:") then
 | 
						|
            if line then
 | 
						|
              line = "?	" .. line
 | 
						|
            end
 | 
						|
            table.insert(context.lines, line)
 | 
						|
          end
 | 
						|
        end),
 | 
						|
        on_stderr = function(err, line)
 | 
						|
          if err and err > 0 then
 | 
						|
            log.error("status_async untracked error: ", err, line)
 | 
						|
          end
 | 
						|
        end,
 | 
						|
      })
 | 
						|
 | 
						|
      Job:new({
 | 
						|
        command = "git",
 | 
						|
        args = {
 | 
						|
          "-C",
 | 
						|
          git_root,
 | 
						|
          "config",
 | 
						|
          "--get",
 | 
						|
          "status.showUntrackedFiles",
 | 
						|
        },
 | 
						|
        enabled_recording = true,
 | 
						|
        on_exit = function(self, _, _)
 | 
						|
          local result = self:result()
 | 
						|
          log.debug("git status.showUntrackedFiles =", result[1])
 | 
						|
          if result[1] == "no" then
 | 
						|
            unstaged_job:after(parse_lines)
 | 
						|
            Job.chain(staged_job, unstaged_job)
 | 
						|
          else
 | 
						|
            untracked_job:after(parse_lines)
 | 
						|
            Job.chain(staged_job, unstaged_job, untracked_job)
 | 
						|
          end
 | 
						|
        end,
 | 
						|
      }):start()
 | 
						|
    end, 1000, utils.debounce_strategy.CALL_FIRST_AND_LAST, utils.debounce_action.START_ASYNC_JOB)
 | 
						|
 | 
						|
    return true
 | 
						|
  end)
 | 
						|
end
 | 
						|
 | 
						|
return M
 |