diff --git a/src/python/OBis/obis/dm/config.py b/src/python/OBis/obis/dm/config.py
index 3aea5734faff77e6178b0445c6ca92a1cc8f73c5..91a4b9bf3d2fa93c2b8965547e65404e80ec3055 100644
--- a/src/python/OBis/obis/dm/config.py
+++ b/src/python/OBis/obis/dm/config.py
@@ -123,7 +123,15 @@ class ConfigEnv(object):
         return True
 
 
-class PropertiesEnv(ConfigEnv):
+class DataSetEnv(ConfigEnv):
+
+    # TODO remove data_set from property names
+    def initialize_params(self):
+        self.add_param(ConfigParam(name='data_set_type', private=False))
+        self.add_param(ConfigParam(name='data_set_properties', private=False, is_json=True))        
+
+
+class RepositoryEnv(ConfigEnv):
     """ These are properties which are not configured by the user but set by obis. """
 
     def initialize_params(self):
@@ -290,7 +298,7 @@ class ConfigResolver(object):
     def __init__(self, location_resolver=None):
         self.resolvers = []
         self.resolvers.append(ConfigResolverImpl(location_resolver=location_resolver, env=ConfigEnv()))
-        self.resolvers.append(ConfigResolverImpl(location_resolver=location_resolver, env=PropertiesEnv(), config_file='properties.json'))
+        self.resolvers.append(ConfigResolverImpl(location_resolver=location_resolver, env=RepositoryEnv(), config_file='reposiory.json'))
 
     def config_dict(self, local_only=False):
         combined_dict = {}
@@ -309,10 +317,13 @@ class ConfigResolver(object):
             if json_param_name in resolver.env.params:
                 return resolver.set_value_for_json_parameter(json_param_name, name, value, loc)
 
-    def local_public_properties_path(self):
+    # TODO return a list
+    def local_public_properties_paths(self, get_usersettings=False):
+        paths = []
         for resolver in self.resolvers:
-            if not resolver.is_usersetting():
-                return resolver.local_public_properties_path()        
+            if get_usersettings == resolver.is_usersetting():
+                paths.append(resolver.local_public_properties_path())
+        return paths
 
     def copy_global_to_local(self):
         for resolver in self.resolvers:
diff --git a/src/python/OBis/obis/dm/config_test.py b/src/python/OBis/obis/dm/config_test.py
index e5f5dc4cc65ef41df1935f8b825bb14c1d6fab2e..d635c70af3cfd71bdfb753cd66a8af5543d29bc9 100644
--- a/src/python/OBis/obis/dm/config_test.py
+++ b/src/python/OBis/obis/dm/config_test.py
@@ -46,7 +46,7 @@ def test_read_config(tmpdir):
         expected_dict = json.load(f)
     assert config_dict['user'] == expected_dict['user']
 
-    assert './.obis/properties.json' == resolver.local_public_properties_path()
+    assert './.obis/properties.json' in resolver.local_public_properties_paths()
 
 
 def test_write_config(tmpdir):
diff --git a/src/python/OBis/obis/dm/data_mgmt.py b/src/python/OBis/obis/dm/data_mgmt.py
index 36d21714bb5a3a94ae658238278ccdaaf7e9280f..228e2c311adcc2b73e87408912184579ee917e3e 100644
--- a/src/python/OBis/obis/dm/data_mgmt.py
+++ b/src/python/OBis/obis/dm/data_mgmt.py
@@ -312,12 +312,16 @@ class GitDataMgmt(AbstractDataMgmt):
         return CommandResult(returncode=0, output=output)
 
     def commit_metadata_updates(self, msg_fragment=None):
-        properties_path = self.config_resolver.local_public_properties_path()
-        status = self.git_wrapper.git_status(properties_path)
-        if len(status.output.strip()) < 1:
+        properties_paths = self.config_resolver.local_public_properties_paths()
+        total_status = ''
+        for properties_path in properties_paths:
+            status = self.git_wrapper.git_status(properties_path).output.strip()
+            total_status += status
+            if len(status) > 0:
+                self.git_wrapper.git_add(properties_path)
+        if len(total_status) < 1:
             # Nothing to commit
             return CommandResult(returncode=0, output="")
-        self.git_wrapper.git_add(properties_path)
         if msg_fragment is None:
             msg = "OBIS: Update openBIS metadata cache."
         else:
