Example #1
0
  static {
    System.setProperty("svnkit.log.native.calls", "true");
    final JavaSVNDebugLogger logger =
        new JavaSVNDebugLogger(
            Boolean.getBoolean(LOG_PARAMETER_NAME), Boolean.getBoolean(TRACE_NATIVE_CALLS), LOG);
    SVNDebugLog.setDefaultLog(logger);

    SVNJNAUtil.setJNAEnabled(true);
    SvnHttpAuthMethodsDefaultChecker.check();

    SVNAdminAreaFactory.setSelector(new SvnFormatSelector());

    DAVRepositoryFactory.setup();
    SVNRepositoryFactoryImpl.setup();
    FSRepositoryFactory.setup();

    // non-optimized writing is fast enough on Linux/MacOS, and somewhat more reliable
    if (SystemInfo.isWindows) {
      SVNAdminArea14.setOptimizedWritingEnabled(true);
    }

    if (!SVNJNAUtil.isJNAPresent()) {
      LOG.warn("JNA is not found by svnkit library");
    }
    initLogFilters();

    // Alexander Kitaev says it is default value (SSLv3) - since 8254
    if (!SystemInfo.JAVA_RUNTIME_VERSION.startsWith("1.7")
        && System.getProperty(SVNKIT_HTTP_SSL_PROTOCOLS) == null) {
      System.setProperty(SVNKIT_HTTP_SSL_PROTOCOLS, "SSLv3");
    }
  }
  static {
    System.setProperty("svnkit.log.native.calls", "true");
    final JavaSVNDebugLogger logger =
        new JavaSVNDebugLogger(
            Boolean.getBoolean(LOG_PARAMETER_NAME), Boolean.getBoolean(TRACE_NATIVE_CALLS), LOG);
    SVNDebugLog.setDefaultLog(logger);

    SVNJNAUtil.setJNAEnabled(true);
    SvnHttpAuthMethodsDefaultChecker.check();

    SVNAdminAreaFactory.setSelector(new SvnFormatSelector());

    DAVRepositoryFactory.setup();
    SVNRepositoryFactoryImpl.setup();
    FSRepositoryFactory.setup();

    // non-optimized writing is fast enough on Linux/MacOS, and somewhat more reliable
    if (SystemInfo.isWindows) {
      SVNAdminArea14.setOptimizedWritingEnabled(true);
    }

    if (!SVNJNAUtil.isJNAPresent()) {
      LOG.warn("JNA is not found by svnkit library");
    }
    initLogFilters();

    ourSSLProtocolsExplicitlySet = System.getProperty(SVNKIT_HTTP_SSL_PROTOCOLS) != null;
  }
 public void refreshSSLProperty() {
   if (ourSSLProtocolsExplicitlySet) return;
   if (SvnConfiguration.SSLProtocols.all.equals(myConfiguration.SSL_PROTOCOLS)) {
     System.clearProperty(SVNKIT_HTTP_SSL_PROTOCOLS);
   } else if (SvnConfiguration.SSLProtocols.sslv3.equals(myConfiguration.SSL_PROTOCOLS)) {
     System.setProperty(SVNKIT_HTTP_SSL_PROTOCOLS, "SSLv3");
   } else if (SvnConfiguration.SSLProtocols.tlsv1.equals(myConfiguration.SSL_PROTOCOLS)) {
     System.setProperty(SVNKIT_HTTP_SSL_PROTOCOLS, "TLSv1");
   }
 }
