skshetry: ```
E       AssertionError: assert {'version.dvc...available'}}]} == {'dir.dvc': [...available'}}]}
E         Omitting 1 identical items, use -vv to show
E         Right contains 1 more item:
E         {'dir.dvc': [{'changed deps': {'dir (/tmp/pytest-of-runner/pytest-0/popen-gw2/erepo25)': 'update '
E                                                                                                  'available'}}]}
E         Full diff:
E           {
E         -  'dir.dvc': [{'changed deps': {'dir (/tmp/pytest-of-runner/pytest-0/popen-gw2/erepo25)': 'update '
E         -                                                                                          'available'}}],
E            'version.dvc': [{'changed deps': {'version (/tmp/pytest-of-runner/pytest-0/popen-gw2/erepo25)': 'update '
E                                                                                                            'available'}}],
E           }
```

This test fails from time to time. It's very hard to reproduce, and I have tried to debug on failure, but haven't had much luck.

Once when I was able to reproduce, I debugged through it and found that somehow the "dir.dvc"'s `Output::checksum` gets changed when calculating the checksum, so when it later compares, it does not find any differences at all.
But I couldn't figure out why/when that happened.


pared: @skshetry thanks for this info, that saved me a lot of time for sure :)
efiop: This is great :fire:

Oh, I see only windows 3.8 fails. Could create a p0 ticket and just skip 3.8 in this PR for now. Could  also mark it as "allow_failure" so we have a constant reminder :slightly_smiling_face: 
codecov[bot]: # [Codecov](https://codecov.io/gh/iterative/dvc/pull/4469?src=pr&el=h1) Report
> Merging [#4469](https://codecov.io/gh/iterative/dvc/pull/4469?src=pr&el=desc) into [master](https://codecov.io/gh/iterative/dvc/commit/226322d31253e65536703b36b542dfaabb4381ed?el=desc) will **increase** coverage by `0.58%`.
> The diff coverage is `95.83%`.

[![Impacted file tree graph](https://codecov.io/gh/iterative/dvc/pull/4469/graphs/tree.svg?width=650&height=150&src=pr&token=VOfa1EJWC4)](https://codecov.io/gh/iterative/dvc/pull/4469?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #4469      +/-   ##
==========================================
+ Coverage   88.76%   89.34%   +0.58%     
==========================================
  Files         185      171      -14     
  Lines       13082    11755    -1327     
==========================================
- Hits        11612    10503    -1109     
+ Misses       1470     1252     -218     
```


| [Impacted Files](https://codecov.io/gh/iterative/dvc/pull/4469?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [dvc/tree/dvc.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3RyZWUvZHZjLnB5) | `95.85% <ø> (-0.05%)` | :arrow_down: |
| [dvc/tree/repo.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3RyZWUvcmVwby5weQ==) | `91.88% <83.33%> (-0.33%)` | :arrow_down: |
| [dvc/dvcfile.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL2R2Y2ZpbGUucHk=) | `97.48% <100.00%> (ø)` | |
| [dvc/repo/checkout.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3JlcG8vY2hlY2tvdXQucHk=) | `97.87% <100.00%> (+0.09%)` | :arrow_up: |
| [dvc/repo/commit.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3JlcG8vY29tbWl0LnB5) | `93.33% <100.00%> (ø)` | |
| [dvc/repo/experiments/\_\_init\_\_.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3JlcG8vZXhwZXJpbWVudHMvX19pbml0X18ucHk=) | `87.74% <100.00%> (ø)` | |
| [dvc/repo/fetch.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3JlcG8vZmV0Y2gucHk=) | `92.68% <100.00%> (+0.37%)` | :arrow_up: |
| [dvc/repo/freeze.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3JlcG8vZnJlZXplLnB5) | `100.00% <100.00%> (ø)` | |
| [dvc/repo/pull.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3JlcG8vcHVsbC5weQ==) | `100.00% <100.00%> (ø)` | |
| [dvc/repo/reproduce.py](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree#diff-ZHZjL3JlcG8vcmVwcm9kdWNlLnB5) | `95.00% <100.00%> (ø)` | |
| ... and [32 more](https://codecov.io/gh/iterative/dvc/pull/4469/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/iterative/dvc/pull/4469?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/iterative/dvc/pull/4469?src=pr&el=footer). Last update [2931d30...e024265](https://codecov.io/gh/iterative/dvc/pull/4469?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

efiop: @pared Is this WIP?
pared: @efiop yes
diff --git a/.travis.yml b/.travis.yml
index 1dd401c1b..052574898 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -32,32 +32,6 @@ jobs:
     before_install: bash ./scripts/ci/install.sh
     install:
     script: ./scripts/ci/check_patch.sh
-  # test jobs
-  - name: "3.7 on Windows"
-    stage: test
-    os: windows           # Windows 10.0.17134 N/A Build 17134
-    language: shell       # 'language: python' is an error on Travis CI Windows
-    env:
-      - PYTHON_VERSION=3.7.5
-      - PATH=/c/Python37:/c/Python37/Scripts:$PATH
-  - name: "3.8 on Windows"
-    os: windows           # Windows 10.0.17134 N/A Build 17134
-    language: shell       # 'language: python' is an error on Travis CI Windows
-    env:
-      - PYTHON_VERSION=3.8.0
-      - PATH=/c/Python38:/c/Python38/Scripts:$PATH
-  - name: "3.6 on Linux"
-    language: python
-    python: 3.6
-  - name: "3.7 on Linux"
-    language: python
-    python: 3.7
-  - name: "3.8 on Linux"
-    language: python
-    python: 3.8
-  - name: "3.7 on Mac"
-    os: osx
-    osx_image: xcode11.3
   # build jobs
   - name: "Mac pkg"
     stage: build
diff --git a/dvc/api.py b/dvc/api.py
index 5bf1517af..bf79b682e 100644
--- a/dvc/api.py
+++ b/dvc/api.py
@@ -34,7 +34,8 @@ def get_url(path, repo=None, rev=None, remote=None):
             raise OutputNotFoundError(path, repo)
 
         cloud = metadata.repo.cloud
-        hash_info = _repo.repo_tree.get_hash(path_info)
+        with _repo.state:
+            hash_info = _repo.repo_tree.get_hash(path_info)
         return cloud.get_url_for(remote, checksum=hash_info.value)
 
 
diff --git a/dvc/config.py b/dvc/config.py
index 50bcfddc8..a01e39291 100644
--- a/dvc/config.py
+++ b/dvc/config.py
@@ -139,6 +139,7 @@ SCHEMA = {
         Optional("analytics", default=True): Bool,
         Optional("hardlink_lock", default=False): Bool,
         Optional("no_scm", default=False): Bool,
+        Optional("autostage", default=False): Bool,
         Optional("experiments", default=False): Bool,
         Optional("check_update", default=True): Bool,
     },
diff --git a/dvc/repo/experiments/__init__.py b/dvc/repo/experiments/__init__.py
index c57a24221..2a5e18f42 100644
--- a/dvc/repo/experiments/__init__.py
+++ b/dvc/repo/experiments/__init__.py
@@ -2,6 +2,7 @@ import logging
 import os
 import re
 import tempfile
+from collections import namedtuple
 from collections.abc import Mapping
 from concurrent.futures import (
     ProcessPoolExecutor,
@@ -72,14 +73,17 @@ class Experiments:
 
     EXPERIMENTS_DIR = "experiments"
     PACKED_ARGS_FILE = "repro.dat"
-    STASH_MSG_PREFIX = "dvc-exp-"
+    STASH_MSG_PREFIX = "dvc-exp:"
     STASH_EXPERIMENT_RE = re.compile(
-        r"(?:On \(.*\): )dvc-exp-(?P<baseline_rev>[0-9a-f]+)$"
+        r"(?:On \(.*\): )"
+        r"dvc-exp:(?P<baseline_rev>[0-9a-f]+)(:(?P<branch>.+))?$"
     )
     BRANCH_RE = re.compile(
         r"^(?P<baseline_rev>[a-f0-9]{7})-(?P<exp_sha>[a-f0-9]+)$"
     )
 
+    StashEntry = namedtuple("StashEntry", ["index", "baseline_rev", "branch"])
+
     def __init__(self, repo):
         from dvc.lock import make_lock
 
@@ -144,7 +148,9 @@ class Experiments:
         for i, entry in enumerate(self.stash_reflog):
             m = self.STASH_EXPERIMENT_RE.match(entry.message)
             if m:
-                revs[entry.newhexsha] = (i, m.group("baseline_rev"))
+                revs[entry.newhexsha] = self.StashEntry(
+                    i, m.group("baseline_rev"), m.group("branch")
+                )
         return revs
 
     def _init_clone(self):
@@ -184,30 +190,54 @@ class Experiments:
             branch.set_tracking_branch(ref)
         branch.checkout()
 
-    def _stash_exp(self, *args, params: Optional[dict] = None, **kwargs):
+    def _stash_exp(
+        self,
+        *args,
+        params: Optional[dict] = None,
+        branch: Optional[str] = None,
+        allow_unchanged: Optional[bool] = False,
+        apply_workspace: Optional[bool] = True,
+        **kwargs,
+    ):
         """Stash changes from the current (parent) workspace as an experiment.
 
         Args:
             params: Optional dictionary of parameter values to be used.
                 Values take priority over any parameters specified in the
                 user's workspace.
+            branch: Optional experiment branch name. If specified, the
+                experiment will be added to `branch` instead of creating
+                a new branch.
+            allow_unchanged: Force experiment reproduction even if params are
+                unchanged from the baseline.
+            apply_workspace: Apply changes from the user workspace to the
+                experiment workspace.
         """
-        rev = self.scm.get_rev()
-
-        # patch user's workspace into experiments clone
-        tmp = tempfile.NamedTemporaryFile(delete=False).name
-        try:
-            self.repo.scm.repo.git.diff(
-                patch=True, full_index=True, binary=True, output=tmp
-            )
-            if os.path.getsize(tmp):
-                logger.debug("Patching experiment workspace")
-                self.scm.repo.git.apply(tmp)
-            elif not params:
-                # experiment matches original baseline
-                raise UnchangedExperimentError(rev)
-        finally:
-            remove(tmp)
+        if branch:
+            rev = self.scm.resolve_rev(branch)
+        else:
+            rev = self.scm.get_rev()
+
+        if apply_workspace:
+            # patch user's workspace into experiments clone
+            #
+            # TODO: patching changes into an extended branch will require
+            # propagating changes down the full branch, since the workspace
+            # patch will be based on the original branch point, not the tip
+            # of the experiment branch
+            tmp = tempfile.NamedTemporaryFile(delete=False).name
+            try:
+                self.repo.scm.repo.git.diff(
+                    patch=True, full_index=True, binary=True, output=tmp
+                )
+                if os.path.getsize(tmp):
+                    logger.debug("Patching experiment workspace")
+                    self.scm.repo.git.apply(tmp)
+                elif not params and not allow_unchanged:
+                    # experiment matches original baseline
+                    raise UnchangedExperimentError(rev)
+            finally:
+                remove(tmp)
 
         # update experiment params from command line
         if params:
@@ -219,10 +249,15 @@ class Experiments:
         # save experiment as a stash commit w/message containing baseline rev
         # (stash commits are merge commits and do not contain a parent commit
         # SHA)
-        msg = f"{self.STASH_MSG_PREFIX}{rev}"
+        msg = self._stash_msg(rev, branch)
         self.scm.repo.git.stash("push", "-m", msg)
         return self.scm.resolve_rev("stash@{0}")
 
+    def _stash_msg(self, rev, branch=None):
+        if branch:
+            return f"{self.STASH_MSG_PREFIX}{rev}:{branch}"
+        return f"{self.STASH_MSG_PREFIX}{rev}"
+
     def _pack_args(self, *args, **kwargs):
         ExperimentExecutor.pack_repro_args(self.args_file, *args, **kwargs)
         self.scm.add(self.args_file)
@@ -252,19 +287,21 @@ class Experiments:
             with modify_data(path, tree=self.exp_dvc.tree) as data:
                 _update(data, params[params_fname])
 
-    def _commit(self, exp_hash, check_exists=True, branch=True):
+    def _commit(self, exp_hash, check_exists=True, create_branch=True):
         """Commit stages as an experiment and return the commit SHA."""
         if not self.scm.is_dirty():
             raise UnchangedExperimentError(self.scm.get_rev())
 
         rev = self.scm.get_rev()
         exp_name = f"{rev[:7]}-{exp_hash}"
-        if branch:
+        if create_branch:
             if check_exists and exp_name in self.scm.list_branches():
                 logger.debug("Using existing experiment branch '%s'", exp_name)
                 return self.scm.resolve_rev(exp_name)
             self.scm.checkout(exp_name, create_new=True)
-        logger.debug("Commit new experiment branch '%s'", exp_name)
+            logger.debug("Commit new experiment branch '%s'", exp_name)
+        else:
+            logger.debug("Commit to current experiment branch")
         self.scm.repo.git.add(A=True)
         self.scm.commit(f"Add experiment {exp_name}")
         return self.scm.get_rev()
@@ -302,7 +339,14 @@ class Experiments:
         Experiment will be reproduced and checked out into the user's
         workspace.
         """
-        rev = self.repo.scm.get_rev()
+        branch = kwargs.get("branch")
+        if branch:
+            rev = self.scm.resolve_rev(branch)
+            logger.debug(
+                "Using '%s' (tip of branch '%s') as baseline", rev, branch
+            )
+        else:
+            rev = self.repo.scm.get_rev()
         self._scm_checkout(rev)
         try:
             stash_rev = self._stash_exp(*args, **kwargs)
@@ -337,13 +381,12 @@ class Experiments:
         # commit) and baseline_rev is the baseline to compare output against.
         # The final experiment commit will be branched from baseline_rev.
         if revs is None:
-            to_run = {
-                rev: baseline_rev
-                for rev, (_, baseline_rev) in stash_revs.items()
-            }
+            to_run = dict(stash_revs)
         else:
             to_run = {
-                rev: stash_revs[rev][1] if rev in stash_revs else rev
+                rev: stash_revs[rev]
+                if rev in stash_revs
+                else self.StashEntry(None, rev, None)
                 for rev in revs
             }
 
@@ -355,12 +398,13 @@ class Experiments:
         # setup executors - unstash experiment, generate executor, upload
         # contents of (unstashed) exp workspace to the executor tree
         executors = {}
-        for rev, baseline_rev in to_run.items():
-            self._scm_checkout(baseline_rev)
+        for rev, item in to_run.items():
+            self._scm_checkout(item.baseline_rev)
             self.scm.repo.git.stash("apply", rev)
             repro_args, repro_kwargs = self._unpack_args()
             executor = LocalExecutor(
-                baseline_rev,
+                item.baseline_rev,
+                branch=item.branch,
                 repro_args=repro_args,
                 repro_kwargs=repro_kwargs,
                 dvc_dir=self.dvc_dir,
@@ -417,7 +461,10 @@ class Experiments:
                 exc = future.exception()
                 if exc is None:
                     exp_hash = future.result()
-                    self._scm_checkout(executor.baseline_rev)
+                    if executor.branch:
+                        self._scm_checkout(executor.branch)
+                    else:
+                        self._scm_checkout(executor.baseline_rev)
                     try:
                         self._collect_output(executor)
                     except DownloadError:
@@ -431,7 +478,8 @@ class Experiments:
                             remove(self.args_file)
 
                     try:
-                        exp_rev = self._commit(exp_hash)
+                        branch = not executor.branch
+                        exp_rev = self._commit(exp_hash, create_branch=branch)
                     except UnchangedExperimentError:
                         logger.debug(
                             "Experiment '%s' identical to baseline '%s'",
@@ -517,12 +565,12 @@ class Experiments:
 
         from dvc.repo.checkout import checkout as dvc_checkout
 
-        self._check_baseline(rev)
+        baseline_rev = self._check_baseline(rev)
         self._scm_checkout(rev)
 
         tmp = tempfile.NamedTemporaryFile(delete=False).name
         self.scm.repo.head.commit.diff(
-            "HEAD~1", patch=True, full_index=True, binary=True, output=tmp
+            baseline_rev, patch=True, full_index=True, binary=True, output=tmp
         )
 
         dirty = self.repo.scm.is_dirty()
@@ -549,11 +597,14 @@ class Experiments:
 
     def _check_baseline(self, exp_rev):
         baseline_sha = self.repo.scm.get_rev()
-        exp_commit = self.scm.repo.rev_parse(exp_rev)
-        parent = first(exp_commit.parents)
-        if parent is not None and parent.hexsha == baseline_sha:
-            return
-        raise BaselineMismatchError(parent, baseline_sha)
+        exp_baseline = self._get_baseline(exp_rev)
+        if exp_baseline is None:
+            # if we can't tell from branch name, fall back to parent commit
+            exp_commit = self.scm.repo.rev_parse(exp_rev)
+            exp_baseline = first(exp_commit.parents).hexsha
+        if exp_baseline == baseline_sha:
+            return exp_baseline
+        raise BaselineMismatchError(exp_baseline, baseline_sha)
 
     def _unstash_workspace(self):
         # Essentially we want `git stash pop` with `-X ours` merge strategy
@@ -581,6 +632,9 @@ class Experiments:
     @scm_locked
     def get_baseline(self, rev):
         """Return the baseline rev for an experiment rev."""
+        return self._get_baseline(rev)
+
+    def _get_baseline(self, rev):
         from git.exc import GitCommandError
 
         rev = self.scm.resolve_rev(rev)
@@ -588,16 +642,28 @@ class Experiments:
             name = self.scm.repo.git.name_rev(rev, name_only=True)
         except GitCommandError:
             return None
-        if not name:
-            return None
         if name in ("undefined", "stash"):
-            _, baseline = self.stash_revs.get(rev, (None, None))
-            return baseline
+            entry = self.stash_revs.get(rev)
+            if entry:
+                return entry.baseline_rev
+            return None
         m = self.BRANCH_RE.match(name)
         if m:
             return self.scm.resolve_rev(m.group("baseline_rev"))
         return None
 
+    def _get_branch_containing(self, rev):
+        from git.exc import GitCommandError
+
+        if self.scm.repo.head.is_detached:
+            self._checkout_default_branch()
+        try:
+            name = self.scm.repo.git.branch(contains=rev)
+            return name.rsplit("/")[-1].strip()
+        except GitCommandError:
+            pass
+        return None
+
     def checkout(self, *args, **kwargs):
         from dvc.repo.experiments.checkout import checkout
 
diff --git a/dvc/repo/experiments/executor.py b/dvc/repo/experiments/executor.py
index 21ce60142..725c01e5f 100644
--- a/dvc/repo/experiments/executor.py
+++ b/dvc/repo/experiments/executor.py
@@ -2,7 +2,7 @@ import logging
 import os
 import pickle
 from tempfile import TemporaryDirectory
-from typing import Iterable
+from typing import Iterable, Optional
 
 from funcy import cached_property
 
@@ -22,12 +22,16 @@ class ExperimentExecutor:
         baseline_rev: baseline revision that this experiment is derived from.
 
     Optional keyword args:
+        branch: Existing git branch for this experiment.
         repro_args: Args to be passed into reproduce.
         repro_kwargs: Keyword args to be passed into reproduce.
     """
 
-    def __init__(self, baseline_rev: str, **kwargs):
+    def __init__(
+        self, baseline_rev: str, branch: Optional[str] = None, **kwargs,
+    ):
         self.baseline_rev = baseline_rev
+        self.branch = branch
         self.repro_args = kwargs.pop("repro_args", [])
         self.repro_kwargs = kwargs.pop("repro_kwargs", {})
 
diff --git a/dvc/repo/experiments/show.py b/dvc/repo/experiments/show.py
index 6a2a6d50e..1175d4570 100644
--- a/dvc/repo/experiments/show.py
+++ b/dvc/repo/experiments/show.py
@@ -87,12 +87,12 @@ def show(
                 res[rev][exp_rev] = experiment
 
     # collect queued (not yet reproduced) experiments
-    for stash_rev, (_, baseline_rev) in repo.experiments.stash_revs.items():
-        if baseline_rev in revs:
+    for stash_rev, entry in repo.experiments.stash_revs.items():
+        if entry.baseline_rev in revs:
             with repo.experiments.chdir():
                 experiment = _collect_experiment(
                     repo.experiments.exp_dvc, stash_rev, stash=True
                 )
-            res[baseline_rev][stash_rev] = experiment
+            res[entry.baseline_rev][stash_rev] = experiment
 
     return res
diff --git a/dvc/repo/metrics/show.py b/dvc/repo/metrics/show.py
index 0913a5971..d0e7b07ae 100644
--- a/dvc/repo/metrics/show.py
+++ b/dvc/repo/metrics/show.py
@@ -11,28 +11,31 @@ logger = logging.getLogger(__name__)
 
 
 def _collect_metrics(repo, targets, recursive):
-    metrics = set()
 
+    if targets:
+        target_infos = [
+            PathInfo(os.path.abspath(target)) for target in targets
+        ]
+        tree = RepoTree(repo)
+
+        rec_files = []
+        if recursive:
+            for target_info in target_infos:
+                if tree.isdir(target_info):
+                    rec_files.extend(list(tree.walk_files(target_info)))
+
+        result = [t for t in target_infos if tree.isfile(t)]
+        result.extend(rec_files)
+
+        return result
+
+    metrics = set()
     for stage in repo.stages:
         for out in stage.outs:
             if not out.metric:
                 continue
-
             metrics.add(out.path_info)
-
-    if not targets:
-        return list(metrics)
-
-    target_infos = [PathInfo(os.path.abspath(target)) for target in targets]
-
-    def _filter(path_info):
-        for info in target_infos:
-            func = path_info.isin_or_eq if recursive else path_info.__eq__
-            if func(info):
-                return True
-        return False
-
-    return list(filter(_filter, metrics))
+    return list(metrics)
 
 
 def _extract_metrics(metrics, path, rev):
diff --git a/dvc/repo/scm_context.py b/dvc/repo/scm_context.py
index c0b0ffb5b..ec9585bb7 100644
--- a/dvc/repo/scm_context.py
+++ b/dvc/repo/scm_context.py
@@ -3,7 +3,11 @@ def scm_context(method):
         try:
             result = method(repo, *args, **kw)
             repo.scm.reset_ignores()
-            repo.scm.remind_to_track()
+            autostage = repo.config.get("core", {}).get("autostage", False)
+            if autostage:
+                repo.scm.track_changed_files()
+            else:
+                repo.scm.remind_to_track()
             repo.scm.reset_tracked_files()
             return result
         except Exception:
diff --git a/dvc/scm/base.py b/dvc/scm/base.py
index 1bde8f744..699c602e6 100644
--- a/dvc/scm/base.py
+++ b/dvc/scm/base.py
@@ -154,6 +154,11 @@ class Base:
         Method to remind user to track newly created files handled by scm
         """
 
+    def track_changed_files(self):
+        """
+        Method to stage files that have changed
+        """
+
     def track_file(self, path):
         """
         Method to add file to mechanism that will remind user
diff --git a/dvc/scm/git.py b/dvc/scm/git.py
index b777488ac..a3317218e 100644
--- a/dvc/scm/git.py
+++ b/dvc/scm/git.py
@@ -381,6 +381,12 @@ class Git(Base):
             "\tgit add {files}".format(files=files)
         )
 
+    def track_changed_files(self):
+        if not self.files_to_track:
+            return
+
+        self.add(self.files_to_track)
+
     def track_file(self, path):
         self.files_to_track.add(path)
 
diff --git a/dvc/tree/azure.py b/dvc/tree/azure.py
index 1aaa16b7a..3a14aac33 100644
--- a/dvc/tree/azure.py
+++ b/dvc/tree/azure.py
@@ -67,7 +67,10 @@ class AzureTree(BaseTree):
     @cached_property
     def blob_service(self):
         # pylint: disable=no-name-in-module
-        from azure.core.exceptions import ResourceNotFoundError
+        from azure.core.exceptions import (
+            HttpResponseError,
+            ResourceNotFoundError,
+        )
         from azure.storage.blob import BlobServiceClient
 
         logger.debug(f"URL {self.path_info}")
@@ -92,6 +95,10 @@ class AzureTree(BaseTree):
             container_client.get_container_properties()
         except ResourceNotFoundError:
             container_client.create_container()
+        except HttpResponseError as exc:
+            # client may not have account-level privileges
+            if exc.status_code != 403:
+                raise
 
         return blob_service
 
diff --git a/dvc/tree/gdrive.py b/dvc/tree/gdrive.py
index 12779a5c3..302a5ccda 100644
--- a/dvc/tree/gdrive.py
+++ b/dvc/tree/gdrive.py
@@ -366,10 +366,7 @@ class GDriveTree(BaseTree):
                 total=total,
                 disable=no_progress_bar,
             ) as wrapped:
-                # PyDrive doesn't like content property setting for empty files
-                # https://github.com/gsuitedevs/PyDrive/issues/121
-                if total:
-                    item.content = wrapped
+                item.content = wrapped
                 item.Upload()
         return item
 
@@ -381,9 +378,6 @@ class GDriveTree(BaseTree):
         # it does not create a file on the remote
         gdrive_file = self._drive.CreateFile(param)
 
-        gdrive_file.FetchMetadata(fields="fileSize")
-        size = int(gdrive_file["fileSize"])
-
         with Tqdm(
             desc=progress_desc,
             disable=no_progress_bar,
@@ -391,13 +385,7 @@ class GDriveTree(BaseTree):
             # explicit `bar_format` as `total` will be set by `update_to`
             bar_format=Tqdm.BAR_FMT_DEFAULT,
         ) as pbar:
-            if size:
-                gdrive_file.GetContentFile(to_file, callback=pbar.update_to)
-            else:
-                # PyDrive doesn't like downloading empty files
-                # https://github.com/iterative/dvc/issues/4286
-                with open(to_file, "w"):
-                    pass
+            gdrive_file.GetContentFile(to_file, callback=pbar.update_to)
 
     @contextmanager
     @_gdrive_retry
diff --git a/dvc/version.py b/dvc/version.py
index b3b458f85..5f74f0b12 100644
--- a/dvc/version.py
+++ b/dvc/version.py
@@ -6,7 +6,7 @@
 import os
 import subprocess
 
-_BASE_VERSION = "1.7.2"
+_BASE_VERSION = "1.7.4"
 
 
 def _generate_version(base_version):
diff --git a/setup.py b/setup.py
index ae7200af3..3611a3adb 100644
--- a/setup.py
+++ b/setup.py
@@ -88,7 +88,7 @@ install_requires = [
 # Extra dependencies for remote integrations
 
 gs = ["google-cloud-storage==1.19.0"]
-gdrive = ["pydrive2>=1.6.1", "six >= 1.13.0"]
+gdrive = ["pydrive2>=1.6.2", "six >= 1.13.0"]
 s3 = ["boto3>=1.9.201"]
 azure = ["azure-storage-blob>=12.0", "knack"]
 oss = ["oss2==2.6.1"]
@@ -107,6 +107,7 @@ tests_requirements = [
     "wheel>=0.31.1",
     # Test requirements:
     "pytest>=6.0.1",
+    "pytest-cov",
     "pytest-docker>=0.7.2",
     "pytest-timeout>=1.3.3",
     "pytest-cov>=2.6.1",

migrate from travis to github actions
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
new file mode 100644
index 000000000..2ac73bbb9
--- /dev/null
+++ b/.github/workflows/tests.yaml
@@ -0,0 +1,42 @@
+name: Tests build
+
+on: [push, pull_request]
+
+env:
+  DVC_TEST: "true"
+  HOMEBREW_NO_AUTO_UPDATE: 1
+  SHELL: /bin/bash
+
+jobs:
+  tests:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-18.04, windows-2019, macos-10.15]
+        pyv: [3.6, 3.7, 3.8]
+        exclude:
+          - os: windows-2019
+            pyv: 3.8
+    steps:
+    - uses: actions/checkout@v2.3.2
+    - name: Set up Python
+      uses: actions/setup-python@v2.1.2
+      with:
+        python-version: ${{ matrix.pyv }}
+    - name: install
+      run: |
+        pip install ".[all,tests]"
+    - name: setup git
+      run: |
+        git config --global user.email "dvctester@example.com"
+        git config --global user.name "DVC Tester"
+    - name: run tests
+      run: python -m tests -n=2 --cov-report=xml
+    - name: upload coverage report
+      if: github.event.name == 'push'
+      uses: codecov/codecov-action@v1.0.13
+      with:
+        file: ./coverage.xml
+        fail_ci_if_error: true
+
+
diff --git a/tests/func/experiments/test_experiments.py b/tests/func/experiments/test_experiments.py
index 7ebf71a3f..b11a277f5 100644
--- a/tests/func/experiments/test_experiments.py
+++ b/tests/func/experiments/test_experiments.py
@@ -100,3 +100,39 @@ def test_get_baseline(tmp_dir, scm, dvc):
         stage.addressing, experiment=True, params=["foo=3"], queue=True
     )
     assert dvc.experiments.get_baseline("stash@{0}") == expected
+
+
+def test_extend_branch(tmp_dir, scm, dvc):
+    tmp_dir.gen("copy.py", COPY_SCRIPT)
+    tmp_dir.gen("params.yaml", "foo: 1")
+    stage = dvc.run(
+        cmd="python copy.py params.yaml metrics.yaml",
+        metrics_no_cache=["metrics.yaml"],
+        params=["foo"],
+        name="copy-file",
+    )
+    scm.add(["dvc.yaml", "dvc.lock", "copy.py", "params.yaml", "metrics.yaml"])
+    scm.commit("init")
+
+    dvc.reproduce(stage.addressing, experiment=True, params=["foo=2"])
+    exp_a = dvc.experiments.scm.get_rev()
+    exp_branch = dvc.experiments._get_branch_containing(exp_a)
+
+    dvc.reproduce(
+        stage.addressing,
+        experiment=True,
+        params=["foo=3"],
+        branch=exp_branch,
+        apply_workspace=False,
+    )
+    exp_b = dvc.experiments.scm.get_rev()
+
+    assert dvc.experiments._get_branch_containing(exp_b) == exp_branch
+
+    dvc.experiments.checkout(exp_a)
+    assert (tmp_dir / "params.yaml").read_text().strip() == "foo: 2"
+    assert (tmp_dir / "metrics.yaml").read_text().strip() == "foo: 2"
+
+    dvc.experiments.checkout(exp_b)
+    assert (tmp_dir / "params.yaml").read_text().strip() == "foo: 3"
+    assert (tmp_dir / "metrics.yaml").read_text().strip() == "foo: 3"
diff --git a/tests/func/metrics/test_diff.py b/tests/func/metrics/test_diff.py
index c7ea99bf3..8d00c2d81 100644
--- a/tests/func/metrics/test_diff.py
+++ b/tests/func/metrics/test_diff.py
@@ -195,3 +195,17 @@ def test_metrics_diff_cli(tmp_dir, scm, dvc, run_copy_metrics, caplog, capsys):
         "Path    Metric    Old      New      Change\n"
         "m.yaml  foo       1.23457  3.45679  2.22222"
     )
+
+
+def test_metrics_diff_non_metrics(tmp_dir, scm, dvc):
+    def _gen(val):
+        tmp_dir.scm_gen({"some_file.yaml": f"foo: {val}"}, commit=str(val))
+
+    _gen(1)
+    _gen(2)
+    _gen(3)
+
+    result = dvc.metrics.diff(targets=["some_file.yaml"], a_rev="HEAD~2")
+    assert result == {
+        "some_file.yaml": {"foo": {"old": 1, "new": 3, "diff": 2}}
+    }
diff --git a/tests/func/metrics/test_show.py b/tests/func/metrics/test_show.py
index 69414b4e9..bc93f4aef 100644
--- a/tests/func/metrics/test_show.py
+++ b/tests/func/metrics/test_show.py
@@ -110,3 +110,41 @@ def test_missing_cache(tmp_dir, dvc, run_copy_metrics):
     remove(stage.outs[0].cache_path)
 
     assert dvc.metrics.show() == {"": {"metrics.yaml": 1.1}}
+
+
+def test_show_non_metric(tmp_dir, dvc):
+    tmp_dir.gen("metrics.yaml", "foo: 1.1")
+
+    assert dvc.metrics.show(targets=["metrics.yaml"]) == {
+        "": {"metrics.yaml": {"foo": 1.1}}
+    }
+
+
+def test_show_non_metric_branch(tmp_dir, scm, dvc):
+    tmp_dir.scm_gen("metrics.yaml", "foo: 1.1", commit="init")
+    with tmp_dir.branch("branch", new=True):
+        tmp_dir.scm_gen("metrics.yaml", "foo: 2.2", commit="other")
+
+    assert dvc.metrics.show(targets=["metrics.yaml"], revs=["branch"]) == {
+        "workspace": {"metrics.yaml": {"foo": 1.1}},
+        "branch": {"metrics.yaml": {"foo": 2.2}},
+    }
+
+
+def test_non_metric_and_recurisve_show(tmp_dir, dvc, run_copy_metrics):
+    tmp_dir.gen(
+        {"metrics_t.yaml": "foo: 1.1", "metrics": {"metric1.yaml": "bar: 1.2"}}
+    )
+
+    metric2 = os.fspath(tmp_dir / "metrics" / "metric2.yaml")
+    run_copy_metrics("metrics_t.yaml", metric2, metrics=[metric2])
+
+    assert dvc.metrics.show(
+        targets=["metrics_t.yaml", "metrics"], recursive=True
+    ) == {
+        "": {
+            os.path.join("metrics", "metric1.yaml"): {"bar": 1.2},
+            os.path.join("metrics", "metric2.yaml"): {"foo": 1.1},
+            "metrics_t.yaml": {"foo": 1.1},
+        }
+    }
diff --git a/tests/func/test_add.py b/tests/func/test_add.py
index 9b3003f59..18d48a4d4 100644
--- a/tests/func/test_add.py
+++ b/tests/func/test_add.py
@@ -208,9 +208,14 @@ def test_add_file_in_dir(tmp_dir, dvc):
         ),
         (pytest.lazy_fixture("gs"), "md5", "8c7dd922ad47494fc02c388e12c00eac"),
         (
-            pytest.lazy_fixture("hdfs"),
-            "checksum",
-            "000002000000000000000000a86fe4d846edc1bf4c355cb6112f141e",
+            pytest.param(
+                pytest.lazy_fixture("hdfs"),
+                "checksum",
+                "000002000000000000000000a86fe4d846edc1bf4c355cb6112f141e",
+                marks=pytest.mark.xfail(
+                    reason="https://github.com/iterative/dvc/issues/4418"
+                ),
+            )
         ),
     ],
     indirect=["workspace"],
diff --git a/tests/func/test_api.py b/tests/func/test_api.py
index a736103a1..3f1579638 100644
--- a/tests/func/test_api.py
+++ b/tests/func/test_api.py
@@ -1,9 +1,10 @@
 import os
+import platform
 
 import pytest
 
 from dvc import api
-from dvc.exceptions import FileMissingError, OutputNotFoundError
+from dvc.exceptions import DvcException, FileMissingError, OutputNotFoundError
 from dvc.path_info import URLInfo
 from dvc.utils.fs import remove
 from tests.unit.tree.test_repo import make_subrepo
@@ -15,10 +16,16 @@ cloud_names = [
     "gdrive",
     "oss",
     "ssh",
-    "hdfs",
     "http",
 ]
-clouds = [pytest.lazy_fixture(cloud) for cloud in cloud_names]
+clouds = [pytest.lazy_fixture(cloud) for cloud in cloud_names] + [
+    pytest.param(
+        pytest.lazy_fixture("hdfs"),
+        marks=pytest.mark.xfail(
+            reason="https://github.com/iterative/dvc/issues/4418"
+        ),
+    )
+]
 all_clouds = [pytest.lazy_fixture("local_cloud")] + clouds
 
 # `lazy_fixture` is confusing pylint, pylint: disable=unused-argument
@@ -69,17 +76,26 @@ def test_open(tmp_dir, dvc, remote):
 @pytest.mark.parametrize(
     "cloud",
     [
-        pytest.lazy_fixture(cloud)
-        for cloud in [
-            "real_s3",  # NOTE: moto's s3 fails in some tests
-            "gs",
-            "azure",
-            "gdrive",
-            "oss",
-            "ssh",
-            "hdfs",
-            "http",
-        ]
+        *[
+            pytest.lazy_fixture(cloud)
+            for cloud in [
+                "real_s3",  # NOTE: moto's s3 fails in some tests
+                "gs",
+                "azure",
+                "gdrive",
+                "oss",
+                "hdfs",
+                "http",
+            ]
+        ],
+        pytest.param(
+            pytest.lazy_fixture("ssh"),
+            marks=pytest.mark.xfail(
+                raises=DvcException,
+                condition=platform.system() == "Windows",
+                reason="https://github.com/iterative/dvc/issues/4418",
+            ),
+        ),
     ],
 )
 def test_open_external(erepo_dir, cloud):
@@ -210,6 +226,9 @@ def test_get_url_granular(tmp_dir, dvc, s3):
         {"dir": {"foo": "foo", "bar": "bar", "nested": {"file": "file"}}}
     )
 
+    expected_url = URLInfo(s3.url) / "5f/c28ea78987408341668eba6525ebd1.dir"
+    assert api.get_url("dir") == expected_url
+
     expected_url = URLInfo(s3.url) / "ac/bd18db4cc2f85cedef654fccc4a4d8"
     assert api.get_url("dir/foo") == expected_url
 
diff --git a/tests/func/test_cache.py b/tests/func/test_cache.py
index a28f3a4f1..36e50ed22 100644
--- a/tests/func/test_cache.py
+++ b/tests/func/test_cache.py
@@ -1,4 +1,5 @@
 import os
+import platform
 import stat
 
 import configobj
@@ -163,6 +164,10 @@ class TestCmdCacheDir(TestDvc):
         config = configobj.ConfigObj(self.dvc.config.files["repo"])
         self.assertEqual(config["cache"]["dir"], dname.replace("\\", "/"))
 
+    @pytest.mark.skipif(
+        platform.system() == "Darwin",
+        reason="https://github.com/iterative/dvc/issues/4418",
+    )
     def test_relative_path(self):
         tmpdir = self.mkdtemp()
         dname = relpath(tmpdir)
diff --git a/tests/func/test_import_url.py b/tests/func/test_import_url.py
index 4af804deb..ae37bf21f 100644
--- a/tests/func/test_import_url.py
+++ b/tests/func/test_import_url.py
@@ -121,7 +121,12 @@ def test_import_url_with_no_exec(tmp_dir, dvc, erepo_dir):
         pytest.lazy_fixture("local_cloud"),
         pytest.lazy_fixture("s3"),
         pytest.lazy_fixture("gs"),
-        pytest.lazy_fixture("hdfs"),
+        pytest.param(
+            pytest.lazy_fixture("hdfs"),
+            marks=pytest.mark.xfail(
+                reason="https://github.com/iterative/dvc/issues/4418"
+            ),
+        ),
         pytest.param(
             pytest.lazy_fixture("ssh"),
             marks=pytest.mark.skipif(
diff --git a/tests/func/test_run_multistage.py b/tests/func/test_run_multistage.py
index e7d49a63d..01986372f 100644
--- a/tests/func/test_run_multistage.py
+++ b/tests/func/test_run_multistage.py
@@ -386,10 +386,15 @@ def test_run_overwrite_preserves_meta_and_comment(tmp_dir, dvc, run_copy):
             "37b51d194a7513e45b56f6524f2d51f2",
         ),
         (
-            pytest.lazy_fixture("hdfs"),
-            "checksum",
-            "0000020000000000000000003dba826b9be9c6a8e2f8310a770555c4",
-            "00000200000000000000000075433c81259d3c38e364b348af52e84d",
+            pytest.param(
+                pytest.lazy_fixture("hdfs"),
+                "checksum",
+                "0000020000000000000000003dba826b9be9c6a8e2f8310a770555c4",
+                "00000200000000000000000075433c81259d3c38e364b348af52e84d",
+                marks=pytest.mark.xfail(
+                    reason="https://github.com/iterative/dvc/issues/4418"
+                ),
+            )
         ),
     ],
     indirect=["workspace"],
diff --git a/tests/func/test_update.py b/tests/func/test_update.py
index 6a21e4967..129660b7c 100644
--- a/tests/func/test_update.py
+++ b/tests/func/test_update.py
@@ -6,7 +6,18 @@ from dvc.dvcfile import Dvcfile
 from tests.unit.tree.test_repo import make_subrepo
 
 
-@pytest.mark.parametrize("cached", [True, False])
+@pytest.mark.parametrize(
+    "cached",
+    [
+        True,
+        pytest.param(
+            False,
+            marks=pytest.mark.skip(
+                reason="https://github.com/iterative/dvc/issues/4418"
+            ),
+        ),
+    ],
+)
 def test_update_import(tmp_dir, dvc, erepo_dir, cached):
     gen = erepo_dir.dvc_gen if cached else erepo_dir.scm_gen
 
diff --git a/tests/remotes/__init__.py b/tests/remotes/__init__.py
index 4cd648f7e..e5e4debf0 100644
--- a/tests/remotes/__init__.py
+++ b/tests/remotes/__init__.py
@@ -40,8 +40,8 @@ def docker():
     import os
 
     # See https://travis-ci.community/t/docker-linux-containers-on-windows/301
-    if os.environ.get("TRAVIS") and os.name == "nt":
-        pytest.skip("disabled for Windows on Travis")
+    if os.environ.get("CI") and os.name == "nt":
+        pytest.skip("disabled for Windows on Github Actions")
 
     try:
         subprocess.check_output("docker ps", shell=True)
diff --git a/tests/unit/output/test_local.py b/tests/unit/output/test_local.py
index c2a2d07c5..236fe50ed 100644
--- a/tests/unit/output/test_local.py
+++ b/tests/unit/output/test_local.py
@@ -1,5 +1,7 @@
 import os
+import platform
 
+import pytest
 from mock import patch
 
 from dvc.cache.local import LocalCache
@@ -25,6 +27,10 @@ class TestLocalOutput(TestDvc):
                 o.save()
 
 
+@pytest.mark.skipif(
+    platform.system() == "Windows",
+    reason="https://github.com/iterative/dvc/issues/4418",
+)
 def test_str_workdir_outside_repo(erepo_dir):
     stage = Stage(erepo_dir.dvc)
     output = LocalOutput(stage, "path", cache=False)
diff --git a/tests/unit/scm/test_scm.py b/tests/unit/scm/test_scm.py
index 4fb590fc5..1121d4df5 100644
--- a/tests/unit/scm/test_scm.py
+++ b/tests/unit/scm/test_scm.py
@@ -17,6 +17,7 @@ class TestScmContext(TestCase):
         method = mock.Mock()
         wrapped = scm_context(method)
 
+        self.repo_mock.configure_mock(config={})
         wrapped(self.repo_mock)
 
         self.assertEqual(1, method.call_count)
@@ -25,6 +26,20 @@ class TestScmContext(TestCase):
 
         self.assertEqual(0, self.scm_mock.cleanup_ignores.call_count)
 
+    def test_should_check_autostage(self):
+        method = mock.Mock()
+        wrapped = scm_context(method)
+
+        config_autostage_attrs = {"config": {"core": {"autostage": True}}}
+        self.repo_mock.configure_mock(**config_autostage_attrs)
+        wrapped(self.repo_mock)
+
+        self.assertEqual(1, method.call_count)
+        self.assertEqual(1, self.scm_mock.reset_ignores.call_count)
+        self.assertEqual(1, self.scm_mock.track_changed_files.call_count)
+
+        self.assertEqual(0, self.scm_mock.cleanup_ignores.call_count)
+
     def test_should_throw_and_cleanup(self):
         method = mock.Mock(side_effect=Exception("some problem"))
         wrapped = scm_context(method)

pip install -e .[all]
parse_log_pytest
pytest
pytest pytest-cov pytest-xdist pytest-mock pytest-lazy-fixture flaky mock xmltodict awscli google-compute-engine Pygments collective.checkdocs flake8 psutil flake8-docstrings pydocstyle jaraco.windows mock-ssh-server moto rangehttpserver beautifulsoup4 flake8-bugbear flake8-comprehensions flake8-string-format pylint pylint-pytest pylint-plugin-utils wget filelock
apt-get update apt-get install -y gcc
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aliyun-python-sdk-core==2.16.0
aliyun-python-sdk-core-v3==2.13.33
aliyun-python-sdk-kms==2.16.5
appdirs==1.4.4
argcomplete==3.1.2
astroid==2.15.8
atpublic==3.1.2
attrs @ file:///croot/attrs_1668696182826/work
autocommand==2.2.2
awscli==1.31.13
azure-core==1.30.1
azure-storage-blob==12.19.1
bcrypt==4.2.1
beautifulsoup4==4.13.3
boto==2.49.0
boto3==1.33.13
botocore==1.33.13
cachetools==4.2.4
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
charset-normalizer==3.4.1
collective.checkdocs==0.2
colorama==0.4.4
configobj==5.0.9
coverage==7.2.7
crcmod==1.7
cryptography==44.0.2
decorator==5.1.1
dictdiffer==0.9.0
dill==0.3.7
distro==1.9.0
docutils==0.16
dpath==2.2.0
-e git+https://github.com/iterative/dvc.git@de768375ed72ce171daad91c38beb7af387c2cf2#egg=dvc
execnet==2.0.2
filelock==3.12.2
flake8==5.0.4
flake8-bugbear==23.3.12
flake8-comprehensions==3.13.0
flake8-docstrings==1.7.0
flake8-string-format==0.3.0
flaky==3.8.1
flatten-dict==0.4.2
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
flufl.lock==3.2
funcy==2.0
future==1.0.0
gitdb==4.0.12
GitPython==3.1.44
google-api-core==2.10.2
google-api-python-client==2.166.0
google-auth==1.35.0
google-auth-httplib2==0.2.0
google-cloud-core==1.7.3
google-cloud-storage==1.19.0
google-compute-engine==2.8.13
google-crc32c==1.5.0
google-resumable-media==2.7.2
googleapis-common-protos==1.69.2
grandalf==0.6
httplib2==0.22.0
idna==3.10
importlib-metadata==4.2.0
importlib-resources==5.12.0
inflect==6.0.5
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isodate==0.7.2
isort==5.11.5
jaraco.classes==3.2.3
jaraco.collections==4.2.0
jaraco.context==4.3.0
jaraco.functools==3.7.0
jaraco.structures==2.1.0
jaraco.text==3.11.1
jaraco.ui==2.3.0
jaraco.windows==5.7.0
Jinja2==3.1.6
jmespath==0.10.0
jsonpath-ng==1.7.0
knack==0.12.0
lazy-object-proxy==1.9.0
lxml==5.3.1
markdown-it-py==2.2.0
MarkupSafe==2.1.5
mccabe==0.7.0
mdurl==0.1.2
mock==5.2.0
mock-ssh-server==0.9.1
more-itertools==9.1.0
moto==4.2.14
nanotime==0.5.2
networkx==2.4
numpy==1.21.6
oauth2client==4.1.3
oss2==2.6.1
packaging @ file:///croot/packaging_1671697413597/work
paramiko==3.5.1
path==16.6.0
pathspec==0.11.2
platformdirs==4.0.0
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
ply==3.11
protobuf==4.24.4
psutil==7.0.0
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyarrow==12.0.1
pyasn1==0.5.1
pyasn1-modules==0.3.0
pycodestyle==2.9.1
pycparser==2.21
pycryptodome==3.22.0
pydantic==1.10.21
pydocstyle==6.3.0
pydot==2.0.0
PyDrive2==1.15.4
pyflakes==2.5.0
Pygments==2.17.2
pygtrie==2.3.2
pylint==2.17.7
pylint-plugin-utils==0.8.2
pylint-pytest==1.1.8
PyNaCl==1.5.0
pyOpenSSL==25.0.0
pyparsing==3.1.4
pytest==7.1.2
pytest-cov==4.1.0
pytest-lazy-fixture==0.6.3
pytest-mock==3.11.1
pytest-xdist==3.5.0
python-dateutil==2.9.0.post0
PyYAML==6.0.1
rangehttpserver==1.4.0
requests==2.31.0
responses==0.23.3
rich==13.8.1
rsa==4.7.2
ruamel.yaml==0.18.10
ruamel.yaml.clib==0.2.8
s3transfer==0.8.2
shortuuid==1.0.13
shtab==1.7.1
six==1.17.0
smmap==5.0.2
snowballstemmer==2.2.0
soupsieve==2.4.1
tabulate==0.9.0
toml==0.10.2
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
tomlkit==0.12.5
tqdm==4.67.1
typed-ast==1.5.5
types-PyYAML==6.0.12.12
typing_extensions==4.7.1
uritemplate==4.1.1
urllib3==1.26.20
voluptuous==0.14.1
webdavclient3==3.14.6
Werkzeug==2.2.3
wget==3.2
wrapt==1.16.0
xmltodict==0.14.2
zc.lockfile==3.0.post1
zipp @ file:///croot/zipp_1672387121353/work

name: dvc
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
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_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=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aliyun-python-sdk-core==2.16.0
      - aliyun-python-sdk-core-v3==2.13.33
      - aliyun-python-sdk-kms==2.16.5
      - appdirs==1.4.4
      - argcomplete==3.1.2
      - astroid==2.15.8
      - atpublic==3.1.2
      - autocommand==2.2.2
      - awscli==1.31.13
      - azure-core==1.30.1
      - azure-storage-blob==12.19.1
      - bcrypt==4.2.1
      - beautifulsoup4==4.13.3
      - boto==2.49.0
      - boto3==1.33.13
      - botocore==1.33.13
      - cachetools==4.2.4
      - cffi==1.15.1
      - charset-normalizer==3.4.1
      - collective-checkdocs==0.2
      - colorama==0.4.4
      - configobj==5.0.9
      - coverage==7.2.7
      - crcmod==1.7
      - cryptography==44.0.2
      - decorator==5.1.1
      - dictdiffer==0.9.0
      - dill==0.3.7
      - distro==1.9.0
      - docutils==0.16
      - dpath==2.2.0
      - dvc==1.7.2
      - execnet==2.0.2
      - filelock==3.12.2
      - flake8==5.0.4
      - flake8-bugbear==23.3.12
      - flake8-comprehensions==3.13.0
      - flake8-docstrings==1.7.0
      - flake8-string-format==0.3.0
      - flaky==3.8.1
      - flatten-dict==0.4.2
      - flufl-lock==3.2
      - funcy==2.0
      - future==1.0.0
      - gitdb==4.0.12
      - gitpython==3.1.44
      - google-api-core==2.10.2
      - google-api-python-client==2.166.0
      - google-auth==1.35.0
      - google-auth-httplib2==0.2.0
      - google-cloud-core==1.7.3
      - google-cloud-storage==1.19.0
      - google-compute-engine==2.8.13
      - google-crc32c==1.5.0
      - google-resumable-media==2.7.2
      - googleapis-common-protos==1.69.2
      - grandalf==0.6
      - httplib2==0.22.0
      - idna==3.10
      - importlib-metadata==4.2.0
      - importlib-resources==5.12.0
      - inflect==6.0.5
      - isodate==0.7.2
      - isort==5.11.5
      - jaraco-classes==3.2.3
      - jaraco-collections==4.2.0
      - jaraco-context==4.3.0
      - jaraco-functools==3.7.0
      - jaraco-structures==2.1.0
      - jaraco-text==3.11.1
      - jaraco-ui==2.3.0
      - jaraco-windows==5.7.0
      - jinja2==3.1.6
      - jmespath==0.10.0
      - jsonpath-ng==1.7.0
      - knack==0.12.0
      - lazy-object-proxy==1.9.0
      - lxml==5.3.1
      - markdown-it-py==2.2.0
      - markupsafe==2.1.5
      - mccabe==0.7.0
      - mdurl==0.1.2
      - mock==5.2.0
      - mock-ssh-server==0.9.1
      - more-itertools==9.1.0
      - moto==4.2.14
      - nanotime==0.5.2
      - networkx==2.4
      - numpy==1.21.6
      - oauth2client==4.1.3
      - oss2==2.6.1
      - paramiko==3.5.1
      - path==16.6.0
      - pathspec==0.11.2
      - platformdirs==4.0.0
      - ply==3.11
      - protobuf==4.24.4
      - psutil==7.0.0
      - pyarrow==12.0.1
      - pyasn1==0.5.1
      - pyasn1-modules==0.3.0
      - pycodestyle==2.9.1
      - pycparser==2.21
      - pycryptodome==3.22.0
      - pydantic==1.10.21
      - pydocstyle==6.3.0
      - pydot==2.0.0
      - pydrive2==1.15.4
      - pyflakes==2.5.0
      - pygments==2.17.2
      - pygtrie==2.3.2
      - pylint==2.17.7
      - pylint-plugin-utils==0.8.2
      - pylint-pytest==1.1.8
      - pynacl==1.5.0
      - pyopenssl==25.0.0
      - pyparsing==3.1.4
      - pytest-cov==4.1.0
      - pytest-lazy-fixture==0.6.3
      - pytest-mock==3.11.1
      - pytest-xdist==3.5.0
      - python-dateutil==2.9.0.post0
      - pyyaml==6.0.1
      - rangehttpserver==1.4.0
      - requests==2.31.0
      - responses==0.23.3
      - rich==13.8.1
      - rsa==4.7.2
      - ruamel-yaml==0.18.10
      - ruamel-yaml-clib==0.2.8
      - s3transfer==0.8.2
      - shortuuid==1.0.13
      - shtab==1.7.1
      - six==1.17.0
      - smmap==5.0.2
      - snowballstemmer==2.2.0
      - soupsieve==2.4.1
      - tabulate==0.9.0
      - toml==0.10.2
      - tomlkit==0.12.5
      - tqdm==4.67.1
      - typed-ast==1.5.5
      - types-pyyaml==6.0.12.12
      - typing-extensions==4.7.1
      - uritemplate==4.1.1
      - urllib3==1.26.20
      - voluptuous==0.14.1
      - webdavclient3==3.14.6
      - werkzeug==2.2.3
      - wget==3.2
      - wrapt==1.16.0
      - xmltodict==0.14.2
      - zc-lockfile==3.0.post1
prefix: /opt/conda/envs/dvc

tests/unit/scm/test_scm.py::TestScmContext::test_should_check_autostage
tests/func/experiments/test_experiments.py::test_new_simple tests/func/experiments/test_experiments.py::test_update_with_pull tests/func/experiments/test_experiments.py::test_checkout tests/func/experiments/test_experiments.py::test_get_baseline tests/func/experiments/test_experiments.py::test_extend_branch tests/func/metrics/test_diff.py::test_metrics_diff_simple tests/func/metrics/test_diff.py::test_metrics_diff_yaml tests/func/metrics/test_diff.py::test_metrics_diff_json tests/func/metrics/test_diff.py::test_metrics_diff_json_unchanged tests/func/metrics/test_diff.py::test_metrics_diff_broken_json tests/func/metrics/test_diff.py::test_metrics_diff_no_metrics tests/func/metrics/test_diff.py::test_metrics_diff_new_metric tests/func/metrics/test_diff.py::test_metrics_diff_deleted_metric tests/func/metrics/test_diff.py::test_metrics_diff_with_unchanged tests/func/metrics/test_diff.py::test_metrics_diff_dirty tests/func/metrics/test_diff.py::test_metrics_diff_cli tests/func/metrics/test_diff.py::test_metrics_diff_non_metrics tests/func/metrics/test_show.py::test_show_empty tests/func/metrics/test_show.py::test_show_simple tests/func/metrics/test_show.py::test_show tests/func/metrics/test_show.py::test_show_multiple tests/func/metrics/test_show.py::test_show_invalid_metric tests/func/metrics/test_show.py::test_show_branch tests/func/metrics/test_show.py::test_show_subrepo_with_preexisting_tags tests/func/metrics/test_show.py::test_missing_cache tests/func/metrics/test_show.py::test_show_non_metric tests/func/metrics/test_show.py::test_show_non_metric_branch tests/func/metrics/test_show.py::test_non_metric_and_recurisve_show tests/func/test_add.py::test_add tests/func/test_add.py::test_add_unicode tests/func/test_add.py::test_add_unsupported_file tests/func/test_add.py::test_add_directory tests/func/test_add.py::TestAddDirectoryRecursive::test tests/func/test_add.py::TestAddCmdDirectoryRecursive::test tests/func/test_add.py::TestAddCmdDirectoryRecursive::test_warn_about_large_directories tests/func/test_add.py::TestAddDirectoryWithForwardSlash::test tests/func/test_add.py::test_add_tracked_file tests/func/test_add.py::TestAddDirWithExistingCache::test tests/func/test_add.py::TestAddModifiedDir::test tests/func/test_add.py::test_add_file_in_dir tests/func/test_add.py::test_add_external_file[local_cloud-md5-8c7dd922ad47494fc02c388e12c00eac] tests/func/test_add.py::test_add_external_file[ssh-md5-8c7dd922ad47494fc02c388e12c00eac] tests/func/test_add.py::test_add_external_file[s3-etag-8c7dd922ad47494fc02c388e12c00eac] tests/func/test_add.py::test_add_external_dir[local_cloud-md5-b6dcab6ccd17ca0a8bf4a215a37d14cc.dir] tests/func/test_add.py::test_add_external_dir[ssh-md5-b6dcab6ccd17ca0a8bf4a215a37d14cc.dir] tests/func/test_add.py::test_add_external_dir[s3-etag-ec602a6ba97b2dd07bd6d2cd89674a60.dir] tests/func/test_add.py::TestAddLocalRemoteFile::test tests/func/test_add.py::TestCmdAdd::test tests/func/test_add.py::TestDoubleAddUnchanged::test_dir tests/func/test_add.py::TestDoubleAddUnchanged::test_file tests/func/test_add.py::test_should_update_state_entry_for_file_after_add tests/func/test_add.py::test_should_update_state_entry_for_directory_after_add tests/func/test_add.py::TestAddCommit::test tests/func/test_add.py::test_should_collect_dir_cache_only_once tests/func/test_add.py::TestShouldAddDataFromExternalSymlink::test tests/func/test_add.py::TestShouldAddDataFromInternalSymlink::test tests/func/test_add.py::TestShouldPlaceStageInDataDirIfRepositoryBelowSymlink::test tests/func/test_add.py::TestShouldThrowProperExceptionOnCorruptedStageFile::test tests/func/test_add.py::TestAddFilename::test tests/func/test_add.py::test_failed_add_cleanup tests/func/test_add.py::test_should_not_track_git_internal_files tests/func/test_add.py::TestAddUnprotected::test tests/func/test_add.py::test_readding_dir_should_not_unprotect_all tests/func/test_add.py::test_should_not_checkout_when_adding_cached_copy tests/func/test_add.py::test_should_relink_on_repeated_add[hardlink-copy-<lambda>] tests/func/test_add.py::test_should_relink_on_repeated_add[symlink-copy-<lambda>] tests/func/test_add.py::test_should_relink_on_repeated_add[copy-hardlink-is_hardlink] tests/func/test_add.py::test_should_relink_on_repeated_add[copy-symlink-is_symlink] tests/func/test_add.py::test_should_protect_on_repeated_add[hardlink] tests/func/test_add.py::test_should_protect_on_repeated_add[symlink] tests/func/test_add.py::test_should_protect_on_repeated_add[copy] tests/func/test_add.py::test_escape_gitignore_entries tests/func/test_add.py::test_add_from_data_dir tests/func/test_add.py::test_not_raises_on_re_add tests/func/test_add.py::test_add_empty_files[hardlink] tests/func/test_add.py::test_add_empty_files[symlink] tests/func/test_add.py::test_add_empty_files[copy] tests/func/test_add.py::test_add_optimization_for_hardlink_on_empty_files tests/func/test_add.py::test_output_duplication_for_pipeline_tracked tests/func/test_add.py::test_add_pipeline_file tests/func/test_add.py::test_add_symlink tests/func/test_api.py::test_get_url[s3] tests/func/test_api.py::test_get_url[ssh] tests/func/test_api.py::test_get_url[http] tests/func/test_api.py::test_get_url_external[s3] tests/func/test_api.py::test_get_url_external[ssh] tests/func/test_api.py::test_get_url_external[http] tests/func/test_api.py::test_open[local_cloud] tests/func/test_api.py::test_open[s3] tests/func/test_api.py::test_open[ssh] tests/func/test_api.py::test_open[http] tests/func/test_api.py::test_open_external[http] tests/func/test_api.py::test_open_external[ssh] tests/func/test_api.py::test_open_granular[local_cloud] tests/func/test_api.py::test_open_granular[s3] tests/func/test_api.py::test_open_granular[ssh] tests/func/test_api.py::test_open_granular[http] tests/func/test_api.py::test_missing[ssh] tests/func/test_api.py::test_missing[http] tests/func/test_api.py::test_open_not_cached tests/func/test_api.py::test_read_with_subrepos[False] tests/func/test_api.py::test_read_with_subrepos[True] tests/func/test_api.py::test_get_url_granular tests/func/test_api.py::test_get_url_subrepos tests/func/test_cache.py::TestExternalCacheDir::test tests/func/test_cache.py::TestSharedCacheDir::test tests/func/test_cache.py::TestCacheLinkType::test tests/func/test_cache.py::TestCmdCacheDir::test_relative_path tests/func/test_cache.py::test_shared_cache[False-493] tests/func/test_cache.py::test_shared_cache[True-509] tests/func/test_import_url.py::TestCmdImport::test tests/func/test_import_url.py::TestDefaultOutput::test tests/func/test_import_url.py::test_should_remove_outs_before_import tests/func/test_import_url.py::TestImportFilename::test tests/func/test_import_url.py::test_import_url_to_dir[.] tests/func/test_import_url.py::test_import_url_to_dir[dir] tests/func/test_import_url.py::test_import_url_to_dir[dir/subdir] tests/func/test_import_url.py::test_import_stage_accompanies_target tests/func/test_import_url.py::test_import_url_nonexistent tests/func/test_import_url.py::test_import_url_with_no_exec tests/func/test_import_url.py::test_import_url[local_cloud] tests/func/test_import_url.py::test_import_url[s3] tests/func/test_import_url.py::test_import_url[ssh] tests/func/test_import_url.py::test_import_url[http] tests/func/test_import_url.py::test_import_url_dir[local_cloud-dc24e1271084ee317ac3c2656fb8812b-b6dcab6ccd17ca0a8bf4a215a37d14cc.dir] tests/func/test_import_url.py::test_import_url_dir[s3-2aa17f8daa26996b3f7a4cf8888ac9ac-ec602a6ba97b2dd07bd6d2cd89674a60.dir] tests/func/test_import_url.py::test_import_url_dir[ssh-dc24e1271084ee317ac3c2656fb8812b-b6dcab6ccd17ca0a8bf4a215a37d14cc.dir] tests/func/test_run_multistage.py::test_run_with_name tests/func/test_run_multistage.py::test_run_no_exec tests/func/test_run_multistage.py::test_run_with_multistage_and_single_stage tests/func/test_run_multistage.py::test_run_multi_stage_repeat tests/func/test_run_multistage.py::test_multi_stage_run_cached tests/func/test_run_multistage.py::test_multistage_dump_on_non_cached_outputs tests/func/test_run_multistage.py::test_multistage_with_wdir tests/func/test_run_multistage.py::test_multistage_always_changed tests/func/test_run_multistage.py::test_graph tests/func/test_run_multistage.py::test_run_dump_on_multistage tests/func/test_run_multistage.py::test_run_with_name_having_hyphen_underscore tests/func/test_run_multistage.py::test_run_already_exists tests/func/test_run_multistage.py::test_run_params_default tests/func/test_run_multistage.py::test_run_params_custom_file tests/func/test_run_multistage.py::test_run_params_no_exec tests/func/test_run_multistage.py::test_run_overwrite_order tests/func/test_run_multistage.py::test_run_overwrite_preserves_meta_and_comment tests/func/test_run_multistage.py::test_run_external_outputs[local_cloud-md5-acbd18db4cc2f85cedef654fccc4a4d8-37b51d194a7513e45b56f6524f2d51f2] tests/func/test_run_multistage.py::test_run_external_outputs[ssh-md5-acbd18db4cc2f85cedef654fccc4a4d8-37b51d194a7513e45b56f6524f2d51f2] tests/func/test_run_multistage.py::test_run_external_outputs[s3-etag-acbd18db4cc2f85cedef654fccc4a4d8-37b51d194a7513e45b56f6524f2d51f2] tests/func/test_update.py::test_update_import[True] tests/func/test_update.py::test_update_import_after_remote_updates_to_dvc tests/func/test_update.py::test_update_before_and_after_dvc_init tests/func/test_update.py::test_update_import_url[local_cloud] tests/func/test_update.py::test_update_import_url[s3] tests/func/test_update.py::test_update_import_url[ssh] tests/func/test_update.py::test_update_rev tests/func/test_update.py::test_update_recursive tests/func/test_update.py::test_update_from_subrepos[True] tests/func/test_update.py::test_update_from_subrepos[False] tests/unit/output/test_local.py::TestLocalOutput::test_save_missing tests/unit/output/test_local.py::test_str_workdir_outside_repo tests/unit/output/test_local.py::test_str_workdir_inside_repo tests/unit/output/test_local.py::test_str_on_local_absolute_path tests/unit/output/test_local.py::test_str_on_external_absolute_path tests/unit/output/test_local.py::TestGetFilesNumber::test_return_0_on_no_cache tests/unit/output/test_local.py::TestGetFilesNumber::test_return_1_on_single_file_cache tests/unit/output/test_local.py::TestGetFilesNumber::test_return_multiple_for_dir
tests/func/metrics/test_diff.py::test_no_commits tests/func/test_api.py::test_get_url_requires_dvc tests/func/test_api.py::test_open_scm_controlled tests/func/test_cache.py::TestCache::test_all tests/func/test_cache.py::TestCache::test_get tests/func/test_cache.py::TestCacheLoadBadDirCache::test tests/func/test_cache.py::TestExternalCacheDir::test_remote_references tests/func/test_cache.py::TestCmdCacheDir::test tests/func/test_cache.py::TestCmdCacheDir::test_abs_path tests/func/test_cache.py::test_default_cache_type tests/func/test_import_url.py::TestCmdImport::test_unsupported tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[@:] tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[#] tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[$] tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[:] tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[/] tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[\\] tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[.] tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[;] tests/func/test_run_multistage.py::test_run_with_invalid_stage_name[,] tests/func/test_run_multistage.py::test_run_without_cmd[kwargs0] tests/func/test_run_multistage.py::test_run_without_cmd[kwargs1] tests/unit/scm/test_scm.py::TestScmContext::test_should_successfully_perform_method tests/unit/scm/test_scm.py::TestScmContext::test_should_throw_and_cleanup tests/unit/scm/test_scm.py::test_remind_to_track
dvc/config.py dvc/version.py dvc/scm/git.py setup.py dvc/repo/experiments/__init__.py dvc/repo/experiments/executor.py dvc/scm/base.py dvc/repo/metrics/show.py .travis.yml dvc/api.py dvc/tree/gdrive.py dvc/repo/scm_context.py dvc/tree/azure.py dvc/repo/experiments/show.py
dvc/config.py dvc/version.py dvc/scm/git.py setup.py dvc/repo/experiments/__init__.py dvc/repo/experiments/executor.py dvc/scm/base.py dvc/repo/metrics/show.py .travis.yml dvc/api.py dvc/tree/gdrive.py dvc/repo/scm_context.py dvc/tree/azure.py dvc/repo/experiments/show.py