diff --git a/dvc/command/base.py b/dvc/command/base.py index c2c96695d..94874854b 100644 --- a/dvc/command/base.py +++ b/dvc/command/base.py @@ -50,7 +50,7 @@ class CmdBase(object): def set_skip_git_actions(self, parser): parser.add_argument('--skip-git-actions', '-s', action='store_true', - help='skip all git actions including reproducibility check and commits') + help='Skip all git actions including reproducibility check and commits') @property def skip_git_actions(self): diff --git a/dvc/command/repro.py b/dvc/command/repro.py index a0e9a1122..bb4278732 100644 --- a/dvc/command/repro.py +++ b/dvc/command/repro.py @@ -29,6 +29,8 @@ class CmdRepro(CmdRun): self.set_skip_git_actions(parser) parser.add_argument('target', metavar='', help='Data item to reproduce', nargs='*') + parser.add_argument('-f', '--force', action='store_true', default=False, + help='Force reproduction') pass @property @@ -51,13 +53,13 @@ class CmdRepro(CmdRun): return 1 try: - return self.repro_target(self.parsed_args.target) + return self.repro_target(self.parsed_args.target, self.parsed_args.force) finally: lock.release() pass - def repro_target(self, target): + def repro_target(self, target, force): if not self.skip_git_actions and not self.git.is_ready_to_go(): return 1 @@ -67,11 +69,11 @@ class CmdRepro(CmdRun): format(self.config.data_dir, ' '.join(external_files_names))) return 1 - if self.repro_data_items(data_item_list): + if self.repro_data_items(data_item_list, force): return self.commit_if_needed('DVC repro: {}'.format(' '.join(target))) pass - def repro_data_items(self, data_item_list): + def repro_data_items(self, data_item_list, force): error = False changed = False @@ -79,7 +81,7 @@ class CmdRepro(CmdRun): try: target_commit = self.git.get_target_commit(data_item.data.relative) repro_change = ReproChange(data_item, self, target_commit) - if repro_change.reproduce(): + if repro_change.reproduce(force): changed = True Logger.info(u'Data item "{}" was reproduced.'.format( data_item.data.relative @@ -148,11 +150,12 @@ class ReproChange(object): self.state.stderr) def reproduce(self, force=False): - Logger.debug('Reproduce data item {} with dependencies: {}'.format( + Logger.debug('Reproduce data item {} with dependencies, force={}: {}'.format( self._data_item.data.dvc, + force, ', '.join([x.data.dvc for x in self.dependencies]))) - if not self.state.is_reproducible: + if not force and not self.state.is_reproducible: Logger.debug('Data item "{}" is not reproducible'.format(self._data_item.data.relative)) return False diff --git a/dvc/state_file.py b/dvc/state_file.py index d6ab87233..de906b58d 100644 --- a/dvc/state_file.py +++ b/dvc/state_file.py @@ -4,6 +4,7 @@ import json import time from dvc.exceptions import DvcException +from dvc.logger import Logger from dvc.system import System @@ -84,8 +85,8 @@ class StateFile(object): res = { self.PARAM_TYPE: self.MAGIC, self.PARAM_VERSION: self.VERSION, - self.PARAM_ARGV: self.argv, - self.PARAM_NORM_ARGV: self.norm_argv, + self.PARAM_ARGV: self.process_args(self.argv, 'argv'), + self.PARAM_NORM_ARGV: self.process_args(self.norm_argv, 'normalized argv'), self.PARAM_CWD: self.cwd, self.PARAM_CREATED_AT: self.created_at, self.PARAM_INPUT_FILES: self.input_files, @@ -106,6 +107,27 @@ class StateFile(object): json.dump(res, fd, indent=2) pass + def process_args(self, argv, name='argv'): + was_changed = False + result = [] + + for arg in argv: + if arg.endswith('dvc2.py'): + result.append('dvc') + was_changed = True + else: + result.append(arg) + + if was_changed: + Logger.debug('Save state file {}. Replace {} "{}" to "{}"'.format( + self.file, + name, + argv, + result + )) + + return result + def normalized_args(self): result = []