Example #4
0
 private static int computeLoadedFileSize() {
   int result = (int) PersistentFSConstants.FILE_LENGTH_TO_CACHE_THRESHOLD;
   final String userLimitKb = System.getProperty(MAX_VCS_LOADED_SIZE_KB);
   try {
     return userLimitKb != null ? Math.min(Integer.parseInt(userLimitKb) * 1024, result) : result;
   } catch (NumberFormatException ignored) {
     return result;
   }
 }
 private void createPool() {
   if (myPool != null) return;
   final String property = System.getProperty(KEEP_CONNECTIONS_KEY);
   final boolean keep;
   boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
   // pool variant by default
   if (StringUtil.isEmptyOrSpaces(property) || unitTestMode) {
     keep = !unitTestMode; // default
   } else {
     keep = Boolean.getBoolean(KEEP_CONNECTIONS_KEY);
   }
   myPool =
       new SvnIdeaRepositoryPoolManager(
           false,
           myConfiguration.getAuthenticationManager(this),
           myConfiguration.getOptions(myProject));
 }
 @Override
 public void log(final SVNLogType logType, final Throwable th, final Level logLevel) {
   if (th instanceof SSLHandshakeException) {
     final long time = System.currentTimeMillis();
     if ((time - myPreviousTime) > ourMaxFrequency) {
       myPreviousTime = time;
       String info = myHelper.getAddInfo();
       info = info == null ? "" : " (" + info + ") ";
       if (th.getCause() instanceof CertificateException) {
         PopupUtil.showBalloonForActiveComponent(
             "Subversion: " + info + th.getCause().getMessage(), MessageType.ERROR);
       } else {
         final String postMessage =
             "\nPlease check Subversion SSL settings (Settings | Version Control | Subversion | Network)";
         PopupUtil.showBalloonForActiveComponent(
             "Subversion: " + info + th.getMessage() + postMessage, MessageType.ERROR);
       }
     }
   }
   if (shouldLog(logType)) {
     myLog.info(th);
   }
 }
 @Override
 public UpdateInfoTree showUpdateProjectInfo(
     UpdatedFiles updatedFiles,
     String displayActionName,
     ActionInfo actionInfo,
     boolean canceled) {
   if (!myProject.isOpen() || myProject.isDisposed()) return null;
   ContentManager contentManager = getContentManager();
   if (contentManager == null) {
     return null; // content manager is made null during dispose; flag is set later
   }
   final UpdateInfoTree updateInfoTree =
       new UpdateInfoTree(contentManager, myProject, updatedFiles, displayActionName, actionInfo);
   ContentUtilEx.addTabbedContent(
       contentManager,
       updateInfoTree,
       "Update Info",
       DateFormatUtil.formatDateTime(System.currentTimeMillis()),
       true,
       updateInfoTree);
   ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.VCS).activate(null);
   updateInfoTree.expandRootChildren();
   return updateInfoTree;
 }
Example #8
0
public class HgVcs extends AbstractVcs<CommittedChangeList> {

  public static final Topic<HgUpdater> BRANCH_TOPIC =
      new Topic<HgUpdater>("hg4idea.branch", HgUpdater.class);
  public static final Topic<HgUpdater> REMOTE_TOPIC =
      new Topic<HgUpdater>("hg4idea.remote", HgUpdater.class);
  public static final Topic<HgUpdater> STATUS_TOPIC =
      new Topic<HgUpdater>("hg4idea.status", HgUpdater.class);
  public static final Topic<HgHideableWidget> INCOMING_OUTGOING_CHECK_TOPIC =
      new Topic<HgHideableWidget>("hg4idea.incomingcheck", HgHideableWidget.class);
  private static final Logger LOG = Logger.getInstance(HgVcs.class);

  public static final String VCS_NAME = "hg4idea";
  private static final VcsKey ourKey = createKey(VCS_NAME);
  private static final int MAX_CONSOLE_OUTPUT_SIZE = 10000;

  private static final String ORIG_FILE_PATTERN = "*.orig";
  @Nullable public static final String HGENCODING = System.getenv("HGENCODING");

