|
| 1 | +package commit |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + git2go "github.com/libgit2/git2go/v31" |
| 6 | + "github.com/neel1996/gitconvex/global" |
| 7 | + "strings" |
| 8 | +) |
| 9 | + |
| 10 | +type Changes interface { |
| 11 | + Add() error |
| 12 | +} |
| 13 | + |
| 14 | +type changes struct { |
| 15 | + repo *git2go.Repository |
| 16 | + commitMessage []string |
| 17 | +} |
| 18 | + |
| 19 | +// Add commits the staged changes to the repo |
| 20 | +// Rewrites the repo index tree with the staged files to commit the changes |
| 21 | +func (c changes) Add() error { |
| 22 | + var ( |
| 23 | + headCommit *git2go.Commit |
| 24 | + formattedMessage string |
| 25 | + repo = c.repo |
| 26 | + ) |
| 27 | + |
| 28 | + signature, err := c.validateSignature(repo) |
| 29 | + if err != nil { |
| 30 | + return err |
| 31 | + } |
| 32 | + |
| 33 | + formattedMessage = c.formatCommitMessage() |
| 34 | + |
| 35 | + headCommit, headCommitErr := c.currentHeadCommit() |
| 36 | + if headCommitErr != nil { |
| 37 | + logger.Log(headCommitErr.Error(), global.StatusError) |
| 38 | + return headCommitErr |
| 39 | + } |
| 40 | + |
| 41 | + newTree, treeErr := c.treeForNewCommit(repo) |
| 42 | + if treeErr != nil { |
| 43 | + logger.Log(treeErr.Error(), global.StatusError) |
| 44 | + return treeErr |
| 45 | + } |
| 46 | + |
| 47 | + newCommitId, newCommitIdErr := c.createNewCommit(headCommit, repo, signature, formattedMessage, newTree) |
| 48 | + if newCommitIdErr != nil { |
| 49 | + logger.Log(newCommitIdErr.Error(), global.StatusError) |
| 50 | + return newCommitIdErr |
| 51 | + } |
| 52 | + |
| 53 | + setHeadErr := c.setHeadToNewCommit(repo, newCommitId, formattedMessage) |
| 54 | + if setHeadErr != nil { |
| 55 | + logger.Log(setHeadErr.Error(), global.StatusError) |
| 56 | + return setHeadErr |
| 57 | + } |
| 58 | + |
| 59 | + logger.Log(fmt.Sprintf("New commit %s created", newCommitId.String()), global.StatusInfo) |
| 60 | + return nil |
| 61 | +} |
| 62 | + |
| 63 | +func (c changes) validateSignature(repo *git2go.Repository) (*git2go.Signature, error) { |
| 64 | + signature, signatureErr := repo.DefaultSignature() |
| 65 | + if signatureErr != nil { |
| 66 | + logger.Log(fmt.Sprintf("Error occurred while fetching repo signature -> %s", signatureErr.Error()), global.StatusError) |
| 67 | + logger.Log("Setup you user name and email using `git config user.name and git config user.email`", global.StatusWarning) |
| 68 | + return nil, signatureErr |
| 69 | + } |
| 70 | + |
| 71 | + return signature, nil |
| 72 | +} |
| 73 | + |
| 74 | +func (c changes) setHeadToNewCommit(repo *git2go.Repository, newCommitId *git2go.Oid, formattedMessage string) error { |
| 75 | + if _, newCommitErr := repo.LookupCommit(newCommitId); newCommitErr != nil { |
| 76 | + logger.Log(newCommitErr.Error(), global.StatusError) |
| 77 | + return newCommitErr |
| 78 | + } |
| 79 | + |
| 80 | + head, headErr := repo.Head() |
| 81 | + if headErr != nil { |
| 82 | + logger.Log(headErr.Error(), global.StatusError) |
| 83 | + return headErr |
| 84 | + } |
| 85 | + |
| 86 | + _, newRefErr := head.SetTarget(newCommitId, formattedMessage) |
| 87 | + if newRefErr != nil { |
| 88 | + logger.Log(newRefErr.Error(), global.StatusError) |
| 89 | + return newRefErr |
| 90 | + } |
| 91 | + |
| 92 | + return nil |
| 93 | +} |
| 94 | + |
| 95 | +func (c changes) createNewCommit(headCommit *git2go.Commit, repo *git2go.Repository, signature *git2go.Signature, formattedMessage string, newTree *git2go.Tree) (*git2go.Oid, error) { |
| 96 | + var ( |
| 97 | + newCommitId *git2go.Oid |
| 98 | + newCommitIdErr error |
| 99 | + ) |
| 100 | + |
| 101 | + if headCommit == nil { |
| 102 | + newCommitId, newCommitIdErr = repo.CreateCommit("HEAD", signature, signature, formattedMessage, newTree) |
| 103 | + } else { |
| 104 | + newCommitId, newCommitIdErr = repo.CreateCommit("HEAD", signature, signature, formattedMessage, newTree, headCommit) |
| 105 | + } |
| 106 | + |
| 107 | + return newCommitId, newCommitIdErr |
| 108 | +} |
| 109 | + |
| 110 | +func (c changes) treeForNewCommit(repo *git2go.Repository) (*git2go.Tree, error) { |
| 111 | + treeId, indexErr := c.updateRepoIndex() |
| 112 | + if indexErr != nil { |
| 113 | + logger.Log(indexErr.Error(), global.StatusError) |
| 114 | + return nil, indexErr |
| 115 | + } |
| 116 | + |
| 117 | + newTree, newTreeErr := repo.LookupTree(treeId) |
| 118 | + if newTreeErr != nil { |
| 119 | + logger.Log(newTreeErr.Error(), global.StatusError) |
| 120 | + return nil, newTreeErr |
| 121 | + } |
| 122 | + return newTree, nil |
| 123 | +} |
| 124 | + |
| 125 | +func (c changes) updateRepoIndex() (*git2go.Oid, error) { |
| 126 | + repoIndex, indexErr := c.repo.Index() |
| 127 | + if indexErr != nil { |
| 128 | + return nil, indexErr |
| 129 | + } |
| 130 | + |
| 131 | + return repoIndex.WriteTree() |
| 132 | +} |
| 133 | + |
| 134 | +func (c changes) currentHeadCommit() (*git2go.Commit, error) { |
| 135 | + head, headErr := c.repo.Head() |
| 136 | + if headErr != nil { |
| 137 | + logger.Log("Repo has no HEAD. Proceeding with a NIL HEAD", global.StatusWarning) |
| 138 | + return nil, nil |
| 139 | + } |
| 140 | + |
| 141 | + return c.repo.LookupCommit(head.Target()) |
| 142 | +} |
| 143 | + |
| 144 | +func (c changes) formatCommitMessage() string { |
| 145 | + if len(c.commitMessage) > 0 { |
| 146 | + return strings.Join(c.commitMessage, "\n") |
| 147 | + } |
| 148 | + |
| 149 | + return strings.Join(c.commitMessage, "") |
| 150 | +} |
| 151 | + |
| 152 | +func NewCommitChanges(repo *git2go.Repository, message []string) Changes { |
| 153 | + return changes{ |
| 154 | + repo: repo, |
| 155 | + commitMessage: message, |
| 156 | + } |
| 157 | +} |
0 commit comments