diff --git a/dvc/command/experiments.py b/dvc/command/experiments.py index 19d96cefb..d6343afc7 100644 --- a/dvc/command/experiments.py +++ b/dvc/command/experiments.py @@ -2,6 +2,7 @@ import argparse import io import logging from collections import OrderedDict +from datetime import date from itertools import groupby from typing import Iterable, Optional @@ -101,19 +102,18 @@ def _collect_rows( row = [] style = None queued = "*" if exp.get("queued", False) else "" - if no_timestamp: - timestamp = "" - else: - commit_time = exp.get("timestamp") - timestamp = f" ({commit_time})" if commit_time is not None else "" if rev == "baseline": - row.append(f"{base_rev}{timestamp}") + name = exp.get("name", base_rev) + row.append(f"{name}") style = "bold" elif i < len(experiments) - 1: - row.append(f"├── {queued}{rev[:7]}{timestamp}") + row.append(f"├── {queued}{rev[:7]}") else: - row.append(f"└── {queued}{rev[:7]}{timestamp}") + row.append(f"└── {queued}{rev[:7]}") + + if not no_timestamp: + row.append(_format_time(exp.get("timestamp"))) _extend_row( row, metric_names, exp.get("metrics", {}).items(), precision @@ -123,6 +123,16 @@ def _collect_rows( yield row, style +def _format_time(timestamp): + if timestamp is None: + return "-" + if timestamp.date() == date.today(): + fmt = "%I:%M %p" + else: + fmt = "%b %d, %Y" + return timestamp.strftime(fmt) + + def _extend_row(row, names, items, precision): from flatten_json import flatten @@ -181,6 +191,8 @@ def _show_experiments(all_experiments, console, **kwargs): table = Table() table.add_column("Experiment", no_wrap=True) + if not kwargs.get("no_timestamp", False): + table.add_column("Created") for name in metric_names: table.add_column(name, justify="right", no_wrap=True) for name in param_names: @@ -212,6 +224,7 @@ class CmdExperimentsShow(CmdBase): all_branches=self.args.all_branches, all_tags=self.args.all_tags, all_commits=self.args.all_commits, + sha_only=self.args.sha, ) if self.args.no_pager: @@ -414,6 +427,12 @@ def add_parser(subparsers, parent_parser): default=False, help="Do not show experiment timestamps.", ) + experiments_show_parser.add_argument( + "--sha", + action="store_true", + default=False, + help="Always show git commit SHAs instead of branch/tag names.", + ) experiments_show_parser.set_defaults(func=CmdExperimentsShow) EXPERIMENTS_CHECKOUT_HELP = "Checkout experiments." diff --git a/dvc/repo/experiments/show.py b/dvc/repo/experiments/show.py index 55594d951..6a2a6d50e 100644 --- a/dvc/repo/experiments/show.py +++ b/dvc/repo/experiments/show.py @@ -9,7 +9,9 @@ from dvc.repo.params.show import _collect_configs, _read_params logger = logging.getLogger(__name__) -def _collect_experiment(repo, branch, stash=False): +def _collect_experiment(repo, branch, stash=False, sha_only=True): + from git.exc import GitCommandError + res = defaultdict(dict) for rev in repo.brancher(revs=[branch]): if rev == "workspace": @@ -29,12 +31,27 @@ def _collect_experiment(repo, branch, stash=False): vals = _read_metrics(repo, metrics, rev) res["metrics"] = vals + if not sha_only and rev != "workspace": + try: + name = repo.scm.repo.git.describe( + rev, all=True, exact_match=True + ) + name = name.rsplit("/")[-1] + res["name"] = name + except GitCommandError: + pass + return res @locked def show( - repo, all_branches=False, all_tags=False, revs=None, all_commits=False + repo, + all_branches=False, + all_tags=False, + revs=None, + all_commits=False, + sha_only=False, ): res = defaultdict(OrderedDict) @@ -52,7 +69,9 @@ def show( ) for rev in revs: - res[rev]["baseline"] = _collect_experiment(repo, rev) + res[rev]["baseline"] = _collect_experiment( + repo, rev, sha_only=sha_only + ) # collect reproduced experiments for exp_branch in repo.experiments.scm.list_branches(): diff --git a/tests/func/experiments/test_show.py b/tests/func/experiments/test_show.py index 564d1d24a..63e23ddf7 100644 --- a/tests/func/experiments/test_show.py +++ b/tests/func/experiments/test_show.py @@ -48,6 +48,7 @@ def test_show_experiment(tmp_dir, scm, dvc): "params": {"params.yaml": {"foo": 1}}, "queued": False, "timestamp": timestamp, + "name": "master", } expected_params = {"foo": 2} diff --git a/tests/unit/command/test_experiments.py b/tests/unit/command/test_experiments.py index 8b1048147..0d751f00c 100644 --- a/tests/unit/command/test_experiments.py +++ b/tests/unit/command/test_experiments.py @@ -37,6 +37,7 @@ def test_experiments_show(dvc, mocker): "--all-tags", "--all-branches", "--all-commits", + "--sha", ] ) assert cli_args.func == CmdExperimentsShow @@ -47,5 +48,9 @@ def test_experiments_show(dvc, mocker): assert cmd.run() == 0 m.assert_called_once_with( - cmd.repo, all_tags=True, all_branches=True, all_commits=True + cmd.repo, + all_tags=True, + all_branches=True, + all_commits=True, + sha_only=True, )