diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c
index fade4fe..a22a447 100644
--- a/cmd-set-buffer.c
+++ b/cmd-set-buffer.c
@@ -24,10 +24,11 @@
 #include "tmux.h"
 
 /*
- * Add or set a paste buffer.
+ * Add, set, or append to a paste buffer.
  */
 
 enum cmd_retval	 cmd_set_buffer_exec(struct cmd *, struct cmd_q *);
+enum cmd_retval	 cmd_append_buffer_exec(struct cmd *, struct cmd_q *);
 
 const struct cmd_entry cmd_set_buffer_entry = {
 	"set-buffer", "setb",
@@ -38,6 +39,15 @@ const struct cmd_entry cmd_set_buffer_entry = {
 	cmd_set_buffer_exec
 };
 
+const struct cmd_entry cmd_append_buffer_entry = {
+	"append-buffer", "appb",
+	"b:", 1, 1,
+	CMD_BUFFER_USAGE " data",
+	0,
+	NULL,
+	cmd_append_buffer_exec
+};
+
 enum cmd_retval
 cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 {
@@ -73,3 +83,53 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 
 	return (CMD_RETURN_NORMAL);
 }
+
+enum cmd_retval
+cmd_append_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
+{
+	struct args		*args = self->args;
+	struct paste_buffer	*pb;
+	u_int			 limit;
+	char			*pdata, *cause;
+	size_t			 psize, arg0size;
+	int			 buffer = 0;
+
+	limit = options_get_number(&global_options, "buffer-limit");
+
+	if (args_has(args, 'b')) {
+		buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
+		if (cause != NULL) {
+			cmdq_error(cmdq, "buffer %s", cause);
+			free(cause);
+			return (CMD_RETURN_ERROR);
+		}
+	}
+	pb = paste_get_index(&global_buffers, buffer);
+
+	if (pb == NULL) {
+		psize = 0;
+		pdata = NULL;
+	} else {
+		psize = pb->size;
+		pdata = xmalloc(psize);
+		memcpy(pdata, pb->data, psize);
+	}
+
+	arg0size = strlen(args->argv[0]);
+	pdata = xrealloc(pdata, 1, psize + arg0size);
+	memcpy(pdata + psize, args->argv[0], arg0size);
+	psize += arg0size;
+
+	if (buffer == 0 && paste_get_top(&global_buffers) == NULL) {
+		paste_add(&global_buffers, pdata, psize, limit);
+		return (CMD_RETURN_NORMAL);
+	}
+
+	if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) {
+		cmdq_error(cmdq, "no buffer %d", buffer);
+		free(pdata);
+		return (CMD_RETURN_ERROR);
+	}
+
+	return (CMD_RETURN_NORMAL);
+}
diff --git a/cmd.c b/cmd.c
index 5e6b93a..a501e78 100644
--- a/cmd.c
+++ b/cmd.c
@@ -28,6 +28,7 @@
 #include "tmux.h"
 
 const struct cmd_entry *cmd_table[] = {
+	&cmd_append_buffer_entry,
 	&cmd_attach_session_entry,
 	&cmd_bind_key_entry,
 	&cmd_break_pane_entry,
diff --git a/mode-key.c b/mode-key.c
index dfa3fb2..df71825 100644
--- a/mode-key.c
+++ b/mode-key.c
@@ -96,6 +96,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = {
 
 /* Copy keys command strings. */
 const struct mode_key_cmdstr mode_key_cmdstr_copy[] = {
+	{ MODEKEYCOPY_APPENDSELECTION, "append-selection" },
 	{ MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" },
 	{ MODEKEYCOPY_BOTTOMLINE, "bottom-line" },
 	{ MODEKEYCOPY_CANCEL, "cancel" },
@@ -259,6 +260,7 @@ const struct mode_key_entry mode_key_vi_copy[] = {
 	{ '9',			    0, MODEKEYCOPY_STARTNUMBERPREFIX },
 	{ ':',			    0, MODEKEYCOPY_GOTOLINE },
 	{ '?',			    0, MODEKEYCOPY_SEARCHUP },
+	{ 'A',			    0, MODEKEYCOPY_APPENDSELECTION },
 	{ 'B',			    0, MODEKEYCOPY_PREVIOUSSPACE },
 	{ 'D',			    0, MODEKEYCOPY_COPYENDOFLINE },
 	{ 'E',			    0, MODEKEYCOPY_NEXTSPACEEND },
diff --git a/tmux.h b/tmux.h
index 7d5f230..ad61298 100644
--- a/tmux.h
+++ b/tmux.h
@@ -529,6 +529,7 @@ enum mode_key_cmd {
 	MODEKEYCHOICE_UP,
 
 	/* Copy keys. */
+	MODEKEYCOPY_APPENDSELECTION,
 	MODEKEYCOPY_BACKTOINDENTATION,
 	MODEKEYCOPY_BOTTOMLINE,
 	MODEKEYCOPY_CANCEL,
@@ -1744,6 +1745,7 @@ char		*cmd_template_replace(const char *, const char *, int);
 struct window	*cmd_lookup_windowid(const char *);
 struct window_pane *cmd_lookup_paneid(const char *);
 extern const struct cmd_entry *cmd_table[];
+extern const struct cmd_entry cmd_append_buffer_entry;
 extern const struct cmd_entry cmd_attach_session_entry;
 extern const struct cmd_entry cmd_bind_key_entry;
 extern const struct cmd_entry cmd_break_pane_entry;
diff --git a/window-copy.c b/window-copy.c
index 3e9ca49..f0d4bbd 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -58,6 +58,7 @@ void	window_copy_copy_buffer(struct window_pane *, int, void *, size_t);
 void	window_copy_copy_pipe(
 	    struct window_pane *, struct session *, int, const char *);
 void	window_copy_copy_selection(struct window_pane *, int);
+void	window_copy_append_selection(struct window_pane *, int);
 void	window_copy_clear_selection(struct window_pane *);
 void	window_copy_copy_line(
 	    struct window_pane *, char **, size_t *, u_int, u_int, u_int);
@@ -414,6 +415,13 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
 
 	cmd = mode_key_lookup(&data->mdata, key, &arg);
 	switch (cmd) {
+	case MODEKEYCOPY_APPENDSELECTION:
+		if (sess != NULL) {
+			window_copy_append_selection(wp, data->numprefix);
+			window_pane_reset_mode(wp);
+			return;
+		}
+		break;
 	case MODEKEYCOPY_CANCEL:
 		window_pane_reset_mode(wp);
 		return;
@@ -1492,6 +1500,46 @@ window_copy_copy_selection(struct window_pane *wp, int idx)
 }
 
 void
+window_copy_append_selection(struct window_pane *wp, int idx)
+{
+	char			*buf;
+	struct paste_buffer	*pb;
+	size_t			 len;
+	u_int			 limit;
+	struct screen_write_ctx	 ctx;
+
+	buf = window_copy_get_selection(wp, &len);
+	if (buf == NULL)
+		return;
+
+	if (options_get_number(&global_options, "set-clipboard")) {
+		screen_write_start(&ctx, wp, NULL);
+		screen_write_setselection(&ctx, buf, len);
+		screen_write_stop(&ctx);
+	}
+
+	if (idx == -1)
+		idx = 0;
+
+	if (idx == 0 && paste_get_top(&global_buffers) == NULL) {
+		limit = options_get_number(&global_options, "buffer-limit");
+		paste_add(&global_buffers, buf, len, limit);
+		return;
+	}
+
+	pb = paste_get_index(&global_buffers, idx);
+	if (pb != NULL) {
+		buf = xrealloc(buf, 1, len + pb->size);
+		memmove(buf + pb->size, buf, len);
+		memcpy(buf, pb->data, pb->size);
+		len += pb->size;
+	}
+
+	if (paste_replace(&global_buffers, idx, buf, len) != 0)
+		free(buf);
+}
+
+void
 window_copy_copy_line(struct window_pane *wp,
     char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
 {
