JGIT的git status源码简析

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:52   1143   0

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获取方式有异曲同工之妙

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP