git add -p

I have auto-formatting and linting configured in my text editor. It’s great for automatically fixing most code format issues and I highly recommend it. However, when working with legacy codebases, this can make for some messy diffs.

I’ll usually try to disable auto-formatting if I anticipate an issue, but sometimes I’m not expecting it or forget. Rather than manually reverting the changes, it’s usually easier to stage the changes I want, commit, and then reset the unstaged changes.

To interactively choose which parts I want to stage, I use git add -p.

From the manpage:

-p
--patch
Interactively choose hunks of patch between the index and the work tree and add them to the index. This gives the user a chance to review the difference before adding modified contents to the index.

This effectively runs add --interactive, but bypasses the initial command menu and directly jumps to the patch subcommand. See “Interactive mode” for details.

When you run the command, you’ll initially see the first part of the patch with a prompt for how you want to handle it.

(1/55) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,?]?

From the Interactive Mode docs:

y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help

After staging all the relevant hunks, you can commit and push as normal. Then git reset --hard resets the other pending changes.

The GitHub Desktop app as similar functionality to commit parts of a given change using a GUI interface.

Taylor Swift and Big Machine Records

This Twitter thread is an excellent summary of Taylor Swift’s history with Big Machine Records, the sale to Scooter Braun (against her wishes), and how much power she has to block sales or usage of the original songs.

If you haven’t been following along lately, she just released a remastered version of her 2008 album, Fearless. This time she owns the masters.

Grandfather, Father, Son Rotation Script

Here’s a simple script I put together to rotate backup files in a given directory with decreasing resolution the further away we get from the current time.

#!/bin/bash

set -euo pipefail

DRYRUN=${DRYRUN:-1}
DIR=${DIR:-'/backups/mysql'}
HOURS=${HOURS:-48}
DAYS=${DAYS:-35}
MONTHS=${MONTHS:-24}

usage() {
	echo "usage: DRYRUN=0 DIR=/backups HOURS=48 DAYS=30 MONTHS=24 $0"
	exit 1
}

if [ $DRYRUN -eq 1 ]; then
	echo "DRYRUN: No changes will be made"
fi

if [ "$HOURS" -lt 0 ] || [ "$DAYS" -lt 1 ] || [ "$MONTHS" -lt 1 ]; then
	echo "ERROR: Cannot have negative rotation"
	usage
fi

## Bail and cleanup if we exit early
cleanup() {
	find $DIR -type f -execdir rename 's/.process$//g' '{}' \;
}
trap cleanup EXIT
trap cleanup SIGINT

## Flag all the files for processing
find $DIR -type f | grep -v '.process$' | xargs -I{} mv {}{,.process}

while [ $MONTHS -gt 0 ]; do
	MONTHS=$(($MONTHS-1))
	days=$(( ( $(date '+%s') - $(date -d "$MONTHS months ago" '+%s') ) / 86400 ))
	file=$(find $DIR -type f -mtime "+$days" | sort -n | tail -n1)
	if [ "$DRYRUN" -eq 0 ]; then
		rename 's/.process$//g' "$file"
	else
		echo "MONTH:	$MONTHS	$file" | sed 's/.process$//'
	fi
done

while [ $DAYS -gt 0 ]; do
	DAYS=$(($DAYS-1))
	file=$(find $DIR -type f -mtime "+$DAYS" | sort -n | tail -n1)
	if [ "$DRYRUN" -eq 0 ]; then
		rename 's/.process$//g' "$file"
	else
		echo "DAY:	$DAYS	$file" | sed 's/.process$//'
	fi
done

while [ $HOURS -gt 0 ]; do
	HOURS=$(($HOURS-1))
	minutes=$(($HOURS*60))
	file=$(find $DIR -type f -mmin "+$minutes" | sort -n | tail -n1)
	if [ "$DRYRUN" -eq 0 ]; then
		rename 's/.process$//g' "$file"
	else
		echo "HOUR: 	$HOURS	$file" | sed 's/.process$//'
	fi
done

## Delete all remaining files
[ "$DRYRUN" -eq 0 ] && rm "$DIR"/*.process 2> /dev/null