Index: django/template/__init__.py
===================================================================
--- django/template/__init__.py	(revision 5457)
+++ django/template/__init__.py	(working copy)
@@ -190,6 +190,10 @@
     def split_contents(self):
         return list(smart_split(self.contents))
 
+    def __cmp__(self, other):
+        return cmp((self.token_type, self.contents),
+                   (other.token_type, other.contents))
+
 class Lexer(object):
     def __init__(self, template_string, origin):
         self.template_string = template_string
@@ -251,47 +255,56 @@
         self.tokens = tokens
         self.tags = {}
         self.filters = {}
+        self._token_trackers = {}
         for lib in builtins:
             self.add_library(lib)
 
-    def parse(self, parse_until=None):
-        if parse_until is None: parse_until = []
-        nodelist = self.create_nodelist()
-        while self.tokens:
-            token = self.next_token()
-            if token.token_type == TOKEN_TEXT:
-                self.extend_nodelist(nodelist, TextNode(token.contents), token)
-            elif token.token_type == TOKEN_VAR:
-                if not token.contents:
-                    self.empty_variable(token)
-                filter_expression = self.compile_filter(token.contents)
-                var_node = self.create_variable_node(filter_expression)
-                self.extend_nodelist(nodelist, var_node,token)
-            elif token.token_type == TOKEN_BLOCK:
-                if token.contents in parse_until:
-                    # put token back on token list so calling code knows why it terminated
-                    self.prepend_token(token)
-                    return nodelist
-                try:
-                    command = token.contents.split()[0]
-                except IndexError:
-                    self.empty_block_tag(token)
-                # execute callback function for this tag and append resulting node
-                self.enter_command(command, token)
-                try:
-                    compile_func = self.tags[command]
-                except KeyError:
-                    self.invalid_block_tag(token, command)
-                try:
-                    compiled_result = compile_func(self, token)
-                except TemplateSyntaxError, e:
-                    if not self.compile_function_error(token, e):
-                        raise
-                self.extend_nodelist(nodelist, compiled_result, token)
-                self.exit_command()
-        if parse_until:
-            self.unclosed_block_tag(parse_until)
-        return nodelist
+    def parse(self, parse_until=None, token_tracker=None):
+        if token_tracker is not None:
+            self._token_trackers[id(token_tracker)] = token_tracker
+        try:
+            if parse_until is None: parse_until = []
+            nodelist = self.create_nodelist()
+            while self.tokens:
+                token = self.next_token()
+                for x in self._token_trackers.itervalues():
+                    x.append(token)
+                if token.token_type == TOKEN_TEXT:
+                    self.extend_nodelist(nodelist, TextNode(token.contents), token)
+                elif token.token_type == TOKEN_VAR:
+                    if not token.contents:
+                        self.empty_variable(token)
+                    filter_expression = self.compile_filter(token.contents)
+                    var_node = self.create_variable_node(filter_expression)
+                    self.extend_nodelist(nodelist, var_node,token)
+                elif token.token_type == TOKEN_BLOCK:
+                    if token.contents in parse_until:
+                        # put token back on token list so calling code knows why it terminated
+                        self.prepend_token(token)
+                        return nodelist
+                    try:
+                        command = token.contents.split()[0]
+                    except IndexError:
+                        self.empty_block_tag(token)
+                    # execute callback function for this tag and append resulting node
+                    self.enter_command(command, token)
+                    try:
+                        compile_func = self.tags[command]
+                    except KeyError:
+                        self.invalid_block_tag(token, command)
+                    try:
+                        compiled_result = compile_func(self, token)
+                    except TemplateSyntaxError, e:
+                        if not self.compile_function_error(token, e):
+                            raise
+                    self.extend_nodelist(nodelist, compiled_result, token)
+                    self.exit_command()
+            if parse_until:
+                self.unclosed_block_tag(parse_until)
+            return nodelist
+        finally:
+            if token_tracker is not None:
+                del self._token_trackers[id(token_tracker)]
 
     def skip_past(self, endtag):
         while self.tokens:
@@ -934,3 +947,4 @@
 
 add_to_builtins('django.template.defaulttags')
 add_to_builtins('django.template.defaultfilters')
+add_to_builtins('django.template.loader_tags')
Index: django/template/loader_tags.py
===================================================================
--- django/template/loader_tags.py	(revision 5457)
+++ django/template/loader_tags.py	(working copy)
@@ -121,18 +121,32 @@
     if len(bits) != 2:
         raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
     block_name = bits[1]
-    # Keep track of the names of BlockNodes found in this template, so we can
-    # check for duplication.
-    try:
-        if block_name in parser.__loaded_blocks:
-            raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
-        parser.__loaded_blocks.append(block_name)
-    except AttributeError: # parser.__loaded_blocks isn't a list yet
-        parser.__loaded_blocks = [block_name]
-    nodelist = parser.parse(('endblock', 'endblock %s' % block_name))
+    # If multiple blocks with the same name are defined in the same template,
+    # all of them must have the same content. To accomplish this, we save
+    # in the parser all blocks which were already encountered, together with
+    # the token list from which each one was constructed. When a new block
+    # with a name that already appeared is encountered, we check if it has
+    # the same token list, and if so the previously created block is returned.
+    tokenlist = []
+    nodelist = parser.parse(('endblock', 'endblock %s' % block_name),
+                            token_tracker=tokenlist)
     parser.delete_first_token()
-    return BlockNode(block_name, nodelist)
 
+    try:
+        loaded_blocks = parser.__loaded_blocks
+    except AttributeError:
+        loaded_blocks = parser.__loaded_blocks = {}
+
+    try:
+        prev_block, prev_tokenlist = loaded_blocks[block_name]
+    except KeyError:
+        loaded_blocks[block_name] = BlockNode(block_name, nodelist), tokenlist
+    else:
+        if prev_tokenlist != tokenlist:
+            raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once and doesn't have exactly the same contents in all appearances" % (bits[0], block_name)
+
+    return loaded_blocks[block_name][0]
+
 def do_extends(parser, token):
     """
     Signal that this template extends a parent template.
Index: django/template/loader.py
===================================================================
--- django/template/loader.py	(revision 5457)
+++ django/template/loader.py	(working copy)
@@ -115,4 +115,4 @@
     # If we get here, none of the templates could be loaded
     raise TemplateDoesNotExist, ', '.join(template_name_list)
 
-add_to_builtins('django.template.loader_tags')
+#add_to_builtins('django.template.loader_tags')
