package git /* #include extern void _go_git_populate_checkout_callbacks(git_checkout_options *opts); */ import "C" import ( "os" "runtime" "unsafe" ) type CheckoutNotifyType uint type CheckoutStrategy uint const ( CheckoutNotifyNone CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_NONE CheckoutNotifyConflict CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_CONFLICT CheckoutNotifyDirty CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_DIRTY CheckoutNotifyUpdated CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_UPDATED CheckoutNotifyUntracked CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_UNTRACKED CheckoutNotifyIgnored CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_IGNORED CheckoutNotifyAll CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_ALL CheckoutNone CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates CheckoutSafe CheckoutStrategy = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data CheckoutForce CheckoutStrategy = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index CheckoutRecreateMissing CheckoutStrategy = C.GIT_CHECKOUT_RECREATE_MISSING // Allow checkout to recreate missing files CheckoutAllowConflicts CheckoutStrategy = C.GIT_CHECKOUT_ALLOW_CONFLICTS // Allow checkout to make safe updates even if conflicts are found CheckoutRemoveUntracked CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_UNTRACKED // Remove untracked files not in index (that are not ignored) CheckoutRemoveIgnored CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index CheckoutUpdateOnly CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_ONLY // Only update existing files, don't create new ones CheckoutDontUpdateIndex CheckoutStrategy = C.GIT_CHECKOUT_DONT_UPDATE_INDEX // Normally checkout updates index entries as it goes; this stops that CheckoutNoRefresh CheckoutStrategy = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout CheckoutSkipUnmerged CheckoutStrategy = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files CheckoutUseOurs CheckoutStrategy = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index CheckoutUseTheirs CheckoutStrategy = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index CheckoutDisablePathspecMatch CheckoutStrategy = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH // Treat pathspec as simple list of exact match file paths CheckoutSkipLockedDirectories CheckoutStrategy = C.GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES // Ignore directories in use, they will be left empty CheckoutDontOverwriteIgnored CheckoutStrategy = C.GIT_CHECKOUT_DONT_OVERWRITE_IGNORED // Don't overwrite ignored files that exist in the checkout target CheckoutConflictStyleMerge CheckoutStrategy = C.GIT_CHECKOUT_CONFLICT_STYLE_MERGE // Write normal merge files for conflicts CheckoutConflictStyleDiff3 CheckoutStrategy = C.GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 // Include common ancestor data in diff3 format files for conflicts CheckoutDontRemoveExisting CheckoutStrategy = C.GIT_CHECKOUT_DONT_REMOVE_EXISTING // Don't overwrite existing files or folders CheckoutDontWriteIndex CheckoutStrategy = C.GIT_CHECKOUT_DONT_WRITE_INDEX // Normally checkout writes the index upon completion; this prevents that CheckoutUpdateSubmodules CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES // Recursively checkout submodules with same options (NOT IMPLEMENTED) CheckoutUpdateSubmodulesIfChanged CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) ) type CheckoutNotifyCallback func(why CheckoutNotifyType, path string, baseline, target, workdir DiffFile) error type CheckoutProgressCallback func(path string, completed, total uint) type CheckoutOptions struct { Strategy CheckoutStrategy // Default will be a dry run DisableFilters bool // Don't apply filters like CRLF conversion DirMode os.FileMode // Default is 0755 FileMode os.FileMode // Default is 0644 or 0755 as dictated by blob FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY NotifyFlags CheckoutNotifyType // Default will be none NotifyCallback CheckoutNotifyCallback ProgressCallback CheckoutProgressCallback TargetDirectory string // Alternative checkout path to workdir Paths []string Baseline *Tree } func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOptions { opts := CheckoutOptions{ Strategy: CheckoutStrategy(c.checkout_strategy), DisableFilters: c.disable_filters != 0, DirMode: os.FileMode(c.dir_mode), FileMode: os.FileMode(c.file_mode), FileOpenFlags: int(c.file_open_flags), NotifyFlags: CheckoutNotifyType(c.notify_flags), } if c.notify_payload != nil { opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(*checkoutCallbackData).options.NotifyCallback } if c.progress_payload != nil { opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(*checkoutCallbackData).options.ProgressCallback } if c.target_directory != nil { opts.TargetDirectory = C.GoString(c.target_directory) } return opts } type checkoutCallbackData struct { options *CheckoutOptions errorTarget *error } //export checkoutNotifyCallback func checkoutNotifyCallback( why C.git_checkout_notify_t, cpath *C.char, cbaseline, ctarget, cworkdir, handle unsafe.Pointer, ) C.int { if handle == nil { return C.int(ErrorCodeOK) } path := C.GoString(cpath) var baseline, target, workdir DiffFile if cbaseline != nil { baseline = diffFileFromC((*C.git_diff_file)(cbaseline)) } if ctarget != nil { target = diffFileFromC((*C.git_diff_file)(ctarget)) } if cworkdir != nil { workdir = diffFileFromC((*C.git_diff_file)(cworkdir)) } data := pointerHandles.Get(handle).(*checkoutCallbackData) if data.options.NotifyCallback == nil { return C.int(ErrorCodeOK) } err := data.options.NotifyCallback(CheckoutNotifyType(why), path, baseline, target, workdir) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } //export checkoutProgressCallback func checkoutProgressCallback( path *C.char, completed_steps, total_steps C.size_t, handle unsafe.Pointer, ) { data := pointerHandles.Get(handle).(*checkoutCallbackData) if data.options.ProgressCallback == nil { return } data.options.ProgressCallback(C.GoString(path), uint(completed_steps), uint(total_steps)) } // populateCheckoutOptions populates the provided C-struct with the contents of // the provided CheckoutOptions struct. Returns copts, or nil if opts is nil, // in order to help with what to pass. func populateCheckoutOptions(copts *C.git_checkout_options, opts *CheckoutOptions, errorTarget *error) *C.git_checkout_options { C.git_checkout_options_init(copts, C.GIT_CHECKOUT_OPTIONS_VERSION) if opts == nil { return nil } copts.checkout_strategy = C.uint(opts.Strategy) copts.disable_filters = cbool(opts.DisableFilters) copts.dir_mode = C.uint(opts.DirMode.Perm()) copts.file_mode = C.uint(opts.FileMode.Perm()) copts.notify_flags = C.uint(opts.NotifyFlags) if opts.NotifyCallback != nil || opts.ProgressCallback != nil { C._go_git_populate_checkout_callbacks(copts) data := &checkoutCallbackData{ options: opts, errorTarget: errorTarget, } payload := pointerHandles.Track(data) if opts.NotifyCallback != nil { copts.notify_payload = payload } if opts.ProgressCallback != nil { copts.progress_payload = payload } } if opts.TargetDirectory != "" { copts.target_directory = C.CString(opts.TargetDirectory) } if len(opts.Paths) > 0 { copts.paths.strings = makeCStringsFromStrings(opts.Paths) copts.paths.count = C.size_t(len(opts.Paths)) } if opts.Baseline != nil { copts.baseline = opts.Baseline.cast_ptr } return copts } func freeCheckoutOptions(copts *C.git_checkout_options) { if copts == nil { return } C.free(unsafe.Pointer(copts.target_directory)) if copts.paths.count > 0 { freeStrarray(&copts.paths) } if copts.notify_payload != nil { pointerHandles.Untrack(copts.notify_payload) } else if copts.progress_payload != nil { pointerHandles.Untrack(copts.progress_payload) } } // Updates files in the index and the working tree to match the content of // the commit pointed at by HEAD. opts may be nil. func (v *Repository) CheckoutHead(opts *CheckoutOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := populateCheckoutOptions(&C.git_checkout_options{}, opts, &err) defer freeCheckoutOptions(cOpts) ret := C.git_checkout_head(v.ptr, cOpts) runtime.KeepAlive(v) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } // Updates files in the working tree to match the content of the given // index. If index is nil, the repository's index will be used. opts // may be nil. func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOptions) error { var iptr *C.git_index = nil if index != nil { iptr = index.ptr } runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := populateCheckoutOptions(&C.git_checkout_options{}, opts, &err) defer freeCheckoutOptions(cOpts) ret := C.git_checkout_index(v.ptr, iptr, cOpts) runtime.KeepAlive(v) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := populateCheckoutOptions(&C.git_checkout_options{}, opts, &err) defer freeCheckoutOptions(cOpts) ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts) runtime.KeepAlive(v) runtime.KeepAlive(tree) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil }