diff --git a/obis/src/python/integration_tests/integration_tests.py b/obis/src/python/integration_tests/integration_tests.py
index 4651f7e527716beaeefbe88432e9f1a5b10ae0a7..540a467e9bcd251330ee91ca4f461deced31482b 100644
--- a/obis/src/python/integration_tests/integration_tests.py
+++ b/obis/src/python/integration_tests/integration_tests.py
@@ -11,6 +11,7 @@ import subprocess
 from subprocess import PIPE
 from subprocess import SubprocessError
 from contextlib import contextmanager
+from random import randrange
 from pybis import Openbis
 
 
@@ -31,6 +32,7 @@ def test_obis(tmpdir):
 
     o = Openbis('https://obisserver:8443', verify_certificates=False)
     o.login('admin', 'admin', save_token=True)
+    setup_masterdata(o)
 
     output_buffer = '=================== 1. Global settings ===================\n'
     if os.path.exists('~/.obis'):
@@ -322,6 +324,18 @@ def test_obis(tmpdir):
                 timeout = True
             assert timeout == True
 
+        output_buffer = '=================== 22. changing identifier ===================\n'
+        settings = create_repository_and_commit(tmpdir, o, 'data14', '/DEFAULT/BIGDATA2')
+        move_sample(o, settings['object']['permId'], 'BIGDATA')
+        try:
+            settings = commit_new_change(tmpdir, o, 'data14')
+            assert settings['object']['id'] == '/BIGDATA/BIGDATA2'
+        finally:
+            move_sample(o, settings['object']['permId'], 'DEFAULT')
+        with cd('data14'): assert get_settings()['object']['permId'] is not None
+        cmd('obis object set id=/DEFAULT/DEFAULT')
+        with cd('data14'): assert get_settings()['object']['permId'] is not None
+
 
 def assert_file_paths(files, expected_paths):
     paths = list(map(lambda file: file['path'], files))
@@ -373,5 +387,57 @@ def assert_matching(settings, data_set, tmpdir, path):
     assert content_copy['gitRepositoryId'] == settings['repository']['id']
     if settings['object']['id'] is not None:
         assert data_set['sample']['identifier']['identifier'] == settings['object']['id']
+        assert data_set['sample']['permId']['permId'] == settings['object']['permId']
     if settings['collection']['id'] is not None:
         assert data_set['experiment']['identifier']['identifier'] == settings['collection']['id']
+        assert data_set['experiment']['permId']['permId'] == settings['collection']['permId']
+
+def move_sample(o, sample_permId, space):
+    field_update_value = {
+        "@type": "as.dto.common.update.FieldUpdateValue",
+        "value": {
+            "@type": "as.dto.space.id.SpacePermId",
+            "permId": space,
+        },
+        "isModified": True,
+    }
+    o.update_sample(sample_permId, space=field_update_value)
+
+def create_repository_and_commit(tmpdir, o, repo_name, object_id):
+    cmd('obis init ' + repo_name)
+    with cd(repo_name):
+        cmd('touch file')
+        result = cmd('obis status')
+        assert '? file' in result
+        cmd('obis object set id=' + object_id)
+        result = cmd('obis commit -m \'commit-message\'')
+        settings = get_settings()
+        assert settings['repository']['external_dms_id'].startswith('ADMIN-' + socket.gethostname().upper())
+        assert len(settings['repository']['id']) == 36
+        assert "Created data set {}.".format(settings['repository']['data_set_id']) in result
+        data_set = o.get_dataset(settings['repository']['data_set_id']).data
+        assert_matching(settings, data_set, tmpdir, 'obis_data/' + repo_name)
+        return settings
+
+def commit_new_change(tmpdir, o, repo_name):
+    with cd(repo_name):
+        filename = 'file' + str(randrange(100000))
+        cmd('touch ' + filename)
+        result = cmd('obis status')
+        assert '? ' + filename in result
+        result = cmd('obis commit -m \'commit-message\'')
+        settings = get_settings()
+        assert settings['repository']['external_dms_id'].startswith('ADMIN-' + socket.gethostname().upper())
+        assert len(settings['repository']['id']) == 36
+        assert "Created data set {}.".format(settings['repository']['data_set_id']) in result
+        data_set = o.get_dataset(settings['repository']['data_set_id']).data
+        assert_matching(settings, data_set, tmpdir, 'obis_data/' + repo_name)
+        return settings
+
+
+def setup_masterdata(o):
+    spaces = o.get_spaces()
+    if 'BIGDATA' not in o.get_spaces().df.code.values:
+        o.new_space(code='BIGDATA').save()
+    if '/DEFAULT/BIGDATA2' not in o.get_samples().df.identifier.values:
+        o.new_sample(type="UNKNOWN", code='BIGDATA2', space='DEFAULT').save()
diff --git a/obis/src/python/obis/dm/commands/openbis_command.py b/obis/src/python/obis/dm/commands/openbis_command.py
index 734282e8ae65563faa14df55a11600109c3236e2..599d66ca2400e5dda99a7e35d4daca1bb4814b6d 100644
--- a/obis/src/python/obis/dm/commands/openbis_command.py
+++ b/obis/src/python/obis/dm/commands/openbis_command.py
@@ -60,12 +60,18 @@ class OpenbisCommand(object):
     def object_id(self):
         return self.config_dict['object']['id']
 
