def build_rev_history_status(gs, rev, relevant_revs, status_cache={}, options={})
    return status_cache[rev] if status_cache[rev]
    stat = nil
    if relevant_revs[rev]
      parent_revs = gs.parent_revs(rev)
      if parent_revs.size > 0
        # build status for all parent nodes
        parent_stats = parent_revs.collect do |p|
          build_rev_history_status(gs, p, relevant_revs, status_cache, options)
        end

        # if this is a merge commit with multiple parents
        # we decide to use the first commit (git primary parent)
        # note that it's not really important, which one we choose
        # just make sure to use the same commit when checking for changed files
        base_stat = parent_stats.first
        
        changed_files = gs.changed_files(rev, parent_revs.first)

        # build list of modules in this commit
        module_dirs = base_stat.modules.collect{|m| m.dir}
        changed_files.each do |f|
          if File.basename(f.path) == RimInfo::InfoFileName
            if f.kind == :added
              module_dirs << File.dirname(f.path)
            elsif f.kind == :deleted
              module_dirs.delete(File.dirname(f.path))
            end
          end
        end

        # a module needs to be checked if any of the files within were touched
        check_dirs = module_dirs.select{|d| changed_files.any?{|f| f.path.start_with?(d)} }

        module_stats = []
        # check out all modules to be checked at once
        if check_dirs.size > 0
          gs.within_exported_rev(rev, check_dirs) do |ws|
            check_dirs.each do |d|
              module_stats << build_module_status(ws, File.join(ws, d))
            end
          end
        end
        (module_dirs - check_dirs).each do |d|
          base_mod = base_stat.modules.find{|m| m.dir == d}
          module_stats << RevStatus::ModuleStatus.new(d, base_mod.rim_info, base_mod.dirty?)
        end

        stat = RevStatus.new(module_stats)
        stat.git_rev = gs.rev_sha1(rev)
        stat.parents.concat(parent_stats)
      else
        # no parents, need to do a full check
        if options[:fast]
          stat = rev_status_fast(gs, rev)
        else
          stat = rev_status(gs, rev)
        end
      end
    else
      # first "non-relevant", do the full check
      if options[:fast]
        stat = rev_status_fast(gs, rev)
      else
        stat = rev_status(gs, rev)
      end
    end
    status_cache[rev] = stat
  end
def build_rev_history_status(gs, rev, relevant_revs, status_cache={}, options={})
    return status_cache[rev] if status_cache[rev]
    stat = nil
    if relevant_revs[rev]
      parent_revs = gs.parent_revs(rev)
      if parent_revs.size > 0
        # build status for all parent nodes
        parent_stats = parent_revs.collect do |p|
          build_rev_history_status(gs, p, relevant_revs, status_cache, options)
        end

        # if this is a merge commit with multiple parents
        # we decide to use the first commit (git primary parent)
        # note that it's not really important, which one we choose
        # just make sure to use the same commit when checking for changed files
        base_stat = parent_stats.first
        
        changed_files = gs.changed_files(rev, parent_revs.first)

        # build list of modules in this commit
        module_dirs = base_stat.modules.collect{|m| m.dir}
        changed_files.each do |f|
          if File.basename(f.path) == RimInfo::InfoFileName
            if f.kind == :added
              module_dirs << File.dirname(f.path)
            elsif f.kind == :deleted
              module_dirs.delete(File.dirname(f.path))
            end
          end
        end

        # a module needs to be checked if any of the files within were touched
        check_dirs = module_dirs.select{|d| changed_files.any?{|f| f.path.start_with?(d)} }

        module_stats = []
        # check out all modules to be checked at once
        if check_dirs.size > 0
          gs.within_exported_rev(rev, check_dirs) do |ws|
            check_dirs.each do |d|
              module_stats << build_module_status(ws, File.join(ws, d))
            end
          end
        end
        (module_dirs - check_dirs).each do |d|
          base_mod = base_stat.modules.find{|m| m.dir == d}
          module_stats << RevStatus::ModuleStatus.new(d, base_mod.rim_info, base_mod.dirty?)
        end

        stat = RevStatus.new(module_stats)
        stat.git_rev = gs.rev_sha1(rev)
        stat.parents.concat(parent_stats)
      else
        # no parents, need to do a full check
        if options[:fast]
          stat = rev_status_fast(gs, rev)
        else
          stat = rev_status(gs, rev)
        end
      end
    else
      # first "non-relevant", do the full check
      if options[:fast]
        stat = rev_status_fast(gs, rev)
      else
        stat = rev_status(gs, rev)
      end
    end
    status_cache[rev] = stat
  end
building of the status of an ancestor chain works by checking
 the dirty state of modules only when any files affecting some module
 were changed; otherwise the status of the module in the ancestor is assumed

 for this to work, the chain must be walked from older commit to newer ones

 at the end of the chain, the status must be calculated in the regular "non-fast" way