#10869: do not visit root node twice in ast.increment_lineno().
diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index a9821e1..9d19771 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -173,9 +173,9 @@
 
 .. function:: walk(node)
 
-   Recursively yield all child nodes of *node*, in no specified order.  This is
-   useful if you only want to modify nodes in place and don't care about the
-   context.
+   Recursively yield all descendant nodes in the tree starting at *node*
+   (including *node* itself), in no specified order.  This is useful if you only
+   want to modify nodes in place and don't care about the context.
 
 
 .. class:: NodeVisitor()
diff --git a/Lib/ast.py b/Lib/ast.py
index 4b2063b..ba880c9 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -159,8 +159,6 @@
     Increment the line number of each node in the tree starting at *node* by *n*.
     This is useful to "move code" to a different location in a file.
     """
-    if 'lineno' in node._attributes:
-        node.lineno = getattr(node, 'lineno', 0) + n
     for child in walk(node):
         if 'lineno' in child._attributes:
             child.lineno = getattr(child, 'lineno', 0) + n
@@ -211,9 +209,9 @@
 
 def walk(node):
     """
-    Recursively yield all child nodes of *node*, in no specified order.  This is
-    useful if you only want to modify nodes in place and don't care about the
-    context.
+    Recursively yield all descendant nodes in the tree starting at *node*
+    (including *node* itself), in no specified order.  This is useful if you
+    only want to modify nodes in place and don't care about the context.
     """
     from collections import deque
     todo = deque([node])
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 34456bc..499facd 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -264,6 +264,13 @@
             'op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, '
             'col_offset=0))'
         )
+        # issue10869: do not increment lineno of root twice
+        self.assertEqual(ast.increment_lineno(src.body, n=3), src.body)
+        self.assertEqual(ast.dump(src, include_attributes=True),
+            'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), '
+            'op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, '
+            'col_offset=0))'
+        )
 
     def test_iter_fields(self):
         node = ast.parse('foo()', mode='eval')
diff --git a/Misc/ACKS b/Misc/ACKS
index 6929e36..93e948d 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -905,6 +905,7 @@
 Rickard Westman
 Jeff Wheeler
 Christopher White
+David White
 Mats Wichmann
 Truida Wiedijk
 Felix Wiemann
diff --git a/Misc/NEWS b/Misc/NEWS
index 2f8b6db..2b2c283 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,9 @@
 Library
 -------
 
+- Issue #10869: Fixed bug where ast.increment_lineno modified the root
+  node twice.
+
 - Issue #5871: email.header.Header.encode now raises an error if any
   continuation line in the formatted value has no leading white space
   and looks like a header.  Since Generator uses Header to format all