public synchronized List<Message> importDoc(GitRepoDoc gitRepoDoc) throws IOException {
        List<Message> messages = new ArrayList<>();

        DocRef gitRepoDocRef = GitRepoDoc.getDocRef(gitRepoDoc.getUuid());
        Optional<ExplorerNode> optGitRepoExplorerNode = explorerService.getFromDocRef(gitRepoDocRef);
        ExplorerNode gitRepoExplorerNode = optGitRepoExplorerNode.orElseThrow(IOException::new);

        // Work out where the GitRepo node is in the explorer tree
        List<ExplorerNode> gitRepoNodePath = this.explorerNodeService.getPath(gitRepoDocRef);
        gitRepoNodePath.add(gitRepoExplorerNode);

        // Only try to do anything if the settings exist
        if (!gitRepoDoc.getUrl().isEmpty()) {
            // Find the path to the root of the local Git repository
            final Path localDir = pathCreator.toAppPath(config.getLocalDir());
            final Path gitWork = localDir.resolve(gitRepoDoc.getUuid());

            // Grab everything from server - it won't be too big
            this.gitCloneForPull(gitRepoDoc, gitWork);
            messages.add(new Message(Severity.INFO, "Cloned from Git repository"));

            // ImportSettings.auto() is used in a few places. This consists of
            // .importMode(ImportMode.IGNORE_CONFIRMATION)
            // .enableFilters(true)
            // We want to make sure that the import goes to the root of the
            // gitRepoDocRef rather than where it might have been in the
            // original export.

            List<ImportState> importStates = new ArrayList<>();
            ImportSettings importSettings = ImportSettings.builder()
                    .importMode(ImportMode.IGNORE_CONFIRMATION)
                    .enableFilters(false)
                    .useImportFolders(true)
                    .useImportNames(true)
                    .rootDocRef(gitRepoDocRef)
                    .build();

            // Set (remote) directory
            final Path pathToImport;
            if (gitRepoDoc.getPath() != null && !gitRepoDoc.getPath().isEmpty()) {
                pathToImport = addDirectoryToPath(gitWork, Paths.get(gitRepoDoc.getPath()));
            } else {
                pathToImport = gitWork;
            }

            Set<DocRef> docRefs = importExportSerializer.read(pathToImport, importStates, importSettings);
            for (var docRef : docRefs) {
                // ImportExportSerializerImpl adds the System docref to the returned set,
                // but we don't use that here, so ignore it
                if (!docRef.equals(ExplorerConstants.SYSTEM_DOC_REF)) {
                    messages.add(new Message(Severity.INFO, "Imported '" + docRef.getName() + "'"));
                }
            }
            messages.add(new Message(Severity.INFO, "Completed Git Pull"));

        } else {
            throw new IOException("Git repository URL isn't configured; cannot pull");
        }

        return messages;
    }
Called when the user presses the Pull from Git button in the UI.
Synchronised to avoid multiple threads writing to the same
directory structure.

@param gitRepoDoc The document holding the Git repo settings
@return A list of messages about the import
@throws IOException if something goes wrong
stroom-gitrepo/stroom-gitrepo-impl/src/main/java/stroom/gitrepo/impl/GitRepoStorageService.java