This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 221498
Collapse All | Expand All

(-)a/git/nbproject/project.xml (-1 / +1 lines)
Lines 20-26 Link Here
20
                    <compile-dependency/>
20
                    <compile-dependency/>
21
                    <run-dependency>
21
                    <run-dependency>
22
                        <release-version>1</release-version>
22
                        <release-version>1</release-version>
23
                        <specification-version>1.0</specification-version>
23
                        <specification-version>1.5</specification-version>
24
                    </run-dependency>
24
                    </run-dependency>
25
                </dependency>
25
                </dependency>
26
                <dependency>
26
                <dependency>
(-)a/git/src/org/netbeans/modules/git/FileStatusCache.java (-1 / +7 lines)
Lines 65-70 Link Here
65
import org.netbeans.modules.versioning.spi.VCSContext;
65
import org.netbeans.modules.versioning.spi.VCSContext;
66
import org.netbeans.modules.versioning.spi.VersioningSupport;
66
import org.netbeans.modules.versioning.spi.VersioningSupport;
67
import org.netbeans.modules.git.FileInformation.Status;
67
import org.netbeans.modules.git.FileInformation.Status;
68
import org.netbeans.modules.git.client.GitClient;
68
import org.netbeans.modules.git.utils.GitUtils;
69
import org.netbeans.modules.git.utils.GitUtils;
69
import org.openide.filesystems.FileUtil;
70
import org.openide.filesystems.FileUtil;
70
import org.openide.util.RequestProcessor;
71
import org.openide.util.RequestProcessor;
Lines 205-213 Link Here
205
                    LOG.log(Level.FINE, "refreshAllRoots() roots: {0}, repositoryRoot: {1} ", new Object[] {refreshEntry.getValue(), repository.getAbsolutePath()}); // NOI18N
206
                    LOG.log(Level.FINE, "refreshAllRoots() roots: {0}, repositoryRoot: {1} ", new Object[] {refreshEntry.getValue(), repository.getAbsolutePath()}); // NOI18N
206
                }
207
                }
207
                Map<File, GitStatus> interestingFiles;
208
                Map<File, GitStatus> interestingFiles;
209
                GitClient client = null;
208
                try {
210
                try {
209
                    // find all files with not up-to-date or ignored status
211
                    // find all files with not up-to-date or ignored status
210
                    interestingFiles = Git.getInstance().getClient(repository).getStatus(refreshEntry.getValue().toArray(new File[refreshEntry.getValue().size()]), pm);
212
                    client = Git.getInstance().getClient(repository);
213
                    interestingFiles = client.getStatus(refreshEntry.getValue().toArray(new File[refreshEntry.getValue().size()]), pm);
211
                    if (pm.isCanceled()) {
214
                    if (pm.isCanceled()) {
212
                        return;
215
                        return;
213
                    }
216
                    }
Lines 258-263 Link Here
258
                } catch (GitException ex) {
261
                } catch (GitException ex) {
259
                    LOG.log(Level.INFO, "refreshAllRoots() file: {0} {1} {2} ", new Object[] {repository.getAbsolutePath(), refreshEntry.getValue(), ex.toString()}); //NOI18N
262
                    LOG.log(Level.INFO, "refreshAllRoots() file: {0} {1} {2} ", new Object[] {repository.getAbsolutePath(), refreshEntry.getValue(), ex.toString()}); //NOI18N
260
                } finally {
263
                } finally {
264
                    if (client != null) {
265
                        client.release();
266
                    }
261
                    if (LOG.isLoggable(Level.FINE)) {
267
                    if (LOG.isLoggable(Level.FINE)) {
262
                        LOG.log(Level.FINE, "refreshAllRoots() roots: finished repositoryRoot: {0} ", new Object[] { repository.getAbsolutePath() } ); // NOI18N
268
                        LOG.log(Level.FINE, "refreshAllRoots() roots: finished repositoryRoot: {0} ", new Object[] { repository.getAbsolutePath() } ); // NOI18N
263
                    }
269
                    }
(-)a/git/src/org/netbeans/modules/git/FilesystemInterceptor.java (-6 / +32 lines)
Lines 61-66 Link Here
61
import org.netbeans.libs.git.GitException;
61
import org.netbeans.libs.git.GitException;
62
import org.netbeans.libs.git.GitRemoteConfig;
62
import org.netbeans.libs.git.GitRemoteConfig;
63
import org.netbeans.modules.git.FileInformation.Status;
63
import org.netbeans.modules.git.FileInformation.Status;
64
import org.netbeans.modules.git.client.GitClient;
64
import org.netbeans.modules.git.ui.history.SearchHistoryAction;
65
import org.netbeans.modules.git.ui.history.SearchHistoryAction;
65
import org.netbeans.modules.git.ui.repository.RepositoryInfo;
66
import org.netbeans.modules.git.ui.repository.RepositoryInfo;
66
import org.netbeans.modules.git.utils.GitUtils;
67
import org.netbeans.modules.git.utils.GitUtils;
Lines 122-131 Link Here
122
            Git git = Git.getInstance();
123
            Git git = Git.getInstance();
123
            final File root = git.getRepositoryRoot(file);
124
            final File root = git.getRepositoryRoot(file);
124
            if (root == null) return false;
125
            if (root == null) return false;
126
            GitClient client = null;
125
            try {
127
            try {
126
                git.getClient(root).reset(new File[] { file }, "HEAD", true, GitUtils.NULL_PROGRESS_MONITOR);
128
                client = git.getClient(root);
129
                client.reset(new File[] { file }, "HEAD", true, GitUtils.NULL_PROGRESS_MONITOR);
127
            } catch (GitException ex) {
130
            } catch (GitException ex) {
128
                LOG.log(Level.INFO, "beforeCreate(): File: {0} {1}", new Object[] { file.getAbsolutePath(), ex.toString()}); //NOI18N
131
                LOG.log(Level.INFO, "beforeCreate(): File: {0} {1}", new Object[] { file.getAbsolutePath(), ex.toString()}); //NOI18N
132
            } finally {
133
                if (client != null) {
134
                    client.release();
135
                }
129
            }
136
            }
130
            LOG.log(Level.FINER, "beforeCreate(): finished: {0}", file); // NOI18N
137
            LOG.log(Level.FINER, "beforeCreate(): finished: {0}", file); // NOI18N
131
        }
138
        }
Lines 158-166 Link Here
158
        if (file == null) return;
165
        if (file == null) return;
159
        Git git = Git.getInstance();
166
        Git git = Git.getInstance();
160
        File root = git.getRepositoryRoot(file);
167
        File root = git.getRepositoryRoot(file);
168
        GitClient client = null;
