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

Add new command "Move commits to new branch" #3876

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

stefanhaller
Copy link
Collaborator

  • PR Description

Add a new command "Move commits to new branch" (bound to N by default), which is useful if you have just started some new work, you already made some commits, and then you realize that you forgot to create a new branch first, accidentally making those commits on main or whichever other feature branch you happened to be on.

This commit prompts you to type a name for the new branch; it then creates this new branch starting from the current branch (so unlike the n command, the selection doesn't matter), hard-resets the current branch to its upstream, and checks out the new branch. If you had uncommitted changes in your working copy, those are automatically stashed and brought along.

Inspired by Magit's magit-branch-spinoff command.

The conditions under which the command is available are rather restrictive: the current branch must have an upstream, it must not be behind its upstream, but it must be ahead of it (otherwise there wouldn't be any commits to move, and you might as well just create a new branch normally).

  • Please check if the PR fulfills these requirements
  • Cheatsheets are up-to-date (run go generate ./...)
  • Code has been formatted (see here)
  • Tests have been added/updated (see here for the integration test guide)
  • Text is internationalised (see here)
  • If a new UserConfig entry was added, make sure it can be hot-reloaded (see here)
  • Docs have been updated if necessary
  • You've read through your own file changes for silly mistakes etc

@stefanhaller stefanhaller added the enhancement New feature or request label Aug 30, 2024
The long story: I want to call this function from RefsHelper; however, I can't
make WorkingTreeHelper a field of RefsHelper because RefsHelper is already a
field in WorkingTreeHelper, so that would be a circular dependency.

The shorter story: there's really little reason to have to instantiate a helper
object in order to call a simple function like this. Long term I would like to
get to a state where a lot more of these helper functions are free-standing, and
you pass in the data they need.

While at it, simplify the implementation of AnyStagedFiles and AnyTrackedFiles
to one-liners.
GetDisabledReason: self.canMoveCommitsToNewBranch,
Description: self.c.Tr.MoveCommitsToNewBranch,
Tooltip: self.c.Tr.MoveCommitsToNewBranchTooltip,
},
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't totally sure in which views to make the command available. We could consider making it global, because it doesn't depend on the selection at all. However, I found it important that the command shows up next to New Branch in the keybindings panel, so I decided to make it available in local branches and in BasicCommitsController (see code comment there).

Copy link

codacy-production bot commented Aug 30, 2024

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
Report missing for 05ae0801 85.71%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (05ae080) Report Missing Report Missing Report Missing
Head commit (67a61e4) 49902 42730 85.63%

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#3876) 175 150 85.71%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

Codacy stopped sending the deprecated coverage status on June 5th, 2024. Learn more

Footnotes

  1. Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

Copy link
Owner

@jesseduffield jesseduffield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM. One thing that would be good is if we could change the prompt or add a description in a secondary panel so that we're being clear about what we're about to do: right now there's no way to know whether you've pressed 'n' or 'N' after pressing the key, because the two prompts are identical.

@stefanhaller
Copy link
Collaborator Author

Code LGTM. One thing that would be good is if we could change the prompt or add a description in a secondary panel so that we're being clear about what we're about to do: right now there's no way to know whether you've pressed 'n' or 'N' after pressing the key, because the two prompts are identical.

I thought about this, or rather about an extra confirmation panel that comes before ("are you sure you want to..."). After all, a hard reset is involved, and this feels like a confirmation is in order. But then I realized that there's really no potential for losing data with the current approach, so I left it out. I can look into changing the prompt text though.

However, while testing it more I noticed that this only really works in the expected way when you start on master. If you start on an unrelated feature branch, it will create the new branch as a stacked branch on top of that one, which might sometimes be what you want if you are into stacked branches, but most of the time it's probably not. You want to create the new branch off of master most of the time.

If we want to achieve this, it has a few consequences:

  • we can provide the command only in the branches panel, and the selection becomes important. The new branch will be created off of the selected branch. (Probably a good thing because that makes it more similar to n.)
  • the simple approach of creating the new branch and hard resetting the other one is not enough. We do actually need to cherry-pick the commits to bring them over (except in the stacked branches example, so we need to check if the new branch contains the commits already).
  • this means we can now get conflicts, both when cherry-picking and when popping the auto-stash. Neither could happen with the current approach.

Very keen to hear your thoughts on this @jesseduffield

@jesseduffield
Copy link
Owner

we can provide the command only in the branches panel, and the selection becomes important. The new branch will be created off of the selected branch. (Probably a good thing because that makes it more similar to n.)

I actually don't think that's a good thing: the purpose is to move the commits from the checked out branch to a new branch. The selection shouldn't matter.

However, while testing it more I noticed that this only really works in the expected way when you start on master. If you start on an unrelated feature branch, it will create the new branch as a stacked branch on top of that one, which might sometimes be what you want if you are into stacked branches, but most of the time it's probably not. You want to create the new branch off of master most of the time.

I'm fine if we just only allow this command from the main branch

@stefanhaller
Copy link
Collaborator Author

we can provide the command only in the branches panel, and the selection becomes important. The new branch will be created off of the selected branch. (Probably a good thing because that makes it more similar to n.)

I actually don't think that's a good thing: the purpose is to move the commits from the checked out branch to a new branch. The selection shouldn't matter.

Hm, ok. That means we wouldn't provide the option of creating a new branch stacked on the current one. Maybe that's fine as it should be rather rare, but I do remember that I was in this situation once or twice.

What we need to do then is to create the new branch off of the current branch's base branch.

I'm fine if we just only allow this command from the main branch

This restriction would make it almost useless for me. I almost never have main checked out, so I don't accidentally make commits there; I'm much more likely to run into the problem while I'm on an unrelated feature branch.

@stefanhaller stefanhaller mentioned this pull request Sep 6, 2024
@jesseduffield
Copy link
Owner

I see. For whatever reason I seem to only get into this state from the master branch.

If we need to handle the case where this happens on a feature branch, then it seems to me that we'd need to specify two things: the commits we want to move, and the branch we want to use as the new base (defaulting to master). So I'm thinking we could make it that the keybinding is only present from the commits panel (after you've selected the commits to move) and the branch is specified via an input prompt. Or as you say, we could just programmatically work out the base branch; I'm fine with that too.

Then we would:

  1. copy the commit hashes
  2. checkout a new branch off of the specified base
  3. cherry-pick the commits
  4. hard reset the original branch to remove the commits (which we can do without checking it out, see here)

If there are conflicts with the cherry-pick, we could just skip the fourth step and leave that as an exercise for the user.

What do you think of that?

@stefanhaller
Copy link
Collaborator Author

Hm, I'm not sure. What you propose would only work if the selected range is at the end of the branch. Otherwise we'd have to drop the commits from the branch, which is not a simple reset, so there's potential for conflicts both at the source and the destination.

I'm not sure we need this much flexibility though; I think I'd be fine with restricting it to moving the unpushed commits. If you have the need to move an arbitrary commit range (whether at the end of the branch or in the middle), you still have to do that manually, like today.

So I'm still in favor of offering the command only in the branches panel; you select the target branch, and it works on the unpushed commits of the current branch. If you're on a branch that doesn't have an upstream (yet), the command is disabled.

My feeling is that this will cover most real-world cases, but of course that's hard to tell without using it for a while and seeing how much it helps.

@jesseduffield
Copy link
Owner

Okay that works for me.

@jesseduffield
Copy link
Owner

@stefanhaller are you waiting on my re-review for this PR or were there more changes to push?

@stefanhaller
Copy link
Collaborator Author

No, I was planning to implement the behavior that I proposed above, but didn't get around to it yet. Setting to draft in the meantime to make that clearer.

@stefanhaller stefanhaller marked this pull request as draft September 28, 2024 06:31
@jesseduffield
Copy link
Owner

No worries

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
Development

Successfully merging this pull request may close these issues.

2 participants