Source: util/git_config.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/>.
 */

'use strict';

/** Git config
 */
class GitConfig
{
	/** constructor
	 * 
	 * @param {string} [git_config_output] - text output of 'git config -l'
	 */
	constructor(git_config_output)
	{
		if(git_config_output !== undefined)
		{
			function assign(obj, path, schema, value)
			{
				function rec_assign(obj, path, schema, value, depth)
				{
					const key = path[depth];
					
					let schema_value;
					if(schema !== undefined)
					{
						schema_value = schema[key];
						if(schema_value === undefined)
						{
							schema_value = schema['*'];
						}
					}
					if(depth < (path.length - 1))
					{
						if(obj[key] === undefined) obj[key] = {};
						rec_assign(obj[key], path, schema_value, value, depth + 1);
					}
					else
					{
						if(schema_value !== undefined)
						{
							if(typeof schema_value === 'boolean')
							{
								if(value == 'true')
								{
									obj[key] = true;
									return;
								}
								else if(value == 'false')
								{
									obj[key] = false;
									return;
								}
							}
							else if(typeof schema_value === 'number')
							{
								const number = Number(value);
								if(number !== NaN)
								{
									obj[key] = number;
									return;
								}
							}
						}
						
						obj[key] = value;
					}
				}
				
				rec_assign(obj, path, schema, value, 0);
			}
			
			const git_config_lines = git_config_output.replace(/\r/, '').split('\n');

			git_config_lines.forEach((assignment) =>
			{
				let i = assignment.indexOf('=');
				if(i > -1)
				{
					const var_name = assignment.slice(0, i);
					const var_value = assignment.slice(i + 1);
					
					const path = var_name.split('.');
					
					assign(this, path, GitConfig.schema, var_value);
				}
			});
		}
	}
	
	static from(obj)
	{
		const { deep_copy } = require('./deep_copy');
		return Object.assign(new GitConfig(), deep_copy(obj));
	}
	
