Save the following script to ~/tree-filter.sh and chmod +x it:
#!/bin/bash
set -eo pipefail
fd '\.cs$' -H -E .git -E Migrations -tf -x perl -pi -e '
if ($. == 1) {
print <<'\''EOF'\'';
// Copyright (c) Your Name
// Licensed under the Apache License, Version 2.0
EOF
s/^\xEF\xBB\xBF// # Remove BOM
}
'
fd -H -E .git -tf -X chmod 644
Then run:
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f --tree-filter ~/tree-filter.sh HEAD
This example will prepend the header to all .cs files (removing the BOM if present), skipping EF migrations, and remove the executable bit from all files. Using fd as it excludes .gitignore'd files by default and runs -x
in parallel. Passing -H -E .git
will include any git-tracked hidden files ("include hidden except for .git").
filter-branch will leave the commits unsigned, however. To resign them:
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f --commit-filter 'git commit-tree -S "$@";' HEAD
Both commands will leave the committer date as-is (unlike rebasing).
This SO answer suggests using sed to prepend a license header:
https://stackoverflow.com/questions/8866416/add-a-licenses-to-file-headers-in-tree-retroactively
However, this won't work if the file is a single line. The r
command only queues the file to be written to the output once sed moves to the next line; it doesn't output the file immediately (moving the pattern buffer to the hold space and back with h
/g
is therefore unnecessary, although it's entirely possible the behavior is different on some versions of sed). With auto printing on, the current line will be printed first. The trick with N
is that, by manually reading the next line and appending it to the current line (still held in the pattern buffer), it causes sed to write the queued file before the first line (now the first two lines) is printed. Unfortunately, if there are no further lines to be read, N
causes sed to quit, resulting in the current line being auto-printed first, followed by the file contents.