JGIT的git status源碼簡析

JGIT庫可以通過git.status().call()來獲取當前working copy的文件變動情況,功能類似與命令行的git status,以下從源碼角度簡析git status的實現原理:

    public Status call() throws GitAPIException, NoWorkTreeException {
        if (workingTreeIt == null)
            workingTreeIt = new FileTreeIterator(repo);

        try {
            IndexDiff diff = new IndexDiff(repo, Constants.HEAD, workingTreeIt);
            if (ignoreSubmoduleMode != null)
                diff.setIgnoreSubmoduleMode(ignoreSubmoduleMode);
            if (paths != null)
                diff.setFilter(PathFilterGroup.createFromStrings(paths));
            if (progressMonitor == null)
                diff.diff();
            else
                diff.diff(progressMonitor, ProgressMonitor.UNKNOWN,
                        ProgressMonitor.UNKNOWN, ""); //$NON-NLS-1$
            return new Status(diff);
        } catch (IOException e) {
            throw new JGitInternalException(e.getMessage(), e);
        }
    }

1.  獲取工作目錄的迭代器

workingTreeIt = new FileTreeIterator(repo)
2.  設置Diff條件

			IndexDiff diff = new IndexDiff(repo, Constants.HEAD, workingTreeIt);
			if (ignoreSubmoduleMode != null)
				diff.setIgnoreSubmoduleMode(ignoreSubmoduleMode);
			if (paths != null)
				diff.setFilter(PathFilterGroup.createFromStrings(paths));

3. 生成Diff

diff() go through revTree and compare with workingTreeIterator to get the changed info, which will be save in diff

1.    diff.diff();

2.	public boolean diff() throws IOException {
		return diff(null, 0, 0, ""); //$NON-NLS-1$
	}

