diff --git a/CHANGELOG.md b/CHANGELOG.md
index e8225782..45b726eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
 * Enhancements
     * Send all startup script paths to run_script. Previously we didn't do this if the file was empty, but that
     showed no record of the run_script command in history. 
+    * Made it easier for developers to override `edit` command by having `do_history` no longer call `do_edit`. This
+    also removes the need to exclude `edit` command from history list.
 
 ## 0.9.19 (October 14, 2019)
 * Bug Fixes
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 6a600107..1af5e932 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -433,7 +433,7 @@ class Cmd(cmd.Cmd):
         self._initialize_history(persistent_history_file)
 
         # Commands to exclude from the history command
-        self.exclude_from_history = '''history edit eof'''.split()
+        self.exclude_from_history = ['eof', 'history']
 
         # Dictionary of macro names and their values
         self.macros = dict()
@@ -3520,14 +3520,9 @@ class Cmd(cmd.Cmd):
                     else:
                         fobj.write('{}\n'.format(command.raw))
             try:
-                # Handle potential edge case where the temp file needs to be quoted on the command line
-                quoted_fname = utils.quote_string(fname)
-
-                # noinspection PyTypeChecker
-                self.do_edit(quoted_fname)
-
+                self._run_editor(fname)
                 # noinspection PyTypeChecker
-                self.do_run_script(quoted_fname)
+                self.do_run_script(utils.quote_string(fname))
             finally:
                 os.remove(fname)
         elif args.output_file:
@@ -3719,7 +3714,7 @@ class Cmd(cmd.Cmd):
             msg = '{} {} saved to transcript file {!r}'
             self.pfeedback(msg.format(commands_run, plural, transcript_file))
 