@@ -329,9 +333,10 @@ class GitDataMgmt(AbstractDataMgmt):
 
     def restore(self):
         self.git_wrapper.git_reset_to(self.previous_git_commit_hash)
-        properties_path = self.config_resolver.local_public_properties_path()
-        self.git_wrapper.git_checkout(properties_path)
-        self.git_wrapper.git_delete_if_untracked('.obis/properties.json')
+        properties_paths = self.config_resolver.local_public_properties_paths()
+        for properties_path in properties_paths:
+            self.git_wrapper.git_checkout(properties_path)
+            self.git_wrapper.git_delete_if_untracked(properties_path)
 
     def clone(self, data_set_id, ssh_user, content_copy_index):
         cmd = Clone(self, data_set_id, ssh_user, content_copy_index)
diff --git a/src/python/OBis/obis/scripts/cli.py b/src/python/OBis/obis/scripts/cli.py
index bc232b4da7df7129c15cbcc9cbe5a4701ee8a1ed..2b7aa84bc76a1b37cdec848e0105a55c6e50d3d3 100644
--- a/src/python/OBis/obis/scripts/cli.py
+++ b/src/python/OBis/obis/scripts/cli.py
@@ -71,79 +71,6 @@ def cli(ctx, quiet, skip_verification):
         ctx.obj['verify_certificates'] = False
 
 
-@cli.command()
-@click.pass_context
-@click.argument('repository', type=click.Path(exists=True))
-def addref(ctx, repository):
-    """Add the given repository as a reference to openBIS.
-    """
-    with cd(repository):
-        data_mgmt = shared_data_mgmt(ctx.obj)
-        return check_result("addref", run(data_mgmt.addref))
-
-
-@cli.command()
-@click.pass_context
-@click.argument('repository', type=click.Path(exists=True))
-def removeref(ctx, repository):
-    """Remove the reference to the given repository from openBIS.
-    """
-    with cd(repository):
-        data_mgmt = shared_data_mgmt(ctx.obj)
-        return check_result("addref", run(data_mgmt.removeref))
-
-
-@cli.command()
-@click.pass_context
-@click.option('-u', '--ssh_user', default=None, help='User to connect to remote systems via ssh')
-@click.option('-c', '--content_copy_index', type=int, default=None, help='Index of the content copy to clone from in case there are multiple copies')
-@click.argument('data_set_id')
-def clone(ctx, ssh_user, content_copy_index, data_set_id):
-    """Clone the repository found in the given data set id.
-    """
-    data_mgmt = shared_data_mgmt(ctx.obj)
-    return check_result("clone", run(lambda: data_mgmt.clone(data_set_id, ssh_user, content_copy_index)))
-
-
-@cli.command()
-@click.pass_context
-@click.option('-m', '--msg', prompt=True, help='A message explaining what was done.')
-@click.option('-a', '--auto_add', default=True, is_flag=True, help='Automatically add all untracked files.')
-@click.option('-i', '--ignore_missing_parent', default=True, is_flag=True, help='If parent data set is missing, ignore it.')
-def commit(ctx, msg, auto_add, ignore_missing_parent):
-    """Commit the repository to git and inform openBIS.
-    """
-    data_mgmt = shared_data_mgmt(ctx.obj)
-    return check_result("commit", run(lambda: data_mgmt.commit(msg, auto_add, ignore_missing_parent)))
-
-
-@cli.command()
-@click.option('-g', '--is_global', default=False, is_flag=True, help='Configure global or local.')
-@click.option('-p', '--is_data_set_property', default=False, is_flag=True, help='Configure data set property.')
-@click.argument('prop', default="")
-@click.argument('value', default="")
-@click.pass_context
-def config(ctx, is_global, is_data_set_property, prop, value):
-    """Configure the openBIS setup.
-
-    Configure the openBIS server url, the data set type, and the data set properties.
-    """
-    data_mgmt = shared_data_mgmt(ctx.obj)
-    config_internal(data_mgmt, is_global, is_data_set_property, prop, value)
-
-
-@cli.command()
-@click.option('-c', '--content_copy_index', type=int, default=None, help='Index of the content copy to download from.')
-@click.option('-f', '--file', help='File in the data set to download - downloading all if not given.')
-@click.argument('data_set_id')
-@click.pass_context
-def download(ctx, content_copy_index, file, data_set_id):
-    """ Download files of a linked data set.
-    """
-    data_mgmt = shared_data_mgmt(ctx.obj)
-    return check_result("download", run(lambda: data_mgmt.download(data_set_id, content_copy_index, file)))
-
-
 def config_internal(data_mgmt, is_global, is_data_set_property, prop, value):
     resolver = data_mgmt.config_resolver
     if is_global:
@@ -212,28 +139,138 @@ def init_handle_cleanup(result, object_id, collection_id, folder, data_mgmt):
             return check_result("init_data", set_property(data_mgmt, 'collection_id', collection_id, False, False))
 
 
+# setting commands
+# obis [type] [get|set] [-g]? [-p]? [[key]+ | [key=value]+]?
+
+
+class GetOrSet(click.ParamType):
+    name = 'get_or_set'
+
+    def convert(self, value, param, ctx):
+        result = {}
+        result['get'] = value == 'get'
+        result['set'] = value == 'set'
+        if result['get'] == False and result['set'] == False:
+            self.fail(param=param, message='Parameter has to be \'get\' or \'set\'.')
+        return result
+
+
+class Settings(click.ParamType):
+    name = 'settings'
+
+    def convert(self, value, param, ctx):
+        print(value)
+        try:
+            properties = {}
+            split = list(filter(lambda term: len(term) > 0, value.split(',')))
+            for setting in split:
+                setting_split = setting.split('=')
+                if len(setting_split) != 2:
+                    self._fail(param)
+                key = setting_split[0]
+                value = setting_split[1]
+                properties[key] = value
+            return properties
+        except:
+            self._fail(param)
+
+    def _fail(self, param):
+            self.fail(param=param, message='Settings must be in the format: key1=value1, key2=value2, ...')
+
+
+def _join_settings(setting_dicts):
+    joined = {}
+    for setting_dict in setting_dicts:
+        for key, value in setting_dict.items():
+            joined[key] = value
+    return joined
+
+
 @cli.command()
+@click.argument('get_or_set', type=GetOrSet())
+@click.option('-g', '--is_global', default=False, is_flag=True, help='Configure global or local.')
+@click.argument('settings', type=Settings(), nargs=-1)
 @click.pass_context
-@click.option('-oi', '--object_id', help='Set the id of the owning sample.')
-@click.option('-ci', '--collection_id', help='Set the id of the owning experiment.')
-@click.argument('folder', type=click.Path(exists=False, file_okay=False))
-@click.argument('description', default="")
-def init(ctx, object_id, collection_id, folder, description):
-    """Initialize the folder as a data folder (alias for init_data)."""
-    return init_data_impl(ctx, object_id, collection_id, folder, description)
+def repository(ctx, get_or_set, is_global, settings):
+    print(get_or_set)
+    print(is_global)
+    settings_dict = _join_settings(settings)
+    print(settings_dict)
+    for prop, value in settings_dict.items():
+        config(ctx, is_global, False, prop, value)
+
+
+## repository -> properties.json
+### repository_id
+### external_dms_id
+### data_set_id
+
+## config -> config.json
+### fileservice_url
+### git_annex_hash_as_checksum
+### hostname
+### openbis_url
+### user
+### verify_certificates
+
+## object -> config.json
+### id
+
+## collection -> config.json
+### id
+
+## data_set -> config.json
+### type
+### properties
+
+
+# TODO replace by multiple commands
+@cli.command()
+@click.option('-g', '--is_global', default=False, is_flag=True, help='Configure global or local.')
+@click.option('-p', '--is_data_set_property', default=False, is_flag=True, help='Configure data set property.')
+@click.argument('prop', default="")
+@click.argument('value', default="")
+@click.pass_context
+def config(ctx, is_global, is_data_set_property, prop, value):
+    """Configure the openBIS setup.
+
+    Configure the openBIS server url, the data set type, and the data set properties.
+    """
+    data_mgmt = shared_data_mgmt(ctx.obj)
+    config_internal(data_mgmt, is_global, is_data_set_property, prop, value)
+
+
+# repository commands: status, sync, commit, init, addref, removeref, init_analysis
+
+
+# TODO commit from without repository
+# TODO add optional repository receiver
+@cli.command()
+@click.pass_context
+@click.option('-m', '--msg', prompt=True, help='A message explaining what was done.')
+@click.option('-a', '--auto_add', default=True, is_flag=True, help='Automatically add all untracked files.')
+@click.option('-i', '--ignore_missing_parent', default=True, is_flag=True, help='If parent data set is missing, ignore it.')
+def commit(ctx, msg, auto_add, ignore_missing_parent):
+    """Commit the repository to git and inform openBIS.
+    """
+    data_mgmt = shared_data_mgmt(ctx.obj)
+    return check_result("commit", run(lambda: data_mgmt.commit(msg, auto_add, ignore_missing_parent)))
 
 
+# TODO allow init from within repository
+# TODO add optional repository receiver
 @cli.command()
 @click.pass_context
 @click.option('-oi', '--object_id', help='Set the id of the owning sample.')
 @click.option('-ci', '--collection_id', help='Set the id of the owning experiment.')
 @click.argument('folder', type=click.Path(exists=False, file_okay=False))
 @click.argument('description', default="")