161
        try {
169
        try {
162
            if (GitUtils.getGitFolderForRoot(root).exists()) {
170
            if (GitUtils.getGitFolderForRoot(root).exists()) {
163
                git.getClient(root).remove(new File[] { file }, false, GitUtils.NULL_PROGRESS_MONITOR);
171
                client = git.getClient(root);
172
                client.remove(new File[] { file }, false, GitUtils.NULL_PROGRESS_MONITOR);
164
            } else if (file.exists()) {
173
            } else if (file.exists()) {
165
                Utils.deleteRecursively(file);
174
                Utils.deleteRecursively(file);
166
                if (file.exists()) {
175
                if (file.exists()) {
Lines 178-183 Link Here
178
            Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_DeleteFailed", new Object[] { file, e.getLocalizedMessage() })); //NOI18N
187
            Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_DeleteFailed", new Object[] { file, e.getLocalizedMessage() })); //NOI18N
179
            ex.initCause(e);
188
            ex.initCause(e);
180
            throw ex;
189
            throw ex;
190
        } finally {
191
            if (client != null) {
192
                client.release();
193
            }
181
        }
194
        }
182
    }
195
    }
183
196
Lines 207-223 Link Here
207
        Git git = Git.getInstance();
220
        Git git = Git.getInstance();
208
        File root = git.getRepositoryRoot(from);
221
        File root = git.getRepositoryRoot(from);
209
        File dstRoot = git.getRepositoryRoot(to);
222
        File dstRoot = git.getRepositoryRoot(to);
223
        GitClient client = null;
210
        try {
224
        try {
211
            if (root != null && root.equals(dstRoot) && !cache.getStatus(to).containsStatus(Status.NOTVERSIONED_EXCLUDED)) {
225
            if (root != null && root.equals(dstRoot) && !cache.getStatus(to).containsStatus(Status.NOTVERSIONED_EXCLUDED)) {
212
                // target does not lie under ignored folder and is in the same repo as src
226
                // target does not lie under ignored folder and is in the same repo as src
227
                client = git.getClient(root);
213
                if (equalPathsIgnoreCase(from, to)) {
228
                if (equalPathsIgnoreCase(from, to)) {
214
                    // must do rename --after because the files/paths equal on Win or Mac
229
                    // must do rename --after because the files/paths equal on Win or Mac
215
                    if (!from.renameTo(to)) {
230
                    if (!from.renameTo(to)) {
216
                        throw new IOException(NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, "" })); //NOI18N
231
                        throw new IOException(NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, "" })); //NOI18N
217
                    }
232
                    }
218
                    git.getClient(root).rename(from, to, true, GitUtils.NULL_PROGRESS_MONITOR);
233
                    client.rename(from, to, true, GitUtils.NULL_PROGRESS_MONITOR);
219
                } else {
234
                } else {
220
                    git.getClient(root).rename(from, to, false, GitUtils.NULL_PROGRESS_MONITOR);
235
                    client.rename(from, to, false, GitUtils.NULL_PROGRESS_MONITOR);
221
                }
236
                }
222
            } else {
237
            } else {
223
                boolean result = from.renameTo(to);
238
                boolean result = from.renameTo(to);
Lines 225-231 Link Here
225
                    throw new IOException(NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, "" })); //NOI18N
240
                    throw new IOException(NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, "" })); //NOI18N
226
                }
241
                }
227
                if (root != null) {
242
                if (root != null) {
228
                    git.getClient(root).remove(new File[] { from }, true, GitUtils.NULL_PROGRESS_MONITOR);
243
                    client = git.getClient(root);
244
                    client.remove(new File[] { from }, true, GitUtils.NULL_PROGRESS_MONITOR);
229
                }
245
                }
230
            }
246
            }
231
        } catch (GitException e) {
247
        } catch (GitException e) {
Lines 233-238 Link Here
233
            Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, e.getLocalizedMessage() })); //NOI18N
249
            Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_MoveFailed", new Object[] { from, to, e.getLocalizedMessage() })); //NOI18N
234
            ex.initCause(e);
250
            ex.initCause(e);
235
            throw ex;
251
            throw ex;
252
        } finally {
253
            if (client != null) {
254
                client.release();
255
            }
236
        }
256
        }
237
    }
257
    }
238
258
Lines 283-297 Link Here
283
            // target lies under ignored folder, do not add it
303
            // target lies under ignored folder, do not add it
284
            return;
304
            return;
285
        }
305
        }
306
        GitClient client = null;
286
        try {
307
        try {
287
            if (root.equals(dstRoot)) {
308
            if (root.equals(dstRoot)) {
288
                git.getClient(root).copyAfter(from, to, GitUtils.NULL_PROGRESS_MONITOR);
309
                client = git.getClient(root);
310
                client.copyAfter(from, to, GitUtils.NULL_PROGRESS_MONITOR);
289
            }
311
            }
290
        } catch (GitException e) {
312
        } catch (GitException e) {
291
            IOException ex = new IOException();
313
            IOException ex = new IOException();
292
            Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_CopyFailed", new Object[] { from, to, e.getLocalizedMessage() })); //NOI18N
314
            Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(FilesystemInterceptor.class, "MSG_CopyFailed", new Object[] { from, to, e.getLocalizedMessage() })); //NOI18N
293
            ex.initCause(e);
315
            ex.initCause(e);
294
            throw ex;
316
            throw ex;
317
        } finally {
318
            if (client != null) {
319
                client.release();
320
            }
295
        }
321
        }
296
    }
322
    }
