Repository: iterative/dvc

Issue:
checkpoints: `--continue` with no arguments should resume the most recent experiment
Now that branches are sortable by most recent commit, this just requires a command update


Discussion:

Resolving patch:
diff --git a/dvc/command/experiments.py b/dvc/command/experiments.py
--- a/dvc/command/experiments.py
+++ b/dvc/command/experiments.py
@@ -710,12 +710,16 @@ def add_parser(subparsers, parent_parser):
     experiments_run_parser.add_argument(
         "--continue",
         type=str,
+        nargs="?",
         default=None,
+        const=":last",
         dest="checkpoint_continue",
         help=(
             "Continue from the specified checkpoint experiment "
-            "(implies --checkpoint)."
+            "(implies --checkpoint). If no experiment revision is provided, "
+            "the most recently run checkpoint experiment will be used."
         ),
+        metavar="<experiment_rev>",
     )
     experiments_run_parser.add_argument(
         "--reset",
diff --git a/dvc/repo/experiments/__init__.py b/dvc/repo/experiments/__init__.py
--- a/dvc/repo/experiments/__init__.py
+++ b/dvc/repo/experiments/__init__.py
@@ -426,13 +426,31 @@ def new(
         workspace.
         """
         if checkpoint_continue:
-            rev = self.scm.resolve_rev(checkpoint_continue)
-            branch = self._get_branch_containing(rev)
-            if not branch:
-                raise DvcException(
-                    "Could not find checkpoint experiment "
-                    f"'{checkpoint_continue}'"
-                )
+            branch = None
+            if checkpoint_continue == ":last":
+                # Continue from most recently committed checkpoint
+                for head in sorted(
+                    self.scm.repo.heads,
+                    key=lambda h: h.commit.committed_date,
+                    reverse=True,
+                ):
+                    exp_branch = head.name
+                    m = self.BRANCH_RE.match(exp_branch)
+                    if m and m.group("checkpoint"):
+                        branch = exp_branch
+                        break
+                if not branch:
+                    raise DvcException(
+                        "No existing checkpoint experiment to continue"
+                    )
+            else:
+                rev = self.scm.resolve_rev(checkpoint_continue)
+                branch = self._get_branch_containing(rev)
+                if not branch:
+                    raise DvcException(
+                        "Could not find checkpoint experiment "
+                        f"'{checkpoint_continue}'"
+                    )
             logger.debug(
                 "Continuing checkpoint experiment '%s'", checkpoint_continue
             )


Test patch:
diff --git a/tests/func/experiments/test_experiments.py b/tests/func/experiments/test_experiments.py
--- a/tests/func/experiments/test_experiments.py
+++ b/tests/func/experiments/test_experiments.py
@@ -301,7 +301,8 @@ def test_new_checkpoint(tmp_dir, scm, dvc, mocker):
     ).read_text().strip() == "foo: 2"
 
 
-def test_continue_checkpoint(tmp_dir, scm, dvc, mocker):
+@pytest.mark.parametrize("last", [True, False])
+def test_continue_checkpoint(tmp_dir, scm, dvc, mocker, last):
     tmp_dir.gen("checkpoint.py", CHECKPOINT_SCRIPT)
     tmp_dir.gen("params.yaml", "foo: 1")
     stage = dvc.run(
@@ -326,7 +327,10 @@ def test_continue_checkpoint(tmp_dir, scm, dvc, mocker):
     results = dvc.experiments.run(
         stage.addressing, checkpoint=True, params=["foo=2"]
     )
-    exp_rev = first(results)
+    if last:
+        exp_rev = ":last"
+    else:
+        exp_rev = first(results)
 
     dvc.experiments.run(
         stage.addressing, checkpoint=True, checkpoint_continue=exp_rev,