Skip to content

Commit

Permalink
Merge pull request #676 from morgsmccauley/feat/spin-off-branch
Browse files Browse the repository at this point in the history
  • Loading branch information
CKolkey authored Aug 3, 2023
2 parents 1d74014 + 5657ecf commit f06c256
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 3 deletions.
4 changes: 4 additions & 0 deletions lua/neogit/lib/git/status.lua
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ local status = {
unstage_all = function()
git.cli.reset.call()
end,
is_dirty = function()
local repo = require("neogit.lib.git.repository")
return #repo.staged.items > 0 or #repo.unstaged.items > 0
end,
}

status.register = function(meta)
Expand Down
40 changes: 40 additions & 0 deletions lua/neogit/popups/branch/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,46 @@ local function parse_remote_branch_name(ref)
return remote, branch_name
end

local function spin_off_branch(checkout)
if git.status.is_dirty() and not checkout then
notif.create("Staying on HEAD due to uncommitted changes", vim.log.levels.INFO)
checkout = true
end

local name = input.get_user_input("branch > ")
if not name or name == "" then
return
end

name, _ = name:gsub("%s", "-")
git.branch.create(name)

local current_branch_name = git.branch.current_full_name()

if checkout then
git.cli.checkout.branch(name).call_sync()
end

local upstream = git.branch.upstream()
if upstream then
if checkout then
git.log.update_ref(current_branch_name, upstream)
else
git.cli.reset.hard.args(upstream).call()
end
end
end

M.spin_off_branch = operation("spin_off_branch", function()
spin_off_branch(true)
status.refresh(true, "spin_off_branch")
end)

M.spin_out_branch = operation("spin_out_branch", function()
spin_off_branch(false)
status.refresh(true, "spin_out_branch")
end)

M.checkout_branch_revision = operation("checkout_branch_revision", function(popup)
local selected_branch = FuzzyFinderBuffer.new(git.branch.get_all_branches()):open_async()
if not selected_branch then
Expand Down
4 changes: 2 additions & 2 deletions lua/neogit/popups/branch/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ function M.create()
:action("l", "local branch", actions.checkout_local_branch)
:new_action_group()
:action("c", "new branch", actions.checkout_create_branch)
:action("s", "new spin-off")
:action("s", "new spin-off", actions.spin_off_branch)
:action("w", "new worktree")
:new_action_group("Create")
:action("n", "new branch", actions.create_branch)
:action("S", "new spin-out")
:action("S", "new spin-out", actions.spin_out_branch)
:action("W", "new worktree")
:new_action_group("Do")
:action("C", "Configure...", actions.configure_branch)
Expand Down
99 changes: 98 additions & 1 deletion tests/specs/neogit/operations_spec.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require("plenary.async").tests.add_to_env()
local async = require("plenary.async")
async.tests.add_to_env()
local eq = assert.are.same
local operations = require("neogit.operations")
local harness = require("tests.util.git_harness")
Expand Down Expand Up @@ -110,4 +111,100 @@ describe("branch popup", function()
assert.False(vim.tbl_contains(get_git_branches(), "second-branch"))
end)
)

describe("spin out", function()
it(
"moves unpushed commits to a new branch unchecked out branch",
in_prepared_repo(function()
util.system([[
git reset --hard origin/master
touch feature.js
git add .
git commit -m 'some feature'
]])
async.util.block_on(status.reset)

local input_branch = "spin-out-branch"
input.values = { input_branch }

local branch_before = get_current_branch()
local commit_before = get_git_rev(branch_before)

local remote_commit = get_git_rev("origin/" .. branch_before)

act("bS<cr><cr>")
operations.wait("spin_out_branch")

local branch_after = get_current_branch()

eq(branch_after, branch_before)
eq(get_git_rev(input_branch), commit_before)
eq(get_git_rev(branch_before), remote_commit)
end)
)

it(
"checks out the new branch if uncommitted changes present",
in_prepared_repo(function()
util.system([[
git reset --hard origin/master
touch feature.js
git add .
git commit -m 'some feature'
touch wip.js
git add .
]])
async.util.block_on(status.reset)

local input_branch = "spin-out-branch"
input.values = { input_branch }

local branch_before = get_current_branch()
local commit_before = get_git_rev(branch_before)

local remote_commit = get_git_rev("origin/" .. branch_before)

act("bS<cr><cr>")
operations.wait("spin_out_branch")

local branch_after = get_current_branch()

eq(branch_after, input_branch)
eq(get_git_rev(branch_after), commit_before)
eq(get_git_rev(branch_before), remote_commit)
end)
)
end)

describe("spin off", function()
it(
"moves unpushed commits to a new checked out branch",
in_prepared_repo(function()
util.system([[
git reset --hard origin/master
touch feature.js
git add .
git commit -m 'some feature'
]])
async.util.block_on(status.reset)

local input_branch = "spin-off-branch"
input.values = { input_branch }

local branch_before = get_current_branch()
local commit_before = get_git_rev(branch_before)

local remote_commit = get_git_rev("origin/" .. branch_before)

act("bs<cr><cr>")
operations.wait("spin_off_branch")

local branch_after = get_current_branch()

eq(branch_after, input_branch)
eq(get_git_rev(branch_after), commit_before)
eq(get_git_rev(branch_before), remote_commit)
end)
)
end)
end)

0 comments on commit f06c256

Please sign in to comment.