| | |
| | | import java.text.MessageFormat; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | |
| | | import org.eclipse.jgit.lib.ObjectId; |
| | | import org.eclipse.jgit.lib.ObjectInserter; |
| | | import org.eclipse.jgit.lib.PersonIdent; |
| | | import org.eclipse.jgit.lib.Ref; |
| | | import org.eclipse.jgit.lib.RefRename; |
| | | import org.eclipse.jgit.lib.RefUpdate; |
| | | import org.eclipse.jgit.lib.RefUpdate.Result; |
| | |
| | | /** |
| | | * Utility class for maintaining a reflog within a git repository on an |
| | | * orphan branch. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | | public class RefLogUtils { |
| | | |
| | | |
| | | private static final String GB_REFLOG = "refs/gitblit/reflog"; |
| | | |
| | | private static final Logger LOGGER = LoggerFactory.getLogger(RefLogUtils.class); |
| | | |
| | | /** |
| | | * Log an error message and exception. |
| | | * |
| | | * |
| | | * @param t |
| | | * @param repository |
| | | * if repository is not null it MUST be the {0} parameter in the |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns true if the repository has a reflog branch. |
| | | * |
| | | * @param repository |
| | | * @return true if the repository has a reflog branch |
| | | */ |
| | | public static boolean hasRefLogBranch(Repository repository) { |
| | | try { |
| | | return repository.getRef(GB_REFLOG) != null; |
| | | } catch(Exception e) { |
| | | LOGGER.error("failed to determine hasRefLogBranch", e); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Returns a RefModel for the reflog branch in the repository. If the |
| | | * branch can not be found, null is returned. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @return a refmodel for the reflog branch or null |
| | | */ |
| | |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | | private static UserModel newUserModelFrom(PersonIdent ident) { |
| | | String name = ident.getName(); |
| | | String username; |
| | |
| | | displayname = name; |
| | | username = ident.getEmailAddress(); |
| | | } |
| | | |
| | | |
| | | UserModel user = new UserModel(username); |
| | | user.displayName = displayname; |
| | | user.emailAddress = ident.getEmailAddress(); |
| | | return user; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Logs a ref deletion. |
| | | * |
| | | * @param user |
| | | * @param repository |
| | | * @param ref |
| | | * @return true, if the update was successful |
| | | */ |
| | | public static boolean deleteRef(UserModel user, Repository repository, Ref ref) { |
| | | try { |
| | | if (ref == null) { |
| | | return false; |
| | | } |
| | | RefModel reflogBranch = getRefLogBranch(repository); |
| | | if (reflogBranch == null) { |
| | | return false; |
| | | } |
| | | |
| | | List<RevCommit> log = JGitUtils.getRevLog(repository, reflogBranch.getName(), ref.getName(), 0, 1); |
| | | if (log.isEmpty()) { |
| | | // this ref is not in the reflog branch |
| | | return false; |
| | | } |
| | | ReceiveCommand cmd = new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), ref.getName()); |
| | | return updateRefLog(user, repository, Arrays.asList(cmd)); |
| | | } catch (Throwable t) { |
| | | error(t, repository, "Failed to commit reflog entry to {0}"); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Updates the reflog with the received commands. |
| | | * |
| | | * |
| | | * @param user |
| | | * @param repository |
| | | * @param commands |
| | |
| | | */ |
| | | public static boolean updateRefLog(UserModel user, Repository repository, |
| | | Collection<ReceiveCommand> commands) { |
| | | |
| | | // only track branches and tags |
| | | List<ReceiveCommand> filteredCommands = new ArrayList<ReceiveCommand>(); |
| | | for (ReceiveCommand cmd : commands) { |
| | | if (!cmd.getRefName().startsWith(Constants.R_HEADS) |
| | | && !cmd.getRefName().startsWith(Constants.R_TAGS)) { |
| | | continue; |
| | | } |
| | | filteredCommands.add(cmd); |
| | | } |
| | | |
| | | if (filteredCommands.isEmpty()) { |
| | | // nothing to log |
| | | return true; |
| | | } |
| | | |
| | | RefModel reflogBranch = getRefLogBranch(repository); |
| | | if (reflogBranch == null) { |
| | | JGitUtils.createOrphanBranch(repository, GB_REFLOG, null); |
| | | } |
| | | |
| | | |
| | | boolean success = false; |
| | | String message = "push"; |
| | | |
| | | |
| | | try { |
| | | ObjectId headId = repository.resolve(GB_REFLOG + "^{commit}"); |
| | | ObjectInserter odi = repository.newObjectInserter(); |
| | |
| | | PersonIdent ident; |
| | | if (UserModel.ANONYMOUS.equals(user)) { |
| | | // anonymous push |
| | | ident = new PersonIdent("anonymous", "anonymous"); |
| | | ident = new PersonIdent(user.username + "/" + user.username, user.username); |
| | | } else { |
| | | // construct real pushing account |
| | | ident = new PersonIdent(MessageFormat.format("{0}/{1}", user.getDisplayName(), user.username), |
| | |
| | | } |
| | | return success; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Creates an in-memory index of the push log entry. |
| | | * |
| | | * |
| | | * @param repo |
| | | * @param headId |
| | | * @param commands |
| | | * @return an in-memory index |
| | | * @throws IOException |
| | | */ |
| | | private static DirCache createIndex(Repository repo, ObjectId headId, |
| | | private static DirCache createIndex(Repository repo, ObjectId headId, |
| | | Collection<ReceiveCommand> commands) throws IOException { |
| | | |
| | | DirCache inCoreIndex = DirCache.newInCore(); |
| | |
| | | continue; |
| | | } |
| | | String content = change.toString(); |
| | | |
| | | |
| | | // create an index entry for this attachment |
| | | final DirCacheEntry dcEntry = new DirCacheEntry(path); |
| | | dcEntry.setLength(content.length()); |
| | |
| | | } |
| | | return inCoreIndex; |
| | | } |
| | | |
| | | |
| | | public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository) { |
| | | return getRefLog(repositoryName, repository, null, 0, -1); |
| | | } |
| | |
| | | public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository, Date minimumDate) { |
| | | return getRefLog(repositoryName, repository, minimumDate, 0, -1); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of reflog entries as they were recorded by Gitblit. |
| | | * Each RefLogEntry may represent multiple ref updates. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param minimumDate |
| | |
| | | if (maxCount == 0) { |
| | | return list; |
| | | } |
| | | |
| | | |
| | | Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository); |
| | | List<RevCommit> pushes; |
| | | if (minimumDate == null) { |
| | |
| | | |
| | | UserModel user = newUserModelFrom(push.getAuthorIdent()); |
| | | Date date = push.getAuthorIdent().getWhen(); |
| | | |
| | | |
| | | RefLogEntry log = new RefLogEntry(repositoryName, date, user); |
| | | |
| | | // only report HEADS and TAGS for now |
| | | List<PathChangeModel> changedRefs = new ArrayList<PathChangeModel>(); |
| | | for (PathChangeModel refChange : JGitUtils.getFilesInCommit(repository, push)) { |
| | | if (refChange.path.startsWith(Constants.R_HEADS) |
| | | || refChange.path.startsWith(Constants.R_TAGS)) { |
| | | changedRefs.add(refChange); |
| | | } |
| | | } |
| | | if (changedRefs.isEmpty()) { |
| | | // skip empty commits |
| | | continue; |
| | | } |
| | | list.add(log); |
| | | List<PathChangeModel> changedRefs = JGitUtils.getFilesInCommit(repository, push); |
| | | for (PathChangeModel change : changedRefs) { |
| | | switch (change.changeType) { |
| | | case DELETE: |
| | | log.updateRef(change.path, ReceiveCommand.Type.DELETE); |
| | | break; |
| | | case ADD: |
| | | log.updateRef(change.path, ReceiveCommand.Type.CREATE); |
| | | default: |
| | | String content = JGitUtils.getStringContent(repository, push.getTree(), change.path); |
| | | String [] fields = content.split(" "); |
| | | String oldId = fields[1]; |
| | | String newId = fields[2]; |
| | | log.updateRef(change.path, ReceiveCommand.Type.valueOf(fields[0]), oldId, newId); |
| | | List<RevCommit> pushedCommits = JGitUtils.getRevLog(repository, oldId, newId); |
| | | for (RevCommit pushedCommit : pushedCommits) { |
| | | RepositoryCommit repoCommit = log.addCommit(change.path, pushedCommit); |
| | | if (repoCommit != null) { |
| | | repoCommit.setRefs(allRefs.get(pushedCommit.getId())); |
| | | if (ObjectId.zeroId().getName().equals(newId)) { |
| | | // ref deletion |
| | | continue; |
| | | } |
| | | try { |
| | | List<RevCommit> pushedCommits = JGitUtils.getRevLog(repository, oldId, newId); |
| | | for (RevCommit pushedCommit : pushedCommits) { |
| | | RepositoryCommit repoCommit = log.addCommit(change.path, pushedCommit); |
| | | if (repoCommit != null) { |
| | | repoCommit.setRefs(allRefs.get(pushedCommit.getId())); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | |
| | | } |
| | | } |
| | | } |
| | |
| | | /** |
| | | * Returns the list of pushes separated by ref (e.g. each ref has it's own |
| | | * PushLogEntry object). |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param maxCount |
| | |
| | | public static List<RefLogEntry> getLogByRef(String repositoryName, Repository repository, int maxCount) { |
| | | return getLogByRef(repositoryName, repository, 0, maxCount); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of pushes separated by ref (e.g. each ref has it's own |
| | | * PushLogEntry object). |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param offset |
| | |
| | | if (!refMap.containsKey(ref)) { |
| | | refMap.put(ref, new ArrayList<RefLogEntry>()); |
| | | } |
| | | |
| | | |
| | | // construct new ref-specific ref change entry |
| | | RefLogEntry refChange; |
| | | if (entry instanceof DailyLogEntry) { |
| | |
| | | refMap.get(ref).add(refChange); |
| | | } |
| | | } |
| | | |
| | | |
| | | // merge individual ref changes into master list |
| | | List<RefLogEntry> mergedRefLog = new ArrayList<RefLogEntry>(); |
| | | for (List<RefLogEntry> refPush : refMap.values()) { |
| | | mergedRefLog.addAll(refPush); |
| | | } |
| | | |
| | | |
| | | // sort ref log |
| | | Collections.sort(mergedRefLog); |
| | | |
| | | |
| | | return mergedRefLog; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of ref changes separated by ref (e.g. each ref has it's own |
| | | * RefLogEntry object). |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param minimumDate |
| | |
| | | refMap.get(ref).add(refPush); |
| | | } |
| | | } |
| | | |
| | | |
| | | // merge individual ref pushes into master list |
| | | List<RefLogEntry> refPushLog = new ArrayList<RefLogEntry>(); |
| | | for (List<RefLogEntry> refPush : refMap.values()) { |
| | | refPushLog.addAll(refPush); |
| | | } |
| | | |
| | | |
| | | // sort ref push log |
| | | Collections.sort(refPushLog); |
| | | |
| | | |
| | | return refPushLog; |
| | | } |
| | | |
| | |
| | | Map<String, DailyLogEntry> dailydigests = new HashMap<String, DailyLogEntry>(); |
| | | String linearParent = null; |
| | | for (RefModel local : JGitUtils.getLocalBranches(repository, true, -1)) { |
| | | if (!local.getDate().after(minimumDate)) { |
| | | // branch not recently updated |
| | | continue; |
| | | } |
| | | String branch = local.getName(); |
| | | List<RepositoryCommit> commits = CommitCache.instance().getCommits(repositoryName, repository, branch, minimumDate); |
| | | linearParent = null; |
| | | for (RepositoryCommit commit : commits) { |
| | | if (linearParent != null) { |
| | | if (!commit.getName().equals(linearParent)) { |
| | |
| | | linearParent = commit.getParents()[0].getId().getName(); |
| | | digest.updateRef(branch, ReceiveCommand.Type.UPDATE, linearParent, commit.getName()); |
| | | } |
| | | |
| | | |
| | | RepositoryCommit repoCommit = digest.addCommit(commit); |
| | | if (repoCommit != null) { |
| | | List<RefModel> matchedRefs = allRefs.get(commit.getId()); |
| | | repoCommit.setRefs(matchedRefs); |
| | | |
| | | |
| | | if (!ArrayUtils.isEmpty(matchedRefs)) { |
| | | for (RefModel ref : matchedRefs) { |
| | | if (ref.getName().startsWith(Constants.R_TAGS)) { |
| | | // treat tags as special events in the log |
| | | if (!tags.containsKey(dateStr)) { |
| | | UserModel tagUser = newUserModelFrom(commit.getAuthorIdent()); |
| | | UserModel tagUser = newUserModelFrom(ref.getAuthorIdent()); |
| | | Date tagDate = commit.getAuthorIdent().getWhen(); |
| | | tags.put(dateStr, new DailyLogEntry(repositoryName, tagDate, tagUser)); |
| | | } |
| | |
| | | } else if (ref.getName().startsWith(Constants.R_PULL)) { |
| | | // treat pull requests as special events in the log |
| | | if (!pulls.containsKey(dateStr)) { |
| | | UserModel commitUser = newUserModelFrom(commit.getAuthorIdent()); |
| | | UserModel commitUser = newUserModelFrom(ref.getAuthorIdent()); |
| | | Date commitDate = commit.getAuthorIdent().getWhen(); |
| | | pulls.put(dateStr, new DailyLogEntry(repositoryName, commitDate, commitUser)); |
| | | } |