diff --git a/dvc/command/experiments.py b/dvc/command/experiments.py index 9b68a4666..5c3c8b6be 100644 --- a/dvc/command/experiments.py +++ b/dvc/command/experiments.py @@ -337,6 +337,7 @@ class CmdExperimentsShow(CmdBase): all_tags=self.args.all_tags, all_commits=self.args.all_commits, sha_only=self.args.sha, + num=self.args.num, ) if self.args.show_json: @@ -674,11 +675,21 @@ def add_parser(subparsers, parent_parser): help="Show experiments derived from all Git tags.", ) experiments_show_parser.add_argument( + "-A", "--all-commits", action="store_true", default=False, help="Show experiments derived from all Git commits.", ) + experiments_show_parser.add_argument( + "-n", + "--num", + type=int, + default=1, + dest="num", + metavar="", + help="Show the last `num` commits from HEAD.", + ) experiments_show_parser.add_argument( "--no-pager", action="store_true", diff --git a/dvc/repo/experiments/executor/base.py b/dvc/repo/experiments/executor/base.py index 81df4d99a..9dd061905 100644 --- a/dvc/repo/experiments/executor/base.py +++ b/dvc/repo/experiments/executor/base.py @@ -379,7 +379,7 @@ class BaseExecutor(ABC): ): """Commit stages as an experiment and return the commit SHA.""" rev = scm.get_rev() - if not scm.is_dirty(untracked_files=True): + if not scm.is_dirty(): logger.debug("No changes to commit") raise UnchangedExperimentError(rev) diff --git a/dvc/repo/experiments/show.py b/dvc/repo/experiments/show.py index bb1dfb457..0da395397 100644 --- a/dvc/repo/experiments/show.py +++ b/dvc/repo/experiments/show.py @@ -2,10 +2,12 @@ import logging from collections import OrderedDict, defaultdict from datetime import datetime +from dvc.exceptions import InvalidArgumentError from dvc.repo import locked from dvc.repo.experiments.base import ExpRefInfo from dvc.repo.metrics.show import _collect_metrics, _read_metrics from dvc.repo.params.show import _collect_configs, _read_params +from dvc.scm.base import SCMError logger = logging.getLogger(__name__) @@ -79,11 +81,20 @@ def show( revs=None, all_commits=False, sha_only=False, + num=1, ): res = defaultdict(OrderedDict) + if num < 1: + raise InvalidArgumentError(f"Invalid number of commits '{num}'") + if revs is None: - revs = [repo.scm.get_rev()] + revs = [] + for n in range(num): + try: + revs.append(repo.scm.resolve_rev(f"HEAD~{n}")) + except SCMError: + break revs = OrderedDict( (rev, None) diff --git a/tests/func/experiments/test_show.py b/tests/func/experiments/test_show.py index 4474e56e6..9522aa4cf 100644 --- a/tests/func/experiments/test_show.py +++ b/tests/func/experiments/test_show.py @@ -192,3 +192,25 @@ def test_show_filter(tmp_dir, scm, dvc, exp_stage, capsys): cap = capsys.readouterr() assert f"{div} foo {div}" not in cap.out + + +def test_show_multiple_commits(tmp_dir, scm, dvc, exp_stage): + from dvc.exceptions import InvalidArgumentError + + init_rev = scm.get_rev() + tmp_dir.scm_gen("file", "file", "commit") + next_rev = scm.get_rev() + + with pytest.raises(InvalidArgumentError): + dvc.experiments.show(num=-1) + + expected = {"workspace", init_rev, next_rev} + results = dvc.experiments.show(num=2) + assert set(results.keys()) == expected + + expected = {"workspace"} | set(scm.branch_revs("master")) + results = dvc.experiments.show(all_commits=True) + assert set(results.keys()) == expected + + results = dvc.experiments.show(num=100) + assert set(results.keys()) == expected diff --git a/tests/unit/command/test_experiments.py b/tests/unit/command/test_experiments.py index a99b80e91..2745036ce 100644 --- a/tests/unit/command/test_experiments.py +++ b/tests/unit/command/test_experiments.py @@ -65,6 +65,8 @@ def test_experiments_show(dvc, scm, mocker): "--all-branches", "--all-commits", "--sha", + "-n", + "1", ] ) assert cli_args.func == CmdExperimentsShow @@ -80,6 +82,7 @@ def test_experiments_show(dvc, scm, mocker): all_branches=True, all_commits=True, sha_only=True, + num=1, )