  private final HgChangeProvider changeProvider;
  private final HgRollbackEnvironment rollbackEnvironment;
  private final HgDiffProvider diffProvider;
  private final HgHistoryProvider historyProvider;
  private final HgCheckinEnvironment checkinEnvironment;
  private final HgAnnotationProvider annotationProvider;
  private final HgUpdateEnvironment updateEnvironment;
  private final HgCachingCommittedChangesProvider committedChangesProvider;
  private MessageBusConnection messageBusConnection;
  @NotNull private final HgGlobalSettings globalSettings;
  @NotNull private final HgProjectSettings projectSettings;
  private final ProjectLevelVcsManager myVcsManager;

  private HgVFSListener myVFSListener;
  private final HgMergeProvider myMergeProvider;
  private HgExecutableValidator myExecutableValidator;
  private final Object myExecutableValidatorLock = new Object();
  private File myPromptHooksExtensionFile;
  private CommitExecutor myCommitAndPushExecutor;

  private HgRemoteStatusUpdater myHgRemoteStatusUpdater;
  private HgStatusWidget myStatusWidget;
  private HgIncomingOutgoingWidget myIncomingWidget;
  private HgIncomingOutgoingWidget myOutgoingWidget;
  @NotNull private HgVersion myVersion = HgVersion.NULL; // version of Hg which this plugin uses.

  public HgVcs(
      Project project,
      @NotNull HgGlobalSettings globalSettings,
      @NotNull HgProjectSettings projectSettings,
      ProjectLevelVcsManager vcsManager) {
    super(project, VCS_NAME);
    this.globalSettings = globalSettings;
    this.projectSettings = projectSettings;
    myVcsManager = vcsManager;
    changeProvider = new HgChangeProvider(project, getKeyInstanceMethod());
    rollbackEnvironment = new HgRollbackEnvironment(project);
    diffProvider = new HgDiffProvider(project);
    historyProvider = new HgHistoryProvider(project);
    checkinEnvironment = new HgCheckinEnvironment(project);
    annotationProvider = new HgAnnotationProvider(project);
    updateEnvironment = new HgUpdateEnvironment(project);
    committedChangesProvider = new HgCachingCommittedChangesProvider(project, this);
    myMergeProvider = new HgMergeProvider(myProject);
    myCommitAndPushExecutor = new HgCommitAndPushExecutor(checkinEnvironment);
  }

  public String getDisplayName() {
    return HgVcsMessages.message("hg4idea.mercurial");
  }

  public Configurable getConfigurable() {
    return new HgProjectConfigurable(getProject(), projectSettings);
  }

  @NotNull
  public HgProjectSettings getProjectSettings() {
    return projectSettings;
  }

  @Override
  public ChangeProvider getChangeProvider() {
    return changeProvider;
  }

  @Nullable
  @Override
  public RollbackEnvironment createRollbackEnvironment() {
    return rollbackEnvironment;
  }

  @Override
  public DiffProvider getDiffProvider() {
    return diffProvider;
  }

  @Override
  public VcsHistoryProvider getVcsHistoryProvider() {
    return historyProvider;
  }

  @Override
  public VcsHistoryProvider getVcsBlockHistoryProvider() {
    return getVcsHistoryProvider();
  }

  @Nullable
  @Override
  public CheckinEnvironment createCheckinEnvironment() {
    return checkinEnvironment;
  }

  @Override
  public AnnotationProvider getAnnotationProvider() {
    return annotationProvider;
  }

  @Override
  public MergeProvider getMergeProvider() {
    return myMergeProvider;
  }

  @Nullable
  @Override
  public UpdateEnvironment createUpdateEnvironment() {
    return updateEnvironment;
  }

  @Override
  public UpdateEnvironment getIntegrateEnvironment() {
    return null;
  }

  @Override
  public boolean fileListenerIsSynchronous() {
    return false;
  }

  @Override
  public CommittedChangesProvider getCommittedChangesProvider() {
    return committedChangesProvider;
  }

  @Override
  public boolean allowsNestedRoots() {
    return true;
  }