+    def object_permId(self):
+        return self.config_dict['object']['permId']
+
     def set_object_id(self, value):
         self.config_dict['object']['id'] = value
 
     def collection_id(self):
         return self.config_dict['collection']['id']
 
+    def collection_permId(self):
+        return self.config_dict['collection']['permId']
+
     def set_collection_id(self, value):
         self.config_dict['collection']['id'] = value
 
diff --git a/obis/src/python/obis/dm/commands/openbis_sync.py b/obis/src/python/obis/dm/commands/openbis_sync.py
index e6e1476a2ec4cdaad5075a5cd252d009be5853f7..3cd70b28335d97e0b7e0ca6d4890620a796d2898 100644
--- a/obis/src/python/obis/dm/commands/openbis_sync.py
+++ b/obis/src/python/obis/dm/commands/openbis_sync.py
@@ -50,8 +50,7 @@ class OpenbisSync(OpenbisCommand):
         if result.failure():
             return result
         commit_id = result.output
-        sample_id = self.object_id()
-        experiment_id = self.collection_id()
+        sample_id, experiment_id = self._update_and_get_object_or_collection_id()
         contents = GitRepoFileInfo(self.git_wrapper).contents(git_annex_hash_as_checksum=self.git_annex_hash_as_checksum())
         try:
             data_set = self.openbis.new_git_data_set(data_set_type, top_level_path, commit_id, repository_id, external_dms.code,
@@ -62,8 +61,40 @@ class OpenbisSync(OpenbisCommand):
             return CommandResult(returncode=-1, output=str(e)), None
 
 
-    def commit_metadata_updates(self, msg_fragment=None):
-        return self.data_mgmt.commit_metadata_updates(msg_fragment)
+    def _update_and_get_object_or_collection_id(self):
+        """ Updates identifier of object / collection in case it has changed in openBIS 
+        if the permId is available. Returns sample / experiment identifier."""
+        sample_id = self.object_id()
+        experiment_id = self.collection_id()
+        if self.object_permId() is not None:
+            sample_id = self.openbis.get_sample(self.object_permId()).identifier
+            if sample_id != self.object_id():
+                self.settings_resolver.object.set_value_for_parameter('id', sample_id, 'local')
+                # permId is cleared when the id is set - set it again
+                self.settings_resolver.object.set_value_for_parameter('permId', self.object_permId(), 'local')
+                self.commit_metadata_updates("object identifier changed in openBIS")
+        if self.collection_permId() is not None:
+            experiment_id = self.openbis.get_experiment(self.collection_permId()).identifier
+            if experiment_id != self.collection_id():
+                self.settings_resolver.collection.set_value_for_parameter('id', experiment_id, 'local')
+                # permId is cleared when the id is set - set it again
+                self.settings_resolver.collection.set_value_for_parameter('permId', self.collection_permId(), 'local')
+                self.commit_metadata_updates("collection identifier changed in openBIS")
+        return sample_id, experiment_id
+
+    def _storePermId(self):
+        if self.object_permId() is None and self.object_id() is not None:
+            sample = self.openbis.get_sample(self.object_id())
+            self.settings_resolver.object.set_value_for_parameter('permId', sample.permId, 'local')
+            self.commit_metadata_updates("object permId", omit_usersettings=False)
+        if self.collection_permId() is None and self.collection_id() is not None:
+            experiment = self.openbis.get_experiment(self.collection_id())
+            self.settings_resolver.collection.set_value_for_parameter('permId', experiment.permId, 'local')
+            self.commit_metadata_updates("collection permId", omit_usersettings=False)
+
+
+    def commit_metadata_updates(self, msg_fragment=None, omit_usersettings=True):
+        return self.data_mgmt.commit_metadata_updates(msg_fragment, omit_usersettings=omit_usersettings)
 
 
     def prepare_repository_id(self):
@@ -157,10 +188,14 @@ class OpenbisSync(OpenbisCommand):
 
         self.commit_metadata_updates()
 
+        # store permId of object / collection so we can use those as a reference in the future
+        self._storePermId()
+
         # Update data set id as last commit so we can easily revert it on failure
         self.settings_resolver.repository.set_value_for_parameter('data_set_id', data_set_code, 'local')
         self.commit_metadata_updates("data set id")
 
         # create a data set, using the existing data set as a parent, if there is one
         result, data_set = self.create_data_set(data_set_code, external_dms, repository_id, ignore_parent)
+
         return result
diff --git a/obis/src/python/obis/dm/config.py b/obis/src/python/obis/dm/config.py
index 763404934fcc43312498e1170247f905a0da35ad..a2766838598ed8113034b539651007c7d98fe503 100644
--- a/obis/src/python/obis/dm/config.py
+++ b/obis/src/python/obis/dm/config.py
@@ -9,6 +9,7 @@ Configuration for obis.
 Created by Chandrasekhar Ramakrishnan on 2017-02-10.
 Copyright (c) 2017 Chandrasekhar Ramakrishnan. All rights reserved.
 """
+import abc
 import json
 import os
 
@@ -78,8 +79,10 @@ class ConfigEnv(object):
     def __init__(self):
         self.locations = {}
         self.params = {}
+        self.rules = []
         self.initialize_locations()
         self.initialize_params()
+        self.initialize_rules()
 
     def initialize_locations(self):
         self.add_location(ConfigLocation(['global'], 'user_home', '.obis'))
@@ -120,17 +123,31 @@ class ConfigEnv(object):
     def is_usersetting(self):
         return True
 
+    def add_rule(self, rule):
+        self.rules.append(rule)
+
+    def initialize_rules(self):
+        pass
+
 
 class CollectionEnv(ConfigEnv):
 
     def initialize_params(self):
         self.add_param(ConfigParam(name='id', private=False, ignore_global=True))
+        self.add_param(ConfigParam(name='permId', private=False, ignore_global=True))
+
+    def initialize_rules(self):
+        self.add_rule(ClearPermIdRule())
 
 
 class ObjectEnv(ConfigEnv):
 
     def initialize_params(self):
         self.add_param(ConfigParam(name='id', private=False, ignore_global=True))
+        self.add_param(ConfigParam(name='permId', private=False, ignore_global=True))
+
+    def initialize_rules(self):
+        self.add_rule(ClearPermIdRule())
 
 
 class DataSetEnv(ConfigEnv):
@@ -244,6 +261,9 @@ class ConfigResolver(object):
         with open(config_path, "w") as f:
             json.dump(location_config_dict, f, sort_keys=True, indent=4)
 
+        for rule in self.env.rules:
+            rule.on_set(self, name, value, loc)
+
     def set_value_for_json_parameter(self, json_param_name, name, value, loc):
         """Set one field for the json parameter
         :param json_param_name: Name of the json parameter
@@ -331,10 +351,10 @@ class SettingsResolver(object):
             combined_dict[resolver.categoty] = resolver.config_dict(local_only=local_only)
         return combined_dict
 
-    def local_public_properties_paths(self, get_usersettings=False):
+    def local_public_properties_paths(self, omit_usersettings=True):
         paths = []
         for resolver in self.resolvers:
-            if get_usersettings == resolver.is_usersetting():
+            if omit_usersettings == False or resolver.is_usersetting() ==  False:
                 paths.append(resolver.local_public_properties_path())
         return paths
 
@@ -349,3 +369,19 @@ class SettingsResolver(object):
     def set_location_search_order(self, order):
         for resolver in self.resolvers:
             resolver.location_search_order = order
+
+
+class SettingRule(object):
+    """ Setting rules can react to setting changes and trigger further actions. """
+
+    @abc.abstractmethod
+    def on_set(self, setting, value):
+        pass
+
+
+class ClearPermIdRule(SettingRule):
+    """ When the user sets a new id, the permId might be invalid, so it will be cleared. """
+
+    def on_set(self, config_resolver, name, value, loc):
+        if name == "id":
+            config_resolver.set_value_for_parameter("permId", None, loc)
diff --git a/obis/src/python/obis/dm/data_mgmt.py b/obis/src/python/obis/dm/data_mgmt.py
index c5ec2315d74ec31164d741764a018293e22f077e..339bb2f40b8016b119352f3ba0cace732c649a85 100644
--- a/obis/src/python/obis/dm/data_mgmt.py
+++ b/obis/src/python/obis/dm/data_mgmt.py
@@ -365,8 +365,8 @@ class GitDataMgmt(AbstractDataMgmt):
             output += sync_status.output
         return CommandResult(returncode=0, output=output)
 
-    def commit_metadata_updates(self, msg_fragment=None):
-        properties_paths = self.settings_resolver.local_public_properties_paths()
+    def commit_metadata_updates(self, msg_fragment=None, omit_usersettings=True):
+        properties_paths = self.settings_resolver.local_public_properties_paths(omit_usersettings=omit_usersettings)
         total_status = ''
         for properties_path in properties_paths:
             status = self.git_wrapper.git_status(properties_path).output.strip()