From 51cce677ce0554f4fe2b7b46ccbc36c0ebdf5137 Mon Sep 17 00:00:00 2001 From: Ruslan Kuprieiev Date: Wed, 11 Oct 2017 06:04:55 +0300 Subject: [PATCH] dvc: add 'merge' command Fixes #246 Signed-off-by: Ruslan Kuprieiev --- dvc/cli.py | 8 +++++++ dvc/command/merge.py | 55 ++++++++++++++++++++++++++++++++++++++++++++ dvc/git_wrapper.py | 16 +++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 dvc/command/merge.py diff --git a/dvc/cli.py b/dvc/cli.py index c2564be1c..a5344e07b 100644 --- a/dvc/cli.py +++ b/dvc/cli.py @@ -16,6 +16,7 @@ from dvc.command.show_workflow import CmdShowWorkflow from dvc.command.instance_create import CmdInstanceCreate from dvc.command.config import CmdConfig from dvc.command.show_pipeline import CmdShowPipeline +from dvc.command.merge import CmdMerge from dvc import VERSION @@ -407,6 +408,13 @@ def parse_args(argv=None): workflow_parser.set_defaults(func=CmdShowWorkflow) + # Merge + merge_parser = subparsers.add_parser( + 'merge', + parents=[parent_parser], + help='Merge') + merge_parser.set_defaults(func=CmdMerge) + if isinstance(argv, str): argv = argv.split() diff --git a/dvc/command/merge.py b/dvc/command/merge.py new file mode 100644 index 000000000..871a854f0 --- /dev/null +++ b/dvc/command/merge.py @@ -0,0 +1,55 @@ +import json + +from dvc.command.base import CmdBase, DvcLock +from dvc.logger import Logger +from dvc.state_file import StateFile +from dvc.path.data_item import NotInDataDirError + +class CmdMerge(CmdBase): + def __init__(self, settings): + super(CmdMerge, self).__init__(settings) + + def print_info(self, targets): + for fname in targets: + Logger.info('Restored original data after merge:') + Logger.info(' {}'.format(fname)) + + def collect_targets(self): + targets = [] + flist = self.git.get_last_merge_changed_files() + for fname in flist: + try: + item = self.settings.path_factory.data_item(fname) + except NotInDataDirError: + continue + + try: + state = StateFile.load(item, self.git) + except Exception as ex: + Logger.error('Failed to load state file for {}'.format(fname), exc_info=True) + return None + + if not state.command == StateFile.COMMAND_IMPORT_FILE: + continue + + targets.append(fname) + + return targets + + def checkout_targets(self, targets): + for fname in targets: + self.git.checkout_file_before_last_merge(fname) + + msg = 'DVC merge files: {}'.format(' '.join(targets)) + self.commit_if_needed(msg) + + def run(self): + with DvcLock(self.is_locker, self.git): + targets = self.collect_targets() + if not targets: + return 1 + + self.checkout_targets(targets) + self.print_info(targets) + + return 0 diff --git a/dvc/git_wrapper.py b/dvc/git_wrapper.py index 2e307159e..a0afee4eb 100644 --- a/dvc/git_wrapper.py +++ b/dvc/git_wrapper.py @@ -253,6 +253,22 @@ class GitWrapper(GitWrapperI): except ExecutorError: return None + @staticmethod + def get_last_merge_changed_files(): + cmd = 'git diff --name-only @{1}' + Logger.debug('[dvc-git] Get last merge changed files. Command: {}'.format(cmd)) + try: + fstr = Executor.exec_cmd_only_success(cmd.split()) + return fstr.split('\n') + except ExecutorError: + return None + + @staticmethod + def checkout_file_before_last_merge(fname): + cmd = 'git checkout @{1} -- ' + fname + Logger.debug('[dvc-git] Checkout file {} before last merge. Command: {}'.format(fname, cmd)) + Executor.exec_cmd_only_success(cmd.split()) + def separate_dependency_files_and_dirs(self, code_dependencies): code_files = [] code_dirs = []