diff --git a/contrib/openldap/core-fd.schema b/contrib/openldap/core-fd.schema
index 070a1ba9f090a47f4662ae17ae86fc0e49f5469b..f7c7ea36371d4704919b9bdb7e9cfd336907f9eb 100644
--- a/contrib/openldap/core-fd.schema
+++ b/contrib/openldap/core-fd.schema
@@ -2,7 +2,7 @@
 ## core-fd.schema - Needed by FusionDirectory for its basic functionalities
 ##
 
-# Last OID used for attributes  : 1.3.6.1.4.1.38414.62.1.67 05/02/24 #
+# Last OID used for attributes  : 1.3.6.1.4.1.38414.62.1.68 12/03/24 #
 # Last OID used for objectClass : 1.3.6.1.4.1.38414.62.2.11 29/01/24 #
 
 ##### Attributes from gosa ######
@@ -73,6 +73,12 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.51 NAME 'fdSnapshotDataSource'
   SUBSTR caseIgnoreSubstringsMatch
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
 
+attributetype ( 1.3.6.1.4.1.38414.62.1.68 NAME 'fdSnapshotHash'
+  DESC 'FusionDirectory - hash of the current snapShot allowing diff verification with MD5'
+  EQUALITY caseIgnoreMatch
+  SUBSTR caseIgnoreSubstringsMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+
 ##### Subscriptions Attributes ######
 
 attributetype ( 1.3.6.1.4.1.38414.62.11.1 NAME 'fdSubscriptionStartDate'
@@ -504,7 +510,7 @@ objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.19 NAME 'gosaSnapshotObject'
   DESC 'GOsa - Container object for undo and snapshot data'
   SUP top STRUCTURAL
   MUST ( gosaSnapshotTimestamp $ gosaSnapshotDN $ gosaSnapshotData $ fdSnapshotDataSource )
-  MAY  ( fdSnapshotObjectType $ description ) )
+  MAY  ( fdSnapshotObjectType $ description $ fdSnapshotHash) )
 
 ### New FusionDirectory Objectclass ###
 
diff --git a/include/management/snapshot/class_SnapshotHandler.inc b/include/management/snapshot/class_SnapshotHandler.inc
index 61744b61dd46db5920a9eff8eac9ff2915ce1ae0..c95078c5a82d78595771f81549b56f5897ab1082 100644
--- a/include/management/snapshot/class_SnapshotHandler.inc
+++ b/include/management/snapshot/class_SnapshotHandler.inc
@@ -254,6 +254,7 @@ class SnapshotHandler
     $target['description']          = $description;
     $target['fdSnapshotObjectType'] = $objectType;
     $target['fdSnapshotDataSource'] = $snapshotSource;
