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/ring.c vte-0.34.7/src/ring.c
--- vte-0.34.7.orig/src/ring.c	2013-06-28 16:44:33.000000000 +0200
+++ vte-0.34.7/src/ring.c	2013-09-10 21:44:46.799380161 +0200
@@ -587,6 +587,129 @@
 }
 
 /**
+ * _vte_ring_rewrap:
+ * @ring: a #VteRing
+ * @columns: new number of columns
+ * @markers: NULL-terminated array of #VteVisualPosition
+ *
+ * Reflow the @ring to match the new number of @columns.
+ * For all @markers, find the cell at that position and update them to
+ * reflect the cell's position in the new ring, or put (-1,-1) if not found.
+ */
+/*
+ * TODO: reimplement using VteRing's internals, so that:
+ * - becomes much faster
+ * - doesn't need twice the memory during its operation
+ */
+void
+_vte_ring_rewrap (VteRing *ring,
+		  glong columns,
+		  VteVisualPosition **markers)
+{
+	VteRing *old_ring = ring;  // "old_" prefix for readability only
+	VteVisualPosition **old_markers = markers;  // "old_" prefix for readability only
+	VteRing new_ring;
+	glong old_row_index, old_col_index, new_col = 0;
+	int i;
+	int num_markers = 0;
+	VteVisualPosition *new_markers;
+	gboolean old_row_contains_marker;  // small speed optimization
+	const VteRowData *old_row;
+	VteRowData *new_row = NULL;
+	const VteCell *cell;
+
+	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++;
+	}
+	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);
+	}
+
+	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;
+		}
+		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);
+					}
+				}
+			}
+			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_ring_write_contents:
  * @ring: a #VteRing
  * @stream: a #GOutputStream to write to
diff -ur vte-0.34.7.orig/src/ring.h vte-0.34.7/src/ring.h
--- vte-0.34.7.orig/src/ring.h	2013-06-28 16:44:33.000000000 +0200
+++ vte-0.34.7/src/ring.h	2013-09-10 21:33:37.615352418 +0200
@@ -32,6 +32,10 @@
 G_BEGIN_DECLS
 
 
+typedef struct _VteVisualPosition {
+	long row, col;
+} VteVisualPosition;
+
 typedef struct _VteCellAttrChange {
 	gsize text_offset;
 	VteIntCellAttr attr;
@@ -80,6 +84,7 @@
 VteRowData *_vte_ring_insert (VteRing *ring, gulong position);
 VteRowData *_vte_ring_append (VteRing *ring);
 void _vte_ring_remove (VteRing *ring, gulong position);
+void _vte_ring_rewrap (VteRing *ring, glong columns, VteVisualPosition **markers);
 gboolean _vte_ring_write_contents (VteRing *ring,
 				   GOutputStream *stream,
 				   VteTerminalWriteFlags flags,
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 21:34:01.563353411 +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
 		}
 	}
 }
@@ -8022,10 +8023,13 @@
 vte_terminal_set_size(VteTerminal *terminal, glong columns, glong rows)
 {
 	glong old_columns, old_rows;
+	VteVisualPosition cursor_saved_absolute, below_bottom_left;
+	VteVisualPosition *markers[4];
+	gboolean was_scrolled_to_top, was_scrolled_to_bottom;
 
 	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,15 +8051,104 @@
 		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) {
+		// TODO: Don't waste time resizing the normal screen if the alternate screen is active.
+		// Resize it only once when returning to normal screen.
+
+		glong new_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_current (absolute)  row=%ld  (visual line %ld)  col=%ld\n"
+				"Old cursor_saved (relative to insert_delta)  row=%ld  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,
+				normal_screen->cursor_saved.row, normal_screen->cursor_saved.col);
+
+		cursor_saved_absolute.row = normal_screen->cursor_saved.row + normal_screen->insert_delta;
+		cursor_saved_absolute.col = normal_screen->cursor_saved.col;
+		below_bottom_left.row = normal_screen->scroll_delta + old_rows;
+		below_bottom_left.col = 0;
+		markers[0] = &normal_screen->cursor_current;
+		markers[1] = &cursor_saved_absolute;
+		markers[2] = &below_bottom_left;
+		markers[3] = NULL;
+
+		was_scrolled_to_top = (normal_screen->scroll_delta == _vte_ring_delta(ring));
+		was_scrolled_to_bottom = (normal_screen->scroll_delta == normal_screen->insert_delta);
+
+		if (old_columns != terminal->column_count) {
+			_vte_ring_rewrap(ring, terminal->column_count, markers);
+#ifdef VTE_DEBUG
+			g_assert(normal_screen->cursor_current.row != -1);
+			g_assert(cursor_saved_absolute.row != -1);  // TODO: I'm a bit afraid of this one!
+#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);
+			new_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. */
+				new_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. */
+				new_scroll_delta = _vte_ring_delta(ring);
+				_vte_debug_print(VTE_DEBUG_RESIZE,
+						"Scroll to top\n");
+			} else {
+				/* Try to scroll so that the bottom row stays.
+				   More precisely, the character below the bottom left corner stays in that
+				   (invisible) row.
+				   So if the bottom of the screen was at a hard line break then that hard
+				   line break will stay there.
+				   TODO: What would be the best behavior if the bottom of the screen is a
+				   soft line break, i.e. only a partial line is visible at the bottom? */
+#ifdef VTE_DEBUG
+				/* If -1, we should've entered the "scroll to bottom" branch. */
+				g_assert(below_bottom_left.row != -1);
+#endif
+				new_scroll_delta = below_bottom_left.row - terminal->row_count;
+				_vte_debug_print(VTE_DEBUG_RESIZE,
+						"Scroll so bottom row stays\n");
+			}
+		}
+		/* Don't clamp, they'll be clamped when restored. Until then remember off-screen values
+		   since they might become on-screen again on subsequent resizes. */
+		normal_screen->cursor_saved.row = cursor_saved_absolute.row - normal_screen->insert_delta;
+		normal_screen->cursor_saved.col = cursor_saved_absolute.col;
+
+		_vte_debug_print(VTE_DEBUG_RESIZE,
+				"New  insert_delta=%ld  scroll_delta=%ld\n"
+				"New cursor_current (absolute)  row=%ld  (visual line %ld)  col=%ld\n"
+				"New cursor_saved (relative to insert_delta)  row=%ld  col=%ld\n",
+				normal_screen->insert_delta, new_scroll_delta,
+				normal_screen->cursor_current.row,
+				normal_screen->cursor_current.row - new_scroll_delta + 1,
+				normal_screen->cursor_current.col,
+				normal_screen->cursor_saved.row, normal_screen->cursor_saved.col);
+
+		if (terminal->pvt->screen == normal_screen) {
+			vte_terminal_queue_adjustment_value_changed(
+				terminal,
+				new_scroll_delta);
+		} else {
+			normal_screen->scroll_delta = new_scroll_delta;
 		}
 		gtk_widget_queue_resize_no_redraw (&terminal->widget);
 		/* Our visible text changed. */
diff -ur vte-0.34.7.orig/src/vte-private.h vte-0.34.7/src/vte-private.h
--- vte-0.34.7.orig/src/vte-private.h	2013-06-28 16:44:33.000000000 +0200
+++ vte-0.34.7/src/vte-private.h	2013-09-10 20:33:32.183202946 +0200
@@ -143,10 +143,6 @@
 	gunichar start, end;
 } VteWordCharRange;
 
-typedef struct _VteVisualPosition {
-	long row, col;
-} VteVisualPosition;
-
 /* Terminal private data. */
 struct _VteTerminalPrivate {
 	/* Emulation setup data. */
