diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 04e7a37..b84ed4e 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -65,6 +65,9 @@ class CycleNode(Node):
         self.variable_name = variable_name
         self.silent = silent
         self.escape = escape        # only while the "future" version exists
+        
+    def reset(self, context):
+        context.render_context[self] = itertools_cycle(self.cyclevars)
 
     def render(self, context):
         if self not in context.render_context:
@@ -80,6 +83,13 @@ class CycleNode(Node):
             value = mark_safe(value)
         return render_value_in_context(value, context)
 
+class ResetCycleNode(Node):
+    def __init__(self, node):
+        self.node = node
+    def render(self, context):
+        self.node.reset(context)
+        return ''
+
 class DebugNode(Node):
     def render(self, context):
         from pprint import pformat
@@ -578,7 +588,7 @@ def cycle(parser, token, escape=False):
     # Ugly hack warning: This stuffs the named template dict into parser so
     # that names are only unique within each template (as opposed to using
     # a global variable, which would make cycle names have to be unique across
-    # *all* templates.
+    # *all* templates. Also keeps last unnamed node in the parser.
 
     args = token.split_contents()
 
@@ -623,7 +633,36 @@ def cycle(parser, token, escape=False):
     else:
         values = [parser.compile_filter(arg) for arg in args[1:]]
         node = CycleNode(values, escape=escape)
+        parser._lastUnnamedCycleNode = node
     return node
+    
+@register.tag
+def resetcycle(parser, token):
+    """
+    Resets a cycle tag.
+
+    If an argument is given, resets the last rendered cycle tag whose name
+    matches the argument, else resets last rendered unnamed cycle tag.
+
+    """
+    args = token.split_contents()
+
+    if len(args) > 2:
+        raise TemplateSyntaxError("%r tag accepts at most one argument."
+                                  % args[0])
+    if len(args) == 2:
+        name = args[1]
+        try:
+            return ResetCycleNode(parser._namedCycleNodes[name])
+        except AttributeError:
+            raise TemplateSyntaxError("No named cycles in template. "
+                                      "%r is not defined" % name)
+        except KeyError:
+            raise TemplateSyntaxError("Named cycle %r does not exist" % name)
+    try:
+        return ResetCycleNode(parser._lastUnnamedCycleNode)
+    except AttributeError:
+        raise TemplateSyntaxError("No unnamed cycles in template.")
 
 @register.tag
 def csrf_token(parser, token):
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 287fd4f..86f5d0f 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -149,7 +149,10 @@ string literals, while values without quotes are treated as template variables.
 
 Note that currently the variables included in the cycle will not be escaped.
 Any HTML or Javascript code contained in the printed variable will be rendered
-as-is, which could potentially lead to security issues.
+as-is, which could potentially lead to security issues.86
+
+You can use the `resetcycle`_ tag to reset a ``{% cycle %}`` tag to restart
+from its first value when it's next encountered.
 
 For backwards compatibility, the ``{% cycle %}`` tag supports the much inferior
 old syntax from previous Django versions. You shouldn't use this in any new
@@ -893,6 +896,44 @@ attribute, allowing  you to group on the display string rather than the
 ``{{ country.grouper }}`` will now display the value fields from the
 ``choices`` set rather than the keys.
 
