Skip to content

Commit 53e5a12

Browse files
committed
Add option to show hashes in dvc ls
1 parent 9b5772f commit 53e5a12

File tree

3 files changed

+129
-22
lines changed

3 files changed

+129
-22
lines changed

dvc/commands/ls/__init__.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,25 @@
99
logger = logger.getChild(__name__)
1010

1111

12-
def _format_entry(entry, fmt):
12+
def _format_entry(entry, fmt, with_size=True, with_md5=False):
1313
from dvc.utils.humanize import naturalsize
1414

15-
size = entry.get("size")
16-
if size is None:
17-
size = ""
18-
else:
19-
size = naturalsize(size)
20-
return size, fmt(entry)
15+
ret = []
16+
if with_size:
17+
size = entry.get("size")
18+
if size is None:
19+
size = ""
20+
else:
21+
size = naturalsize(size)
22+
ret.append(size)
23+
if with_md5:
24+
md5 = entry.get("md5", "")
25+
ret.append(md5)
26+
ret.append(fmt(entry))
27+
return ret
2128

2229

23-
def show_entries(entries, with_color=False, with_size=False):
30+
def show_entries(entries, with_color=False, with_size=False, with_md5=False):
2431
if with_color:
2532
ls_colors = LsColors()
2633
fmt = ls_colors.format
@@ -29,8 +36,13 @@ def show_entries(entries, with_color=False, with_size=False):
2936
def fmt(entry):
3037
return entry["path"]
3138

32-
if with_size:
33-
ui.table([_format_entry(entry, fmt) for entry in entries])
39+
if with_size or with_md5:
40+
ui.table(
41+
[
42+
_format_entry(entry, fmt, with_size=with_size, with_md5=with_md5)
43+
for entry in entries
44+
]
45+
)
3446
return
3547

3648
# NOTE: this is faster than ui.table for very large number of entries
@@ -55,7 +67,12 @@ def run(self):
5567
if self.args.json:
5668
ui.write_json(entries)
5769
elif entries:
58-
show_entries(entries, with_color=True, with_size=self.args.size)
70+
show_entries(
71+
entries,
72+
with_color=True,
73+
with_size=self.args.size,
74+
with_md5=self.args.show_hash,
75+
)
5976
return 0
6077
except FileNotFoundError:
6178
logger.exception("")
@@ -123,6 +140,12 @@ def add_parser(subparsers, parent_parser):
123140
),
124141
)
125142
list_parser.add_argument("--size", action="store_true", help="Show sizes.")
143+
list_parser.add_argument(
144+
"--show-hash",
145+
help="Display hash value for each item.",
146+
action="store_true",
147+
default=False,
148+
)
126149
list_parser.add_argument(
127150
"path",
128151
nargs="?",

dvc/repo/ls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def _ls(
110110
"isdir": info["type"] == "directory",
111111
"isexec": info.get("isexec", False),
112112
"size": info.get("size"),
113+
"md5": dvc_info.get("md5") or dvc_info.get("md5-dos2unix"),
113114
}
114115

115116
return ret

tests/func/test_ls.py

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,12 @@ def test_ls_repo_with_missed_path_dvc_only(tmp_dir, dvc, scm):
222222
tmp_dir.dvc_gen(DVC_STRUCTURE, commit="dvc")
223223

224224
with pytest.raises(FileNotFoundError):
225-
Repo.ls(os.fspath(tmp_dir), path="missed_path", recursive=True, dvc_only=True)
225+
Repo.ls(
226+
os.fspath(tmp_dir),
227+
path="missed_path",
228+
recursive=True,
229+
dvc_only=True,
230+
)
226231

227232

228233
def test_ls_repo_with_removed_dvc_dir(tmp_dir, dvc, scm):
@@ -467,20 +472,49 @@ def test_ls_granular(erepo_dir, M):
467472

468473
entries = Repo.ls(os.fspath(erepo_dir), os.path.join("dir", "subdir"))
469474
assert entries == [
470-
{"isout": True, "isdir": False, "isexec": False, "path": "bar", "size": 3},
471-
{"isout": True, "isdir": False, "isexec": False, "path": "foo", "size": 3},
475+
{
476+
"isout": True,
477+
"isdir": False,
478+
"isexec": False,
479+
"path": "bar",
480+
"size": 3,
481+
"md5": "37b51d194a7513e45b56f6524f2d51f2",
482+
},
483+
{
484+
"isout": True,
485+
"isdir": False,
486+
"isexec": False,
487+
"path": "foo",
488+
"size": 3,
489+
"md5": "acbd18db4cc2f85cedef654fccc4a4d8",
490+
},
472491
]
473492

474493
entries = Repo.ls(os.fspath(erepo_dir), "dir")
475494
assert entries == [
476-
{"isout": True, "isdir": False, "isexec": False, "path": "1", "size": 1},
477-
{"isout": True, "isdir": False, "isexec": False, "path": "2", "size": 1},
495+
{
496+
"isout": True,
497+
"isdir": False,
498+
"isexec": False,
499+
"path": "1",
500+
"size": 1,
501+
"md5": "c4ca4238a0b923820dcc509a6f75849b",
502+
},
503+
{
504+
"isout": True,
505+
"isdir": False,
506+
"isexec": False,
507+
"path": "2",
508+
"size": 1,
509+
"md5": "c81e728d9d4c2f636f067f89cc14862c",
510+
},
478511
{
479512
"isout": True,
480513
"isdir": True,
481514
"isexec": False,
482515
"path": "subdir",
483516
"size": M.instance_of(int),
517+
"md5": None,
484518
},
485519
]
486520

@@ -506,14 +540,42 @@ def _ls(path):
506540
return Repo.ls(os.fspath(erepo_dir), path)
507541

508542
assert _ls(os.path.join("dir", "1")) == [
509-
{"isout": isout, "isdir": False, "isexec": False, "path": "1", "size": 1}
543+
{
544+
"isout": isout,
545+
"isdir": False,
546+
"isexec": False,
547+
"path": "1",
548+
"size": 1,
549+
"md5": "c4ca4238a0b923820dcc509a6f75849b" if not use_scm else None,
550+
}
510551
]
511552
assert _ls(os.path.join("dir", "subdir", "foo")) == [
512-
{"isout": isout, "isdir": False, "isexec": False, "path": "foo", "size": 3}
553+
{
554+
"isout": isout,
555+
"isdir": False,
556+
"isexec": False,
557+
"path": "foo",
558+
"size": 3,
559+
"md5": "acbd18db4cc2f85cedef654fccc4a4d8" if not use_scm else None,
560+
}
513561
]
514562
assert _ls(os.path.join("dir", "subdir")) == [
515-
{"isdir": False, "isexec": 0, "isout": isout, "path": "bar", "size": 3},
516-
{"isdir": False, "isexec": 0, "isout": isout, "path": "foo", "size": 3},
563+
{
564+
"isdir": False,
565+
"isexec": 0,
566+
"isout": isout,
567+
"path": "bar",
568+
"size": 3,
569+
"md5": "37b51d194a7513e45b56f6524f2d51f2" if not use_scm else None,
570+
},
571+
{
572+
"isdir": False,
573+
"isexec": 0,
574+
"isout": isout,
575+
"path": "foo",
576+
"size": 3,
577+
"md5": "acbd18db4cc2f85cedef654fccc4a4d8" if not use_scm else None,
578+
},
517579
]
518580

519581

@@ -576,13 +638,15 @@ def test_broken_symlink(tmp_dir, dvc, M):
576638
"isexec": False,
577639
"path": ".dvcignore",
578640
"size": M.instance_of(int),
641+
"md5": None,
579642
},
580643
{
581644
"isout": False,
582645
"isdir": False,
583646
"isexec": False,
584647
"path": "link",
585648
"size": 0,
649+
"md5": None,
586650
},
587651
]
588652

