]> arthur.barton.de Git - bup.git/blob - lib/bup/ls.py
ls: add --commit-hash and drop vfs nominal_oid
[bup.git] / lib / bup / ls.py
1 """Common code for listing files from a bup repository."""
2
3 from __future__ import print_function
4 from itertools import chain
5 from stat import S_ISDIR, S_ISLNK
6 import copy, locale, os.path, stat, sys, xstat
7
8 from bup import metadata, options, vfs2 as vfs
9 from bup.repo import LocalRepo
10 from helpers import columnate, istty1, last, log
11
12 def item_hash(item, tree_for_commit):
13     """If the item is a Commit, return its commit oid, otherwise return
14     the item's oid, if it has one.
15
16     """
17     if tree_for_commit and isinstance(item, vfs.Commit):
18         return item.coid
19     return getattr(item, 'oid', None)
20
21 def item_info(item, name,
22               show_hash = False,
23               commit_hash=False,
24               long_fmt = False,
25               classification = None,
26               numeric_ids = False,
27               human_readable = False):
28     """Return a string containing the information to display for the VFS
29     item.  Classification may be "all", "type", or None.
30
31     """
32     result = ''
33     if show_hash:
34         oid = item_hash(item, commit_hash)
35         result += '%s ' % (oid.encode('hex') if oid
36                            else '0000000000000000000000000000000000000000')
37     if long_fmt:
38         meta = item.meta.copy()
39         meta.path = name
40         # FIXME: need some way to track fake vs real meta items?
41         result += metadata.summary_str(meta,
42                                        numeric_ids=numeric_ids,
43                                        classification=classification,
44                                        human_readable=human_readable)
45     else:
46         result += name
47         if classification:
48             result += xstat.classification_str(item.meta.mode,
49                                                classification == 'all')
50     return result
51
52
53 optspec = """
54 %sls [-a] [path...]
55 --
56 s,hash   show hash for each file
57 commit-hash show commit hash instead of tree for commits (implies -s)
58 a,all    show hidden files
59 A,almost-all    show hidden files except . and ..
60 l        use a detailed, long listing format
61 d,directory show directories, not contents; don't follow symlinks
62 F,classify append type indicator: dir/ sym@ fifo| sock= exec*
63 file-type append type indicator: dir/ sym@ fifo| sock=
64 human-readable    print human readable file sizes (i.e. 3.9K, 4.7M)
65 n,numeric-ids list numeric IDs (user, group, etc.) rather than names
66 """
67
68 def do_ls(args, default='.', onabort=None, spec_prefix=''):
69     """Output a listing of a file or directory in the bup repository.
70
71     When a long listing is not requested and stdout is attached to a
72     tty, the output is formatted in columns. When not attached to tty
73     (for example when the output is piped to another command), one
74     file is listed per line.
75
76     """
77     if onabort:
78         o = options.Options(optspec % spec_prefix, onabort=onabort)
79     else:
80         o = options.Options(optspec % spec_prefix)
81     (opt, flags, extra) = o.parse(args)
82
83     # Handle order-sensitive options.
84     classification = None
85     show_hidden = None
86     for flag in flags:
87         (option, parameter) = flag
88         if option in ('-F', '--classify'):
89             classification = 'all'
90         elif option == '--file-type':
91             classification = 'type'
92         elif option in ('-a', '--all'):
93             show_hidden = 'all'
94         elif option in ('-A', '--almost-all'):
95             show_hidden = 'almost'
96
97     if opt.commit_hash:
98         opt.hash = True
99
100     def item_line(item, name):
101         return item_info(item, name,
102                          show_hash = opt.hash,
103                          commit_hash=opt.commit_hash,
104                          long_fmt = opt.l,
105                          classification = classification,
106                          numeric_ids = opt.numeric_ids,
107                          human_readable = opt.human_readable)
108
109     repo = LocalRepo()
110     ret = 0
111     pending = []
112     for path in (extra or [default]):
113         try:
114             if opt.directory:
115                 resolved = vfs.lresolve(repo, path)
116             else:
117                 # FIXME: deal with invalid symlinks i.e. old vfs try_resolve
118                 resolved = vfs.resolve(repo, path)
119
120             leaf_name, leaf_item = resolved[-1]
121             if not leaf_item:
122                 log('error: cannot access %r in %r\n'
123                     % ('/'.join(name for name, item in resolved),
124                        path))
125                 ret = 1
126                 continue
127             if not opt.directory and S_ISDIR(vfs.item_mode(leaf_item)):
128                 items = vfs.contents(repo, leaf_item)
129                 if show_hidden == 'all':
130                     # Match non-bup "ls -a ... /".
131                     parent = resolved[-2] if len(resolved) > 1 else resolved[0]
132                     items = chain(items, (('..', parent[1]),))
133
134                 items = ((x[0], vfs.augment_item_meta(repo, x[1],
135                                                       include_size=True))
136                          for x in items)
137                 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
138                     if show_hidden != 'all' and sub_name == '.':
139                         continue
140                     if sub_name.startswith('.') and \
141                        show_hidden not in ('almost', 'all'):
142                         continue
143                     line = item_line(sub_item, sub_name)
144                     pending.append(line) if not opt.l and istty1 else print(line)
145             else:
146                 leaf_item = vfs.augment_item_meta(repo, leaf_item,
147                                                   include_size=True)
148                 line = item_line(leaf_item, os.path.normpath(path))
149                 pending.append(line) if not opt.l and istty1 else print(line)
150         except vfs.IOError as ex:
151             log('bup: %s\n' % ex)
152             ret = 1
153
154     if pending:
155         sys.stdout.write(columnate(pending, ''))
156
157     return ret