-def init_data(ctx, object_id, collection_id, folder, description):
-    """Initialize the folder as a data folder."""
+def init(ctx, object_id, collection_id, folder, description):
+    """Initialize the folder as a data folder (alias for init_data)."""
     return init_data_impl(ctx, object_id, collection_id, folder, description)
 
 
+# TODO add optional repository receiver
 @cli.command()
 @click.pass_context
 @click.option('-p', '--parent', type=click.Path(exists=False, file_okay=False))
@@ -246,6 +283,8 @@ def init_analysis(ctx, parent, object_id, collection_id, folder, description):
     return init_analysis_impl(ctx, parent, object_id, collection_id, folder, description)
 
 
+# TODO allow from without repository with repository folder as parameter
+# TODO add optional repository receiver
 @cli.command()
 @click.pass_context
 def status(ctx):
@@ -256,6 +295,8 @@ def status(ctx):
     click.echo(result.output)
 
 
+# TODO allow from without repository with repository folder as parameter
+# TODO add optional repository receiver
 @cli.command()
 @click.pass_context
 @click.option('-i', '--ignore_missing_parent', default=True, is_flag=True, help='If parent data set is missing, ignore it.')
@@ -266,6 +307,60 @@ def sync(ctx, ignore_missing_parent):
     return check_result("sync", run(lambda: data_mgmt.sync(ignore_missing_parent)))
 
 
+# TODO allow to addref from within repository without argument
+# TODO add optional repository receiver
+@cli.command()
+@click.pass_context
+@click.argument('repository', type=click.Path(exists=True))
+def addref(ctx, repository):
+    """Add the given repository as a reference to openBIS.
+    """
+    with cd(repository):
+        data_mgmt = shared_data_mgmt(ctx.obj)
+        return check_result("addref", run(data_mgmt.addref))
+
+
+# TODO allow to removeref from within repository without argument
+# TODO add optional repository receiver
+@cli.command()
+@click.pass_context
+@click.argument('repository', type=click.Path(exists=True))
+def removeref(ctx, repository):
+    """Remove the reference to the given repository from openBIS.
+    """
+    with cd(repository):
+        data_mgmt = shared_data_mgmt(ctx.obj)
+        return check_result("addref", run(data_mgmt.removeref))
+
+
+# data set commands: download / clone
+
+# TODO obis data_set? clone data_set_id
+@cli.command()
+@click.option('-c', '--content_copy_index', type=int, default=None, help='Index of the content copy to download from.')
+@click.option('-f', '--file', help='File in the data set to download - downloading all if not given.')
+@click.argument('data_set_id')
+@click.pass_context
+def download(ctx, content_copy_index, file, data_set_id):
+    """ Download files of a linked data set.
+    """
+    data_mgmt = shared_data_mgmt(ctx.obj)
+    return check_result("download", run(lambda: data_mgmt.download(data_set_id, content_copy_index, file)))
+
+
+# TODO obis dataset? clone data_set_id
+@cli.command()
+@click.pass_context
+@click.option('-u', '--ssh_user', default=None, help='User to connect to remote systems via ssh')
+@click.option('-c', '--content_copy_index', type=int, default=None, help='Index of the content copy to clone from in case there are multiple copies')
+@click.argument('data_set_id')
+def clone(ctx, ssh_user, content_copy_index, data_set_id):
+    """Clone the repository found in the given data set id.
+    """
+    data_mgmt = shared_data_mgmt(ctx.obj)
+    return check_result("clone", run(lambda: data_mgmt.clone(data_set_id, ssh_user, content_copy_index)))
+
+
 def main():
     cli(obj={})