  @Override
  public <S> List<S> filterUniqueRoots(
      final List<S> in, final Convertor<S, VirtualFile> convertor) {
    Collections.sort(
        in, new ComparatorDelegate<S, VirtualFile>(convertor, FilePathComparator.getInstance()));

    for (int i = 1; i < in.size(); i++) {
      final S sChild = in.get(i);
      final VirtualFile child = convertor.convert(sChild);
      final VirtualFile childRoot = HgUtil.getHgRootOrNull(myProject, child);
      if (childRoot == null) {
        continue;
      }
      for (int j = i - 1; j >= 0; --j) {
        final S sParent = in.get(j);
        final VirtualFile parent = convertor.convert(sParent);
        // if the parent is an ancestor of the child and that they share common root, the child is
        // removed
        if (VfsUtilCore.isAncestor(parent, child, false)
            && VfsUtilCore.isAncestor(childRoot, parent, false)) {
          in.remove(i);
          //noinspection AssignmentToForLoopParameter
          --i;
          break;
        }
      }
    }
    return in;
  }

  @Override
  public RootsConvertor getCustomConvertor() {
    return HgRootsHandler.getInstance(myProject);
  }

  @Override
  public boolean isVersionedDirectory(VirtualFile dir) {
    return HgUtil.getNearestHgRoot(dir) != null;
  }

  /**
   * @return the prompthooks.py extension used for capturing prompts from Mercurial and requesting
   *     IDEA's user about authentication.
   */
  @NotNull
  public File getPromptHooksExtensionFile() {
    if (myPromptHooksExtensionFile == null) {
      // check that hooks are available
      myPromptHooksExtensionFile = HgUtil.getTemporaryPythonFile("prompthooks");
      if (myPromptHooksExtensionFile == null || !myPromptHooksExtensionFile.exists()) {
        LOG.error(
            "prompthooks.py Mercurial extension is not found. Please reinstall "
                + ApplicationNamesInfo.getInstance().getProductName());
      }
    }
    return myPromptHooksExtensionFile;
  }

  @Override
  public void activate() {
    // validate hg executable on start and update hg version
    checkExecutableAndVersion();

    // status bar
    myStatusWidget = new HgStatusWidget(this, getProject(), projectSettings);
    myStatusWidget.activate();

    myIncomingWidget = new HgIncomingOutgoingWidget(this, getProject(), projectSettings, true);
    myOutgoingWidget = new HgIncomingOutgoingWidget(this, getProject(), projectSettings, false);

    ApplicationManager.getApplication()
        .invokeAndWait(
            new Runnable() {
              @Override
              public void run() {
                myIncomingWidget.activate();
                myOutgoingWidget.activate();
              }
            },
            ModalityState.NON_MODAL);

    // updaters and listeners
    myHgRemoteStatusUpdater =
        new HgRemoteStatusUpdater(
            this,
            myIncomingWidget.getChangesetStatus(),
            myOutgoingWidget.getChangesetStatus(),
            projectSettings);
    myHgRemoteStatusUpdater.activate();

    messageBusConnection = myProject.getMessageBus().connect();
    messageBusConnection.subscribe(
        FileEditorManagerListener.FILE_EDITOR_MANAGER,
        new FileEditorManagerAdapter() {
          @Override
          public void selectionChanged(@NotNull FileEditorManagerEvent event) {
            Project project = event.getManager().getProject();
            project.getMessageBus().syncPublisher(BRANCH_TOPIC).update(project, null);
          }
        });

    myVFSListener = new HgVFSListener(myProject, this);

    // ignore temporary files
    final String ignoredPattern = FileTypeManager.getInstance().getIgnoredFilesList();
    if (!ignoredPattern.contains(ORIG_FILE_PATTERN)) {
      final String newPattern =
          ignoredPattern + (ignoredPattern.endsWith(";") ? "" : ";") + ORIG_FILE_PATTERN;
      HgUtil.runWriteActionLater(
          new Runnable() {
            public void run() {
              FileTypeManager.getInstance().setIgnoredFilesList(newPattern);
            }
          });
    }

    // Force a branch topic update
    myProject.getMessageBus().syncPublisher(BRANCH_TOPIC).update(myProject, null);
  }

