Currently, pacdiff only allows users to diff between the current file and the new file. However, the merging of files could be automated by the use of some 3-way merge utility. Teach pacdiff the (M)erge mode which performs a 3-way merge using a given $MERGEPROG (`diff3 -m` by default). The base file is taken from from the second-newest package in the cache. Signed-off-by: Denton Liu <liu.denton@gmail.com> --- doc/pacdiff.8.txt | 9 +++-- src/pacdiff.sh.in | 83 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/doc/pacdiff.8.txt b/doc/pacdiff.8.txt index a89c0e7..592da72 100644 --- a/doc/pacdiff.8.txt +++ b/doc/pacdiff.8.txt @@ -18,8 +18,8 @@ Description ----------- pacdiff is a script which looks for pacorig, pacnew and pacsave files from the backup entries found in the local Pacman db. For every found file the option is -given to view, skip, diff, remove or overwrite the found pacorig, pacnew or -pacsave file. +given to view, merge, skip, diff, remove or overwrite the found pacorig, pacnew +or pacsave file. Environment ----------- @@ -29,6 +29,9 @@ Environment *DIFFSEARCHPATH*:: Override the default search path '/etc', only when using find. +*MERGEPROG*:: + Override the default 'diff3 -m' 3-way merge program. One possible + alternative is 'git merge-file -p'. Options ------- @@ -47,6 +50,8 @@ Options *\--nocolor*:: Remove colors from output. +*-c, \--cachedir <dir>*:: + Scan 'dir' instead as the pacman cache for 3-way merge base candidates. See Also -------- diff --git a/src/pacdiff.sh.in b/src/pacdiff.sh.in index a50cb93..fc18023 100644 --- a/src/pacdiff.sh.in +++ b/src/pacdiff.sh.in @@ -27,6 +27,8 @@ LIBRARY=${LIBRARY:-'@libmakepkgdir@'} diffprog=${DIFFPROG:-'vim -d'} diffsearchpath=${DIFFSEARCHPATH:-/etc} +mergeprog=${MERGEPROG:-'diff3 -m'} +cachedir= USE_COLOR='y' declare -a oldsaves declare -i USE_FIND=0 USE_LOCATE=0 USE_PACDB=0 OUTPUTONLY=0 @@ -53,15 +55,18 @@ Search Options: select one (default: --pacmandb) -p/--pacmandb scan active config files from pacman database General Options: - -o/--output print files instead of merging them - --nocolor remove colors from output + -o/--output print files instead of merging them + --nocolor remove colors from output + -c/--cachedir <dir> scan "dir" for 3-way merge base candidates. + (default: read from @sysconfdir@/pacman.conf) Environment Variables: DIFFPROG override the merge program: (default: 'vim -d') DIFFSEARCHPATH override the search path. (only when using find) (default: /etc) + MERGEPROG override the 3-way merge program: (default: 'diff3 -m') -Example: DIFFPROG=meld DIFFSEARCHPATH="/boot /etc /usr" $myname +Example: DIFFPROG=meld DIFFSEARCHPATH="/boot /etc /usr" MERGEPROG="git merge-file -p" $myname Example: $myname --output --locate EOF @@ -83,6 +88,62 @@ print_existing_pacsave(){ done } +base_cache_tar() { + package="$1" + + [[ -d $cachedir ]] || + die "cachedir '%s' does not exist or is not a directory" "$cachedir" + + # unlikely that this will fail, but better make sure + pushd "$cachedir" &>/dev/null || die "failed to chdir to '%s'" "$cachedir" + + find "$PWD" -name "$package-[0-9]*.pkg.tar*" | pacsort --files | sed -ne '2p' + + popd &>/dev/null +} + +merge_file() { + pacfile="$1" + file="$2" + + package="$(pacman -Qoq "$file")" || return 1 + base_tar="$(base_cache_tar "$package")" + + if [[ -z $base_tar ]]; then + msg2 "Unable to find a base package." + return 1 + fi + + basename="$(basename "$file")" + base="$(mktemp --tmpdir "$basename.base.XXX")" + merged="$(mktemp --tmpdir "$basename.merged.XXX")" + + tar -xOf "$base_tar" "${file#/}" >"$base" + if $mergeprog "$file" "$base" "$pacfile" >"$merged"; then + msg2 "Merged without conflicts." + fi + + $diffprog "$file" "$merged" + + while :; do + ask "Would you like to use the results of the merge? [y/n] " + + read c || return 1 + case $c in + y|Y) break ;; + n|N) return 1 ;; + *) msg2 "Invalid answer." ;; + esac + done + + if ! cp -v "$merged" "$file"; then + warning "Unable to write merged file to %s. Merged file is preserved at %s" "$file" "$merged" + return 1 + fi + rm -v "$pacfile" "$base" "$merged" + return 0 +} + cmd() { if (( USE_LOCATE )); then locate -0 -e -b \*.pacnew \*.pacorig \*.pacsave '*.pacsave.[0-9]*' @@ -114,6 +175,8 @@ while [[ -n "$1" ]]; do OUTPUTONLY=1;; --nocolor) USE_COLOR='n';; + -c|--cachedir) + cachedir="$2"; shift;; -V|--version) version; exit 0;; -h|--help) @@ -135,6 +198,10 @@ if ! type -p ${diffprog%% *} >/dev/null && (( ! OUTPUTONLY )); then die "Cannot find the $diffprog binary required for viewing differences." fi +if ! type -p ${mergeprog%% *} >/dev/null && (( ! OUTPUTONLY )); then + die "Cannot find the $mergeprog binary required for merging differences." +fi + case $(( USE_FIND + USE_LOCATE + USE_PACDB )) in 0) USE_PACDB=1;; # set the default search option [^1]) error "Only one search option may be used at a time" @@ -153,6 +220,10 @@ if (( USE_PACDB )); then fi fi +if [[ -z $cachedir ]]; then + cachedir="$(pacman-conf CacheDir)" +fi + # see http://mywiki.wooledge.org/BashFAQ/020 while IFS= read -u 3 -r -d '' pacfile; do file="${pacfile%.pac*}" @@ -181,7 +252,7 @@ while IFS= read -u 3 -r -d '' pacfile; do rm -v "$pacfile" else while :; do - ask "(V)iew, (S)kip, (R)emove %s, (O)verwrite with %s, (Q)uit: [v/s/r/o/q] " "$file_type" "$file_type" + ask "(V)iew, (M)erge, (S)kip, (R)emove %s, (O)verwrite with %s, (Q)uit: [v/m/s/r/o/q] " "$file_type" "$file_type" read c || break case $c in q|Q) exit 0;; @@ -194,6 +265,10 @@ while IFS= read -u 3 -r -d '' pacfile; do rm -v "$pacfile" break fi ;; + m|M) + if merge_file "$pacfile" "$file"; then + break + fi ;; s|S) break ;; *) msg2 "Invalid answer." ;; esac -- 2.31.0.rc1.228.gb75b4e4ce2