Package org.uberfire.java.nio.fs.jgit.util

Source Code of org.uberfire.java.nio.fs.jgit.util.JGitUtil$JGitPathInfo

/*
* Copyright 2013 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.uberfire.java.nio.fs.jgit.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;

import org.eclipse.jgit.api.CreateBranchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.LogCommand;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.MultipleParentsNotAllowedException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.uberfire.commons.data.Pair;
import org.uberfire.java.nio.IOException;
import org.uberfire.java.nio.base.FileTimeImpl;
import org.uberfire.java.nio.base.version.VersionAttributes;
import org.uberfire.java.nio.base.version.VersionHistory;
import org.uberfire.java.nio.base.version.VersionRecord;
import org.uberfire.java.nio.file.NoSuchFileException;
import org.uberfire.java.nio.file.attribute.BasicFileAttributes;
import org.uberfire.java.nio.file.attribute.FileTime;
import org.uberfire.java.nio.fs.jgit.CommitInfo;
import org.uberfire.java.nio.fs.jgit.JGitFileSystem;

import static java.util.Collections.*;
import static org.apache.commons.io.FileUtils.*;
import static org.eclipse.jgit.lib.Constants.*;
import static org.eclipse.jgit.lib.FileMode.*;
import static org.eclipse.jgit.treewalk.filter.PathFilterGroup.*;
import static org.eclipse.jgit.util.FS.*;
import static org.uberfire.commons.data.Pair.*;
import static org.uberfire.commons.validation.Preconditions.*;

public final class JGitUtil {

    private JGitUtil() {
    }

    public static Git newRepository( final File repoFolder,
                                     final boolean bare ) throws IOException {
        checkNotNull( "repoFolder", repoFolder );

        try {
            return Git.init().setBare( bare ).setDirectory( repoFolder ).call();
        } catch ( GitAPIException e ) {
            throw new IOException( e );
        }
    }

    public static List<Ref> branchList( final Git git ) {
        checkNotNull( "git", git );
        return branchList( git, null );
    }

    public static List<Ref> branchList( final Git git,
                                        final ListBranchCommand.ListMode listMode ) {
        checkNotNull( "git", git );
        try {
            return git.branchList().setListMode( listMode ).call();
        } catch ( GitAPIException e ) {
            throw new RuntimeException( e );
        }
    }

    public static InputStream resolveInputStream( final Git git,
                                                  final String treeRef,
                                                  final String path ) {
        checkNotNull( "git", git );
        checkNotEmpty( "treeRef", treeRef );
        checkNotEmpty( "path", path );

        final String gitPath = fixPath( path );

        RevWalk rw = null;
        TreeWalk tw = null;
        try {
            final ObjectId tree = git.getRepository().resolve( treeRef + "^{tree}" );
            rw = new RevWalk( git.getRepository() );
            tw = new TreeWalk( git.getRepository() );
            tw.setFilter( createFromStrings( singleton( gitPath ) ) );
            tw.reset( tree );
            while ( tw.next() ) {
                if ( tw.isSubtree() && !gitPath.equals( tw.getPathString() ) ) {
                    tw.enterSubtree();
                    continue;
                }
                return new ByteArrayInputStream( git.getRepository().open( tw.getObjectId( 0 ), Constants.OBJ_BLOB ).getBytes() );
            }
        } catch ( final Throwable t ) {
            throw new NoSuchFileException( "Can't find '" + gitPath + "' in tree '" + treeRef + "'" );
        } finally {
            if ( rw != null ) {
                rw.dispose();
            }
            if ( tw != null ) {
                tw.release();
            }
        }
        throw new NoSuchFileException( "" );
    }

    public static String fixPath( final String path ) {

        if ( path.equals( "/" ) ) {
            return "";
        }

        boolean startsWith = path.startsWith( "/" );
        boolean endsWith = path.endsWith( "/" );
        if ( startsWith && endsWith ) {
            return path.substring( 1, path.length() - 1 );
        }
        if ( startsWith ) {
            return path.substring( 1 );
        }
        if ( endsWith ) {
            return path.substring( 0, path.length() - 1 );
        }
        return path;
    }

    public static Git cloneRepository( final File repoFolder,
                                       final String fromURI,
                                       final boolean bare,
                                       final CredentialsProvider credentialsProvider ) {

        if ( !repoFolder.getName().endsWith( DOT_GIT_EXT ) ) {
            throw new RuntimeException( "Invalid name" );
        }

        try {
            final File gitDir = RepositoryCache.FileKey.resolve( repoFolder, DETECTED );
            final Repository repository;
            final Git git;
            if ( gitDir != null && gitDir.exists() ) {
                repository = new FileRepository( gitDir );
                git = new Git( repository );
            } else {
                git = Git.cloneRepository()
                        .setBare( bare )
                        .setCloneAllBranches( true )
                        .setURI( fromURI )
                        .setDirectory( repoFolder )
                        .setCredentialsProvider( credentialsProvider )
                        .call();
                repository = git.getRepository();
            }

            fetchRepository( git, credentialsProvider );

            repository.close();

            return git;
        } catch ( final Exception ex ) {
            try {
                forceDelete( repoFolder );
            } catch ( final java.io.IOException e ) {
                throw new RuntimeException( e );
            }
            throw new RuntimeException( ex );
        }
    }

    public static void fetchRepository( final Git git,
                                        final CredentialsProvider credentialsProvider,
                                        final RefSpec... refSpecs )
            throws InvalidRemoteException {
        final List<RefSpec> specs = new ArrayList<RefSpec>();
        if ( refSpecs == null || refSpecs.length == 0 ) {
            specs.add( new RefSpec( "+refs/heads/*:refs/remotes/origin/*" ) );
            specs.add( new RefSpec( "+refs/tags/*:refs/tags/*" ) );
            specs.add( new RefSpec( "+refs/notes/*:refs/notes/*" ) );
        } else {
            specs.addAll( Arrays.asList( refSpecs ) );
        }

        try {
            git.fetch()
                    .setCredentialsProvider( credentialsProvider )
                    .setRefSpecs( specs )
                    .call();

        } catch ( final InvalidRemoteException e ) {
            throw e;
        } catch ( final Exception ex ) {
            throw new RuntimeException( ex );
        }
    }

    public static void syncRepository( final Git git,
                                       final CredentialsProvider credentialsProvider,
                                       final String origin,
                                       boolean force )
            throws InvalidRemoteException {

        if ( origin == null || origin.isEmpty() ) {
            fetchRepository( git, credentialsProvider );
        } else {
            try {
                final StoredConfig config = git.getRepository().getConfig();
                config.setString( "remote", "upstream", "url", origin );
                config.save();
            } catch ( final Exception ex ) {
                throw new RuntimeException( ex );
            }

            final List<RefSpec> specs = new ArrayList<RefSpec>();
            specs.add( new RefSpec( "+refs/heads/*:refs/remotes/upstream/*" ) );
            specs.add( new RefSpec( "+refs/tags/*:refs/tags/*" ) );
            specs.add( new RefSpec( "+refs/notes/*:refs/notes/*" ) );

            try {
                git.fetch()
                        .setCredentialsProvider( credentialsProvider )
                        .setRefSpecs( specs )
                        .setRemote( origin )
                        .call();

                final List<Ref> branches = git.branchList().setListMode( ListBranchCommand.ListMode.ALL ).call();

                for ( final Ref branch : branches ) {
                    final String branchName = branch.getName().substring( branch.getName().lastIndexOf( "/" ) + 1 );
                    git.branchCreate()
                            .setName( branchName )
                            .setUpstreamMode( CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM )
                            .setStartPoint( "upstream/" + branchName )
                            .setForce( true )
                            .call();
                }

            } catch ( final InvalidRemoteException e ) {
                throw e;
            } catch ( final Exception ex ) {
                throw new RuntimeException( ex );
            }
        }
    }

    public static void pushRepository( final Git git,
                                       final CredentialsProvider credentialsProvider,
                                       final String origin,
                                       boolean force )
            throws InvalidRemoteException {

        if ( origin != null && !origin.isEmpty() ) {

            try {
                final StoredConfig config = git.getRepository().getConfig();
                config.setString( "remote", "upstream", "url", origin );
                config.save();
            } catch ( final Exception ex ) {
                throw new RuntimeException( ex );
            }

            final List<RefSpec> specs = new ArrayList<RefSpec>();
            specs.add( new RefSpec( "+refs/heads/*:refs/remotes/upstream/*" ) );
            specs.add( new RefSpec( "+refs/tags/*:refs/tags/*" ) );
            specs.add( new RefSpec( "+refs/notes/*:refs/notes/*" ) );

            try {
                git.push()
                        .setCredentialsProvider( credentialsProvider )
                        .setRefSpecs( specs )
                        .setRemote( origin )
                        .setForce( force )
                        .setPushAll()
                        .call();

            } catch ( final InvalidRemoteException e ) {
                throw e;
            } catch ( final Exception ex ) {
                throw new RuntimeException( ex );
            }
        }
    }

    public static void cherryPick( final Repository repo,
                                   final String targetBranch,
                                   final String... _commits ) {
        final String reflogPrefix = "cherry-pick:";

        final Git git = new Git( repo );

        RevCommit newHead = null;

        final RevWalk revWalk = new RevWalk( repo );

        final ObjectId[] commits = resolveObjectIds( git, _commits );

        if ( _commits.length != commits.length ) {
            throw new IOException( "Couldn't resolve some commits." );
        }

        try {
            final Ref headRef = getBranch( git, targetBranch );
            newHead = revWalk.parseCommit( headRef.getObjectId() );

            // loop through all refs to be cherry-picked
            for ( final ObjectId src : commits ) {
                final RevCommit srcCommit = revWalk.parseCommit( src );

                // get the parent of the commit to cherry-pick
                if ( srcCommit.getParentCount() != 1 ) {
                    throw new IOException( new MultipleParentsNotAllowedException(
                            MessageFormat.format(
                                    JGitText.get().canOnlyCherryPickCommitsWithOneParent,
                                    srcCommit.name(),
                                    Integer.valueOf( srcCommit.getParentCount() ) ) ) );
                }

                final RevCommit srcParent = srcCommit.getParent( 0 );
                revWalk.parseHeaders( srcParent );

                final RevCommit revCommit = revWalk.parseCommit( src );
                final RefUpdate ru = git.getRepository().updateRef( "refs/heads/" + targetBranch );
                if ( newHead == null ) {
                    ru.setExpectedOldObjectId( ObjectId.zeroId() );
                } else {
                    ru.setExpectedOldObjectId( newHead );
                }
                ru.setNewObjectId( src );
                ru.setRefLogMessage( reflogPrefix + " " + srcCommit.getShortMessage(), false );
                final RefUpdate.Result rc = ru.forceUpdate();
                switch ( rc ) {
                    case NEW:
                    case FORCED:
                    case FAST_FORWARD:
                        newHead = revCommit;
                        break;
                    case REJECTED:
                    case LOCK_FAILURE:
                        throw new ConcurrentRefUpdateException( JGitText.get().couldNotLockHEAD, ru.getRef(), rc );
                    default:
                        throw new JGitInternalException( MessageFormat.format( JGitText.get().updatingRefFailed, Constants.HEAD, src.toString(), rc ) );
                }
            }
        } catch ( final java.io.IOException e ) {
            throw new IOException( new JGitInternalException(
                    MessageFormat.format(
                            JGitText.get().exceptionCaughtDuringExecutionOfCherryPickCommand,
                            e ), e ) );
        } catch ( final Exception e ) {
            throw new IOException( e );
        } finally {
            revWalk.release();
        }
    }

    private static String calculateOurName( final Ref headRef ) {
        final String targetRefName = headRef.getTarget().getName();
        return Repository.shortenRefName( targetRefName );
    }

    public static ObjectId getTreeRefObjectId( final Repository repo,
                                               final String treeRef ) {
        try {
            return repo.resolve( treeRef + "^{tree}" );
        } catch ( java.io.IOException ex ) {
            throw new RuntimeException( ex );
        }
    }

    public static List<DiffEntry> getDiff( final Repository repo,
                                           final ObjectId oldRef,
                                           final ObjectId newRef ) {
        if ( oldRef == null || newRef == null || repo == null ) {
            return emptyList();
        }

        try {
            ObjectReader reader = repo.newObjectReader();
            CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
            oldTreeIter.reset( reader, oldRef );
            CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
            newTreeIter.reset( reader, newRef );
            return new CustomDiffCommand( repo ).setNewTree( newTreeIter ).setOldTree( oldTreeIter ).setShowNameAndStatusOnly( true ).call();
        } catch ( final Exception ex ) {
            throw new RuntimeException( ex );
        }
    }

    public static List<RevCommit> getCommits( final JGitFileSystem fs,
                                              final String branch,
                                              final ObjectId startRange,
                                              final ObjectId endRange ) {
        final List<RevCommit> list = new ArrayList<RevCommit>();
        RevWalk rw = null;
        try {
            // resolve branch
            rw = new RevWalk( fs.gitRepo().getRepository() );
            rw.markStart( rw.parseCommit( endRange ) );
            if ( startRange != null ) {
                rw.markUninteresting( rw.parseCommit( startRange ) );
            }
            for ( RevCommit rev : rw ) {
                list.add( rev );
            }
        } catch ( final Exception ignored ) {
        } finally {
            if ( rw != null ) {
                rw.dispose();
            }
        }
        return list;
    }

    public static void commit( final Git git,
                               final String branchName,
                               final String name,
                               final String email,
                               final String message,
                               final TimeZone timeZone,
                               final Date when,
                               final boolean amend,
                               final Map<String, File> content ) {
        commit( git, branchName, new CommitInfo( null, name, email, message, timeZone, when ), amend, new DefaultCommitContent( content ) );
    }

    public static boolean commit( final Git git,
                                  final String branchName,
                                  final CommitInfo commitInfo,
                                  final boolean amend,
                                  final CommitContent content ) {
        if ( content instanceof RevertCommitContent ) {
            return commit( git, branchName, commitInfo, amend, resolveObjectId( git, ( (RevertCommitContent) content ).getRefTree() ), content );
        } else {
            return commit( git, branchName, commitInfo, amend, null, content );
        }
    }

    public static boolean commit( final Git git,
                                  final String branchName,
                                  final CommitInfo commitInfo,
                                  final boolean amend,
                                  final ObjectId _originId,
                                  final CommitContent content ) {
        boolean hadEffecitiveCommit = true;
        final PersonIdent author = buildPersonIdent( git, commitInfo.getName(), commitInfo.getEmail(), commitInfo.getTimeZone(), commitInfo.getWhen() );

        try {
            final ObjectInserter odi = git.getRepository().newObjectInserter();
            try {
                // Create the in-memory index of the new/updated issue.
                final ObjectId headId = git.getRepository().resolve( branchName + "^{commit}" );

                final ObjectId originId;
                if ( _originId == null ) {
                    originId = git.getRepository().resolve( branchName + "^{commit}" );
                } else {
                    originId = _originId;
                }

                final DirCache index;
                if ( content instanceof DefaultCommitContent ) {
                    index = createTemporaryIndex( git, originId, (DefaultCommitContent) content );
                } else if ( content instanceof MoveCommitContent ) {
                    index = createTemporaryIndex( git, originId, (MoveCommitContent) content );
                } else if ( content instanceof CopyCommitContent ) {
                    index = createTemporaryIndex( git, originId, (CopyCommitContent) content );
                } else if ( content instanceof RevertCommitContent ) {
                    index = createTemporaryIndex( git, originId );
                } else {
                    index = null;
                }

                if ( index != null ) {
                    final ObjectId indexTreeId = index.writeTree( odi );

                    // Create a commit object
                    final CommitBuilder commit = new CommitBuilder();
                    commit.setAuthor( author );
                    commit.setCommitter( author );
                    commit.setEncoding( Constants.CHARACTER_ENCODING );
                    commit.setMessage( commitInfo.getMessage() );
                    //headId can be null if the repository has no commit yet
                    if ( headId != null ) {
                        if ( amend ) {
                            final List<ObjectId> parents = new LinkedList<ObjectId>();
                            final RevCommit previousCommit = new RevWalk( git.getRepository() ).parseCommit( headId );
                            final RevCommit[] p = previousCommit.getParents();
                            for ( final RevCommit revCommit : p ) {
                                parents.add( 0, revCommit.getId() );
                            }
                            commit.setParentIds( parents );
                        } else {
                            commit.setParentId( headId );
                        }
                    }
                    commit.setTreeId( indexTreeId );

                    // Insert the commit into the repository
                    final ObjectId commitId = odi.insert( commit );
                    odi.flush();

                    final RevWalk revWalk = new RevWalk( git.getRepository() );
                    try {
                        final RevCommit revCommit = revWalk.parseCommit( commitId );
                        final RefUpdate ru = git.getRepository().updateRef( "refs/heads/" + branchName );
                        if ( headId == null ) {
                            ru.setExpectedOldObjectId( ObjectId.zeroId() );
                        } else {
                            ru.setExpectedOldObjectId( headId );
                        }
                        ru.setNewObjectId( commitId );
                        ru.setRefLogMessage( "commit: " + revCommit.getShortMessage(), false );
                        final RefUpdate.Result rc = ru.forceUpdate();
                        switch ( rc ) {
                            case NEW:
                            case FORCED:
                            case FAST_FORWARD:
                                break;
                            case REJECTED:
                            case LOCK_FAILURE:
                                throw new ConcurrentRefUpdateException( JGitText.get().couldNotLockHEAD, ru.getRef(), rc );
                            default:
                                throw new JGitInternalException( MessageFormat.format( JGitText.get().updatingRefFailed, Constants.HEAD, commitId.toString(), rc ) );
                        }

                    } finally {
                        revWalk.release();
                    }
                } else {
                    hadEffecitiveCommit = false;
                }
            } finally {
                odi.release();
            }
        } catch ( final Throwable t ) {
            throw new RuntimeException( t );
        }
        return hadEffecitiveCommit;
    }

    private static PersonIdent buildPersonIdent( final Git git,
                                                 final String name,
                                                 final String _email,
                                                 final TimeZone timeZone,
                                                 final Date when ) {
        final TimeZone tz = timeZone == null ? TimeZone.getDefault() : timeZone;
        final String email = _email == null ? "" : _email;

        if ( name != null ) {
            if ( when != null ) {
                return new PersonIdent( name, email, when, tz );
            } else {
                return new PersonIdent( name, email );
            }
        }
        return new PersonIdent( git.getRepository() );
    }

    /**
     * Creates an in-memory index of the issue change.
     */
    private static DirCache createTemporaryIndex( final Git git,
                                                  final ObjectId headId,
                                                  final DefaultCommitContent commitContent ) {

        final Map<String, File> content = commitContent.getContent();

        final Map<String, Pair<File, ObjectId>> paths = new HashMap<String, Pair<File, ObjectId>>( content.size() );
        final Set<String> path2delete = new HashSet<String>();

        final DirCache inCoreIndex = DirCache.newInCore();
        final ObjectInserter inserter = git.getRepository().newObjectInserter();
        final DirCacheEditor editor = inCoreIndex.editor();

        try {
            for ( final Map.Entry<String, File> pathAndContent : content.entrySet() ) {
                final String gPath = fixPath( pathAndContent.getKey() );
                if ( pathAndContent.getValue() == null ) {
                    final TreeWalk treeWalk = new TreeWalk( git.getRepository() );
                    treeWalk.addTree( new RevWalk( git.getRepository() ).parseTree( headId ) );
                    treeWalk.setRecursive( true );
                    treeWalk.setFilter( PathFilter.create( gPath ) );

                    while ( treeWalk.next() ) {
                        path2delete.add( treeWalk.getPathString() );
                    }
                    treeWalk.release();
                } else {
                    try {
                        final InputStream inputStream = new FileInputStream( pathAndContent.getValue() );
                        try {
                            final ObjectId objectId = inserter.insert( Constants.OBJ_BLOB, pathAndContent.getValue().length(), inputStream );
                            paths.put( gPath, Pair.newPair( pathAndContent.getValue(), objectId ) );
                        } finally {
                            inputStream.close();
                        }
                    } catch ( final Exception ex ) {
                        throw new RuntimeException( ex );
                    }
                }
            }

            if ( headId != null ) {
                final TreeWalk treeWalk = new TreeWalk( git.getRepository() );
                final int hIdx = treeWalk.addTree( new RevWalk( git.getRepository() ).parseTree( headId ) );
                treeWalk.setRecursive( true );

                while ( treeWalk.next() ) {
                    final String walkPath = treeWalk.getPathString();
                    final CanonicalTreeParser hTree = treeWalk.getTree( hIdx, CanonicalTreeParser.class );

                    if ( paths.containsKey( walkPath ) && paths.get( walkPath ).getK2().equals( hTree.getEntryObjectId() ) ) {
                        paths.remove( walkPath );
                    }

                    if ( paths.get( walkPath ) == null && !path2delete.contains( walkPath ) ) {
                        final DirCacheEntry dcEntry = new DirCacheEntry( walkPath );
                        final ObjectId _objectId = hTree.getEntryObjectId();
                        final FileMode _fileMode = hTree.getEntryFileMode();

                        // add to temporary in-core index
                        editor.add( new DirCacheEditor.PathEdit( dcEntry ) {
                            @Override
                            public void apply( final DirCacheEntry ent ) {
                                ent.setObjectId( _objectId );
                                ent.setFileMode( _fileMode );
                            }
                        } );
                    }
                }
                treeWalk.release();
            }

            for ( final Map.Entry<String, Pair<File, ObjectId>> pathAndContent : paths.entrySet() ) {
                if ( pathAndContent.getValue().getK1() != null ) {
                    editor.add( new DirCacheEditor.PathEdit( new DirCacheEntry( pathAndContent.getKey() ) ) {
                        @Override
                        public void apply( final DirCacheEntry ent ) {
                            ent.setLength( pathAndContent.getValue().getK1().length() );
                            ent.setLastModified( pathAndContent.getValue().getK1().lastModified() );
                            ent.setFileMode( REGULAR_FILE );
                            ent.setObjectId( pathAndContent.getValue().getK2() );
                        }
                    } );
                }
            }

            editor.finish();
        } catch ( Exception e ) {
            throw new RuntimeException( e );
        } finally {
            inserter.release();
        }

        if ( path2delete.isEmpty() && paths.isEmpty() ) {
            //no changes!
            return null;
        }

        return inCoreIndex;
    }

    private static DirCache createTemporaryIndex( final Git git,
                                                  final ObjectId headId,
                                                  final MoveCommitContent commitContent ) {
        final Map<String, String> content = commitContent.getContent();

        final DirCache inCoreIndex = DirCache.newInCore();
        final DirCacheEditor editor = inCoreIndex.editor();

        try {
            if ( headId != null ) {
                final TreeWalk treeWalk = new TreeWalk( git.getRepository() );
                final int hIdx = treeWalk.addTree( new RevWalk( git.getRepository() ).parseTree( headId ) );
                treeWalk.setRecursive( true );

                while ( treeWalk.next() ) {
                    final String walkPath = treeWalk.getPathString();
                    final CanonicalTreeParser hTree = treeWalk.getTree( hIdx, CanonicalTreeParser.class );

                    final String toPath = content.get( walkPath );

                    if ( toPath == null ) {
                        final DirCacheEntry dcEntry = new DirCacheEntry( walkPath );
                        final ObjectId _objectId = hTree.getEntryObjectId();
                        final FileMode _fileMode = hTree.getEntryFileMode();

                        // add to temporary in-core index
                        editor.add( new DirCacheEditor.PathEdit( dcEntry ) {
                            @Override
                            public void apply( final DirCacheEntry ent ) {
                                ent.setObjectId( _objectId );
                                ent.setFileMode( _fileMode );
                            }
                        } );
                    } else {
                        final DirCacheEntry dcEntry = new DirCacheEntry( toPath );
                        final ObjectId _objectId = hTree.getEntryObjectId();
                        final FileMode _fileMode = hTree.getEntryFileMode();

                        editor.add( new DirCacheEditor.PathEdit( dcEntry ) {
                            @Override
                            public void apply( final DirCacheEntry ent ) {
                                ent.setFileMode( _fileMode );
                                ent.setObjectId( _objectId );
                            }
                        } );
                    }
                }
                treeWalk.release();
            }

            editor.finish();
        } catch ( final Exception e ) {
            throw new RuntimeException( e );
        }

        return inCoreIndex;
    }

    private static DirCache createTemporaryIndex( final Git git,
                                                  final ObjectId headId,
                                                  final CopyCommitContent commitContent ) {
        final Map<String, String> content = commitContent.getContent();

        final DirCache inCoreIndex = DirCache.newInCore();
        final DirCacheEditor editor = inCoreIndex.editor();

        try {
            if ( headId != null ) {
                final TreeWalk treeWalk = new TreeWalk( git.getRepository() );
                final int hIdx = treeWalk.addTree( new RevWalk( git.getRepository() ).parseTree( headId ) );
                treeWalk.setRecursive( true );

                while ( treeWalk.next() ) {
                    final String walkPath = treeWalk.getPathString();
                    final CanonicalTreeParser hTree = treeWalk.getTree( hIdx, CanonicalTreeParser.class );

                    final String toPath = content.get( walkPath );

                    final DirCacheEntry dcEntry = new DirCacheEntry( walkPath );
                    final ObjectId _objectId = hTree.getEntryObjectId();
                    final FileMode _fileMode = hTree.getEntryFileMode();

                    // add to temporary in-core index
                    editor.add( new DirCacheEditor.PathEdit( dcEntry ) {
                        @Override
                        public void apply( final DirCacheEntry ent ) {
                            ent.setObjectId( _objectId );
                            ent.setFileMode( _fileMode );
                        }
                    } );

                    if ( toPath != null ) {
                        final DirCacheEntry newdcEntry = new DirCacheEntry( toPath );
                        final ObjectId newObjectId = hTree.getEntryObjectId();
                        final FileMode newFileMode = hTree.getEntryFileMode();

                        editor.add( new DirCacheEditor.PathEdit( newdcEntry ) {
                            @Override
                            public void apply( final DirCacheEntry ent ) {
                                ent.setFileMode( newFileMode );
                                ent.setObjectId( newObjectId );
                            }
                        } );
                    }
                }
                treeWalk.release();
            }

            editor.finish();
        } catch ( final Exception e ) {
            throw new RuntimeException( e );
        }

        return inCoreIndex;
    }

    private static DirCache createTemporaryIndex( final Git git,
                                                  final ObjectId headId ) {

        final DirCache inCoreIndex = DirCache.newInCore();
        final DirCacheEditor editor = inCoreIndex.editor();

        try {
            if ( headId != null ) {
                final TreeWalk treeWalk = new TreeWalk( git.getRepository() );
                final int hIdx = treeWalk.addTree( new RevWalk( git.getRepository() ).parseTree( headId ) );
                treeWalk.setRecursive( true );

                while ( treeWalk.next() ) {
                    final String walkPath = treeWalk.getPathString();
                    final CanonicalTreeParser hTree = treeWalk.getTree( hIdx, CanonicalTreeParser.class );

                    final DirCacheEntry dcEntry = new DirCacheEntry( walkPath );
                    final ObjectId _objectId = hTree.getEntryObjectId();
                    final FileMode _fileMode = hTree.getEntryFileMode();

                    // add to temporary in-core index
                    editor.add( new DirCacheEditor.PathEdit( dcEntry ) {
                        @Override
                        public void apply( final DirCacheEntry ent ) {
                            ent.setObjectId( _objectId );
                            ent.setFileMode( _fileMode );
                        }
                    } );
                }
                treeWalk.release();
            }

            editor.finish();
        } catch ( final Exception e ) {
            throw new RuntimeException( e );
        }

        return inCoreIndex;
    }

    public static ObjectId resolveObjectId( final Git git,
                                            final String name ) {

        final ObjectId[] result = resolveObjectIds( git, name );
        if ( result == null || result.length == 0 ) {
            return null;
        }

        return result[ 0 ];
    }

    public static ObjectId[] resolveObjectIds( final Git git,
                                               final String... ids ) {
        final Collection<ObjectId> result = new ArrayList<ObjectId>();
        for ( final String id : ids ) {
            try {
                final Ref refName = getBranch( git, id );
                if ( refName != null ) {
                    result.add( refName.getObjectId() );
                    continue;
                }

                try {
                    final ObjectId _id = ObjectId.fromString( id );
                    if ( git.getRepository().getObjectDatabase().has( _id ) ) {
                        result.add( _id );
                    }
                } catch ( final IllegalArgumentException ignored ) {
                }
            } catch ( final java.io.IOException ignored ) {
            }
        }

        return result.toArray( new ObjectId[ result.size() ] );
    }

    public static Ref getBranch( final Git git,
                                 final String name ) {

        try {
            return git.getRepository().getRefDatabase().getRef( name );
        } catch ( java.io.IOException e ) {
        }

        return null;
    }

    public static void deleteBranch( final Git git,
                                     final Ref branch ) {
        try {
            git.branchDelete().setBranchNames( branch.getName() ).setForce( true ).call();
        } catch ( final GitAPIException e ) {
            throw new IOException( e );
        }
    }

    public static VersionAttributes buildVersionAttributes( final JGitFileSystem fs,
                                                            final String branchName,
                                                            final String path ) {
        final JGitPathInfo pathInfo = resolvePath( fs.gitRepo(), branchName, path );

        if ( pathInfo == null ) {
            throw new NoSuchFileException( path );
        }

        final String gPath = fixPath( path );

        final ObjectId id = resolveObjectId( fs.gitRepo(), branchName );

        final List<VersionRecord> records = new ArrayList<VersionRecord>();

        if ( id != null ) {
            try {
                final LogCommand logCommand = fs.gitRepo().log().add( id );
                if ( !gPath.isEmpty() ) {
                    logCommand.addPath( gPath );
                }

                for ( final RevCommit commit : logCommand.call() ) {

                    records.add( new VersionRecord() {
                        @Override
                        public String id() {
                            return commit.name();
                        }

                        @Override
                        public String author() {
                            return commit.getAuthorIdent().getName();
                        }

                        @Override
                        public String email() {
                            return commit.getAuthorIdent().getEmailAddress();
                        }

                        @Override
                        public String comment() {
                            return commit.getFullMessage();
                        }

                        @Override
                        public Date date() {
                            return commit.getAuthorIdent().getWhen();
                        }

                        @Override
                        public String uri() {
                            return fs.getPath( commit.name(), path ).toUri().toString();
                        }
                    } );
                }
            } catch ( Exception e ) {
                throw new RuntimeException( e );
            }
        }

        Collections.sort( records, new Comparator<VersionRecord>() {
            @Override
            public int compare( final VersionRecord o1,
                                final VersionRecord o2 ) {
                return o1.date().compareTo( o2.date() );
            }
        } );

        return new VersionAttributes() {
            @Override
            public VersionHistory history() {
                return new VersionHistory() {
                    @Override
                    public List<VersionRecord> records() {
                        return records;
                    }
                };
            }

            @Override
            public FileTime lastModifiedTime() {
                if ( records.size() > 0 ) {
                    return new FileTimeImpl( records.get( records.size() - 1 ).date().getTime() );
                }
                return null;
            }

            @Override
            public FileTime lastAccessTime() {
                return null;
            }

            @Override
            public FileTime creationTime() {
                if ( records.size() > 0 ) {
                    return new FileTimeImpl( records.get( 0 ).date().getTime() );
                }
                return null;
            }

            @Override
            public boolean isRegularFile() {
                return pathInfo.getPathType().equals( PathType.FILE );
            }

            @Override
            public boolean isDirectory() {
                return pathInfo.getPathType().equals( PathType.DIRECTORY );
            }

            @Override
            public boolean isSymbolicLink() {
                return false;
            }

            @Override
            public boolean isOther() {
                return false;
            }

            @Override
            public long size() {
                return pathInfo.getSize();
            }

            @Override
            public Object fileKey() {
                return pathInfo.getObjectId() == null ? null : pathInfo.getObjectId().toString();
            }
        };
    }

    public static BasicFileAttributes buildBasicAttributes( final JGitFileSystem fs,
                                                            final String branchName,
                                                            final String path ) {
        final JGitPathInfo pathInfo = resolvePath( fs.gitRepo(), branchName, path );

        if ( pathInfo == null ) {
            throw new NoSuchFileException( path );
        }

        final ObjectId id = resolveObjectId( fs.gitRepo(), branchName );
        final String gPath = fixPath( path );

        return new BasicFileAttributes() {

            private long lastModifiedDate = -1;
            private long creationDate = -1;

            @Override
            public FileTime lastModifiedTime() {
                if ( lastModifiedDate == -1L ) {
                    RevWalk revWalk = null;
                    try {
                        final LogCommand logCommand = fs.gitRepo().log().add( id ).setMaxCount( 1 );
                        if ( !gPath.isEmpty() ) {
                            logCommand.addPath( gPath );
                        }
                        revWalk = (RevWalk) logCommand.call();
                        lastModifiedDate = revWalk.iterator().next().getCommitterIdent().getWhen().getTime();
                    } catch ( Exception ex ) {
                        lastModifiedDate = 0;
                    } finally {
                        if ( revWalk != null ) {
                            revWalk.dispose();
                        }
                    }
                }
                return new FileTimeImpl( lastModifiedDate );
            }

            @Override
            public FileTime lastAccessTime() {
                return null;
            }

            @Override
            public FileTime creationTime() {
                if ( creationDate == -1L ) {
                    RevWalk revWalk = null;
                    try {
                        final LogCommand logCommand = fs.gitRepo().log().add( id ).setMaxCount( 1 );
                        if ( !gPath.isEmpty() ) {
                            logCommand.addPath( gPath );
                        }
                        revWalk = (RevWalk) logCommand.call();
                        creationDate = revWalk.iterator().next().getCommitterIdent().getWhen().getTime();
                    } catch ( Exception ex ) {
                        creationDate = 0;
                    } finally {
                        if ( revWalk != null ) {
                            revWalk.dispose();
                        }
                    }
                }
                return new FileTimeImpl( creationDate );
            }

            @Override
            public boolean isRegularFile() {
                return pathInfo.getPathType().equals( PathType.FILE );
            }

            @Override
            public boolean isDirectory() {
                return pathInfo.getPathType().equals( PathType.DIRECTORY );
            }

            @Override
            public boolean isSymbolicLink() {
                return false;
            }

            @Override
            public boolean isOther() {
                return false;
            }

            @Override
            public long size() {
                return pathInfo.getSize();
            }

            @Override
            public Object fileKey() {
                return pathInfo.getObjectId() == null ? null : pathInfo.getObjectId().toString();
            }
        };
    }

    public static void createBranch( final Git git,
                                     final String source,
                                     final String target ) {
        try {
            git.branchCreate().setName( target ).setStartPoint( source ).call();
        } catch ( GitAPIException e ) {
            throw new RuntimeException( e );
        }
    }

    public static boolean hasBranch( final Git git,
                                     final String branchName ) {
        checkNotNull( "git", git );
        checkNotEmpty( "branchName", branchName );

        return getBranch( git, branchName ) != null;
    }

    public static RevCommit getLastCommit( final Git git,
                                           final String branchName ) {

        RevWalk walk = null;
        RevCommit lastCommit = null;
        try {
            walk = new RevWalk( git.getRepository() );
            final RevCommit head = walk.parseCommit( git.getRepository().resolve( branchName ) );
            walk.markStart( head );
            lastCommit = walk.next();
        } catch ( final Exception ignored ) {
        } finally {
            if ( walk != null ) {
                walk.dispose();
            }
        }
        return lastCommit;
    }

    public static enum PathType {
        NOT_FOUND, DIRECTORY, FILE
    }

    public static Pair<PathType, ObjectId> checkPath( final Git git,
                                                      final String branchName,
                                                      final String path ) {
        checkNotNull( "git", git );
        checkNotNull( "path", path );
        checkNotEmpty( "branchName", branchName );

        final String gitPath = fixPath( path );

        if ( gitPath.isEmpty() ) {
            return newPair( PathType.DIRECTORY, null );
        }

        TreeWalk tw = null;
        try {
            final ObjectId tree = git.getRepository().resolve( branchName + "^{tree}" );
            tw = new TreeWalk( git.getRepository() );
            tw.setFilter( PathFilter.create( gitPath ) );
            tw.reset( tree );
            while ( tw.next() ) {
                if ( tw.getPathString().equals( gitPath ) ) {
                    if ( tw.getFileMode( 0 ).equals( FileMode.TYPE_TREE ) ) {
                        return newPair( PathType.DIRECTORY, tw.getObjectId( 0 ) );
                    } else if ( tw.getFileMode( 0 ).equals( FileMode.TYPE_FILE ) ||
                            tw.getFileMode( 0 ).equals( FileMode.EXECUTABLE_FILE ) ||
                            tw.getFileMode( 0 ).equals( FileMode.REGULAR_FILE ) ) {
                        return newPair( PathType.FILE, tw.getObjectId( 0 ) );
                    }
                }
                if ( tw.isSubtree() ) {
                    tw.enterSubtree();
                }
            }
        } catch ( final Throwable ignored ) {
        } finally {
            if ( tw != null ) {
                tw.release();
            }
        }
        return newPair( PathType.NOT_FOUND, null );
    }

    public static JGitPathInfo resolvePath( final Git git,
                                            final String branchName,
                                            final String path ) {
        checkNotNull( "git", git );
        checkNotNull( "path", path );
        checkNotEmpty( "branchName", branchName );

        final String gitPath = fixPath( path );

        if ( gitPath.isEmpty() ) {
            return new JGitPathInfo( null, "/", TREE );
        }

        TreeWalk tw = null;
        try {
            final ObjectId tree = git.getRepository().resolve( branchName + "^{tree}" );
            tw = new TreeWalk( git.getRepository() );
            tw.setFilter( PathFilter.create( gitPath ) );
            tw.reset( tree );
            while ( tw.next() ) {
                if ( tw.getPathString().equals( gitPath ) ) {
                    if ( tw.getFileMode( 0 ).equals( TREE ) ) {
                        return new JGitPathInfo( tw.getObjectId( 0 ), tw.getPathString(), TREE );
                    } else if ( tw.getFileMode( 0 ).equals( REGULAR_FILE ) || tw.getFileMode( 0 ).equals( EXECUTABLE_FILE ) ) {
                        final long size = tw.getObjectReader().getObjectSize( tw.getObjectId( 0 ), OBJ_BLOB );
                        return new JGitPathInfo( tw.getObjectId( 0 ), tw.getPathString(), REGULAR_FILE, size );
                    }
                }
                if ( tw.isSubtree() ) {
                    tw.enterSubtree();
                }
            }
        } catch ( final Throwable ignored ) {
        } finally {
            if ( tw != null ) {
                tw.release();
            }
        }

        return null;
    }

    public static List<JGitPathInfo> listPathContent( final Git git,
                                                      final String branchName,
                                                      final String path ) {
        checkNotNull( "git", git );
        checkNotNull( "path", path );
        checkNotEmpty( "branchName", branchName );

        final String gitPath = fixPath( path );

        TreeWalk tw = null;
        final List<JGitPathInfo> result = new ArrayList<JGitPathInfo>();
        try {
            final ObjectId tree = git.getRepository().resolve( branchName + "^{tree}" );
            tw = new TreeWalk( git.getRepository() );
            boolean found = false;
            if ( gitPath.isEmpty() ) {
                found = true;
            } else {
                tw.setFilter( PathFilter.create( gitPath ) );
            }
            tw.reset( tree );
            while ( tw.next() ) {
                if ( !found && tw.isSubtree() ) {
                    tw.enterSubtree();
                }
                if ( tw.getPathString().equals( gitPath ) ) {
                    found = true;
                    continue;
                }
                if ( found ) {
                    result.add( new JGitPathInfo( tw.getObjectId( 0 ), tw.getPathString(), tw.getFileMode( 0 ) ) );
                }
            }
        } catch ( final Throwable ignored ) {
        } finally {
            if ( tw != null ) {
                tw.release();
            }
        }

        return result;
    }

    public static class JGitPathInfo {

        private final ObjectId objectId;
        private final String path;
        private final long size;
        private final PathType pathType;

        public JGitPathInfo( final ObjectId objectId,
                             final String path,
                             final FileMode fileMode ) {
            this( objectId, path, fileMode, -1 );
        }

        public JGitPathInfo( final ObjectId objectId,
                             final String path,
                             final FileMode fileMode,
                             long size ) {
            this.objectId = objectId;
            this.size = size;
            this.path = path;

            if ( fileMode.equals( FileMode.TYPE_TREE ) ) {
                this.pathType = PathType.DIRECTORY;
            } else if ( fileMode.equals( TYPE_FILE ) ) {
                this.pathType = PathType.FILE;
            } else {
                this.pathType = null;
            }
        }

        public ObjectId getObjectId() {
            return objectId;
        }

        public String getPath() {
            return path;
        }

        public PathType getPathType() {
            return pathType;
        }

        public long getSize() {
            return size;
        }
    }

}
TOP

Related Classes of org.uberfire.java.nio.fs.jgit.util.JGitUtil$JGitPathInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.