+    $target['fdSnapshotHash'] = md5($data);
 
     /* Insert the new snapshot
        But we have to check first, if the given gosaSnapshotTimestamp
@@ -380,7 +381,7 @@ class SnapshotHandler
     $ldap->cd($new_base);
     $ldap->search(
       '(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN='.ldap_escape_f($dn).'))',
-      ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'],
+      ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType','fdSnapshotHash'],
       'one'
     );
 
@@ -416,7 +417,7 @@ class SnapshotHandler
     $ldap->cd($new_base);
     $ldap->search(
       '(objectClass=gosaSnapshotObject)',
-      ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType'],
+      ['gosaSnapshotTimestamp','gosaSnapshotDN','description','fdSnapshotObjectType', 'fdSnapshotHash'],
       'one'
     );
     while ($entry = $ldap->fetch(TRUE)) {
diff --git a/plugins/personal/generic/class_user.inc b/plugins/personal/generic/class_user.inc
index 08f4f2053bf3fe8230c6e64064b54c325b369f20..f32e0f14017a8f934794dfe539e13cce178d5d63 100644
--- a/plugins/personal/generic/class_user.inc
+++ b/plugins/personal/generic/class_user.inc
@@ -23,8 +23,8 @@ class PostalAddressAttribute extends TextAreaAttribute
   function inputValue ($ldapValue)
   {
     return str_replace(
-      ['$',  '\24','\5C'],
-      ["\n", '$',  '\\'],
+      ['$', '\24', '\5C'],
+      ["\n", '$', '\\'],
       $ldapValue
     );
   }
@@ -36,7 +36,7 @@ class PostalAddressAttribute extends TextAreaAttribute
       '$',
       str_replace(
         ['\\', '$'],
-        ['\5C','\24'],
+        ['\5C', '\24'],
         $this->getValue()
       )
     );
@@ -55,7 +55,7 @@ class user extends simplePlugin
       'plIcon'        => 'geticon.php?context=applications&icon=user-info&size=48',
       'plSmallIcon'   => 'geticon.php?context=applications&icon=user-info&size=16',
       'plSelfModify'  => TRUE,
-      'plObjectClass' => ['inetOrgPerson','organizationalPerson','person'],
+      'plObjectClass' => ['inetOrgPerson', 'organizationalPerson', 'person'],
       'plFilter'      => '(objectClass=inetOrgPerson)',
       'plObjectType'  => ['user' => [
         'name'        => _('User'),
@@ -65,10 +65,10 @@ class user extends simplePlugin
         'icon'        => 'geticon.php?context=types&icon=user&size=16',
         'ou'          => get_ou('userRDN'),
       ]],
-      'plForeignKeys'  => [
-        'manager' => ['user','dn','manager=%oldvalue%','*']
+      'plForeignKeys' => [
+        'manager' => ['user', 'dn', 'manager=%oldvalue%', '*']
       ],
-      'plSearchAttrs' => ['uid','description'],
+      'plSearchAttrs' => ['uid', 'description'],
 
       'plProvidedAcls' => array_merge(
         parent::generatePlProvidedAcls(static::getAttributesInfo()),
@@ -82,9 +82,9 @@ class user extends simplePlugin
     global $config;
     $languages = Language::getList(TRUE);
     asort($languages);
-    $languages = array_merge(['' => ''], $languages);
+    $languages      = array_merge(['' => ''], $languages);
     $attributesInfo = [
-      'perso' => [
+      'perso'        => [
         'name'  => _('Personal information'),
         'icon'  => 'geticon.php?context=types&icon=user&size=16',
         'attrs' => [
@@ -115,7 +115,7 @@ class user extends simplePlugin
           ),
         ]
       ],
-      'contact' => [
+      'contact'      => [
         'name'  => _('Organizational contact information'),
         'icon'  => 'geticon.php?context=types&icon=contact&size=16',
         'attrs' => [
@@ -161,7 +161,7 @@ class user extends simplePlugin
           ),
         ]
       ],
-      'account' => [
+      'account'      => [
         'name'  => _('Account information'),
         'icon'  => 'geticon.php?context=applications&icon=ldap&size=16',
         'attrs' => [
@@ -181,7 +181,7 @@ class user extends simplePlugin
           ),
         ]
       ],
-      'homecontact' => [
+      'homecontact'  => [
         'name'  => _('Personal contact information'),
         'icon'  => 'geticon.php?context=types&icon=contact&size=16',
         'attrs' => [
@@ -259,6 +259,7 @@ class user extends simplePlugin
 
   function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE)
   {
+    global $config;
     parent::__construct($dn, $object, $parent, $mainTab);
 
     if ($this->is_template && !$this->initially_was_account) {
@@ -274,6 +275,14 @@ class user extends simplePlugin
     $this->attributesAccess['jpegPhoto']->setPlaceholder(fread($fd, filesize($filename)));
 
     $this->was_locked = $this->attributesAccess['userPassword']->isLocked();
+
+    // Verification is snapshot is enabled and automatic.
+    // Note : string values are stored in that array.
+    if (isset($config->current['ENABLEAUTOMATICSNAPSHOTS']) && isset($config->current['ENABLESNAPSHOTS'])) {
+      if (strtolower($config->current['ENABLEAUTOMATICSNAPSHOTS']) === 'true' && strtolower($config->current['ENABLESNAPSHOTS']) === 'true') {
+        $this->generateAutomaticSnapshot();
+      }
+    }
   }
 
   function resetCopyInfos ()
@@ -285,14 +294,14 @@ class user extends simplePlugin
   private function update_cn ()
   {
     global $config;
-    $pattern  = $config->get_cfg_value('CnPattern', '%givenName% %sn%');
+    $pattern = $config->get_cfg_value('CnPattern', '%givenName% %sn%');
     $this->attributesAccess['cn']->setValue($this->applyPattern($pattern));
   }
 
   private function applyPattern ($pattern)
   {
-    $fields   = templateHandling::listFields($pattern);
-    $attrs    = [];
+    $fields = templateHandling::listFields($pattern);
+    $attrs  = [];
     foreach ($fields as $field) {
       if (in_array($field, $this->attributes)) {
         $attrs[$field] = $this->$field;
@@ -307,7 +316,7 @@ class user extends simplePlugin
           }
         }
       }
-      trigger_error('Could not find field '.$field.' in any tab!');
+      trigger_error('Could not find field ' . $field . ' in any tab!');
     }
 
     return templateHandling::parseString($pattern, $attrs);
@@ -318,13 +327,13 @@ class user extends simplePlugin
     global $config;
 
     if ($this->is_template) {
-      return 'cn='.ldap_escape_dn($this->_template_cn).',ou=templates,'.get_ou('userRDN').$this->base;
+      return 'cn=' . ldap_escape_dn($this->_template_cn) . ',ou=templates,' . get_ou('userRDN') . $this->base;
     }
 
     $this->update_cn();
     $attribute = $config->get_cfg_value('accountPrimaryAttribute', 'uid');
 
-    return $this->create_unique_dn($attribute, get_ou('userRDN').$this->base);
+    return $this->create_unique_dn($attribute, get_ou('userRDN') . $this->base);
   }
 
   public function render (): string
@@ -365,8 +374,8 @@ class user extends simplePlugin
     }
 
     if (!$this->is_template && $this->was_locked && $this->attributesAccess['userPassword']->hasChanged()) {
-      $methods  = passwordMethod::get_available_methods();
-      $pmethod  = new $methods[$this->attributesAccess['userPassword']->getMethod()]($this->dn);
+      $methods = passwordMethod::get_available_methods();
+      $pmethod = new $methods[$this->attributesAccess['userPassword']->getMethod()]($this->dn);
       $pmethod->lock_account($this->dn);
     }
 
@@ -383,24 +392,40 @@ class user extends simplePlugin
       session::set('ui', $ui);
       session::set('Last_init_lang', 'update');
     }
-    // Verification is snapshot is enabled and automatic.
-    // Note : string values are stored in that array.
-    if (isset($config->current['ENABLEAUTOMATICSNAPSHOTS']) && isset($config->current['ENABLESNAPSHOTS'])) {
-      if (strtolower($config->current['ENABLEAUTOMATICSNAPSHOTS']) === 'true' && strtolower($config->current['ENABLESNAPSHOTS']) === 'true' ) {
-        $this->generateAutomaticSnapshot();
-      }
-    }
     return parent::post_save();
   }
 
-  /*
-   * Create the snapshot object in case of automated snapshot.
+
+//  public function verifyLastSnapshotMD5 () : BOOL
+//  {
+//
+//  }
+
+  /**
+   * @return void
+   * Note : Create a snapshot of current data before save, verifying if data have changed before taking a snap.
    */
   public function generateAutomaticSnapshot ()
   {
-    $snapshotHandler = new SnapshotHandler();
-    $snapshotHandler->createSnapshot($this->dn, 'automatic snapshot', 'USER', 'FD');
-    $snapshotHandler->verifySnapshotRetention($this->dn);
+    // Get the hash of current data before modifications.
+    global $config;
+    $ldap            = $config->get_ldap_link();
+    $currentSnapHash = md5($ldap->generateLdif($this->dn, '(!(objectClass=gosaDepartment))', 'sub'));
+
+    // Verify if current snap hash already exist in the list of existing snapshots - not taking a snap if it exists.
+    $snapshotHandler   = new SnapshotHandler();
+    $existingSnapshots = $snapshotHandler->getAvailableSnapsShots($this->dn);
+    $sameHashExist     = FALSE;
+    foreach ($existingSnapshots as $snap) {
+      if (!empty($snap['fdSnapshotHash'][0]) && $snap['fdSnapshotHash'][0] === $currentSnapHash) {
+        $sameHashExist = TRUE;
+      }
+    }
+    // Create the snapshot
+    if ($sameHashExist === FALSE) {
+      $snapshotHandler->createSnapshot($this->dn, 'automatic snapshot', 'USER', 'FD');
+      $snapshotHandler->verifySnapshotRetention($this->dn);
+    }
   }
 
   function adapt_from_template (array $attrs, array $skip = [])
