I have a bash alias which just commits to my branch with "wip" and execute that regularly. When I'm done with my feature I rebase all commits and rewrite the message in the angular commit format with a thought through message and summary + ticket references
I have a rough history of what changed in case I need to revert but before actually merging the branch I want to make sure all is cleaned up for future reference.
I found it surprising how little people do that and just push with poor ungrounded messages
Similarly I am a very big fan of the phabricator flow with their arc cli which kind of works very similar
Or just clean up your local history before you push. Frequent commits are handy for checkpointing a known state so you can experiment freely and easily revert failed attempts; then, when you know you've got something good, you can squash the relevant commits and rewrite the messages to provide solid detail, and push a clean result.
Interactive rebase is an amazingly powerful tool - a little hard to use at first, but the investment of effort to learn its capabilities pays off manyfold. If you're looking for an easy way to level up, and you haven't investigated it before, I can't think of a better choice.
Tooling makes interactive rebasing a bit easier. Magit in Emacs makes it simple and it's just part of my normal flow now. The other thing you can do is do git add -p. In CLI, that menu sucks, but in emacs you can simply stage and unstage hunks or even individual lines from the diff with a few keys. Using those has made committing small chunks much smoother of an experience for me.
Having it in my editor is an important property for me, but that's good to hear! Getting more people comfortable with that is good for git and histories everywhere.
What magit offers over the CLI, in the area of reworking commits, is actually rather spectacular. It's more than interactive rebase -- it's e.g. the ability to selectively stage individual lines (or hunks or files) and chooose which commit to apply them to as a fix up. It's definitely worth trying as a non-emacs user; if you're interested I can post instructions for starting a one-off emacs instance for magit and nothing else.
Plus the ergonomics and mnemonics of the keys is really nice. You bring up a menu with a keypress, and then the most common command in that menu is typically just a second press of that key. E.g. c brings up commit menu and pressing c again to bring up the commit message writer, or c a for commit amend, etc. For branches, b b switches branches, l l for logs, r r for rebase continue, p p for push upstream, things like that. Then at any commit sha or file, you can move your point to it and hit enter to view that thing. Or just hit tab to see the diff. It's quite nice.
Completely agree. One of git's strengths is the flexibility to support many development styles and workflows. It's possible for each developer to follow their own style locally, but still have a consistent team style once commits land on 'master' following rebase.
Learning about interactive rebase greatly aided by local development flow, because it freed me from too much agonizing about commit structure before much code is written. It's a lot easier to decide how to structure commits to tell a story with the code in front of you.
Please don't do that. Development history like frequent commits and merges has enormous value, for bisecting and debugging. Once detroyed, it's never coming back.
What's the purpose of git commit tree at all if it doesn't truthfully represent the development process?
Why not preserve full commit history in feature branches, iterate with code reviews on pull requests, then squash when merging to an integration branch? This way master is clean and comprehensible, and there's an accurate, complete commit history available if needed.
That is exactly what I do, even if working on a project by myself.
My feature development branch will have a commit for each logical change, typically 3-5/day done by staging individual chunks with "git add -p" (exclusively) as a "I'm done with this piece" checkpoint, still leaving all debug and still in-progress parts in my working copy only. But I wouldn't hesitate to do a "I'm moving from my laptop to my desktop" work-in-progress commit either. Later commits will frequently undo changes done in earlier feature commits, or just revert a commit outright if it proved to go down the wrong development path. You really can't commit too often in my opinion, and it's incredibly freeing knowing you can make sweeping changes and then back out of them at a moments notice.
For merging, I will rebase on master, squash everything into 1 or a few logical commits, and _review_ the resulting commit diff manually as if I were to send it to a maintainer. I will write an extensive commit message based on the code review as a kindness to my future self. The development branch still exists and can be referred to at any time, but I don't want to see that cruft when looking at the project log.
Rebase is not destructive. It creates new patches based on the previous. This confusion is caused because a rebased branch tag is given a new home; however, you can always add another branch tag to the original HEAD so you can revisit it easily, and you can always find the original HEAD via the reflog. If the branch is not tagged, the default git policy is about a month before garbage collecting the untagged end.
Pre-push commit history is purely for the developer. A push is a publish, which means you are now presenting work for others to view and understand, code review, maintain, and use to debug. There is no more value to people to see every commit for a typo-fix, rollback, and redirection than there would be if our text files kept a complete and honest history of your every backspace. Nobody else wants to watch the rambling movie. BUT that movie can be important to you for a time, and can remain on your local git repo.
If the commit history is getting long in the tooth, it does help to clean it up fairly often, and I have found that cleanup for long-running branches to be much more valuable to my memory than trying to walk or bisect through every minute of my development process.
A quick note about merges: A merge should have something more interesting than "developer merged branch foo onto master"; a merge introduces something important, and old branch names may not exist in the future at all or on the same commits. If one follows this rule of thumb -- make commit messages useful -- then the fact that you were updating your branch with the latest master becomes unimportant. It is actually noise and confusion for anyone having to dig through commit histories, due to the massive number of forks. In other words, always rebase onto master first before merging.
I look at commit history as a document that I can polish and present to others. I look at my code the same way, trying to make it readable for other humans. This is writing prose. I group like things together. Each commit is a solid microfeature. There are no typo commits.
When you're debugging or reading code, you often want to know what a particular line of code was originally supposed to be doing. Maybe you want to know if it's OK to remove it, or you don't understand why it's written a certain way. If you can go back to the commit where the line was added and find some meaningful information about what that commit was supposed to be doing, that gives you a lot of information about what the code itself was for.
This leads to a commit style where each commit has a well-defined purpose rather than just being a snapshot of the code at a point in time. You make frequent commits while writing the code, but once it's working, you go back and create distinct commits for each distinct change. While you're splitting your changes up, you sometimes realize you forgot something, made a mistake, or left some junk around you didn't mean to commit, which is a nice secondary benefit.
The git history shouldn't reflect the dev process, it should be maximally useful to other devs. Bugs and mistakes should be squashed/fixup'd before merging.
One squashed commit per feature is also terrible. A commit should contain a set of changes that accomplishes one thing, whatever that means to you and your team.
I get your point about the development process representation, but I don't think you get the OP's point completely, there are ways to get there which do involve rebasing. I assume OP works the same way like I work; not every commit represents an interesting part of the development process (more like from the thought process while working) and rebasing afterwards is simply mandatory.
By example: this morning I was working on some huge refactoring and already had 5 short meaningful commits (not all with proper commit message yet) when I noticed some small bug creepd in so I added a line to fix it. That line then goes in it's own fixup commit for one of the earlier commits. Likewise, I noticed one of the new interfaces would be better if it had 2 more methods. So: new fixup commit again. A couple of commits later one logical piece of the work was done, so I did a rebase to get all the fixup commits amended to where they belong. Then I reviewed all commits carefully, rebasing again and squashing some commits because it made more sense to me like that, and adding/editing proper commit messages on the go. I work best like this (mainly for pretty large changes or new features which add a lot of code - for small fixes here and there it's obviously not as involved) and it does produce a perfecly viable story about the development process, useful to other readers, but only because of the rebasing. Otherwise it would be a mess. This to illustrate it's not always as simple and general as 'please don't do that rebasing madness'.
imagine if you could express that 'rebase' commit as a summary commit, without destroying history and history, but allowing you to present the change as a documented logical unit.
(i.e. what if git log just showed the highest level aggregate commit message by default)
commenting your changes properly doesn't have to involve destroying history - thats just an accidental failure of git that has caused a lot of people a lot of trouble
I do think it is a bit of a mistake for git UI tools to so prominently feature the DAG first instead of something more akin to `--first-parent` views.
(Especially in the way that a lot of old SVN/CVS users came into git wanting a simple straight line view and finding tools like rebase instead of `git log --first-parent`, we've ended up in a bit of a mess where people expect to do the heavy lifting in "sandblaster" tools like rebase instead of better UI/UX tools.)
You bring up a good point. Not all editing is a good thing.
There are two kinds of commits: permanent history and work-in-progress current stack of commits. The permanent history should not be edited except in extreme circumstance. The WIP stack should be edited mercilessly until it looks as good as possible.
In git, the separation between history and WIP is usually enforced by social conventions (e.g. a decree to the effect of "never rebase master, only rebase features branches"). Mercurial codifies this social convention into the repository by marking commits as public or as draft depending if they've been shared on a publishing repo or not, and forbidding editing public commits without a heavy use of `--force`.
By manually deciding when to `git commit` in the first place, you're already losing some history. Nobody argues to keep the history of every keystroke, so clearly there is some point in between that and "no history" that is useful.
The argument for rebase is that some of your local commits (especially ones like "going home for the day" or "whoops typo") are just as useless a level of detail as your other random keystrokes.
One of the teams I work on is pretty religious about one commit per PR and mandatory rebasing, which is a nuisance at times, particularly when one feature is made up of multiple different steps
e.g. refactor code to allow thing X to be pluggable, implement new alternative for thing X, load thing X if user has the right permissions all need to be squashed down to just "implement thing X"
I think I would dislike that style as well. My goals in committing and editing history are, in order:
1. Is master always safe and deployable?
2. Does the history tell the logical story?
3. Does the history tell the physical story?
That is, I prefer safety/deployability above all other considerations. Then I prefer a history that makes sense and can be comprehended. Then and only then do I care about the actual order and content of commits.
Rebasing down to a single commit satisfies 1, but ignores 2 and 3. In my experience 1 and 2 go together very well.
That kind of dogma does seem a bit silly, but why wouldn't you split the commits into separate pull requests instead of squasing, e.g. PR1: "refactor code to allow thing X to be pluggable", PR2: "implement new alternative for thing X" etc. The discussion around PR1 can then focus on just the plugin interface for example.
Each PR needs its own branch build and then a CI build after merging. So there'd be lots of downtime just screwing around with CI builds and resolving interim merge conflicts.
It's an overreaction to how messy our commits were before.
Some team members hate seeing merge commits, and our Stash system did a --no-ff merge previously. Plus there's a lot of contributors so people often had to merge master into their branch to resolve merge conflicts so they could merge, as there's sometimes 10 or more merges to master a day. So one branch might have 2-3 commits of work, and 2 merges from master. So our commit history might look like:
* Merge foo into master
* Merge master into foo
* Update foo per PR comments
* Merge master into foo
* Second meaningful commit on foo
* First meaningful commit on foo
This is not a long running branch, it could have been one that was started and had a PR opened at 11am and was reviewed and merged at 4pm.
So they instead configured our Stash instance to do an autosquash and if your PR didn't squash required it done manually.
Now it looks like
* Merge foo into master
* Merge bar into master
* Meaningful message about foobar from someone that does a manual rebase to avoid the autosquash
* Merge quu into master
I'm think the new situation is even worse, but I'm outvoted there.
I have this problem as well. My solution is to commit on a successful test. I still miss a few ("alright, now that foo is working let's get bar to work with it!"), but my cadence is much improved for it.
I also make an empty commit describing what the session's supposed to do, then amend it when done.
I find that pairing is good for this. One of us usually chimes in about whether we've reached a good commit point.
Sometimes we'll keep a running list of things we want to loop back to. For example, a rename to cuts across multiple files, some unrelated. I like those to be in separate commits for a cleaner history.
While it is, as others have pointed out, sometimes easier to commit "messily" and clean up the history.
This is very helpful in some cases, but I see it as tactical and not strategic. In particular it is easy to go far enough down a road that it becomes difficult to disentangle what ought to be separate commits, especially because you need to go back and run the tests on each independently before committing to master.
So if I see a decent commit point (the tests pass!) I will prefer to take it then and there, before turning to refactoring or to the next step in the work.
On those occasions where the ultimate solution was so unclear that we wandered around on a WIP branch for longer than a day or two, I sometimes forced into what I call Athena Commits (they spring fully-formed from the brow of Zeus).
I tend to hold to the short headline, but typically I find we write longer narrative blocks and then longer dotpoint lists of subsumed changes. I dislike this position but, if I have to do it, then my successors to deserve to know why it happened and what, in an ideal world, the shorter steps in smaller commits might have looked like.
One last tip: always always use `git add -p`. I've caught so many commented lines and half-baked thoughts and things intended for later commits this way.
Basically a lot of devs including myself tend to write a lot of code, then wait until the code is ready then we decide how to commit. Part of this is just we are focused on solving the problem, the other half is we like the commit to contain working code and code that looks pretty to read. Who would want to review code that's messy? I don't mind reading code that does not work for the sake of giving feedback whether one is heading in the right approach/direction.
I've known people who don't care about how good the code is, they just keep committing, stating that's okay, because they will make sure at some point, most likely the head will contain fully working and tested code. When they merge they don't squash commits, they just merge because they believe full history instead of the glorified end state. I only do this to my personal project which I really don't care if the project ever takes off or if I am starting out the project for the first few weeks. I just need to get my project working.
I usually take a trade off. I use the --patch option often to split my changes into logical commits. I also try to make sure when I have a working code I commit and if I need to fix a small typo here and there I just squash and rebase. For slightly larger change I can add that changeset as a new commit.
I recommend folks to get used to using gir add -p option. It isn't hard to use the 'e' (manual edit) function, since the 's' split option cannot split lines further for something like:
I have a seperate git gui open in the background all the time.
It shows me all the changes I made since last commit.
So coding without commiting a long time will result in lots of changes piled up there.
When that gets to much to overview or I feel the need for a savepoint (to do some experimentation) I commit.
When I didn't commit for a long time (mostly during long refactorings) I break up the changes into logical groups and commit them seperately using the interactive commiting feature. Using a good GUI to help me there improved that workflow a lot for me.
I loved this concept. I understand that practically we generally first write the whole code and then think about commits. By my manager always says "Coding is the last and easiest part of the job. Hard and creamy part of the job is to think about solutions and choose the best one among them." And this approach (given in dev.to) of thinking of the commit messages before actually coding gives a framework to that thought. That means before hand one is clear what to do (definitely during actual implementation we will find some extra nit-picks here and there and will add some extra code).
So with this style one can follow the style :-
Jot down the requirement/problem ==> Think of all solutions for the problem ==> Evaluate solutions ==> Choose best one ==> Jot down commit messages before hand ==> Implement solution ==> Use "git add -p" to separate logical units of your commit as per your commit messages already prepared ==> Tada !!
I'm using Visual Studio to both code and commit. In there is a "pending changes" window that allows me to cherry-pick the changes I've made and make small consistent commits instead of a big one.
I'm not sure if such a UI exists for other coding environments, but I definitely wouldn't bother doing it if I was using command line to commit.
I'd go the opposite direction: commit as often as you can. Ideally after each micro-iteration, when something new is working.
This way, at the end of the day you can just rebase the whole branch and squash all of the micro-commits in a whole commit implementing the whole new features.
It's nothing fancy, just rebasing (which is very very easy in git).
It's not so easy if you have changes belonging to different commits that are close together in code (e.g. affect the same lines). For instance, if you need to adjust formatting in a place where also functionality will change, it's extremely tedious to rebase if you have logical changes before and after the formatting commit.
Sadly, you're right, but while AST-based diff (vs text-as-code based) has yet to become mainstream, code formatting standards date back to the second ever programmer, and some of those are for good reason.
To use a trivial example, Python allows trailing commas for list items, eg: [1,2,]. With diffing in mind,
list_o_things = [
thing1,
thing2,
]
then, the diff to add "thing3," is a single line , rather than two in order to add a comma to the preceeding line.
The solution, then, is the code should be "properly" formatted at all times, to avoid running into a situation that goes formatting -> logical change -> formatting leading to the problem you describe.
There are similar hacks, where git's diff implementation supports ignoring white space, and doing word-based (vs line-based) diffing, but what we're really after is AST-based diffing tool(s).
This article exemplifies something I see novice programmers doing which it would be good to avoid. Basically, it places too much emphasis on "commit". This attitude does indeed lead newcomers to commit rarely. I think the word itself doesn't help; we shouldn't think of it as "commit", but rather "quick painless snapshot". The milestone beginners should aim for is where they commit frequently, and come to see the git UI as the way to change the state of their code (rather than the undo button in the editor!) If reworking commits were easier, people wouldn't think they had to be so perfect and they'd commit more. Magit can solve this, even for non emacs users.
Yeah, I remember a time when I never used to remember to commit at the right places, and I'd end up with a pile of work I had to laboriously sort through. I ended up writing a little bit of VimScript that helped me get into the habit of committing in the right places.
The crucial idea is to make the changelog a first-class file that I can see and interact with in my text editor alongside my code. As I bounce around my text editor, the changelog keeps popping up in front of me, making sure it's never too far from my thoughts, and encouraging me to write in it. Everytime I save the changelog, editor automation records my latest update to the changelog as a new commit message. The changelog now becomes conversational, a record of my thought process as I work through a problem. Commit messages go from cacophonous to harmonizing.
Over time I stopped needing these training wheels, but they were very helpful for a couple of years. More details: http://akkartik.name/codelog.html
I don't plan my commits. I start writing whatever seems necessary. It's kind of impossible to draw a map for a terrain you haven't seen yet. If I feel like, ok, now it's time to record what I've done, I use `hg commit --interactive` to pick apart my working directory into relevant things using a very pleasant curses interface for diff selection. I can always later on `hg amend` any commit that I think may need it.
Then there's the awesome `hg absorb` that will let me amend a whole stack of commits at once, automatically. It figures out which commit on earlier in my stack should get whatever change is in my working directory and automatically cherry-picks the right fix from my working directory to the right commit.
If I need to change gears, I make a temporary commit, labelled by some bookmark, and move to a different branch of my repo. If I want to throw out part of my working directory, I use `hg revert --interactive` or `hg shelve --interactive` depending on how permanent I want the throwing out to be. This uses the exact same curses interface as `hg commit --interactive`. I also rebase (i.e. change the base only, not rewrite the commit), fold/squash, and edit commit messages, authors or dates as needed. The best part is this all propagates metahistorically via changeset evolution (available now as an open beta on Bitbucket). It's all really nice, and I wish all of the Mercurial workflows for polishing commits were more well-known.
I have a basic script I wrote that uses inotify and commits on every save. Then I can easily just use the interactive rebase and squash commits together and have a clean history.
Yes, every save. The commit message is a timestamp and the file touched, which is usually enough for me to figure out what I did there, making the squashing relatively straightforward.
This isn't as bad as it sounds because git has tools built in to handle merging commits together and deleting unwanted commits through `git rebase -i`
This is the right answer. This makes easier to stay in flow because you are not distracted by side tasks like committing and have no worries about code getting lost. SVN was doing great here with autoversioning via WebDAV but git is even better because before you put in a PR you can squash your entire "run".
As for what the commit message is, who cares. I use uuidgen -r but whatever works. If the proverbial manure hits the circular cooling device and you need to back to a working version, you will work your way back via git log -p and similar tools anyways. It's more of a controlled undo than anything else.
This is not a meaningful phrase, you know that? It is a demonstration of illiteracy. I know the writer is trying to give the impression of making long pauses between the words, but this is not the function nor the meaning of dots in English.
It is a meaningful phrase, and maybe wikipedia doesn't have an entry for it, but it's a fairly common informal emphasis mechanism, not something the author made up.
If you can blithely produce the sentence (if you can call it that), "This is not a meaningful phrase, you know that?", I think you ought to be able to find it in your heart to accept the use of full stops as an indication of a lengthy pause.
I think it really depends of the project, the team size...
- When I work solo on a project, I do my "daily commit of work".
- On an open source project, I squash my commits at the end into one per "feature" or "issue".
- In a larger team, I usually work in a branch, then I submit the whole branch for review, this let people checkout the branch, test it and review it, without breaking their own workflow. After this, the branch is merged in the format that make the most sense for the project (usually, but not alway, 1 commit per branch is kept).
For the planning thing, I prefer to create issues (even for my own personal project).
Now you're making "planning" seem like a huge deal. No, sometimes you just want to do little change, that's what the article is talking about.
In other words: you commit a lot, right? Do you have an issue for each commit? If not, then you're not using "planning" in the same way OP is doing, and your comments about "planning" don't apply.
Most of my commits end up being "Yep" or "more stuff" or "Fixing tests". But before I make a pull request and/or push the branch to my remote, I squash them all together and
What you're saying about planning out/writing out what to do before the code, however, is still great advice. I end up doing that in the ticket description so I know what I'm doing before I touch any code.
My first memory of browsing a GitHub repository, just a little before I started using Git (and any version control whatsoever), is one of a repository that had a brief description of each file in it right in front of it.
Actually, the brief description was the commit message of the last commit that modified each file, you know what I'm talking about, but somehow in that particular repository they did some trick to make these commit messages appear as descriptions of each file.
Or maybe it is just an erroneous impression I had at that time.
Another thing I've learned over time is "don't be afraid to introduce commits before your current working state".
Let's say you're writing code and you need to use component X, and as you dig into it, you realize it's not factored the way you want. Rather than lumping a refactor of component X into one code review with all the rest, it's often better to save your work (using stash or commit), move your git HEAD before your latest work (using interactive rebase), do the refactor that you want, and then apply your other work on top of that refactor. If you're doing more work with component X and realize the refactor needs a tweak, or if you notice a bug from the refactor, you can go back and fix that commit using interactive rebase.
When you're happy with the refactor, you send it out as its own code review with a commit message mentioning "This is preparatory refactoring for feature Y". Then both the refactor commit and the feature commit are self-contained and easy for you to look over and easier for code reviewers to understand.
In Mercurial, all of this is a single `hg absorb` command. There's no need to stash, move the HEAD, or use interactive rebase. Just modify the working directory and `hg absorb` will figure out which commit back in your stack you really wanted to modify. The pre-refactored commits are also automatically marked as obsoleted by the edits, which means they're still there, just hidden by default, and they know which commits replace them.
I've seen a lot of people take the approach where they commit all the time with little things like "fix typo" or whatever, then clean it all up before sending to code review. One downside to that approach, though, it that it can make it harder to organize your work into small coherent chunks, each of which is a self-contained step that can be its own code review. My preference is to instead have one commit per code review, and use "git commit --amend" to make changes to those commits.
If you're worried about losing work with the "git commit --amend" approach, some things to keep in mind:
* Git keeps your history even when you use "git commit --amend"; it's in the reflog. So you can do "git reset HEAD@{1}" to undo an amend operation, etc.
* All JetBrains IDEs (IntelliJ, PyCharm, WebStorm, etc) have a "local history" feature that automatically keeps track of every change you've made to your code, independent of what git commits you made. I've found it useful several times, especially when I mess some git command up. If you're not using a JetBrains IDE, I'm sure there are other similar tools.
More generally, plan your work. For a big change, find valid intermediate states to get from where you are to where you want to be. Always keep the build working and tests passing but accept that there may be redundancy or inefficiency until you finish the series of changes.
Sometimes you have a change partially complete and realize you need to make another distinct change in order to finish this one. Have the discipline to stash this change, do that one in its own commit, then continue working on this one.
I don't plan my commits but think it's a good idea for certain types of developers.
I find that testing really helps me define commit borders. For instance, I'll often commit once each new endpoint is tested, new class (if small) is tested, etc. Testing is great because it defines a natural place to commit (once the tests pass and the tests describe the functionality). However, some things aren't tested (shame on me) and I find myself saying to commit once something is working.
Keeping a clean working tree is critical to me. As is using a visual commit tool. I often see better commit messages and chunking done with visual committing
68 comments
[ 1125 ms ] story [ 1230 ms ] threadI have a rough history of what changed in case I need to revert but before actually merging the branch I want to make sure all is cleaned up for future reference.
I found it surprising how little people do that and just push with poor ungrounded messages
Similarly I am a very big fan of the phabricator flow with their arc cli which kind of works very similar
Interactive rebase is an amazingly powerful tool - a little hard to use at first, but the investment of effort to learn its capabilities pays off manyfold. If you're looking for an easy way to level up, and you haven't investigated it before, I can't think of a better choice.
Learning about interactive rebase greatly aided by local development flow, because it freed me from too much agonizing about commit structure before much code is written. It's a lot easier to decide how to structure commits to tell a story with the code in front of you.
[0]: http://www.youtube.com/watch?v=4XpnKHJAok8
What's the purpose of git commit tree at all if it doesn't truthfully represent the development process?
My feature development branch will have a commit for each logical change, typically 3-5/day done by staging individual chunks with "git add -p" (exclusively) as a "I'm done with this piece" checkpoint, still leaving all debug and still in-progress parts in my working copy only. But I wouldn't hesitate to do a "I'm moving from my laptop to my desktop" work-in-progress commit either. Later commits will frequently undo changes done in earlier feature commits, or just revert a commit outright if it proved to go down the wrong development path. You really can't commit too often in my opinion, and it's incredibly freeing knowing you can make sweeping changes and then back out of them at a moments notice.
For merging, I will rebase on master, squash everything into 1 or a few logical commits, and _review_ the resulting commit diff manually as if I were to send it to a maintainer. I will write an extensive commit message based on the code review as a kindness to my future self. The development branch still exists and can be referred to at any time, but I don't want to see that cruft when looking at the project log.
Pre-push commit history is purely for the developer. A push is a publish, which means you are now presenting work for others to view and understand, code review, maintain, and use to debug. There is no more value to people to see every commit for a typo-fix, rollback, and redirection than there would be if our text files kept a complete and honest history of your every backspace. Nobody else wants to watch the rambling movie. BUT that movie can be important to you for a time, and can remain on your local git repo.
If the commit history is getting long in the tooth, it does help to clean it up fairly often, and I have found that cleanup for long-running branches to be much more valuable to my memory than trying to walk or bisect through every minute of my development process.
A quick note about merges: A merge should have something more interesting than "developer merged branch foo onto master"; a merge introduces something important, and old branch names may not exist in the future at all or on the same commits. If one follows this rule of thumb -- make commit messages useful -- then the fact that you were updating your branch with the latest master becomes unimportant. It is actually noise and confusion for anyone having to dig through commit histories, due to the massive number of forks. In other words, always rebase onto master first before merging.
I look at commit history as a document that I can polish and present to others. I look at my code the same way, trying to make it readable for other humans. This is writing prose. I group like things together. Each commit is a solid microfeature. There are no typo commits.
This leads to a commit style where each commit has a well-defined purpose rather than just being a snapshot of the code at a point in time. You make frequent commits while writing the code, but once it's working, you go back and create distinct commits for each distinct change. While you're splitting your changes up, you sometimes realize you forgot something, made a mistake, or left some junk around you didn't mean to commit, which is a nice secondary benefit.
One squashed commit per feature is also terrible. A commit should contain a set of changes that accomplishes one thing, whatever that means to you and your team.
By example: this morning I was working on some huge refactoring and already had 5 short meaningful commits (not all with proper commit message yet) when I noticed some small bug creepd in so I added a line to fix it. That line then goes in it's own fixup commit for one of the earlier commits. Likewise, I noticed one of the new interfaces would be better if it had 2 more methods. So: new fixup commit again. A couple of commits later one logical piece of the work was done, so I did a rebase to get all the fixup commits amended to where they belong. Then I reviewed all commits carefully, rebasing again and squashing some commits because it made more sense to me like that, and adding/editing proper commit messages on the go. I work best like this (mainly for pretty large changes or new features which add a lot of code - for small fixes here and there it's obviously not as involved) and it does produce a perfecly viable story about the development process, useful to other readers, but only because of the rebasing. Otherwise it would be a mess. This to illustrate it's not always as simple and general as 'please don't do that rebasing madness'.
(i.e. what if git log just showed the highest level aggregate commit message by default)
commenting your changes properly doesn't have to involve destroying history - thats just an accidental failure of git that has caused a lot of people a lot of trouble
(Especially in the way that a lot of old SVN/CVS users came into git wanting a simple straight line view and finding tools like rebase instead of `git log --first-parent`, we've ended up in a bit of a mess where people expect to do the heavy lifting in "sandblaster" tools like rebase instead of better UI/UX tools.)
There are two kinds of commits: permanent history and work-in-progress current stack of commits. The permanent history should not be edited except in extreme circumstance. The WIP stack should be edited mercilessly until it looks as good as possible.
In git, the separation between history and WIP is usually enforced by social conventions (e.g. a decree to the effect of "never rebase master, only rebase features branches"). Mercurial codifies this social convention into the repository by marking commits as public or as draft depending if they've been shared on a publishing repo or not, and forbidding editing public commits without a heavy use of `--force`.
The argument for rebase is that some of your local commits (especially ones like "going home for the day" or "whoops typo") are just as useless a level of detail as your other random keystrokes.
e.g. refactor code to allow thing X to be pluggable, implement new alternative for thing X, load thing X if user has the right permissions all need to be squashed down to just "implement thing X"
1. Is master always safe and deployable? 2. Does the history tell the logical story? 3. Does the history tell the physical story?
That is, I prefer safety/deployability above all other considerations. Then I prefer a history that makes sense and can be comprehended. Then and only then do I care about the actual order and content of commits.
Rebasing down to a single commit satisfies 1, but ignores 2 and 3. In my experience 1 and 2 go together very well.
It's an overreaction to how messy our commits were before.
Some team members hate seeing merge commits, and our Stash system did a --no-ff merge previously. Plus there's a lot of contributors so people often had to merge master into their branch to resolve merge conflicts so they could merge, as there's sometimes 10 or more merges to master a day. So one branch might have 2-3 commits of work, and 2 merges from master. So our commit history might look like:
* Merge foo into master
* Merge master into foo
* Update foo per PR comments
* Merge master into foo
* Second meaningful commit on foo
* First meaningful commit on foo
This is not a long running branch, it could have been one that was started and had a PR opened at 11am and was reviewed and merged at 4pm.
So they instead configured our Stash instance to do an autosquash and if your PR didn't squash required it done manually.
Now it looks like
* Merge foo into master
* Merge bar into master
* Meaningful message about foobar from someone that does a manual rebase to avoid the autosquash
* Merge quu into master
I'm think the new situation is even worse, but I'm outvoted there.
I also make an empty commit describing what the session's supposed to do, then amend it when done.
Sometimes we'll keep a running list of things we want to loop back to. For example, a rename to cuts across multiple files, some unrelated. I like those to be in separate commits for a cleaner history.
While it is, as others have pointed out, sometimes easier to commit "messily" and clean up the history.
This is very helpful in some cases, but I see it as tactical and not strategic. In particular it is easy to go far enough down a road that it becomes difficult to disentangle what ought to be separate commits, especially because you need to go back and run the tests on each independently before committing to master.
So if I see a decent commit point (the tests pass!) I will prefer to take it then and there, before turning to refactoring or to the next step in the work.
On those occasions where the ultimate solution was so unclear that we wandered around on a WIP branch for longer than a day or two, I sometimes forced into what I call Athena Commits (they spring fully-formed from the brow of Zeus).
I tend to hold to the short headline, but typically I find we write longer narrative blocks and then longer dotpoint lists of subsumed changes. I dislike this position but, if I have to do it, then my successors to deserve to know why it happened and what, in an ideal world, the shorter steps in smaller commits might have looked like.
One last tip: always always use `git add -p`. I've caught so many commented lines and half-baked thoughts and things intended for later commits this way.
I've known people who don't care about how good the code is, they just keep committing, stating that's okay, because they will make sure at some point, most likely the head will contain fully working and tested code. When they merge they don't squash commits, they just merge because they believe full history instead of the glorified end state. I only do this to my personal project which I really don't care if the project ever takes off or if I am starting out the project for the first few weeks. I just need to get my project working.
I usually take a trade off. I use the --patch option often to split my changes into logical commits. I also try to make sure when I have a working code I commit and if I need to fix a small typo here and there I just squash and rebase. For slightly larger change I can add that changeset as a new commit.
I recommend folks to get used to using gir add -p option. It isn't hard to use the 'e' (manual edit) function, since the 's' split option cannot split lines further for something like:
You use 'e' mode to split.My commits drop into the repo like I save. Frequently and often, out of sheer paranoia.
There is no plan, and there will never be a plan. The goal is to preserve work, not weave a tale, and tickle the reader's intuition.
My commits land multiple times an hour, Sometimes minutes apart, and they all have only one thing it common: they don't break the build.
They don't tell a story. They don't reassure you, that someday it will all make sense. There is no elegant pattern to it all.
Be glad you get any of my commits at all. Be glad I make useful changes that move the needle in the right direction.
I'm not here to play into someone else's pedantry and neurotic preferences.
When I didn't commit for a long time (mostly during long refactorings) I break up the changes into logical groups and commit them seperately using the interactive commiting feature. Using a good GUI to help me there improved that workflow a lot for me.
So with this style one can follow the style :-
Jot down the requirement/problem ==> Think of all solutions for the problem ==> Evaluate solutions ==> Choose best one ==> Jot down commit messages before hand ==> Implement solution ==> Use "git add -p" to separate logical units of your commit as per your commit messages already prepared ==> Tada !!
I'm not sure if such a UI exists for other coding environments, but I definitely wouldn't bother doing it if I was using command line to commit.
The '-p' flag is also supported on reset and checkout commands.
https://github.com/tpope/vim-fugitive
This way, at the end of the day you can just rebase the whole branch and squash all of the micro-commits in a whole commit implementing the whole new features.
It's nothing fancy, just rebasing (which is very very easy in git).
To use a trivial example, Python allows trailing commas for list items, eg: [1,2,]. With diffing in mind,
then, the diff to add "thing3," is a single line , rather than two in order to add a comma to the preceeding line.The solution, then, is the code should be "properly" formatted at all times, to avoid running into a situation that goes formatting -> logical change -> formatting leading to the problem you describe.
There are similar hacks, where git's diff implementation supports ignoring white space, and doing word-based (vs line-based) diffing, but what we're really after is AST-based diffing tool(s).
This is why I love gofmt(1).
The crucial idea is to make the changelog a first-class file that I can see and interact with in my text editor alongside my code. As I bounce around my text editor, the changelog keeps popping up in front of me, making sure it's never too far from my thoughts, and encouraging me to write in it. Everytime I save the changelog, editor automation records my latest update to the changelog as a new commit message. The changelog now becomes conversational, a record of my thought process as I work through a problem. Commit messages go from cacophonous to harmonizing.
Over time I stopped needing these training wheels, but they were very helpful for a couple of years. More details: http://akkartik.name/codelog.html
Then there's the awesome `hg absorb` that will let me amend a whole stack of commits at once, automatically. It figures out which commit on earlier in my stack should get whatever change is in my working directory and automatically cherry-picks the right fix from my working directory to the right commit.
http://files.lihdd.net/hgabsorb-note.pdf
If I need to change gears, I make a temporary commit, labelled by some bookmark, and move to a different branch of my repo. If I want to throw out part of my working directory, I use `hg revert --interactive` or `hg shelve --interactive` depending on how permanent I want the throwing out to be. This uses the exact same curses interface as `hg commit --interactive`. I also rebase (i.e. change the base only, not rewrite the commit), fold/squash, and edit commit messages, authors or dates as needed. The best part is this all propagates metahistorically via changeset evolution (available now as an open beta on Bitbucket). It's all really nice, and I wish all of the Mercurial workflows for polishing commits were more well-known.
If it is, what do you assign to each commit?
This isn't as bad as it sounds because git has tools built in to handle merging commits together and deleting unwanted commits through `git rebase -i`
Think of it as cheap time travel.
As for what the commit message is, who cares. I use uuidgen -r but whatever works. If the proverbial manure hits the circular cooling device and you need to back to a working version, you will work your way back via git log -p and similar tools anyways. It's more of a controlled undo than anything else.
This is not a meaningful phrase, you know that? It is a demonstration of illiteracy. I know the writer is trying to give the impression of making long pauses between the words, but this is not the function nor the meaning of dots in English.
https://en.wikipedia.org/wiki/Full_stop
https://english.stackexchange.com/questions/248861/how-commo...
https://english.stackexchange.com/questions/41078/name-and-o...
- When I work solo on a project, I do my "daily commit of work".
- On an open source project, I squash my commits at the end into one per "feature" or "issue".
- In a larger team, I usually work in a branch, then I submit the whole branch for review, this let people checkout the branch, test it and review it, without breaking their own workflow. After this, the branch is merged in the format that make the most sense for the project (usually, but not alway, 1 commit per branch is kept).
For the planning thing, I prefer to create issues (even for my own personal project).
In other words: you commit a lot, right? Do you have an issue for each commit? If not, then you're not using "planning" in the same way OP is doing, and your comments about "planning" don't apply.
What you're saying about planning out/writing out what to do before the code, however, is still great advice. I end up doing that in the ticket description so I know what I'm doing before I touch any code.
Actually, the brief description was the commit message of the last commit that modified each file, you know what I'm talking about, but somehow in that particular repository they did some trick to make these commit messages appear as descriptions of each file.
Or maybe it is just an erroneous impression I had at that time.
Let's say you're writing code and you need to use component X, and as you dig into it, you realize it's not factored the way you want. Rather than lumping a refactor of component X into one code review with all the rest, it's often better to save your work (using stash or commit), move your git HEAD before your latest work (using interactive rebase), do the refactor that you want, and then apply your other work on top of that refactor. If you're doing more work with component X and realize the refactor needs a tweak, or if you notice a bug from the refactor, you can go back and fix that commit using interactive rebase.
When you're happy with the refactor, you send it out as its own code review with a commit message mentioning "This is preparatory refactoring for feature Y". Then both the refactor commit and the feature commit are self-contained and easy for you to look over and easier for code reviewers to understand.
Un autre monde est possible!
http://files.lihdd.net/hgabsorb-note.pdf
If you're worried about losing work with the "git commit --amend" approach, some things to keep in mind:
* Git keeps your history even when you use "git commit --amend"; it's in the reflog. So you can do "git reset HEAD@{1}" to undo an amend operation, etc.
* All JetBrains IDEs (IntelliJ, PyCharm, WebStorm, etc) have a "local history" feature that automatically keeps track of every change you've made to your code, independent of what git commits you made. I've found it useful several times, especially when I mess some git command up. If you're not using a JetBrains IDE, I'm sure there are other similar tools.
Sometimes you have a change partially complete and realize you need to make another distinct change in order to finish this one. Have the discipline to stash this change, do that one in its own commit, then continue working on this one.
I find that testing really helps me define commit borders. For instance, I'll often commit once each new endpoint is tested, new class (if small) is tested, etc. Testing is great because it defines a natural place to commit (once the tests pass and the tests describe the functionality). However, some things aren't tested (shame on me) and I find myself saying to commit once something is working.
Keeping a clean working tree is critical to me. As is using a visual commit tool. I often see better commit messages and chunking done with visual committing