diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index c08ecc4..ddf1783 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -39,15 +39,27 @@ class CommentNode(Node):
 
 class CycleNode(Node):
     def __init__(self, cyclevars, variable_name=None):
-        self.cycle_iter = itertools_cycle(cyclevars)
+        self.cyclevars = cyclevars
+        self.reset()
         self.variable_name = variable_name
 
+    def reset(self):
+        self.cycle_iter = itertools_cycle(self.cyclevars)
+
     def render(self, context):
         value = self.cycle_iter.next().resolve(context)
         if self.variable_name:
             context[self.variable_name] = value
         return value
 
+class ResetCycleNode(Node):
+    def __init__(self, node):
+        self.node = node
+
+    def render(self, context):
+        self.node.reset()
+        return ''
+
 class DebugNode(Node):
     def render(self, context):
         from pprint import pformat
@@ -481,7 +493,7 @@ def cycle(parser, token):
     # 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()
 
@@ -512,9 +524,37 @@ def cycle(parser, token):
     else:
         values = [parser.compile_filter(arg) for arg in args[1:]]
         node = CycleNode(values)
+        parser._lastUnnamedCycleNode = node
     return node
 cycle = register.tag(cycle)
 
+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.")
+resetcycle = register.tag(resetcycle)
+
 def debug(parser, token):
     """
     Outputs a whole load of debugging information, including the current
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 17e6e8b..0193c8d 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -101,6 +101,9 @@ You can use any number of values in a ``{% cycle %}`` tag, separated by spaces.
 Values enclosed in single (``'``) or double quotes (``"``) are treated as
 string literals, while values without quotes are treated as template variables.
 
+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
 projects, but for the sake of the people who are still using it, here's what it
@@ -622,6 +625,44 @@ filter, if your data is in a list of dictionaries::
 
     {% regroup people|dictsort:"gender" by gender as gender_list %}
 
+.. templatetag:: resetcycle
+
+resetcycle
+~~~~~~~~~~
+
+Resets a previous `cycle`_ so that it restarts from its first item when it's
+next encountered.  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/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index f0eee52..a4f1a26 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -434,6 +434,15 @@ class Templates(unittest.TestCase):
             'cycle15': ("{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}", {'test': range(5), 'aye': 'a', 'bee': 'b'}, 'a0,b1,a2,b3,a4,'),
             'cycle16': ("{% cycle one|lower two as foo %}{% cycle foo %}", {'one': 'A','two': '2'}, 'a2'),
 
+            ### RESETCYCLE TAG ########################################################
+            'resetcycle01': ("{% for i in test %}{% cycle a,b %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'aaaaa'),
+            'resetcycle02': ("{% cycle a,b,c as abc %}{% for i in test %}{% cycle abc %}{% cycle '-' '+' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'ab-c-a-b-c-'),
+            'resetcycle03': ("{% cycle a,b,c as abc %}{% for i in test %}{% resetcycle abc %}{% cycle abc %}{% cycle '-' '+' %}{% endfor %}", {'test': range(5)}, 'aa-a+a-a+a-'),
+            'resetcycle04': ("{% for i in outer %}{% for j in inner %}{% cycle a,b %}{% endfor %}{% resetcycle %}{% endfor %}", {'outer': range(2), 'inner': range(3)}, 'abaaba'),
+            'resetcycle05': ("{% for i in outer %}{% cycle a,b %}{% for j in inner %}{% cycle X,Y %}{% endfor %}{% resetcycle %}{% endfor %}", {'outer': range(2), 'inner': range(3)}, 'aXYXbXYX'),
+            'resetcycle06': ("{% 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'),
+            'resetcycle07': ("{% 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 ############################################################
 
             # Raise exception for invalid template name