@@ -430,7 +455,7 @@ class user extends simplePlugin
 
     $ldap = $config->get_ldap_link();
     $ldap->cat($userdn, ['pwdPolicySubentry', 'pwdHistory', 'pwdChangedTime', 'userPassword']);
-    $attrs = $ldap->fetch(TRUE);
+    $attrs     = $ldap->fetch(TRUE);
     $ppolicydn = '';
     if (isset($attrs['pwdPolicySubentry'][0])) {
       $ppolicydn = $attrs['pwdPolicySubentry'][0];
@@ -482,7 +507,7 @@ class user extends simplePlugin
       if (isset($policy['pwdMinAge'][0]) && isset($attrs['pwdChangedTime'][0])) {
         $date = LdapGeneralizedTime::fromString($attrs['pwdChangedTime'][0]);
         $date->setTimezone(timezone::utc());
-        $now  = new DateTime('now', timezone::utc());
+        $now = new DateTime('now', timezone::utc());
         if ($now->getTimeStamp() < $date->getTimeStamp() + $policy['pwdMinAge'][0]) {
           return sprintf(_('You must wait %d seconds before changing your password again'), $policy['pwdMinAge'][0] - ($now->getTimeStamp() - $date->getTimeStamp()));
         }
@@ -494,7 +519,7 @@ class user extends simplePlugin
         unset($attrs['pwdHistory']['count']);
         foreach ($attrs['pwdHistory'] as $pwdHistory) {
           $pwdHistory = explode('#', $pwdHistory, 4);
-          $method = passwordMethod::get_method($pwdHistory[3], $user);
+          $method     = passwordMethod::get_method($pwdHistory[3], $user);
           if (($method !== NULL) && $method->checkPassword($new_password, $pwdHistory[3])) {
             return _('Password is in history of old passwords');
           }