2016-08-06 23:40:59 -05:00
package git
/ *
# include < git2 . h >
2020-08-18 11:25:31 -05:00
2020-12-05 15:13:59 -06:00
extern void _go_git_populate_rebase_callbacks ( git_rebase_options * opts ) ;
2016-08-06 23:40:59 -05:00
* /
import "C"
import (
2017-07-08 13:58:08 -05:00
"errors"
2018-01-21 15:46:54 -06:00
"fmt"
2021-09-03 08:40:31 -05:00
"reflect"
2016-08-06 23:40:59 -05:00
"runtime"
"unsafe"
)
// RebaseOperationType is the type of rebase operation
type RebaseOperationType uint
const (
// RebaseOperationPick The given commit is to be cherry-picked. The client should commit the changes and continue if there are no conflicts.
RebaseOperationPick RebaseOperationType = C . GIT_REBASE_OPERATION_PICK
2018-01-21 15:46:08 -06:00
// RebaseOperationReword The given commit is to be cherry-picked, but the client should prompt the user to provide an updated commit message.
RebaseOperationReword RebaseOperationType = C . GIT_REBASE_OPERATION_REWORD
2016-08-06 23:40:59 -05:00
// RebaseOperationEdit The given commit is to be cherry-picked, but the client should stop to allow the user to edit the changes before committing them.
RebaseOperationEdit RebaseOperationType = C . GIT_REBASE_OPERATION_EDIT
// RebaseOperationSquash The given commit is to be squashed into the previous commit. The commit message will be merged with the previous message.
RebaseOperationSquash RebaseOperationType = C . GIT_REBASE_OPERATION_SQUASH
// RebaseOperationFixup No commit will be cherry-picked. The client should run the given command and (if successful) continue.
RebaseOperationFixup RebaseOperationType = C . GIT_REBASE_OPERATION_FIXUP
// RebaseOperationExec No commit will be cherry-picked. The client should run the given command and (if successful) continue.
RebaseOperationExec RebaseOperationType = C . GIT_REBASE_OPERATION_EXEC
)
2018-01-21 15:46:54 -06:00
func ( t RebaseOperationType ) String ( ) string {
switch t {
case RebaseOperationPick :
return "pick"
case RebaseOperationReword :
return "reword"
case RebaseOperationEdit :
return "edit"
case RebaseOperationSquash :
return "squash"
case RebaseOperationFixup :
return "fixup"
case RebaseOperationExec :
return "exec"
}
return fmt . Sprintf ( "RebaseOperationType(%d)" , t )
}
2017-07-08 13:58:08 -05:00
// Special value indicating that there is no currently active operation
var RebaseNoOperation uint = ^ uint ( 0 )
// Error returned if there is no current rebase operation
2017-10-01 23:04:08 -05:00
var ErrRebaseNoOperation = errors . New ( "no current rebase operation" )
2017-07-08 13:58:08 -05:00
2016-08-06 23:40:59 -05:00
// RebaseOperation describes a single instruction/operation to be performed during the rebase.
type RebaseOperation struct {
Type RebaseOperationType
2016-10-31 18:57:23 -05:00
Id * Oid
2016-08-06 23:40:59 -05:00
Exec string
}
2016-08-07 02:31:42 -05:00
func newRebaseOperationFromC ( c * C . git_rebase_operation ) * RebaseOperation {
2016-08-06 23:40:59 -05:00
operation := & RebaseOperation { }
operation . Type = RebaseOperationType ( c . _type )
2016-10-31 18:57:23 -05:00
operation . Id = newOidFromC ( & c . id )
2016-08-06 23:40:59 -05:00
operation . Exec = C . GoString ( c . exec )
return operation
}
2021-09-03 08:40:31 -05:00
//export commitCreateCallback
func commitCreateCallback (
errorMessage * * C . char ,
_out * C . git_oid ,
_author , _committer * C . git_signature ,
_message_encoding , _message * C . char ,
_tree * C . git_tree ,
_parent_count C . size_t ,
_parents * * C . git_commit ,
handle unsafe . Pointer ,
) C . int {
2020-12-10 09:19:41 -06:00
data , ok := pointerHandles . Get ( handle ) . ( * rebaseOptionsData )
2020-08-18 11:25:31 -05:00
if ! ok {
panic ( "invalid sign payload" )
}
2021-09-03 08:40:31 -05:00
if data . options . CommitCreateCallback == nil && data . options . CommitSigningCallback == nil {
2020-12-05 15:13:59 -06:00
return C . int ( ErrorCodePassthrough )
2020-08-18 11:25:31 -05:00
}
2021-09-03 08:40:31 -05:00
messageEncoding := MessageEncodingUTF8
if _message_encoding != nil {
messageEncoding = MessageEncoding ( C . GoString ( _message_encoding ) )
}
tree := & Tree {
Object : Object {
ptr : ( * C . git_object ) ( _tree ) ,
repo : data . repo ,
} ,
cast_ptr : _tree ,
}
2020-08-18 11:25:31 -05:00
2021-09-03 08:40:31 -05:00
var goParents [ ] * C . git_commit
if _parent_count > 0 {
hdr := reflect . SliceHeader {
Data : uintptr ( unsafe . Pointer ( _parents ) ) ,
Len : int ( _parent_count ) ,
Cap : int ( _parent_count ) ,
2020-12-10 09:19:41 -06:00
}
2021-09-03 08:40:31 -05:00
goParents = * ( * [ ] * C . git_commit ) ( unsafe . Pointer ( & hdr ) )
2020-08-18 11:25:31 -05:00
}
2021-09-03 08:40:31 -05:00
parents := make ( [ ] * Commit , int ( _parent_count ) )
for i , p := range goParents {
parents [ i ] = & Commit {
Object : Object {
ptr : ( * C . git_object ) ( p ) ,
repo : data . repo ,
} ,
cast_ptr : p ,
2020-08-18 11:25:31 -05:00
}
}
2021-09-03 08:40:31 -05:00
if data . options . CommitCreateCallback != nil {
oid , err := data . options . CommitCreateCallback (
newSignatureFromC ( _author ) ,
newSignatureFromC ( _committer ) ,
messageEncoding ,
C . GoString ( _message ) ,
tree ,
parents ... ,
)
if err != nil {
if data . errorTarget != nil {
* data . errorTarget = err
}
return setCallbackError ( errorMessage , err )
}
if oid == nil {
return C . int ( ErrorCodePassthrough )
}
* _out = * oid . toC ( )
} else if data . options . CommitSigningCallback != nil {
commitContent , err := data . repo . CreateCommitBuffer (
newSignatureFromC ( _author ) ,
newSignatureFromC ( _committer ) ,
messageEncoding ,
C . GoString ( _message ) ,
tree ,
parents ... ,
)
2020-08-18 11:25:31 -05:00
if err != nil {
2020-12-10 09:19:41 -06:00
if data . errorTarget != nil {
* data . errorTarget = err
}
2020-12-05 15:13:59 -06:00
return setCallbackError ( errorMessage , err )
2020-08-18 11:25:31 -05:00
}
2021-09-03 08:40:31 -05:00
signature , signatureField , err := data . options . CommitSigningCallback ( string ( commitContent ) )
if err != nil {
if data . errorTarget != nil {
* data . errorTarget = err
}
return setCallbackError ( errorMessage , err )
}
oid , err := data . repo . CreateCommitWithSignature ( string ( commitContent ) , signature , signatureField )
if err != nil {
if data . errorTarget != nil {
* data . errorTarget = err
}
return setCallbackError ( errorMessage , err )
2020-12-10 09:19:41 -06:00
}
2021-09-03 08:40:31 -05:00
* _out = * oid . toC ( )
2020-08-18 11:25:31 -05:00
}
2020-12-05 15:13:59 -06:00
return C . int ( ErrorCodeOK )
2020-08-18 11:25:31 -05:00
}
2021-09-03 08:40:31 -05:00
// RebaseOptions are used to tell the rebase machinery how to operate.
2016-09-13 01:03:16 -05:00
type RebaseOptions struct {
2021-09-03 08:40:31 -05:00
Quiet int
InMemory int
RewriteNotesRef string
MergeOptions MergeOptions
CheckoutOptions CheckoutOptions
// CommitCreateCallback is an optional callback that allows users to override
// commit creation when rebasing. If specified, users can create
// their own commit and provide the commit ID, which may be useful for
// signing commits or otherwise customizing the commit creation. If this
// callback returns a nil Oid, then the rebase will continue to create the
// commit.
CommitCreateCallback CommitCreateCallback
// Deprecated: CommitSigningCallback is an optional callback that will be
// called with the commit content, allowing a signature to be added to the
// rebase commit. This field is only used when rebasing. This callback is
// not invoked if a CommitCreateCallback is specified. CommitCreateCallback
// should be used instead of this.
2020-08-18 11:25:31 -05:00
CommitSigningCallback CommitSigningCallback
2016-09-13 01:03:16 -05:00
}
2020-12-10 09:19:41 -06:00
type rebaseOptionsData struct {
options * RebaseOptions
2021-09-03 08:40:31 -05:00
repo * Repository
2020-12-10 09:19:41 -06:00
errorTarget * error
}
2016-09-13 01:03:16 -05:00
// DefaultRebaseOptions returns a RebaseOptions with default values.
func DefaultRebaseOptions ( ) ( RebaseOptions , error ) {
opts := C . git_rebase_options { }
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
2020-10-22 08:18:11 -05:00
ecode := C . git_rebase_options_init ( & opts , C . GIT_REBASE_OPTIONS_VERSION )
2016-09-13 01:03:16 -05:00
if ecode < 0 {
return RebaseOptions { } , MakeGitError ( ecode )
}
return rebaseOptionsFromC ( & opts ) , nil
}
func rebaseOptionsFromC ( opts * C . git_rebase_options ) RebaseOptions {
return RebaseOptions {
Quiet : int ( opts . quiet ) ,
InMemory : int ( opts . inmemory ) ,
RewriteNotesRef : C . GoString ( opts . rewrite_notes_ref ) ,
MergeOptions : mergeOptionsFromC ( & opts . merge_options ) ,
CheckoutOptions : checkoutOptionsFromC ( & opts . checkout_options ) ,
}
}
2021-09-03 08:40:31 -05:00
func populateRebaseOptions ( copts * C . git_rebase_options , opts * RebaseOptions , repo * Repository , errorTarget * error ) * C . git_rebase_options {
2020-12-10 09:19:41 -06:00
C . git_rebase_options_init ( copts , C . GIT_REBASE_OPTIONS_VERSION )
if opts == nil {
2016-09-13 01:03:16 -05:00
return nil
}
2020-12-05 15:13:59 -06:00
2020-12-10 09:19:41 -06:00
copts . quiet = C . int ( opts . Quiet )
copts . inmemory = C . int ( opts . InMemory )
copts . rewrite_notes_ref = mapEmptyStringToNull ( opts . RewriteNotesRef )
populateMergeOptions ( & copts . merge_options , & opts . MergeOptions )
populateCheckoutOptions ( & copts . checkout_options , & opts . CheckoutOptions , errorTarget )
2020-08-18 11:25:31 -05:00
2021-09-03 08:40:31 -05:00
if opts . CommitCreateCallback != nil || opts . CommitSigningCallback != nil {
2020-12-10 09:19:41 -06:00
data := & rebaseOptionsData {
options : opts ,
2021-09-03 08:40:31 -05:00
repo : repo ,
2020-12-10 09:19:41 -06:00
errorTarget : errorTarget ,
}
C . _go_git_populate_rebase_callbacks ( copts )
copts . payload = pointerHandles . Track ( data )
2020-08-18 11:25:31 -05:00
}
2020-12-10 09:19:41 -06:00
return copts
2020-12-05 15:13:59 -06:00
}
2020-12-10 09:19:41 -06:00
func freeRebaseOptions ( copts * C . git_rebase_options ) {
if copts == nil {
2020-12-05 15:13:59 -06:00
return
}
2020-12-10 09:19:41 -06:00
C . free ( unsafe . Pointer ( copts . rewrite_notes_ref ) )
freeMergeOptions ( & copts . merge_options )
freeCheckoutOptions ( & copts . checkout_options )
if copts . payload != nil {
pointerHandles . Untrack ( copts . payload )
2020-12-05 15:13:59 -06:00
}
2016-09-13 01:03:16 -05:00
}
2016-08-06 23:40:59 -05:00
2016-10-31 18:57:23 -05:00
func mapEmptyStringToNull ( ref string ) * C . char {
2016-09-13 23:42:58 -05:00
if ref == "" {
return nil
}
return C . CString ( ref )
}
2016-10-31 18:57:23 -05:00
// Rebase is the struct representing a Rebase object.
2016-08-06 23:40:59 -05:00
type Rebase struct {
2021-09-05 15:59:36 -05:00
doNotCompare
2020-12-05 15:13:59 -06:00
ptr * C . git_rebase
r * Repository
options * C . git_rebase_options
2016-08-06 23:40:59 -05:00
}
2016-10-31 18:57:23 -05:00
// InitRebase initializes a rebase operation to rebase the changes in branch relative to upstream onto another branch.
func ( r * Repository ) InitRebase ( branch * AnnotatedCommit , upstream * AnnotatedCommit , onto * AnnotatedCommit , opts * RebaseOptions ) ( * Rebase , error ) {
2016-08-06 23:40:59 -05:00
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
if branch == nil {
branch = & AnnotatedCommit { ptr : nil }
}
if upstream == nil {
upstream = & AnnotatedCommit { ptr : nil }
}
if onto == nil {
onto = & AnnotatedCommit { ptr : nil }
}
var ptr * C . git_rebase
2020-12-05 15:13:59 -06:00
var err error
2021-09-03 08:40:31 -05:00
cOpts := populateRebaseOptions ( & C . git_rebase_options { } , opts , r , & err )
2020-12-05 15:13:59 -06:00
ret := C . git_rebase_init ( & ptr , r . ptr , branch . ptr , upstream . ptr , onto . ptr , cOpts )
2017-07-08 09:07:51 -05:00
runtime . KeepAlive ( branch )
runtime . KeepAlive ( upstream )
runtime . KeepAlive ( onto )
2020-12-05 15:13:59 -06:00
if ret == C . int ( ErrorCodeUser ) && err != nil {
freeRebaseOptions ( cOpts )
return nil , err
}
if ret < 0 {
freeRebaseOptions ( cOpts )
return nil , MakeGitError ( ret )
2016-08-06 23:40:59 -05:00
}
2022-02-24 07:27:26 -06:00
return newRebaseFromC ( ptr , r , cOpts ) , nil
2016-08-06 23:40:59 -05:00
}
2016-10-31 18:57:23 -05:00
// OpenRebase opens an existing rebase that was previously started by either an invocation of InitRebase or by another client.
func ( r * Repository ) OpenRebase ( opts * RebaseOptions ) ( * Rebase , error ) {
2016-09-05 23:15:10 -05:00
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
var ptr * C . git_rebase
2020-12-05 15:13:59 -06:00
var err error
2021-09-03 08:40:31 -05:00
cOpts := populateRebaseOptions ( & C . git_rebase_options { } , opts , r , & err )
2020-12-05 15:13:59 -06:00
ret := C . git_rebase_open ( & ptr , r . ptr , cOpts )
2017-07-08 09:07:51 -05:00
runtime . KeepAlive ( r )
2020-12-05 15:13:59 -06:00
if ret == C . int ( ErrorCodeUser ) && err != nil {
freeRebaseOptions ( cOpts )
return nil , err
}
if ret < 0 {
freeRebaseOptions ( cOpts )
return nil , MakeGitError ( ret )
2016-09-05 23:15:10 -05:00
}
2022-02-24 07:27:26 -06:00
return newRebaseFromC ( ptr , r , cOpts ) , nil
2016-09-05 23:15:10 -05:00
}
2016-08-07 02:31:42 -05:00
// OperationAt gets the rebase operation specified by the given index.
func ( rebase * Rebase ) OperationAt ( index uint ) * RebaseOperation {
operation := C . git_rebase_operation_byindex ( rebase . ptr , C . size_t ( index ) )
2017-07-08 09:07:51 -05:00
2016-08-07 02:31:42 -05:00
return newRebaseOperationFromC ( operation )
}
2017-07-08 13:58:08 -05:00
// CurrentOperationIndex gets the index of the rebase operation that is
// currently being applied. There is also an error returned for API
// compatibility.
2016-10-31 18:57:23 -05:00
func ( rebase * Rebase ) CurrentOperationIndex ( ) ( uint , error ) {
2016-11-23 00:10:59 -06:00
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
2017-07-08 13:58:08 -05:00
var err error
operationIndex := uint ( C . git_rebase_operation_current ( rebase . ptr ) )
runtime . KeepAlive ( rebase )
if operationIndex == RebaseNoOperation {
err = ErrRebaseNoOperation
2016-10-31 18:57:23 -05:00
}
2016-11-23 00:10:59 -06:00
2017-07-08 13:58:08 -05:00
return uint ( operationIndex ) , err
2016-08-07 02:31:42 -05:00
}
// OperationCount gets the count of rebase operations that are to be applied.
func ( rebase * Rebase ) OperationCount ( ) uint {
2017-07-08 09:07:51 -05:00
ret := uint ( C . git_rebase_operation_entrycount ( rebase . ptr ) )
runtime . KeepAlive ( rebase )
return ret
2016-08-07 02:31:42 -05:00
}
2016-08-06 23:40:59 -05:00
// Next performs the next rebase operation and returns the information about it.
2016-10-31 18:57:23 -05:00
// If the operation is one that applies a patch (which is any operation except RebaseOperationExec)
2016-08-06 23:40:59 -05:00
// then the patch will be applied and the index and working directory will be updated with the changes.
// If there are conflicts, you will need to address those before committing the changes.
func ( rebase * Rebase ) Next ( ) ( * RebaseOperation , error ) {
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
var ptr * C . git_rebase_operation
err := C . git_rebase_next ( & ptr , rebase . ptr )
2017-07-08 09:07:51 -05:00
runtime . KeepAlive ( rebase )
2016-08-06 23:40:59 -05:00
if err < 0 {
return nil , MakeGitError ( err )
}
2016-08-07 02:31:42 -05:00
return newRebaseOperationFromC ( ptr ) , nil
2016-08-06 23:40:59 -05:00
}
2022-02-24 07:27:26 -06:00
// InmemoryIndex gets the index produced by the last operation, which is the
// result of `Next()` and which will be committed by the next invocation of
// `Commit()`. This is useful for resolving conflicts in an in-memory rebase
// before committing them.
//
// This is only applicable for in-memory rebases; for rebases within a working
// directory, the changes were applied to the repository's index.
func ( rebase * Rebase ) InmemoryIndex ( ) ( * Index , error ) {
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
var ptr * C . git_index
err := C . git_rebase_inmemory_index ( & ptr , rebase . ptr )
runtime . KeepAlive ( rebase )
if err < 0 {
return nil , MakeGitError ( err )
}
return newIndexFromC ( ptr , rebase . r ) , nil
}
2016-08-06 23:40:59 -05:00
// Commit commits the current patch.
2016-10-31 18:57:23 -05:00
// You must have resolved any conflicts that were introduced during the patch application from the Next() invocation.
2016-08-06 23:40:59 -05:00
func ( rebase * Rebase ) Commit ( ID * Oid , author , committer * Signature , message string ) error {
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
authorSig , err := author . toC ( )
if err != nil {
return err
}
defer C . git_signature_free ( authorSig )
committerSig , err := committer . toC ( )
if err != nil {
return err
}
2016-10-31 18:57:23 -05:00
defer C . git_signature_free ( committerSig )
2016-08-06 23:40:59 -05:00
cmsg := C . CString ( message )
defer C . free ( unsafe . Pointer ( cmsg ) )
cerr := C . git_rebase_commit ( ID . toC ( ) , rebase . ptr , authorSig , committerSig , nil , cmsg )
2017-07-08 09:07:51 -05:00
runtime . KeepAlive ( ID )
runtime . KeepAlive ( rebase )
2016-08-06 23:40:59 -05:00
if cerr < 0 {
return MakeGitError ( cerr )
}
return nil
}
// Finish finishes a rebase that is currently in progress once all patches have been applied.
func ( rebase * Rebase ) Finish ( ) error {
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
err := C . git_rebase_finish ( rebase . ptr , nil )
2017-07-08 09:07:51 -05:00
runtime . KeepAlive ( rebase )
2016-08-06 23:40:59 -05:00
if err < 0 {
return MakeGitError ( err )
}
return nil
}
2016-08-07 02:31:42 -05:00
// Abort aborts a rebase that is currently in progress, resetting the repository and working directory to their state before rebase began.
func ( rebase * Rebase ) Abort ( ) error {
runtime . LockOSThread ( )
defer runtime . UnlockOSThread ( )
err := C . git_rebase_abort ( rebase . ptr )
2017-07-08 09:07:51 -05:00
runtime . KeepAlive ( rebase )
2016-08-07 02:31:42 -05:00
if err < 0 {
return MakeGitError ( err )
}
return nil
2016-08-07 01:33:06 -05:00
}
2016-10-31 18:57:23 -05:00
// Free frees the Rebase object.
2020-12-05 15:13:59 -06:00
func ( r * Rebase ) Free ( ) {
runtime . SetFinalizer ( r , nil )
C . git_rebase_free ( r . ptr )
freeRebaseOptions ( r . options )
2016-08-06 23:40:59 -05:00
}
2022-02-24 07:27:26 -06:00
func newRebaseFromC ( ptr * C . git_rebase , repo * Repository , opts * C . git_rebase_options ) * Rebase {
rebase := & Rebase { ptr : ptr , r : repo , options : opts }
2016-08-06 23:40:59 -05:00
runtime . SetFinalizer ( rebase , ( * Rebase ) . Free )
return rebase
}