def f_merge(self, other_trajectory, trial_parameter=None, remove_duplicates=False,
                ignore_data=(),
                backup=True,
                move_data=False,
                delete_other_trajectory=False,
                keep_info=True,
                keep_other_trajectory_info=True,
                merge_config=True,
                consecutive_merge=False,
                slow_merge=False):
        """Merges another trajectory into the current trajectory.

        Both trajectories must live in the same space. This means both need to have the same
        parameters with similar types of values.

        Note that links are also merged. There are exceptions: Links found under
        a generic run group called `run_ALL` or links linking to a node under such a
        group are NOT merged and simply skipped, because there is no straightforward
        way to resolve the link.

        :param other_trajectory: Other trajectory instance to merge into the current one.

        :param trial_parameter:

            If you have a particular parameter that specifies only the trial
            number, i.e. an integer parameter running form 0 to T1 and
            0 to T2, the parameter is modified such that after merging it will
            cover the range 0 to T1+T2+1. T1 is the number of individual trials in the current
            trajectory and T2 number of trials in the other trajectory.

        :param remove_duplicates:

            Whether you want to remove duplicate parameter points.
            Requires N1 * N2 (quadratic complexity in single runs).
            A ValueError is raised if no runs would be merged.

        :param ignore_data:

            List of full names of data that should be ignored and not merged.

        :param backup:

            If ``True``, backs up both trajectories into files chosen automatically
            by the storage services. If you want to customize your backup use
            the `f_backup` function instead.

        :param move_data:

           Tells the storage service to move data from one trajectory to the other
           instead of copying it.

           If you use the HDF5 storage service and both trajectories are
           stored in the same file, merging is performed fast directly within
           the file. You can choose if you want to copy nodes ('move_nodes=False`)
           from the other trajectory to the current one, or if you want to move them.
           Accordingly, the stored data is no longer accessible in the other trajectory.

        :param delete_other_trajectory:

            If you want to delete the other trajectory after merging.

        :param keep_info:

            If `True`, information about the merge is added to the trajectory `config` tree under
            `config.merge`.

        :param merge_config:

            Whether or not to merge all config parameters under `.config.git`,
            `.config.environment`, and `.config.merge` of the other trajectory
            into the current one.

        :param keep_other_trajectory_info:

            Whether to keep information like length, name, etc. of the other trajectory
            in case you want to keep all the information. Setting of `keep_other_trajectory_info`
            is irrelevant in case `keep_info=False`.

        :param consecutive_merge:
            Can be set to `True` if you are about to merge several trajectories into the current
            one within a loop to avoid quadratic complexity.
            But remember to store your trajectory manually after
            all merges. Also make sure that all parameters and derived parameters are available
            in your current trajectory and load them before the consecutive merging.
            Also avoid specifying a `trial_parameter` and set `backup=False`
            to avoid quadratic complexity in case of consecutive merges.

        :param slow_merge:
            Enforces a slow merging. This means all data is loaded one after the other to
            memory and stored to disk. Otherwise it is tried to directly copy the data
            from one file into another without explicitly loading the data.

        If you cannot directly merge trajectories within one HDF5 file, a slow merging process
        is used. Results are loaded, stored, and emptied again one after the other. Might take
        some time!

        Annotations of parameters and derived parameters under `.derived_parameters.trajectory`
        are NOT merged. If you wish to extract the annotations of these parameters you have to
        do that manually before merging. Note that annotations of results and derived parameters
        of single runs are copied, so you don't have to worry about these.

        """
        if consecutive_merge and trial_parameter is not None:
            self._logger.warning('If you do a consecutive merge and specify a trial parameter, '
                                 'your merging will still suffer from quadratic time complexity!')
        if consecutive_merge and backup:
            self._logger.warning('If you do a consecutive merge and backup, '
                                 'your merging will still suffer from quadratic time complexity!')

        # Keep the timestamp of the merge
        timestamp = time.time()
        original_ignore_data = set(ignore_data)
        ignore_data = original_ignore_data.copy()

        old_len = len(self)

        # Check if trajectories can be merged
        self._check_if_both_have_same_parameters(other_trajectory, ignore_data, consecutive_merge)

        # Create a full mapping set for renaming
        self._make_reversed_wildcards(old_length=old_len)
        other_trajectory._make_reversed_wildcards()

        # BACKUP if merge is possible
        if backup:
            other_trajectory.f_backup()
            self.f_backup()

        # Merge parameters and keep track which runs where used and which parameters need
        # to be updated
        self._logger.info('Merging the parameters')
        used_runs, changed_parameters = self._merge_parameters(
            other_trajectory,
            remove_duplicates,
            trial_parameter,
            ignore_data)

        ignore_data.update(set(changed_parameters))

        if len(used_runs) == 0:
            raise ValueError('Your merge discards all runs of the other trajectory, maybe you '
                             'try to merge a trajectory with itself?')

        # Dictionary containing the mappings between run names in the other trajectory
        # and their new names in the current trajectory
        rename_dict = {}

        self._logger.info('Merging run information')
        allowed_translations = set([translation for translation, pair in
                                    other_trajectory._reversed_wildcards.items() if
                                        any(x in used_runs for x in pair[0])])
        self._merge_single_runs(other_trajectory, used_runs)

        self._logger.info('Merging derived parameters')
        self._merge_derived_parameters(other_trajectory=other_trajectory,
                                         used_runs=used_runs,
                                         rename_dict=rename_dict,
                                         allowed_translations=allowed_translations,
                                         ignore_data=ignore_data)

        # Keep track of all trajectory results that should be merged and put
        # information into `rename_dict`
        self._merge_results(other_trajectory=other_trajectory,
                             used_runs=used_runs,
                             rename_dict=rename_dict,
                             allowed_translations=allowed_translations,
                             ignore_data=ignore_data)



        # The storage service needs to prepare the file for merging.
        # This includes updating meta information and already storing the merged parameters
        self._logger.info('Start copying results and single run derived parameters')
        self._logger.info('Updating Trajectory information and changed parameters in storage')
        self._storage_service.store(pypetconstants.PREPARE_MERGE, self,
                                    trajectory_name=self.v_name,
                                    changed_parameters=changed_parameters,
                                    old_length=old_len)

        if not slow_merge:
            try:
                # Merge the single run derived parameters and all results
                # within the same hdf5 file based on `renamed_dict`
                try:
                    # try to get the other file
                    other_filename = other_trajectory.v_storage_service.filename
                except AttributeError:
                    self._logger.warning('Could not determine the filename of the other '
                                         'trajectory, I will assume it`s in the same file.')
                    other_filename = None
                self._storage_service.store(pypetconstants.MERGE, 'FAST MERGE', trajectory_name=self.v_name,
                                            other_trajectory_name=other_trajectory.v_name,
                                            rename_dict=rename_dict, move_nodes=move_data,
                                            delete_trajectory=delete_other_trajectory,
                                            other_filename=other_filename)

            except pex.NoSuchServiceError as exc:
                # If the storage service does not support merge we end up here
                self._logger.exception('My storage service does not support merging of trajectories, '
                                     'I will use the f_load mechanism of the other trajectory and '
                                     'store the results slowly item by item. '
                                     'Note that thereby the other '
                                     'trajectory will be altered (in RAM).')
                slow_merge = True

            except ValueError as exc:
                # If both trajectories are stored in separate files we end up here
                self._logger.exception('Could not perfom fast merging. '
                                     'I will use the `f_load` method of the other trajectory and '
                                     'store the results slowly item by item. '
                                     'Note that thereby the other '
                                     'trajectory will be altered (in RAM).')
                slow_merge = True

        if slow_merge:
            self._merge_slowly(other_trajectory, rename_dict)

        # We will merge the git commits and other config data
        if merge_config:
            self._merge_config(other_trajectory)

        # Finally merging links
        self._merge_links(other_trajectory=other_trajectory,
                         used_runs=used_runs,
                         allowed_translations=allowed_translations,
                         ignore_data=original_ignore_data)

        # Add meta information about the merge to the current trajectory
        self._logger.info('Adding merge information')
        formatted_time = datetime.datetime.fromtimestamp(timestamp).strftime('%Y_%m_%d_%Hh%Mm%Ss')
        hexsha = hashlib.sha1((self.v_name +
                                             str(self.v_timestamp) +
                                             other_trajectory.v_name +
                                             str(other_trajectory.v_timestamp) +
                                             VERSION).encode('utf-8')).hexdigest()

        short_hexsha = hexsha[0:7]

        if keep_info:
            merge_name = 'merge_%s_%s' % (short_hexsha, formatted_time)

            config_name = 'merge.%s.merged_runs' % merge_name
            self.f_add_config(config_name, len(used_runs),
                              comment='Number of merged runs')

            config_name = 'merge.%s.timestamp' % merge_name
            self.f_add_config(config_name, timestamp,
                              comment='Timestamp of merge')

            config_name = 'merge.%s.hexsha' % merge_name
            self.f_add_config(config_name, hexsha,
                              comment='SHA-1 identifier of the merge')

            config_name = 'merge.%s.remove_duplicates' % merge_name
            self.f_add_config(config_name, remove_duplicates,
                              comment='Option to remove duplicate entries')

            if original_ignore_data:
                config_name = 'merge.%s.ignore_data' % merge_name
                self.f_add_config(config_name, tuple(original_ignore_data),
                                  comment='Data to ignore during merge')

            config_name = 'merge.%s.length_before_merge' % merge_name
            self.f_add_config(config_name, len(self),
                              comment='Length of trajectory before merge')

            self.config.merge.v_comment = 'Settings and information of the different merges'

            if self.v_version != VERSION:
                config_name = 'merge.%s.version' % merge_name
                self.f_add_config(config_name, self.v_version,
                                  comment='Pypet version if it differs from the version'
                                          ' of the trajectory')

            if trial_parameter is not None:
                config_name = 'merge.%s.trial_parameter' % merge_name
                self.f_add_config(config_name, len(other_trajectory),
                                  comment='Name of trial parameter')

            if keep_other_trajectory_info:

                if other_trajectory.v_version != self.v_version:
                    config_name = 'merge.%s.other_trajectory.version' % merge_name
                    self.f_add_config(config_name, other_trajectory.v_version,
                                      comment='The version of pypet you used to manage the other'
                                              ' trajectory. Only added if other trajectory\'s'
                                              ' version differs from current trajectory version.')

                config_name = 'merge.%s.other_trajectory.name' % merge_name
                self.f_add_config(config_name, other_trajectory.v_name,
                                  comment='Name of other trajectory merged into the current one')

                config_name = 'merge.%s.other_trajectory.timestamp' % merge_name
                self.f_add_config(config_name, other_trajectory.v_timestamp,
                                  comment='Timestamp of creation of other trajectory '
                                          'merged into the current one')

                config_name = 'merge.%s.other_trajectory.length' % merge_name
                self.f_add_config(config_name, len(other_trajectory),
                                  comment='Length of other trajectory')

                if other_trajectory.v_comment:
                    config_name = 'merge.%s.other_trajectory.comment' % merge_name
                    self.f_add_config(config_name, other_trajectory.v_comment,
                                      comment='Comment of other trajectory')

        # Write out the merged data to disk
        if not consecutive_merge:
            self._logger.info('Writing merged data to disk')
            self.f_store(store_data=pypetconstants.STORE_DATA)
            self._reversed_wildcards = {}
        other_trajectory._reversed_wildcards = {}

        self._logger.info('Finished Merging!')
