2015-09-21 06:50:57 -05:00
|
|
|
package git
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <git2.h>
|
|
|
|
|
2020-12-05 18:23:04 -06:00
|
|
|
extern void _go_git_populate_stash_apply_callbacks(git_stash_apply_options *opts);
|
2015-09-21 06:50:57 -05:00
|
|
|
extern int _go_git_stash_foreach(git_repository *repo, void *payload);
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
import (
|
|
|
|
"runtime"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
// StashFlag are flags that affect the stash save operation.
|
|
|
|
type StashFlag int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// StashDefault represents no option, default.
|
|
|
|
StashDefault StashFlag = C.GIT_STASH_DEFAULT
|
|
|
|
|
|
|
|
// StashKeepIndex leaves all changes already added to the
|
|
|
|
// index intact in the working directory.
|
|
|
|
StashKeepIndex StashFlag = C.GIT_STASH_KEEP_INDEX
|
|
|
|
|
|
|
|
// StashIncludeUntracked means all untracked files are also
|
|
|
|
// stashed and then cleaned up from the working directory.
|
|
|
|
StashIncludeUntracked StashFlag = C.GIT_STASH_INCLUDE_UNTRACKED
|
|
|
|
|
|
|
|
// StashIncludeIgnored means all ignored files are also
|
|
|
|
// stashed and then cleaned up from the working directory.
|
|
|
|
StashIncludeIgnored StashFlag = C.GIT_STASH_INCLUDE_IGNORED
|
|
|
|
)
|
|
|
|
|
|
|
|
// StashCollection represents the possible operations that can be
|
|
|
|
// performed on the collection of stashes for a repository.
|
|
|
|
type StashCollection struct {
|
2021-09-05 16:00:24 -05:00
|
|
|
doNotCompare
|
2015-09-21 06:50:57 -05:00
|
|
|
repo *Repository
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save saves the local modifications to a new stash.
|
|
|
|
//
|
|
|
|
// Stasher is the identity of the person performing the stashing.
|
|
|
|
// Message is the optional description along with the stashed state.
|
|
|
|
// Flags control the stashing process and are given as bitwise OR.
|
|
|
|
func (c *StashCollection) Save(
|
|
|
|
stasher *Signature, message string, flags StashFlag) (*Oid, error) {
|
|
|
|
|
|
|
|
oid := new(Oid)
|
|
|
|
|
|
|
|
stasherC, err := stasher.toC()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer C.git_signature_free(stasherC)
|
|
|
|
|
|
|
|
messageC := C.CString(message)
|
|
|
|
defer C.free(unsafe.Pointer(messageC))
|
|
|
|
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
ret := C.git_stash_save(
|
|
|
|
oid.toC(), c.repo.ptr,
|
2016-02-20 06:58:48 -06:00
|
|
|
stasherC, messageC, C.uint32_t(flags))
|
2017-07-08 09:07:51 -05:00
|
|
|
runtime.KeepAlive(c)
|
2015-09-21 06:50:57 -05:00
|
|
|
if ret < 0 {
|
|
|
|
return nil, MakeGitError(ret)
|
|
|
|
}
|
|
|
|
return oid, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StashApplyFlag are flags that affect the stash apply operation.
|
|
|
|
type StashApplyFlag int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// StashApplyDefault is the default.
|
|
|
|
StashApplyDefault StashApplyFlag = C.GIT_STASH_APPLY_DEFAULT
|
|
|
|
|
|
|
|
// StashApplyReinstateIndex will try to reinstate not only the
|
|
|
|
// working tree's changes, but also the index's changes.
|
|
|
|
StashApplyReinstateIndex StashApplyFlag = C.GIT_STASH_APPLY_REINSTATE_INDEX
|
|
|
|
)
|
|
|
|
|
|
|
|
// StashApplyProgress are flags describing the progress of the apply operation.
|
|
|
|
type StashApplyProgress int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// StashApplyProgressNone means loading the stashed data from the object store.
|
|
|
|
StashApplyProgressNone StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_NONE
|
|
|
|
|
|
|
|
// StashApplyProgressLoadingStash means the stored index is being analyzed.
|
|
|
|
StashApplyProgressLoadingStash StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_LOADING_STASH
|
|
|
|
|
|
|
|
// StashApplyProgressAnalyzeIndex means the stored index is being analyzed.
|
|
|
|
StashApplyProgressAnalyzeIndex StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX
|
|
|
|
|
|
|
|
// StashApplyProgressAnalyzeModified means the modified files are being analyzed.
|
|
|
|
StashApplyProgressAnalyzeModified StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED
|
|
|
|
|
|
|
|
// StashApplyProgressAnalyzeUntracked means the untracked and ignored files are being analyzed.
|
|
|
|
StashApplyProgressAnalyzeUntracked StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED
|
|
|
|
|
|
|
|
// StashApplyProgressCheckoutUntracked means the untracked files are being written to disk.
|
|
|
|
StashApplyProgressCheckoutUntracked StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED
|
|
|
|
|
|
|
|
// StashApplyProgressCheckoutModified means the modified files are being written to disk.
|
|
|
|
StashApplyProgressCheckoutModified StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED
|
|
|
|
|
|
|
|
// StashApplyProgressDone means the stash was applied successfully.
|
|
|
|
StashApplyProgressDone StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_DONE
|
|
|
|
)
|
|
|
|
|
|
|
|
// StashApplyProgressCallback is the apply operation notification callback.
|
|
|
|
type StashApplyProgressCallback func(progress StashApplyProgress) error
|
2020-12-05 18:23:04 -06:00
|
|
|
type stashApplyProgressCallbackData struct {
|
|
|
|
callback StashApplyProgressCallback
|
|
|
|
errorTarget *error
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:23:04 -06:00
|
|
|
//export stashApplyProgressCallback
|
|
|
|
func stashApplyProgressCallback(progress C.git_stash_apply_progress_t, handle unsafe.Pointer) C.int {
|
2015-09-21 06:50:57 -05:00
|
|
|
payload := pointerHandles.Get(handle)
|
2020-12-05 18:23:04 -06:00
|
|
|
data, ok := payload.(*stashApplyProgressCallbackData)
|
2015-09-21 06:50:57 -05:00
|
|
|
if !ok {
|
|
|
|
panic("could not retrieve data for handle")
|
|
|
|
}
|
2020-12-05 18:23:04 -06:00
|
|
|
if data == nil || data.callback == nil {
|
|
|
|
return C.int(ErrorCodeOK)
|
|
|
|
}
|
2015-09-21 06:50:57 -05:00
|
|
|
|
2020-12-05 18:23:04 -06:00
|
|
|
err := data.callback(StashApplyProgress(progress))
|
|
|
|
if err != nil {
|
|
|
|
*data.errorTarget = err
|
|
|
|
return C.int(ErrorCodeUser)
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
2020-12-05 18:23:04 -06:00
|
|
|
return C.int(ErrorCodeOK)
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// StashApplyOptions represents options to control the apply operation.
|
|
|
|
type StashApplyOptions struct {
|
|
|
|
Flags StashApplyFlag
|
2020-12-05 13:43:02 -06:00
|
|
|
CheckoutOptions CheckoutOptions // options to use when writing files to the working directory
|
2015-09-21 06:50:57 -05:00
|
|
|
ProgressCallback StashApplyProgressCallback // optional callback to notify the consumer of application progress
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultStashApplyOptions initializes the structure with default values.
|
|
|
|
func DefaultStashApplyOptions() (StashApplyOptions, error) {
|
|
|
|
optsC := C.git_stash_apply_options{}
|
|
|
|
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
2020-10-22 08:18:11 -05:00
|
|
|
ecode := C.git_stash_apply_options_init(&optsC, C.GIT_STASH_APPLY_OPTIONS_VERSION)
|
2015-09-21 06:50:57 -05:00
|
|
|
if ecode < 0 {
|
|
|
|
return StashApplyOptions{}, MakeGitError(ecode)
|
|
|
|
}
|
|
|
|
return StashApplyOptions{
|
|
|
|
Flags: StashApplyFlag(optsC.flags),
|
|
|
|
CheckoutOptions: checkoutOptionsFromC(&optsC.checkout_options),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2020-12-10 20:01:41 -06:00
|
|
|
func populateStashApplyOptions(copts *C.git_stash_apply_options, opts *StashApplyOptions, errorTarget *error) *C.git_stash_apply_options {
|
|
|
|
C.git_stash_apply_options_init(copts, C.GIT_STASH_APPLY_OPTIONS_VERSION)
|
2020-12-05 18:23:04 -06:00
|
|
|
if opts == nil {
|
|
|
|
return nil
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
2020-12-10 20:01:41 -06:00
|
|
|
copts.flags = C.uint32_t(opts.Flags)
|
|
|
|
populateCheckoutOptions(&copts.checkout_options, &opts.CheckoutOptions, errorTarget)
|
2020-12-05 18:23:04 -06:00
|
|
|
if opts.ProgressCallback != nil {
|
|
|
|
progressData := &stashApplyProgressCallbackData{
|
|
|
|
callback: opts.ProgressCallback,
|
|
|
|
errorTarget: errorTarget,
|
|
|
|
}
|
2020-12-10 20:01:41 -06:00
|
|
|
C._go_git_populate_stash_apply_callbacks(copts)
|
|
|
|
copts.progress_payload = pointerHandles.Track(progressData)
|
2016-02-20 06:34:29 -06:00
|
|
|
}
|
2020-12-10 20:01:41 -06:00
|
|
|
return copts
|
2016-02-20 06:34:29 -06:00
|
|
|
}
|
|
|
|
|
2020-12-10 20:01:41 -06:00
|
|
|
func freeStashApplyOptions(copts *C.git_stash_apply_options) {
|
|
|
|
if copts == nil {
|
2020-12-05 18:23:04 -06:00
|
|
|
return
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
2020-12-10 20:01:41 -06:00
|
|
|
if copts.progress_payload != nil {
|
|
|
|
pointerHandles.Untrack(copts.progress_payload)
|
2020-12-05 18:23:04 -06:00
|
|
|
}
|
2020-12-10 20:01:41 -06:00
|
|
|
freeCheckoutOptions(&copts.checkout_options)
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply applies a single stashed state from the stash list.
|
|
|
|
//
|
|
|
|
// If local changes in the working directory conflict with changes in the
|
2020-12-05 13:43:02 -06:00
|
|
|
// stash then ErrorCodeConflict will be returned. In this case, the index
|
2015-09-21 06:50:57 -05:00
|
|
|
// will always remain unmodified and all files in the working directory will
|
|
|
|
// remain unmodified. However, if you are restoring untracked files or
|
|
|
|
// ignored files and there is a conflict when applying the modified files,
|
|
|
|
// then those files will remain in the working directory.
|
|
|
|
//
|
|
|
|
// If passing the StashApplyReinstateIndex flag and there would be conflicts
|
2020-12-05 13:43:02 -06:00
|
|
|
// when reinstating the index, the function will return ErrorCodeConflict
|
2015-09-21 06:50:57 -05:00
|
|
|
// and both the working directory and index will be left unmodified.
|
|
|
|
//
|
|
|
|
// Note that a minimum checkout strategy of 'CheckoutSafe' is implied.
|
|
|
|
//
|
|
|
|
// 'index' is the position within the stash list. 0 points to the most
|
|
|
|
// recent stashed state.
|
|
|
|
//
|
2020-12-05 13:43:02 -06:00
|
|
|
// Returns error code ErrorCodeNotFound if there's no stashed state for the given
|
|
|
|
// index, error code ErrorCodeConflict if local changes in the working directory
|
2015-09-21 06:50:57 -05:00
|
|
|
// conflict with changes in the stash, the user returned error from the
|
|
|
|
// StashApplyProgressCallback, if any, or other error code.
|
|
|
|
//
|
2020-12-05 13:43:02 -06:00
|
|
|
// Error codes can be interogated with IsErrorCode(err, ErrorCodeNotFound).
|
2015-09-21 06:50:57 -05:00
|
|
|
func (c *StashCollection) Apply(index int, opts StashApplyOptions) error {
|
2020-12-05 18:23:04 -06:00
|
|
|
var err error
|
2020-12-10 20:01:41 -06:00
|
|
|
optsC := populateStashApplyOptions(&C.git_stash_apply_options{}, &opts, &err)
|
2015-09-21 06:50:57 -05:00
|
|
|
defer freeStashApplyOptions(optsC)
|
|
|
|
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
ret := C.git_stash_apply(c.repo.ptr, C.size_t(index), optsC)
|
2017-07-08 09:07:51 -05:00
|
|
|
runtime.KeepAlive(c)
|
2020-12-05 18:23:04 -06:00
|
|
|
if ret == C.int(ErrorCodeUser) && err != nil {
|
|
|
|
return err
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
if ret < 0 {
|
|
|
|
return MakeGitError(ret)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StashCallback is called per entry when interating over all
|
|
|
|
// the stashed states.
|
|
|
|
//
|
|
|
|
// 'index' is the position of the current stash in the stash list,
|
|
|
|
// 'message' is the message used when creating the stash and 'id'
|
|
|
|
// is the commit id of the stash.
|
|
|
|
type StashCallback func(index int, message string, id *Oid) error
|
|
|
|
type stashCallbackData struct {
|
2020-12-05 18:23:04 -06:00
|
|
|
callback StashCallback
|
|
|
|
errorTarget *error
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:23:04 -06:00
|
|
|
//export stashForeachCallback
|
|
|
|
func stashForeachCallback(index C.size_t, message *C.char, id *C.git_oid, handle unsafe.Pointer) C.int {
|
2015-09-21 06:50:57 -05:00
|
|
|
payload := pointerHandles.Get(handle)
|
|
|
|
data, ok := payload.(*stashCallbackData)
|
|
|
|
if !ok {
|
|
|
|
panic("could not retrieve data for handle")
|
|
|
|
}
|
|
|
|
|
2020-12-05 18:23:04 -06:00
|
|
|
err := data.callback(int(index), C.GoString(message), newOidFromC(id))
|
2015-09-21 06:50:57 -05:00
|
|
|
if err != nil {
|
2020-12-05 18:23:04 -06:00
|
|
|
*data.errorTarget = err
|
|
|
|
return C.int(ErrorCodeUser)
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
2020-12-05 18:23:04 -06:00
|
|
|
return C.int(ErrorCodeOK)
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Foreach loops over all the stashed states and calls the callback
|
|
|
|
// for each one.
|
|
|
|
//
|
|
|
|
// If callback returns an error, this will stop looping.
|
|
|
|
func (c *StashCollection) Foreach(callback StashCallback) error {
|
2020-12-05 18:23:04 -06:00
|
|
|
var err error
|
2015-09-21 06:50:57 -05:00
|
|
|
data := stashCallbackData{
|
2020-12-05 18:23:04 -06:00
|
|
|
callback: callback,
|
|
|
|
errorTarget: &err,
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
handle := pointerHandles.Track(&data)
|
|
|
|
defer pointerHandles.Untrack(handle)
|
|
|
|
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
ret := C._go_git_stash_foreach(c.repo.ptr, handle)
|
2017-07-08 09:07:51 -05:00
|
|
|
runtime.KeepAlive(c)
|
2020-12-05 18:23:04 -06:00
|
|
|
if ret == C.int(ErrorCodeUser) && err != nil {
|
|
|
|
return err
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
if ret < 0 {
|
|
|
|
return MakeGitError(ret)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drop removes a single stashed state from the stash list.
|
|
|
|
//
|
|
|
|
// 'index' is the position within the stash list. 0 points
|
|
|
|
// to the most recent stashed state.
|
|
|
|
//
|
2020-12-05 13:43:02 -06:00
|
|
|
// Returns error code ErrorCodeNotFound if there's no stashed
|
2015-09-21 06:50:57 -05:00
|
|
|
// state for the given index.
|
|
|
|
func (c *StashCollection) Drop(index int) error {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
ret := C.git_stash_drop(c.repo.ptr, C.size_t(index))
|
2017-07-08 09:07:51 -05:00
|
|
|
runtime.KeepAlive(c)
|
2015-09-21 06:50:57 -05:00
|
|
|
if ret < 0 {
|
|
|
|
return MakeGitError(ret)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pop applies a single stashed state from the stash list
|
|
|
|
// and removes it from the list if successful.
|
|
|
|
//
|
|
|
|
// 'index' is the position within the stash list. 0 points
|
|
|
|
// to the most recent stashed state.
|
|
|
|
//
|
|
|
|
// 'opts' controls how stashes are applied.
|
|
|
|
//
|
2020-12-05 13:43:02 -06:00
|
|
|
// Returns error code ErrorCodeNotFound if there's no stashed
|
2015-09-21 06:50:57 -05:00
|
|
|
// state for the given index.
|
|
|
|
func (c *StashCollection) Pop(index int, opts StashApplyOptions) error {
|
2020-12-05 18:23:04 -06:00
|
|
|
var err error
|
2020-12-10 20:01:41 -06:00
|
|
|
optsC := populateStashApplyOptions(&C.git_stash_apply_options{}, &opts, &err)
|
2015-09-21 06:50:57 -05:00
|
|
|
defer freeStashApplyOptions(optsC)
|
|
|
|
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
|
|
|
ret := C.git_stash_pop(c.repo.ptr, C.size_t(index), optsC)
|
2017-07-08 09:07:51 -05:00
|
|
|
runtime.KeepAlive(c)
|
2020-12-05 18:23:04 -06:00
|
|
|
if ret == C.int(ErrorCodeUser) && err != nil {
|
|
|
|
return err
|
2015-09-21 06:50:57 -05:00
|
|
|
}
|
|
|
|
if ret < 0 {
|
|
|
|
return MakeGitError(ret)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|