-    edit_description = ("Edit a file in a text editor\n"
+    edit_description = ("Run a text editor and optionally open a file with it\n"
                         "\n"
                         "The editor used is determined by a settable parameter. To set it:\n"
                         "\n"
@@ -3727,17 +3722,25 @@ class Cmd(cmd.Cmd):
 
     edit_parser = Cmd2ArgumentParser(description=edit_description)
     edit_parser.add_argument('file_path', nargs=argparse.OPTIONAL,
-                             help="path to a file to open in editor", completer_method=path_complete)
+                             help="optional path to a file to open in editor", completer_method=path_complete)
 
     @with_argparser(edit_parser)
     def do_edit(self, args: argparse.Namespace) -> None:
-        """Edit a file in a text editor"""
+        """Run a text editor and optionally open a file with it"""
+        self._run_editor(args.file_path)
+
+    def _run_editor(self, file_path: Optional[str]) -> None:
+        """
+        Run a text editor and optionally open a file with it
+        :param file_path: optional path of the file to edit
+        :raises EnvironmentError if self.editor is not set
+        """
         if not self.editor:
             raise EnvironmentError("Please use 'set editor' to specify your text editing program of choice.")
 
         command = utils.quote_string(os.path.expanduser(self.editor))
-        if args.file_path:
-            command += " " + utils.quote_string(os.path.expanduser(args.file_path))
+        if file_path:
+            command += " " + utils.quote_string(os.path.expanduser(file_path))
 
         # noinspection PyTypeChecker
         self.do_shell(command)

History should not call do_edit
The `do_history` method calls `do_edit` and this causes problems when a **cmd2** app has overridden the **edit** command. Change `do_history` to call non-command function for its edit functionality.
diff --git a/tests/test_history.py b/tests/test_history.py
index 476cdd7e..4b900030 100755
--- a/tests/test_history.py
+++ b/tests/test_history.py
@@ -476,9 +476,9 @@ def test_history_edit(base_app, monkeypatch):
     # going to call it due to the mock
     base_app.editor = 'fooedit'
 
-    # Mock out the edit call so we don't actually open an editor
-    edit_mock = mock.MagicMock(name='do_edit')
-    monkeypatch.setattr("cmd2.Cmd.do_edit", edit_mock)
+    # Mock out the _run_editor call so we don't actually open an editor
+    edit_mock = mock.MagicMock(name='_run_editor')
+    monkeypatch.setattr("cmd2.Cmd._run_editor", edit_mock)
 
     # Mock out the run_script call since the mocked edit won't produce a file
     run_script_mock = mock.MagicMock(name='do_run_script')
@@ -590,17 +590,6 @@ def test_base_help_history(base_app):
     assert out == normalize(HELP_HISTORY)
 
 def test_exclude_from_history(base_app, monkeypatch):
-    # Set a fake editor just to make sure we have one.  We aren't
-    # really going to call it due to the mock
-    base_app.editor = 'fooedit'
-
-    # Mock out the subprocess.Popen call so we don't actually open an editor
-    m = mock.MagicMock(name='Popen')
-    monkeypatch.setattr("subprocess.Popen", m)
-
-    # Run edit command
-    run_cmd(base_app, 'edit')
-
     # Run history command
     run_cmd(base_app, 'history')
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.16
anyio==4.9.0
attrs==25.3.0
babel==2.17.0
backports.tarfile==1.2.0
cachetools==5.5.2
certifi==2025.1.31
cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.4.1
click==8.1.8
-e git+https://github.com/python-cmd2/cmd2.git@406f017e2ee99fb76bada7bde4b52c3b0f1aa1d2#egg=cmd2
codecov==2.1.13
colorama==0.4.6
coverage==7.8.0
cryptography==44.0.2
distlib==0.3.9
doc8==1.1.2
docutils==0.21.2
exceptiongroup==1.2.2
filelock==3.18.0
flake8==7.2.0
h11==0.14.0
id==1.5.0
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig==2.1.0
invoke==2.2.0
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
Jinja2==3.1.6
keyring==25.6.0
markdown-it-py==3.0.0
MarkupSafe==3.0.2
mccabe==0.7.0
mdurl==0.1.2
more-itertools==10.6.0
nh3==0.2.21
packaging==24.2
pbr==6.1.1
platformdirs==4.3.7
pluggy==1.5.0
pycodestyle==2.13.0
pycparser==2.22
pyflakes==3.3.1
Pygments==2.19.1
pyperclip==1.9.0
pyproject-api==1.9.0
pytest==8.3.5
pytest-cov==6.0.0
pytest-mock==3.14.0
readme_renderer==44.0
requests==2.32.3
requests-toolbelt==1.0.0
restructuredtext_lint==1.4.0
rfc3986==2.0.0
rich==14.0.0
SecretStorage==3.3.3
sniffio==1.3.1
snowballstemmer==2.2.0
Sphinx==7.4.7
sphinx-autobuild==2024.10.3
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
starlette==0.46.1
stevedore==5.4.1
tomli==2.2.1
tox==4.25.0
twine==6.1.0
typing_extensions==4.13.0
urllib3==2.3.0
uvicorn==0.34.0
virtualenv==20.29.3
watchfiles==1.0.4
wcwidth==0.2.13
websockets==15.0.1
zipp==3.21.0

name: cmd2
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.16
      - anyio==4.9.0
      - attrs==25.3.0
      - babel==2.17.0
      - backports-tarfile==1.2.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - cffi==1.17.1
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - click==8.1.8
      - codecov==2.1.13
      - colorama==0.4.6
      - coverage==7.8.0
      - cryptography==44.0.2
      - distlib==0.3.9
      - doc8==1.1.2
      - docutils==0.21.2
      - exceptiongroup==1.2.2
      - filelock==3.18.0
      - flake8==7.2.0
      - h11==0.14.0
      - id==1.5.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - invoke==2.2.0
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==25.6.0
      - markdown-it-py==3.0.0
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - mdurl==0.1.2
      - more-itertools==10.6.0
      - nh3==0.2.21
      - packaging==24.2
      - pbr==6.1.1
      - platformdirs==4.3.7
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pycparser==2.22
      - pyflakes==3.3.1
      - pygments==2.19.1
      - pyperclip==1.9.0
      - pyproject-api==1.9.0
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - readme-renderer==44.0
      - requests==2.32.3
      - requests-toolbelt==1.0.0
      - restructuredtext-lint==1.4.0
      - rfc3986==2.0.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - sniffio==1.3.1
      - snowballstemmer==2.2.0
      - sphinx==7.4.7
      - sphinx-autobuild==2024.10.3
      - sphinx-rtd-theme==3.0.2
      - sphinxcontrib-applehelp==2.0.0
      - sphinxcontrib-devhelp==2.0.0
      - sphinxcontrib-htmlhelp==2.1.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==2.0.0
      - sphinxcontrib-serializinghtml==2.0.0
      - starlette==0.46.1
      - stevedore==5.4.1
      - tomli==2.2.1
      - tox==4.25.0
      - twine==6.1.0
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - uvicorn==0.34.0
      - virtualenv==20.29.3
      - watchfiles==1.0.4
      - wcwidth==0.2.13
      - websockets==15.0.1
      - zipp==3.21.0
prefix: /opt/conda/envs/cmd2

tests/test_history.py::test_history_edit
tests/test_history.py::test_readline_remove_history_item tests/test_history.py::test_history_class_span tests/test_history.py::test_persisted_history_span tests/test_history.py::test_history_class_get tests/test_history.py::test_history_str_search tests/test_history.py::test_history_regex_search tests/test_history.py::test_history_max_length_zero tests/test_history.py::test_history_max_length_negative tests/test_history.py::test_history_max_length tests/test_history.py::test_multiline_histitem tests/test_history.py::test_multiline_histitem_verbose tests/test_history.py::test_history_item_instantiate tests/test_history.py::test_history_item_properties tests/test_history.py::test_base_history tests/test_history.py::test_history_script_format tests/test_history.py::test_history_with_string_argument tests/test_history.py::test_history_expanded_with_string_argument tests/test_history.py::test_history_expanded_with_regex_argument tests/test_history.py::test_history_with_integer_argument tests/test_history.py::test_history_with_integer_span tests/test_history.py::test_history_with_span_start tests/test_history.py::test_history_with_span_end tests/test_history.py::test_history_with_span_index_error tests/test_history.py::test_history_output_file tests/test_history.py::test_history_bad_output_file tests/test_history.py::test_history_run_all_commands tests/test_history.py::test_history_run_one_command tests/test_history.py::test_history_clear tests/test_history.py::test_history_verbose_with_other_options tests/test_history.py::test_history_verbose tests/test_history.py::test_history_script_with_invalid_options tests/test_history.py::test_history_script tests/test_history.py::test_history_expanded_with_invalid_options tests/test_history.py::test_history_expanded tests/test_history.py::test_history_script_expanded tests/test_history.py::test_base_help_history tests/test_history.py::test_exclude_from_history tests/test_history.py::test_history_file_is_directory tests/test_history.py::test_history_can_create_directory tests/test_history.py::test_history_cannot_create_directory tests/test_history.py::test_history_file_permission_error tests/test_history.py::test_history_file_conversion_no_truncate_on_init tests/test_history.py::test_history_populates_readline tests/test_history.py::test_persist_history_ensure_no_error_if_no_histfile tests/test_history.py::test_persist_history_permission_error
cmd2/cmd2.py CHANGELOG.md
cmd2/cmd2.py CHANGELOG.md