diff -ur vte-0.34.7.orig/src/debug.c vte-0.34.7/src/debug.c
--- vte-0.34.7.orig/src/debug.c	2013-06-28 16:44:33.000000000 +0200
+++ vte-0.34.7/src/debug.c	2013-09-10 12:50:14.137206393 +0200
@@ -50,7 +50,8 @@
     { "ally",         VTE_DEBUG_ALLY         },
     { "pangocairo",   VTE_DEBUG_PANGOCAIRO   },
     { "widget-size",  VTE_DEBUG_WIDGET_SIZE  },
-    { "bg",           VTE_DEBUG_BG           }
+    { "bg",           VTE_DEBUG_BG           },
+    { "resize",       VTE_DEBUG_RESIZE       }
   };
 
   _vte_debug_flags = g_parse_debug_string (g_getenv("VTE_DEBUG"),
diff -ur vte-0.34.7.orig/src/debug.h vte-0.34.7/src/debug.h
--- vte-0.34.7.orig/src/debug.h	2013-06-28 16:44:33.000000000 +0200
+++ vte-0.34.7/src/debug.h	2013-09-10 12:49:47.353205282 +0200
@@ -50,7 +50,8 @@
 	VTE_DEBUG_ADJ		= 1 << 19,
 	VTE_DEBUG_PANGOCAIRO    = 1 << 20,
 	VTE_DEBUG_WIDGET_SIZE   = 1 << 21,
-        VTE_DEBUG_BG            = 1 << 22
+	VTE_DEBUG_BG            = 1 << 22,
+	VTE_DEBUG_RESIZE        = 1 << 23
 } VteDebugFlags;
 
 void _vte_debug_init(void);
diff -ur vte-0.34.7.orig/src/vte.c vte-0.34.7/src/vte.c
--- vte-0.34.7.orig/src/vte.c	2013-06-28 16:44:33.000000000 +0200
+++ vte-0.34.7/src/vte.c	2013-09-10 14:40:39.929481082 +0200
@@ -2082,6 +2082,7 @@
 			delta = terminal->pvt->screen->scroll_delta;
 			terminal->pvt->screen->scroll_delta = v;
 			gtk_adjustment_set_value(terminal->adjustment, delta);
+			terminal->pvt->screen->scroll_delta = delta;  // http://bugzilla.gnome.org/707592
 		}
 	}
 }
@@ -4363,7 +4364,7 @@
 			 _vte_ring_delta(screen->row_data));
 		/* The cursor shouldn't be above or below the addressable
 		 * part of the display buffer. */
-		g_assert(screen->cursor_current.row >= screen->insert_delta);
+//		g_assert(screen->cursor_current.row >= screen->insert_delta);  // http://bugzilla.gnome.org/707572
 #endif
 
 next_match:
@@ -8009,6 +8010,116 @@
 	}
 }
 
+
+/* Reflow the ring to match the new number of columns.
+   For each marker (NULL terinated list), find the cell at that position and update
+   to reflect the cell's position in the new ring, or put (-1,-1) if not found.
+
+   TODO: move to ring.c and reimplement using ring's internals, so that:
+   - becomes much faster
+   - doesn't need twice the memory during its operation
+ */
+static void
+ring_rewrap(VteRing *old_ring, glong columns, VteVisualPosition **old_markers) {
+	VteRing new_ring;
+	gulong old_row_index, old_col_index, new_col = 0;
+	int i;
+	int num_markers = 0;
+	gboolean old_row_contains_marker;  // small speed optimization
+
+	if (_vte_ring_length(old_ring) == 0)
+		return;
+	_vte_ring_init(&new_ring, old_ring->max);
+
+	_vte_debug_print(VTE_DEBUG_RESIZE,
+			"Old ring  start=%ld  length=%ld  next=%ld\n",
+			_vte_ring_delta(old_ring), _vte_ring_length(old_ring), _vte_ring_next(old_ring));
+
+	while (old_markers[num_markers] != NULL) {
+		num_markers++;
+	}
+	VteVisualPosition *new_markers = g_malloc(num_markers * sizeof(VteVisualPosition));
+	for (i = 0; i < num_markers; i++) {
+		new_markers[i].col = new_markers[i].row = -1;
+		_vte_debug_print(VTE_DEBUG_RESIZE,
+				"Old marker #%d is at  row=%ld  col=%ld\n",
+				i, old_markers[i]->row, old_markers[i]->col);
+	}
+
+	VteRowData *new_row = NULL;
+	for (old_row_index = _vte_ring_delta(old_ring); old_row_index < _vte_ring_next(old_ring); old_row_index++) {
+		old_row_contains_marker = FALSE;
+		for (i = 0; i < num_markers; i++) {
+			if (old_row_index == old_markers[i]->row)
+				old_row_contains_marker = TRUE;
+		}
+		VteRowData *old_row = _vte_ring_index(old_ring, old_row_index);
+		if (new_row == NULL) {
+			new_row = _vte_ring_append(&new_ring);
+			new_col = 0;
+		}
+		for (old_col_index = 0; old_col_index < old_row->len; old_col_index++) {
+			if (G_UNLIKELY (old_row_contains_marker)) {
+				for (i = 0; i < num_markers; i++) {
+					/* if cursor is above a character, keep above the same char */
+					if (old_row_index == old_markers[i]->row &&
+					    old_col_index == old_markers[i]->col) {
+						new_markers[i].row = _vte_ring_next(&new_ring) - 1;
+						new_markers[i].col = new_col;
+						_vte_debug_print(VTE_DEBUG_RESIZE,
+								"Marker #%d was above a char, new coords are  row=%ld  col=%ld\n",
+								i, new_markers[i].row, new_markers[i].col);
+					}
+				}
+			}
+			VteCell *cell = _vte_row_data_get(old_row, old_col_index);
+			/* If char's whole width won't fit, wrap it now */
+			if (G_UNLIKELY (!cell->attr.fragment && new_col + cell->attr.columns > columns)) {
+				new_row->attr.soft_wrapped = 1;
+				new_row = _vte_ring_append(&new_ring);
+				new_col = 0;
+			}
+			_vte_row_data_append(new_row, cell);
+			new_col++;
+		}
+		if (G_UNLIKELY (old_row_contains_marker)) {
+			for (i = 0; i < num_markers; i++) {
+				/* if cursor is at EOL or beyond, put exactly at EOL (but not beyond) */
+				if (old_row_index == old_markers[i]->row &&
+				    old_col_index <= old_markers[i]->col) {
+					new_markers[i].row = _vte_ring_next(&new_ring) - 1;
+					new_markers[i].col = new_col;
+					_vte_debug_print(VTE_DEBUG_RESIZE,
+							"Marker #%d was at or beyond EOL, moved to EOL, new coords are  row=%ld  col=%ld\n",
+							i, new_markers[i].row, new_markers[i].col);
+				}
+			}
+		}
+		if (!old_row->attr.soft_wrapped) {
+			new_row = NULL;
+		}
+	}
+
+	/* Replace old ring and markers with the new ones */
+	_vte_debug_print(VTE_DEBUG_RESIZE,
+			"New ring  start=%ld  length=%ld  next=%ld\n",
+			_vte_ring_delta(&new_ring), _vte_ring_length(&new_ring), _vte_ring_next(&new_ring));
+	_vte_ring_fini(old_ring);
+	memcpy(old_ring, &new_ring, sizeof(VteRing));
+
+	for (i = 0; i < num_markers; i++) {
+		if (new_markers[i].row == -1) {
+			_vte_debug_print(VTE_DEBUG_RESIZE,
+					"Marker #%d not found, new coords are (-1, -1)\n",
+					i);
+		}
+		old_markers[i]->row = new_markers[i].row;
+		old_markers[i]->col = new_markers[i].col;
+	}
+	g_free(new_markers);
+}
+
+
 /**
  * vte_terminal_set_size:
  * @terminal: a #VteTerminal
@@ -8025,7 +8136,7 @@
 
 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
 
-	_vte_debug_print(VTE_DEBUG_MISC,
+	_vte_debug_print(VTE_DEBUG_RESIZE,
 			"Setting PTY size to %ldx%ld.\n",
 			columns, rows);
 
@@ -8047,16 +8158,83 @@
 		terminal->row_count = rows;
 		terminal->column_count = columns;
 	}
-	if (old_rows != terminal->row_count || old_columns != terminal->column_count) {
-		VteScreen *screen = terminal->pvt->screen;
-		glong visible_rows = MIN (old_rows, _vte_ring_length (screen->row_data));
-		if (terminal->row_count < visible_rows) {
-			glong delta = visible_rows - terminal->row_count;
-			screen->insert_delta += delta;
-			vte_terminal_queue_adjustment_value_changed (
-					terminal,
-					screen->scroll_delta + delta);
+
+	if ((old_rows != terminal->row_count || old_columns != terminal->column_count) &&
+	    _vte_ring_length(terminal->pvt->normal_screen.row_data) >= 1) {
+		glong scroll_delta;
+		VteScreen *normal_screen = &terminal->pvt->normal_screen;  // Alternate screen has no scrollback buffer, rewrapping there doesn't make much sense.
+		VteRing *ring = normal_screen->row_data;
+
+		_vte_debug_print(VTE_DEBUG_RESIZE,
+				"Old  insert_delta=%ld  scroll_delta=%ld\n"
+				"Old cursor  row=%ld  (visual line %d)  col=%ld\n",
+				normal_screen->insert_delta, normal_screen->scroll_delta,
+				normal_screen->cursor_current.row, normal_screen->cursor_current.row - normal_screen->scroll_delta + 1, normal_screen->cursor_current.col);
+
+		VteVisualPosition bottom_left_marker = { row: normal_screen->scroll_delta + old_rows - 1, col: 0 };
+		VteVisualPosition *markers[4] = { &normal_screen->cursor_current, &normal_screen->cursor_saved, &bottom_left_marker, NULL };
+
+		gboolean was_scrolled_to_top = (normal_screen->scroll_delta == _vte_ring_delta(ring));
+		gboolean was_scrolled_to_bottom = (normal_screen->scroll_delta == normal_screen->insert_delta);
+
+		if (old_columns != terminal->column_count) {
+			ring_rewrap(ring, terminal->column_count, &markers);
+			if (normal_screen->cursor_saved.row == -1) {
+				normal_screen->cursor_saved.row = 0;
+				normal_screen->cursor_saved.col = 0;
+			}
+#ifdef VTE_DEBUG
+			g_assert(normal_screen->cursor_current.row != -1 && normal_screen->cursor_current.col != -1);
+#endif
 		}
+
+		// TODO: what to do if normal_screen->scrolling_restricted is true?
+
+		if (_vte_ring_length(ring) <= terminal->row_count) {
+			/* Everything fits without scrollbars. Align at top. */
+			normal_screen->insert_delta = _vte_ring_delta(ring);
+			scroll_delta = normal_screen->insert_delta;
+			_vte_debug_print(VTE_DEBUG_RESIZE,
+					"Everything fits without scrollbars\n");
+		} else {
+			normal_screen->insert_delta = _vte_ring_next(ring) - terminal->row_count;
+			if (was_scrolled_to_bottom) {
+				/* Was scrolled to bottom, keep this way. */
+				scroll_delta = normal_screen->insert_delta;
+				_vte_debug_print(VTE_DEBUG_RESIZE,
+						"Scroll to bottom\n");
+			} else if (was_scrolled_to_top) {
+				/* Was scrolled to top, keep this way. Not sure if this special case is worth it. */
+				scroll_delta = _vte_ring_delta(ring);
+				_vte_debug_print(VTE_DEBUG_RESIZE,
+						"Scroll to top\n");
+			} else {
+				/* Try to scroll so that bottom left corner stays.
+				   (Note: aligning at bottom right corner instead would cause
+				   crazy behavior when bottom row starts wrapping.) */
+#ifdef VTE_DEBUG
+				g_assert(bottom_left_marker.row != -1);
+#endif
+				scroll_delta = bottom_left_marker.row + 1 - terminal->row_count;
+				_vte_debug_print(VTE_DEBUG_RESIZE,
+						"Scroll so bottom left corner stays\n");
+			}
+		}
+
+		_vte_debug_print(VTE_DEBUG_RESIZE,
+				"New  insert_delta=%ld  scroll_delta=%ld\n"
+				"New cursor  row=%ld  (visual line %d)  col=%ld\n",
+				normal_screen->insert_delta, /* don't prefix with normal_screen-> */ scroll_delta,
+				normal_screen->cursor_current.row, normal_screen->cursor_current.row - scroll_delta + 1, normal_screen->cursor_current.col);
+
+		if (terminal->pvt->screen == normal_screen) {
+			vte_terminal_queue_adjustment_value_changed(
+				terminal,
+				scroll_delta);
+		} else {
+			normal_screen->scroll_delta = scroll_delta;
+		}
+
 		gtk_widget_queue_resize_no_redraw (&terminal->widget);
 		/* Our visible text changed. */
 		vte_terminal_emit_text_modified(terminal);
