diff --git a/git/nbproject/project.xml b/git/nbproject/project.xml
--- a/git/nbproject/project.xml
+++ b/git/nbproject/project.xml
@@ -20,7 +20,7 @@
1
- 1.0
+ 1.5
diff --git a/git/src/org/netbeans/modules/git/FileStatusCache.java b/git/src/org/netbeans/modules/git/FileStatusCache.java
--- a/git/src/org/netbeans/modules/git/FileStatusCache.java
+++ b/git/src/org/netbeans/modules/git/FileStatusCache.java
@@ -65,6 +65,7 @@
import org.netbeans.modules.versioning.spi.VCSContext;
import org.netbeans.modules.versioning.spi.VersioningSupport;
import org.netbeans.modules.git.FileInformation.Status;
+import org.netbeans.modules.git.client.GitClient;
import org.netbeans.modules.git.utils.GitUtils;
import org.openide.filesystems.FileUtil;
import org.openide.util.RequestProcessor;
@@ -205,9 +206,11 @@
LOG.log(Level.FINE, "refreshAllRoots() roots: {0}, repositoryRoot: {1} ", new Object[] {refreshEntry.getValue(), repository.getAbsolutePath()}); // NOI18N
}
Map interestingFiles;
+ GitClient client = null;
try {
// find all files with not up-to-date or ignored status
- interestingFiles = Git.getInstance().getClient(repository).getStatus(refreshEntry.getValue().toArray(new File[refreshEntry.getValue().size()]), pm);
+ client = Git.getInstance().getClient(repository);
+ interestingFiles = client.getStatus(refreshEntry.getValue().toArray(new File[refreshEntry.getValue().size()]), pm);
if (pm.isCanceled()) {
return;
}
@@ -258,6 +261,9 @@
} catch (GitException ex) {
LOG.log(Level.INFO, "refreshAllRoots() file: {0} {1} {2} ", new Object[] {repository.getAbsolutePath(), refreshEntry.getValue(), ex.toString()}); //NOI18N
} finally {
+ if (client != null) {
+ client.release();
+ }
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "refreshAllRoots() roots: finished repositoryRoot: {0} ", new Object[] { repository.getAbsolutePath() } ); // NOI18N
}
diff --git a/git/src/org/netbeans/modules/git/FilesystemInterceptor.java b/git/src/org/netbeans/modules/git/FilesystemInterceptor.java
--- a/git/src/org/netbeans/modules/git/FilesystemInterceptor.java
+++ b/git/src/org/netbeans/modules/git/FilesystemInterceptor.java
@@ -61,6 +61,7 @@
import org.netbeans.libs.git.GitException;
import org.netbeans.libs.git.GitRemoteConfig;
import org.netbeans.modules.git.FileInformation.Status;
+import org.netbeans.modules.git.client.GitClient;
import org.netbeans.modules.git.ui.history.SearchHistoryAction;
import org.netbeans.modules.git.ui.repository.RepositoryInfo;
import org.netbeans.modules.git.utils.GitUtils;
@@ -122,10 +123,16 @@
Git git = Git.getInstance();
final File root = git.getRepositoryRoot(file);
if (root == null) return false;
+ GitClient client = null;
try {
- git.getClient(root).reset(new File[] { file }, "HEAD", true, GitUtils.NULL_PROGRESS_MONITOR);
+ client = git.getClient(root);
+ client.reset(new File[] { file }, "HEAD", true, GitUtils.NULL_PROGRESS_MONITOR);
} catch (GitException ex) {
LOG.log(Level.INFO, "beforeCreate(): File: {0} {1}", new Object[] { file.getAbsolutePath(), ex.toString()}); //NOI18N
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
LOG.log(Level.FINER, "beforeCreate(): finished: {0}", file); // NOI18N
}
@@ -158,9 +165,11 @@
if (file == null) return;
Git git = Git.getInstance();
File root = git.getRepositoryRoot(file);
+ GitClient client = null;
try {
if (GitUtils.getGitFolderForRoot(root).exists()) {
- git.getClient(root).remove(new File[] { file }, false, GitUtils.NULL_PROGRESS_MONITOR);
+ client = git.getClient(root);
+ client.remove(new File[] { file }, false, GitUtils.NULL_PROGRESS_MONITOR);
} else if (file.exists()) {
Utils.deleteRecursively(file);
if (file.exists()) {
@@ -178,6 +187,10 @@
Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_DeleteFailed", new Object[] { file, e.getLocalizedMessage() })); //NOI18N
ex.initCause(e);
throw ex;
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
@@ -207,17 +220,19 @@
Git git = Git.getInstance();
File root = git.getRepositoryRoot(from);
File dstRoot = git.getRepositoryRoot(to);
+ GitClient client = null;
try {
if (root != null && root.equals(dstRoot) && !cache.getStatus(to).containsStatus(Status.NOTVERSIONED_EXCLUDED)) {
// target does not lie under ignored folder and is in the same repo as src
+ client = git.getClient(root);
if (equalPathsIgnoreCase(from, to)) {
// must do rename --after because the files/paths equal on Win or Mac
if (!from.renameTo(to)) {
throw new IOException(NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, "" })); //NOI18N
}
- git.getClient(root).rename(from, to, true, GitUtils.NULL_PROGRESS_MONITOR);
+ client.rename(from, to, true, GitUtils.NULL_PROGRESS_MONITOR);
} else {
- git.getClient(root).rename(from, to, false, GitUtils.NULL_PROGRESS_MONITOR);
+ client.rename(from, to, false, GitUtils.NULL_PROGRESS_MONITOR);
}
} else {
boolean result = from.renameTo(to);
@@ -225,7 +240,8 @@
throw new IOException(NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, "" })); //NOI18N
}
if (root != null) {
- git.getClient(root).remove(new File[] { from }, true, GitUtils.NULL_PROGRESS_MONITOR);
+ client = git.getClient(root);
+ client.remove(new File[] { from }, true, GitUtils.NULL_PROGRESS_MONITOR);
}
}
} catch (GitException e) {
@@ -233,6 +249,10 @@
Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, e.getLocalizedMessage() })); //NOI18N
ex.initCause(e);
throw ex;
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
@@ -283,15 +303,21 @@
// target lies under ignored folder, do not add it
return;
}
+ GitClient client = null;
try {
if (root.equals(dstRoot)) {
- git.getClient(root).copyAfter(from, to, GitUtils.NULL_PROGRESS_MONITOR);
+ client = git.getClient(root);
+ client.copyAfter(from, to, GitUtils.NULL_PROGRESS_MONITOR);
}
} catch (GitException e) {
IOException ex = new IOException();
Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_CopyFailed", new Object[] { from, to, e.getLocalizedMessage() })); //NOI18N
ex.initCause(e);
throw ex;
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
diff --git a/git/src/org/netbeans/modules/git/Git.java b/git/src/org/netbeans/modules/git/Git.java
--- a/git/src/org/netbeans/modules/git/Git.java
+++ b/git/src/org/netbeans/modules/git/Git.java
@@ -145,8 +145,9 @@
void getOriginalFile (File workingCopy, File originalFile) {
File repository = getRepositoryRoot(workingCopy);
if (repository != null) {
+ GitClient client = null;
try {
- GitClient client = getClient(repository);
+ client = getClient(repository);
FileOutputStream fos = new FileOutputStream(originalFile);
boolean ok;
try {
@@ -168,6 +169,10 @@
originalFile.delete();
} catch (IOException ex) {
LOG.log(Level.INFO, "IO exception", ex); //NOI18N
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
}
diff --git a/git/src/org/netbeans/modules/git/HistoryProvider.java b/git/src/org/netbeans/modules/git/HistoryProvider.java
--- a/git/src/org/netbeans/modules/git/HistoryProvider.java
+++ b/git/src/org/netbeans/modules/git/HistoryProvider.java
@@ -333,8 +333,9 @@
HistoryEntry ancestorEntry = commonAncestors.get(file);
if (ancestorEntry == null && !commonAncestors.containsKey(file)) {
GitRevisionInfo parent = null;
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
if (info.getParents().length == 1) {
File historyFile = info.getModifiedFiles().containsKey(file)
? file
@@ -347,6 +348,10 @@
}
} catch (GitException ex) {
LOG.log(Level.INFO, null, ex);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
ancestorEntry = parent == null ? null : createHistoryEntry(parent, files, repository);
commonAncestors.put(file, ancestorEntry);
diff --git a/git/src/org/netbeans/modules/git/HistoryRegistry.java b/git/src/org/netbeans/modules/git/HistoryRegistry.java
--- a/git/src/org/netbeans/modules/git/HistoryRegistry.java
+++ b/git/src/org/netbeans/modules/git/HistoryRegistry.java
@@ -85,13 +85,19 @@
crit.setFiles(files);
crit.setFollowRenames(true);
crit.setIncludeMerges(false);
- GitRevisionInfo[] history = client.log(crit, pm);
- if (!pm.isCanceled() && history.length > 0) {
- for (File f : files) {
- logs.put(f, Arrays.asList(history));
+ try {
+ GitRevisionInfo[] history = client.log(crit, pm);
+ if (!pm.isCanceled() && history.length > 0) {
+ for (File f : files) {
+ logs.put(f, Arrays.asList(history));
+ }
+ }
+ return history;
+ } finally {
+ if (client != null) {
+ client.release();
}
}
- return history;
}
public File getHistoryFile(final File repository, final File originalFile, final String revision, final boolean dryTry) {
@@ -150,13 +156,18 @@
if(changePaths == null && !dryTry) {
long t1 = System.currentTimeMillis();
Map cps = null;
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
GitRevisionInfo lms = client.log(historyRevision, pm);
assert lms != null;
cps = lms.getModifiedFiles();
} catch (GitException ex) {
LOG.log(Level.INFO, null, ex);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
if (cps == null) {
changePaths = Collections.emptyList();
diff --git a/git/src/org/netbeans/modules/git/VersionsCache.java b/git/src/org/netbeans/modules/git/VersionsCache.java
--- a/git/src/org/netbeans/modules/git/VersionsCache.java
+++ b/git/src/org/netbeans/modules/git/VersionsCache.java
@@ -84,8 +84,9 @@
} else {
File tempFile = new File(Utils.getTempFolder(), "nb-git-" + base.getName()); //NOI18N
tempFile.deleteOnExit();
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
boolean result;
FileOutputStream fos = new FileOutputStream(tempFile);
try {
@@ -109,6 +110,10 @@
tempFile = null;
} catch (GitException ex) {
throw new IOException(ex);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
return tempFile;
}
diff --git a/git/src/org/netbeans/modules/git/api/Git.java b/git/src/org/netbeans/modules/git/api/Git.java
--- a/git/src/org/netbeans/modules/git/api/Git.java
+++ b/git/src/org/netbeans/modules/git/api/Git.java
@@ -95,8 +95,9 @@
}
public static void initializeRepository (File localFolder, String repositoryUrl, PasswordAuthentication credentials) throws IOException, URISyntaxException {
+ GitClient client = null;
try {
- GitClient client = org.netbeans.modules.git.Git.getInstance().getClient(localFolder);
+ client = org.netbeans.modules.git.Git.getInstance().getClient(localFolder);
client.init(GitUtils.NULL_PROGRESS_MONITOR);
String remoteName = "origin"; //NOI18N
client.setRemote(new GitRemoteConfig(remoteName, Arrays.asList(repositoryUrl),
@@ -122,6 +123,9 @@
} catch (GitException ex) {
throw new IOException(ex);
} finally {
+ if (client != null) {
+ client.release();
+ }
org.netbeans.modules.git.Git.getInstance().clearAncestorCaches();
VersioningSupport.versionedRootsChanged();
}
diff --git a/git/src/org/netbeans/modules/git/client/GitClient.java b/git/src/org/netbeans/modules/git/client/GitClient.java
--- a/git/src/org/netbeans/modules/git/client/GitClient.java
+++ b/git/src/org/netbeans/modules/git/client/GitClient.java
@@ -46,8 +46,10 @@
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -77,6 +79,7 @@
import org.netbeans.modules.versioning.util.IndexingBridge;
import org.netbeans.modules.versioning.util.Utils;
import org.openide.util.NetworkSettings;
+import org.openide.util.RequestProcessor;
/**
*
@@ -87,6 +90,9 @@
private final org.netbeans.libs.git.GitClient delegate;
private final GitProgressSupport progressSupport;
private final boolean handleAuthenticationIssues;
+ private static final int CLEANUP_TIME = 15000;
+ private static final List unusedClients = new LinkedList();
+ private static RequestProcessor.Task cleanTask = Git.getInstance().getRequestProcessor().create(new CleanTask());
/**
* Set of commands that do not need to run under repository lock
@@ -194,6 +200,7 @@
));
private static final Logger LOG = Logger.getLogger(GitClient.class.getName());
private final File repositoryRoot;
+ private boolean released;
public GitClient (File repository, GitProgressSupport progressSupport, boolean handleAuthenticationIssues) throws GitException {
this.repositoryRoot = repository;
@@ -586,6 +593,20 @@
}
}, "push"); //NOI18N
}
+
+ /**
+ * Schedule cleanup of git repository used by this client
+ */
+ public void release () {
+ synchronized (unusedClients) {
+ if (released) {
+ return;
+ }
+ unusedClients.add(delegate);
+ released = true;
+ }
+ cleanTask.schedule(CLEANUP_TIME);
+ }
public void remove (final File[] roots, final boolean cached, final ProgressMonitor monitor) throws GitException {
new CommandInvoker().runMethod(new Callable() {
@@ -681,10 +702,29 @@
}
}, "unignore"); //NOI18N
}
+
+ private static class CleanTask implements Runnable {
+
+ @Override
+ public void run () {
+ Set toRelease;
+ synchronized (unusedClients) {
+ toRelease = new HashSet(unusedClients);
+ unusedClients.clear();
+ }
+ for (org.netbeans.libs.git.GitClient unusuedClient : toRelease) {
+ unusuedClient.release();
+ }
+ }
+
+ }
private final class CommandInvoker {
private T runMethod (Callable callable, String methodName) throws GitException {
+ if (released) {
+ throw new IllegalStateException("Client already released.");
+ }
return runMethod(callable, methodName, new File[0]);
}
@@ -796,4 +836,12 @@
private static boolean withoutAuthenticator (String commandName) {
return NETWORK_COMMANDS.contains(commandName);
}
+
+ @Override
+ protected void finalize () throws Throwable {
+ if (!released) {
+ release();
+ }
+ super.finalize();
+ }
}
diff --git a/git/src/org/netbeans/modules/git/client/GitProgressSupport.java b/git/src/org/netbeans/modules/git/client/GitProgressSupport.java
--- a/git/src/org/netbeans/modules/git/client/GitProgressSupport.java
+++ b/git/src/org/netbeans/modules/git/client/GitProgressSupport.java
@@ -105,6 +105,9 @@
LOG.log(Level.FINE, "End - {0}", originalDisplayName); //NOI18N
finishProgress();
getLogger().closeLog();
+ if (gitClient != null) {
+ gitClient.release();
+ }
}
}
diff --git a/git/src/org/netbeans/modules/git/ui/actions/SingleRepositoryAction.java b/git/src/org/netbeans/modules/git/ui/actions/SingleRepositoryAction.java
--- a/git/src/org/netbeans/modules/git/ui/actions/SingleRepositoryAction.java
+++ b/git/src/org/netbeans/modules/git/ui/actions/SingleRepositoryAction.java
@@ -120,8 +120,9 @@
@Override
public void run () {
Set urls = new HashSet();
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repositoryRoot);
+ client = Git.getInstance().getClient(repositoryRoot);
Map cfgs = client.getRemotes(GitUtils.NULL_PROGRESS_MONITOR);
for (Map.Entry e : cfgs.entrySet()) {
GitRemoteConfig cfg = e.getValue();
@@ -133,6 +134,10 @@
}
} catch (GitException ex) {
// not interested
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
for (String url : urls) {
if (!url.trim().isEmpty()) {
diff --git a/git/src/org/netbeans/modules/git/ui/branch/CreateBranch.java b/git/src/org/netbeans/modules/git/ui/branch/CreateBranch.java
--- a/git/src/org/netbeans/modules/git/ui/branch/CreateBranch.java
+++ b/git/src/org/netbeans/modules/git/ui/branch/CreateBranch.java
@@ -168,8 +168,9 @@
@Override
public void run () {
final String branchName = CreateBranch.this.branchName;
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
final Map branches = client.getBranches(false, GitUtils.NULL_PROGRESS_MONITOR);
EventQueue.invokeLater(new Runnable () {
@Override
@@ -182,6 +183,10 @@
});
} catch (GitException ex) {
GitClientExceptionHandler.notifyException(ex, true);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
}
diff --git a/git/src/org/netbeans/modules/git/ui/clone/RepositoryStep.java b/git/src/org/netbeans/modules/git/ui/clone/RepositoryStep.java
--- a/git/src/org/netbeans/modules/git/ui/clone/RepositoryStep.java
+++ b/git/src/org/netbeans/modules/git/ui/clone/RepositoryStep.java
@@ -189,8 +189,9 @@
@Override
public void perform() {
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(getRepositoryRoot(), this, false);
+ client = Git.getInstance().getClient(getRepositoryRoot(), this, false);
client.init(getProgressMonitor());
branches = new HashMap();
branches.putAll(client.listRemoteBranches(uri.toPrivateString(), getProgressMonitor()));
@@ -204,6 +205,9 @@
message = new Message(str, false);
setValid(false, message);
} finally {
+ if (client != null) {
+ client.release();
+ }
Utils.deleteRecursively(getRepositoryRoot());
if (message == null && isCanceled()) {
message = new Message(NbBundle.getMessage(RepositoryStep.class, "MSG_RepositoryStep.validationCanceled"), true); //NOI18N
diff --git a/git/src/org/netbeans/modules/git/ui/commit/CommitAction.java b/git/src/org/netbeans/modules/git/ui/commit/CommitAction.java
--- a/git/src/org/netbeans/modules/git/ui/commit/CommitAction.java
+++ b/git/src/org/netbeans/modules/git/ui/commit/CommitAction.java
@@ -114,12 +114,17 @@
public void run() {
GitUser user = null;
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
user = client.getUser();
} catch (GitException ex) {
GitClientExceptionHandler.notifyException(ex, true);
return;
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
GitCommitPanel panel = state == GitRepositoryState.MERGING_RESOLVED
@@ -326,11 +331,16 @@
commitPermitted = false;
Map conflicts = Collections.emptyMap();
if (state.equals(GitRepositoryState.MERGING)) {
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
conflicts = client.getConflicts(new File[] { repository }, GitUtils.NULL_PROGRESS_MONITOR);
} catch (GitException ex) {
LOG.log(Level.INFO, null, ex);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
NotifyDescriptor nd;
diff --git a/git/src/org/netbeans/modules/git/ui/commit/DeleteLocalAction.java b/git/src/org/netbeans/modules/git/ui/commit/DeleteLocalAction.java
--- a/git/src/org/netbeans/modules/git/ui/commit/DeleteLocalAction.java
+++ b/git/src/org/netbeans/modules/git/ui/commit/DeleteLocalAction.java
@@ -114,14 +114,19 @@
};
for (Map.Entry> e : sortedFiles.entrySet()) {
File root = e.getKey();
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(root);
+ client = Git.getInstance().getClient(root);
client.addNotificationListener(list);
File[] roots = e.getValue().toArray(new File[e.getValue().size()]);
client.reset(roots, GitUtils.HEAD, false, getProgressMonitor());
client.clean(roots, getProgressMonitor());
} catch (GitException ex) {
LOG.log(Level.INFO, null, ex);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
}
diff --git a/git/src/org/netbeans/modules/git/ui/ignore/IgnoreAction.java b/git/src/org/netbeans/modules/git/ui/ignore/IgnoreAction.java
--- a/git/src/org/netbeans/modules/git/ui/ignore/IgnoreAction.java
+++ b/git/src/org/netbeans/modules/git/ui/ignore/IgnoreAction.java
@@ -174,12 +174,17 @@
private File[] filterFolders (File repository, File[] roots) {
List unignoredFolders = new LinkedList();
Map statuses;
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
statuses = client.getStatus(roots, GitUtils.NULL_PROGRESS_MONITOR);
} catch (GitException ex) {
LOG.log(Level.INFO, null, ex);
statuses = Collections.emptyMap();
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
for (File f : roots) {
GitStatus st = statuses.get(f);
diff --git a/git/src/org/netbeans/modules/git/ui/push/PushAction.java b/git/src/org/netbeans/modules/git/ui/push/PushAction.java
--- a/git/src/org/netbeans/modules/git/ui/push/PushAction.java
+++ b/git/src/org/netbeans/modules/git/ui/push/PushAction.java
@@ -193,15 +193,21 @@
List revisionList = new LinkedList();
Set visitedRevisions = new HashSet();
GitClient client = Git.getInstance().getClient(getRepositoryRoot()); // do not use progresssupport's client, that one logs into output
- for (PushMapping mapping : pushMappings) {
- if (mapping instanceof PushMapping.PushBranchMapping) {
- PushMapping.PushBranchMapping branchMapping = (PushMapping.PushBranchMapping) mapping;
- String remoteRevisionId = branchMapping.getRemoteRepositoryBranchHeadId();
- String localRevisionId = branchMapping.getLocalRepositoryBranchHeadId();
- revisionList.addAll(addRevisions(client, visitedRevisions, remoteRevisionId, localRevisionId));
+ try {
+ for (PushMapping mapping : pushMappings) {
+ if (mapping instanceof PushMapping.PushBranchMapping) {
+ PushMapping.PushBranchMapping branchMapping = (PushMapping.PushBranchMapping) mapping;
+ String remoteRevisionId = branchMapping.getRemoteRepositoryBranchHeadId();
+ String localRevisionId = branchMapping.getLocalRepositoryBranchHeadId();
+ revisionList.addAll(addRevisions(client, visitedRevisions, remoteRevisionId, localRevisionId));
+ }
+ if (isCanceled()) {
+ break;
+ }
}
- if (isCanceled()) {
- break;
+ } finally {
+ if (client != null) {
+ client.release();
}
}
return revisionList;
@@ -211,12 +217,18 @@
List revisionList = new LinkedList();
Set visitedRevisions = new HashSet();
GitClient client = Git.getInstance().getClient(getRepositoryRoot()); // do not use progresssupport's client, that one logs into output
- for (Map.Entry update : remoteRepositoryUpdates.entrySet()) {
- String remoteRevisionId = update.getValue().getOldObjectId();
- String localRevisionId = update.getValue().getNewObjectId();
- revisionList.addAll(addRevisions(client, visitedRevisions, remoteRevisionId, localRevisionId));
- if (isCanceled()) {
- break;
+ try {
+ for (Map.Entry update : remoteRepositoryUpdates.entrySet()) {
+ String remoteRevisionId = update.getValue().getOldObjectId();
+ String localRevisionId = update.getValue().getNewObjectId();
+ revisionList.addAll(addRevisions(client, visitedRevisions, remoteRevisionId, localRevisionId));
+ if (isCanceled()) {
+ break;
+ }
+ }
+ } finally {
+ if (client != null) {
+ client.release();
}
}
return revisionList;
diff --git a/git/src/org/netbeans/modules/git/ui/repository/RepositoryBrowserPanel.java b/git/src/org/netbeans/modules/git/ui/repository/RepositoryBrowserPanel.java
--- a/git/src/org/netbeans/modules/git/ui/repository/RepositoryBrowserPanel.java
+++ b/git/src/org/netbeans/modules/git/ui/repository/RepositoryBrowserPanel.java
@@ -948,8 +948,9 @@
@Override
public void run () {
String tt = null;
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
GitRevisionInfo info = client.getCommonAncestor(new String[] { id, trackedBranch.getId() }, GitUtils.NULL_PROGRESS_MONITOR);
if (info == null || !(info.getRevision().equals(id) || info.getRevision().equals(trackedBranch.getId()))) {
tt = NbBundle.getMessage(RepositoryBrowserPanel.class, "MSG_BranchNode.tracking.mergeNeeded", trackedBranch.getName()); //NOI18N
@@ -976,6 +977,10 @@
}
} catch (GitException ex) {
LOG.log(Level.INFO, null, ex);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
final String toolTip = tt;
EventQueue.invokeLater(new Runnable() {
diff --git a/git/src/org/netbeans/modules/git/ui/repository/RepositoryInfo.java b/git/src/org/netbeans/modules/git/ui/repository/RepositoryInfo.java
--- a/git/src/org/netbeans/modules/git/ui/repository/RepositoryInfo.java
+++ b/git/src/org/netbeans/modules/git/ui/repository/RepositoryInfo.java
@@ -160,12 +160,13 @@
public void refresh () {
assert !java.awt.EventQueue.isDispatchThread();
File root = rootRef.get();
+ GitClient client = null;
try {
if (root == null) {
LOG.log(Level.WARNING, "refresh (): root is null, it has been collected in the meantime"); //NOI18N
} else {
LOG.log(Level.FINE, "refresh (): starting for {0}", root); //NOI18N
- GitClient client = Git.getInstance().getClient(root);
+ client = Git.getInstance().getClient(root);
// get all needed information at once before firing events. Thus we supress repeated annotations' refreshing
Map newBranches = client.getBranches(true, GitUtils.NULL_PROGRESS_MONITOR);
setBranches(newBranches);
@@ -180,6 +181,10 @@
} catch (GitException ex) {
Level level = root.exists() ? Level.INFO : Level.FINE; // do not polute the message log with messages concerning temporary or deleted repositories
LOG.log(level, null, ex);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
@@ -189,17 +194,22 @@
*/
public void refreshRemotes () {
assert !java.awt.EventQueue.isDispatchThread();
+ GitClient client = null;
try {
File root = rootRef.get();
if (root == null) {
LOG.log(Level.WARNING, "refreshRemotes (): root is null, it has been collected in the meantime"); //NOI18N
} else {
LOG.log(Level.FINE, "refreshRemotes (): starting for {0}", root); //NOI18N
- GitClient client = Git.getInstance().getClient(root);
+ client = Git.getInstance().getClient(root);
refreshRemotes(client);
}
} catch (GitException ex) {
LOG.log(Level.INFO, null, ex);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
diff --git a/git/src/org/netbeans/modules/git/ui/repository/RevisionInfoPanelController.java b/git/src/org/netbeans/modules/git/ui/repository/RevisionInfoPanelController.java
--- a/git/src/org/netbeans/modules/git/ui/repository/RevisionInfoPanelController.java
+++ b/git/src/org/netbeans/modules/git/ui/repository/RevisionInfoPanelController.java
@@ -159,18 +159,23 @@
public void run () {
final String revision = currentCommit;
GitRevisionInfo revisionInfo;
+ GitClient client = null;
try {
monitor = new ProgressMonitor.DefaultProgressMonitor();
if (Thread.interrupted()) {
return;
}
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
revisionInfo = client.log(revision, monitor);
} catch (GitException ex) {
if (!(ex instanceof GitException.MissingObjectException)) {
GitClientExceptionHandler.notifyException(ex, true);
}
revisionInfo = null;
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
final GitRevisionInfo info = revisionInfo;
final ProgressMonitor.DefaultProgressMonitor m = monitor;
diff --git a/git/src/org/netbeans/modules/git/ui/repository/remote/SelectUriStep.java b/git/src/org/netbeans/modules/git/ui/repository/remote/SelectUriStep.java
--- a/git/src/org/netbeans/modules/git/ui/repository/remote/SelectUriStep.java
+++ b/git/src/org/netbeans/modules/git/ui/repository/remote/SelectUriStep.java
@@ -240,8 +240,8 @@
@Override
protected void perform () {
String uri = getSelectedUri();
+ GitClient client = null;
try {
- GitClient client;
if (newRepositorySpecification) {
repository.store();
client = Git.getInstance().getClient(getRepositoryRoot(), this, false);
@@ -258,6 +258,10 @@
}
Logger.getLogger(SelectUriStep.class.getName()).log(Level.INFO, "Cannot connect to " + uri, ex); //NOI18N
message[0] = new Message(NbBundle.getMessage(SelectUriStep.class, "MSG_SelectUriStep.errorCannotConnect", uri), false); //NOI18N
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
diff --git a/git/src/org/netbeans/modules/git/ui/tag/CreateTag.java b/git/src/org/netbeans/modules/git/ui/tag/CreateTag.java
--- a/git/src/org/netbeans/modules/git/ui/tag/CreateTag.java
+++ b/git/src/org/netbeans/modules/git/ui/tag/CreateTag.java
@@ -196,8 +196,9 @@
@Override
public void run () {
final String tagName = CreateTag.this.branchName;
+ GitClient client = null;
try {
- GitClient client = Git.getInstance().getClient(repository);
+ client = Git.getInstance().getClient(repository);
final Map tags = client.getTags(GitUtils.NULL_PROGRESS_MONITOR, true);
EventQueue.invokeLater(new Runnable () {
@Override
@@ -214,6 +215,10 @@
});
} catch (GitException ex) {
GitClientExceptionHandler.notifyException(ex, true);
+ } finally {
+ if (client != null) {
+ client.release();
+ }
}
}
}
diff --git a/libs.git/apichanges.xml b/libs.git/apichanges.xml
--- a/libs.git/apichanges.xml
+++ b/libs.git/apichanges.xml
@@ -111,6 +111,23 @@
+ Adding new method to GitClient: release()
+
+
+
+
+
+ JGit repository normally does not close all opened file descriptors
+ and prevents external commands from working with the same repository.
+ Clients of the JGit API are supposed to call JGitRepository.close()
+ when they're done with the repository. We need to pass this method to the
+ Git Library API so when clients are done with calling all required commands
+ the library closes all descriptors and frees the git metadata files
+ for other tools.
+
+
+
+
Initial version of the API
diff --git a/libs.git/manifest.mf b/libs.git/manifest.mf
--- a/libs.git/manifest.mf
+++ b/libs.git/manifest.mf
@@ -1,5 +1,5 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.libs.git/1
OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/git/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.4
+OpenIDE-Module-Specification-Version: 1.5
diff --git a/libs.git/src/org/netbeans/libs/git/GitClient.java b/libs.git/src/org/netbeans/libs/git/GitClient.java
--- a/libs.git/src/org/netbeans/libs/git/GitClient.java
+++ b/libs.git/src/org/netbeans/libs/git/GitClient.java
@@ -202,10 +202,11 @@
private final Set listeners;
private JGitCredentialsProvider credentialsProvider;
- GitClient (JGitRepository gitRepository) {
+ GitClient (JGitRepository gitRepository) throws GitException {
this.gitRepository = gitRepository;
listeners = new HashSet();
delegateListener = new DelegateListener();
+ gitRepository.increaseClientUsage();
}
/**
@@ -755,6 +756,17 @@
cmd.execute();
return cmd.getResult();
}
+
+ /**
+ * Marks this client as released and notifies the repository it does not
+ * have to stay open for this client. When all repository's clients are
+ * released the repository closes, flushes all cached metadata and closes
+ * all opened metadata files and file descriptors.
+ * @since 1.5
+ */
+ public void release () {
+ gitRepository.decreaseClientUsage();
+ }
/**
* Removes given files/folders from the index and/or from the working tree
diff --git a/libs.git/src/org/netbeans/libs/git/GitRepository.java b/libs.git/src/org/netbeans/libs/git/GitRepository.java
--- a/libs.git/src/org/netbeans/libs/git/GitRepository.java
+++ b/libs.git/src/org/netbeans/libs/git/GitRepository.java
@@ -55,14 +55,20 @@
* still have to provide a local file which indicates where the repository would be created when
* {@link GitClient#init(org.netbeans.libs.git.progress.ProgressMonitor) } was called.
* To get an instance of GitClient
to run git commands with, use {@link #createClient() } method. It always returns
- * a new instance of the GitClient
, it is not shared among the callers.
+ * a new instance of the GitClient
, it is not shared among the callers.
+ *
When done with the client - you finish calling all desired commands and do not
+ * plan to use the client's instance any more - {@link GitClient#release() } must be called.
+ * When all created clients are released this way, repository metadata are flushed,
+ * all open metadata files are closed and thus do not block any external tools
+ * (such as a commandline client).
* Internally the class keeps a map of its instances that are cached under
* a weak reference to the instance of the local file passed in the {@link #getInstance(java.io.File) } method.
* Along with the instance it caches also all repository metadata (branches, index, references etc.)
* needed to construct the client and operate with the actual Git repository.
* Every call to the getInstance
method with the same instance of the file
* will always return the same instance of GitRepository
. It is up to a caller's
- * responsibility to hold a strong reference to the file so all metadata are kept in memory and not garbage collected.
+ * responsibility to hold a strong reference to the file so a created client always works with
+ * the same instance of the git repository.
*
* @author Ondra Vrabec
*/
@@ -119,7 +125,7 @@
}
}
- private GitClient createClient (JGitRepository repository) {
+ private GitClient createClient (JGitRepository repository) throws GitException {
return new GitClient(repository);
}
diff --git a/libs.git/src/org/netbeans/libs/git/jgit/JGitRepository.java b/libs.git/src/org/netbeans/libs/git/jgit/JGitRepository.java
--- a/libs.git/src/org/netbeans/libs/git/jgit/JGitRepository.java
+++ b/libs.git/src/org/netbeans/libs/git/jgit/JGitRepository.java
@@ -53,11 +53,23 @@
* @author ondra
*/
public final class JGitRepository {
- private final Repository repository;
- private boolean closed;
+ private Repository repository;
+ private final File location;
- public JGitRepository (File location) throws GitException {
- this.repository = getRepository(location);
+ public JGitRepository (File location) {
+ this.location = location;
+ }
+
+ public synchronized void increaseClientUsage () throws GitException {
+ if (repository == null) {
+ repository = getRepository(location);
+ } else {
+ repository.incrementOpen();
+ }
+ }
+
+ public synchronized void decreaseClientUsage () {
+ repository.close();
}
private Repository getRepository (File workDir) throws GitException {
@@ -75,21 +87,7 @@
}
public Repository getRepository () {
+ assert repository != null;
return repository;
}
-
- public synchronized void close () {
- if (repository != null) {
- if (!closed) {
- repository.close();
- closed = true;
- }
- }
- }
-
- @Override
- protected void finalize () throws Throwable {
- close();
- super.finalize();
- }
}
diff --git a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/AbstractGitTestCase.java b/libs.git/test/unit/src/org/netbeans/libs/git/jgit/AbstractGitTestCase.java
--- a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/AbstractGitTestCase.java
+++ b/libs.git/test/unit/src/org/netbeans/libs/git/jgit/AbstractGitTestCase.java
@@ -180,10 +180,12 @@
if (createLocalClone()) {
GitRepository fact = GitRepository.getInstance(wc);
- fact.createClient().init(NULL_PROGRESS_MONITOR);
+ GitClient client = fact.createClient();
+ client.init(NULL_PROGRESS_MONITOR);
Field f = GitRepository.class.getDeclaredField("gitRepository");
f.setAccessible(true);
localRepository = (JGitRepository) f.get(fact);
+ client.release();
}
}
diff --git a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/factory/CreateClientTest.java b/libs.git/test/unit/src/org/netbeans/libs/git/jgit/factory/CreateClientTest.java
--- a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/factory/CreateClientTest.java
+++ b/libs.git/test/unit/src/org/netbeans/libs/git/jgit/factory/CreateClientTest.java
@@ -43,7 +43,9 @@
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
@@ -126,4 +128,39 @@
assertStatus(statuses, subRepo, newFile, true, GitStatus.Status.STATUS_ADDED, GitStatus.Status.STATUS_NORMAL, GitStatus.Status.STATUS_ADDED, false);
}
+ public void testClientRelease () throws Exception {
+ GitClient client1 = GitRepository.getInstance(workDir).createClient();
+ Repository jgitRepo1 = getRepository(client1);
+ assertRepoClients(jgitRepo1, 1);
+ client1.release();
+ assertRepoClients(jgitRepo1, 0);
+
+ client1 = GitRepository.getInstance(workDir).createClient();
+ assertEquals(jgitRepo1, getRepository(client1));
+ assertRepoClients(jgitRepo1, 1);
+ // some commands
+ client1.getStatus(new File[] { workDir }, NULL_PROGRESS_MONITOR);
+ client1.add(new File[] { workDir }, NULL_PROGRESS_MONITOR);
+
+ GitClient client2 = GitRepository.getInstance(workDir).createClient();
+ assertEquals(jgitRepo1, getRepository(client2));
+ assertRepoClients(jgitRepo1, 2);
+ // some commands
+ client2.getStatus(new File[] { workDir }, NULL_PROGRESS_MONITOR);
+ client2.add(new File[] { workDir }, NULL_PROGRESS_MONITOR);
+
+ assertRepoClients(jgitRepo1, 2);
+ client1.release();
+ assertRepoClients(jgitRepo1, 1);
+ client2.release();
+ assertRepoClients(jgitRepo1, 0);
+ }
+
+ private void assertRepoClients (Repository jgitRepo1, int expectedClients) throws Exception {
+ Field f = Repository.class.getDeclaredField("useCnt");
+ f.setAccessible(true);
+ AtomicInteger cnt = (AtomicInteger) f.get(jgitRepo1);
+ assertEquals(expectedClients, cnt.intValue());
+ }
+
}