mercurial/cmdutil.py
changeset 33548 4cd4344a53c4
parent 33509 a3acacbd0ff3
child 33616 5ac845ca059a
child 33694 6f4bc9688ca9
equal deleted inserted replaced
33547:a6af8560494e 33548:4cd4344a53c4
   397     def recordinwlock(ui, repo, message, match, opts):
   397     def recordinwlock(ui, repo, message, match, opts):
   398         with repo.wlock():
   398         with repo.wlock():
   399             return recordfunc(ui, repo, message, match, opts)
   399             return recordfunc(ui, repo, message, match, opts)
   400 
   400 
   401     return commit(ui, repo, recordinwlock, pats, opts)
   401     return commit(ui, repo, recordinwlock, pats, opts)
       
   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
   402 
   574 
   403 def findpossible(cmd, table, strict=False):
   575 def findpossible(cmd, table, strict=False):
   404     """
   576     """
   405     Return cmd -> (aliases, command table entry)
   577     Return cmd -> (aliases, command table entry)
   406     for each matching command.
   578     for each matching command.