Murky / Source / HgDir.m

//
//  HgDir.m
//  Murky
//
//  Copyright 2008-2009 Jens Alfke. All rights reserved.
//

#import "HgDir.h"
#import "HgRepository.h"


#define kStatusPriorities "I!U?RAM"


@implementation HgDir


- (id) initWithName: (NSString*)name directory: (HgDir*)parentDir
{
    self = [super initWithName: name directory: parentDir];
    if (self != nil) {
        _status = kClean;
        _files = [[NSMutableArray alloc] init];
        _sorted = YES;
    }
    return self;
}

- (id) initRootOfRevision: (HgRevision*)revision
{
    self = [self initWithName: revision.absolutePath.lastPathComponent directory: nil];
    if( self ) {
        _revision = revision;
    }
    return self;
}


- (BOOL) isFile         {return NO;}
- (BOOL) isDirectory    {return YES;}

- (HgRevision*) revision
{
    return _revision ?_revision :[super revision];
}


- (void) _computeStatus
{
    int maxPos = -1;
    for( HgFile *file in _files ) {
        if( file.isDirectory )
            [(HgDir*)file _computeStatus];
        const char *pos = strchr(kStatusPriorities,file.status);
        if( pos )
            maxPos = MAX(maxPos, pos-kStatusPriorities);
    }
    if( maxPos >= 0 )
        self.status = kStatusPriorities[maxPos];
    else
        self.status = kClean;
}

- (NSMutableString*) _getPath: (BOOL)absolute
{
    if( _dir ) {
        NSMutableString *path = [super _getPath: absolute];
        [path appendString: @"/"];
        return path;
    } else if( absolute ) {
        return self.repository.absolutePath.mutableCopy;
    } else {
        return [NSMutableString string];
    }
}

- (NSArray*) files
{
    if( ! _sorted ) {
        [_files sortUsingSelector: @selector(compare:)];
        _sorted = YES;
    }
    return _files;
}

- (HgFile*) fileNamed: (NSString*)name
{
    for( HgFile *file in _files )
        if( [file.name isEqualToString: name] )
            return file;
    return nil;
}


- (HgFile*) fileAtPath: (NSString*)path
{
    HgFile *file = self;
    for( NSString *name in [path componentsSeparatedByString: @"/"] ) {
        if( name.length > 0 ) {
            if( ! file.isDirectory )
                return nil;
            file = [(HgDir*)file fileNamed: name];
            if( ! file )
                return nil;
        }
    }
    return file;
}


- (void) addFilesMatching: (Predicate)predicate toArray: (NSMutableArray*)array
{
    for( HgFile *file in self.files )
        [file addFilesMatching: predicate toArray: array];
}


- (NSArray*) allFilesMatching: (Predicate)predicate
{
    NSMutableArray *files = [NSMutableArray array];
    [self addFilesMatching: predicate toArray: files];
    return files;
}


- (void) _addFile: (HgFile*)file
{
    [self willChangeValueForKey: @"files"];
    [_files addObject: file];
    _sorted = NO;
    [self didChangeValueForKey: @"files"];
}


- (HgFile*) addFileNamed: (NSString*)name isDirectory: (BOOL)isDir
{
    HgFile *file = [self fileNamed: name];
    if( file ) {
        if( file.isDirectory != isDir )
            return nil;
    } else {
        Class klass = isDir ?[HgDir class] :[HgFile class];
        file = [[klass alloc] initWithName: name directory: self];
        [self _addFile: file];
    }
    return file;
}

- (HgFile*) addFileWithPath: (NSString*)path
{
    HgFile *file = self;
    NSArray *components = [path componentsSeparatedByString: @"/"];
    unsigned n = components.count;
    for( unsigned i=0; i<n; i++ ) {
        NSString *name = [components objectAtIndex: i];
        if( name.length > 0 ) {
            BOOL isDir = (i<n-1);
            file = [(HgDir*)file addFileNamed: name isDirectory: isDir];
            if( ! file )
                return nil;
        }
    }
    return file;
}


- (void) _removeFile: (HgFile*)file
{
    [self willChangeValueForKey: @"files"];
    [_files removeObjectIdenticalTo: file];
    [self didChangeValueForKey: @"files"];

    if( _files.count == 0 )
        [_dir _removeFile: self];
}


- (void) updateStatusFromOutput: (NSArray*)statusLines
{
    NSString *myPath = self.path;
    NSMutableSet *oldFiles = [NSMutableSet setWithArray: [self allFilesMatching: TruePredicate]];
    
    for( NSString *line in statusLines ) {
        if( line.length > 0 ) {
            if( line.length<3 || [line characterAtIndex: 1] != ' ' ) {
                Warn(@"Unknown line from hg status: \"%@\"",line);
                continue;
            }
            HgStatus status = (HgStatus) [line characterAtIndex: 0];
            if( status != kIgnored ) {
                // Get path relative to me:
                NSString *path = [line substringFromIndex: 2];
                if( myPath.length > 0 && ! [path hasPrefix: myPath] )
                    continue;
                else if (status == kNotTracked && [path hasSuffix: @".orig"]) {
                    // Ignore .orig files not in repo; they're backups from 'hg revert'.
                    LogTo(HgFile,@"Ignoring backup: %@", line);
                    continue;
                }
                // OK, add/update it:
                path = [path substringFromIndex: myPath.length];
                HgFile *file = [self addFileWithPath: path];
                file.status = status;
                [oldFiles removeObject: file];
                LogTo(HgFile,@"    %c %@", file.status,file.path);
            }
        }
    }
    
    // Remove files that didn't appear in the list:
    for( HgFile *file in oldFiles )
        [file remove];
    
    // Status changes may propagate all the way up to the root
    [self.root _computeStatus];
}


@end
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.