Merges another trajectory into the current trajectory.

        Both trajectories must live in the same space. This means both need to have the same
        parameters with similar types of values.

        Note that links are also merged. There are exceptions: Links found under
        a generic run group called `run_ALL` or links linking to a node under such a
        group are NOT merged and simply skipped, because there is no straightforward
        way to resolve the link.

        :param other_trajectory: Other trajectory instance to merge into the current one.

        :param trial_parameter:

            If you have a particular parameter that specifies only the trial
            number, i.e. an integer parameter running form 0 to T1 and
            0 to T2, the parameter is modified such that after merging it will
            cover the range 0 to T1+T2+1. T1 is the number of individual trials in the current
            trajectory and T2 number of trials in the other trajectory.

        :param remove_duplicates:

            Whether you want to remove duplicate parameter points.
            Requires N1 * N2 (quadratic complexity in single runs).
            A ValueError is raised if no runs would be merged.

        :param ignore_data:

            List of full names of data that should be ignored and not merged.

        :param backup:

            If ``True``, backs up both trajectories into files chosen automatically
            by the storage services. If you want to customize your backup use
            the `f_backup` function instead.

        :param move_data:

           Tells the storage service to move data from one trajectory to the other
           instead of copying it.

           If you use the HDF5 storage service and both trajectories are
           stored in the same file, merging is performed fast directly within
           the file. You can choose if you want to copy nodes ('move_nodes=False`)
           from the other trajectory to the current one, or if you want to move them.
           Accordingly, the stored data is no longer accessible in the other trajectory.

        :param delete_other_trajectory:

            If you want to delete the other trajectory after merging.

        :param keep_info:

            If `True`, information about the merge is added to the trajectory `config` tree under
            `config.merge`.

        :param merge_config:

            Whether or not to merge all config parameters under `.config.git`,
            `.config.environment`, and `.config.merge` of the other trajectory
            into the current one.

        :param keep_other_trajectory_info:

            Whether to keep information like length, name, etc. of the other trajectory
            in case you want to keep all the information. Setting of `keep_other_trajectory_info`
            is irrelevant in case `keep_info=False`.

        :param consecutive_merge:
            Can be set to `True` if you are about to merge several trajectories into the current
            one within a loop to avoid quadratic complexity.
            But remember to store your trajectory manually after
            all merges. Also make sure that all parameters and derived parameters are available
            in your current trajectory and load them before the consecutive merging.
            Also avoid specifying a `trial_parameter` and set `backup=False`
            to avoid quadratic complexity in case of consecutive merges.

        :param slow_merge:
            Enforces a slow merging. This means all data is loaded one after the other to
            memory and stored to disk. Otherwise it is tried to directly copy the data
            from one file into another without explicitly loading the data.

        If you cannot directly merge trajectories within one HDF5 file, a slow merging process
        is used. Results are loaded, stored, and emptied again one after the other. Might take
        some time!

        Annotations of parameters and derived parameters under `.derived_parameters.trajectory`
        are NOT merged. If you wish to extract the annotations of these parameters you have to
        do that manually before merging. Note that annotations of results and derived parameters
        of single runs are copied, so you don't have to worry about these.