I've found that separating work from recording that work is the easiest approach. So what I end up doing is waiting the code and tests and making sure things work as expected. Only then will I start recording that work into a sensible series of commits.
To do this, I'll generate the diff of my work relative to the base branch and then stage parts of that work using the git apply command and make a commit out of it.
If it turns out that I need to update what I changed in a commit I already made, I'll just make a fixup commit (by creating a commit with the changes and titling it with
fixup! Exact title of commit
This way, when I run a rebase with the autosquash parameter, git will rearrange the commits such that the fixup commit is listed right after the commit I want to amend.
If I just need to update the commit message itself, I'll do the same thing except that I use the squash! prefix instead of fixup!. I then make the commit using the allow-empty option (making sure I have no changes states in the git index). During the autosquash rebase, this brings up the editor where I can delete the text except for the updates commit message I typed earlier.
That sounds like too long to wait to commit, by my judgement. There's too much risk of loss of work, or at least annoyance getting it back via editor undo.
What about making smaller commits and going back and editing it later? You could either 'git reset --soft' to "undo" some commit(s) and then craft them how you like, or anything else. Seems like your git skills should easily be good enough to handle that.
By the way, you know about 'git add -p', right? That's a fancier way to do what I think you're doing with diff and apply, though yours may work better for you.
Backups can be made independent of source control.
> What about making smaller commits and going back and editing it later?
In experience, it's actually more time consuming to restructure an existing set of commits (especially if they're making changes to the same files and same lines of code) compared to just making a new set of commits.
> By the way, you know about 'git add -p', right?
I am aware of it, but I'm not a fan of the menu based approach. When I stage code with git apply, I actually use vim's feature of visually highlighting the part of the diff I want to stage and filtering those lines through the git apply command. I can skip things like debug statements I've added to the code when staging. I can also unstage code by using the same command with the -R parameter.
I even commit within vim by reading the output of git status -v, writing my commit message and filtering that message through git commit with the -F parameter to have it read from standard input.
> Backups can be made independent of source control.
They can be, but it's ~hard to do it in a way that lines up with saving all your work, and you miss out on conveniences like the reflog (eg what was the state of _this_ branch half an hour ago?) Zfs snapshots can do it if they're manual or ~minutes apart and automatic, but that's a pretty blunt tool for this IMO.
> In experience, it's actually more time consuming to restructure an existing set of commits [...]
That's fair, but you _can_ just do that. If you have the commits in the first place, you have the choice. 'git reset --soft master' for example is "I don't want those commits on this branch anymore, but I want the final changes to rework into new commit(s)". I'd say about half the time I nuke the commits, half the time I rework them or they were just good enough already.
Sounds like you have a workflow that works for you though, just throwing out ideas in case they help.
> I am aware of it, but I'm not a fan of the menu based approach.
Agreed, it's sometimes tedious. I'll look into the way you do it, seems like for larger ~features your way might be less annoying.
> Backups can be made independent of source control.
To be fair there's a middle ground. You can just commit as WIP and force push to save progress. Then undo that commit and start making the individual commits. Everyone's happy.
Thanks to tpope, vim comes with some out of the box editing support for rebase¹. So, you don't even need to use change word. :F[ixup] to change to fixup, which supports ranges too(super useful with <count>V or V<movement>).
The standard vim {in,de}crement commands are bound to :Cycle in rebase buffers as well, so you can use <C-{a/x}> from anywhere in the line to twiddle the command for the current commit.
I still don't really understand why so many devs seem to have a need for this. For me every commit I make is a good one that I would like to keep in the history. Why should I need to reorder or rename or group any of my commits? All my work (of days or even weeks) is neatly containted within one branch, and that branch gets merged at the end. Why is that not ok? Why should I hide my work history into one "blob" commit instead of just having the Merge commit on the master branch?
The only use I could imagine is, if you have very short-lived branches (i.e. 1-2 days) with lots of random commits. Like e.g. "Oh I'm going for lunch, lets commit."
> every commit I make is a good one that I would like to keep in the history
This is certainly not typical.
When working in a team, you may need to push something half-finished so that someone else can work on what they need to. Or testing something before refactoring.
For these scenarios, rebase is very handy for cleaning things up before merging.
Why can't the merged branch contain commits that don't compile? As long as it has a clean commit message, I don't see the issue. It's just part of the history. On that day you e.g. committed some broken prototype code so your teammate could improve it. So?
If I clean up something I only do it for new commits before they are ever pushed to remote. My rule is basically to never rewrite history, that someone else might already have checked out on their machine.
It feels like there are as many git workflows as developers. Personally, for almost all of my development I work on a single commit at a time. When I'm positive some changed code is going to be in the final commit I `git commit --amend` it to the tip of the main branch. Unstaged changes are what is in progress. I use `git show` to see the stuff I'm done with but haven't pushed and `git diff` to see the stuff that's in progress. Sometimes I want to undo something I've committed... I either do it manually or use aliases to help out. I rebase frequently to bring in changes from other devs.
Yeah, I've long been a fan of rewriting my feature branches to clean them up before pushing. I wrote an extensive blog post last year describing useful Git techniques and usage patterns [0], and I tried to emphasize that as a key point.
I use SourceTree as my primary Git GUI, largely _because_ it has a UI specifically for interactive rebasing. Fork is another good GUI with interactive rebase support.
That looks like it solves my number one annoyance: maintaining a bunch of concurrent patches on a dev branch, including personal patches I don't intend to merge.
Personally, the only GUI I have ever used is GitKraken during my studies. Since then I do a lot of my work on remote servers so CLI has been much more convenient.
I know that some people say using CLI from the beginning is best for learning git but the visualisation from a GUI definitely helped me get to grips with the concepts.
To do this, I'll generate the diff of my work relative to the base branch and then stage parts of that work using the git apply command and make a commit out of it.
If it turns out that I need to update what I changed in a commit I already made, I'll just make a fixup commit (by creating a commit with the changes and titling it with
This way, when I run a rebase with the autosquash parameter, git will rearrange the commits such that the fixup commit is listed right after the commit I want to amend.If I just need to update the commit message itself, I'll do the same thing except that I use the squash! prefix instead of fixup!. I then make the commit using the allow-empty option (making sure I have no changes states in the git index). During the autosquash rebase, this brings up the editor where I can delete the text except for the updates commit message I typed earlier.