	static schema =
	{
		advice:
		{
			'*': true
		},
		core:
		{
			repositoryformatversion: 0,
			filemode: true,
			bare: true,
			logallrefupdates: true,
			hideDotFiles: true,
			ignoreCase: true,
			precomposeUnicode: true,
			protectHFS: true,
			protectNTFS: true,
			fsmonitor: 'string',
			fsmonitorHookVersion: 1,
			trustctime: true,
			splitIndex: true,
			untrackedCache: true,
			checkStat: 'string',
			quotePath: 'string',
			eol: 'string',
			safecrlf: 'string',
			autocrlf: 'string',
			checkRoundtripEncoding: 'string',
			symlinks: true,
			gitProxy: 'string',
			sshCommand: 'string',
			ignoreStat: true,
			preferSymlinkRefs: true,
			alternateRefsCommand: 'string',
			alternateRefsPrefixes: 'string',
			worktree: 'string',
			sharedRepository : 'string',
			warnAmbiguousRefs: true,
			compression: -1,
			looseCompression: -1,
			packedGitWindowSize: 'string',
			packedGitLimit: 'string',
			deltaBaseCacheLimit: 'string',
			bigFileThreshold: 'string',
			excludesFile: 'string',
			askPass: 'string',
			attributesFile: 'string',
			hooksPath: 'string',
			editor: 'string',
			commentChar: 'string',
			filesRefLockTimeout: 100,
			packedRefsTimeout: 1000,
			pager: 'string',
			whitespace: 'string',
			fsyncObjectFiles: true,
			preloadIndex: true,
			unsetenvvars: 'string',
			restrictinheritedhandles: 'string',
			createObject: 'string',
			notesRef: 'string',
			commitGraph: true,
			useReplaceRefs: true,
			multiPackIndex: true,
			sparseCheckout: true,
			sparseCheckoutCone: true,
			abbrev: 4
		},
		add:
		{
			ignoreErrors: true,
			interactive:
			{
				useBuiltin: true
			}
		},
		alias:
		{
			'*' : 'string'
		},
		am:
		{
			keepcr: true,
			threeWay: true
		},
		apply:
		{
			ignoreWhitespace: 'string',
			whitespace: 'string'
		},
		blame:
		{
			blankBoundary: true,
			coloring: 'string',
			date: 'string',
			showEmail: true,
			showRoot: true,
			ignoreRevsFile: true,
			markUnblamables: true,
			markIgnoredLines: true
		},
		branch:
		{
			autoSetupMerge: true,
			autoSetupRebase: true,
			sort: 'string',
			'*':
			{
				remote: 'string',
				pushRemote: 'string',
				merge: 'string',
				mergeOptions: 'string',
				rebase: true,
				description: 'string'
			}
		},
		browser:
		{
			'*':
			{
				cmd: 'string',
				path: 'string'
			}
		},
		checkout:
		{
			defaultRemote: 'string',
			guess: true,
			workers: 1,
			thresholdForParallelism: 100,
			requireForce: true,
			defaultRemoteName: 'string',
			rejectShallow: true
		},
		color:
		{
			advice: true,
			//advice
			//{
			//	hint: 'string'
			//},
			blame:
			{
				highlightRecent: 'string',
				repeatedLines: 'string',
				branch: true,
				//branch
				//{
				//	'*': 'string'
				//}
			},
			diff: 'string',
			//diff
			//{
			//	'*': 'string'
			//},
			decorate:
			{
				'*': 'string'
			},
			grep: 'string',
			//grep
			//{
			//	'*': 'string'
			//},
			interactive: 'string',
			//interactive
			//{
			//	'*': 'string'
			//},
			pager: true,
			push: true,
			//push:
			//{
			//	error: 'string'
			//},
			remote: 'string',
			//remote:
			//{
			//	error: 'string'
			//},
			showBranch: true,
			status: true,
			//status:
			//{
			//	'*': 'string'
			//},
			transport: true,
			//transport:
			//{
			//	rejected: 'string'
			//},
			ui: 'string'
		},
		column:
		{
			ui: 'string',
			branch: 'string',
			clean: 'string',
			status: 'string',
			tag: 'string'
		},
		commit:
		{
			cleanup:  'string',
			gpgSign: true,
			status: true,
			template: 'string',
			verbose: true,
			generationVersion: 1,
			maxNewFilters: 1,
			readChangedPaths: true
		},
		credential:
		{
			helper: 'string',
			useHttpPath: true,
			username: 'string',
			'*':
			{
				helper: 'string',
				useHttpPath: true,
				username: 'string'
			}
		},
		credentialCache:
		{
			ignoreSIGHUP: true,
			lockTimeoutMS: 1000,
			commands: 'string',
			autoRefreshIndex: true,
			dirstat: 'string'
		},
		diff:
		{
			statGraphWidth: 1,
			context: 3,
			interHunkContext: 1,
			external: 'string',
			ignoreSubmodules: 'string',
			mnemonicPrefix: 'string',
			noprefix: true,
			relative: true,
			orderFile: 'string',
			renameLimit: 1,
			renames: true,
			suppressBlankEmpty: true,
			submodule: 'string',
			wordRegex: 'string',
			tool: 'string',
			guitool: 'string',
			indentHeuristic: true,
			algorithm: 'string',
			wsErrorHighlight: 'string',
			colorMoved: 'string',
			colorMovedWS: 'string',
			'*':
			{
				command: 'string',
				xfuncname: 'string',
				binary: true,
				textconv: 'string',
				wordRegex: 'string',
				cachetextconv: true
			},
		},
		difftool:
		{
			prompt: 'string',
			'*':
			{
				path: 'string',
				cmd: 'string'
			}
		},
		extensions:
		{
			objectFormat: 'string'
		},
		fastimport:
		{
			unpackLimit: 1
		},
		feature:
		{
			experimental: 'string',
			manyFiles: true
		},
		fetch:
		{
			recurseSubmodules: true,
			fsckObjects: true,
			fsck:
			{
				skipList: 'string'
// 						'*': 'string'
			}
		},
		gc:
		{
			aggressiveDepth: 50,
			aggressiveWindow: 250,
			auto: 6700,
			autoPackLimit: 50,
			autoDetach: true,
			bigPackThreshold: 'string',
			writeCommitGraph: true,
			logExpiry: 'string',
			packRefs: true,
			pruneExpire: 'string',
			worktreePruneExpire: 'string',
			reflogExpire: 'string',
			'*':
			{
				reflogExpire: 'string'
			},
			reflogExpireUnreachable: 'string',
			'*':
			{
				reflogExpireUnreachable: 'string'
			},
			rerereResolved: 'string',
			rerereUnresolved: 'string'
		},
		gitcvs:
		{
			commitMsgAnnotation: 'string',
			enabled: true,
			logFile: 'string',
			usecrlfattr: true,
			allBinary: true,
			dbName: 'string',
			dbDriver: 'string',
			dbUser: 'string',
			dbPass: 'string',
			dbTableNamePrefix: 'string'
		},
		gitweb:
		{
			category: 'string',
			description: 'string',
			owner: 'string',
			url: 'string',
			avatar: 'string',
			blame: true,
			grep: true,
			highlight: true,
			patches: 16,
			pickaxe: true,
			remote_heads: 'string',
			showSizes: true,
			snapshot : 'string'
		},
		grep:
		{
			lineNumber: true,
			column: true,
			patternType: 'string',
			extendedRegexp: true,
			threads: 1,
			fallbackToNoIndex: true
		},
		gpg:
		{
			program: 'string',
			format: 'string',
			minTrustLevel: 'string',
			'*':
			{
				program: 'string'
			}
		},
		gui:
		{
			commitMsgWidth: 75,
			diffContext: 5,
			displayUntracked: true,
			encoding: 'string',
			matchTrackingBranch: true,
			newBranchTemplate: 'string',
			pruneDuringFetch: true,
			trustmtime: true,
			spellingDictionary: 'string',
			fastCopyBlame: true,
			copyBlameThreshold: 1,
			blamehistoryctx: 1
		},
		guitool:
		{
			'*':
			{
				cmd: 'string',
				needsFile: true,
				noConsole: true,
				noRescan: true,
				confirm: true,
				argPrompt: true,
				revPrompt: true,
				revUnmerged: true,
				title: 'string',
				prompt: 'string'
				
			}
		},
		help:
		{
			browser: 'string',
			format: 'string',
			autoCorrect: 'string',
			htmlPath: 'string'
		},
		http:
		{
			proxy: 'string',
			proxyAuthMethod: 'string',
			proxySSLCert: 'string',
			proxySSLKey: 'string',
			proxySSLCertPasswordProtected: true,
			proxySSLCAInfo: 'string',
			emptyAuth: true,
			delegation: 'string',
			extraHeader: 'string',
			cookieFile: 'string',
			saveCookies: 'string',
			version: 'string',
			sslVersion: 'string',
			sslCipherList: 'string',
			sslVerify: true,
			sslCert: 'string',
			sslKey: 'string',
			sslCertPasswordProtected: 'string',
			sslCAInfo: 'string',
			sslCAPath: 'string',
			sslBackend: 'string',
			schannelCheckRevoke: 'string',
			schannelUseSSLCAInfo: 'string',
			pinnedpubkey: 'string',
			sslTry: true,
			maxRequests: 5,
			minSessions: 1,
			postBuffer: 1,
			lowSpeedLimit: 1,
			lowSpeedTime: 1,
			noEPSV: true,
			userAgent: 'string',
			followRedirects: true,
			'*':
			{
				proxy: 'string',
				proxyAuthMethod: 'string',
				proxySSLCert: 'string',
				proxySSLKey: 'string',
				proxySSLCertPasswordProtected: true,
				proxySSLCAInfo: 'string',
				emptyAuth: true,
				delegation: 'string',
				extraHeader: 'string',
				cookieFile: 'string',
				saveCookies: 'string',
				version: 'string',
				sslVersion: 'string',
				sslCipherList: 'string',
				sslVerify: true,
				sslCert: 'string',
				sslKey: 'string',
				sslCertPasswordProtected: 'string',
				sslCAInfo: 'string',
				sslCAPath: 'string',
				sslBackend: 'string',
				schannelCheckRevoke: 'string',
				schannelUseSSLCAInfo: 'string',
				pinnedpubkey: 'string',
				sslTry: true,
				maxRequests: 5,
				minSessions: 1,
				postBuffer: 1,
				lowSpeedLimit: 1,
				lowSpeedTime: 1,
				noEPSV: true,
				userAgent: 'string',
				followRedirects: true
			}
		},
		i18n:
		{
			commitEncoding: 'string',
			logOutputEncoding: 'string'
		},
		imap:
		{
			folder: 'string',
			tunnel: 'string',
			host: 'string',
			user: 'string',
			pass: 'string',
			port: 1,
			sslverify: true,
			preformattedHTML: true,
			authMethod: 'string'
		},
		index:
		{
			recordEndOfIndexEntries: true,
			recordOffsetTable: true,
			sparse: true,
			threads: 1,
			version: 1
		},
		init:
		{
			templateDir: 'string',
			defaultBranch: 'string'
		},
		instaweb:
		{
			httpd: 'string',
			local: true,
			modulePath: 'string',
			port: 1
		},
		interactive:
		{
			singleKey: true,
			diffFilter: 'string'
		},
		log:
		{
			abbrevCommit: true,
			date: 'string',
			decorate: true,
			excludeDecoration: 'string',
			diffMerges: 'string',
			follow: true,
			graphColors: 'string',
			showRoot: true,
			showSignature: true,
			mailmap: true
		},
		lsrefs:
		{
			unborn: 'string'
		},
		mailinfo:
		{
			scissors: true
		},
		mailmap:
		{
			file: 'string',
			blob: 'string'
		},
		maintenance:
		{
			auto: true,
			strategy: 'string',
			'commit-graph':
			{
				auto: 100,
			},
			'loose-objects':
			{
				auto: 100
			},
			'incremental-repack':
			{
				auto: 10
			},
			'*':
			{
				enabled: true,
				schedule: 'string'
			}
		},
		man:
		{
			viewer: 'string',
			'*':
			{
				cmd: 'string',
				path: 'string'
			}
		},
		merge:
		{
			conflictStyle: 'string',
			defaultToUpstream: true,
			ff: true,
			verifySignatures: true,
			branchdesc: true,
			log: true,
			suppressDest: true,
			renameLimit: 1,
			renames: true,
			directoryRenames: true,
			renormalize: true,
			stat: true,
			autoStash: true,
			tool: 'string',
			guitool: 'string',
			verbosity: 1,
			meld:
			{
				hasOutput: true,
				useAutoMerge: true
				
			},
			hideResolved: true,
			keepBackup: true,
			keepTemporaries: true,
			writeToTemp: true,
			prompt: 'string',
			mergeStrategy: 'string',
			'*':
			{
				name: 'string',
				driver: 'string',
				recursive: 'string',
				path: 'string',
				cmd: 'string',
				hideResolved: true,
				trustExitCode: true
				
			}
		},
		notes:
		{
			mergeStrategy: 'string',
			displayRef: 'string',
			rewrite:
			{
				'*': true
			},
			rewriteMode: 'string',
			rewriteRef: 'string',
			'*':
			{
				mergeStrategy: 'string'
			}
		},
		pack:
		{
			window: 10,
			depth: 50,
			windowMemory: 'string',
			compression: 1,
			allowPackReuse: true,
			island: 'string',
			islandCore: 'string',
			deltaCacheSize: 1,
			deltaCacheLimit: 1,
			threads: 1,
			indexVersion: 1,
			packSizeLimit: 'string',
			useBitmaps: true,
			useSparse: true,
			preferBitmapTips: 'string',
			writeBitmaps: true,
			writeBitmapHashCache: true,
			writeReverseIndex: true
		},
		pager:
		{
			'*': 'string'
		},
		pretty:
		{
			'*': 'string'
		},
		protocol:
		{
			allow: 'string',
			version: 1,
			'*':
			{
				allow: 'string',
			}
		},
		pull:
		{
			ff: true,
			rebase: true,
			octopus: 'string',
			twohead: 'string'
			
		},
		push:
		{
			default: 'string',
			followTags: true,
			gpgSign: true,
			pushOption: 'string',
			recurseSubmodules: 'string',
			useForceIfIncludes: true,
			negotiate: true
		},
		rebase:
		{
			backend: 'string',
			stat: true,
			autoSquash: true,
			missingCommitsCheck: 'string',
			instructionFormat: 'string',
			abbreviateCommands: true,
			rescheduleFailedExec: true,
			forkPoint: true
		},
		receive:
		{
			advertiseAtomic: true,
			advertisePushOptions: true,
			autogc: true,
			certNonceSeed: 'string',
			certNonceSlop: 'string',
			fsckObjects: true,
			fsck:
			{
				skipList: 'string',
				'*': 'string'
			},
			keepAlive: 5,
			unpackLimit: 1,
			maxInputSize: 1,
			denyDeletes: true,
			denyDeleteCurrent: true,
			denyCurrentBranch: true,
			denyNonFastForwards: true,
			hideRefs: 'string',
			procReceiveRefs: 'string',
			updateServerInfo: true,
			shallowUpdate: true
		},
		remote:
		{
			pushDefault: 'string',
			'*':
			{
				url: 'string',
				pushurl: 'string',
				proxy: 'string',
				proxyAuthMethod: 'string',
				fetch: 'string',
				push: 'string',
				mirror: true,
				skipDefaultUpdate: true,
				skipFetchAll: true,
				receivepack: 'string',
				uploadpack: 'string',
				tagOpt: 'string',
				vcs: 'string',
				prune: true,
				pruneTags: true,
				promisor: true,
				partialclonefilter: 'string'
			}
		},
		remotes:
		{
			'*': 'string'
		},
		repack:
		{
			useDeltaBaseOffset: true,
			packKeptObjects: true,
			useDeltaIslands: true,
			writeBitmaps: true
			
		},
		rerere:
		{
			autoUpdate: true,
			enabled: true
		},
		reset:
		{
			quiet: true
		},
		sendemail:
		{
			identity: 'string',
			smtpEncryption: 'string',
			smtpssl: 'string',
			smtpsslcertpath: 'string',
			aliasesFile: 'string',
			aliasFileType: 'string',
			annotate: true,
			bcc: 'string',
			cc: 'string',
			ccCmd: 'string',
			chainReplyTo: true,
			confirm: 'string',
			envelopeSender: 'string',
			from: 'string',
			multiEdit: true,
			signedoffbycc: true,
			smtpPass: 'string',
			suppresscc: 'string',
			suppressFrom: 'string',
			to: 'string',
			tocmd: 'string',
			smtpDomain: 'string',
			smtpServer: 'string',
			smtpServerPort: 1,
			smtpServerOption: 'string',
			smtpUser: 'string',
			thread: true,
			transferEncoding: 'string',
			validate: true,
			xmailer: true,
			signedoffcc: true,
			smtpBatchSize: 1,
			smtpReloginDelay: 1,
			forbidSendmailVariables: 'string',
			'*':
			{
				'*': 'string'
			}
		},
		sequence:
		{
			editor: 'string'
		},
		showBranch:
		{
			default: 'string'
		},
		splitIndex:
		{
			maxPercentChange: 100,
			sharedIndexExpire: 'string'
		},
		ssh:
		{
			variant: 'string'
		},
		status:
		{
			relativePaths: true,
			short: true,
			branch: true,
			aheadBehind: true,
			displayCommentPrefix: true,
			renameLimit: 1,
			renames: 'string',
			showStash: true,
			showUntrackedFiles: 'string',
			submoduleSummary: 1
		},
		stash:
		{
			useBuiltin: true,
			showIncludeUntracked: true,
			showPatch: true,
			showStat: true
		},
		submodule:
		{
			active: 'string',
			recurse: true,
			fetchJobs: 1,
			alternateLocation: 'string',
			alternateErrorStrategy: 'string',
			'*':
			{
				url: 'string',
				update: 'string',
				branch: 'string',
				fetchRecurseSubmodules: true,
				ignore: 'string',
				active: true
				
			}
		},
		tag:
		{
			forceSignAnnotated: true,
			sort: 'string',
			gpgSign: true,
			umask: 'string'
			
		},
		tar:
		{
			umask: 'string'
		},
		trace2:
		{
			normalTarget: 'string',
			perfTarget: 'string',
			eventTarget: 'string',
			normalBrief: true,
			perfBrief: true,
			eventBrief: true,
			eventNesting: 1,
			configParams: 'string',
			envVars: 'string',
			destinationDebug: true,
			maxFiles: 1
		},
		transfer:
		{
			fsckObjects: 'string',
			hideRefs: 'string',
			unpackLimit: 100,
			advertiseSID: true
		},
		uploadarchive:
		{
			allowUnreachable: true
		},
		uploadpack:
		{
			hideRefs: 'string',
			allowTipSHA1InWant: true,
			allowReachableSHA1InWant: true,
			allowAnySHA1InWant: true,
			keepAlive: 5,
			packObjectsHook: 'string',
			allowFilter: true
		},
		uploadpackfilter:
		{
			allow: true,
			tree:
			{
				maxDepth: 1
			},
			'*':
			{
				allow: true
			}
		},
		uploadpack:
		{
			allowRefInWant: true
		},
		url:
		{
			'*':
			{
				insteadOf: 'string',
				pushInsteadOf: 'string'
			}
		},
		user:
		{
			name: 'string',
			email: 'string',
			useConfigOnly: true,
			signingKey: 'string'
		},
		author:
		{
			name: 'string',
			email: 'string'
		},
		committer:
		{
			name: 'string',
			email: 'string'
		},
		versionsort:
		{
			prereleaseSuffix: 'string',
			suffix: 'string'
		},
		web:
		{
			browser: 'string'
		},
		worktree:
		{
			guessRemote: true
		}
	};
}

module.exports = GitConfig;