Source

Murky with Sparkle / Source / HgUncommittedRevision.m

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

#import "HgUncommittedRevision.h"
#import "HgDir.h"
#import "HgRepository.h"
#import "HgOperation.h"


@implementation HgUncommittedRevision

- (id) initWithRepository: (HgRepository*)repo
{
    self = [super initWithRepository: repo localNumber: NSNotFound identifier: kUncommittedRevisionID];
    if( self ) {
    }
    return self;
}

- (BOOL) isUncommitted              {return YES;}

- (NSString*) localString           {return @"current";}

- (NSString*) identifierString      {return nil;}

- (BOOL) _getDetails                {return YES;}

- (NSString*) comment
{
    return _comment.length ?_comment :@"--locally modified--";
}

- (void) setComment: (NSString*)str
{
    _comment = str.copy;
    _shortComment = nil;
}

- (NSString*) formattedDescription                  {return _comment;}
- (void) setFormattedDescription: (NSString*)str    {self.comment = str;}

- (NSData*) getFileContents: (HgFile*)file error: (NSError**)outError
{
    *outError = nil;
    return file.contents;
}

- (NSString*) getPathToFileContents: (HgFile*)file inTempDir: (HgTempDir*)tempDir error: (NSError**)outError
{
    return file.absolutePath;
}


#pragma mark -
#pragma mark FILES:


- (void) _clearUncleanFiles
{
    if( _uncleanFiles ) {
        [self willChangeValueForKey: @"uncleanFiles"];
        _uncleanFiles = nil;
        [self didChangeValueForKey: @"uncleanFiles"];
    }
}


- (BOOL) updateStatus: (HgDir*)dir error: (NSError**)outError
{
    if( [super updateStatus: dir error: outError] ) {
        [self _clearUncleanFiles];
        return YES;
    } else
        return NO;
}


- (BOOL) updatePaths: (NSSet*)paths error: (NSError**)outError
{
    if (!_root) {
        // If I haven't loaded my files at all, just load 'em and return. Otherwise
        // the code below will end up doing that and then calling -updateStatus again,
        // which is inefficient.
        [self root];
        return YES;
    }
    
    BOOL any = NO;
    for( NSString *path in paths ) {
        HgFile *dir = [self fileAtPath: path];
        if( dir.isDirectory && [super updateStatus: (HgDir*)dir error: outError] )
            any = YES;
    }
    if( any )
        [self _clearUncleanFiles];
    return YES;
}


#pragma mark -
#pragma mark FILE OPERATIONS:

- (NSString*) diffFile: (HgFile*)file withRevisionNumber: (int)otherRev error: (NSError**)outError
{
    if( otherRev==_localNumber )
        return @"";
    // Same as inherited method except only one --rev flag
    [file.repository ignoreNextFileChanges];       // ignore temp file that "hg diff" creates in the repo root dir
    HgOperation *op = [[HgOperation alloc] initWithDirectory: file.root
                                                     command: @"diff",
                       @"--rev", [NSString stringWithFormat: @"%i",otherRev],
                       @"--", file.path, nil];
    if( ! [op run] )
        return nil;
    return op.output;    
}


#pragma mark -
#pragma mark ACTIONS:


- (BOOL) applyCommand: (NSString*)command
            arguments: (NSArray*)arguments
              toFiles: (NSArray*)files
                error: (NSError**)outError
{
    NSMutableArray *args = arguments ?[arguments mutableCopy] :[NSMutableArray array];
    if( files ) {
        if( files.count==0 )
            return YES; // no-op
        [args addObject: @"--"];
        for( HgFile *file in files )
            [args addObject: file.path];
    }
    HgOperation *op = [[HgOperation alloc] initWithDirectory: self
                                                     command: command
                                                   arguments: args];
    [_repository ignoreNextFileChanges];
    BOOL result = [op run: outError];
    return result && [self updateStatus: _root error: outError];
}


- (BOOL) addFiles: (NSArray*)files error: (NSError**)outError
{
    return [self applyCommand: @"add" arguments: nil toFiles: files error: outError];
}

- (BOOL) removeFiles: (NSArray*)files error: (NSError**)outError
{
    Assert(files);
    NSMutableArray *realFiles = [NSMutableArray array], *missingFiles = [NSMutableArray array];
    for( HgFile *file in files ) {
        switch( file.status ) {
            case kNotTracked:
                // Just delete an untracked ('?') file immediately:
                if( ! [[NSFileManager defaultManager] removeItemAtPath: file.absolutePath error: outError] )
                    return NO;
                break;
            case kDeleted:
                [missingFiles addObject: file];
                break;
            default:
                [realFiles addObject: file];
                break;
        }
    }
    
    if( missingFiles.count )
        if( ! [self applyCommand: @"remove" 
                        arguments: [NSArray arrayWithObject: @"--after"]
                          toFiles: missingFiles
                            error: outError] )
            return NO;
    
    if( realFiles.count )
        if( ! [self applyCommand: @"remove" 
                        arguments: [NSArray arrayWithObject: @"--force"]    // allows modified files to be removed
                          toFiles: realFiles
                            error: outError] )
            return NO;
    return YES;
}

- (BOOL) revertFiles: (NSArray*)files backup: (BOOL)backup error: (NSError**)outError
{
    return [self applyCommand: @"revert"
                    arguments: (backup ?nil :$array(@"--no-backup"))
                      toFiles: files
                        error: outError];
}

- (BOOL) commitFiles: (NSArray*)files
             message: (NSString*)message 
               error: (NSError**)outError
{
    if( message.length==0 )
        return MYMiscError(outError, @"Missing commit message");
    if( ! [self applyCommand: @"commit" 
                    arguments: [NSArray arrayWithObjects: @"--message", message, nil]
                      toFiles: files
                        error: outError] )
        return NO;
    
    return YES;
}


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