@@ -614,36 +678,55 @@ def test_ls_broken_dir(tmp_dir, dvc, M):
614678
"isout": False,
615679
"path": ".dvcignore",
616680
"size": M.instance_of(int),
681+
"md5": None,
682+
},
683+
{
684+
"isdir": True,
685+
"isexec": False,
686+
"isout": True,
687+
"path": "broken",
688+
"size": 3,
689+
"md5": "630bd47b538d2a513c7d267d07e0bc44.dir",
617690
},
618-
{"isdir": True, "isexec": False, "isout": True, "path": "broken", "size": 3},
619691
{
620692
"isdir": False,
621693
"isexec": False,
622694
"isout": False,
623695
"path": "broken.dvc",
624696
"size": M.instance_of(int),
697+
"md5": None,
625698
},
626699
{
627700
"isdir": True,
628701
"isexec": False,
629702
"isout": True,
630703
"path": "dir",
631704
"size": M.instance_of(int),
705+
"md5": "91aaa9bb58b657d623ef143b195a67e4.dir",
632706
},
633707
{
634708
"isdir": False,
635709
"isexec": False,
636710
"isout": False,
637711
"path": "dir.dvc",
638712
"size": M.instance_of(int),
713+
"md5": None,
714+
},
715+
{
716+
"isdir": False,
717+
"isexec": False,
718+
"isout": True,
719+
"path": "foo",
720+
"size": 3,
721+
"md5": "acbd18db4cc2f85cedef654fccc4a4d8",
639722
},
640-
{"isdir": False, "isexec": False, "isout": True, "path": "foo", "size": 3},
641723
{
642724
"isdir": False,
643725
"isexec": False,
644726
"isout": False,
645727
"path": "foo.dvc",
646728
"size": M.instance_of(int),
729+
"md5": None,
647730
},
648731
]
649732

0 commit comments

Comments
 (0)