src/vs/base/node/pfs.ts/move
async function move(source: string, target: string): Promise<void> {
	if (source === target) {
		return;  // simulate node.js behaviour here and do a no-op if paths match
	}

	// We have been updating `mtime` for move operations for files since the
	// beginning for reasons that are no longer quite clear, but changing
	// this could be risky as well. As such, trying to reason about it:
	// It is very common as developer to have file watchers enabled that watch
	// the current workspace for changes. Updating the `mtime` might make it
	// easier for these watchers to recognize an actual change. Since changing
	// a source code file also updates the `mtime`, moving a file should do so
	// as well because conceptually it is a change of a similar category.
	async function updateMtime(path: string): Promise<void> {
		try {
			const stat = await Promises.lstat(path);
			if (stat.isDirectory() || stat.isSymbolicLink()) {
				return; // only for files
			}

			await Promises.utimes(path, stat.atime, new Date());
		} catch (error) {
			// Ignore any error
		}
	}

	try {
		await Promises.rename(source, target);
		await updateMtime(target);
	} catch (error) {

		// In two cases we fallback to classic copy and delete:
		//
		// 1.) The EXDEV error indicates that source and target are on different devices
		// In this case, fallback to using a copy() operation as there is no way to
		// rename() between different devices.
		//
		// 2.) The user tries to rename a file/folder that ends with a dot. This is not
		// really possible to move then, at least on UNC devices.
		if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || source.endsWith('.')) {
			await copy(source, target, { preserveSymlinks: false /* copying to another device */ });
			await rimraf(source, RimRafMode.MOVE);
			await updateMtime(target);
		} else {
			throw error;
		}
	}
}