Repository: iterative/dvc

Issue:
dvc: preserve desc when re-add/import[-url]/run'ing
Currently `desc` is not preserved if you decide to update your dataset with `dvc add`. E.g.
```
#!/bin/bash                             
                                        
set -e                                  
set -x                                  
                                        
rm -rf myrepo                           
mkdir myrepo                            
cd myrepo                               
git init                                
dvc init                                
                                        
echo foo > foo                          
dvc add foo --desc 'my data description'
cat foo.dvc                             
# outs:
# - md5: d3b07384d113edec49eaa6238ad5ff00
#   size: 4                              
#   path: foo                            
#   desc: my data description            
                                        
echo foo >> foo                         
dvc add foo                             
cat foo.dvc
# outs:
# - md5: d3b07384d113edec49eaa6238ad5ff00
#   size: 4                              
#   path: foo
```


Discussion:

Resolving patch:
diff --git a/dvc/stage/__init__.py b/dvc/stage/__init__.py
--- a/dvc/stage/__init__.py
+++ b/dvc/stage/__init__.py
@@ -107,6 +107,12 @@ def restore_meta(stage):
     stage._stage_text = old._stage_text
 
     stage.meta = old.meta
+    stage.desc = old.desc
+
+    old_desc = {out.def_path: out.desc for out in old.outs}
+
+    for out in stage.outs:
+        out.desc = old_desc.get(out.def_path, None)
 
 
 class Stage(params.StageParams):
diff --git a/dvc/stage/utils.py b/dvc/stage/utils.py
--- a/dvc/stage/utils.py
+++ b/dvc/stage/utils.py
@@ -161,6 +161,7 @@ def compute_md5(stage):
     # Remove md5 and meta, these should not affect stage md5
     d.pop(stage.PARAM_MD5, None)
     d.pop(stage.PARAM_META, None)
+    d.pop(stage.PARAM_DESC, None)
 
     # Ignore the wdir default value. In this case DVC-file w/o
     # wdir has the same md5 as a file with the default value specified.
@@ -174,6 +175,7 @@ def compute_md5(stage):
         exclude=[
             stage.PARAM_LOCKED,  # backward compatibility
             stage.PARAM_FROZEN,
+            BaseOutput.PARAM_DESC,
             BaseOutput.PARAM_METRIC,
             BaseOutput.PARAM_PERSIST,
             BaseOutput.PARAM_CHECKPOINT,


Test patch:
diff --git a/tests/func/test_add.py b/tests/func/test_add.py
--- a/tests/func/test_add.py
+++ b/tests/func/test_add.py
@@ -929,8 +929,10 @@ def test_add_preserve_meta(tmp_dir, dvc):
     text = textwrap.dedent(
         """\
         # top comment
+        desc: top desc
         outs:
         - path: foo # out comment
+          desc: out desc
         meta: some metadata
     """
     )
@@ -940,8 +942,10 @@ def test_add_preserve_meta(tmp_dir, dvc):
     assert (tmp_dir / "foo.dvc").read_text() == textwrap.dedent(
         """\
         # top comment
+        desc: top desc
         outs:
         - path: foo # out comment
+          desc: out desc
           md5: acbd18db4cc2f85cedef654fccc4a4d8
           size: 3
         meta: some metadata
diff --git a/tests/func/test_import_url.py b/tests/func/test_import_url.py
--- a/tests/func/test_import_url.py
+++ b/tests/func/test_import_url.py
@@ -214,10 +214,12 @@ def test_import_url_preserve_meta(tmp_dir, dvc):
     text = textwrap.dedent(
         """\
         # top comment
+        desc: top desc
         deps:
         - path: foo # dep comment
         outs:
         - path: bar # out comment
+          desc: out desc
         meta: some metadata
     """
     )
@@ -228,12 +230,14 @@ def test_import_url_preserve_meta(tmp_dir, dvc):
     assert (tmp_dir / "bar.dvc").read_text() == textwrap.dedent(
         """\
         # top comment
+        desc: top desc
         deps:
         - path: foo # dep comment
           md5: acbd18db4cc2f85cedef654fccc4a4d8
           size: 3
         outs:
         - path: bar # out comment
+          desc: out desc
           md5: acbd18db4cc2f85cedef654fccc4a4d8
           size: 3
         meta: some metadata
diff --git a/tests/func/test_run_single_stage.py b/tests/func/test_run_single_stage.py
--- a/tests/func/test_run_single_stage.py
+++ b/tests/func/test_run_single_stage.py
@@ -1009,6 +1009,7 @@ def test_run_force_preserves_comments_and_meta(tmp_dir, dvc, run_copy):
     tmp_dir.gen({"foo": "foo", "foo1": "foo1"})
     text = textwrap.dedent(
         """\
+      desc: top desc
       cmd: python copy.py foo bar
       deps:
       - path: copy.py
@@ -1016,16 +1017,58 @@ def test_run_force_preserves_comments_and_meta(tmp_dir, dvc, run_copy):
       outs:
       # comment preserved
       - path: bar
+        desc: out desc
       meta:
         name: copy-foo-bar
     """
     )
     (tmp_dir / "bar.dvc").write_text(text)
     dvc.reproduce("bar.dvc")
-    assert "comment" in (tmp_dir / "bar.dvc").read_text()
-    assert "meta" in (tmp_dir / "bar.dvc").read_text()
 
-    run_copy("foo1", "bar1", single_stage=True, force=True, fname="bar.dvc")
+    # CRLF on windows makes the generated file bigger in size
+    code_size = 143 if os.name == "nt" else 142
+    assert (tmp_dir / "bar.dvc").read_text() == textwrap.dedent(
+        f"""\
+        desc: top desc
+        cmd: python copy.py foo bar
+        deps:
+        - path: copy.py
+          md5: 90c27dd80b698fe766f0c3ee0b6b9729
+          size: {code_size}
+        - path: foo
+          md5: acbd18db4cc2f85cedef654fccc4a4d8
+          size: 3
+        outs:
+        # comment preserved
+        - path: bar
+          desc: out desc
+          md5: acbd18db4cc2f85cedef654fccc4a4d8
+          size: 3
+        meta:
+          name: copy-foo-bar
+        md5: be659ce4a33cebb85d4e8e1335d394ad
+    """
+    )
 
-    assert "comment" in (tmp_dir / "bar.dvc").read_text()
-    assert "meta" in (tmp_dir / "bar.dvc").read_text()
+    run_copy("foo1", "bar1", single_stage=True, force=True, fname="bar.dvc")
+    assert (tmp_dir / "bar.dvc").read_text() == textwrap.dedent(
+        f"""\
+        desc: top desc
+        cmd: python copy.py foo1 bar1
+        deps:
+        - path: foo1
+          md5: 299a0be4a5a79e6a59fdd251b19d78bb
+          size: 4
+        - path: copy.py
+          md5: 90c27dd80b698fe766f0c3ee0b6b9729
+          size: {code_size}
+        outs:
+        # comment preserved
+        - path: bar1
+          md5: 299a0be4a5a79e6a59fdd251b19d78bb
+          size: 4
+        meta:
+          name: copy-foo-bar
+        md5: 9e725b11cb393e6a7468369fa50328b7
+    """
+    )