Repository: xonsh/xonsh

Issue:
`history pull` - pull the command history from parallel sessions to the current session
`history pull` command was implemented in [xontrib-rc-awesome](https://github.com/anki-code/xontrib-rc-awesome) for sqlite history backend - [history-pull](https://github.com/anki-code/xontrib-rc-awesome/blob/db11c3dadb861dc594e069f5b12d3ac34ed78af5/xontrib/rc_awesome.xsh#L218-L248).

We need:
* add `pull` method to [History](https://github.com/xonsh/xonsh/blob/bba684e0bdf182ccb898f28d0e3e63c717df61e6/xonsh/history/base.py)
* add the implementation of `pull` method to [SqliteHistory](https://github.com/xonsh/xonsh/blob/bba684e0bdf182ccb898f28d0e3e63c717df61e6/xonsh/history/sqlite.py#L251)
* [optional] add the same logic to other history backends

## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**



Discussion:

Resolving patch:
diff --git a/xonsh/history/base.py b/xonsh/history/base.py
--- a/xonsh/history/base.py
+++ b/xonsh/history/base.py
@@ -130,6 +130,10 @@ def append(self, cmd):
         """
         pass
 
+    def pull(self, **kwargs):
+        """Pull history from other parallel sessions."""
+        raise NotImplementedError
+
     def flush(self, **kwargs):
         """Flush the history items to disk from a buffer."""
         pass
diff --git a/xonsh/history/main.py b/xonsh/history/main.py
--- a/xonsh/history/main.py
+++ b/xonsh/history/main.py
@@ -281,6 +281,31 @@ def id_cmd(_stdout):
             return
         print(str(hist.sessionid), file=_stdout)
 
+    @staticmethod
+    def pull(show_commands=False, _stdout=None):
+        """Pull history from other parallel sessions.
+
+        Parameters
+        ----------
+        show_commands: -c, --show-commands
+            show pulled commands
+        """
+
+        hist = XSH.history
+
+        if hist.pull.__module__ == "xonsh.history.base":
+            backend = XSH.env.get("XONSH_HISTORY_BACKEND", "unknown")
+            print(
+                f"Pull method is not supported in {backend} history backend.",
+                file=_stdout,
+            )
+
+        lines_added = hist.pull(show_commands)
+        if lines_added:
+            print(f"Added {lines_added} records!", file=_stdout)
+        else:
+            print(f"No records found!", file=_stdout)
+
     @staticmethod
     def flush(_stdout):
         """Flush the current history to disk"""
@@ -428,7 +453,7 @@ def transfer(
 
         dest.flush()
 
-        self.out("done.")
+        self.out("Done")
 
     def build(self):
         parser = self.create_parser(prog="history")
@@ -436,6 +461,7 @@ def build(self):
         parser.add_command(self.id_cmd, prog="id")
         parser.add_command(self.file)
         parser.add_command(self.info)
+        parser.add_command(self.pull)
         parser.add_command(self.flush)
         parser.add_command(self.off)
         parser.add_command(self.on)
diff --git a/xonsh/history/sqlite.py b/xonsh/history/sqlite.py
--- a/xonsh/history/sqlite.py
+++ b/xonsh/history/sqlite.py
@@ -204,6 +204,15 @@ def xh_sqlite_delete_items(size_to_keep, filename=None):
         return _xh_sqlite_delete_records(c, size_to_keep)
 
 
+def xh_sqlite_pull(filename, last_pull_time, current_sessionid):
+    sql = f"SELECT inp FROM xonsh_history WHERE tsb > ? AND sessionid != ? ORDER BY tsb"
+    params = [last_pull_time, current_sessionid]
+    with _xh_sqlite_get_conn(filename=filename) as conn:
+        c = conn.cursor()
+        c.execute(sql, tuple(params))
+        return c.fetchall()
+
+
 def xh_sqlite_wipe_session(sessionid=None, filename=None):
     """Wipe the current session's entries from the database."""
     sql = "DELETE FROM xonsh_history WHERE sessionid = ?"
@@ -256,6 +265,7 @@ def __init__(self, gc=True, filename=None, save_cwd=None, **kwargs):
         if filename is None:
             filename = _xh_sqlite_get_file_name()
         self.filename = filename
+        self.last_pull_time = time.time()
         self.gc = SqliteHistoryGC() if gc else None
         self._last_hist_inp = None
         self.inps = []
@@ -345,6 +355,22 @@ def info(self):
         data["gc options"] = envs.get("XONSH_HISTORY_SIZE")
         return data
 
+    def pull(self, show_commands=False):
+        if not hasattr(XSH.shell.shell, "prompter"):
+            print(f"Shell type {XSH.shell.shell} is not supported.")
+            return 0
+
+        cnt = 0
+        for r in xh_sqlite_pull(
+            self.filename, self.last_pull_time, str(self.sessionid)
+        ):
+            if show_commands:
+                print(r[0])
+            XSH.shell.shell.prompter.history.append_string(r[0])
+            cnt += 1
+        self.last_pull_time = time.time()
+        return cnt
+
     def run_gc(self, size=None, blocking=True):
         self.gc = SqliteHistoryGC(wait_for_shell=False, size=size)
         if blocking:


Test patch: