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"
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
}
2020-12-05 15:13:59 -06:00
//export commitSigningCallback
2020-12-10 09:19:41 -06:00
func commitSigningCallback ( errorMessage * * C . char , _signature * C . git_buf , _signature_field * C . git_buf , _commit_content * C . char , handle unsafe . Pointer ) C . int {
data , ok := pointerHandles . Get ( handle ) . ( * rebaseOptionsData )
2020-08-18 11:25:31 -05:00
if ! ok {
panic ( "invalid sign payload" )
}
2020-12-10 09:19:41 -06:00
if data . options . CommitSigningCallback == nil {
2020-12-05 15:13:59 -06:00
return C . int ( ErrorCodePassthrough )
2020-08-18 11:25:31 -05:00
}
commitContent := C . GoString ( _commit_content )
2020-12-10 09:19:41 -06:00
signature , signatureField , err := data . options . CommitSigningCallback ( commitContent )
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
}
fillBuf := func ( bufData string , buf * C . git_buf ) error {
clen := C . size_t ( len ( bufData ) )
cstr := unsafe . Pointer ( C . CString ( bufData ) )
defer C . free ( cstr )
// libgit2 requires the contents of the buffer to be NULL-terminated.
// C.CString() guarantees that the returned buffer will be
// NULL-terminated, so we can safely copy the terminator.
if int ( C . git_buf_set ( buf , cstr , clen + 1 ) ) != 0 {
return errors . New ( "could not set buffer" )
}
return nil
}
if signatureField != "" {
err := fillBuf ( signatureField , _signature_field )
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
}
}
err = fillBuf ( signature , _signature )
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
}
2020-12-05 15:13:59 -06:00
return C . int ( ErrorCodeOK )
2020-08-18 11:25:31 -05:00
}
2016-08-06 23:40:59 -05:00
// RebaseOptions are used to tell the rebase machinery how to operate
2016-09-13 01:03:16 -05:00
type RebaseOptions struct {
2020-08-18 11:25:31 -05:00
Version uint
Quiet int
InMemory int
RewriteNotesRef string
MergeOptions MergeOptions
2020-12-05 09:23:44 -06:00
CheckoutOptions CheckoutOptions
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
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 {
Version : uint ( opts . version ) ,
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 ) ,
}
}
2020-12-10 09:19:41 -06:00
func populateRebaseOptions ( copts * C . git_rebase_options , opts * RebaseOptions , errorTarget * error ) * C . git_rebase_options {
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
2020-12-10 09:19:41 -06:00
if opts . CommitSigningCallback != nil {
data := & rebaseOptionsData {
options : opts ,
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
2020-12-10 09:19:41 -06:00
cOpts := populateRebaseOptions ( & C . git_rebase_options { } , opts , & 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
}
2020-12-05 15:13:59 -06:00
return newRebaseFromC ( ptr , 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
2020-12-10 09:19:41 -06:00
cOpts := populateRebaseOptions ( & C . git_rebase_options { } , opts , & 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
}
2020-12-05 15:13:59 -06:00
return newRebaseFromC ( ptr , 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
}
// 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
}
2020-12-05 15:13:59 -06:00
func newRebaseFromC ( ptr * C . git_rebase , opts * C . git_rebase_options ) * Rebase {
rebase := & Rebase { ptr : ptr , options : opts }
2016-08-06 23:40:59 -05:00
runtime . SetFinalizer ( rebase , ( * Rebase ) . Free )
return rebase
}