Development - parent
This book is essential reading for users of all levels []
- One patch one change. E.g. don't change the URLs in the HTML and the indenting in the same patch, first do the whitespace changes, then do the content changes. This requires self control, but is made much easier one you know how to split your edits into multiple commits. (See below)
- In a patchset, do non-functional cleanup/cosmetic changes, or anything that will make the subsequent patches easier to review, first. Then do bugfix changes. Then do feature adds.
- One patch one change.
- Write meaningful commit messages. [] []
- One patch one change. I mean it.
- Work in your own repo, and always work on a branch. That way, your master can be kept exactly in step with the upstream master. Pull, and rebase your branch on the updated master regularly. That makes it trivial for the repo maintainer to pull your branch, verify it, and merge it onto master. Do not expect the repo maintainer to rebase your changes for you, that's your job.
- Get your patches reviewed. Add "Reviewed-by:", "Acked-by:", or "Signed-off-by:" as appropriate to the commit messages before pushing to your externally-visable repo.
Do not worry about having lots of small patches - every single one will hopefully be utterly trivial to review and verify to be correct.
I'm presuming you know how to:
- clone a repo (git clone)
- make a branch, and switch between branches (git branch, git checkout)
- commit changes (git add, git commit)
- fetch/pull remote branches (git fetch, git pull)
- rebasing your branch when upstream changes (git rebase)
- create a patchset for review (git format-patch), and mail that patchset to a mailing list (git send-email)
- create a pull request (git request-pull)
I can expand on any of those if needed.
Splitting Edits Into Multiple Commits
You've changed several things in a single, or multiple files, and you realise that really they ought to be in separate patches. Solution:
- git add -p
You can select each hunk, and even recurse into each hunk if it includes multiple changed parts. Once you've added all the bits that belong in the first patch:
- git commit -s
Add your meaningful commit message, then GOTO 10
Undoing a change you've just committed
Undo it in the editor, and then use git add (possibly add -p as above) to stage that change, and then git commit --amend to squash that change into the previous commit.
You can edit old commit messages, tweak old patches, squash two adjacent patches together, and even reorder the patches with git rebase -i
To re-order the last 6 patches:
- git rebase -i HEAD~6
If the patches touch the same code, you might need to deal with merge conflicts - search for <<< in the affected file(s), git add that file(s), then git rebase --continue. If the conflicts are too hard to resolve - just abort the whole rebase with git merge --abort
Do not rewrite any history that you've shared with anyone else, so anything that you've pushed to an externally-facing repo.
It is possible to do all kinds of clever things in the middle of a rebase. Sometimes you can get carried away and forget you're in the middle of a rebase. This can lead to confusion, and the inability to do various things, so I find it best to keep my rebases quick and simple, done in one session.
Deleting Branches From Your Public Repo
If you've put all of your changes into their own branch, which you should be doing, then you might notice you're accumulating branches. Delete them remotely by pushing nothing onto them.
- git push <remoterepo> :<remotebranchname>
Normally you'd use <sourcebranchname>:<remotebranchname>, but having nothing as the sourcebranchname is a special case just for deletion. Of course, you can delete your local branches with
- git branch -D <branchname>
Oh Shit. Oh Shit Oh Shit. Oh Shiiiiiiit!!!!
Yup, you've chopped a limb off. git rebase --abort and git merge --abort not there to save the day? Don't panic! If you remember the last commit you made that you'd be happy to return to, then use:
- git reflog
It will show you everything you've done recently. You can then:
- git reset --hard <hash>
To just jump back in time to that commit, and start again.
This scenario reinforces the "always work on a branch" policy. You don't want to accidentally rewrite the history of a branch (master, which you pulled from upstream) that isn't yours.
What Idiot Wrote That?
- git blame <filename>
Shows the commit where each line of code appeared in its current form. Use git show <hash> to see the whole patch where that line was added/changed, and see if you can work out what's what.