Index: db/models/signals.py
===================================================================
--- db/models/signals.py	(revision 6087)
+++ db/models/signals.py	(working copy)
@@ -10,3 +10,9 @@
 post_delete = object()
 
 post_syncdb = object()
+
+m2m_add_items = object()
+
+m2m_remove_items = object()
+
+m2m_clear_items = object()
Index: db/models/fields/related.py
===================================================================
--- db/models/fields/related.py	(revision 6087)
+++ db/models/fields/related.py	(working copy)
@@ -2,6 +2,8 @@
 from django.db.models import signals, get_model
 from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class
 from django.db.models.related import RelatedObject
+from django.db.models import signals
+from django.dispatch import dispatcher
 from django.utils.text import capfirst
 from django.utils.translation import ugettext_lazy, string_concat, ungettext, ugettext as _
 from django.utils.functional import curry
@@ -339,10 +341,16 @@
                 existing_ids = set([row[0] for row in cursor.fetchall()])
 
                 # Add the ones that aren't there already
-                for obj_id in (new_ids - existing_ids):
+                new_ids = new_ids - existing_ids
+                for obj_id in new_ids:
                     cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
                         (self.join_table, source_col_name, target_col_name),
                         [self._pk_val, obj_id])
+                        
+                added_objs = [obj for obj in objs if (isinstance(obj, self.model) and obj._get_pk_val() in new_ids) or obj in new_ids]
+                dispatcher.send(signal=signals.m2m_add_items, sender=self.model, added=added_objs,
+                    source_col_name=source_col_name, target_col_name=target_col_name, instance=self.instance)
+                        
                 transaction.commit_unless_managed()
 
         def _remove_items(self, source_col_name, target_col_name, *objs):
@@ -365,6 +373,8 @@
                     (self.join_table, source_col_name,
                     target_col_name, ",".join(['%s'] * len(old_ids))),
                     [self._pk_val] + list(old_ids))
+                dispatcher.send(signal=signals.m2m_remove_items, sender=self.model, added=objs,
+                    source_col_name=source_col_name, target_col_name=target_col_name, instance=self.instance)
                 transaction.commit_unless_managed()
 
         def _clear_items(self, source_col_name):
@@ -373,6 +383,8 @@
             cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
                 (self.join_table, source_col_name),
                 [self._pk_val])
+            dispatcher.send(signal=signals.m2m_clear_items, sender=self.model,
+                source_col_name=source_col_name, instance=self.instance)
             transaction.commit_unless_managed()
 
     return ManyRelatedManager