+.. templatetag:: resetcycle
+
+resetcycle
+^^^^^^^^^^
+
+Resets a previous `cycle`_ so that it restarts from its first item at its next
+invocation.  Without arguments, ``{% resetcycle %}`` will reset the last
+unnamed ``{% cycle %}`` defined in the template.
+
+Example usage::
+
+    {% for coach in coach_list %}
+        <h1>{{ coach.name }}</h1>
+        {% for athlete in coach.athlete_set.all %}
+            <p class="{% cycle 'odd' 'even' %}">{{ athlete.name }}</p>
+        {% endfor %}
+        {% resetcycle %}
+    {% endfor %}
+
+The athlete list for every coach starts with ``class="odd"``.  Without the
+``{% resetcycle %}`` tag, the first athlete of a coach might be rendered with
+``class="even"`` if the last athlete of the previous coach had ``class="odd"``.
+
+You can also reset named cycle tags::
+
+    {% for item in list %}
+        <p class="{% cycle 'odd' 'evn' as stripe %} {% cycle 'majr' 'minr' 'minr' 'minr' 'minr' as tick %}">
+	    {{ item.data }}
+	</p>
+        {% ifchanged item.category %}
+            <h1>{{ item.category }}</h1>
+            {% if not forloop.first %}{% resetcycle tick %}{% endif %}
+        {% endifchanged %}
+    {% endfor %}
+
+In this example we have both the alternating odd/even rows and a "major" row
+every fifth row.  Only the five-row cycle is reset when a category changes.
+
 .. templatetag:: spaceless
 
 spaceless
diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py
index 2aeaee9..c2b3a76 100644
--- a/tests/template_tests/tests.py
+++ b/tests/template_tests/tests.py
@@ -818,6 +818,20 @@ class Templates(TestCase):
             'cycle26': ('{% load cycle from future %}{% cycle a b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '&lt;&gt;'),
             'cycle27': ('{% load cycle from future %}{% autoescape off %}{% cycle a b as ab %}{% cycle ab %}{% endautoescape %}', {'a': '<', 'b': '>'}, '<>'),
             'cycle28': ('{% load cycle from future %}{% cycle a|safe b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '<&gt;'),
+            
+            ### RESETCYCLE TAG ########################################################
+            'resetcycle01': ("{% resetcycle %}", {}, template.TemplateSyntaxError),
+            'resetcycle02': ("{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
+            'resetcycle03': ("{% cycle 'a' 'b' 'c' as abc %}{% resetcycle %}", {}, template.TemplateSyntaxError),
+            'resetcycle04': ("{% cycle 'a' 'b' 'c' %}{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
+            'resetcycle05': ("{% cycle 'a' 'b' 'c' as abc %}{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
+            'resetcycle06': ("{% for i in test %}{% cycle 'a' 'b' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'aaaaa'),
+            'resetcycle07': ("{% cycle 'a' 'b' 'c' as abc %}{% for i in test %}{% cycle abc %}{% cycle '-' '+' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'ab-c-a-b-c-'),
+            'resetcycle08': ("{% cycle 'a' 'b' 'c' as abc %}{% for i in test %}{% resetcycle abc %}{% cycle abc %}{% cycle '-' '+' %}{% endfor %}", {'test': range(5)}, 'aa-a+a-a+a-'),
+            'resetcycle09': ("{% for i in outer %}{% for j in inner %}{% cycle 'a' 'b' %}{% endfor %}{% resetcycle %}{% endfor %}", {'outer': range(2), 'inner': range(3)}, 'abaaba'),
+            'resetcycle10': ("{% for i in outer %}{% cycle 'a' 'b' %}{% for j in inner %}{% cycle 'X' 'Y' %}{% endfor %}{% resetcycle %}{% endfor %}", {'outer': range(2), 'inner': range(3)}, 'aXYXbXYX'),
+            'resetcycle11': ("{% for i in test %}{% cycle 'X' 'Y' 'Z' as XYZ %}{% cycle 'a' 'b' 'c' as abc %}{% ifequal i 1 %}{% resetcycle abc %}{% endifequal %}{% endfor %}", {'test': range(5)}, 'XaYbZaXbYc'),
+            'resetcycle12': ("{% for i in test %}{% cycle 'X' 'Y' 'Z' as XYZ %}{% cycle 'a' 'b' 'c' as abc %}{% ifequal i 1 %}{% resetcycle XYZ %}{% endifequal %}{% endfor %}", {'test': range(5)}, 'XaYbXcYaZb'),
 
             ### EXCEPTIONS ############################################################
 
