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. |