  private void checkExecutableAndVersion() {
    if (!ApplicationManager.getApplication().isUnitTestMode()
        && getExecutableValidator().checkExecutableAndNotifyIfNeeded()) {
      checkVersion();
    }
  }

  @Override
  public void deactivate() {
    if (myHgRemoteStatusUpdater != null) {
      myHgRemoteStatusUpdater.deactivate();
      myHgRemoteStatusUpdater = null;
    }
    if (myStatusWidget != null) {
      myStatusWidget.deactivate();
      myStatusWidget = null;
    }
    if (myIncomingWidget != null) {
      myIncomingWidget.deactivate();
      myIncomingWidget = null;
    }
    if (myOutgoingWidget != null) {
      myOutgoingWidget.deactivate();
      myOutgoingWidget = null;
    }
    if (messageBusConnection != null) {
      messageBusConnection.disconnect();
    }

    if (myVFSListener != null) {
      Disposer.dispose(myVFSListener);
      myVFSListener = null;
    }

    super.deactivate();
  }

  @Nullable
  public static HgVcs getInstance(Project project) {
    if (project == null || project.isDisposed()) {
      return null;
    }
    final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project);
    if (vcsManager == null) {
      return null;
    }
    return (HgVcs) vcsManager.findVcsByName(VCS_NAME);
  }

  @NotNull
  public HgGlobalSettings getGlobalSettings() {
    return globalSettings;
  }

  public void showMessageInConsole(String message, final TextAttributes style) {
    if (message.length() > MAX_CONSOLE_OUTPUT_SIZE) {
      message = message.substring(0, MAX_CONSOLE_OUTPUT_SIZE);
    }
    myVcsManager.addMessageToConsoleWindow(message, style);
  }

  public HgExecutableValidator getExecutableValidator() {
    synchronized (myExecutableValidatorLock) {
      if (myExecutableValidator == null) {
        myExecutableValidator = new HgExecutableValidator(myProject, this);
      }
      return myExecutableValidator;
    }
  }

  @Override
  public boolean reportsIgnoredDirectories() {
    return false;
  }

  @Override
  public List<CommitExecutor> getCommitExecutors() {
    return Collections.singletonList(myCommitAndPushExecutor);
  }

  public static VcsKey getKey() {
    return ourKey;
  }

  @Override
  public VcsType getType() {
    return VcsType.distributed;
  }

  @Override
  public CheckoutProvider getCheckoutProvider() {
    return new HgCheckoutProvider();
  }

  /**
   * Checks Hg version and updates the myVersion variable. In the case of nullable or unsupported
   * version reports the problem.
   */
  public void checkVersion() {
    final String executable = getGlobalSettings().getHgExecutable();
    HgCommandResultNotifier errorNotification = new HgCommandResultNotifier(myProject);
    final String SETTINGS_LINK = "settings";
    final String UPDATE_LINK = "update";
    NotificationListener linkAdapter =
        new NotificationListener.Adapter() {
          @Override
          protected void hyperlinkActivated(
              @NotNull Notification notification, @NotNull HyperlinkEvent e) {
            if (SETTINGS_LINK.equals(e.getDescription())) {
              ShowSettingsUtil.getInstance()
                  .showSettingsDialog(myProject, getConfigurable().getDisplayName());
            } else if (UPDATE_LINK.equals(e.getDescription())) {
              BrowserUtil.browse("http://mercurial.selenic.com");
            }
          }
        };
    try {
      myVersion = HgVersion.identifyVersion(executable);
      // if version is not supported, but have valid hg executable
      if (!myVersion.isSupported()) {
        LOG.info("Unsupported Hg version: " + myVersion);
        String message =
            String.format(
                "The <a href='"
                    + SETTINGS_LINK
                    + "'>configured</a> version of Hg is not supported: %s.<br/> "
                    + "The minimal supported version is %s. Please <a href='"
                    + UPDATE_LINK
                    + "'>update</a>.",
                myVersion,
                HgVersion.MIN);
        errorNotification.notifyError(null, "Unsupported Hg version", message, linkAdapter);
      } else if (myVersion.hasUnsupportedExtensions()) {
        String unsupportedExtensionsAsString = myVersion.getUnsupportedExtensions().toString();
        LOG.warn("Unsupported Hg extensions: " + unsupportedExtensionsAsString);
        String message =
            String.format(
                "Some hg extensions %s are not found or not supported by your hg version and will be ignored.\n"
                    + "Please, update your hgrc or Mercurial.ini file",
                unsupportedExtensionsAsString);
        errorNotification.notifyWarning("Unsupported Hg version", message);
      }
    } catch (Exception e) {
      if (getExecutableValidator().checkExecutableAndNotifyIfNeeded()) {
        // sometimes not hg application has version command, but we couldn't parse an answer as
        // valid hg,
        // so parse(output) throw ParseException, but hg and git executable seems to be valid in
        // this case
        final String reason = (e.getCause() != null ? e.getCause() : e).getMessage();
        String message = HgVcsMessages.message("hg4idea.unable.to.run.hg", executable);
        errorNotification.notifyError(
            null,
            message,
            String.format(
                reason
                    + "<br/> Please check your hg executable path in <a href='"
                    + SETTINGS_LINK
                    + "'> settings </a>"),
            linkAdapter);
      }
    }
  }

  /**
   * @return the version number of Hg, which is used by IDEA. Or {@link HgVersion#NULL} if version
   *     info is unavailable.
   */
  @NotNull
  public HgVersion getVersion() {
    return myVersion;
  }
}
  public boolean updateStep() {
    final MultiMap<VcsRoot, String> dirty = new MultiMap<VcsRoot, String>();
    final long oldPoint =
        System.currentTimeMillis()
            - (myVcsConfiguration.CHANGED_ON_SERVER_INTERVAL > 0
                ? myVcsConfiguration.CHANGED_ON_SERVER_INTERVAL * 60000
                : DISCRETE);

    synchronized (myLock) {
      // just copies myQueries MultiMap to dirty MultiMap
      for (VcsRoot root : myQueries.keySet()) {
        final Collection<String> collection = myQueries.get(root);
        for (String s : collection) {
          dirty.putValue(root, s);
        }
      }
      myQueries.clear();

      // collect roots for which cache update should be performed (by timestamp)
      final Set<VcsRoot> roots = new HashSet<VcsRoot>();
      for (Map.Entry<VcsRoot, Long> entry : myTs.entrySet()) {
        // ignore timestamp, as still remote changes checking is required
        // TODO: why not to add in roots anyway??? - as dirty is still checked when adding myChanged
        // files.
        if (!dirty.get(entry.getKey()).isEmpty()) continue;

        // update only if timeout expired
        final Long ts = entry.getValue();
        if ((ts == null) || (oldPoint > ts)) {
          roots.add(entry.getKey());
        }
      }

      // Add dirty files from those vcs roots, that
      // - needs to be update by timestamp criteria
      // - that already contain files for update through manually added requests
      for (Map.Entry<String, Pair<Boolean, VcsRoot>> entry : myChanged.entrySet()) {
        final VcsRoot vcsRoot = entry.getValue().getSecond();
        if ((!dirty.get(vcsRoot).isEmpty()) || roots.contains(vcsRoot)) {
          dirty.putValue(vcsRoot, entry.getKey());
        }
      }
    }

    if (dirty.isEmpty()) return false;

    final Map<String, Pair<Boolean, VcsRoot>> results =
        new HashMap<String, Pair<Boolean, VcsRoot>>();
    for (VcsRoot vcsRoot : dirty.keySet()) {
      // todo - actually it means nothing since the only known VCS to use this scheme is Git and now
      // it always allow
      // todo - background operations. when it changes, develop more flexible behavior here
      if (!vcsRoot.getVcs().isVcsBackgroundOperationsAllowed(vcsRoot.getPath())) continue;
      final TreeDiffProvider provider = vcsRoot.getVcs().getTreeDiffProvider();
      if (provider == null) continue;

      final Collection<String> paths = dirty.get(vcsRoot);
      final Collection<String> remotelyChanged =
          provider.getRemotelyChanged(vcsRoot.getPath(), paths);
      for (String path : paths) {
        // TODO: Contains invoked for each file - better to use Set (implementations just use List)
        // TODO: Why to store boolean for changed or not - why not just remove such values from
        // myChanged???
        results.put(path, new Pair<Boolean, VcsRoot>(remotelyChanged.contains(path), vcsRoot));
      }
    }

    final long curTime = System.currentTimeMillis();
    synchronized (myLock) {
      myChanged.putAll(results);
      for (VcsRoot vcsRoot : dirty.keySet()) {
        myTs.put(vcsRoot, curTime);
      }
    }

    return true;
  }
  public boolean updateStep(final AtomicSectionsAware atomicSectionsAware) {
    final MultiMap<VcsRoot, String> dirty = new MultiMap<VcsRoot, String>();
    final long oldPoint =
        System.currentTimeMillis()
            - (myVcsConfiguration.CHANGED_ON_SERVER_INTERVAL > 0
                ? myVcsConfiguration.CHANGED_ON_SERVER_INTERVAL * 60000
                : DISCRETE);

    synchronized (myLock) {
      for (VcsRoot root : myQueries.keySet()) {
        final Collection<String> collection = myQueries.get(root);
        for (String s : collection) {
          dirty.putValue(root, s);
        }
      }
      myQueries.clear();

      final Set<VcsRoot> roots = new HashSet<VcsRoot>();
      for (Map.Entry<VcsRoot, Long> entry : myTs.entrySet()) {
        if (!dirty.get(entry.getKey()).isEmpty()) continue;

        final Long ts = entry.getValue();
        if ((ts == null) || (oldPoint > ts)) {
          roots.add(entry.getKey());
        }
      }
      for (Map.Entry<String, Pair<Boolean, VcsRoot>> entry : myChanged.entrySet()) {
        final VcsRoot vcsRoot = entry.getValue().getSecond();
        if ((!dirty.get(vcsRoot).isEmpty()) || roots.contains(vcsRoot)) {
          dirty.putValue(vcsRoot, entry.getKey());
        }
      }
    }

    if (dirty.isEmpty()) return false;

    final Map<String, Pair<Boolean, VcsRoot>> results =
        new HashMap<String, Pair<Boolean, VcsRoot>>();
    for (VcsRoot vcsRoot : dirty.keySet()) {
      atomicSectionsAware.checkShouldExit();
      // todo - actually it means nothing since the only known VCS to use this scheme is Git and now
      // it always allow
      // todo - background operations. when it changes, develop more flexible behavior here
      if (!vcsRoot.vcs.isVcsBackgroundOperationsAllowed(vcsRoot.path)) continue;
      final TreeDiffProvider provider = vcsRoot.vcs.getTreeDiffProvider();
      if (provider == null) continue;

      final Collection<String> paths = dirty.get(vcsRoot);
      final Collection<String> remotelyChanged = provider.getRemotelyChanged(vcsRoot.path, paths);
      for (String path : paths) {
        results.put(path, new Pair<Boolean, VcsRoot>(remotelyChanged.contains(path), vcsRoot));
      }
    }

    final long curTime = System.currentTimeMillis();
    synchronized (myLock) {
      myChanged.putAll(results);
      for (VcsRoot vcsRoot : dirty.keySet()) {
        myTs.put(vcsRoot, curTime);
      }
    }

    return true;
  }