This is a tricky problem but one that many people face. I prefer using the Gitflow setup as a starting point.
Development -> New stuff being worked on
Master -> Finished stuff needing testing
Production -> Stuff that has been published to production.
At minor (shorter) features I create a branch from development do the work there then merger the branch back to development.
At major (long term) features I create a branch from development, create smaller branches from that branch, then merge back to the first branch. Once the major feature is complete then back into development branch it goes.
At regular intervals (depends on project) I merge development back into master and a testing cycle begins. If any fixes come up in testing they are done in the master branch (sub branch then merge in). And development can continue on the master branch during testing.
At any time master should be merged into development, and development should be merged to any of it's long term sub branches.
master should always (in theory) be ready for production. Development should always (in theory) be ready for production. The only reason there is a difference it to provide a solid set of features for testers to test.
When ready, a commit in master that is tested is merged in to production and deployment in production happens from that branch. HOTFIXs that need to be done in an emergency can then take place on Production branch without having to merge in master (which may have many un-tested changes).
My normal Tree looks like
LongTerm -> Development -> Master -> Production
LongTerm <- Development | |
| Development -> Master |
LongTerm <- Development -> Master |
Development <- Master |
Master -> Production
It is my general rule that no single change should take more then a few hours. If it does then it needs to be made into smaller changes. If it's a huge feature (like a UI re-write) then that goes in long term so that normal development can continue at the same time. LongTerm branches are normally only local branches while Development, Master, and Production are remote branches. Any sub branches are also local only. This keeps the repository clean for others, without loosing the usefulness of git on a long term feature set.
I would like to note, however, that the existence of a long term branch is a rare thing. Normally, all my work is in development. Only when I have a feature (set) that is going to take so long that I need to be able to work on normal dev stuff too, do I use the LongTerm branch. If it's just a set of changes that should be together then I just don't merge to master till there all done.