public override async Task ExecuteAsync(IOperationExecutionContext context)
        {
            if (!string.IsNullOrWhiteSpace(this.SourceServerName) && string.IsNullOrWhiteSpace(this.SourceDirectory))
            {
                this.LogError("If SourceServer is specified, SourceDirectory must also be specified.");
                return;
            }

            var sourceAgent = context.Agent;
            var targetAgent = context.Agent;

            if (!string.IsNullOrWhiteSpace(this.SourceServerName))
            {
                this.LogDebug($"Attempting to resolve source server \"{this.SourceServerName}\"...");
                sourceAgent = await context.GetAgentAsync(this.SourceServerName).ConfigureAwait(false);
            }
            else
            {
                this.LogDebug("Using default source server.");
            }

            if (!string.IsNullOrWhiteSpace(this.TargetServerName))
            {
                this.LogDebug($"Attempting to resolve target server \"{this.TargetServerName}\"...");
                targetAgent = await context.GetAgentAsync(this.TargetServerName).ConfigureAwait(false);
            }
            else
            {
                this.LogDebug("Using default target server.");
            }

            if (sourceAgent == null)
            {
                this.LogError("Source server was not specified and there is no server in the current context.");
                return;
            }

            if (targetAgent == null)
            {
                this.LogError("Target server was not specified and there is no server in the current context.");
                return;
            }

            var sourceFileOps = await sourceAgent.GetServiceAsync <IFileOperationsExecuter>().ConfigureAwait(false);

            var targetFileOps = await targetAgent.GetServiceAsync <IFileOperationsExecuter>().ConfigureAwait(false);

            var sourceDirectory = this.SourceDirectory ?? context.WorkingDirectory;

            if (SDK.ProductName == "BuildMaster")
            {
                if (sourceDirectory.StartsWith("~\\") || sourceDirectory.StartsWith("~/"))
                {
                    sourceDirectory = sourceFileOps.CombinePath(
                        GetBuildMasterExecutionBaseWorkingDirectory(sourceFileOps, context),
                        sourceDirectory.Substring(2)
                        );
                }
            }

            this.LogDebug("Source directory: " + sourceDirectory);
            this.LogDebug("Getting source file list...");
            var sourceItems = await sourceFileOps.GetFileSystemInfosAsync(sourceDirectory, new MaskingContext(this.Includes, this.Excludes)).ConfigureAwait(false);

            var targetDirectory = this.TargetDirectory ?? context.WorkingDirectory;

            if (SDK.ProductName == "BuildMaster")
            {
                if (targetDirectory.StartsWith("~\\") || targetDirectory.StartsWith("~/"))
                {
                    targetDirectory = targetFileOps.CombinePath(
                        GetBuildMasterExecutionBaseWorkingDirectory(targetFileOps, context),
                        targetDirectory.Substring(2)
                        );
                }
            }

            this.LogDebug("Target directory: " + targetDirectory);
            if (!PathEx.IsPathRooted(targetDirectory))
            {
                this.LogError("Target directory must be rooted.");
                return;
            }

            if (this.VerboseLogging)
            {
                this.LogDebug($"Ensuring that {targetDirectory} exists...");
            }
            await targetFileOps.CreateDirectoryAsync(targetDirectory);

            this.LogDebug("Getting target file list...");
            var targetItems = targetFileOps
                              .GetFileSystemInfos(targetDirectory, new MaskingContext(this.Includes, this.Excludes))
                              .ToDictionary(f => f.FullName.Replace('\\', '/'));

            var sourcePath = sourceDirectory.TrimEnd('/', '\\');
            var targetPath = targetDirectory;

            int filesCopied        = 0;
            int directoriesCopied  = 0;
            int filesDeleted       = 0;
            int directoriesDeleted = 0;

            var sourceFiles = sourceItems.OfType <SlimFileInfo>().ToList();
            var sourceDirs  = sourceItems.OfType <SlimDirectoryInfo>().ToList();

            Interlocked.Exchange(ref this.totalBytes, sourceFiles.Sum(f => f.Size));

            Func <SlimFileInfo, Task> transferFile = async(file) =>
            {
                var targetFileName = PathEx.Combine(targetPath, file.FullName.Substring(sourcePath.Length).TrimStart('/', '\\')).Replace(sourceFileOps.DirectorySeparator, targetFileOps.DirectorySeparator);
                if (this.VerboseLogging)
                {
                    this.LogDebug($"Copying {file.FullName} to {targetFileName}...");
                }

                try
                {
                    await this.TransferFileAsync(sourceFileOps, file, targetFileOps, targetItems.GetValueOrDefault(targetFileName.Replace('\\', '/')), PathEx.GetDirectoryName(targetFileName)).ConfigureAwait(false);

                    Interlocked.Increment(ref filesCopied);
                }
                catch (Exception ex)
                {
                    this.LogError($"Cannot copy {file.FullName}: {ex.Message}");
                }

                Interlocked.Add(ref this.bytesCopied, file.Size);
            };

            int batches = sourceFiles.Count / this.BatchSize;

            for (int batch = 0; batch < batches; batch++)
            {
                var tasks = new Task[this.BatchSize];
                for (int i = 0; i < this.BatchSize; i++)
                {
                    var file = sourceFiles[batch * this.BatchSize + i];
                    tasks[i] = transferFile(file);
                }
                await Task.WhenAll(tasks).ConfigureAwait(false);
            }
            if (batches * this.BatchSize != sourceFiles.Count)
            {
                var remaining = new Task[sourceFiles.Count % this.BatchSize];
                for (int i = batches * this.BatchSize; i < sourceFiles.Count; i++)
                {
                    var file = sourceFiles[i];
                    remaining[i % this.BatchSize] = transferFile(file);
                }
                await Task.WhenAll(remaining).ConfigureAwait(false);
            }

            foreach (var dir in sourceDirs)
            {
                var targetDir = PathEx.Combine(targetPath, dir.FullName.Substring(sourcePath.Length).TrimStart('/', '\\')).Replace(sourceFileOps.DirectorySeparator, targetFileOps.DirectorySeparator);
                if (this.VerboseLogging)
                {
                    this.LogDebug($"Creating directory {targetDir}...");
                }

                await targetFileOps.CreateDirectoryAsync(targetDir).ConfigureAwait(false);

                directoriesCopied++;
            }

            if (this.DeleteTarget)
            {
                var sourceItems2 = sourceItems.Select(f => f.FullName.Substring(sourcePath.Length).TrimStart('/', '\\').Replace('\\', '/')).ToHashSet(StringComparer.OrdinalIgnoreCase);

                foreach (var target in targetItems.Values)
                {
                    var relativeItemPath = target.FullName.Substring(targetPath.Length).TrimStart('/', '\\');
                    if (!sourceItems2.Contains(relativeItemPath.Replace('\\', '/')))
                    {
                        if (target is SlimFileInfo)
                        {
                            if (this.VerboseLogging)
                            {
                                this.LogDebug($"Deleting {target.FullName}...");
                            }

                            await targetFileOps.DeleteFileAsync(target.FullName).ConfigureAwait(false);

                            filesDeleted++;
                        }
                        else
                        {
                            if (this.VerboseLogging)
                            {
                                this.LogDebug($"Deleting directory {target.FullName}...");
                            }

                            await targetFileOps.DeleteDirectoriesAsync(new[] { target.FullName }).ConfigureAwait(false);

                            directoriesDeleted++;
                        }
                    }
                }
            }

            this.LogDebug($"Copied {filesCopied} files, deleted {filesDeleted} files and {directoriesDeleted} directories over {directoriesCopied} directories.");
        }