2 * This file is part of the vng project
3 * Copyright (C) 2008-2009 Thomas Zander <tzander@trolltech.com>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "ChangeSet.h"
20 #include "../GitRunner.h"
21 #include "../Logger.h" // for debugging level only
22 #include "../AbstractCommand.h"
27 #include <QMutexLocker>
29 #include <QWaitCondition>
32 class HunksFetcher
: public QThread
35 HunksFetcher(const QList
<File
> &files
, ChangeSet
&changeSet
, bool changeSetOnIndex
)
37 m_changeSet(changeSet
),
38 m_changeSetOnIndex(changeSetOnIndex
),
45 setPriority(QThread::LowPriority
);
46 foreach(File file
, m_files
) {
49 m_changeSet
.lockFile(file
);
51 file
.fetchHunks(m_changeSetOnIndex
);
53 if (!file
.isBinary() && file
.count() == 0 && !file
.hasChanged()) { // No change in file at all.
54 Logger::debug() << "file: `" << QString::fromUtf8(file
.oldFileName()) << "' => `" << QString::fromUtf8(file
.fileName()) << "'\n";
55 Logger::debug() << " +- Unchanged file, skipping\n";
56 m_changeSet
.removeFile(file
);
58 QStringList arguments
;
59 arguments
<< "update-index" << "-q" << "--refresh" << file
.fileName();
60 GitRunner
runner(git
, arguments
);
61 runner
.start(GitRunner::WaitUntilFinished
);
64 if (file
.fileName().isEmpty() || file
.oldFileName().isEmpty())
65 file
.setProtectionAcceptance(Vng::Accepted
);
68 if (Logger::verbosity() >= Logger::Debug
) {
69 Logger::debug() << "file: `" << QString::fromUtf8(file
.oldFileName()) << "' => `" << QString::fromUtf8(file
.fileName()) << "'\n";
71 Logger::debug() << " +- is a binary file" << endl
;
72 Logger::debug() << " +- " << file
.oldProtection() << " => " << file
.protection() << endl
;
73 foreach(Hunk h
, file
.hunks()) {
74 Logger::debug() << " +-(" << i
++ << ") @" << h
.lineNumber() << "; " << h
.patch().size() << " bytes\n";
75 for(int i
= 0; i
< h
.subHunkCount(); i
++) {
76 Logger::debug() << " " << i
<<"/"<< h
.subHunkCount() <<"; "<< h
.subHunk(i
).size() <<" bytes\n";
81 m_changeSet
.lockFile(File());
82 m_changeSet
.allHunksFetched();
92 ChangeSet
&m_changeSet
;
93 bool m_changeSetOnIndex
, m_interrupted
;
96 class ChangeSet::Private
100 changeSetOnIndex(false),
103 finishedOneHunk(true)
109 hunksFetcher
->interrupt();
110 hunksFetcher
->wait();
116 bool changeSetOnIndex
; // the changesSet shows the changes of the working dir
117 QMutex fileAccessLock
;
118 QWaitCondition fileAccessWaiter
;
119 QWaitCondition cursorAccessWaiter
;
120 QMutex cursorAccessLock
;
122 HunksFetcher
*hunksFetcher
;
123 #if QT_VERSION >= 0x040400
128 bool finishedOneHunk
;
131 ChangeSet::ChangeSet()
136 ChangeSet::~ChangeSet()
138 #if QT_VERSION >= 0x040400
146 ChangeSet::ChangeSet(const ChangeSet
&other
)
149 #if QT_VERSION >= 0x040400
156 AbstractCommand::ReturnCodes
ChangeSet::fillFromDiffFile(QIODevice
&file
)
158 file
.open(QIODevice::ReadOnly
);
160 foreach (File f
, readGitDiff(file
))
163 if (Logger::verbosity() >= Logger::Debug
) {
164 foreach(File f
, d
->files
) {
165 Logger::debug() << "changes in file: " << f
.fileName() << endl
;
167 foreach(Hunk h
, f
.hunks()) {
168 Logger::debug() << " +-(" << i
++ << ") @" << h
.lineNumber() << "; " << h
.patch().size() << " bytes\n";
169 for(int i
= 0; i
< h
.subHunkCount(); i
++) {
170 Logger::debug() << " " << i
<<"/"<< h
.subHunkCount() <<"; "<< h
.subHunk(i
).size() <<" bytes\n";
174 Logger::debug().flush();
176 return AbstractCommand::Ok
;
179 AbstractCommand::ReturnCodes
ChangeSet::fillFromCurrentChanges(const QStringList
&paths
, bool doGenerateHunks
)
181 d
->changeSetOnIndex
= true;
182 QDir
refs(".git/refs/heads");
183 bool emptyRepo
= refs
.count() == 2; // only '.' and '..'
185 QFile
refs(QLatin1String(".git/packed-refs"));
186 emptyRepo
= !refs
.exists();
188 if (emptyRepo
) { // all files added are new, just add all.
190 QStringList arguments
;
191 arguments
<< "ls-files" << "-s";
192 GitRunner
runner(git
, arguments
);
193 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
198 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
199 if (lineLength
== -1)
202 file
.setProtection(QString::fromAscii(buf
, 6));
203 file
.setSha1(QString::fromAscii(buf
+7, 40));
204 file
.setFileName(File::escapeGitFilename(QByteArray(buf
+ 50, lineLength
- 51)));
205 d
->files
.append(file
);
207 return AbstractCommand::Ok
;
210 // TODO the below misses the usecase of vng add and then a filesystem rm. Use diff-index --cached to show those.
212 QStringList arguments
;
213 arguments
<< "diff-index" << "-M" << "HEAD";
214 if (! paths
.isEmpty())
215 arguments
<< "--" << paths
;
219 // :000000 100644 0000000000000000000000000000000000000000 8c3ae1d344f18b23c3bdde5d26658b70b03c65d9 A bar
220 // rename main.cpp => notmain.cpp
221 // :100644 100644 e58cfe72cb7a9559a0090886bea5b0ce00db6b47 477c729e5abbd701eb708df563e7e4f749b50435 R074 main.cpp notmain.cpp
223 // :100644 100644 0bdd73e9ea0ba026f6796799946c4bfc9dd1b0b8 0000000000000000000000000000000000000000 M test
225 GitRunner
runner(git
, arguments
);
226 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
231 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
232 if (lineLength
== -1)
234 if (lineLength
> 0 && buf
[0] != ':') // not a diff line, ignore.
238 file
.setOldProtection(QString::fromAscii(buf
+1, 6));
239 file
.setProtection(QString::fromAscii(buf
+8, 6));
240 file
.setOldSha1(QString::fromAscii(buf
+15, 40));
241 file
.setSha1(QString::fromAscii(buf
+56, 40));
243 while (buf
[offset
] != '\t' && offset
< lineLength
)
245 if (buf
[97] == 'R') { // rename
246 int tab
= offset
+ 1;
247 while (buf
[tab
] != '\t' && tab
< lineLength
)
249 file
.setOldFileName(File::escapeGitFilename(QByteArray(buf
+ offset
+ 1, tab
- offset
- 1)));
250 file
.setFileName(File::escapeGitFilename(QByteArray(buf
+ tab
+ 1, lineLength
- tab
- 2)));
252 else if (buf
[97] == 'C') { // Copied file
253 int tab
= offset
+ 1;
254 while (buf
[tab
] != '\t' && tab
< lineLength
)
256 QByteArray
filename(buf
+ offset
+ 1, tab
- offset
- 1);
257 filename
= File::escapeGitFilename(filename
);
258 file
.setOldFileName(filename
);
259 file
.setFileName(filename
);
262 QByteArray
filename(buf
+ offset
+ 1, lineLength
- offset
- 2);
263 filename
= File::escapeGitFilename(filename
);
264 if (buf
[97] != 'A') // Add
265 file
.setOldFileName(filename
);
266 if (buf
[97] != 'D') // Delete
267 file
.setFileName(filename
);
269 d
->files
.append(file
);
274 // call git-diff-files which will find all files that have been removed from the filesystem but are in the index.
275 // Since we don't like the index we just remove them from the index here.
277 arguments
<< "diff-files";
278 if (! paths
.isEmpty())
279 arguments
<< "--" << paths
;
280 runner
.setArguments(arguments
);
281 rc
= runner
.start(GitRunner::WaitForStandardOutput
);
285 arguments
<< "update-index" << "--remove";
287 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
288 if (lineLength
== -1)
290 if (lineLength
> 0 && buf
[0] != ':') // not a diff line, ignore.
292 if (lineLength
< 97 ||buf
[97] != 'D')
296 while (buf
[offset
] != '\t' && offset
< lineLength
)
298 arguments
.append(QString::fromUtf8(File::escapeGitFilename(QByteArray(buf
+ offset
+ 1, lineLength
- offset
- 2))));
300 if (arguments
.count() > 2) {
301 runner
.setArguments(arguments
);
302 runner
.start(GitRunner::WaitUntilFinished
);
305 return AbstractCommand::Ok
;
308 void ChangeSet::generateHunks()
310 Q_ASSERT(d
->hunksFetcher
== 0);
311 d
->hunksFetcher
= new HunksFetcher(d
->files
, *this, d
->changeSetOnIndex
);
312 d
->finishedOneHunk
= false;
313 d
->hunksFetcher
->start();
316 void ChangeSet::lockFile(const File
&file
)
318 // qDebug() << "ChangeSet::lockFile";
319 QMutexLocker
ml(&d
->fileAccessLock
);
320 d
->lockedFile
= file
;
321 if (d
->files
.count() == 0 || d
->files
.at(0) != file
) { // as soon as we have done a file, we can start the interaction
322 // qDebug() << " unlock cursorAccessWaiter";
323 d
->cursorAccessLock
.lock();
324 d
->finishedOneHunk
= true;
325 d
->cursorAccessWaiter
.wakeAll();
326 d
->cursorAccessLock
.unlock();
329 d
->fileAccessWaiter
.wakeAll();
330 // qDebug() << "~ChangeSet::lockFile";
333 void ChangeSet::removeFile(const File
&file
)
335 QMutexLocker
ml(&d
->fileAccessLock
);
336 // TODO move the cursor if this file is the current file.
337 d
->files
.removeAll(file
);
338 d
->fileAccessWaiter
.wakeAll();
341 void ChangeSet::allHunksFetched()
343 // qDebug() << "ChangeSet::allHunksFetched";
344 QMutexLocker
ml(&d
->fileAccessLock
);
345 d
->lockedFile
= File();
346 d
->fileAccessWaiter
.wakeAll();
347 // qDebug() << "~ChangeSet::allHunksFetched";
350 bool ChangeSet::hasAllHunks() const
352 return d
->hunksFetcher
== 0 || d
->hunksFetcher
->isFinished();
356 QList
<File
> ChangeSet::readGitDiff(QIODevice
&git
, File
*fileToDiff
)
360 States(QIODevice
&device
, File
*fileToDiff
)
362 m_fileToDiff(fileToDiff
),
368 Q_ASSERT(m_filesInDiff
.isEmpty()); // only call start once, please ;)
371 qint64 lineLength
= Vng::readLine(&m_input
, buf
, sizeof(buf
));
372 if (lineLength
== -1 || m_state
== Empty
)
375 if (lineLength
== -1)
377 if (addLine(QString::fromLocal8Bit(buf
, lineLength
)))
379 if (m_state
== InPatch
) {
380 QByteArray
array(buf
, lineLength
);
381 m_hunk
.addLine(array
);
386 // try to find out if there are renames
387 foreach (File addedFile
, m_newFiles
) {
388 foreach (File removedFile
, m_removedFiles
) {
389 if (!addedFile
.sha1().isEmpty() && removedFile
.oldSha1() == addedFile
.sha1()) {
390 // TODO if this is a partial sha1 we may want to check some of the content
391 m_removedFiles
.removeAll(removedFile
);
392 addedFile
.setOldSha1(removedFile
.oldSha1());
393 addedFile
.setOldFileName(removedFile
.oldFileName());
394 addedFile
.setOldProtection(removedFile
.oldProtection());
399 m_filesInDiff
<< m_newFiles
;
400 m_filesInDiff
<< m_removedFiles
;
402 m_removedFiles
.clear();
405 QList
<File
> results() const {
406 return m_filesInDiff
;
416 m_file
.addHunk(m_hunk
);
418 if (m_file
.isValid()) {
419 if (m_file
.oldFileName().isEmpty())
420 m_newFiles
<< m_file
;
421 else if (m_file
.fileName().isEmpty())
422 m_removedFiles
<< m_file
;
424 m_filesInDiff
<< m_file
;
427 m_file
= File(*m_fileToDiff
);
433 // returns true if we should exit.
434 bool addLine(const QString
&line
) {
435 const bool newfile
= line
.startsWith("--- /dev/null");
436 if (line
.length() > 6 && (newfile
|| line
.startsWith("--- a/")
437 || line
.startsWith("--- \"a/"))) {
438 if (m_state
== InPatch
)
441 if (!newfile
&& m_fileToDiff
== 0) {
442 if (line
[4].unicode() == '"') { // git-encoding...
443 QByteArray
array(buf
+ 7, strlen(buf
) - 8);
445 m_file
.setOldFileName(File::escapeGitFilename(array
));
447 QByteArray
array(buf
+ 6, strlen(buf
) - 7);
448 m_file
.setOldFileName(File::escapeGitFilename(array
));
452 else if (m_fileToDiff
== 0 && line
.length() > 6 &&
453 (line
.startsWith("+++ b/") || line
.startsWith("+++ \"b"))) {
454 if (m_state
== InPatch
)
457 if (line
[4].unicode() == '"') { // git-encoding...
458 QByteArray
array(buf
+ 7, strlen(buf
) - 8);
460 m_file
.setFileName(File::escapeGitFilename(array
));
462 m_file
.setFileName(QByteArray(buf
+ 6, strlen(buf
) - 7));
465 else if (line
.length() > 5 && line
.startsWith("@@ -")) {
466 m_file
.addHunk(m_hunk
);
470 else if (line
.startsWith("diff --git ")) {
471 if (m_state
== InPatch
)
475 else if (line
.startsWith("Binary files ") && line
.indexOf(" differ") > 0) {
476 Q_ASSERT(m_fileToDiff
);
477 m_fileToDiff
->setBinary(true);
480 else if (line
.startsWith("index ")) {
481 if (m_state
== InPatch
)
484 int dot
= line
.indexOf(QLatin1Char('.'), 6);
485 if (dot
> 0 && line
.length() > dot
+3) {
486 m_file
.setOldSha1(line
.mid(6, dot
-6));
487 int space
= line
.indexOf(QLatin1Char(' '), dot
);
489 space
= line
.length()-1; // cut off the linefeed
491 m_file
.setProtection(line
.mid(space
+1).trimmed());
493 m_file
.setSha1(line
.mid(dot
+2, space
- dot
- 2));
496 else if (line
.startsWith("deleted file mode ")) {
497 if (m_state
== InPatch
)
500 m_file
.setProtection(line
.mid(18).trimmed());
502 else if (line
.startsWith("new file mode ")) {
503 if (m_state
== InPatch
)
506 m_file
.setProtection(line
.mid(13).trimmed());
511 QList
<File
> m_filesInDiff
;
512 QList
<File
> m_newFiles
;
513 QList
<File
> m_removedFiles
;
523 States
stateMachine(git
, fileToDiff
);
524 stateMachine
.start();
525 return stateMachine
.results();
528 void ChangeSet::addFile(const File
&file
)
534 int ChangeSet::count() const
536 return d
->files
.count();
539 void ChangeSet::writeDiff(QIODevice
&outDevice
, ChangeSet::Selection selection
) const
541 waitFinishGenerateHunks();
542 outDevice
.open(QIODevice::WriteOnly
| QIODevice::Truncate
);
543 QDataStream
diff(&outDevice
);
544 foreach(File file
, d
->files
) {
545 if ((selection
== AllHunks
546 || (selection
== UserSelection
&& file
.renameAcceptance() == Vng::Accepted
))
547 && !file
.oldFileName().isEmpty() && !file
.fileName().isEmpty()
548 && file
.oldFileName() != file
.fileName()) {
549 writeRenameDiff(diff
, file
);
552 if (file
.isBinary() && (selection
== AllHunks
|| (selection
== UserSelection
553 && file
.binaryChangeAcceptance() == Vng::Accepted
))) {
554 diff
.writeRawData("diff --git a/", 13);
555 QByteArray fileName
= file
.oldFileName();
556 if (fileName
.isEmpty())
557 fileName
= file
.fileName();
558 diff
.writeRawData(fileName
.data(), fileName
.size());
559 diff
.writeRawData(" b/", 3);
560 fileName
= file
.fileName();
561 if (fileName
.isEmpty())
562 fileName
= file
.oldFileName();
563 diff
.writeRawData(fileName
.data(), fileName
.size());
564 if (file
.oldFileName().isEmpty()) {
565 diff
.writeRawData("\nnew file mode ", 15);
566 QByteArray protection
= file
.protection().toLatin1();
567 diff
.writeRawData(protection
.data(), protection
.size());
568 } else if (file
.fileName().isEmpty()) {
569 diff
.writeRawData("\ndeleted file mode ", 19);
570 QByteArray protection
= file
.oldProtection().toLatin1();
571 diff
.writeRawData(protection
.data(), protection
.size());
573 diff
.writeRawData("\nindex ", 7);
574 QByteArray sha1
= file
.oldSha1().toLatin1();
575 diff
.writeRawData(sha1
.data(), sha1
.size());
576 diff
.writeRawData("..", 2);
577 sha1
= file
.sha1().toLatin1();
578 diff
.writeRawData(sha1
.data(), sha1
.size());
579 diff
.writeRawData("\nGIT binary patch\n", 18);
580 //file.writeBinaryDataAsPatch(outDevice); // TODO :)
581 diff
.writeRawData("literal 0\nHcmV?d00001\n\n", 23);
585 bool fileHeaderWritten
= false;
586 foreach(Hunk hunk
, file
.hunks()) {
587 if (selection
== AllHunks
588 || (selection
== UserSelection
589 && (hunk
.acceptance() == Vng::Accepted
|| hunk
.acceptance() == Vng::MixedAcceptance
))
590 || (selection
== InvertedUserSelection
&& hunk
.acceptance() != Vng::Accepted
)) {
591 if (!fileHeaderWritten
) {
592 if (file
.oldFileName().isEmpty()) { // new file
593 diff
.writeRawData("--- /dev/null", 13);
595 diff
.writeRawData("--- a/", 6);
596 diff
.writeRawData(file
.oldFileName().data(), file
.oldFileName().size());
598 if (file
.fileName().isEmpty()) { // deleted file
599 diff
.writeRawData("\n+++ /dev/null\n", 15);
601 diff
.writeRawData("\n+++ b/", 7);
602 diff
.writeRawData(file
.fileName().data(), file
.fileName().size());
603 diff
.writeRawData("\n", 1);
605 fileHeaderWritten
= true;
607 QByteArray acceptedPatch
;
608 if (selection
== InvertedUserSelection
)
609 acceptedPatch
=hunk
.rejectedPatch();
610 else if (selection
== AllHunks
) {
611 acceptedPatch
= hunk
.header();
612 acceptedPatch
.append(hunk
.patch());
615 acceptedPatch
= hunk
.acceptedPatch();
616 diff
.writeRawData(acceptedPatch
.data(), acceptedPatch
.size());
623 void ChangeSet::writeRenameDiff(QDataStream
&out
, const File
&file
) const
625 out
.writeRawData("diff --git a/", 13);
626 out
.writeRawData(file
.fileName().data(), file
.fileName().size());
627 out
.writeRawData(" b/", 3);
628 out
.writeRawData(file
.fileName().data(), file
.fileName().size());
629 out
.writeRawData("\nnew file mode ", 15);
630 QByteArray tmp
= file
.protection().toLatin1();
631 out
.writeRawData(tmp
.data(), tmp
.length());
632 out
.writeRawData("\nindex 0000000..", 16);
633 tmp
= file
.sha1().toLatin1();
634 out
.writeRawData(tmp
.data(), 7);
635 out
.writeRawData("\n--- /dev/null\n+++ b/", 21);
636 out
.writeRawData(file
.fileName().data(), file
.fileName().size());
639 QStringList arguments
;
640 arguments
<< "cat-file" << "blob" << file
.sha1();
641 GitRunner
runner(git
, arguments
);
642 runner
.start(GitRunner::WaitForStandardOutput
);
643 QList
<QByteArray
> lines
;
646 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
647 if (lineLength
== -1)
649 lines
.append(QByteArray(buf
, lineLength
));
652 QByteArray countAsString
= QString::number(lines
.count()).toLatin1();
653 out
.writeRawData("\n@@ -0,0 +1,", 12);
654 out
.writeRawData(countAsString
.data(), countAsString
.length());
655 out
.writeRawData(" @@\n", 4);
656 foreach (const QByteArray
&line
, lines
) {
657 out
.writeRawData("+", 1);
658 out
.writeRawData(line
.data(), line
.length());
662 out
.writeRawData("\ndiff --git a/", 14);
663 out
.writeRawData(file
.oldFileName().data(), file
.oldFileName().size());
664 out
.writeRawData(" b/", 3);
665 out
.writeRawData(file
.oldFileName().data(), file
.oldFileName().size());
666 out
.writeRawData("\ndeleted file mode ", 19);
667 tmp
= file
.oldProtection().toLatin1();
668 out
.writeRawData(tmp
.data(), tmp
.length());
669 out
.writeRawData("\nindex ", 7);
670 tmp
= file
.oldSha1().toLatin1();
671 out
.writeRawData(tmp
.data(), 7);
672 out
.writeRawData("..0000000\n--- a/", 16);
673 out
.writeRawData(file
.oldFileName().data(), file
.oldFileName().size());
675 out
.writeRawData("\n+++ /dev/null\n@@ -1,", 21);
676 out
.writeRawData(countAsString
.data(), countAsString
.length());
677 out
.writeRawData(" +0,0 @@\n", 9);
679 foreach (const QByteArray
&line
, lines
) {
680 out
.writeRawData("-", 1);
681 out
.writeRawData(line
.data(), line
.length());
685 bool ChangeSet::hasAcceptedChanges() const
687 waitFinishGenerateHunks();
688 foreach(File file
, d
->files
) {
689 if (file
.renameAcceptance() == Vng::Accepted
&& file
.fileName() != file
.oldFileName())
691 if (file
.protectionAcceptance() == Vng::Accepted
&& file
.protection() != file
.oldProtection())
693 if (file
.isBinary() && file
.binaryChangeAcceptance() == Vng::Accepted
)
695 foreach(Hunk hunk
, file
.hunks()) {
696 Vng::Acceptance a
= hunk
.acceptance();
697 if (a
== Vng::Accepted
|| a
== Vng::MixedAcceptance
)
704 File
ChangeSet::file(int index
) const
706 // qDebug() << "ChangeSet::file" << index << d->finishedOneHunk;
707 waitForFinishFirstFile();
708 QMutexLocker
ml2(&d
->fileAccessLock
);
709 while (d
->files
.count() > index
&& d
->files
[index
] == d
->lockedFile
)
710 // { qDebug() << " waiting for file to be unlocked";
711 d
->fileAccessWaiter
.wait(&d
->fileAccessLock
);
713 if (d
->files
.count() <= index
)
716 // qDebug() << "ChangeSet::~file";
717 return d
->files
[index
];
720 ChangeSet
&ChangeSet::operator=(const ChangeSet
&other
)
722 #if QT_VERSION >= 0x040400
734 void ChangeSet::waitFinishGenerateHunks() const
737 d
->hunksFetcher
->wait();
740 void ChangeSet::waitForFinishFirstFile() const
742 d
->cursorAccessLock
.lock();
743 if (! d
->finishedOneHunk
)
744 d
->cursorAccessWaiter
.wait(&d
->cursorAccessLock
);
745 d
->cursorAccessLock
.unlock();