Repository: iterative/dvc

Issue:
dvc exp reset: continues from previous checkpoint?
# Bug Report


## Description

`dvc exp reset` seems to be continuing from the previous checkpoint.

### Reproduce

1. git clone https://github.com/dberenbaum/dvc-checkpoint.git.
2. dvc exp run
3. dvc exp reset
4. dvc exp show --no-pager

```console
┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃ Experiment    ┃ Created      ┃ epoch ┃ start ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━┩
│ workspace     │ -            │     3 │ 0     │
│ main          │ Jan 21, 2021 │     - │ 0     │
│ │ ╓ exp-5bd88 │ 04:04 PM     │     3 │ 0     │
│ ├─╨ 8ab8c4d   │ 04:04 PM     │     2 │ 0     │
│ │ ╓ exp-f3c4a │ 04:04 PM     │     1 │ 0     │
│ ├─╨ 04e6a25   │ 04:04 PM     │     0 │ 0     │
└───────────────┴──────────────┴───────┴───────┘
```

### Expected

```console
┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃ Experiment    ┃ Created      ┃ epoch ┃ start ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━┩
│ workspace     │ -            │     1 │ 0     │
│ main          │ Jan 21, 2021 │     - │ 0     │
│ │ ╓ exp-5bd88 │ 04:04 PM     │     1 │ 0     │
│ ├─╨ 8ab8c4d   │ 04:04 PM     │     0 │ 0     │
│ │ ╓ exp-f3c4a │ 04:04 PM     │     1 │ 0     │
│ ├─╨ 04e6a25   │ 04:04 PM     │     0 │ 0     │
└───────────────┴──────────────┴───────┴───────┘
```

### Environment information

**Output of `dvc version`:**

```console
$ dvc version
DVC version: 2.0.0a0+b2c292
---------------------------------
Platform: Python 3.8.5 on Linux-5.8.0-38-generic-x86_64-with-glibc2.10
Supports: http, https
Cache types: hardlink, symlink
Cache directory: ext4 on /dev/mapper/vgubuntu-root
Caches: local
Remotes: None
Workspace directory: ext4 on /dev/mapper/vgubuntu-root
Repo: dvc, git
```


Discussion:
So the reason for this issue is that in the initial state before any experiment is run, `dvc.lock` for this pipeline does not exist yet.

After the first `exp run`, `dvc.lock` is created and contains a hash for `foo`.

When we do `exp reset`, we do the equivalent of
```
git checkout HEAD
git reset --hard
dvc checkout --force
```
to "reset" the state of the repo in terms of checkpoint runs. The problem being that this does not remove `dvc.lock` from the workspace, since it is not tracked by git at all in `HEAD`. So we still end up checking out the leftover hash for `foo` from `dvc.lock`.

We could do the equivalent of `git clean` and just delete any untracked lockfiles on `exp reset`, but I'm not sure if that's safe for us to do here? It's possible that the user could have just forgotten to `git add/commit` `dvc.lock` at some point prior to this experiment run (i.e. this lock file could contain hashes for other non-checkpoint dependencies or outputs that we would end up deleting).

Resolving patch:
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
@@ -258,9 +258,22 @@ def _prune_lockfiles(self):
             if is_lock_file(fname)
         ]
         if lock_files:
+
             self.scm.reset(paths=lock_files)
             self.scm.checkout_index(paths=lock_files, force=True)
 
+    def _prune_untracked_lockfiles(self):
+        from dvc.dvcfile import is_lock_file
+        from dvc.utils.fs import remove
+
+        untracked = [
+            fname
+            for fname in self.scm.untracked_files()
+            if is_lock_file(fname)
+        ]
+        for fname in untracked:
+            remove(fname)
+
     def _stash_msg(
         self,
         rev: str,
@@ -698,6 +711,7 @@ def _workspace_repro(self) -> Mapping[str, str]:
         # result in conflict between workspace params and stashed CLI params).
         self.scm.reset(hard=True)
         with self.scm.detach_head(entry.rev):
+            self._prune_untracked_lockfiles()
             rev = self.stash.pop()
             self.scm.set_ref(EXEC_BASELINE, entry.baseline_rev)
             if entry.branch:
diff --git a/dvc/repo/experiments/executor/base.py b/dvc/repo/experiments/executor/base.py
--- a/dvc/repo/experiments/executor/base.py
+++ b/dvc/repo/experiments/executor/base.py
@@ -250,7 +250,9 @@ def filter_pipeline(stages):
             args, kwargs = cls._repro_args(dvc)
 
             repro_force = kwargs.get("force", False)
-            logger.debug("force = %s", str(repro_force))
+            logger.trace(  # type: ignore[attr-defined]
+                "Executor repro with force = '%s'", str(repro_force)
+            )
 
             # NOTE: for checkpoint experiments we handle persist outs slightly
             # differently than normal:


Test patch: