diff --git a/dotdrop/comparator.py b/dotdrop/comparator.py index 121de2a..3f2893b 100644 --- a/dotdrop/comparator.py +++ b/dotdrop/comparator.py @@ -40,9 +40,27 @@ class Comparator: ignore = [] local_path = os.path.expanduser(local_path) deployed_path = os.path.expanduser(deployed_path) + self.log.dbg(f'comparing \"{local_path}\" and \"{deployed_path}\"') self.log.dbg(f'ignore pattern(s): {ignore}') + return self._compare(local_path, deployed_path, + ignore=ignore, mode=mode, + recurse=True) + + def _compare(self, local_path, deployed_path, + ignore=None, mode=None, + recurse=False): + if not ignore: + ignore = [] + + # test existence + if not os.path.exists(local_path): + return f'=> \"{local_path}\" does not exist on destination\n' + if not self.ignore_missing_in_dotdrop: + if not os.path.exists(deployed_path): + return f'=> \"{deployed_path}\" does not exist in dotdrop\n' + # test type of file if os.path.isdir(local_path) and not os.path.isdir(deployed_path): ret = f'\"{local_path}\" is a dir' @@ -53,7 +71,7 @@ class Comparator: ret += f' while \"{deployed_path}\" is a dir\n' return ret - # test content + # is a file if not os.path.isdir(local_path): self.log.dbg(f'{local_path} is a file') ret = self._comp_file(local_path, deployed_path, ignore) @@ -61,9 +79,11 @@ class Comparator: ret = self._comp_mode(local_path, deployed_path, mode=mode) return ret + # is a directory self.log.dbg(f'\"{local_path}\" is a directory') - - ret = self._comp_dir(local_path, deployed_path, ignore) + ret = '' + if recurse: + ret = self._comp_dir(local_path, deployed_path, ignore) if not ret: ret = self._comp_mode(local_path, deployed_path, mode=mode) return ret @@ -95,7 +115,7 @@ class Comparator: debug=self.debug): self.log.dbg(f'ignoring diff {local_path} and {deployed_path}') return '' - return self._diff(local_path, deployed_path) + return self._diff(local_path, deployed_path, header=True) def _comp_dir(self, local_path, deployed_path, ignore): """compare a directory""" @@ -114,7 +134,7 @@ class Comparator: if not os.path.isdir(deployed_path): return f'\"{deployed_path}\" is a file\n' - # return self._compare_dirs(local_path, deployed_path, ignore) + #return self._compare_dirs(local_path, deployed_path, ignore) return self._compare_dirs2(local_path, deployed_path, ignore) def _compare_dirs2(self, local_path, deployed_path, ignore): @@ -126,11 +146,13 @@ class Comparator: deploy_tree = FTreeDir(deployed_path, ignores=ignore, debug=self.debug) lonly, ronly, common = local_tree.compare(deploy_tree) + for i in lonly: + path = os.path.join(local_path, i) + ret.append(f'=> \"{path}\" does not exist on destination\n') if not self.ignore_missing_in_dotdrop: - for i in lonly: - ret.append(f'=> \"{i}\" does not exist on destination\n') - for i in ronly: - ret.append(f'=> \"{i}\" does not exist in dotdrop\n') + for i in ronly: + path = os.path.join(deployed_path, i) + ret.append(f'=> \"{path}\" does not exist in dotdrop\n') # test for content difference # and mode difference @@ -138,14 +160,10 @@ class Comparator: for i in common: source_file = os.path.join(local_path, i) deployed_file = os.path.join(deployed_path, i) - diff = self._diff(source_file, deployed_file, header=True) - if diff: - ret.append(diff) - continue - ret = self._comp_mode(local_path, deployed_path) - if ret: - short = os.path.basename(source_file) - ret.append(f'=> different type: \"{short}\"\n') + subret = self._compare(source_file, deployed_file, + ignore=None, mode=None, + recurse=False) + ret.extend(subret) return ''.join(ret) @@ -230,7 +248,7 @@ class Comparator: """diff two files""" out = diff(modified=local_path, original=deployed_path, diff_cmd=self.diff_cmd, debug=self.debug) - if header: + if header and out: lshort = os.path.basename(local_path) out = f'=> diff \"{lshort}\":\n{out}' return out diff --git a/dotdrop/ftree.py b/dotdrop/ftree.py index 7bd5222..cd1b220 100644 --- a/dotdrop/ftree.py +++ b/dotdrop/ftree.py @@ -13,7 +13,9 @@ from dotdrop.utils import must_ignore class FTreeDir: - """directory tree for comparison""" + """ + directory tree for comparison + """ def __init__(self, path, ignores=None, debug=False): self.path = path @@ -24,14 +26,27 @@ class FTreeDir: self._walk() def _walk(self): - for root, _, files in os.walk(self.path): + """ + index directory + ignore empty directory + test for ignore pattern + """ + for root, dirs, files in os.walk(self.path): for file in files: fpath = os.path.join(root, file) if must_ignore([fpath], ignores=self.ignores, debug=self.debug): continue self.entries.append(fpath) - self.entries.sort() + for dname in dirs: + dpath = os.path.join(root, dname) + if len(os.listdir(dpath)) < 1: + # ignore empty directory + continue + if must_ignore([dpath], ignores=self.ignores, + debug=self.debug): + continue + self.entries.append(dpath) def compare(self, other): """ @@ -39,8 +54,11 @@ class FTreeDir: - left_only (only in self) - right_only (only in other) - in_both (in both) + the relative path are returned """ - left_only = set(self.entries) - set(other.entries) - right_only = set(other.entries) - set(self.entries) - in_both = set(self.entries) & set(other.entries) + left = [os.path.relpath(entry, self.path) for entry in self.entries] + right = [os.path.relpath(entry, other.path) for entry in other.entries] + left_only = set(left) - set(right) + right_only = set(right) - set(left) + in_both = set(left) & set(right) return list(left_only), list(right_only), list(in_both) diff --git a/tests-ng/chmod-compare.sh b/tests-ng/chmod-compare.sh index 3a0d2db..9f1d446 100755 --- a/tests-ng/chmod-compare.sh +++ b/tests-ng/chmod-compare.sh @@ -92,6 +92,7 @@ _EOF cd "${ddpath}" | ${bin} install -f -c "${cfg}" -p p1 # compare +echo "compare after install..." cd "${ddpath}" | ${bin} compare -c "${cfg}" -p p1 # import @@ -109,6 +110,7 @@ chmod 700 "${fnormal}" chmod 700 "${flink}" set +e +cd "${ddpath}" | ${bin} compare -c "${cfg}" -p p1 -V out=$(cd "${ddpath}" | ${bin} compare -c "${cfg}" -p p1 2>&1) cnt=$(echo "${out}" | grep 'modes differ' | wc -l) set -e diff --git a/tests-ng/compare-negative-ignore-relative.sh b/tests-ng/compare-negative-ignore-relative.sh index 211da70..d2daf19 100755 --- a/tests-ng/compare-negative-ignore-relative.sh +++ b/tests-ng/compare-negative-ignore-relative.sh @@ -66,8 +66,7 @@ cd "${ddpath}" | ${bin} compare -c "${cfg}" --verbose --ignore="${patt0}" --igno [ "$?" = "0" ] && exit 1 cnt=$(cd "${ddpath}" | ${bin} compare -c "${cfg}" --ignore="${patt0}" --ignore=${patt1} | grep '^=> diff' | wc -l) set -e - -[ "${cnt}" != "2" ] && echo "bad number of diffs: ${cnt}/2" && exit 1 +[ "${cnt}" != "2" ] && echo "bad number of diffs (1): ${cnt}/2" && exit 1 ######################################## # Test ignores specified in config.yaml @@ -101,7 +100,7 @@ cd "${ddpath}" | ${bin} compare -c "${cfg}" --verbose -C "${tmpd}"/.zsh --ignore cnt=$(cd "${ddpath}" | ${bin} compare -c "${cfg}" -C "${tmpd}"/.zsh --ignore="${patt0}" --ignore=${patt1} | grep 'does not exist in dotdrop$' | wc -l) set -e -[ "${cnt}" != "1" ] && echo "bad number of diffs: ${cnt}/1" && exit 1 +[ "${cnt}" != "1" ] && echo "bad number of diffs (2): ${cnt}/1" && exit 1 # expects one diff echo "[+] comparing .zsh with ignore in dotfile - expect 1 diff" @@ -111,6 +110,6 @@ cd "${ddpath}" | ${bin} compare -c "${cfg2}" --verbose -C "${tmpd}"/.zsh cnt=$(cd "${ddpath}" | ${bin} compare -c "${cfg2}" -C "${tmpd}"/.zsh | grep 'does not exist in dotdrop$' | wc -l) set -e -[ "${cnt}" != "1" ] && echo "bad number of diffs: ${cnt}/1" && exit 1 +[ "${cnt}" != "1" ] && echo "bad number of diffs (3): ${cnt}/1" && exit 1 echo "OK"