3.	public boolean diff(final ProgressMonitor monitor, int estWorkTreeSize,
			int estIndexSize, final String title)
			throws IOException {
		dirCache = repository.readDirCache();

		try (TreeWalk treeWalk = new TreeWalk(repository)) {
			treeWalk.setOperationType(OperationType.CHECKIN_OP);
			treeWalk.setRecursive(true);
			// add the trees (tree, dirchache, workdir)
			if (tree != null)
				treeWalk.addTree(tree);
			else
				treeWalk.addTree(new EmptyTreeIterator());
			treeWalk.addTree(new DirCacheIterator(dirCache));
			treeWalk.addTree(initialWorkingTreeIterator);
			initialWorkingTreeIterator.setDirCacheIterator(treeWalk, 1);
			Collection<TreeFilter> filters = new ArrayList<>(4);

			if (monitor != null) {
				// Get the maximum size of the work tree and index
				// and add some (quite arbitrary)
				if (estIndexSize == 0)
					estIndexSize = dirCache.getEntryCount();
				int total = Math.max(estIndexSize * 10 / 9,
						estWorkTreeSize * 10 / 9);
				monitor.beginTask(title, total);
				filters.add(new ProgressReportingFilter(monitor, total));
			}

			if (filter != null)
				filters.add(filter);
			filters.add(new SkipWorkTreeFilter(INDEX));
			indexDiffFilter = new IndexDiffFilter(INDEX, WORKDIR);
			filters.add(indexDiffFilter);
			treeWalk.setFilter(AndTreeFilter.create(filters));
			fileModes.clear();
			while (treeWalk.next()) {
				AbstractTreeIterator treeIterator = treeWalk.getTree(TREE,
						AbstractTreeIterator.class);
				DirCacheIterator dirCacheIterator = treeWalk.getTree(INDEX,
						DirCacheIterator.class);
				WorkingTreeIterator workingTreeIterator = treeWalk
						.getTree(WORKDIR, WorkingTreeIterator.class);

				if (dirCacheIterator != null) {
					final DirCacheEntry dirCacheEntry = dirCacheIterator
							.getDirCacheEntry();
					if (dirCacheEntry != null) {
						int stage = dirCacheEntry.getStage();
						if (stage > 0) {
							String path = treeWalk.getPathString();
							addConflict(path, stage);
							continue;
						}
					}
				}

				if (treeIterator != null) {
					if (dirCacheIterator != null) {
						if (!treeIterator.idEqual(dirCacheIterator)
								|| treeIterator
										.getEntryRawMode() != dirCacheIterator
												.getEntryRawMode()) {
							// in repo, in index, content diff => changed
							if (!isEntryGitLink(treeIterator)
									|| !isEntryGitLink(dirCacheIterator)
									|| ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
								changed.add(treeWalk.getPathString());
						}
					} else {
						// in repo, not in index => removed
						if (!isEntryGitLink(treeIterator)
								|| ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
							removed.add(treeWalk.getPathString());
						if (workingTreeIterator != null)
							untracked.add(treeWalk.getPathString());
					}
				} else {
					if (dirCacheIterator != null) {
						// not in repo, in index => added
						if (!isEntryGitLink(dirCacheIterator)
								|| ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
							added.add(treeWalk.getPathString());
					} else {
						// not in repo, not in index => untracked
						if (workingTreeIterator != null
								&& !workingTreeIterator.isEntryIgnored()) {
							untracked.add(treeWalk.getPathString());
						}
					}
				}

				if (dirCacheIterator != null) {
					if (workingTreeIterator == null) {
						// in index, not in workdir => missing
						if (!isEntryGitLink(dirCacheIterator)
								|| ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
							missing.add(treeWalk.getPathString());
					} else {
						if (workingTreeIterator.isModified(
								dirCacheIterator.getDirCacheEntry(), true,
								treeWalk.getObjectReader())) {
							// in index, in workdir, content differs => modified
							if (!isEntryGitLink(dirCacheIterator)
									|| !isEntryGitLink(workingTreeIterator)
									|| (ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL
											&& ignoreSubmoduleMode != IgnoreSubmoduleMode.DIRTY))
								modified.add(treeWalk.getPathString());
						}
					}
				}

				String path = treeWalk.getPathString();
				if (path != null) {
					for (int i = 0; i < treeWalk.getTreeCount(); i++) {
						recordFileMode(path, treeWalk.getFileMode(i));
					}
				}
			}
		}

		if (ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL) {
			IgnoreSubmoduleMode localIgnoreSubmoduleMode = ignoreSubmoduleMode;
			SubmoduleWalk smw = SubmoduleWalk.forIndex(repository);
			while (smw.next()) {
				try {
					if (localIgnoreSubmoduleMode == null)
						localIgnoreSubmoduleMode = smw.getModulesIgnore();
					if (IgnoreSubmoduleMode.ALL
							.equals(localIgnoreSubmoduleMode))
						continue;
				} catch (ConfigInvalidException e) {
					throw new IOException(MessageFormat.format(
							JGitText.get().invalidIgnoreParamSubmodule,
							smw.getPath()), e);
				}
				try (Repository subRepo = smw.getRepository()) {
					if (subRepo != null) {
						String subRepoPath = smw.getPath();
						ObjectId subHead = subRepo.resolve("HEAD"); //$NON-NLS-1$
						if (subHead != null
								&& !subHead.equals(smw.getObjectId())) {
							modified.add(subRepoPath);
							recordFileMode(subRepoPath, FileMode.GITLINK);
						} else if (ignoreSubmoduleMode != IgnoreSubmoduleMode.DIRTY) {
							IndexDiff smid = submoduleIndexDiffs.get(smw
									.getPath());
							if (smid == null) {
								smid = new IndexDiff(subRepo,
										smw.getObjectId(),
										wTreeIt.getWorkingTreeIterator(subRepo));
								submoduleIndexDiffs.put(subRepoPath, smid);
							}
							if (smid.diff()) {
								if (ignoreSubmoduleMode == IgnoreSubmoduleMode.UNTRACKED
										&& smid.getAdded().isEmpty()
										&& smid.getChanged().isEmpty()
										&& smid.getConflicting().isEmpty()
										&& smid.getMissing().isEmpty()
										&& smid.getModified().isEmpty()
										&& smid.getRemoved().isEmpty()) {
									continue;
								}
								modified.add(subRepoPath);
								recordFileMode(subRepoPath, FileMode.GITLINK);
							}
						}
					}
				}
			}

		}

		// consume the remaining work
		if (monitor != null)
			monitor.endTask();

		ignored = indexDiffFilter.getIgnoredPaths();
		if (added.isEmpty() && changed.isEmpty() && removed.isEmpty()
				&& missing.isEmpty() && modified.isEmpty()
				&& untracked.isEmpty())
			return false;
		else
			return true;
	}

4. 將Diff轉換城Status

return new Status(diff);

因此從這個意思上來說,DocSys的文件同步和JGIT的Status獲取方式有異曲同工之妙

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章