297
323
(-)a/git/src/org/netbeans/modules/git/Git.java (-1 / +6 lines)
Lines 145-152 Link Here
145
    void getOriginalFile (File workingCopy, File originalFile) {
145
    void getOriginalFile (File workingCopy, File originalFile) {
146
        File repository = getRepositoryRoot(workingCopy);
146
        File repository = getRepositoryRoot(workingCopy);
147
        if (repository != null) {
147
        if (repository != null) {
148
            GitClient client = null;
148
            try {
149
            try {
149
                GitClient client = getClient(repository);
150
                client = getClient(repository);
150
                FileOutputStream fos = new FileOutputStream(originalFile);
151
                FileOutputStream fos = new FileOutputStream(originalFile);
151
                boolean ok;                
152
                boolean ok;                
152
                try {
153
                try {
Lines 168-173 Link Here
168
                originalFile.delete();
169
                originalFile.delete();
169
            } catch (IOException ex) {
170
            } catch (IOException ex) {
170
                LOG.log(Level.INFO, "IO exception", ex); //NOI18N
171
                LOG.log(Level.INFO, "IO exception", ex); //NOI18N
172
            } finally {
173
                if (client != null) {
174
                    client.release();
175
                }
171
            }
176
            }
172
        }
177
        }
173
    }
178
    }
(-)a/git/src/org/netbeans/modules/git/HistoryProvider.java (-1 / +6 lines)
Lines 333-340 Link Here
333
            HistoryEntry ancestorEntry = commonAncestors.get(file);
333
            HistoryEntry ancestorEntry = commonAncestors.get(file);
334
            if (ancestorEntry == null && !commonAncestors.containsKey(file)) {
334
            if (ancestorEntry == null && !commonAncestors.containsKey(file)) {
335
                GitRevisionInfo parent = null;
335
                GitRevisionInfo parent = null;
336
                GitClient client = null;
336
                try {
337
                try {
337
                    GitClient client = Git.getInstance().getClient(repository);
338
                    client = Git.getInstance().getClient(repository);
338
                    if (info.getParents().length == 1) {
339
                    if (info.getParents().length == 1) {
339
                        File historyFile = info.getModifiedFiles().containsKey(file)
340
                        File historyFile = info.getModifiedFiles().containsKey(file)
340
                                ? file
341
                                ? file
Lines 347-352 Link Here
347
                    }
348
                    }
348
                } catch (GitException ex) {
349
                } catch (GitException ex) {
349
                    LOG.log(Level.INFO, null, ex);
350
                    LOG.log(Level.INFO, null, ex);
351
                } finally {
352
                    if (client != null) {
353
                        client.release();
354
                    }
350
                }
355
                }
351
                ancestorEntry = parent == null ? null : createHistoryEntry(parent, files, repository);
356
                ancestorEntry = parent == null ? null : createHistoryEntry(parent, files, repository);
352
                commonAncestors.put(file, ancestorEntry);
357
                commonAncestors.put(file, ancestorEntry);
(-)a/git/src/org/netbeans/modules/git/HistoryRegistry.java (-6 / +17 lines)
Lines 85-97 Link Here
85
        crit.setFiles(files);
85
        crit.setFiles(files);
86
        crit.setFollowRenames(true);
86
        crit.setFollowRenames(true);
87
        crit.setIncludeMerges(false);
87
        crit.setIncludeMerges(false);
88
        GitRevisionInfo[] history = client.log(crit, pm);
88
        try {
89
        if (!pm.isCanceled() && history.length > 0) {
89
            GitRevisionInfo[] history = client.log(crit, pm);
90
            for (File f : files) {
90
            if (!pm.isCanceled() && history.length > 0) {
91
                logs.put(f, Arrays.asList(history));
91
                for (File f : files) {
92
                    logs.put(f, Arrays.asList(history));
93
                }
94
            }
95
            return history;
96
        } finally {
97
            if (client != null) {
98
                client.release();
92
            }
99
            }
93
        }
100
        }
94
        return history;
95
    }
101
    }
96
    
102
    
97
    public File getHistoryFile(final File repository, final File originalFile, final String revision, final boolean dryTry) {
103
    public File getHistoryFile(final File repository, final File originalFile, final String revision, final boolean dryTry) {
Lines 150-162 Link Here
150
            if(changePaths == null && !dryTry) {
156
            if(changePaths == null && !dryTry) {
151
                long t1 = System.currentTimeMillis();
157
                long t1 = System.currentTimeMillis();
152
                Map<File, GitFileInfo> cps = null;
158
                Map<File, GitFileInfo> cps = null;
159
                GitClient client = null;
153
                try {
160
                try {
154
                    GitClient client = Git.getInstance().getClient(repository);
161
                    client = Git.getInstance().getClient(repository);
155
                    GitRevisionInfo lms = client.log(historyRevision, pm);
162
                    GitRevisionInfo lms = client.log(historyRevision, pm);
156
                    assert lms != null;
163
                    assert lms != null;
157
                    cps = lms.getModifiedFiles();
164
                    cps = lms.getModifiedFiles();
158
                } catch (GitException ex) {
165
                } catch (GitException ex) {
159
                    LOG.log(Level.INFO, null, ex);
166
                    LOG.log(Level.INFO, null, ex);
167
                } finally {
168
                    if (client != null) {
169
                        client.release();
170
                    }
160
                }
171
                }
161
                if (cps == null) {
172
                if (cps == null) {
162
                    changePaths = Collections.<GitFileInfo>emptyList();
173
                    changePaths = Collections.<GitFileInfo>emptyList();
(-)a/git/src/org/netbeans/modules/git/VersionsCache.java (-1 / +6 lines)
Lines 84-91 Link Here
84
        } else {
84
        } else {
85
            File tempFile = new File(Utils.getTempFolder(), "nb-git-" + base.getName()); //NOI18N
85
            File tempFile = new File(Utils.getTempFolder(), "nb-git-" + base.getName()); //NOI18N
86
            tempFile.deleteOnExit();
86
            tempFile.deleteOnExit();
87
            GitClient client = null;
87
            try {
88
            try {
88
                GitClient client = Git.getInstance().getClient(repository);
89
                client = Git.getInstance().getClient(repository);
89
                boolean result;
90
                boolean result;
90
                FileOutputStream fos = new FileOutputStream(tempFile);
91
                FileOutputStream fos = new FileOutputStream(tempFile);
91
                try {
92
                try {
Lines 109-114 Link Here
109
                tempFile = null;
110
                tempFile = null;
110
            } catch (GitException ex) {
111
            } catch (GitException ex) {
111
                throw new IOException(ex);
112
                throw new IOException(ex);
113
            } finally {
114
                if (client != null) {
115
                    client.release();
116
                }
112
            }
117
            }
113
            return tempFile;
118
            return tempFile;
114
        }
119
        }
(-)a/git/src/org/netbeans/modules/git/api/Git.java (-1 / +5 lines)
Lines 95-102 Link Here
95
    }
95
    }
96
96
97
    public static void initializeRepository (File localFolder, String repositoryUrl, PasswordAuthentication credentials) throws IOException, URISyntaxException {
97
    public static void initializeRepository (File localFolder, String repositoryUrl, PasswordAuthentication credentials) throws IOException, URISyntaxException {
98
        GitClient client = null;
98
        try {
99
        try {
99
            GitClient client = org.netbeans.modules.git.Git.getInstance().getClient(localFolder);
100
            client = org.netbeans.modules.git.Git.getInstance().getClient(localFolder);
100
            client.init(GitUtils.NULL_PROGRESS_MONITOR);
101
            client.init(GitUtils.NULL_PROGRESS_MONITOR);
101
            String remoteName = "origin"; //NOI18N
102
            String remoteName = "origin"; //NOI18N
102
            client.setRemote(new GitRemoteConfig(remoteName, Arrays.asList(repositoryUrl),
103
            client.setRemote(new GitRemoteConfig(remoteName, Arrays.asList(repositoryUrl),
Lines 122-127 Link Here
122
        } catch (GitException ex) {
123
        } catch (GitException ex) {
123
            throw new IOException(ex);
124
            throw new IOException(ex);
124
        } finally {
125
        } finally {
126
            if (client != null) {
127
                client.release();
128
            }
125
            org.netbeans.modules.git.Git.getInstance().clearAncestorCaches();
129
            org.netbeans.modules.git.Git.getInstance().clearAncestorCaches();
126
            VersioningSupport.versionedRootsChanged();
130
            VersioningSupport.versionedRootsChanged();
127
        }
131
        }
(-)a/git/src/org/netbeans/modules/git/client/GitClient.java (+40 lines)
Lines 46-53 Link Here
46
import java.lang.reflect.InvocationTargetException;
46
import java.lang.reflect.InvocationTargetException;
47
import java.util.Arrays;
47
import java.util.Arrays;
48
import java.util.HashSet;
48
import java.util.HashSet;
49
import java.util.LinkedList;
49
import java.util.List;
50
import java.util.List;
50
import java.util.Map;
51
import java.util.Map;
52
import java.util.Set;
51
import java.util.concurrent.Callable;
53
import java.util.concurrent.Callable;
52
import java.util.logging.Level;
54
import java.util.logging.Level;
53
import java.util.logging.Logger;
55
import java.util.logging.Logger;
Lines 77-82 Link Here
77
import org.netbeans.modules.versioning.util.IndexingBridge;
79
import org.netbeans.modules.versioning.util.IndexingBridge;
78
import org.netbeans.modules.versioning.util.Utils;
80
import org.netbeans.modules.versioning.util.Utils;
79
import org.openide.util.NetworkSettings;
81
import org.openide.util.NetworkSettings;
82
import org.openide.util.RequestProcessor;
80
83
81
/**
84
/**
82
 *
85
 *
Lines 87-92 Link Here
87
    private final org.netbeans.libs.git.GitClient delegate;
90
    private final org.netbeans.libs.git.GitClient delegate;
88
    private final GitProgressSupport progressSupport;
91
    private final GitProgressSupport progressSupport;
89
    private final boolean handleAuthenticationIssues;
92
    private final boolean handleAuthenticationIssues;
93
    private static final int CLEANUP_TIME = 15000;
94
    private static final List<org.netbeans.libs.git.GitClient> unusedClients = new LinkedList<org.netbeans.libs.git.GitClient>();
95
    private static RequestProcessor.Task cleanTask = Git.getInstance().getRequestProcessor().create(new CleanTask());
90
    
96
    
91
    /**
97
    /**
92
     * Set of commands that do not need to run under repository lock
98
     * Set of commands that do not need to run under repository lock
Lines 194-199 Link Here
194
            ));
200
            ));
195
    private static final Logger LOG = Logger.getLogger(GitClient.class.getName());
201
    private static final Logger LOG = Logger.getLogger(GitClient.class.getName());
196
    private final File repositoryRoot;
202
    private final File repositoryRoot;
203
    private boolean released;
197
204
198
    public GitClient (File repository, GitProgressSupport progressSupport, boolean handleAuthenticationIssues) throws GitException {
205
    public GitClient (File repository, GitProgressSupport progressSupport, boolean handleAuthenticationIssues) throws GitException {
199
        this.repositoryRoot = repository;
206
        this.repositoryRoot = repository;
Lines 586-591 Link Here
586
            }
593
            }
587
        }, "push"); //NOI18N
594
        }, "push"); //NOI18N
588
    }
595
    }
596
    
597
    /**
598
     * Schedule cleanup of git repository used by this client
599
     */
600
    public void release () {
601
        synchronized (unusedClients) {
602
            if (released) {
603
                return;
604
            }
605
            unusedClients.add(delegate);
606
            released = true;
607
        }
608
        cleanTask.schedule(CLEANUP_TIME);
609
    }
589
610
590
    public void remove (final File[] roots, final boolean cached, final ProgressMonitor monitor) throws GitException {
611
    public void remove (final File[] roots, final boolean cached, final ProgressMonitor monitor) throws GitException {
591
        new CommandInvoker().runMethod(new Callable<Void>() {
612
        new CommandInvoker().runMethod(new Callable<Void>() {
Lines 681-690 Link Here
681
            }
702
            }
682
        }, "unignore"); //NOI18N
703
        }, "unignore"); //NOI18N
683
    }
704
    }
705
706
    private static class CleanTask implements Runnable {
707
708
        @Override
709
        public void run () {
710
            Set<org.netbeans.libs.git.GitClient> toRelease;
711
            synchronized (unusedClients) {
712
                toRelease = new HashSet<org.netbeans.libs.git.GitClient>(unusedClients);
713
                unusedClients.clear();
714
            }
715
            for (org.netbeans.libs.git.GitClient unusuedClient : toRelease) {
716
                unusuedClient.release();
717
            }
718
        }
719
        
720
    }
684
    
721
    
685
    private final class CommandInvoker {
722
    private final class CommandInvoker {
686
        
723
        
687
        private <T> T runMethod (Callable<T> callable, String methodName) throws GitException {
724
        private <T> T runMethod (Callable<T> callable, String methodName) throws GitException {
725
            if (released) {
726
                throw new IllegalStateException("Client already released.");
727
            }
688
            return runMethod(callable, methodName, new File[0]);
728
            return runMethod(callable, methodName, new File[0]);
689
        }
729
        }
690
730
(-)a/git/src/org/netbeans/modules/git/client/GitProgressSupport.java (+3 lines)
Lines 105-110 Link Here
105
            LOG.log(Level.FINE, "End - {0}", originalDisplayName); //NOI18N
105
            LOG.log(Level.FINE, "End - {0}", originalDisplayName); //NOI18N
106
            finishProgress();
106
            finishProgress();
107
            getLogger().closeLog();
107
            getLogger().closeLog();
108
            if (gitClient != null) {
109
                gitClient.release();
110
            }
108
        }
111
        }
109
    }
112
    }
110
113
(-)a/git/src/org/netbeans/modules/git/ui/actions/SingleRepositoryAction.java (-1 / +6 lines)
Lines 120-127 Link Here
120
                @Override
120
                @Override
121
                public void run () {
121
                public void run () {
122
                    Set<String> urls = new HashSet<String>();
122
                    Set<String> urls = new HashSet<String>();
123
                    GitClient client = null;
123
                    try {
124
                    try {
124
                        GitClient client = Git.getInstance().getClient(repositoryRoot);
125
                        client = Git.getInstance().getClient(repositoryRoot);
125
                        Map<String, GitRemoteConfig> cfgs = client.getRemotes(GitUtils.NULL_PROGRESS_MONITOR);
126
                        Map<String, GitRemoteConfig> cfgs = client.getRemotes(GitUtils.NULL_PROGRESS_MONITOR);
126
                        for (Map.Entry<String, GitRemoteConfig> e : cfgs.entrySet()) {
127
                        for (Map.Entry<String, GitRemoteConfig> e : cfgs.entrySet()) {
127
                            GitRemoteConfig cfg = e.getValue();
128
                            GitRemoteConfig cfg = e.getValue();
Lines 133-138 Link Here
133
                        }
134
                        }
134
                    } catch (GitException ex) {
135
                    } catch (GitException ex) {
135
                        // not interested
136
                        // not interested
137
                    } finally {
138
                        if (client != null) {
139
                            client.release();
140
                        }
136
                    }
141
                    }
137
                    for (String url : urls) {
142
                    for (String url : urls) {
138
                        if (!url.trim().isEmpty()) {
143
                        if (!url.trim().isEmpty()) {
(-)a/git/src/org/netbeans/modules/git/ui/branch/CreateBranch.java (-1 / +6 lines)
Lines 168-175 Link Here
168
        @Override
168
        @Override
169
        public void run () {
169
        public void run () {
170
            final String branchName = CreateBranch.this.branchName;
170
            final String branchName = CreateBranch.this.branchName;
171
            GitClient client = null;
171
            try {
172
            try {
172
                GitClient client = Git.getInstance().getClient(repository);
173
                client = Git.getInstance().getClient(repository);
173
                final Map<String, GitBranch> branches = client.getBranches(false, GitUtils.NULL_PROGRESS_MONITOR);
174
                final Map<String, GitBranch> branches = client.getBranches(false, GitUtils.NULL_PROGRESS_MONITOR);
174
                EventQueue.invokeLater(new Runnable () {
175
                EventQueue.invokeLater(new Runnable () {
175
                    @Override
176
                    @Override
Lines 182-187 Link Here
182
                });
183
                });
183
            } catch (GitException ex) {
184
            } catch (GitException ex) {
184
                GitClientExceptionHandler.notifyException(ex, true);
185
                GitClientExceptionHandler.notifyException(ex, true);
186
            } finally {
187
                if (client != null) {
188
                    client.release();
189
                }
185
            }
190
            }
186
        }
191
        }
187
    }
192
    }
(-)a/git/src/org/netbeans/modules/git/ui/clone/RepositoryStep.java (-1 / +5 lines)
Lines 189-196 Link Here
189
189
190
        @Override
190
        @Override
191
        public void perform() {
191
        public void perform() {
192
            GitClient client = null;
192
            try {
193
            try {
193
                GitClient client = Git.getInstance().getClient(getRepositoryRoot(), this, false);
194
                client = Git.getInstance().getClient(getRepositoryRoot(), this, false);
194
                client.init(getProgressMonitor());
195
                client.init(getProgressMonitor());
195
                branches = new HashMap<String, GitBranch>();
196
                branches = new HashMap<String, GitBranch>();
196
                branches.putAll(client.listRemoteBranches(uri.toPrivateString(), getProgressMonitor()));
197
                branches.putAll(client.listRemoteBranches(uri.toPrivateString(), getProgressMonitor()));
Lines 204-209 Link Here
204
                message = new Message(str, false);
205
                message = new Message(str, false);
205
                setValid(false, message);
206
                setValid(false, message);
206
            } finally {
207
            } finally {
208
                if (client != null) {
209
                    client.release();
210
                }
207
                Utils.deleteRecursively(getRepositoryRoot());
211
                Utils.deleteRecursively(getRepositoryRoot());
208
                if (message == null && isCanceled()) {
212
                if (message == null && isCanceled()) {
209
                    message = new Message(NbBundle.getMessage(RepositoryStep.class, "MSG_RepositoryStep.validationCanceled"), true); //NOI18N
213
                    message = new Message(NbBundle.getMessage(RepositoryStep.class, "MSG_RepositoryStep.validationCanceled"), true); //NOI18N
(-)a/git/src/org/netbeans/modules/git/ui/commit/CommitAction.java (-2 / +12 lines)
Lines 114-125 Link Here
114
            public void run() {
114
            public void run() {
115
                
115
                
116
                GitUser user = null;
116
                GitUser user = null;
117
                GitClient client = null;
117
                try {
118
                try {
118
                    GitClient client = Git.getInstance().getClient(repository);
119
                    client = Git.getInstance().getClient(repository);
119
                    user = client.getUser();
120
                    user = client.getUser();
120
                } catch (GitException ex) {
121
                } catch (GitException ex) {
121
                    GitClientExceptionHandler.notifyException(ex, true);
122
                    GitClientExceptionHandler.notifyException(ex, true);
122
                    return;
123
                    return;
124
                } finally {
125
                    if (client != null) {
126
                        client.release();
127
                    }
123
                }
128
                }
124
                
129
                
125
                GitCommitPanel panel = state == GitRepositoryState.MERGING_RESOLVED
130
                GitCommitPanel panel = state == GitRepositoryState.MERGING_RESOLVED
Lines 326-336 Link Here
326
            commitPermitted = false;
331
            commitPermitted = false;
327
            Map<File, GitStatus> conflicts = Collections.emptyMap();
332
            Map<File, GitStatus> conflicts = Collections.emptyMap();
328
            if (state.equals(GitRepositoryState.MERGING)) {
333
            if (state.equals(GitRepositoryState.MERGING)) {
334
                GitClient client = null;
329
                try {
335
                try {
330
                    GitClient client = Git.getInstance().getClient(repository);
336
                    client = Git.getInstance().getClient(repository);
331
                    conflicts = client.getConflicts(new File[] { repository }, GitUtils.NULL_PROGRESS_MONITOR);
337
                    conflicts = client.getConflicts(new File[] { repository }, GitUtils.NULL_PROGRESS_MONITOR);
332
                } catch (GitException ex) {
338
                } catch (GitException ex) {
333
                    LOG.log(Level.INFO, null, ex);
339
                    LOG.log(Level.INFO, null, ex);
340
                } finally {
341
                    if (client != null) {
342
                        client.release();
343
                    }
334
                }
344
                }
335
            }
345
            }
336
            NotifyDescriptor nd;
346
            NotifyDescriptor nd;
(-)a/git/src/org/netbeans/modules/git/ui/commit/DeleteLocalAction.java (-1 / +6 lines)
Lines 114-127 Link Here
114
                };
114
                };
115
                for (Map.Entry<File, Set<File>> e : sortedFiles.entrySet()) {
115
                for (Map.Entry<File, Set<File>> e : sortedFiles.entrySet()) {
116
                    File root = e.getKey();
116
                    File root = e.getKey();
117
                    GitClient client = null;
117
                    try {
118
                    try {
118
                        GitClient client = Git.getInstance().getClient(root);
119
                        client = Git.getInstance().getClient(root);
119
                        client.addNotificationListener(list);
120
                        client.addNotificationListener(list);
120
                        File[] roots = e.getValue().toArray(new File[e.getValue().size()]);
121
                        File[] roots = e.getValue().toArray(new File[e.getValue().size()]);
121
                        client.reset(roots, GitUtils.HEAD, false, getProgressMonitor());
122
                        client.reset(roots, GitUtils.HEAD, false, getProgressMonitor());
122
                        client.clean(roots, getProgressMonitor());
123
                        client.clean(roots, getProgressMonitor());
123
                    } catch (GitException ex) {
124
                    } catch (GitException ex) {
124
                        LOG.log(Level.INFO, null, ex);
125
                        LOG.log(Level.INFO, null, ex);
126
                    } finally {
127
                        if (client != null) {
128
                            client.release();
129
                        }
125
                    }
130
                    }
126
                }
131
                }
127
            }
132
            }
(-)a/git/src/org/netbeans/modules/git/ui/ignore/IgnoreAction.java (-1 / +6 lines)
Lines 174-185 Link Here
174
    private File[] filterFolders (File repository, File[] roots) {
174
    private File[] filterFolders (File repository, File[] roots) {
175
        List<File> unignoredFolders = new LinkedList<File>();
175
        List<File> unignoredFolders = new LinkedList<File>();
176
        Map<File, GitStatus> statuses;
176
        Map<File, GitStatus> statuses;
177
        GitClient client = null;
177
        try {
178
        try {
178
            GitClient client = Git.getInstance().getClient(repository);
179
            client = Git.getInstance().getClient(repository);
179
            statuses = client.getStatus(roots, GitUtils.NULL_PROGRESS_MONITOR);
180
            statuses = client.getStatus(roots, GitUtils.NULL_PROGRESS_MONITOR);
180
        } catch (GitException ex) {
181
        } catch (GitException ex) {
181
            LOG.log(Level.INFO, null, ex);
182
            LOG.log(Level.INFO, null, ex);
182
            statuses = Collections.<File, GitStatus>emptyMap();
183
            statuses = Collections.<File, GitStatus>emptyMap();
184
        } finally {
185
            if (client != null) {
186
                client.release();
187
            }
183
        }
188
        }
184
        for (File f : roots) {
189
        for (File f : roots) {
185
            GitStatus st = statuses.get(f);
190
            GitStatus st = statuses.get(f);
(-)a/git/src/org/netbeans/modules/git/ui/push/PushAction.java (-14 / +26 lines)
Lines 193-207 Link Here
193
                List<GitRevisionInfo> revisionList = new LinkedList<GitRevisionInfo>();
193
                List<GitRevisionInfo> revisionList = new LinkedList<GitRevisionInfo>();
194
                Set<String> visitedRevisions = new HashSet<String>();
194
                Set<String> visitedRevisions = new HashSet<String>();
195
                GitClient client = Git.getInstance().getClient(getRepositoryRoot()); // do not use progresssupport's client, that one logs into output
195
                GitClient client = Git.getInstance().getClient(getRepositoryRoot()); // do not use progresssupport's client, that one logs into output
196
                for (PushMapping mapping : pushMappings) {
196
                try {
197
                    if (mapping instanceof PushMapping.PushBranchMapping) {
197
                    for (PushMapping mapping : pushMappings) {
198
                        PushMapping.PushBranchMapping branchMapping = (PushMapping.PushBranchMapping) mapping;
198
                        if (mapping instanceof PushMapping.PushBranchMapping) {
199
                        String remoteRevisionId = branchMapping.getRemoteRepositoryBranchHeadId();
199
                            PushMapping.PushBranchMapping branchMapping = (PushMapping.PushBranchMapping) mapping;
200
                        String localRevisionId = branchMapping.getLocalRepositoryBranchHeadId();
200
                            String remoteRevisionId = branchMapping.getRemoteRepositoryBranchHeadId();
201
                        revisionList.addAll(addRevisions(client, visitedRevisions, remoteRevisionId, localRevisionId));
201
                            String localRevisionId = branchMapping.getLocalRepositoryBranchHeadId();
202
                            revisionList.addAll(addRevisions(client, visitedRevisions, remoteRevisionId, localRevisionId));
203
                        }
204
                        if (isCanceled()) {
205
                            break;
206
                        }
202
                    }
207
                    }
203
                    if (isCanceled()) {
208
                } finally {
204
                        break;
209
                    if (client != null) {
210
                        client.release();
205
                    }
211
                    }
206
                }
212
                }
207
                return revisionList;
213
                return revisionList;
Lines 211-222 Link Here
211
                List<GitRevisionInfo> revisionList = new LinkedList<GitRevisionInfo>();
217
                List<GitRevisionInfo> revisionList = new LinkedList<GitRevisionInfo>();
212
                Set<String> visitedRevisions = new HashSet<String>();
218
                Set<String> visitedRevisions = new HashSet<String>();
213
                GitClient client = Git.getInstance().getClient(getRepositoryRoot()); // do not use progresssupport's client, that one logs into output
219
                GitClient client = Git.getInstance().getClient(getRepositoryRoot()); // do not use progresssupport's client, that one logs into output
214
                for (Map.Entry<String, GitTransportUpdate> update : remoteRepositoryUpdates.entrySet()) {
220
                try {
215
                    String remoteRevisionId = update.getValue().getOldObjectId();
221
                    for (Map.Entry<String, GitTransportUpdate> update : remoteRepositoryUpdates.entrySet()) {
216
                    String localRevisionId = update.getValue().getNewObjectId();
222
                        String remoteRevisionId = update.getValue().getOldObjectId();
217
                    revisionList.addAll(addRevisions(client, visitedRevisions, remoteRevisionId, localRevisionId));
223
                        String localRevisionId = update.getValue().getNewObjectId();
218
                    if (isCanceled()) {
224
                        revisionList.addAll(addRevisions(client, visitedRevisions, remoteRevisionId, localRevisionId));
219
                        break;
225
                        if (isCanceled()) {
226
                            break;
227
                        }
228
                    }
229
                } finally {
230
                    if (client != null) {
231
                        client.release();
220
                    }
232
                    }
221
                }
233
                }
222
                return revisionList;
234
                return revisionList;
(-)a/git/src/org/netbeans/modules/git/ui/repository/RepositoryBrowserPanel.java (-1 / +6 lines)
Lines 948-955 Link Here
948
                        @Override
948
                        @Override
949
                        public void run () {
949
                        public void run () {
950
                            String tt = null;
950
                            String tt = null;
951
                            GitClient client = null;
951
                            try {
952
                            try {
952
                                GitClient client = Git.getInstance().getClient(repository);
953
                                client = Git.getInstance().getClient(repository);
953
                                GitRevisionInfo info = client.getCommonAncestor(new String[] { id, trackedBranch.getId() }, GitUtils.NULL_PROGRESS_MONITOR);
954
                                GitRevisionInfo info = client.getCommonAncestor(new String[] { id, trackedBranch.getId() }, GitUtils.NULL_PROGRESS_MONITOR);
954
                                if (info == null || !(info.getRevision().equals(id) || info.getRevision().equals(trackedBranch.getId()))) {
955
                                if (info == null || !(info.getRevision().equals(id) || info.getRevision().equals(trackedBranch.getId()))) {
955
                                    tt = NbBundle.getMessage(RepositoryBrowserPanel.class, "MSG_BranchNode.tracking.mergeNeeded", trackedBranch.getName()); //NOI18N
956
                                    tt = NbBundle.getMessage(RepositoryBrowserPanel.class, "MSG_BranchNode.tracking.mergeNeeded", trackedBranch.getName()); //NOI18N
Lines 976-981 Link Here
976
                                }
977
                                }
977
                            } catch (GitException ex) {
978
                            } catch (GitException ex) {
978
                                LOG.log(Level.INFO, null, ex);
979
                                LOG.log(Level.INFO, null, ex);
980
                            } finally {
981
                                if (client != null) {
982
                                    client.release();
983
                                }
979
                            }
984
                            }
980
                            final String toolTip = tt;
985
                            final String toolTip = tt;
981
                            EventQueue.invokeLater(new Runnable() {
986
                            EventQueue.invokeLater(new Runnable() {
(-)a/git/src/org/netbeans/modules/git/ui/repository/RepositoryInfo.java (-2 / +12 lines)
Lines 160-171 Link Here
160
    public void refresh () {
160
    public void refresh () {
161
        assert !java.awt.EventQueue.isDispatchThread();
161
        assert !java.awt.EventQueue.isDispatchThread();
162
        File root = rootRef.get();
162
        File root = rootRef.get();
163
        GitClient client = null;
163
        try {
164
        try {
164
            if (root == null) {
165
            if (root == null) {
165
                LOG.log(Level.WARNING, "refresh (): root is null, it has been collected in the meantime"); //NOI18N
166
                LOG.log(Level.WARNING, "refresh (): root is null, it has been collected in the meantime"); //NOI18N
166
            } else {
167
            } else {
167
                LOG.log(Level.FINE, "refresh (): starting for {0}", root); //NOI18N
168
                LOG.log(Level.FINE, "refresh (): starting for {0}", root); //NOI18N
168
                GitClient client = Git.getInstance().getClient(root);
169
                client = Git.getInstance().getClient(root);
169
                // get all needed information at once before firing events. Thus we supress repeated annotations' refreshing
170
                // get all needed information at once before firing events. Thus we supress repeated annotations' refreshing
170
                Map<String, GitBranch> newBranches = client.getBranches(true, GitUtils.NULL_PROGRESS_MONITOR);
171
                Map<String, GitBranch> newBranches = client.getBranches(true, GitUtils.NULL_PROGRESS_MONITOR);
171
                setBranches(newBranches);
172
                setBranches(newBranches);
Lines 180-185 Link Here
180
        } catch (GitException ex) {
181
        } catch (GitException ex) {
181
            Level level = root.exists() ? Level.INFO : Level.FINE; // do not polute the message log with messages concerning temporary or deleted repositories
182
            Level level = root.exists() ? Level.INFO : Level.FINE; // do not polute the message log with messages concerning temporary or deleted repositories
182
            LOG.log(level, null, ex);
183
            LOG.log(level, null, ex);
184
        } finally {
185
            if (client != null) {
186
                client.release();
187
            }
183
        }
188
        }
184
    }
189
    }
185
190
Lines 189-205 Link Here
189
     */
194
     */
190
    public void refreshRemotes () {
195
    public void refreshRemotes () {
191
        assert !java.awt.EventQueue.isDispatchThread();
196
        assert !java.awt.EventQueue.isDispatchThread();
197
        GitClient client = null;
192
        try {
198
        try {
193
            File root = rootRef.get();
199
            File root = rootRef.get();
194
            if (root == null) {
200
            if (root == null) {
195
                LOG.log(Level.WARNING, "refreshRemotes (): root is null, it has been collected in the meantime"); //NOI18N
201
                LOG.log(Level.WARNING, "refreshRemotes (): root is null, it has been collected in the meantime"); //NOI18N
196
            } else {
202
            } else {
197
                LOG.log(Level.FINE, "refreshRemotes (): starting for {0}", root); //NOI18N
203
                LOG.log(Level.FINE, "refreshRemotes (): starting for {0}", root); //NOI18N
198
                GitClient client = Git.getInstance().getClient(root);
204
                client = Git.getInstance().getClient(root);
199
                refreshRemotes(client);
205
                refreshRemotes(client);
200
            }
206
            }
201
        } catch (GitException ex) {
207
        } catch (GitException ex) {
202
            LOG.log(Level.INFO, null, ex);
208
            LOG.log(Level.INFO, null, ex);
209
        } finally {
210
            if (client != null) {
211
                client.release();
212
            }
203
        }
213
        }
204
    }
214
    }
205
    
215
    
(-)a/git/src/org/netbeans/modules/git/ui/repository/RevisionInfoPanelController.java (-1 / +6 lines)
Lines 159-176 Link Here
159
        public void run () {
159
        public void run () {
160
            final String revision = currentCommit;
160
            final String revision = currentCommit;
161
            GitRevisionInfo revisionInfo;
161
            GitRevisionInfo revisionInfo;
162
            GitClient client = null;
162
            try {
163
            try {
163
                monitor = new ProgressMonitor.DefaultProgressMonitor();
164
                monitor = new ProgressMonitor.DefaultProgressMonitor();
164
                if (Thread.interrupted()) {
165
                if (Thread.interrupted()) {
165
                    return;
166
                    return;
166
                }
167
                }
167
                GitClient client = Git.getInstance().getClient(repository);
168
                client = Git.getInstance().getClient(repository);
168
                revisionInfo = client.log(revision, monitor);
169
                revisionInfo = client.log(revision, monitor);
169
            } catch (GitException ex) {
170
            } catch (GitException ex) {
170
                if (!(ex instanceof GitException.MissingObjectException)) {
171
                if (!(ex instanceof GitException.MissingObjectException)) {
171
                    GitClientExceptionHandler.notifyException(ex, true);
172
                    GitClientExceptionHandler.notifyException(ex, true);
172
                }
173
                }
173
                revisionInfo = null;
174
                revisionInfo = null;
175
            } finally {
176
                if (client != null) {
177
                    client.release();
178
                }
174
            }
179
            }
175
            final GitRevisionInfo info = revisionInfo;
180
            final GitRevisionInfo info = revisionInfo;
176
            final ProgressMonitor.DefaultProgressMonitor m = monitor;
181
            final ProgressMonitor.DefaultProgressMonitor m = monitor;
(-)a/git/src/org/netbeans/modules/git/ui/repository/remote/SelectUriStep.java (-1 / +5 lines)
Lines 240-247 Link Here
240
                @Override
240
                @Override
241
                protected void perform () {
241
                protected void perform () {
242
                    String uri = getSelectedUri();
242
                    String uri = getSelectedUri();
243
                    GitClient client = null;
243
                    try {
244
                    try {
244
                        GitClient client;
245
                        if (newRepositorySpecification) {
245
                        if (newRepositorySpecification) {
246
                            repository.store();
246
                            repository.store();
247
                            client = Git.getInstance().getClient(getRepositoryRoot(), this, false);
247
                            client = Git.getInstance().getClient(getRepositoryRoot(), this, false);
Lines 258-263 Link Here
258
                        }
258
                        }
259
                        Logger.getLogger(SelectUriStep.class.getName()).log(Level.INFO, "Cannot connect to " + uri, ex); //NOI18N
259
                        Logger.getLogger(SelectUriStep.class.getName()).log(Level.INFO, "Cannot connect to " + uri, ex); //NOI18N
260
                        message[0] = new Message(NbBundle.getMessage(SelectUriStep.class, "MSG_SelectUriStep.errorCannotConnect", uri), false); //NOI18N
260
                        message[0] = new Message(NbBundle.getMessage(SelectUriStep.class, "MSG_SelectUriStep.errorCannotConnect", uri), false); //NOI18N
261
                    } finally {
262
                        if (client != null) {
263
                            client.release();
264
                        }
261
                    }
265
                    }
262
                }
266
                }
263
267
(-)a/git/src/org/netbeans/modules/git/ui/tag/CreateTag.java (-1 / +6 lines)
Lines 196-203 Link Here
196
        @Override
196
        @Override
197
        public void run () {
197
        public void run () {
198
            final String tagName = CreateTag.this.branchName;
198
            final String tagName = CreateTag.this.branchName;
199
            GitClient client = null;
199
            try {
200
            try {
200
                GitClient client = Git.getInstance().getClient(repository);
201
                client = Git.getInstance().getClient(repository);
201
                final Map<String, GitTag> tags = client.getTags(GitUtils.NULL_PROGRESS_MONITOR, true);
202
                final Map<String, GitTag> tags = client.getTags(GitUtils.NULL_PROGRESS_MONITOR, true);
202
                EventQueue.invokeLater(new Runnable () {
203
                EventQueue.invokeLater(new Runnable () {
203
                    @Override
204
                    @Override
Lines 214-219 Link Here
214
                });
215
                });
215
            } catch (GitException ex) {
216
            } catch (GitException ex) {
216
                GitClientExceptionHandler.notifyException(ex, true);
217
                GitClientExceptionHandler.notifyException(ex, true);
218
            } finally {
219
                if (client != null) {
220
                    client.release();
221
                }
217
            }
222
            }
218
        }
223
        }
219
    }
224
    }
(-)a/git/test/unit/src/org/netbeans/modules/git/GitClientTest.java (+5 lines)
Lines 136-141 Link Here
136
                "merge",
136
                "merge",
137
                "pull",
137
                "pull",
138
                "push",
138
                "push",
139
                "release",
139
                "remove",
140
                "remove",
140
                "removeNotificationListener",
141
                "removeNotificationListener",
141
                "removeRemote",
142
                "removeRemote",
Lines 220-225 Link Here
220
                "merge",
221
                "merge",
221
                "pull",
222
                "pull",
222
                "push",
223
                "push",
224
                "release",
223
                "remove",
225
                "remove",
224
                "removeNotificationListener",
226
                "removeNotificationListener",
225
                "removeRemote",
227
                "removeRemote",
Lines 327-332 Link Here
327
                "merge",
329
                "merge",
328
                "pull",
330
                "pull",
329
                "push",
331
                "push",
332
                "release",
330
                "remove",
333
                "remove",
331
                "removeNotificationListener",
334
                "removeNotificationListener",
332
                "removeRemote",
335
                "removeRemote",
Lines 417-422 Link Here
417
                "merge",
420
                "merge",
418
                "pull",
421
                "pull",
419
                "push",
422
                "push",
423
                "release",
420
                "remove",
424
                "remove",
421
                "removeNotificationListener",
425
                "removeNotificationListener",
422
                "removeRemote",
426
                "removeRemote",
Lines 543-548 Link Here
543
                "merge",
547
                "merge",
544
                "pull",
548
                "pull",
545
                "push",
549
                "push",
550
                "release",
546
                "remove",
551
                "remove",
547
                "removeNotificationListener",
552
                "removeNotificationListener",
548
                "removeRemote",
553
                "removeRemote",
(-)a/libs.git/apichanges.xml (+17 lines)
Lines 111-116 Link Here
111
        
111
        
112
        <change>
112
        <change>
113
            <api name="gitlibrary_api"/>
113
            <api name="gitlibrary_api"/>
114
            <summary>Adding new method to GitClient: release()</summary>
115
            <version major="1" minor="5"/>
116
            <date day="5" month="11" year="2012"/>
117
            <author login="ovrabec"/>
118
            <compatibility addition="yes"/>
119
            <description>
120
                JGit repository normally does not close all opened file descriptors
121
                and prevents external commands from working with the same repository.
122
                Clients of the JGit API are supposed to call JGitRepository.close()
123
                when they're done with the repository. We need to pass this method to the 
124
                Git Library API so when clients are done with calling all required commands
125
                the library closes all descriptors and frees the git metadata files
126
                for other tools.
127
            </description>
128
        </change>
129
        <change>
130
            <api name="gitlibrary_api"/>
114
            <summary>Initial version of the API</summary>
131
            <summary>Initial version of the API</summary>
115
            <version major="1" minor="0"/>
132
            <version major="1" minor="0"/>
116
            <date day="11" month="3" year="2012"/>
133
            <date day="11" month="3" year="2012"/>
(-)a/libs.git/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.libs.git/1
2
OpenIDE-Module: org.netbeans.libs.git/1
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/git/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/git/Bundle.properties
4
OpenIDE-Module-Specification-Version: 1.4
4
OpenIDE-Module-Specification-Version: 1.5
5
5
(-)a/libs.git/src/org/netbeans/libs/git/GitClient.java (-1 / +13 lines)
Lines 202-211 Link Here
202
    private final Set<NotificationListener> listeners;
202
    private final Set<NotificationListener> listeners;
203
    private JGitCredentialsProvider credentialsProvider;
203
    private JGitCredentialsProvider credentialsProvider;
204
204
205
    GitClient (JGitRepository gitRepository) {
205
    GitClient (JGitRepository gitRepository) throws GitException {
206
        this.gitRepository = gitRepository;
206
        this.gitRepository = gitRepository;
207
        listeners = new HashSet<NotificationListener>();
207
        listeners = new HashSet<NotificationListener>();
208
        delegateListener = new DelegateListener();
208
        delegateListener = new DelegateListener();
209
        gitRepository.increaseClientUsage();
209
    }
210
    }
210
211
211
    /**
212
    /**
Lines 755-760 Link Here
755
        cmd.execute();
756
        cmd.execute();
756
        return cmd.getResult();
757
        return cmd.getResult();
757
    }
758
    }
759
    
760
    /**
761
     * Marks this client as released and notifies the repository it does not 
762
     * have to stay open for this client. When all repository's clients are
763
     * released the repository closes, flushes all cached metadata and closes
764
     * all opened metadata files and file descriptors.
765
     * @since 1.5
766
     */
767
    public void release () {
768
        gitRepository.decreaseClientUsage();
769
    }
758
770
759
    /**
771
    /**
760
     * Removes given files/folders from the index and/or from the working tree
772
     * Removes given files/folders from the index and/or from the working tree
(-)a/libs.git/src/org/netbeans/libs/git/GitRepository.java (-3 / +9 lines)
Lines 55-68 Link Here
55
 * still have to provide a local file which indicates where the repository would be created when 
55
 * still have to provide a local file which indicates where the repository would be created when 
56
 * {@link GitClient#init(org.netbeans.libs.git.progress.ProgressMonitor) } was called.</p>
56
 * {@link GitClient#init(org.netbeans.libs.git.progress.ProgressMonitor) } was called.</p>
57
 * <p>To get an instance of <code>GitClient</code> to run git commands with, use {@link #createClient() } method. It <strong>always</strong> returns
57
 * <p>To get an instance of <code>GitClient</code> to run git commands with, use {@link #createClient() } method. It <strong>always</strong> returns
58
 * a new instance of the <code>GitClient</code>, it is not shared among the callers.</p>
58
 * a new instance of the <code>GitClient</code>, it is not shared among the callers.<p>
59
 * <p>When done with the client - you finish calling all desired commands and do not
60
 * plan to use the client's instance any more - {@link GitClient#release() } must be called.
61
 * When all created clients are released this way, repository metadata are flushed,
62
 * all open metadata files are closed and thus do not block any external tools 
63
 * (such as a commandline client).</p>
59
 * <p>Internally the class keeps a map of its instances that are cached under
64
 * <p>Internally the class keeps a map of its instances that are cached under
60
 * a weak reference to the instance of the local file passed in the {@link #getInstance(java.io.File) } method.
65
 * a weak reference to the instance of the local file passed in the {@link #getInstance(java.io.File) } method.
61
 * Along with the instance it caches also all repository metadata (branches, index, references etc.) 
66
 * Along with the instance it caches also all repository metadata (branches, index, references etc.) 
62
 * needed to construct the client and operate with the actual Git repository.<br/>
67
 * needed to construct the client and operate with the actual Git repository.<br/>
63
 * Every call to the <code>getInstance</code> method with the same instance of the file 
68
 * Every call to the <code>getInstance</code> method with the same instance of the file 
64
 * will always return the same instance of <code>GitRepository</code>. <strong>It is up to a caller's
69
 * will always return the same instance of <code>GitRepository</code>. <strong>It is up to a caller's
65
 * responsibility</strong> to hold a strong reference to the file so all metadata are kept in memory and not garbage collected.
70
 * responsibility</strong> to hold a strong reference to the file so a created client always works with 
71
 * the same instance of the git repository.</p>
66
 * 
72
 * 
67
 * @author Ondra Vrabec
73
 * @author Ondra Vrabec
68
 */
74
 */
Lines 119-125 Link Here
119
        }
125
        }
120
    }
126
    }
121
127
122
    private GitClient createClient (JGitRepository repository) {
128
    private GitClient createClient (JGitRepository repository) throws GitException {
123
        return new GitClient(repository);
129
        return new GitClient(repository);
124
    }
130
    }
125
131
(-)a/libs.git/src/org/netbeans/libs/git/jgit/JGitRepository.java (-19 / +17 lines)
Lines 53-63 Link Here
53
 * @author ondra
53
 * @author ondra
54
 */
54
 */
55
public final class JGitRepository {
55
public final class JGitRepository {
56
    private final Repository repository;
56
    private Repository repository;
57
    private boolean closed;
57
    private final File location;
58
58
59
    public JGitRepository (File location) throws GitException {
59
    public JGitRepository (File location) {
60
        this.repository = getRepository(location);
60
        this.location = location;
61
    }
62
63
    public synchronized void increaseClientUsage () throws GitException {
64
        if (repository == null) {
65
            repository = getRepository(location);
66
        } else {
67
            repository.incrementOpen();
68
        }
69
    }
70
71
    public synchronized void decreaseClientUsage () {
72
        repository.close();
61
    }
73
    }
62
74
63
    private Repository getRepository (File workDir) throws GitException {
75
    private Repository getRepository (File workDir) throws GitException {
Lines 75-95 Link Here
75
    }
87
    }
76
88
77
    public Repository getRepository () {
89
    public Repository getRepository () {
90
        assert repository != null;
78
        return repository;
91
        return repository;
79
    }
92
    }
80
    
81
    public synchronized void close () {
82
        if (repository != null) {
83
            if (!closed) {
84
                repository.close();
85
                closed = true;
86
            }
87
        }
88
    }
89
90
    @Override
91
    protected void finalize () throws Throwable {
92
        close();
93
        super.finalize();
94
    }
95
}
93
}
(-)a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/AbstractGitTestCase.java (-1 / +3 lines)
Lines 180-189 Link Here
180
180
181
        if (createLocalClone()) {
181
        if (createLocalClone()) {
182
            GitRepository fact = GitRepository.getInstance(wc);
182
            GitRepository fact = GitRepository.getInstance(wc);
183
            fact.createClient().init(NULL_PROGRESS_MONITOR);
183
            GitClient client = fact.createClient();
184
            client.init(NULL_PROGRESS_MONITOR);
184
            Field f = GitRepository.class.getDeclaredField("gitRepository");
185
            Field f = GitRepository.class.getDeclaredField("gitRepository");
185
            f.setAccessible(true);
186
            f.setAccessible(true);
186
            localRepository = (JGitRepository) f.get(fact);
187
            localRepository = (JGitRepository) f.get(fact);
188
            client.release();
187
        }
189
        }
188
    }
190
    }
189
191
(-)a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/factory/CreateClientTest.java (+37 lines)
Lines 43-49 Link Here
43
43
44
import java.io.File;
44
import java.io.File;
45
import java.io.IOException;
45
import java.io.IOException;
46
import java.lang.reflect.Field;
46
import java.util.Map;
47
import java.util.Map;
48
import java.util.concurrent.atomic.AtomicInteger;
47
import org.eclipse.jgit.lib.ConfigConstants;
49
import org.eclipse.jgit.lib.ConfigConstants;
48
import org.eclipse.jgit.lib.Repository;
50
import org.eclipse.jgit.lib.Repository;
49
import org.eclipse.jgit.lib.StoredConfig;
51
import org.eclipse.jgit.lib.StoredConfig;
Lines 126-129 Link Here
126
        assertStatus(statuses, subRepo, newFile, true, GitStatus.Status.STATUS_ADDED, GitStatus.Status.STATUS_NORMAL, GitStatus.Status.STATUS_ADDED, false);
128
        assertStatus(statuses, subRepo, newFile, true, GitStatus.Status.STATUS_ADDED, GitStatus.Status.STATUS_NORMAL, GitStatus.Status.STATUS_ADDED, false);
127
    }
129
    }
128
    
130
    
131
    public void testClientRelease () throws Exception {
132
        GitClient client1 = GitRepository.getInstance(workDir).createClient();
133
        Repository jgitRepo1 = getRepository(client1);
134
        assertRepoClients(jgitRepo1, 1);
135
        client1.release();
136
        assertRepoClients(jgitRepo1, 0);
137
        
138
        client1 = GitRepository.getInstance(workDir).createClient();
139
        assertEquals(jgitRepo1, getRepository(client1));
140
        assertRepoClients(jgitRepo1, 1);
141
        // some commands
142
        client1.getStatus(new File[] { workDir }, NULL_PROGRESS_MONITOR);
143
        client1.add(new File[] { workDir }, NULL_PROGRESS_MONITOR);
144
        
145
        GitClient client2 = GitRepository.getInstance(workDir).createClient();
146
        assertEquals(jgitRepo1, getRepository(client2));
147
        assertRepoClients(jgitRepo1, 2);
148
        // some commands
149
        client2.getStatus(new File[] { workDir }, NULL_PROGRESS_MONITOR);
150
        client2.add(new File[] { workDir }, NULL_PROGRESS_MONITOR);
151
        
152
        assertRepoClients(jgitRepo1, 2);        
153
        client1.release();
154
        assertRepoClients(jgitRepo1, 1);
155
        client2.release();
156
        assertRepoClients(jgitRepo1, 0);
157
    }
158
159
    private void assertRepoClients (Repository jgitRepo1, int expectedClients) throws Exception {
160
        Field f = Repository.class.getDeclaredField("useCnt");
161
        f.setAccessible(true);
162
        AtomicInteger cnt = (AtomicInteger) f.get(jgitRepo1);
163
        assertEquals(expectedClients, cnt.intValue());
164
    }
165
    
129
}
166
}

Return to bug 221498