package bluej.groupwork.git;

import bluej.groupwork.StatusListener;
import bluej.groupwork.TeamStatusInfo;
import bluej.groupwork.TeamStatusInfo.Status;
import bluej.groupwork.TeamworkCommandError;
import bluej.groupwork.TeamworkCommandResult;
import static bluej.groupwork.git.GitUtilities.findForkPoint;
import static bluej.groupwork.git.GitUtilities.getBehindCount;
import static bluej.groupwork.git.GitUtilities.getDiffs;
import static bluej.groupwork.git.GitUtilities.getFileNameFromDiff;
import static bluej.groupwork.git.GitUtilities.isAheadOnly;
import bluej.utility.Debug;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Map;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.lib.IndexDiff;

import threadchecker.OnThread;
import threadchecker.Tag;


| Checks the status of a Git repository | | @author Fabio Hedayioglu | public class GitStatusCommand extends GitCommand{ StatusListener listener; FileFilter filter; boolean includeRemote; public GitStatusCommand(GitRepository repository, StatusListener listener, FileFilter filter, boolean includeRemote) { super(repository); this.listener = listener; this.filter = filter; this.includeRemote = includeRemote; } @Override @OnThread(Tag.Worker) public TeamworkCommandResult getResult() { boolean didFilesChange = true; LinkedList<TeamStatusInfo> returnInfo = new LinkedList<>(); File gitPath = this.getRepository().getProjectPath(); try (Git repo = Git.open(this.getRepository().getProjectPath())) { org.eclipse.jgit.api.Status s = repo.status().call(); s.getMissing().stream() .filter(p -> filter.accept(new File(gitPath, p))) .forEach(item -> { TeamStatusInfo teamInfo = new TeamStatusInfo(new File(gitPath, item), "", null, Status.DELETED); returnInfo.add(teamInfo); }); s.getRemoved().stream() .filter(p -> filter.accept(new File(gitPath, p))) .forEach(item -> { returnInfo.add(new TeamStatusInfo(new File(gitPath, item), "", null, Status.DELETED)); }); s.getUncommittedChanges().stream() .filter(p -> filter.accept(new File(gitPath, p))) .forEach(item -> { TeamStatusInfo teamInfo = new TeamStatusInfo(new File(gitPath, item), "", null, Status.NEEDS_COMMIT); TeamStatusInfo existingStatusInfo = getTeamStatusInfo(returnInfo, teamInfo.getFile()); if (existingStatusInfo == null) { returnInfo.add(teamInfo); } }); s.getUntracked().stream() .filter(p -> filter.accept(new File(gitPath, p))) .forEach(item -> returnInfo.add(new TeamStatusInfo(new File(gitPath, item), "", null, Status.NEEDS_ADD))); s.getUntrackedFolders().stream() .filter(p -> filter.accept(new File(gitPath, p))) .forEach(item -> returnInfo.add(new TeamStatusInfo(new File(gitPath, item), "", null, Status.NEEDS_ADD))); Map<String, IndexDiff.StageState> conflictsMap = s.getConflictingStageState(); s.getConflicting().stream() .filter(p -> filter.accept(new File(gitPath, p))) .forEach(item -> { TeamStatusInfo teamInfo = getTeamStatusInfo(returnInfo, new File(gitPath, item)); if (teamInfo == null) { Debug.message("Git unexpected status: file is " + "conflicting but not otherwise noted? (" + item + ")"); teamInfo = new TeamStatusInfo(new File(gitPath, item), "", null, Status.NEEDS_MERGE); returnInfo.add(teamInfo); } else { IndexDiff.StageState sstate = conflictsMap.get(item); switch (sstate) { case DELETED_BY_THEM: teamInfo.setStatus(Status.CONFLICT_LMRD); break; case DELETED_BY_US: teamInfo.setStatus(Status.CONFLICT_LDRM); break; case BOTH_ADDED: teamInfo.setStatus(Status.CONFLICT_ADD); break; case BOTH_MODIFIED: teamInfo.setStatus(Status.NEEDS_MERGE); break; default: Debug.message("Git status, unknown/unhandled conflict state: " + sstate + " (" + item + ")"); teamInfo.setStatus(Status.NEEDS_MERGE); } } }); List<DiffEntry> listOfDiffsLocal, listOfDiffsRemote; if (includeRemote) { GitFetchCommand fetchCommand = new GitFetchCommand(this.getRepository()); TeamworkCommandResult fetchResult = fetchCommand.getResult(); if (fetchResult.isError()) { return fetchResult; } } RevCommit forkPoint = findForkPoint(repo.getRepository(), "origin/master", "HEAD"); listOfDiffsLocal = getDiffs(repo, "HEAD", forkPoint); listOfDiffsRemote = getDiffs(repo, "origin/master", forkPoint); updateRemoteStatus(gitPath, listOfDiffsLocal, listOfDiffsRemote, returnInfo); if (returnInfo.isEmpty()){ didFilesChange = false; } if (listener != null) { addUpToDateFiles(returnInfo, gitPath); while (!returnInfo.isEmpty()){ TeamStatusInfo teamInfo = returnInfo.removeFirst(); listener.gotStatus(teamInfo); } listener.statusComplete(new GitStatusHandle(getRepository(), didFilesChange && isAheadOnly(repo), didFilesChange && getBehindCount(repo) > 0)); } } catch (IOException | GitAPIException | NoWorkTreeException | GitTreeException ex) { Debug.reportError("Git status command exception", ex); return new TeamworkCommandError(ex.getMessage(), ex.getLocalizedMessage()); } return new TeamworkCommandResult(); }
| Search a directory (recursively). For all files with no status currently recorded, add an | "unchanged" status entry. |* * @param returnInfo list of file status * @param path path to search */ private void addUpToDateFiles(LinkedList<TeamStatusInfo> returnInfo, File path) { for (File item : path.listFiles()) { | |if (filter.accept(item)) { | |if (item.isDirectory()) { | |addUpToDateFiles(returnInfo, item); | |} | |else { | |TeamStatusInfo itemStatus = getTeamStatusInfo(returnInfo, item); | |if (itemStatus == null) { | |//file does not exist in the list, therefore it is up-to-date. | |returnInfo.add(new TeamStatusInfo(item, "", null, Status.UP_TO_DATE, Status.UP_TO_DATE)); } } } } } | |/** | checks if a file already has an entry on returnInfo | | @param returnInfo list of TeamStatusInfo to be checked | @param file file to check | @return null if there is no entry of that file. The entry if it exists. | private TeamStatusInfo getTeamStatusInfo(LinkedList<TeamStatusInfo> returnInfo, File file) { try { return returnInfo.stream().filter(entry -> entry.getFile().getPath().contains(file.getPath())).findFirst().get(); } catch (Exception e) { return null; } } private Optional getDiffFromList(List<DiffEntry> list, DiffEntry entry) { Optional<DiffEntry> result; String entryFileName = getFileNameFromDiff(entry); result = list.stream().filter(p -> getFileNameFromDiff(p).equals(entryFileName)).findFirst(); return result; } private void updateRemoteStatus(LinkedList<TeamStatusInfo> returnInfo, File file, Status remoteStatus) { TeamStatusInfo entry = getTeamStatusInfo(returnInfo, file); if (entry != null) { entry.setRemoteStatus(remoteStatus); } else { entry = new TeamStatusInfo(file, "", null, Status.UP_TO_DATE, remoteStatus); returnInfo.add(entry); } } private void updateRemoteStatus(File gitPath, List<DiffEntry> listOfDiffsLocal, List<DiffEntry> listOfDiffsRemote, LinkedList<TeamStatusInfo> returnInfo) { for (DiffEntry localDiffItem : listOfDiffsLocal) { File file = new File(gitPath, getFileNameFromDiff(localDiffItem)); switch (localDiffItem.getChangeType()) { case MODIFY: updateRemoteStatus(returnInfo, file, Status.NEEDS_COMMIT); break; case DELETE: updateRemoteStatus(returnInfo, file, Status.DELETED); break; case ADD: updateRemoteStatus(returnInfo, file, Status.NEEDS_ADD); break; } } for (DiffEntry remoteDiffItem : listOfDiffsRemote) { Optional<DiffEntry> localDiffItem = getDiffFromList(listOfDiffsLocal, remoteDiffItem); File file = new File(gitPath, getFileNameFromDiff(remoteDiffItem)); switch (remoteDiffItem.getChangeType()) { case MODIFY: if (localDiffItem.isPresent()) { TeamStatusInfo entry = getTeamStatusInfo(returnInfo, file); switch (localDiffItem.get().getChangeType()) { case MODIFY: if (entry == null){ updateRemoteStatus(returnInfo, file, Status.NEEDS_PUSH); } else { updateRemoteStatus(returnInfo, file, Status.NEEDS_MERGE); } break; case DELETE: updateRemoteStatus(returnInfo, file, Status.CONFLICT_LDRM); break; case ADD: updateRemoteStatus(returnInfo, file, Status.CONFLICT_ADD); break; } } else { updateRemoteStatus(returnInfo, file, Status.NEEDS_UPDATE); } break; case DELETE: if (localDiffItem.isPresent()) { switch (localDiffItem.get().getChangeType()) { case MODIFY: updateRemoteStatus(returnInfo, file, Status.CONFLICT_LMRD); break; case DELETE: updateRemoteStatus(returnInfo, file, Status.DELETED); break; case ADD: updateRemoteStatus(returnInfo, file, Status.NEEDS_COMMIT); break; } } else { updateRemoteStatus(returnInfo, file, Status.REMOVED); } break; case ADD: if (localDiffItem.isPresent()) { switch (localDiffItem.get().getChangeType()) { case ADD: updateRemoteStatus(returnInfo, file, Status.CONFLICT_ADD); break; } } else { updateRemoteStatus(returnInfo, file, Status.NEEDS_CHECKOUT); if (!file.exists()){ TeamStatusInfo tsi = getTeamStatusInfo(returnInfo, file); tsi.setStatus(Status.NEEDS_CHECKOUT); } } } } } }
top, use, map, class GitStatusCommand

.   GitStatusCommand
.   getResult
.   getTeamStatusInfo
.   getDiffFromList
.   updateRemoteStatus
.   updateRemoteStatus




390 neLoCode + 18 LoComm