Verified Commit cba9759d authored by doshitan's avatar doshitan
Browse files

Moar pages

parent 93168bbe
title: Git Tips
toc: true
I love git, such an good tool. It's the only VCS I've ever used in anger and is
so ubiquitous now, hard to see seriously using something else^[Though I think
theoretically I prefer something like [darcs](, I'm usually
pushed back to git. I'm hopeful for [Pijul]( to improve upon
It also has a wild and amazing birth story, and in 2016 an added twist of irony.
just [read a bit about it]( and
As great a tool as it is, it's interface can leave much to be desired. This has
improved over the years, but it still remains not an easy tool to pick up.
Thankfully you can get pretty far learning just a few commands. To use it
effectively though takes some time to develop. I'll talk about how I tend to use
it and things I've learned in hopes it's useful.
# Config
[My git config](,
some behavior changing options set for my usual workflow (like `rerere`), but
mostly useful for the aliases.
I use Oh My Zsh and have it's [git
enabled. I tend to use my git aliases, but some of the shell aliases are nice, I
use `gst` *a lot*.
You might find some of the tools in [Git
Extras]( useful.
# pathspec
A less commonly learned, but useful bit of info to read and have as references
is the [pathspec
A useful pattern I use sometimes is `:^*/package-lock.json` for seeing info
about everything *but* the npm package lock files (which are just noise, but
tracked), e.g., `git diff master ":^*/package-lock.json"`.
# Searching History
`git log` is your friend. If you look at my config you'll see a few different
aliases for `git log` with various flags, all providing slightly different views
of the history. These are nice high-level views.
But we can do more than just look at a list of all commits of course. We can
search! The `-S` and `-G` flags will search commits for changes including the
search term, e.g., `git log -S hello` will list all commits that include changes
with `hello` in them^[This is called
[pickaxe]( So if you see/hear
the term "git pickaxe" this searching is what they are referring to.]. Of course
there are a huge number of other flags you can dive into, but note you can
combine these flags with aliases, so if you have views you like aliased, you can
just throw on a `-S` to limit the view to the changes you are interested in
(e.g., for me `git ll -S <term>`, `get lll -S <term>`, etc.).
You can even limit the search to just the history of specific functions
(`:func_name:<file>`) or lines (`10,+15:<file>` or `15,45:<file>`) of a file
with `-L`, though that's getting a little advanced.
`git blame <file>` can also be useful in navigating through the history of line,
though a using blame in a GUI makes it a lot nicer.
Sometimes you aren't interested in searching the changes of a commit, but the
commit message, e.g., looking for all commits mentioned a specific bug number,
which you can do with `git log --grep=<terms>`.
See more in the [official
# Commit messages
The first line should be "subject" or title of the change.
Write in the imperative present tense, that is, as if the commit does an action.
`Enable foo` is good, `Enabled foo` or `I enabled foo` are to be avoided. A
commit didn't do something, it does something.
The summary section should provide any further clarification on the work (esp.
anything non-obvious) and particularly *why* and big-picture *what*, we can see
the *how* by looking at the code.
Many others have written on this, [How to Write a Git Commit
Message]( by Chris Beams is detailed
and links to many of other big posts in this area.
# Rebase/rewriting history
I rebase a lot in my usual [workflow](#my-preferred-workflow). I rewrite history
a lot, sqaushing commits together, breaking them apart. Some folks are scared of
this. Others just hate it, there is an age old [philosophical
It's clear where I fall and not going to get into it here, just give some tips
on how I tend to use this stuff.
First, "rebase". Your work, as represented by a sequence of commits, come after
another commit, they are "based" on that commit, stem from it, so to "rebase",
means to choose a new "base" for your work, literally pick up a sequence of
commits and move them on top of a different starting commit.
When you just want to rebase on branch on another that has some new commits, a
plain `git rebase <branch>` is usually enough. The `--onto` flag to `git rebase`
comes into play in more complicated situations or when you just want to be
A simplified look at it, in the form of `git rebase --onto <branch> <commit
before the first one you want>`. Think of it as "pick up all commits after
<commit> and apply them on top of (i.e., onto) <branch>."
This comes into play a bunch when you are working on a feature branch (`feat-b`)
that depends on another feature branch (`feat-a`). As the other feature branch
continues development, maybe rebases on `master`, you want to catch your branch
up, just a `git rebase feat-a` from `feat-b` is likely to get confused. A `git
rebase --onto feat-a <commit before your work in feat-b>` can get it sorted.
Let's look at an example from the rebase help page:
First let’s assume your topic is based on branch next. For example, a feature developed in topic depends on some functionality which is found in next.
o---o---o---o---o master
o---o---o---o---o next
o---o---o topic
We want to make topic forked from branch master; for example, because the functionality on which topic depends was merged into the more stable master branch. We want our tree to look like this:
o---o---o---o---o master
| \
| o'--o'--o' topic
o---o---o---o---o next
We can get this using the following command:
git rebase --onto master next topic
Next let's talk about interactive rebases, `git rebase -i`. A supremely helpful
feature, it let's us manipulate each commit involved in a rebase, from simply
using it as is, rewording it's commit message, to combining commits or throwing
them out all together.
Let's say you have some work on a branch `new-wizbang-feature` and you want to
clean up it's history. You can run `git rebase -i master` on the branch, this
will bring up your editor with a list of commits with the work `pick` in front
of each. You options will be explained in the buffer as well, but you can change
that work `pick` to any of the other options to take that action on a commit.
When you stop to perform some action on a commit, issue `git rebase --continue`
(my alias `git rc`) to move on to the next action. If you ever get confused or a
rebase goes wrong, just run `git rebase --abort` (my alias `git ra`) and
everything will be undone and you can start again. This should also be explained
in the `git status` message.
And don't be too scared of breaking things, almost everything you do is
recoverable/reversable. `git reflog` is your friend.
- Read the [Git book chapter on rebasing](
# My preferred workflow
My general approach to a git project is simple, there is one branch, called
`master` (or whatever you want), this is "the project", it represents the latest
version of the thing.
Development happens in feature branches, when the feature is ready, it is
rebased and merged to master. I like a clean linear history, but if you like
merge commits, that's fine just make the merge commit message useful so when
viewing the history with `--first-parent` you can easily track what is actually
happening to the codebase.
Looking at the default GitHub template for merge commit messages for example,
`Merge pull request #xxx from <user>/<branch>`, isn't the worst if the branch
name is reasonable, but the information I most care about, what does this commit
do in terms of the code is pushed to the end of the line after a bunch of
gobbledygook. When looking through the history, it would be nicer to see `Do
thing` with the `Merge pull ...` details in the summary section, easily
available, but out of the way.
And I'm against using merges as an excuse to let a messy, meandering history
into the master branch. Some folks like including every part of the dev process
in their commit history including the false starts, dead ends, and intermediate
steps. I'm most interested in the final result of the work and how it impacts
the code. So while developing (or before landing if it comes to it), squash
things together logically so the history reads well.
If you went down an obvious path that didn't work out, then just write that in
the commit message, "The obvious approach would be x but due to y it doesn't
quite work out", "Tried a but went with b because..."
If I want the full back and forth on something, I'll pull up the code review
system with the full conversation and iteration the code went through.
So if you must do merges, instead of:
* Do thing
| * Fix
| * Oops, redo
| * crap
| * Try this?
| * WIP
| * WIP
| * WIP
| * Start thing
* Another thing
* New feature
| * Feedback fixes
| * Try this other thing
| * Feedback fixes
| * Totally new approach
| * WIP
| * WIP
Aim for something like:
* Do thing
| * New thing UI
| * Rework big subsystem
| * New thing API endpoints
* Another thing
* New feature
| * Utilize updated vendor to do thing
| * Update vendor component
But again, I prefer to work on things at the scale they can be logically grouped
and squashed up into a linear history.
* Do thing
* Another thing
* New feature utilizing updated vendor thing
Basically regardless of if you are rebasing or merging make the history easy to
follow, to be useful for *people reading the history*.
Versions get tagged, if fixes or features need backported, branch from
the tag, apply necessary changes and tag new release on that branch. If you
don't do "versions"/releases, but just regularly want to promote work to some
"stable" state, then have a `stable` branch that you update when appropriate.
Every commit that hits `master` should be "buildable"^[The definition of which
could vary based on the project.]. For the rare cases where there is some large
amount of inter-related simultaneous work from multiple people needs to land,
then make an "integration" branch or something to pull together the required
work for testing. But often, just pick an order to land things and work on the
features based on the other appropriate feature branches, rebasing often.
I guess really, just get comfortable rebasing. Frequent small rebases are often
ultimately easier and safer than one big one at the end. You'll probably want to
turn on [`rerere`]( to make
rebasing often less painful.
Lots of folks like git-flow. I think it's over-complicated and I've never be a
part of a project where it was helpful. But if it works for you then cool.
Rather than having a ton of branches interweaving with merges, I prefer to have
one trunk (`master`) with many (mostly temporary) branches from it for specific
Your needs may be different.
Some others thoughts:
- [GitFlow considered harmful](
- [Their suggested model](
# Misc. Stuff
- [Knowledge is Power: Getting out of trouble by understanding Git by Steve
Smith ]( (basic intro)
- When you need to nuke things:
- (very opinionated use of git)
title: Giving
toc: true
I'm an advocate for [[][Effective Altruism]] and I encourage you to read some of the
material on it's site, I won't reproduce it here. I think most people can get
behind the general idea of not giving to *feel good*, but giving to actually *do
good* (the most possible).
And not all organizations are equal in this regard. There are many worthy causes
and well intentioned people, but to maximize the impact of your dollar, charity
guides, like [[][Charity Navigator]] and [[][Charity Watch]], are helpful to compare
options. It's your money, and give wherever you believe in, but I'd recommend
giving the majority of your dollars to vetted and effective organizations.
If you are wondering how much to give, there's no absolute answer. Take care of
yourself and your own first. I personally aim to give about 10% of my income.
I'm still working up to this due to work/life things these past few years and
it's generally been around 4%. The number doesn't matter too much, even $5/month
to a single organization you think does good work is meaningful. And if you shop
at Amazon, be sure to setup and use [[][Amazon Smile]], costs you nothing.
Giving is [[][worth doing]]. A general list of [[][top charities]] might spur your mind.
Note that I am American and my recommendations are biased towards American orgs
or the American branch of orgs. Also this page is currently focused on giving
*money*, giving your time is another thing to consider.
* General
These are things most people will probably find worth helping.
- [[][Médecins Sans Frontières/Doctors Without Borders]] (MSF)
- [[][GiveWell]]
- [[][Electronic Frontier Foundation]] (EFF)
- [[][Effective Altruism Funds]] (some overlap with GiveWell and ACE in certain areas)
- [[][American Civil Liberties Union]] (ACLU) and your local branch
- Support media creators you like, news sources (such as your local NPR
station), etc., although you may technically get something back for this,
think about things you can be a "patron" for and help out just because you
want it to continue to exist.
* Personal
These are areas I find important, but understand others may not agree.
- [[][Animal Charity Evaluators]] (ACE)
- [[][Internet Archive]]
- [[][Public Resource]]
- Your local no-kill animal shelter
- Wichita: [[][Lifeline Animal Placement and Protection]] (LAPP)
- League of Women Voters and/or your local branch
- Misc. technology things
- My local dev group
- [[][bcacefs]]
- [[][GIMP]]
- [[][Signal]]
- [[][]]
- [[][Blender Development Fund]]
- [[][OfBorg]] (NixOS build bot)
title: Make Tips
toc: true
I tend to use make as a task runner, a purpose it is not expressly suited to,
but works well enough.
This own site's [[][makefile]].
Note I almost always use and build for [[][GNU Make]], which is [[#gnu-vs-bsd-make][different in some ways
than other makes]].
* Basics
See the [[][intro section]] of the GNU Make manual.
Basic format:
#+BEGIN_SRC makefile
# v-- filename of the thing the recipe builds or an action/task name
target: prerequisites ...
# ^-- other targets to build before this one
# v-- must start with a tab character, no spaces here
recipe # <-- lines to execute
Each line execute in a separate subshell, so you can not set variables inside a
If you have a long line, use backslashes to break it up across lines.
#+BEGIN_SRC make
command thing $(SOME_VAR) \
--a-flag \
--another-flag \
~@~ at the beginning of a line suppresses make echoing the command.
#+BEGIN_SRC makefile
echo "Hello, world"
#+BEGIN_SRC bash
$ make echo
echo "Hello, world"
"Hello, world"
#+BEGIN_SRC makefile
@echo "Hello, world"
#+BEGIN_SRC bash
$ make echo
"Hello, world"
~-~ at the beginning of a line suppresses errors on that line from killing
target build.
#+BEGIN_SRC makefile
-rm -f *.o
Finishes successfully even if the ~rm~ errors (like if there are no ~.o~ files
to clean).
You can combine them:
#+BEGIN_SRC makefile
@-rm -f *.o
Doesn't say what it's doing or care if it errors.
If make is called without a target, it will run the first target listed in the
makefile, unless the ~.DEFAULT_GOAL~ variable is set, then whatever that says
gets run.
* Capitalize ~makefile~ or not
It [[][doesn't really matter]]. ~Makefile~ is conventional.
Note that GNU make searches for ~makefile~ before ~Makefile~, so if both are
present (why?), the lowercase one wins.
I prefer lowercase ~makefile~ as it's ever so slightly easier to type and it's
usually surrounded by other lowercased names so it looks better to me to match.
* Make as a task runner
Often in projects you have a number of commands that you may wish to run on
occasion. There are many existing task runner solutions out there, the
JavaScript world in particular seems to love to create them. I shy away from
these as I think often they are overcomplicated for the task at hand.
If a project has a JavaScript dependency, often it will use the ~scripts~
section in it's ~package.json~ file, which can easily be run with ~npm run
<script name>~. I dislike this for a few reasons:
1. I avoid JavaScript dependencies as much as possible
2. I generally want a package manager to be responsible for one thing, getting packages
3. I prefer to have npm scripts disabled globally as they are a [[][security hazard]]
Advantages of make:
- Been around forever
- Available everywhere and probably already installed
- Just let's you write shell scripts
- Easy/automatic [[#shell-tab-complete][tab-completion support]] for targets in most shells
Disadvantages of make:
- Old and thus not purposely designed for today's environment
- It's ways and appearances can be odd to the uninitiated, or to the initiated
that have been away for a time
For me, a simple makefile strikes a good balance for many projects.
My suggested approach is this:
1. At the beginning, use a simple makefile for common tasks
2. For bigger tasks, write a standalone script, store it in a ~scripts/~
directory and call the script in the makefile
3. When you get to the point where you have lots of scripts or some complicated
behavior, write a custom application for managing your project. This might
literally just be a CLI library wrapping some scripts, but it's very nice
having a management interface specifically tuned to your project (and having
a proper programming language to build it in). Use a build system library
like [[][Shake]] if needed.
* Shell tab-complete
Many shells support autocomplete for make targets, zsh ships with support by
default, there is often a ~bash-completion~ package for your system which will
enable it in bash.
This is pretty handy, to be able to just type ~make~ then hit
@@html:<kbd>Tab</kbd>@@ to see a list of targets and have tab-completion for
finishing target names.
* Variables
There are [[][two flavors of variables]] in make. In short, variables defined with ~=~
are /recursively expanded/ and their value is evaluated every time they are
referenced, which has performance consequences if it's defined in terms of functions.
The other kind of variables are defined with ~:=~ which are /simply expanded
variables/ that get evaluated only once, when make first encounters them. These
behave much more understandably and should be preferred by default.
There's also another form, ~?=~ which sets a variable only if it hasn't been
defined, which can be useful to define defaults for variables you may want to
inherit from the environment. More on that in [[#passing-arguments-to-make-targets][passing arguments]].
* Passing arguments to make targets
Okay, so there are basically two ways to get values to a target.
** From environment
All environment variables[fn::Except ~MAKE~ and ~SHELL~.] [[][become make variables]]
inside the makefile. This is probably the most common way to modify makes
behavior. If you were to do:
#+BEGIN_SRC bash
THING=val OTHER_THING=val2 make thing
~$(THING)~ and ~$(OTHER_THING)~ would be available in the makefile.
The important thing about inheriting environment variables is that they are only
inherited if they are not set in the makefile[fn::Unless you set ~-e~ or use the
~override~ directive on the variable.]. For instance, if our makefile was:
#+BEGIN_SRC makefile
msg = hello
@echo $(msg)
And then try:
#+BEGIN_SRC bash
$ msg=world make echo
The output is ~hello~, *not* ~world~.
This is where the ~?=~ variable definition comes in.
If we had instead written our makefile like:
#+BEGIN_SRC makefile
msg ?= hello
@echo $(msg)
#+BEGIN_SRC bash
$ msg=world make echo
So in short, define variables with ~?=~ if you want to enable the variables to
be overridden by an environment variable (which is usually what you want).
** As arguments to make
You can also set make variables by passing them as arguments to make. They can
come before or after the target name.
#+BEGIN_SRC bash
make THING=val OTHER_THING=val2 thing
#+BEGIN_SRC bash
make thing THING=val OTHER_THING=val2
Unlike environment variables, it doesn't matter how these variables are defined
in the makefile, their value is what is set in the argument, any definitions
for them are ignored in the makefile.
** Arbitrary arguments
Okay so setting arguments is cool, but what if you want to pass arbitrary
arguments to a target? Well, just use a general variable name, like ~args~.
Write your target like:
#+BEGIN_SRC makefile
ls $(args)
Which you could then call like:
#+BEGIN_SRC bash
$ make ls args='-la ~'
# ls -la ~
You can even set default arguments on a per-target basis, say:
#+BEGIN_SRC makefile
ls: args=~/
ls $(args)
run: args=--flag -t file
command $(args)
But if you *really* don't want to have to type ~args=""~, you can bend and contort
to support passing the other arguments to make directly to a target. Reproduced
from [[][this StackOverflow answer]].
Write your makefile like:
#+BEGIN_SRC makefile
# If the first argument is "run"...
ifeq (run,$(firstword $(MAKECMDGOALS)))
# use the rest as arguments for "run"
RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
# ...and turn them into do-nothing targets
$(eval $(RUN_ARGS):;@:)
prog: # ...
# ...
.PHONY: run
run: prog
@echo prog $(RUN_ARGS)
You could then do:
#+BEGIN_SRC bash
$ make run foo bar baz
prog foo bar baz
If you want to pass options to the target, you do need to separate them from
make with a ~--~ (like other commands), so something like:
#+BEGIN_SRC makefile
make run -- --from here --to there
Generally, if you do need to pass arbitrary arguments to a target all the time,
I would suggest writing a script and running it directly, maybe with make
targets for the common cases. But you do you.
* Auto-documented makefiles
[[][This post]] describes the approach in detail and is quite handy. In sort, annotate
your targets like: