public PromotionResult promote(User user, ContextSet context, Predicate<String> nextGroupPermissionChecker, @Nullable Sender sender, boolean addToFirst) {
        if (getSize() <= 1) {
            throw new IllegalStateException("Track contains one or fewer groups, unable to promote");
        }

        // find all groups that are inherited by the user in the exact contexts given and applicable to this track
        List<InheritanceNode> nodes = user.normalData().inheritanceNodesInContext(context).stream()
                .filter(Node::getValue)
                .filter(node -> containsGroup(node.getGroupName()))
                .distinct()
                .collect(Collectors.toList());

        if (nodes.isEmpty()) {
            if (!addToFirst) {
                return PromotionResults.addedToFirst(null);
            }

            String first = getGroups().get(0);

            Group nextGroup = this.plugin.getGroupManager().getIfLoaded(first);
            if (nextGroup == null) {
                return PromotionResults.malformedTrack(first);
            }

            if (!nextGroupPermissionChecker.test(nextGroup.getName())) {
                return PromotionResults.undefinedFailure();
            }

            user.setNode(DataType.NORMAL, Inheritance.builder(nextGroup.getName()).withContext(context).build(), true);
            this.plugin.getEventDispatcher().dispatchUserPromote(user, this, null, first, sender);
            return PromotionResults.addedToFirst(first);
        }

        if (nodes.size() != 1) {
            return PromotionResults.ambiguousCall();
        }

        InheritanceNode oldNode = nodes.get(0);
        String old = oldNode.getGroupName();
        String next = getNext(old);

        if (next == null) {
            return PromotionResults.endOfTrack();
        }

        Group nextGroup = this.plugin.getGroupManager().getIfLoaded(next);
        if (nextGroup == null) {
            return PromotionResults.malformedTrack(next);
        }

        if (!nextGroupPermissionChecker.test(nextGroup.getName())) {
            return PromotionResults.undefinedFailure();
        }

        user.unsetNode(DataType.NORMAL, oldNode);
        user.setNode(DataType.NORMAL, oldNode.toBuilder().group(nextGroup.getName()).build(), true);

        if (context.isEmpty() && user.getPrimaryGroup().getStoredValue().orElse(GroupManager.DEFAULT_GROUP_NAME).equalsIgnoreCase(old)) {
            user.getPrimaryGroup().setStoredValue(nextGroup.getName());
        }

        this.plugin.getEventDispatcher().dispatchUserPromote(user, this, old, nextGroup.getName(), sender);
        return PromotionResults.success(old, nextGroup.getName());
    }
Clear all of the groups within this track
common/src/main/java/me/lucko/luckperms/common/model/Track.java