-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Directly Changing Code Stored In Commits
I still do not know how best to name this feature. The idea is that often you find snippets of code in commits that shouldn't be there.
- You accidentally left a
console.log
or abinding.pry
line three commits back - You think that a chunk of code makes more sense in commit abc123 than it does in commit def456 where you've found it
- You realise a chunk of code is both incomplete and doesn't belong in the commit you've found it in, and you want to work on it as a whole some more and then put in a fresh commit when completed.
How might you address these situations on the git command line?
1.
Assuming the offending lines are on commit abc123:git rebase --interactive abc123^
- edit the todo file to change the 'pick' into an 'edit' for commit abc123
- in your editor, delete the offending lines
- stage the changes with
git add -A
git commit --amend
git rebase --continue
It's really that easy!
2.
- construct a patch (somehow) with the changes you want to copy from your source commit and save it to a file
git rebase --interactive dest123^
- edit the todo file to change the 'pick' into an 'edit' for commit dest123
git apply <patch> --index
git commit --amend
git rebase --continue
Here the fact that the changes appear in the destination commit means that when the rebase continues and reaches the source commit, the patch that you created will be omitted from the commit because it's already there in its parent commit.
- construct a patch (somehow) with the changes you want to copy from your source commit and save it to a file
git rebase --interactive source123^
- edit the todo file to change the 'pick' into an 'edit' for both commit source123 and commit dest123
- apply the patch in reverse with
git apply <patch> --index --reverse
git commit --amend
git rebase --continue
- once the rebase reaches the destination commit and halts, apply the patch forwards with
git apply <patch> --index
git commit --amend
git rebase --continue
Instead of dealing with patches you could have deleted the code from the source commit manually and then added it back in to the destination commit manually, but that can be hairy when you're dealing with multiple bits of code (admittedly that makes the patch approach hairy as well)
3.
- construct a patch (somehow) with the changes you want to copy from your source commit and save it to a file
git rebase --interactive source123^
- edit the todo file to change the 'pick' into an 'edit' for commit source123
- remove the code from the source commit with
git apply <patch> --index --reverse
git commit --amend
git rebase --continue
- Then apply the patch forwards so you can access it from your working tree/index with
git apply <patch> --index
If you've managed to read through the command line solutions to the 3 use cases you've probably (correctly) come away with the impression that doing this is really hairy and really tedious. I've experienced those emotions myself which is why I've made this feature!
How do we do these things in lazygit? Use case 2 is the most all-encompassing so we'll use that as our example.
Let's say our repo contains a grocery list with some random items, and we've added two additional commits, one for adding junk food, and one for adding dairy. But in the junk food commit we've got the 'milk' item appearing in the list. We should move that to the dairy commit.
(screenshots/video coming soon)
- Open lazygit and go to the commits panel by tapping right-arrow twice
- press 'enter' on the commit that has the code snippet you want to move to another commit
- press 'enter' on the file that has the code snippet
- use the arrow keys to move your cursor around the patch in the main left panel and use 'space' to select lines to be added to our custom patch. You can toggle selecting a range of lines with 'v' or select hunks with 'a'. You can also remove lines from the custom patch with 'd'
- Once you've selected the lines you want, hit escape until you're back at the commits panel, and then navigate to the commit you want to move the changes to
- press
ctrl+p
to bring up the patch options menu and select the second option
And you're done! If you encounter any merge conflicts, resolve them as you would any other merge conflict, and then press 'm' to bring up the merge/rebase options menu and select 'continue'. Repeat as required.
And that's it. Equal or fewer steps than the command line approaches, and far fewer keystrokes!
You may not need to 'stage' individual lines within a file's patch, in which case you can stage the whole file's patch by pressing 'space' on the file in the commit files panel, as you would in the regular files panel. Green filenames represent files where the whole patch has been added to our custom patch, and yellow represents only partial patches being added.
Under the hood lazygit is basically doing the same thing that you would be doing on the command line, but with a pinch of ingenuity sprinkled in to make patches more likely to apply.
Sometimes patches will not apply because the operation makes no sense, for example trying to cut and paste part of a delete-file patch into a later commit. And sometimes the patch won't apply because something else is awry. When this happens, the rebase will be aborted and you will not lose your changes. In case something does go wrong, the patches that were applied (or that were attempted to be applied) will be stored in the same directory as your lazygit config, under a directory matching the current repo name. That way you should never lose any code.
Currently you can only build up a custom patch for one commit at any time. If you want to add patches from another commit, you will need to discard the patch you had been building up until this point.