##// END OF EJS Templates
status: add a flag to terse the output (issue4119)...
Pulkit Goyal -
r33548:4cd4344a default
parent child Browse files
Show More
@@ -0,0 +1,185 b''
1 $ mkdir folder
2 $ cd folder
3 $ hg init
4 $ mkdir x x/l x/m x/n x/l/u x/l/u/a
5 $ touch a b x/aa.o x/bb.o
6 $ hg status
7 ? a
8 ? b
9 ? x/aa.o
10 ? x/bb.o
11
12 $ hg status --terse u
13 ? a
14 ? b
15 ? x/
16 $ hg status --terse maudric
17 ? a
18 ? b
19 ? x/
20 $ hg status --terse madric
21 ? a
22 ? b
23 ? x/aa.o
24 ? x/bb.o
25 $ hg status --terse f
26 abort: 'f' not recognized
27 [255]
28
29 Add a .hgignore so that we can also have ignored files
30
31 $ echo ".*\.o" > .hgignore
32 $ hg status
33 ? .hgignore
34 ? a
35 ? b
36 $ hg status -i
37 I x/aa.o
38 I x/bb.o
39
40 Tersing ignored files
41 $ hg status -t i --ignored
42 I x/
43
44 Adding more files
45 $ mkdir y
46 $ touch x/aa x/bb y/l y/m y/l.o y/m.o
47 $ touch x/l/aa x/m/aa x/n/aa x/l/u/bb x/l/u/a/bb
48
49 $ hg status
50 ? .hgignore
51 ? a
52 ? b
53 ? x/aa
54 ? x/bb
55 ? x/l/aa
56 ? x/l/u/a/bb
57 ? x/l/u/bb
58 ? x/m/aa
59 ? x/n/aa
60 ? y/l
61 ? y/m
62
63 $ hg status --terse u
64 ? .hgignore
65 ? a
66 ? b
67 ? x/
68 ? y/
69
70 $ hg add x/aa x/bb .hgignore
71 $ hg status --terse au
72 A .hgignore
73 A x/aa
74 A x/bb
75 ? a
76 ? b
77 ? x/l/
78 ? x/m/
79 ? x/n/
80 ? y/
81
82 Including ignored files
83
84 $ hg status --terse aui
85 A .hgignore
86 A x/aa
87 A x/bb
88 ? a
89 ? b
90 ? x/l/
91 ? x/m/
92 ? x/n/
93 ? y/l
94 ? y/m
95 $ hg status --terse au -i
96 I x/aa.o
97 I x/bb.o
98 I y/l.o
99 I y/m.o
100
101 Committing some of the files
102
103 $ hg commit x/aa x/bb .hgignore -m "First commit"
104 $ hg status
105 ? a
106 ? b
107 ? x/l/aa
108 ? x/l/u/a/bb
109 ? x/l/u/bb
110 ? x/m/aa
111 ? x/n/aa
112 ? y/l
113 ? y/m
114 $ hg status --terse mardu
115 ? a
116 ? b
117 ? x/l/
118 ? x/m/
119 ? x/n/
120 ? y/
121
122 Modifying already committed files
123
124 $ echo "Hello" >> x/aa
125 $ echo "World" >> x/bb
126 $ hg status --terse maurdc
127 M x/aa
128 M x/bb
129 ? a
130 ? b
131 ? x/l/
132 ? x/m/
133 ? x/n/
134 ? y/
135
136 Respecting other flags
137
138 $ hg status --terse marduic --all
139 M x/aa
140 M x/bb
141 ? a
142 ? b
143 ? x/l/
144 ? x/m/
145 ? x/n/
146 ? y/l
147 ? y/m
148 I x/aa.o
149 I x/bb.o
150 I y/l.o
151 I y/m.o
152 C .hgignore
153 $ hg status --terse marduic -a
154 $ hg status --terse marduic -c
155 C .hgignore
156 $ hg status --terse marduic -m
157 M x/aa
158 M x/bb
159
160 Passing 'i' in terse value will consider the ignored files while tersing
161
162 $ hg status --terse marduic -u
163 ? a
164 ? b
165 ? x/l/
166 ? x/m/
167 ? x/n/
168 ? y/l
169 ? y/m
170
171 Omitting 'i' in terse value does not consider ignored files while tersing
172
173 $ hg status --terse marduc -u
174 ? a
175 ? b
176 ? x/l/
177 ? x/m/
178 ? x/n/
179 ? y/
180
181 Trying with --rev
182
183 $ hg status --terse marduic --rev 0 --rev 1
184 abort: cannot use --terse with --rev
185 [255]
@@ -400,6 +400,178 b' def dorecord(ui, repo, commitfunc, cmdsu'
400 400
401 401 return commit(ui, repo, recordinwlock, pats, opts)
402 402
403 def tersestatus(root, statlist, status, ignorefn, ignore):
404 """
405 Returns a list of statuses with directory collapsed if all the files in the
406 directory has the same status.
407 """
408
409 def numfiles(dirname):
410 """
411 Calculates the number of tracked files in a given directory which also
412 includes files which were removed or deleted. Considers ignored files
413 if ignore argument is True or 'i' is present in status argument.
414 """
415 if lencache.get(dirname):
416 return lencache[dirname]
417 if 'i' in status or ignore:
418 def match(localpath):
419 absolutepath = os.path.join(root, localpath)
420 if os.path.isdir(absolutepath) and isemptydir(absolutepath):
421 return True
422 return False
423 else:
424 def match(localpath):
425 # there can be directory whose all the files are ignored and
426 # hence the drectory should also be ignored while counting
427 # number of files or subdirs in it's parent directory. This
428 # checks the same.
429 # XXX: We need a better logic here.
430 if os.path.isdir(os.path.join(root, localpath)):
431 return isignoreddir(localpath)
432 else:
433 # XXX: there can be files which have the ignored pattern but
434 # are not ignored. That leads to bug in counting number of
435 # tracked files in the directory.
436 return ignorefn(localpath)
437 lendir = 0
438 abspath = os.path.join(root, dirname)
439 # There might be cases when a directory does not exists as the whole
440 # directory can be removed and/or deleted.
441 try:
442 for f in os.listdir(abspath):
443 localpath = os.path.join(dirname, f)
444 if not match(localpath):
445 lendir += 1
446 except OSError:
447 pass
448 lendir += len(absentdir.get(dirname, []))
449 lencache[dirname] = lendir
450 return lendir
451
452 def isemptydir(abspath):
453 """
454 Check whether a directory is empty or not, i.e. there is no files in the
455 directory and all its subdirectories.
456 """
457 for f in os.listdir(abspath):
458 fullpath = os.path.join(abspath, f)
459 if os.path.isdir(fullpath):
460 # recursion here
461 ret = isemptydir(fullpath)
462 if not ret:
463 return False
464 else:
465 return False
466 return True
467
468 def isignoreddir(localpath):
469 """
470 This function checks whether the directory contains only ignored files
471 and hence should the directory be considered ignored. Returns True, if
472 that should be ignored otherwise False.
473 """
474 dirpath = os.path.join(root, localpath)
475 for f in os.listdir(dirpath):
476 filepath = os.path.join(dirpath, f)
477 if os.path.isdir(filepath):
478 # recursion here
479 ret = isignoreddir(os.path.join(localpath, f))
480 if not ret:
481 return False
482 else:
483 if not ignorefn(os.path.join(localpath, f)):
484 return False
485 return True
486
487 def absentones(removedfiles, missingfiles):
488 """
489 Returns a dictionary of directories with files in it which are either
490 removed or missing (deleted) in them.
491 """
492 absentdir = {}
493 absentfiles = removedfiles + missingfiles
494 while absentfiles:
495 f = absentfiles.pop()
496 par = os.path.dirname(f)
497 if par == '':
498 continue
499 # we need to store files rather than number of files as some files
500 # or subdirectories in a directory can be counted twice. This is
501 # also we have used sets here.
502 try:
503 absentdir[par].add(f)
504 except KeyError:
505 absentdir[par] = set([f])
506 absentfiles.append(par)
507 return absentdir
508
509 indexes = {'m': 0, 'a': 1, 'r': 2, 'd': 3, 'u': 4, 'i': 5, 'c': 6}
510 # get a dictonary of directories and files which are missing as os.listdir()
511 # won't be able to list them.
512 absentdir = absentones(statlist[2], statlist[3])
513 finalrs = [[]] * len(indexes)
514 didsomethingchanged = False
515 # dictionary to store number of files and subdir in a directory so that we
516 # don't compute that again.
517 lencache = {}
518
519 for st in pycompat.bytestr(status):
520
521 try:
522 ind = indexes[st]
523 except KeyError:
524 # TODO: Need a better error message here
525 raise error.Abort("'%s' not recognized" % st)
526
527 sfiles = statlist[ind]
528 if not sfiles:
529 continue
530 pardict = {}
531 for a in sfiles:
532 par = os.path.dirname(a)
533 pardict.setdefault(par, []).append(a)
534
535 rs = []
536 newls = []
537 for par, files in pardict.iteritems():
538 lenpar = numfiles(par)
539 if lenpar == len(files):
540 newls.append(par)
541
542 if not newls:
543 continue
544
545 while newls:
546 newel = newls.pop()
547 if newel == '':
548 continue
549 parn = os.path.dirname(newel)
550 pardict[newel] = []
551 # Adding pycompat.ossep as newel is a directory.
552 pardict.setdefault(parn, []).append(newel + pycompat.ossep)
553 lenpar = numfiles(parn)
554 if lenpar == len(pardict[parn]):
555 newls.append(parn)
556
557 # dict.values() for Py3 compatibility
558 for files in pardict.values():
559 rs.extend(files)
560
561 rs.sort()
562 finalrs[ind] = rs
563 didsomethingchanged = True
564
565 # If nothing is changed, make sure the order of files is preserved.
566 if not didsomethingchanged:
567 return statlist
568
569 for x in xrange(len(indexes)):
570 if not finalrs[x]:
571 finalrs[x] = statlist[x]
572
573 return finalrs
574
403 575 def findpossible(cmd, table, strict=False):
404 576 """
405 577 Return cmd -> (aliases, command table entry)
@@ -4617,6 +4617,7 b' def serve(ui, repo, **opts):'
4617 4617 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4618 4618 ('i', 'ignored', None, _('show only ignored files')),
4619 4619 ('n', 'no-status', None, _('hide status prefix')),
4620 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4620 4621 ('C', 'copies', None, _('show source of copied files')),
4621 4622 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4622 4623 ('', 'rev', [], _('show difference from revision'), _('REV')),
@@ -4662,6 +4663,16 b' def status(ui, repo, *pats, **opts):'
4662 4663
4663 4664 .. container:: verbose
4664 4665
4666 The -t/--terse option abbreviates the output by showing directory name
4667 if all the files in it share the same status. The option expects a value
4668 which can be a string formed by using 'm', 'a', 'r', 'd', 'u', 'i', 'c'
4669 where, 'm' stands for 'modified', 'a' for 'added', 'r' for 'removed',
4670 'd' for 'deleted', 'u' for 'unknown', 'i' for 'ignored' and 'c' for clean.
4671
4672 It terses the output of only those status which are passed. The ignored
4673 files are not considered while tersing until 'i' is there in --terse value
4674 or the --ignored option is used.
4675
4665 4676 Examples:
4666 4677
4667 4678 - show changes in the working directory relative to a
@@ -4688,10 +4699,14 b' def status(ui, repo, *pats, **opts):'
4688 4699 opts = pycompat.byteskwargs(opts)
4689 4700 revs = opts.get('rev')
4690 4701 change = opts.get('change')
4702 terse = opts.get('terse')
4691 4703
4692 4704 if revs and change:
4693 4705 msg = _('cannot specify --rev and --change at the same time')
4694 4706 raise error.Abort(msg)
4707 elif revs and terse:
4708 msg = _('cannot use --terse with --rev')
4709 raise error.Abort(msg)
4695 4710 elif change:
4696 4711 node2 = scmutil.revsingle(repo, change, None).node()
4697 4712 node1 = repo[node2].p1().node()
@@ -4712,6 +4727,7 b' def status(ui, repo, *pats, **opts):'
4712 4727 show = [k for k in states if opts.get(k)]
4713 4728 if opts.get('all'):
4714 4729 show += ui.quiet and (states[:4] + ['clean']) or states
4730
4715 4731 if not show:
4716 4732 if ui.quiet:
4717 4733 show = states[:4]
@@ -4722,6 +4738,9 b' def status(ui, repo, *pats, **opts):'
4722 4738 stat = repo.status(node1, node2, m,
4723 4739 'ignored' in show, 'clean' in show, 'unknown' in show,
4724 4740 opts.get('subrepos'))
4741 if terse:
4742 stat = cmdutil.tersestatus(repo.root, stat, terse,
4743 repo.dirstate._ignore, opts.get('ignored'))
4725 4744 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4726 4745
4727 4746 if (opts.get('all') or opts.get('copies')
@@ -231,7 +231,7 b' Show all commands + options'
231 231 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
232 232 remove: after, force, subrepos, include, exclude
233 233 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, subrepos
234 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
234 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
235 235 summary: remote
236 236 update: clean, check, merge, date, rev, tool
237 237 addremove: similarity, subrepos, include, exclude, dry-run
General Comments 0
You need to be logged in to leave comments. Login now