Source: core/delete_git_working_trees.js

/*
 * This file is part of PKM (Persistent Knowledge Monitor).
 * Copyright (c) 2020 Capgemini Group, Commissariat à l'énergie atomique et aux énergies alternatives,
 *                    OW2, Sysgo AG, Technikon, Tree Technology, Universitat Politècnica de València.
 * 
 * PKM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation.
 * 
 * PKM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with PKM.  If not, see <https://www.gnu.org/licenses/>.
 */

/**
 * Delete Git Working Trees together with Git working trees while optionally leaving intact Files in PKM.
 * 
 * @memberof PKM
 * @instance
 * @param {string} dbName - database name
 * @param {Object} [query] - query
 * 
 * @return {Promise} a promise
 */
function delete_git_working_trees(dbName, query = {}, options = {})
{
	return new Promise(function(resolve, reject)
	{
		const debug = this.debug;
		const dont_delete_pkm_files = options.dont_delete_pkm_files;
		let git_file_system = this.get_git_file_system(dbName);
		let git_working_trees;
		
		const { list_git_versioned_files } = require('../util/git');
		const asyncPool = require('tiny-async-pool');
		
		new Promise((resolve, reject) =>
		{
			// (1) Get then delete the Git working trees in the PKM
			this.get_git_working_trees(dbName, query).then((_git_working_trees) =>
			{
				git_working_trees = _git_working_trees;
				
				// delete the Git working tree
				this.delete_documents(dbName, 'GitWorkingTrees', query).then(() =>
				{
					resolve();
				}).catch((err) =>
				{
					reject(this.Error(err));
				});
			}).catch((err) =>
			{
				reject(this.Error(err));
			});
		}).then(() => new Promise((resolve, reject) =>
		{
			// (2) Unbind PKM Files from Git working trees:
			//       - starting with the files of the linked Git working trees
			//       - then finishing with the files of the main Git working tree
			
			let unbind_or_delete_files = function(git_working_tree)
			{
				return new Promise((resolve, reject) =>
				{
					this.get_any_files(dbName, { gitWorkingTree: git_working_tree.directory }, { filecontent : 0 }).then((abbrev_files) =>
					{
						asyncPool(1, abbrev_files, (abbrev_file) => new Promise((resolve, reject) =>
						{
							if(dont_delete_pkm_files)
							{
								this.get_any_files(dbName, { filename: abbrev_file.rel_path }).then((files) =>
								{
									let file = files[0];
									
									// Unbind the file
									delete file.git_working_tree;
									delete file.git_unmerged;
									
									// Put the file in the PKM (this sets the git_dirty flag)
									this.update_any_files(dbName, [ file ], { git : true }).then(() =>
									{
										resolve();
									}).catch((err) =>
									{
										reject(this.Error(err));
									});
								}).catch((err) =>
								{
									reject(this.Error(err));
								});
							}
							else
							{
								this.delete_any_files(dbName, { filename: abbrev_file.rel_path }).then(() =>
								{
									resolve();
								}).catch((err) =>
								{
									reject(this.Error(err));
								});
							}
						})).then(() =>
						{
							resolve();
						}).catch((err) =>
						{
							reject(err);
						});
					}).catch((err) =>
					{
						const error = require('./error');
						if(err instanceof error.PKM_NotFound)
						{
							resolve();
						}
						else
						{
							reject(this.Error(err));
						}
					});
				});
			}.bind(this);
		
			asyncPool(1, git_working_trees, (git_working_tree) => new Promise((resolve, reject) =>
			{
				((git_working_tree.linked === undefined) ? Promise.resolve()
				                                         : asyncPool(1, git_working_tree.linked, (linked_git_working_tree) => unbind_or_delete_files(linked_git_working_tree)))
				.then(() => asyncPool(1, git_working_trees, (git_working_tree) => unbind_or_delete_files(git_working_tree)))
				.then(() =>
				{
					resolve();
				}).catch((err) =>
				{
					reject(this.Error(err));
				});
			})).then(() =>
			{
				resolve();
			}).catch((err) =>
			{
				reject(this.Error(err));
			});
		})).then(() => new Promise((resolve, reject) =>
		{
			// (3) Delete the on-disk directories
			
			let delete_working_tree = function(git_working_tree)
			{
				return new Promise((resolve, reject) =>
				{
					// delete the Git directory associated to the Git working tree
					// Note: the Git directory can be inside or outside of the Git working tree
					git_file_system.rmdir_r(git_working_tree.git_directory).then(() =>
					{
						// the Git directory has been deleted, no more git commands can work from now on
						
						// delete the Git working tree
						git_file_system.rmdir_r(git_working_tree.directory).then(() =>
						{
							// the Git working tree has been deleted
							resolve();
						}).catch((err) =>
						{
							reject(this.Error(err));
						});
					}).catch((err) =>
					{
						reject(this.Error(err));
					});
				});
			}.bind(this);
			
			asyncPool(1, git_working_trees, (git_working_tree) => new Promise((resolve, reject) =>
			{
				((git_working_tree.linked === undefined) ? Promise.resolve()
				                                         : asyncPool(1, git_working_tree.linked, (linked_git_working_tree) => delete_working_tree(linked_git_working_tree)))
				.then(() => asyncPool(1, git_working_trees, (git_working_tree) => delete_working_tree(git_working_tree)))
				.then(() =>
				{
					resolve();
				}).catch((err) =>
				{
					reject(this.Error(err));
				});
			})).then(() =>
			{
				resolve();
			}).catch((err) =>
			{
				reject(this.Error(err));
			});
		})).then(() =>
		{
			// job's done
			resolve();
		}).catch((err) =>
		{
			reject(err);
		});
	}.bind(this));
}

exports.delete_git_working_trees = delete_git_working_trees;