try more advanced diff
This commit is contained in:
parent
e70046ede1
commit
5271c21c50
2 changed files with 165 additions and 55 deletions
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.impermanence.tools;
|
cfg = config.impermanence.tools;
|
||||||
|
|
||||||
|
|
@ -9,7 +14,7 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.impermanence.tools = {
|
options.impermanence.tools = {
|
||||||
enable = lib.mkEnableOption "bcachefs impermanence tools (GC + CLI)";
|
# enable = lib.mkEnableOption "bcachefs impermanence tools (GC + CLI)";
|
||||||
|
|
||||||
snapshotRoot = lib.mkOption {
|
snapshotRoot = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
|
|
@ -44,7 +49,8 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = {
|
||||||
|
# config = lib.mkIf cfg.enable {
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
bcacheImpermanenceBin
|
bcacheImpermanenceBin
|
||||||
pkgs.coreutils
|
pkgs.coreutils
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,9 @@ cmd_ls() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$count" -gt 0 ] 2>/dev/null; then
|
if [ "$count" -gt 0 ] 2>/dev/null; then
|
||||||
printf '%s
|
printf '%s\n' "$snaps" | head -n "$count"
|
||||||
' "$snaps" | head -n "$count"
|
|
||||||
else
|
else
|
||||||
printf '%s
|
printf '%s\n' "$snaps"
|
||||||
' "$snaps"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,8 +105,7 @@ build_keep_set() {
|
||||||
|
|
||||||
# Always keep newest KEEP_RECENT_COUNT snapshots.
|
# Always keep newest KEEP_RECENT_COUNT snapshots.
|
||||||
if [ "$KEEP_RECENT_COUNT" -gt 0 ]; then
|
if [ "$KEEP_RECENT_COUNT" -gt 0 ]; then
|
||||||
printf '%s
|
printf '%s\n' "$snaps" | head -n "$KEEP_RECENT_COUNT" >"$tmpdir/keep_recent"
|
||||||
' "$snaps" | head -n "$KEEP_RECENT_COUNT" >"$tmpdir/keep_recent"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Per-month: keep up to KEEP_PER_MONTH newest per month.
|
# Per-month: keep up to KEEP_PER_MONTH newest per month.
|
||||||
|
|
@ -254,6 +251,150 @@ EOF_SNAPS
|
||||||
echo "GC complete; deleted $deleted snapshots"
|
echo "GC complete; deleted $deleted snapshots"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
browse_diff_tree() {
|
||||||
|
local snapshot_name snapshot_dir diff_list
|
||||||
|
snapshot_name="$1"
|
||||||
|
snapshot_dir="$2"
|
||||||
|
diff_list="$3"
|
||||||
|
|
||||||
|
local current_prefix=""
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
local children
|
||||||
|
children=$(mktemp)
|
||||||
|
local seen
|
||||||
|
seen=$(mktemp)
|
||||||
|
|
||||||
|
# Optional parent entry
|
||||||
|
if [ -n "$current_prefix" ]; then
|
||||||
|
echo "dir .." >"$children"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build immediate children under current_prefix.
|
||||||
|
while read -r st rel; do
|
||||||
|
[ -n "$rel" ] || continue
|
||||||
|
local sub
|
||||||
|
if [ -n "$current_prefix" ]; then
|
||||||
|
case "$rel" in
|
||||||
|
"$current_prefix")
|
||||||
|
# Exact match at this level; treat as leaf, not a separate child.
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
"$current_prefix"/*)
|
||||||
|
sub="${rel#"$current_prefix"/}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
sub="$rel"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "$sub" ] || continue
|
||||||
|
|
||||||
|
local child
|
||||||
|
child="${sub%%/*}"
|
||||||
|
local child_rel
|
||||||
|
if [ -n "$current_prefix" ]; then
|
||||||
|
child_rel="$current_prefix/$child"
|
||||||
|
else
|
||||||
|
child_rel="$child"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -qx "$child_rel" "$seen" 2>/dev/null; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$st $child_rel" >>"$children"
|
||||||
|
echo "$child_rel" >>"$seen"
|
||||||
|
done <"$diff_list"
|
||||||
|
|
||||||
|
rm -f "$seen"
|
||||||
|
|
||||||
|
if [ ! -s "$children" ]; then
|
||||||
|
echo "No further differences under ${current_prefix:-/}" >&2
|
||||||
|
rm -f "$children"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v fzf >/dev/null 2>&1; then
|
||||||
|
echo "fzf is required for diff browsing" >&2
|
||||||
|
rm -f "$children"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
local preview_cmd
|
||||||
|
preview_cmd='sel_rel=$(printf "%s\n" {} | cut -d" " -f2-)
|
||||||
|
if [ "$sel_rel" = ".." ]; then
|
||||||
|
echo "[UP] .."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
snap_dir="'"$snapshot_dir"'"
|
||||||
|
a_path="$snap_dir/$sel_rel"
|
||||||
|
b_path="/$sel_rel"
|
||||||
|
|
||||||
|
if [ ! -e "$a_path" ] && [ -e "$b_path" ]; then
|
||||||
|
echo "[ADDED] /$sel_rel"; echo
|
||||||
|
if [ -d "$b_path" ]; then
|
||||||
|
(cd / && find "$sel_rel" -maxdepth 3 -print 2>/dev/null || true)
|
||||||
|
else
|
||||||
|
diff -u /dev/null "$b_path" 2>/dev/null || cat "$b_path" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
elif [ -e "$a_path" ] && [ ! -e "$b_path" ]; then
|
||||||
|
echo "[REMOVED] /$sel_rel"; echo
|
||||||
|
if [ -d "$a_path" ]; then
|
||||||
|
(cd "$snap_dir" && find "$sel_rel" -maxdepth 3 -print 2>/dev/null || true)
|
||||||
|
else
|
||||||
|
diff -u "$a_path" /dev/null 2>/dev/null || cat "$a_path" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -d "$a_path" ] && [ -d "$b_path" ]; then
|
||||||
|
echo "[CHANGED DIR] /$sel_rel"; echo
|
||||||
|
diff -ru "$a_path" "$b_path" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo "[CHANGED] /$sel_rel"; echo
|
||||||
|
diff -u "$a_path" "$b_path" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
local selection
|
||||||
|
selection=$(FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS:-} --ansi --preview-window=right:70%:wrap" \
|
||||||
|
fzf --prompt="[bcache-impermanence diff ${current_prefix:-/}] " \
|
||||||
|
--preview "$preview_cmd" <"$children") || {
|
||||||
|
rm -f "$children"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -f "$children"
|
||||||
|
|
||||||
|
local sel_rel
|
||||||
|
sel_rel=$(printf "%s\n" "$selection" | cut -d" " -f2-)
|
||||||
|
|
||||||
|
if [ "$sel_rel" = ".." ]; then
|
||||||
|
if [ -z "$current_prefix" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if printf "%s" "$current_prefix" | grep -q '/'; then
|
||||||
|
current_prefix="${current_prefix%/*}"
|
||||||
|
else
|
||||||
|
current_prefix=""
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If this selection has descendants, drill down; otherwise treat as leaf and exit.
|
||||||
|
if grep -q " $sel_rel/" "$diff_list"; then
|
||||||
|
current_prefix="$sel_rel"
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
# Leaf: user already saw diff in preview; exit.
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
cmd_diff() {
|
cmd_diff() {
|
||||||
local snapshot_name=""
|
local snapshot_name=""
|
||||||
|
|
||||||
|
|
@ -305,7 +446,14 @@ cmd_diff() {
|
||||||
set -- /
|
set -- /
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local prefixes=("$@")
|
if ! command -v fzf >/dev/null 2>&1; then
|
||||||
|
echo "fzf is required for diff browsing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local prefixes
|
||||||
|
prefixes=("$@")
|
||||||
|
|
||||||
local tmp
|
local tmp
|
||||||
tmp=$(mktemp)
|
tmp=$(mktemp)
|
||||||
|
|
||||||
|
|
@ -381,51 +529,7 @@ cmd_diff() {
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v fzf >/dev/null 2>&1; then
|
browse_diff_tree "$snapshot_name" "$snapshot_dir" "$diff_list"
|
||||||
echo "fzf is required for diff browsing" >&2
|
|
||||||
rm -f "$diff_list"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS:-} --ansi --preview-window=right:70%:wrap" \
|
|
||||||
fzf --prompt="[bcache-impermanence diff] " --preview '
|
|
||||||
status="$(echo {} | cut -d" " -f1)"
|
|
||||||
rel="$(echo {} | cut -d" " -f2-)"
|
|
||||||
snap_dir="'$snapshot_dir'"
|
|
||||||
a_path="$snap_dir/$rel"
|
|
||||||
b_path="/$rel"
|
|
||||||
|
|
||||||
case "$status" in
|
|
||||||
added)
|
|
||||||
echo "[ADDED] $rel"; echo
|
|
||||||
if [ -d "$b_path" ]; then
|
|
||||||
(cd / && find "${rel}" -maxdepth 3 -print 2>/dev/null || true)
|
|
||||||
else
|
|
||||||
diff -u /dev/null "$b_path" 2>/dev/null || cat "$b_path" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
removed)
|
|
||||||
echo "[REMOVED] $rel"; echo
|
|
||||||
if [ -d "$a_path" ]; then
|
|
||||||
(cd "$snap_dir" && find "${rel}" -maxdepth 3 -print 2>/dev/null || true)
|
|
||||||
else
|
|
||||||
diff -u "$a_path" /dev/null 2>/dev/null || cat "$a_path" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
changed-dir)
|
|
||||||
echo "[CHANGED DIR] $rel"; echo
|
|
||||||
diff -ru "$a_path" "$b_path" 2>/dev/null || true
|
|
||||||
;;
|
|
||||||
changed)
|
|
||||||
echo "[CHANGED] $rel"; echo
|
|
||||||
diff -u "$a_path" "$b_path" 2>/dev/null || true
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown status: $status";
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
' <"$diff_list"
|
|
||||||
|
|
||||||
rm -f "$diff_list"
|
rm -f "$diff_list"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue