diff --git a/README.md b/README.md index fa69bd2..5019719 100644 --- a/README.md +++ b/README.md @@ -610,6 +610,7 @@ the following entries: * `keepdot`: preserve leading dot when importing hidden file in the `dotpath` (default *false*) * `link_by_default`: when importing a dotfile set `link` to that value per default (default *false*) * `workdir`: directory where templates are installed before being symlink when using `link` (default *~/.config/dotdrop*) + * `showdiff`: on install show a diff before asking to overwrite (see `--showdiff`) (default *false*) * **dotfiles** entry: a list of dotfiles * When `link` is true, dotdrop will create a symlink instead of copying (default *false*). diff --git a/dotdrop/config.py b/dotdrop/config.py index b1e66e7..6a6d689 100644 --- a/dotdrop/config.py +++ b/dotdrop/config.py @@ -29,6 +29,7 @@ class Cfg: key_banner = 'banner' key_long = 'longkey' key_keepdot = 'keepdot' + key_showdiff = 'showdiff' key_deflink = 'link_by_default' key_workdir = 'workdir' @@ -64,6 +65,7 @@ class Cfg: default_link = False default_longkey = False default_keepdot = False + default_showdiff = False default_link_by_default = False default_workdir = '~/.config/dotdrop' @@ -315,6 +317,8 @@ class Cfg: self.lnk_settings[self.key_deflink] = self.default_link_by_default if self.key_workdir not in self.lnk_settings: self.lnk_settings[self.key_workdir] = self.default_workdir + if self.key_showdiff not in self.lnk_settings: + self.lnk_settings[self.key_showdiff] = self.default_showdiff def abs_dotpath(self, path): """transform path to an absolute path based on config path""" diff --git a/dotdrop/dotdrop.py b/dotdrop/dotdrop.py index da79064..129fe16 100644 --- a/dotdrop/dotdrop.py +++ b/dotdrop/dotdrop.py @@ -42,26 +42,26 @@ USAGE = """ {} Usage: - dotdrop install [-tfndVb] [-c ] [-p ] [...] - dotdrop import [-ldVb] [-c ] [-p ] ... - dotdrop compare [-Vb] [-c ] [-p ] - [-o ] [-i ...] - [--files=] - dotdrop update [-fdVb] [-c ] ... - dotdrop listfiles [-VTb] [-c ] [-p ] - dotdrop list [-Vb] [-c ] + dotdrop install [-tfndVbD] [-c ] [-p ] [...] + dotdrop import [-ldVb] [-c ] [-p ] ... + dotdrop compare [-Vb] [-c ] [-p ] + [-o ] [-C ] [-i ...] + dotdrop update [-fdVb] [-c ] ... + dotdrop listfiles [-VTb] [-c ] [-p ] + dotdrop list [-Vb] [-c ] dotdrop --help dotdrop --version Options: -p --profile= Specify the profile to use [default: {}]. -c --cfg= Path to the config [default: config.yaml]. - --files= Comma separated list of files to compare. + -C --files= Comma separated list of files to compare. -i --ignore= File name to ignore when diffing. -o --dopts= Diff options [default: ]. -n --nodiff Do not diff when installing. -t --temp Install to a temporary directory for review. -T --template Only template dotfiles. + -D --showdiff Show a diff before overwriting. -l --link Import and link. -f --force Do not warn if exists. -V --verbose Be verbose. @@ -94,9 +94,10 @@ def install(opts, conf, temporary=False, keys=[]): if temporary: tmpdir = get_tmpdir() inst = Installer(create=opts['create'], backup=opts['backup'], - dry=opts['dry'], safe=opts['safe'], base=opts['dotpath'], - workdir=opts['workdir'], diff=opts['installdiff'], - debug=opts['debug'], totemp=tmpdir) + dry=opts['dry'], safe=opts['safe'], + base=opts['dotpath'], workdir=opts['workdir'], + diff=opts['installdiff'], debug=opts['debug'], + totemp=tmpdir, showdiff=opts['showdiff']) installed = [] for dotfile in dotfiles: preactions = [] @@ -361,6 +362,7 @@ def main(): opts['link'] = args['--link'] opts['debug'] = args['--verbose'] opts['variables'] = conf.get_variables() + opts['showdiff'] = opts['showdiff'] or args['--showdiff'] if opts['debug']: LOG.dbg('config file: {}'.format(args['--cfg'])) diff --git a/dotdrop/installer.py b/dotdrop/installer.py index 3573f93..374bb3d 100644 --- a/dotdrop/installer.py +++ b/dotdrop/installer.py @@ -20,7 +20,7 @@ class Installer: def __init__(self, base='.', create=True, backup=True, dry=False, safe=False, workdir='~/.config/dotdrop', - debug=False, diff=True, totemp=None): + debug=False, diff=True, totemp=None, showdiff=False): self.create = create self.backup = backup self.dry = dry @@ -30,6 +30,7 @@ class Installer: self.debug = debug self.diff = diff self.totemp = totemp + self.showdiff = showdiff self.comparing = False self.action_executed = False self.log = Logger() @@ -125,7 +126,7 @@ class Installer: self.log.err('source dotfile does not exist: {}'.format(src)) return [] st = os.stat(src) - ret = self._write(dst, content, st.st_mode, actions=actions) + ret = self._write(src, dst, content, st.st_mode, actions=actions) if ret < 0: self.log.err('installing {} to {}'.format(src, dst)) return [] @@ -164,7 +165,7 @@ class Installer: cur = f.read() return cur == content - def _write(self, dst, content, rights, actions=[]): + def _write(self, src, dst, content, rights, actions=[]): """write content to file return 0 for success, 1 when already exists @@ -178,9 +179,17 @@ class Installer: if self.debug: self.log.dbg('{} is the same'.format(dst)) return 1 - if self.safe and not self.log.ask('Overwrite \"{}\"'.format(dst)): - self.log.warn('ignoring {}, already present'.format(dst)) - return 1 + if self.safe: + if self.debug: + self.log.dbg('change detected for {}'.format(dst)) + if self.showdiff: + comp = Comparator(debug=self.debug) + diff = comp.compare(src, dst) + self.log.log('diff \"{}\" VS \"{}\"'.format(src, dst)) + self.log.emph(diff) + if not self.log.ask('Overwrite \"{}\"'.format(dst)): + self.log.warn('ignoring {}'.format(dst)) + return 1 if self.backup and os.path.lexists(dst): self._backup(dst) base = os.path.dirname(dst) diff --git a/tests/helpers.py b/tests/helpers.py index 4ebec85..5019e88 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -80,6 +80,7 @@ def load_config(confpath, profile): opts['safe'] = True opts['installdiff'] = True opts['link'] = False + opts['showdiff'] = True opts['debug'] = True opts['dopts'] = '' opts['variables'] = {} diff --git a/tests/test_install.py b/tests/test_install.py index 3c7145a..237d473 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -174,6 +174,7 @@ exec bspwm conf, opts = load_config(confpath, profile) opts['safe'] = False opts['debug'] = True + opts['showdiff'] = True opts['variables'] = {} install(opts, conf)