docs: @Override
      public void execute() throws MojoExecutionException, MojoFailureException {
          validateConfiguration(preHotfixGoals, postHotfixGoals);
  
          try {
              // check uncommitted changes
              checkUncommittedChanges();
  
              String hotfixBranchName = null;
              if (settings.isInteractiveMode()) {
                  hotfixBranchName = promptBranchName();
              } else if (StringUtils.isNotBlank(hotfixVersion)) {
                  final String branch = gitFlowConfig.getHotfixBranchPrefix()
                          + hotfixVersion;
                  if (!gitCheckBranchExists(branch)) {
                      throw new MojoFailureException(
                              "Hotfix branch with name '" + branch
                                      + "' doesn't exist. Cannot finish hotfix.");
                  }
                  hotfixBranchName = branch;
              }
  
              if (StringUtils.isBlank(hotfixBranchName)) {
                  throw new MojoFailureException(
                          "Hotfix branch name to finish is blank.");
              }
  
              // support branch hotfix
              String supportBranchName = null;
              boolean supportHotfix = hotfixBranchName
                      .startsWith(gitFlowConfig.getHotfixBranchPrefix()
                              + gitFlowConfig.getSupportBranchPrefix());
              // get support branch name w/o version part
              if (supportHotfix) {
                  supportBranchName = hotfixBranchName.substring(
                          gitFlowConfig.getHotfixBranchPrefix().length());
                  supportBranchName = supportBranchName.substring(0,
                          supportBranchName.lastIndexOf('/'));
              }
  
              // fetch and check remote
              if (fetchRemote) {
                  gitFetchRemoteAndCompare(hotfixBranchName);
  
                  if (supportBranchName != null) {
                      gitFetchRemoteAndCompare(supportBranchName);
                  } else {
                      if (notSameProdDevName()) {
                          gitFetchRemoteAndCreate(gitFlowConfig.getDevelopmentBranch());
                          gitFetchRemoteAndCompare(gitFlowConfig.getDevelopmentBranch());
                      }
                      gitFetchRemoteAndCreate(gitFlowConfig.getProductionBranch());
                      gitFetchRemoteAndCompare(gitFlowConfig.getProductionBranch());
                  }
              }
  
              // git checkout hotfix/...
              gitCheckout(hotfixBranchName);
  
              if (!skipTestProject) {
                  // mvn clean test
                  mvnCleanTest();
              }
  
              // maven goals before merge
              if (StringUtils.isNotBlank(preHotfixGoals)) {
                  mvnRun(preHotfixGoals);
              }
  
              String currentHotfixVersion = getCurrentProjectVersion();
              if (useSnapshotInHotfix && ArtifactUtils.isSnapshot(currentHotfixVersion)) {
                  String commitVersion = currentHotfixVersion.replace("-" + Artifact.SNAPSHOT_VERSION, "");
  
                  mvnSetVersions(commitVersion);
  
                  Map<String, String> properties = new HashMap<String, String>();
                  properties.put("version", commitVersion);
  
                  gitCommit(commitMessages.getHotfixFinishMessage(), properties);
              }
  
              if (supportBranchName != null) {
                  gitCheckout(supportBranchName);
                  // git merge --no-ff hotfix/...
                  gitMergeNoff(hotfixBranchName);
              } else if (!skipMergeProdBranch) {
                  // git checkout master
                  gitCheckout(gitFlowConfig.getProductionBranch());
                  // git merge --no-ff hotfix/...
                  gitMergeNoff(hotfixBranchName);
              }
  
              final String currentVersion = getCurrentProjectVersion();
  
              if (!skipTag) {
                  String tagVersion = currentVersion;
                  if ((tychoBuild || useSnapshotInHotfix) && ArtifactUtils.isSnapshot(tagVersion)) {
                      tagVersion = tagVersion
                              .replace("-" + Artifact.SNAPSHOT_VERSION, "");
                  }
  
                  Map<String, String> properties = new HashMap<String, String>();
                  properties.put("version", tagVersion);
  
                  // git tag -a ...
                  gitTag(gitFlowConfig.getVersionTagPrefix() + tagVersion,
                          commitMessages.getTagHotfixMessage(), gpgSignTag, properties);
              }
  
              if (skipMergeProdBranch && (supportBranchName == null)) {
                  // switch to production branch so hotfix branch can be deleted
                  gitCheckout(gitFlowConfig.getProductionBranch());
              }
  
              // maven goals after merge
              if (StringUtils.isNotBlank(postHotfixGoals)) {
                  mvnRun(postHotfixGoals);
              }
  
              // check whether release branch exists
              // git for-each-ref --count=1 --format="%(refname:short)"
              // refs/heads/release/*
              final String releaseBranch = gitFindBranches(
                      gitFlowConfig.getReleaseBranchPrefix(), true);
  
              if (supportBranchName == null) {
                  // if release branch exists merge hotfix changes into it
                  if (StringUtils.isNotBlank(releaseBranch)) {
                      // git checkout release
                      gitCheckout(releaseBranch);
                      String releaseBranchVersion = getCurrentProjectVersion();
  
                      if (!currentVersion.equals(releaseBranchVersion)) {
                          // set version to avoid merge conflict
                          mvnSetVersions(currentVersion);
                          gitCommit(commitMessages.getUpdateReleaseToAvoidConflictsMessage());
                      }
  
                      // git merge --no-ff hotfix/...
                      gitMergeNoff(hotfixBranchName);
  
                      if (!currentVersion.equals(releaseBranchVersion)) {
                          mvnSetVersions(releaseBranchVersion);
                          gitCommit(commitMessages.getUpdateReleaseBackPreMergeStateMessage());
                      }
                  } else if (!skipMergeDevBranch) {
                      GitFlowVersionInfo developVersionInfo = new GitFlowVersionInfo(
                              currentVersion);
                      if (notSameProdDevName()) {
                          // git checkout develop
                          gitCheckout(gitFlowConfig.getDevelopmentBranch());
  
                          developVersionInfo = new GitFlowVersionInfo(getCurrentProjectVersion());
  
                          // set version to avoid merge conflict
                          mvnSetVersions(currentVersion);
                          gitCommit(commitMessages.getHotfixVersionUpdateMessage());
  
                          // git merge --no-ff hotfix/...
                          gitMergeNoff(hotfixBranchName);
  
                          // which version to increment
                          GitFlowVersionInfo hotfixVersionInfo = new GitFlowVersionInfo(
                                  currentVersion);
                          if (developVersionInfo
                                  .compareTo(hotfixVersionInfo) < 0) {
                              developVersionInfo = hotfixVersionInfo;
                          }
                      }
  
                      // get next snapshot version
                      final String nextSnapshotVersion = developVersionInfo.getSnapshotVersionString();
  
                      if (StringUtils.isBlank(nextSnapshotVersion)) {
                          throw new MojoFailureException(
                                  "Next snapshot version is blank.");
                      }
  
                      // mvn versions:set -DnewVersion=...
                      // -DgenerateBackupPoms=false
                      mvnSetVersions(nextSnapshotVersion);
  
                      Map<String, String> properties = new HashMap<String, String>();
                      properties.put("version", nextSnapshotVersion);
  
                      // git commit -a -m updating for next development version
                      gitCommit(commitMessages.getHotfixFinishMessage(),
                              properties);
                  }
              }
  
              if (installProject) {
                  // mvn clean install
                  mvnCleanInstall();
              }
  
              if (pushRemote) {
                  if (supportBranchName != null) {
                      gitPush(supportBranchName, !skipTag);
                  } else {
                      gitPush(gitFlowConfig.getProductionBranch(), !skipTag);
  
                      if (StringUtils.isNotBlank(releaseBranch)) {
                          gitPush(releaseBranch, !skipTag);
                      } else if (StringUtils.isBlank(releaseBranch)
                              && notSameProdDevName()) { // if no release branch
                          gitPush(gitFlowConfig.getDevelopmentBranch(), !skipTag);
                      }
                  }
  
                  if (!keepBranch) {
                      gitPushDelete(hotfixBranchName);
                  }
              }
  
              if (!keepBranch) {
                  if (skipMergeProdBranch){
                      //force delete as upstream merge is skipped
                      gitBranchDeleteForce(hotfixBranchName);
                  } else {
                      // git branch -d hotfix/...
                      gitBranchDelete(hotfixBranchName);
                  }
              }
          } catch (Exception e) {
              throw new MojoFailureException("hotfix-finish", e);
          }
      }