Skip to content

Commit 62f7315

Browse files
committed
Enable snapshot <> snapshot diffing
This change adds in the ability to diff a snapshot against another snapshot, instead of always against the current state of the filesystem. The tab key is used to select one or two snapshots that can be passed into the diff browser. If two snapshots are selected and a non-diff action is chosen, the second snapshot (as determined by fzf) is discarded from the return value. Since there can potentially be confusion as to which snapshot was selected, the clone/duplicate screens now show the snapshot to be cloned/duplicated at the top of the screen. The diff browser screen no longer shows the snapshot name as the input prompt - a generic '> ' is instead used. Lastly, zfsbootmenu-preview now accepts an optional third argument to be shown on a 3rd preview line. This can be used to provide additional / relevant help on the screen - such as noting the tab key can be used to select two snapshots for diffing.
1 parent 6466997 commit 62f7315

File tree

4 files changed

+54
-17
lines changed

4 files changed

+54
-17
lines changed

90zfsbootmenu/zfsbootmenu-lib.sh

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ draw_kernel() {
540540
expects="--expect=alt-d,alt-u"
541541

542542
if ! selected="$( HELP_SECTION=kernel-management ${FUZZYSEL} \
543-
--prompt "${benv} > " --tac --with-nth=2 --header="${header}" \
543+
--prompt "${benv} > " --tac --with-nth=2 --header="${header}" \
544544
${expects} ${expects//alt-/ctrl-} ${expects//alt-/ctrl-alt-} \
545545
--preview="/libexec/zfsbootmenu-preview ${benv} ${BOOTFS}" \
546546
--preview-window="up:${PREVIEW_HEIGHT}" < "${_kernels}" )"; then
@@ -556,7 +556,7 @@ draw_kernel() {
556556
}
557557

558558
# arg1: ZFS filesystem name
559-
# prints: selected snapshot
559+
# prints: selected snapshot name, optionally a second snapshot
560560
# returns: 130 on error, 0 otherwise
561561

562562
draw_snapshots() {
@@ -575,18 +575,19 @@ draw_snapshots() {
575575
"[CTRL+X] clone and promote" "[CTRL+C] clone only" "" \
576576
"[CTRL+J] jump into chroot" "[CTRL+D] show diff" "" \
577577
"[CTRL+L] view logs" "[CTRL+H] help" )"
578+
context="Note: for diff viewer, use tab to select/deselect up to two items"
578579

579580
expects="--expect=alt-x,alt-c,alt-j,alt-o"
580581

581582
if ! selected="$( zfs list -t snapshot -H -o name "${benv}" -S "${sort_key}" |
582583
HELP_SECTION=snapshot-management ${FUZZYSEL} \
583-
--prompt "Snapshot > " --header="${header}" --tac \
584+
--prompt "Snapshot > " --header="${header}" --tac --multi 2 \
584585
${expects} ${expects//alt-/ctrl-} ${expects//alt-/ctrl-alt-} \
585-
--bind='alt-d:execute[ /libexec/zfunc draw_diff {} ]' \
586-
--bind='ctrl-d:execute[ /libexec/zfunc draw_diff {} ]' \
587-
--bind='ctrl-alt-d:execute[ /libexec/zfunc draw_diff {} ]' \
588-
--preview="/libexec/zfsbootmenu-preview ${benv} ${BOOTFS}" \
589-
--preview-window="up:${PREVIEW_HEIGHT}" )"; then
586+
--bind='alt-d:execute[ /libexec/zfunc draw_diff {+} ]' \
587+
--bind='ctrl-d:execute[ /libexec/zfunc draw_diff {+} ]' \
588+
--bind='ctrl-alt-d:execute[ /libexec/zfunc draw_diff {+} ]' \
589+
--preview="/libexec/zfsbootmenu-preview ${benv} ${BOOTFS} '${context}'" \
590+
--preview-window="up:$(( PREVIEW_HEIGHT + 1 ))" )"; then
590591
return 1
591592
fi
592593

@@ -604,15 +605,34 @@ draw_snapshots() {
604605
# returns: nothing
605606

606607
draw_diff() {
607-
local snapshot pool diff_target mnt
608+
local snapshot diff_target pool base_fs mnt
608609
local zfs_diff zfs_diff_PID
610+
local line_one line_two left_pad
609611

610612
snapshot="${1}"
611613
if [ -z "${snapshot}" ]; then
612614
zerror "snapshot is undefined"
613615
return 130
614616
fi
617+
618+
# if a second parameter was passed in and it's a snapshot, compare
619+
# creation dates and make sure diff_target is newer than snapshot
620+
if [ -n "${2}" ] ; then
621+
local sd td
622+
sd="$( zfs get -H -p -o value creation "${snapshot}" )"
623+
td="$( zfs get -H -p -o value creation "${2}" )"
624+
if [ "${sd}" -lt "${td}" ] ; then
625+
diff_target="${2}"
626+
else
627+
diff_target="${snapshot}"
628+
snapshot="${2}"
629+
fi
630+
else
631+
diff_target="${snapshot%%@*}"
632+
fi
633+
615634
zdebug "snapshot: ${snapshot}"
635+
zdebug "diff target: ${diff_target}"
616636

617637
pool="${snapshot%%/*}"
618638
zdebug "pool: ${pool}"
@@ -622,24 +642,30 @@ draw_diff() {
622642
return
623643
fi
624644

625-
diff_target="${snapshot%%@*}"
626-
zdebug "base filesystem: ${diff_target}"
645+
base_fs="${snapshot%%@*}"
646+
zdebug "base filesystem: ${base_fs}"
627647

628-
CLEAR_SCREEN=1 load_key "${diff_target}"
648+
CLEAR_SCREEN=1 load_key "${base_fs}"
629649

630-
if ! mnt="$( mount_zfs "${diff_target}" )" ; then
631-
zerror "unable to mount ${diff_target}"
650+
if ! mnt="$( mount_zfs "${base_fs}" )" ; then
651+
zerror "unable to mount ${base_fs}"
632652
return
633653
fi
634654

655+
zdebug "executing: zfs diff -F -H ${snapshot} ${diff_target}"
635656
coproc zfs_diff ( zfs diff -F -H "${snapshot}" "${diff_target}" )
636657

637658
# Bash won't use an FD referenced in a variable on the left side of a pipe
638659
exec 3>&"${zfs_diff[0]}"
639660

640661
# shellcheck disable=SC2154
641-
sed "s,${mnt},," <&3 | HELP_SECTION=diff-viewer ${FUZZYSEL} --prompt "${snapshot} > " \
642-
--preview="/libexec/zfsbootmenu-preview ${diff_target} ${BOOTFS}" \
662+
line_one="$( center_string "---${snapshot}" )"
663+
left_pad="${line_one//---${snapshot}/}"
664+
line_one="$( colorize red "${line_one}" )"
665+
line_two="${left_pad}$( colorize green "+++${diff_target}" )"
666+
667+
sed "s,${mnt},," <&3 | HELP_SECTION=diff-viewer ${FUZZYSEL} --prompt "> " \
668+
--preview="echo -e '${line_one}\n${line_two}'" --no-sort \
643669
--preview-window="up:${PREVIEW_HEIGHT}"
644670

645671
[ -n "${zfs_diff_PID}" ] && kill "${zfs_diff_PID}"

90zfsbootmenu/zfsbootmenu-preview.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,8 @@ fi
4343

4444
colorize "${_COLOR}" "${selected_env_str}\n"
4545
echo "${selected_arguments}"
46+
47+
if [ -n "${3}" ]; then
48+
context="$( center_string "${3}" )"
49+
colorize "orange" "${context}"
50+
fi

90zfsbootmenu/zfsbootmenu.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ while true; do
220220

221221
# shellcheck disable=SC2162
222222
IFS=, read subkey selected_snap <<< "${selection}"
223+
224+
# two snapshots were potentially returned - discard the second
225+
selected_snap="${selected_snap%,*}"
223226
zdebug "selected snapshot: ${selected_snap}"
224227

225228
# Parent of the selected dataset, must be nonempty
@@ -261,6 +264,9 @@ while true; do
261264
# Strip snapshot name and append NEW
262265
pre_populated="${pre_populated%%@*}_NEW"
263266

267+
header="$( center_string "${selected_snap}" )"
268+
colorize green "${header}"
269+
264270
while true; do
265271
echo -e "\nNew boot environment name (leave blank to abort)"
266272
new_be="$( /libexec/zfsbootmenu-input "${pre_populated}" )"

pod/online/snapshot-management.pod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The operation will fail gracefully if the pool can not be set I<read/write>.
3939

4040
=item I<[MOD+D]> B<diff>
4141

42-
Compare the differences between the selected snapshot and the current state of the boot environment.
42+
Compare the differences between snapshots and filesystems. A single snapshot can be selected and a diff will be generated between that and the current state of the filesystem. Two snapshots can be selected (and deselected) with the tab key and a diff will be generated between them.
4343

4444
The operation will fail gracefully if the pool can not be set I<read/write>.
4545

0 commit comments

Comments
 (0)