CLogChangedPath::CLogChangedPath
    ( const CRevisionInfoContainer::CChangesIterator& iter
    , const CDictionaryBasedTempPath& logPath)
    : path (iter.GetPath())
    , copyFromPath (NULL, (index_t)NO_INDEX)
    , copyFromRev (0)
{
    flags.nodeKind = static_cast<svn_node_kind_t>(iter->GetPathType());
    flags.action = (DWORD)iter.GetAction() / 4;
    flags.textModifies = static_cast<svn_tristate_t>(iter->GetTextModifies());
    flags.propsModifies = static_cast<svn_tristate_t>(iter->GetPropsModifies());

    // check relevance for log path

    flags.relevantForStartPath = logPath.IsSameOrParentOf (path)
                              || logPath.IsSameOrChildOf (path);

    // set copy-from info, if available

    if (iter.HasFromPath() && (iter.GetFromRevision() != NO_REVISION))
    {
        copyFromPath = iter.GetFromPath();
        copyFromRev = iter.GetFromRevision();
    }
}
bool CLogIteratorBase::ContainsCopyOrDelete
    ( const CRevisionInfoContainer::CChangesIterator& first
    , const CRevisionInfoContainer::CChangesIterator& last)
{
    // close examination of all changes

    for (CRevisionInfoContainer::CChangesIterator iter = first
        ; iter != last
        ; ++iter)
    {
        // the only non-critical operation is the mere modification

        CRevisionInfoContainer::TChangeAction action = iter.GetAction();
        if (action != CRevisionInfoContainer::ACTION_CHANGED)
            return true;
    }

    // no copy / delete / replace found

    return false;
}
bool CLogIteratorBase::PathInRevision
    ( const CRevisionInfoContainer::CChangesIterator& first
    , const CRevisionInfoContainer::CChangesIterator& last
    , const CDictionaryBasedTempPath& path)
{
    // close examination of all changes

    for ( CRevisionInfoContainer::CChangesIterator iter = first
        ; iter != last
        ; ++iter)
    {
        // if (and only if) path is a cached path,
        // it may be a parent of the changedPath
        // (i.e. report a change of this or some sub-path)

        CDictionaryBasedPath changedPath = iter->GetPath();
        if (   path.IsFullyCachedPath()
            && path.GetBasePath().IsSameOrParentOf (changedPath))
            return true;

        // this change affects a true parent path or completely unrelated path
        // -> ignore mere modifications (e.g. properties on a folder)

        if (iter->GetAction() == CRevisionInfoContainer::ACTION_CHANGED)
            continue;

        // this is an add / delete / replace.
        // does it affect our path?

        if (changedPath.IsSameOrParentOf (path.GetBasePath()))
            return true;
    }

    // no paths that we were looking for

    return false;
}
bool CLogIteratorBase::InternalHandleCopyAndDelete
    ( const CRevisionInfoContainer::CChangesIterator& first
    , const CRevisionInfoContainer::CChangesIterator& last
    , const CDictionaryBasedPath& revisionRootPath)
{
    // any chance that this revision affects our search path?

    if (!revisionRootPath.IsValid())
        return false;

    if (!revisionRootPath.IsSameOrParentOf (path.GetBasePath()))
        return false;

    // close examination of all changes

    CRevisionInfoContainer::CChangesIterator bestRename = last;
    for ( CRevisionInfoContainer::CChangesIterator iter = first
        ; iter != last
        ; ++iter)
    {
        // most entries will just be file content changes
        // -> skip them efficiently

        CRevisionInfoContainer::TChangeAction action = iter.GetAction();
        if (action == CRevisionInfoContainer::ACTION_CHANGED)
            continue;

        // deletion / copy / rename / replacement
        // -> skip, if our search path is not affected (only some sub-path)

        CDictionaryBasedPath changedPath = iter->GetPath();
        if (!changedPath.IsSameOrParentOf (path.GetBasePath()))
            continue;

        // now, this is serious

        switch (action)
        {
            // rename?

            case CRevisionInfoContainer::ACTION_ADDED:
            case CRevisionInfoContainer::ACTION_REPLACED:
            case CRevisionInfoContainer::ACTION_MOVED:
            case CRevisionInfoContainer::ACTION_MOVEREPLACED:
            {
                if (iter.HasFromPath())
                {
                    // continue search on copy source path

                    // The last copy found will also be the one closed
                    // to our searchPath (there may be multiple renames,
                    // if the base path got renamed).

                    assert ( ((bestRename == last)
                            || (bestRename.GetPathID() < iter.GetPathID()))
                            && "parent ADDs are not in strict order");

                    bestRename = iter;
                }
                else
                {
                    // as part of a copy / rename, the parent path
                    // may have been added in just the same revision.
                    //
                    // example:
                    //
                    // our path: /trunk/file
                    // renamed to
                    // /trunk/project/file
                    //
                    // this can only happen if
                    // /trunk/project
                    // is added first (usually without a copy from path)
                    //
                    // Stop iteration only if we found an ADD of
                    // the exact search path.

                    if (path == changedPath)
                    {
                        // the path we are following actually started here.

                        addRevision = revision;
                        addPath = path;

                        revision = (revision_t)NO_REVISION;
                        path.Invalidate();
                        return true;
                    }
                }
            }
            break;

            case CRevisionInfoContainer::ACTION_DELETED:
            {
                // deletions are possible!
                // but we don't need to do anything with them.
            }
            break;

            // there should be no other

            default:
            {
                assert (0);
            }
        }
    }

    // there was a rename / copy from some older path,rev

    if (bestRename != last)
    {
        addRevision = revision;
        addPath = path;

        path = path.ReplaceParent ( bestRename.GetPath()
                                  , bestRename.GetFromPath());
        revision = bestRename.GetFromRevision();

        return true;
    }

    // all fine, no special action required

    return false;
}
void CFullHistory::BuildForwardCopies()
{
    // iterate through all revisions and fill copyToRelation:
    // for every copy-from info found, add an entry

    const CRevisionIndex& revisions = cache->GetRevisions();
    const CRevisionInfoContainer& revisionInfo = cache->GetLogInfo();

    // for all revisions ...

    copiesContainer.reserve (revisions.GetLastRevision());
    for ( revision_t revision = revisions.GetFirstRevision()
        , last = revisions.GetLastRevision()
        ; revision < last
        ; ++revision)
    {
        // ... in the cache ...

        index_t index = revisions[revision];
        if (   (index != NO_INDEX)
            && (revisionInfo.GetSumChanges (index) & CRevisionInfoContainer::HAS_COPY_FROM))
        {
            // ... examine all changes ...

            for ( CRevisionInfoContainer::CChangesIterator
                    iter = revisionInfo.GetChangesBegin (index)
                , end = revisionInfo.GetChangesEnd (index)
                ; iter != end
                ; ++iter)
            {
                // ... and if it has a copy-from info ...

                if (iter->HasFromPath())
                {
                    // ... add it to the list

                    SCopyInfo* copyInfo = SCopyInfo::Create (copyInfoPool);

                    copyInfo->fromRevision = iter->GetFromRevision();
                    copyInfo->fromPathIndex = iter->GetFromPathID();
                    copyInfo->toRevision = revision;
                    copyInfo->toPathIndex = iter->GetPathID();

                    copiesContainer.push_back (copyInfo);
                }
            }
        }
    }

    // sort container by source revision and path

    copyToRelation = new SCopyInfo*[copiesContainer.size()];
    copyToRelationEnd = copyToRelation + copiesContainer.size();

    copyFromRelation = new SCopyInfo*[copiesContainer.size()];
    copyFromRelationEnd = copyFromRelation + copiesContainer.size();

    // in early phases, there will be no copies
    // -> front() iterator is invalid

    if (!copiesContainer.empty())
    {
        size_t bytesToCopy = copiesContainer.size() * sizeof (SCopyInfo*[1]);

        memcpy (copyToRelation, &copiesContainer.front(), bytesToCopy);
        memcpy (copyFromRelation, &copiesContainer.front(), bytesToCopy);

        std::sort (copyToRelation, copyToRelationEnd, &AscendingToRevision);
        std::sort (copyFromRelation, copyFromRelationEnd, &AscendingFromRevision);
    }
}