code: package com.beijunyi.parallelgit.filesystem.commands;
  
  import java.io.IOException;
  import java.util.List;
  import javax.annotation.Nonnull;
  import javax.annotation.Nullable;
  
  import com.beijunyi.parallelgit.filesystem.GfsStatusProvider;
  import com.beijunyi.parallelgit.filesystem.GitFileSystem;
  import com.beijunyi.parallelgit.filesystem.exceptions.UnsuccessfulOperationException;
  import com.beijunyi.parallelgit.filesystem.merge.MergeNote;
  import com.beijunyi.parallelgit.utils.BranchUtils;
  import com.beijunyi.parallelgit.utils.CommitUtils;
  import org.eclipse.jgit.lib.AnyObjectId;
  import org.eclipse.jgit.lib.ObjectId;
  import org.eclipse.jgit.lib.PersonIdent;
  import org.eclipse.jgit.revwalk.RevCommit;
  
  import static java.util.Arrays.asList;
  import static java.util.Collections.*;
  
  public class GfsCommit extends GfsCommand<GfsCommit.Result> {
  
    private PersonIdent author;
    private PersonIdent committer;
    private String message;
    private List<? extends AnyObjectId> parents;
    private boolean amend = false;
    private boolean allowEmpty = false;
  
    public GfsCommit(GitFileSystem gfs) {
      super(gfs);
    }
  
    @Nonnull
    @Override
    protected Result doExecute(GfsStatusProvider.Update update) throws IOException {
      prepareMessage();
      prepareCommitter();
      prepareAuthor();
      prepareParents();
      ObjectId resultTree = gfs.flush();
      gfs.updateOrigin(resultTree);
      if(!allowEmpty && !amend && isSameAsParent(resultTree))
        return Result.noChange();
      RevCommit resultCommit = CommitUtils.createCommit(message, resultTree, author, committer, parents, repo);
      updateStatus(update, resultCommit);
      return Result.success(resultCommit);
    }
  
    @Nonnull
    public GfsCommit author(@Nullable PersonIdent author) {
      this.author = author;
      return this;
    }
  
    @Nonnull
    public GfsCommit committer(@Nullable PersonIdent committer) {
      this.committer = committer;
      return this;
    }
  
    @Nonnull
    public GfsCommit message(@Nullable String message) {
      this.message = message;
      return this;
    }
  
    @Nonnull
    public GfsCommit amend(boolean amend) {
      this.amend = amend;
      return this;
    }
  
    @Nonnull
    public GfsCommit allowEmpty(boolean allowEmpty) {
      this.allowEmpty = allowEmpty;
      return this;
    }
  
    private void prepareMessage() {
      if(message == null) {
        MergeNote note = status.mergeNote();
        message = note != null ? note.getMessage() : "";
      }
    }
  
    private void prepareCommitter() {
      if(committer == null) committer = new PersonIdent(repo);
    }
  
    private void prepareAuthor() {
      if(author == null) {
        if(!amend)
          author = committer;
        else
          author = status.commit().getAuthorIdent();
      }
    }
  
    private void prepareParents() {
      if(parents == null) {
        if(!amend) {
          MergeNote mergeNote = status.mergeNote();
          if(mergeNote != null && mergeNote.getSource() != null) {
            parents = asList(status.commit(), mergeNote.getSource());
          } else if(status.isInitialized()) {
            parents = singletonList(status.commit());
          } else {
            parents = emptyList();
          }
        } else {
          parents = asList(status.commit().getParents());
        }
      }
    }
  
    private boolean isSameAsParent(AnyObjectId newTree) {
      return status.isInitialized() && status.commit().getTree().equals(newTree);
    }
  
    private void updateStatus(GfsStatusProvider.Update update, RevCommit newHead) throws IOException {
      if(status.isAttached()) {
        MergeNote mergeNote = status.mergeNote();
        if(!amend) {
          if(mergeNote != null) {
            BranchUtils.mergeCommit(status.branch(), newHead, repo);
          } else if(status.isInitialized()) {
            BranchUtils.newCommit(status.branch(), newHead, repo);
          } else {
            BranchUtils.initBranch(status.branch(), newHead, repo);
          }
        } else {
          BranchUtils.amendCommit(status.branch(), newHead, repo);
        }
      }
      update.commit(newHead);
      update.clearMergeNote();
    }
  
    public enum Status {
      COMMITTED,
      NO_CHANGE
    }
  
    public static class Result implements GfsCommandResult {
  
      private final Status status;
      private final RevCommit commit;
  
      private Result(Status status, @Nullable RevCommit commit) {
        this.status = status;
        this.commit = commit;
      }
  
      @Nonnull
      public static Result success(RevCommit commit) {
        return new Result(Status.COMMITTED, commit);
      }
  
      @Nonnull
      public static Result noChange() {
        return new Result(Status.NO_CHANGE, null);
      }
  
      @Override
      public boolean isSuccessful() {
        return commit != null;
      }
  
      @Nonnull
      public RevCommit getCommit() {
        if(commit == null) throw new UnsuccessfulOperationException();
        return commit;
      }
  
      @Nonnull
      public Status getStatus() {
        return status;
      }
    }
  
  }
  