1
0
Fork 0
mirror of https://github.com/SeriousBug/dotfiles synced 2026-06-16 20:35:08 -05:00

Add archive-coding-project script

Move a coding project into ~/.local/archives/, strip it with
'git clean -dxf', drop git LFS objects, and compress to .7z to
minimize disk use. Reports space reclaimed, and supports
--restore <file.7z> to extract an archive back into the cwd.

Deploys to ~/.local/bin via Dotter.
This commit is contained in:
Kaan Barmore-Genc 2026-06-15 20:54:09 -05:00
parent 0b6abed832
commit 3487eddb18
2 changed files with 207 additions and 0 deletions

View file

@ -74,6 +74,7 @@ depends = []
[bin.files] [bin.files]
"bin/discord-send" = "~/.local/bin/discord-send" "bin/discord-send" = "~/.local/bin/discord-send"
"bin/claude-token-count" = "~/.local/bin/claude-token-count" "bin/claude-token-count" = "~/.local/bin/claude-token-count"
"bin/archive-coding-project" = "~/.local/bin/archive-coding-project"
[bin.variables] [bin.variables]

206
bin/archive-coding-project Executable file
View file

@ -0,0 +1,206 @@
#!/usr/bin/env fish
#
# archive-coding-project — move a coding project into ~/.local/archives/ and
# strip it down to minimize disk use.
#
# After moving the folder, this runs `git clean -dxf` to remove all
# ignored/untracked files (build artifacts, node_modules, etc.). If the repo
# uses git LFS, it also drops the local LFS object cache so those files must be
# re-fetched on the next checkout. Finally it compresses the folder into a
# single .7z archive with 7z and removes the uncompressed copy, reporting how
# much disk space was reclaimed.
#
# Usage:
# archive-coding-project <folder> Archive a project
# archive-coding-project --restore <file.7z> Restore an archive into the cwd
function usage
echo "Usage:" >&2
echo " archive-coding-project <folder> Archive a project" >&2
echo " archive-coding-project --restore <file.7z> Restore into the cwd" >&2
echo "" >&2
echo "Archiving moves <folder> into ~/.local/archives/, runs 'git clean -dxf'," >&2
echo "drops git LFS objects, and compresses it into a .7z to minimize disk use." >&2
end
# Format a size given in KB as a human-readable string.
function human_kb --argument-names kb
echo $kb | awk '{
k=$1
if (k < 1024) printf "%d KB", k
else if (k < 1048576) printf "%.1f MB", k/1024
else printf "%.2f GB", k/1048576
}'
end
# Size of a path in KB (apparent disk usage). Works on files and directories.
function size_kb --argument-names path
du -sk -- $path | awk '{print $1}'
end
if not type -q 7z
echo "Error: '7z' not found on PATH (install p7zip)" >&2
exit 1
end
# ---------------------------------------------------------------------------
# Restore mode
# ---------------------------------------------------------------------------
if test (count $argv) -ge 1; and test "$argv[1]" = --restore
if test (count $argv) -ne 2
usage
exit 1
end
set -l archive $argv[2]
if not test -f $archive
echo "Error: archive '$archive' not found" >&2
exit 1
end
set -l abs_archive (realpath -- $archive)
# Figure out the top-level entries inside the archive so we can refuse to
# clobber existing paths in the cwd. In `7z l -slt` output the first
# `Path =` line is the archive's own filename, so we drop it (tail +2), then
# reduce each entry to its first path component and dedupe.
set -l tops (7z l -slt -- $abs_archive \
| string match -r '^Path = .+' \
| string replace 'Path = ' '' \
| tail -n +2 \
| string replace -r '/.*' '' \
| sort -u)
if test (count $tops) -eq 0
echo "Error: could not read archive contents" >&2
exit 1
end
for t in $tops
if test -e ./$t
echo "Error: '$t' already exists in the current directory" >&2
exit 1
end
end
echo "Restoring "(string join ', ' $tops)" from $abs_archive into "(pwd)"..."
if 7z x -- $abs_archive
echo ""
for t in $tops
echo "Restored "(pwd)"/$t"
end
echo "(The archive is left in place; delete it with: rm -- $abs_archive)"
else
echo "Error: extraction failed" >&2
exit 1
end
exit 0
end
# ---------------------------------------------------------------------------
# Archive mode
# ---------------------------------------------------------------------------
if test (count $argv) -ne 1
usage
exit 1
end
set -l src $argv[1]
# Strip a trailing slash so basename/dirname behave predictably.
set src (string trim --right --chars=/ -- $src)
if not test -d $src
echo "Error: '$src' is not a directory" >&2
exit 1
end
# Resolve to an absolute path.
set -l abs_src (realpath -- $src)
set -l name (basename -- $abs_src)
set -l archive_dir "$HOME/.local/archives"
set -l dest "$archive_dir/$name"
set -l archive_file "$dest.7z"
if test -e $dest
echo "Error: destination '$dest' already exists" >&2
exit 1
end
if test -e $archive_file
echo "Error: archive '$archive_file' already exists" >&2
exit 1
end
# Measure the original footprint before we touch anything, so we can report the
# real space reclaimed (original folder vs. final compressed archive).
set -l orig_kb (size_kb $abs_src)
echo "About to archive:"
echo " from: $abs_src"
echo " to: $archive_file"
echo ""
echo "This will:"
echo " - move the folder into $archive_dir"
echo " - run 'git clean -dxf' (deletes all untracked & ignored files)"
echo " - drop local git LFS objects if the repo uses LFS"
echo " - compress it into a .7z and remove the uncompressed folder"
echo ""
read -l -P "Proceed? [y/N] " confirm
if not string match -qi y -- $confirm
echo "Aborted."
exit 1
end
mkdir -p $archive_dir
echo "Moving folder..."
mv -- $abs_src $dest
# Operate on the archived copy via absolute paths. We deliberately avoid
# pushd/popd: this script may have been launched from inside the folder we just
# moved, in which case returning to the original cwd would fail.
if test -d "$dest/.git"
echo "Running 'git clean -dxf'..."
git -C $dest clean -dxf
# If the repo uses git LFS, drop the local object cache so the files have to
# be re-fetched on the next checkout. We use `git lfs prune --recent`, which
# is aggressive (it ignores the "recent commits" retention window) but still
# safe: `--verify-remote` only deletes objects confirmed to exist on the
# remote, so nothing unpushed is lost. Re-checkout pulls them back.
if type -q git-lfs; and test -d "$dest/.git/lfs"
echo "Dropping git LFS objects..."
git -C $dest lfs prune --recent --verify-remote
end
else
echo "Note: '$name' is not a git repository; skipping git clean / LFS steps."
end
# Compress into a single .7z, then drop the uncompressed folder. We cd into
# archive_dir so the archive stores a clean relative path ($name/...) rather
# than the absolute path. `-mx=9` is max compression. cd in this subprocess
# does not affect the caller's shell.
echo "Compressing into $archive_file..."
if cd $archive_dir; and 7z a -mx=9 -- "$name.7z" "$name"
rm -rf $dest
set -l archive_kb (size_kb $archive_file)
set -l saved_kb (math "$orig_kb - $archive_kb")
set -l pct 0
if test $orig_kb -gt 0
set pct (math -s0 "100 * $saved_kb / $orig_kb")
end
echo ""
echo "Archived to $archive_file"
echo " original: "(human_kb $orig_kb)
echo " archive: "(human_kb $archive_kb)
echo " saved: "(human_kb $saved_kb)" ($pct%)"
else
echo "Error: 7z compression failed; leaving uncompressed folder at $dest" >&2
exit 1
end