/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_git_checkout_h__ #define INCLUDE_git_checkout_h__ #include "common.h" #include "types.h" #include "diff.h" /** * @file git2/checkout.h * @brief Git checkout routines * @defgroup git_checkout Git checkout routines * @ingroup Git * @{ */ GIT_BEGIN_DECL /** * Checkout behavior flags * * In libgit2, checkout is used to update the working directory and index * to match a target tree. Unlike git checkout, it does not move the HEAD * commit for you - use `git_repository_set_head` or the like to do that. * * Checkout looks at (up to) four things: the "target" tree you want to * check out, the "baseline" tree of what was checked out previously, the * working directory for actual files, and the index for staged changes. * * You give checkout one of three strategies for update: * * - `GIT_CHECKOUT_NONE` is a dry-run strategy that checks for conflicts, * etc., but doesn't make any actual changes. * * - `GIT_CHECKOUT_FORCE` is at the opposite extreme, taking any action to * make the working directory match the target (including potentially * discarding modified files). * * - `GIT_CHECKOUT_SAFE` is between these two options, it will only make * modifications that will not lose changes. * * | target == baseline | target != baseline | * ---------------------|-----------------------|----------------------| * workdir == baseline | no action | create, update, or | * | | delete file | * ---------------------|-----------------------|----------------------| * workdir exists and | no action | conflict (notify | * is != baseline | notify dirty MODIFIED | and cancel checkout) | * ---------------------|-----------------------|----------------------| * workdir missing, | notify dirty DELETED | create file | * baseline present | | | * ---------------------|-----------------------|----------------------| * * To emulate `git checkout`, use `GIT_CHECKOUT_SAFE` with a checkout * notification callback (see below) that displays information about dirty * files. The default behavior will cancel checkout on conflicts. * * To emulate `git checkout-index`, use `GIT_CHECKOUT_SAFE` with a * notification callback that cancels the operation if a dirty-but-existing * file is found in the working directory. This core git command isn't * quite "force" but is sensitive about some types of changes. * * To emulate `git checkout -f`, use `GIT_CHECKOUT_FORCE`. * * * There are some additional flags to modified the behavior of checkout: * * - GIT_CHECKOUT_ALLOW_CONFLICTS makes SAFE mode apply safe file updates * even if there are conflicts (instead of cancelling the checkout). * * - GIT_CHECKOUT_REMOVE_UNTRACKED means remove untracked files (i.e. not * in target, baseline, or index, and not ignored) from the working dir. * * - GIT_CHECKOUT_REMOVE_IGNORED means remove ignored files (that are also * untracked) from the working directory as well. * * - GIT_CHECKOUT_UPDATE_ONLY means to only update the content of files that * already exist. Files will not be created nor deleted. This just skips * applying adds, deletes, and typechanges. * * - GIT_CHECKOUT_DONT_UPDATE_INDEX prevents checkout from writing the * updated files' information to the index. * * - Normally, checkout will reload the index and git attributes from disk * before any operations. GIT_CHECKOUT_NO_REFRESH prevents this reload. * * - Unmerged index entries are conflicts. GIT_CHECKOUT_SKIP_UNMERGED skips * files with unmerged index entries instead. GIT_CHECKOUT_USE_OURS and * GIT_CHECKOUT_USE_THEIRS to proceed with the checkout using either the * stage 2 ("ours") or stage 3 ("theirs") version of files in the index. * * - GIT_CHECKOUT_DONT_OVERWRITE_IGNORED prevents ignored files from being * overwritten. Normally, files that are ignored in the working directory * are not considered "precious" and may be overwritten if the checkout * target contains that file. * * - GIT_CHECKOUT_DONT_REMOVE_EXISTING prevents checkout from removing * files or folders that fold to the same name on case insensitive * filesystems. This can cause files to retain their existing names * and write through existing symbolic links. */ typedef enum { GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */ /** Allow safe updates that cannot overwrite uncommitted data */ GIT_CHECKOUT_SAFE = (1u << 0), /** Allow all updates to force working directory to look like index */ GIT_CHECKOUT_FORCE = (1u << 1), /** Allow checkout to recreate missing files */ GIT_CHECKOUT_RECREATE_MISSING = (1u << 2), /** Allow checkout to make safe updates even if conflicts are found */ GIT_CHECKOUT_ALLOW_CONFLICTS = (1u << 4), /** Remove untracked files not in index (that are not ignored) */ GIT_CHECKOUT_REMOVE_UNTRACKED = (1u << 5), /** Remove ignored files not in index */ GIT_CHECKOUT_REMOVE_IGNORED = (1u << 6), /** Only update existing files, don't create new ones */ GIT_CHECKOUT_UPDATE_ONLY = (1u << 7), /** * Normally checkout updates index entries as it goes; this stops that. * Implies `GIT_CHECKOUT_DONT_WRITE_INDEX`. */ GIT_CHECKOUT_DONT_UPDATE_INDEX = (1u << 8), /** Don't refresh index/config/etc before doing checkout */ GIT_CHECKOUT_NO_REFRESH = (1u << 9), /** Allow checkout to skip unmerged files */ GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10), /** For unmerged files, checkout stage 2 from index */ GIT_CHECKOUT_USE_OURS = (1u << 11), /** For unmerged files, checkout stage 3 from index */ GIT_CHECKOUT_USE_THEIRS = (1u << 12), /** Treat pathspec as simple list of exact match file paths */ GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13), /** Ignore directories in use, they will be left empty */ GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18), /** Don't overwrite ignored files that exist in the checkout target */ GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19), /** Write normal merge files for conflicts */ GIT_CHECKOUT_CONFLICT_STYLE_MERGE = (1u << 20), /** Include common ancestor data in diff3 format files for conflicts */ GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1u << 21), /** Don't overwrite existing files or folders */ GIT_CHECKOUT_DONT_REMOVE_EXISTING = (1u << 22), /** Normally checkout writes the index upon completion; this prevents that. */ GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23), /** * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED */ /** Recursively checkout submodules with same options (NOT IMPLEMENTED) */ GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16), /** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */ GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1u << 17), } git_checkout_strategy_t; /** * Checkout notification flags * * Checkout will invoke an options notification callback (`notify_cb`) for * certain cases - you pick which ones via `notify_flags`: * * - GIT_CHECKOUT_NOTIFY_CONFLICT invokes checkout on conflicting paths. * * - GIT_CHECKOUT_NOTIFY_DIRTY notifies about "dirty" files, i.e. those that * do not need an update but no longer match the baseline. Core git * displays these files when checkout runs, but won't stop the checkout. * * - GIT_CHECKOUT_NOTIFY_UPDATED sends notification for any file changed. * * - GIT_CHECKOUT_NOTIFY_UNTRACKED notifies about untracked files. * * - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files. * * Returning a non-zero value from this callback will cancel the checkout. * The non-zero return value will be propagated back and returned by the * git_checkout_... call. * * Notification callbacks are made prior to modifying any files on disk, * so canceling on any notification will still happen prior to any files * being modified. */ typedef enum { GIT_CHECKOUT_NOTIFY_NONE = 0, GIT_CHECKOUT_NOTIFY_CONFLICT = (1u << 0), GIT_CHECKOUT_NOTIFY_DIRTY = (1u << 1), GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2), GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3), GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4), GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu } git_checkout_notify_t; typedef struct { size_t mkdir_calls; size_t stat_calls; size_t chmod_calls; } git_checkout_perfdata; /** Checkout notification callback function */ typedef int (*git_checkout_notify_cb)( git_checkout_notify_t why, const char *path, const git_diff_file *baseline, const git_diff_file *target, const git_diff_file *workdir, void *payload); /** Checkout progress notification function */ typedef void (*git_checkout_progress_cb)( const char *path, size_t completed_steps, size_t total_steps, void *payload); /** Checkout perfdata notification function */ typedef void (*git_checkout_perfdata_cb)( const git_checkout_perfdata *perfdata, void *payload); /** * Checkout options structure * * Zero out for defaults. Initialize with `GIT_CHECKOUT_OPTIONS_INIT` macro to * correctly set the `version` field. E.g. * * git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; */ typedef struct git_checkout_options { unsigned int version; unsigned int checkout_strategy; /**< default will be a dry run */ int disable_filters; /**< don't apply filters like CRLF conversion */ unsigned int dir_mode; /**< default is 0755 */ unsigned int file_mode; /**< default is 0644 or 0755 as dictated by blob */ int file_open_flags; /**< default is O_CREAT | O_TRUNC | O_WRONLY */ unsigned int notify_flags; /**< see `git_checkout_notify_t` above */ git_checkout_notify_cb notify_cb; void *notify_payload; /** Optional callback to notify the consumer of checkout progress. */ git_checkout_progress_cb progress_cb; void *progress_payload; /** When not zeroed out, array of fnmatch patterns specifying which * paths should be taken into account, otherwise all files. Use * GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH to treat as simple list. */ git_strarray paths; /** The expected content of the working directory; defaults to HEAD. * If the working directory does not match this baseline information, * that will produce a checkout conflict. */ git_tree *baseline; /** Like `baseline` above, though expressed as an index. This * option overrides `baseline`. */ git_index *baseline_index; /**< expected content of workdir, expressed as an index. */ const char *target_directory; /**< alternative checkout path to workdir */ const char *ancestor_label; /**< the name of the common ancestor side of conflicts */ const char *our_label; /**< the name of the "our" side of conflicts */ const char *their_label; /**< the name of the "their" side of conflicts */ /** Optional callback to notify the consumer of performance data. */ git_checkout_perfdata_cb perfdata_cb; void *perfdata_payload; } git_checkout_options; #define GIT_CHECKOUT_OPTIONS_VERSION 1 #define GIT_CHECKOUT_OPTIONS_INIT {GIT_CHECKOUT_OPTIONS_VERSION} /** * Initializes a `git_checkout_options` with default values. Equivalent to * creating an instance with GIT_CHECKOUT_OPTIONS_INIT. * * @param opts the `git_checkout_options` struct to initialize. * @param version Version of struct; pass `GIT_CHECKOUT_OPTIONS_VERSION` * @return Zero on success; -1 on failure. */ GIT_EXTERN(int) git_checkout_init_options( git_checkout_options *opts, unsigned int version); /** * Updates files in the index and the working tree to match the content of * the commit pointed at by HEAD. * * Note that this is _not_ the correct mechanism used to switch branches; * do not change your `HEAD` and then call this method, that would leave * you with checkout conflicts since your working directory would then * appear to be dirty. Instead, checkout the target of the branch and * then update `HEAD` using `git_repository_set_head` to point to the * branch you checked out. * * @param repo repository to check out (must be non-bare) * @param opts specifies checkout options (may be NULL) * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non * existing branch, non-zero value returned by `notify_cb`, or * other error code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_head( git_repository *repo, const git_checkout_options *opts); /** * Updates files in the working tree to match the content of the index. * * @param repo repository into which to check out (must be non-bare) * @param index index to be checked out (or NULL to use repository index) * @param opts specifies checkout options (may be NULL) * @return 0 on success, non-zero return value from `notify_cb`, or error * code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_index( git_repository *repo, git_index *index, const git_checkout_options *opts); /** * Updates files in the index and working tree to match the content of the * tree pointed at by the treeish. * * @param repo repository to check out (must be non-bare) * @param treeish a commit, tag or tree which content will be used to update * the working directory (or NULL to use HEAD) * @param opts specifies checkout options (may be NULL) * @return 0 on success, non-zero return value from `notify_cb`, or error * code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_tree( git_repository *repo, const git_object *treeish, const git_checkout_options *opts); /** @} */ GIT_END_DECL #endif