org.eclipse.egit.core.RepositoryUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.egit.core.RepositoryUtil.java

Source

/*******************************************************************************
 * Copyright (c) 2010 SAP AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Mathias Kinzler (SAP AG) - initial implementation
 *******************************************************************************/
package org.eclipse.egit.core;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.FS;
import org.osgi.service.prefs.BackingStoreException;

/**
 * Utility class for handling Repositories in the UI.
 */
public class RepositoryUtil {

    /** The preferences to store the directories known to the Git Repositories view */
    public static final String PREFS_DIRECTORIES = "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$

    private final Map<String, Map<String, String>> commitMappingCache = new HashMap<String, Map<String, String>>();

    private final Map<String, String> repositoryNameCache = new HashMap<String, String>();

    private final IEclipsePreferences prefs = new InstanceScope().getNode(Activator.getPluginId());

    /**
     * Clients should obtain an instance from {@link Activator}
     */
    RepositoryUtil() {
        // nothing
    }

    /**
     * Used by {@link Activator}
     */
    void dispose() {
        commitMappingCache.clear();
        repositoryNameCache.clear();
    }

    /**
     * Tries to map a commit to a symbolic reference.
     * <p>
     * This value will be cached for the given commit ID unless refresh is
     * specified. The return value will be the full name, e.g.
     * "refs/remotes/someBranch", "refs/tags/v.1.0"
     * <p>
     * Since this mapping is not unique, the following precedence rules are
     * used:
     * <ul>
     * <li>Tags take precedence over branches</li>
     * <li>Local branches take preference over remote branches</li>
     * <li>Newer references take precedence over older ones where time stamps
     * are available</li>
     * <li>If there are still ambiguities, the reference name with the highest
     * lexicographic value will be returned</li>
     * </ul>
     *
     * @param repository
     *            the {@link Repository}
     * @param commitId
     *            a commit
     * @param refresh
     *            if true, the cache will be invalidated
     * @return the symbolic reference, or <code>null</code> if no such reference
     *         can be found
     */
    public String mapCommitToRef(Repository repository, String commitId, boolean refresh) {
        synchronized (commitMappingCache) {

            if (!ObjectId.isId(commitId)) {
                return null;
            }

            Map<String, String> cacheEntry = commitMappingCache.get(repository.getDirectory().toString());
            if (!refresh && cacheEntry != null && cacheEntry.containsKey(commitId)) {
                // this may be null in fact
                return cacheEntry.get(commitId);
            }
            if (cacheEntry == null) {
                cacheEntry = new HashMap<String, String>();
                commitMappingCache.put(repository.getDirectory().getPath(), cacheEntry);
            } else {
                cacheEntry.clear();
            }

            Map<String, Date> tagMap = new HashMap<String, Date>();
            try {
                RevWalk rw = new RevWalk(repository);
                Map<String, Ref> tags = repository.getRefDatabase().getRefs(Constants.R_TAGS);
                for (Ref tagRef : tags.values()) {
                    RevTag tag = rw.parseTag(repository.resolve(tagRef.getName()));
                    if (tag.getObject().name().equals(commitId)) {
                        Date timestamp;
                        if (tag.getTaggerIdent() != null) {
                            timestamp = tag.getTaggerIdent().getWhen();
                        } else {
                            timestamp = null;
                        }
                        tagMap.put(tagRef.getName(), timestamp);
                    }
                }
            } catch (IOException e) {
                // ignore here
            }

            String cacheValue = null;

            if (!tagMap.isEmpty()) {
                // we try to obtain the "latest" tag
                Date compareDate = new Date(0);
                for (Map.Entry<String, Date> tagEntry : tagMap.entrySet()) {
                    if (tagEntry.getValue() != null && tagEntry.getValue().after(compareDate)) {
                        compareDate = tagEntry.getValue();
                        cacheValue = tagEntry.getKey();
                    }
                }
                // if we don't have time stamps, we sort
                if (cacheValue == null) {
                    String compareString = ""; //$NON-NLS-1$
                    for (String tagName : tagMap.keySet()) {
                        if (tagName.compareTo(compareString) >= 0) {
                            cacheValue = tagName;
                            compareString = tagName;
                        }
                    }
                }
            }

            if (cacheValue == null) {
                // we didnt't find a tag, so let's look for local branches
                Set<String> branchNames = new TreeSet<String>();
                // put this into a sorted set
                try {
                    Map<String, Ref> remoteBranches = repository.getRefDatabase().getRefs(Constants.R_HEADS);
                    for (Ref branch : remoteBranches.values()) {
                        if (branch.getObjectId().name().equals(commitId)) {
                            branchNames.add(branch.getName());
                        }
                    }
                } catch (IOException e) {
                    // ignore here
                }
                if (!branchNames.isEmpty()) {
                    // get the last (sorted) entry
                    cacheValue = branchNames.toArray(new String[branchNames.size()])[branchNames.size() - 1];
                }
            }

            if (cacheValue == null) {
                // last try: remote branches
                Set<String> branchNames = new TreeSet<String>();
                // put this into a sorted set
                try {
                    Map<String, Ref> remoteBranches = repository.getRefDatabase().getRefs(Constants.R_REMOTES);
                    for (Ref branch : remoteBranches.values()) {
                        if (branch.getObjectId().name().equals(commitId)) {
                            branchNames.add(branch.getName());
                        }
                    }
                    if (!branchNames.isEmpty()) {
                        // get the last (sorted) entry
                        cacheValue = branchNames.toArray(new String[branchNames.size()])[branchNames.size() - 1];
                    }
                } catch (IOException e) {
                    // ignore here
                }
            }
            cacheEntry.put(commitId, cacheValue);
            return cacheValue;
        }
    }

    /**
     * Return a cached UI "name" for a Repository
     * <p>
     * This uses the name of the parent of the repository's directory.
     *
     * @param repository
     * @return the name
     */
    public String getRepositoryName(Repository repository) {
        synchronized (repositoryNameCache) {
            File gitDir = repository.getDirectory();
            if (gitDir != null) {
                String name = repositoryNameCache.get(gitDir.getPath().toString());
                if (name != null)
                    return name;
                name = gitDir.getParentFile().getName();
                repositoryNameCache.put(gitDir.getPath().toString(), name);
                return name;
            }
        }
        return ""; //$NON-NLS-1$
    }

    /**
     * @return the underlying preferences
     */
    public IEclipsePreferences getPreferences() {
        return prefs;
    }

    /**
     *
     * @return the list of configured Repository paths; will be sorted
     */
    public List<String> getConfiguredRepositories() {
        synchronized (prefs) {
            Set<String> configuredStrings = new HashSet<String>();

            String dirs = prefs.get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
            if (dirs != null && dirs.length() > 0) {
                StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
                while (tok.hasMoreTokens()) {
                    String dirName = tok.nextToken();
                    configuredStrings.add(dirName);
                }
            }
            List<String> result = new ArrayList<String>();
            result.addAll(configuredStrings);
            Collections.sort(result);
            return result;
        }
    }

    /**
     *
     * @param repositoryDir
     *            the Repository path
     * @return <code>true</code> if the repository path was not yet configured
     * @throws IllegalArgumentException
     *             if the path does not "look" like a Repository
     */
    public boolean addConfiguredRepository(File repositoryDir) throws IllegalArgumentException {
        synchronized (prefs) {

            if (!FileKey.isGitRepository(repositoryDir, FS.DETECTED))
                throw new IllegalArgumentException();

            String dirString;
            try {
                dirString = repositoryDir.getCanonicalPath();
            } catch (IOException e) {
                dirString = repositoryDir.getAbsolutePath();
            }

            List<String> dirStrings = getConfiguredRepositories();
            if (dirStrings.contains(dirString)) {
                return false;
            } else {
                Set<String> dirs = new HashSet<String>();
                dirs.addAll(dirStrings);
                dirs.add(dirString);
                saveDirs(dirs);
                return true;
            }
        }
    }

    /**
     * @param file
     * @return <code>true</code> if the configuration was changed by the remove
     */
    public boolean removeDir(File file) {
        synchronized (prefs) {

            String dir;
            try {
                dir = file.getCanonicalPath();
            } catch (IOException e1) {
                dir = file.getAbsolutePath();
            }

            Set<String> dirStrings = new HashSet<String>();
            dirStrings.addAll(getConfiguredRepositories());
            if (dirStrings.remove(dir)) {
                saveDirs(dirStrings);
                return true;
            }
            return false;
        }
    }

    private void saveDirs(Set<String> gitDirStrings) {
        StringBuilder sb = new StringBuilder();
        for (String gitDirString : gitDirStrings) {
            sb.append(gitDirString);
            sb.append(File.pathSeparatorChar);
        }

        prefs.put(PREFS_DIRECTORIES, sb.toString());
        try {
            prefs.flush();
        } catch (BackingStoreException e) {
            IStatus error = new Status(IStatus.ERROR, Activator.getPluginId(), e.getMessage(), e);
            Activator.getDefault().getLog().log(error);
        }
    }

}