summaryrefslogtreecommitdiff
path: root/include/git2/checkout.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/git2/checkout.h')
-rw-r--r--include/git2/checkout.h261
1 files changed, 165 insertions, 96 deletions
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index c36e2a41b..5eedd7bfd 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -24,105 +24,121 @@ GIT_BEGIN_DECL
/**
* Checkout behavior flags
*
- * These flags control what checkout does with files. Pass in a
- * combination of these values OR'ed together. If you just pass zero
- * (i.e. no flags), then you are effectively doing a "dry run" where no
- * files will be modified.
- *
- * Checkout groups the working directory content into 3 classes of files:
- * (1) files that don't need a change, and files that do need a change
- * that either (2) we are allowed to modifed or (3) we are not. The flags
- * you pass in will decide which files we are allowed to modify.
- *
- * By default, checkout is not allowed to modify any files. Anything
- * needing a change would be considered a conflict.
- *
- * GIT_CHECKOUT_UPDATE_UNMODIFIED means that checkout is allowed to update
- * any file where the working directory content matches the HEAD
- * (e.g. either the files match or the file is absent in both places).
- *
- * GIT_CHECKOUT_UPDATE_MISSING means checkout can create a missing file
- * that exists in the index and does not exist in the working directory.
- * This is usually desirable for initial checkout, etc. Technically, the
- * missing file differs from the HEAD, which is why this is separate.
- *
- * GIT_CHECKOUT_UPDATE_MODIFIED means checkout is allowed to update files
- * where the working directory does not match the HEAD so long as the file
- * actually exists in the HEAD. This option implies UPDATE_UNMODIFIED.
- *
- * GIT_CHECKOUT_UPDATE_UNTRACKED means checkout is allowed to update files
- * even if there is a working directory version that does not exist in the
- * HEAD (i.e. the file was independently created in the workdir). This
- * implies UPDATE_UNMODIFIED | UPDATE_MISSING (but *not* UPDATE_MODIFIED).
- *
- *
- * On top of these three basic strategies, there are some modifiers
- * options that can be applied:
- *
- * If any files need update but are disallowed by the strategy, normally
- * checkout calls the conflict callback (if given) and then aborts.
- * GIT_CHECKOUT_ALLOW_CONFLICTS means it is okay to update the files that
- * are allowed by the strategy even if there are conflicts. The conflict
- * callbacks are still made, but non-conflicting files will be updated.
- *
- * Any unmerged entries in the index are automatically considered conflicts.
- * If you want to proceed anyhow and just skip unmerged entries, you can use
- * GIT_CHECKOUT_SKIP_UNMERGED which is less dangerous than just allowing all
- * conflicts. Alternatively, use GIT_CHECKOUT_USE_OURS to proceed and
- * checkout the stage 2 ("ours") version. GIT_CHECKOUT_USE_THEIRS means to
- * proceed and use the stage 3 ("theirs") version.
- *
- * GIT_CHECKOUT_UPDATE_ONLY means that update is not allowed to create new
- * files or delete old ones, only update existing content. With this
- * flag, files that needs to be created or deleted are not conflicts -
- * they are just skipped. This also skips typechanges to existing files
- * (because the old would have to be removed).
- *
- * GIT_CHECKOUT_REMOVE_UNTRACKED means that files in the working directory
- * that are untracked (and not ignored) will be removed altogether. These
- * untracked files (that do not shadow index entries) are not considered
- * conflicts and would normally be ignored.
+ * In libgit2, the function of checkout is to update the working directory
+ * to match a target tree given an expected baseline tree. It does not move
+ * the HEAD commit - you do that separately. Typically the expected tree is
+ * the (to-be-moved) HEAD commit.
+ *
+ * Checkout examines the differences between the target and expected trees
+ * plus the current working directory and groups files into five categories:
+ *
+ * 1. UNMODIFIED - Files that match in all places.
+ * 2. SAFE - Files where the working directory and the expect content match
+ * that can be safely updated to the target.
+ * 3. DIRTY/MISSING - Files where the working directory differs from the
+ * expected content but there is no conflicting change with the target
+ * tree. An example is a file that doesn't exist in the working
+ * directory - no data would be lost as a result of writing this file.
+ * The action to take with these files depends on the options you elect.
+ * 4. CONFLICTS - Files where changes in the working directory conflicts
+ * with changes to be applied by the target. If conflicts are found,
+ * they prevent any other modifications from being made (although there
+ * are options to override that and force the update, of course).
+ * 5. UNTRACKED/IGNORED - Files in the working directory that are untracked
+ * or ignored.
+ *
+ *
+ * You control the actions checkout takes with one of four base strategies:
+ *
+ * - `GIT_CHECKOUT_NONE` is the default and applies no changes. It is a dry
+ * run that you can use to find conflicts, etc. if you wish.
+ *
+ * - `GIT_CHECKOUT_SAFE` is like `git checkout` and only applies changes
+ * between the expected and target trees to files in category 2.
+ *
+ * - `GIT_CHECKOUT_SAFE_CREATE` also creates files that are missing from the
+ * working directory (category 3), even if there is no change between the
+ * expected and target trees for those files. See notes below on
+ * emulating `git checkout-index` for some of the subtleties of this.
+ *
+ * - `GIT_CHECKOUT_FORCE` is like `git checkout -f` and will update the
+ * working directory to match the target content regardless of conflicts,
+ * overwriting dirty and conflicting files.
+ *
+ *
+ * There are some additional flags to modified the behavior of checkout:
+ *
+ * - GIT_CHECKOUT_ALLOW_CONFLICTS can be added to apply safe file updates
+ * even if there are conflicts. Normally, the entire checkout will be
+ * cancelled if any files are in category 4. With this flag, conflicts
+ * will be skipped (though the notification callback will still be invoked
+ * on the conflicting files if requested).
+ *
+ * - GIT_CHECKOUT_REMOVE_UNTRACKED means that files in the working directory
+ * that are untracked (but not ignored) should be deleted. The are not
+ * considered conflicts and would normally be ignored by checkout.
+ *
+ * - GIT_CHECKOUT_REMOVE_IGNORED means to remove ignored files from the
+ * working directory as well. Obviously, these would normally be ignored.
+ *
+ * - GIT_CHECKOUT_UPDATE_ONLY means to only update the content of files that
+ * already exist. Files will not be created nor deleted. This does not
+ * make adds and deletes into conflicts - it just skips applying those
+ * changes. This will also skip updates to typechanged files (since that
+ * would involve deleting the old and creating the new).
+ *
+ * - Unmerged entries in the index are also considered conflicts. The
+ * GIT_CHECKOUT_SKIP_UNMERGED flag causes us to skip files with unmerged
+ * index entries. You can also use GIT_CHECKOUT_USE_OURS and
+ * GIT_CHECKOUT_USE_THEIRS to proceeed with the checkout using either the
+ * stage 2 ("ours") or stage 3 ("theirs") version of files in the index.
+ *
+ *
+ * To emulate `git checkout`, use `GIT_CHECKOUT_SAFE` with a checkout
+ * notification callback (see below) that displays information about dirty
+ * files (i.e. files that don't need an update but that no longer match the
+ * expected content). The default behavior will cancel on conflicts.
+ *
+ * To emulate `git checkout-index`, use `GIT_CHECKOUT_SAFE_CREATE` 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`, you use `GIT_CHECKOUT_FORCE`.
*
*
* Checkout is "semi-atomic" as in it will go through the work to be done
* before making any changes and if may decide to abort if there are
- * conflicts, or you can use the conflict callback to explicitly abort the
- * action before any updates are made. Despite this, if a second process
- * is modifying the filesystem while checkout is running, it can't
+ * conflicts, or you can use the notification callback to explicitly abort
+ * the action before any updates are made. Despite this, if a second
+ * process is modifying the filesystem while checkout is running, it can't
* guarantee that the choices is makes while initially examining the
* filesystem are still going to be correct as it applies them.
*/
typedef enum {
- GIT_CHECKOUT_DEFAULT = 0, /** default is a dry run, no actual updates */
-
- /** Allow update of entries where working dir matches HEAD. */
- GIT_CHECKOUT_UPDATE_UNMODIFIED = (1u << 0),
-
- /** Allow update of entries where working dir does not have file. */
- GIT_CHECKOUT_UPDATE_MISSING = (1u << 1),
+ GIT_CHECKOUT_NONE = 0, /** default is a dry run, no actual updates */
/** Allow safe updates that cannot overwrite uncommited data */
- GIT_CHECKOUT_SAFE =
- (GIT_CHECKOUT_UPDATE_UNMODIFIED | GIT_CHECKOUT_UPDATE_MISSING),
+ GIT_CHECKOUT_SAFE = (1u << 0),
- /** Allow update of entries in working dir that are modified from HEAD. */
- GIT_CHECKOUT_UPDATE_MODIFIED = (1u << 2),
-
- /** Update existing untracked files that are now present in the index. */
- GIT_CHECKOUT_UPDATE_UNTRACKED = (1u << 3),
+ /** Allow safe updates plus creation of missing files */
+ GIT_CHECKOUT_SAFE_CREATE = (1u << 1),
/** Allow all updates to force working directory to look like index */
- GIT_CHECKOUT_FORCE =
- (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_MODIFIED | GIT_CHECKOUT_UPDATE_UNTRACKED),
+ GIT_CHECKOUT_FORCE = (1u << 2),
+
- /** Allow checkout to make updates even if conflicts are found */
+ /** 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 << 6),
+ GIT_CHECKOUT_UPDATE_ONLY = (1u << 7),
/**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
@@ -143,34 +159,85 @@ typedef enum {
} git_checkout_strategy_t;
/**
+ * Checkout notification flags
+ *
+ * When running a checkout, you can set a notification callback (`notify_cb`)
+ * to be invoked for some or all files to be checked out. Which files
+ * receive a callback depend on the `notify_flags` value which is a
+ * combination of these flags.
+ *
+ * - GIT_CHECKOUT_NOTIFY_CONFLICTS means that conflicting files that would
+ * prevent the checkout from occurring will receive callbacks. If you
+ * used GIT_CHECKOUT_ALLOW_CONFLICTS, the callbacks are still done, but
+ * the checkout will not be blocked. The callback `status_flags` will
+ * have both index and work tree change bits set (see `git_status_t`).
+ *
+ * - GIT_CHECKOUT_NOTIFY_DIRTY means to notify about "dirty" files, i.e.
+ * those that do not need to be updated but no longer match the expected
+ * content. Core git displays these files when checkout runs, but does
+ * not stop the checkout. For these, `status_flags` will have only work
+ * tree bits set (i.e. GIT_STATUS_WT_MODIFIED, etc).
+ *
+ * - GIT_CHECKOUT_NOTIFY_UPDATED sends notification for any file changed by
+ * the checkout. Callback `status_flags` will have only index bits set.
+ *
+ * - GIT_CHECKOUT_NOTIFY_UNTRACKED notifies for all untracked files that
+ * are not ignored. Passing GIT_CHECKOUT_REMOVE_UNTRACKED would remove
+ * these files. The `status_flags` will be GIT_STATUS_WT_NEW.
+ *
+ * - GIT_CHECKOUT_NOTIFY_IGNORED notifies for the ignored files. Passing
+ * GIT_CHECKOUT_REMOVE_IGNORED will remove these. The `status_flags`
+ * will be to GIT_STATUS_IGNORED.
+ *
+ * If you return a non-zero value from the notify callback, the checkout
+ * will be canceled. Notification callbacks are made prior to making any
+ * modifications, so returning non-zero will cancel the entire checkout.
+ * If you are do not use GIT_CHECKOUT_ALLOW_CONFLICTS and there are
+ * conflicts, you don't need to explicitly cancel from the callback.
+ * Checkout itself will abort after all files are processed.
+ *
+ * To emulate core git checkout output, use GIT_CHECKOUT_NOTIFY_CONFLICTS
+ * and GIT_CHECKOUT_NOTIFY_DIRTY. Conflicts will have `status_flags` with
+ * changes in both the index and work tree (see the `git_status_t` values).
+ * Dirty files will only have work tree flags set.
+ */
+typedef enum {
+ GIT_CHECKOUT_NOTIFY_CONFLICTS = (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_t;
+
+/**
* Checkout options structure
*
* Use zeros to indicate default settings.
- * This needs to be initialized with the `GIT_CHECKOUT_OPTS_INIT` macro:
+ *
+ * This should be initialized with the `GIT_CHECKOUT_OPTS_INIT` macro to
+ * correctly set the `version` field.
*
* git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
*/
typedef struct git_checkout_opts {
unsigned int version;
+
unsigned int checkout_strategy; /** default will be a dry run */
- int disable_filters; /** don't apply filters like CRLF conversion */
- int dir_mode; /** default is 0755 */
- int file_mode; /** default is 0644 or 0755 as dictated by blob */
- int file_open_flags; /** default is O_CREAT | O_TRUNC | O_WRONLY */
+ 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 */
- /** Optional callback made on files where the index differs from the
- * working directory but the rules do not allow update. Return a
- * non-zero value to abort the checkout. All such callbacks will be
- * made before any changes are made to the working directory.
- */
- int (*conflict_cb)(
- const char *conflicting_path,
+ unsigned int notify_flags; /** see `git_checkout_notify_t` above */
+ int (*notify_cb)(
+ const char *path,
+ unsigned int status_flags, /** combo of git_status_t values */
const git_oid *index_oid,
- unsigned int index_mode,
- unsigned int wd_mode,
+ unsigned int checkout_mode,
+ unsigned int workdir_mode,
void *payload);
- void *conflict_payload;
+ void *notify_payload;
/* Optional callback to notify the consumer of checkout progress. */
void (*progress_cb)(
@@ -184,14 +251,16 @@ typedef struct git_checkout_opts {
* paths should be taken into account, otherwise all files.
*/
git_strarray paths;
+
+ git_tree *baseline; /** expected content of workdir, defaults to HEAD */
} git_checkout_opts;
#define GIT_CHECKOUT_OPTS_VERSION 1
#define GIT_CHECKOUT_OPTS_INIT {GIT_CHECKOUT_OPTS_VERSION}
/**
- * Updates files in the index and the working tree to match the content of the
- * commit pointed at by HEAD.
+ * Updates files in the index and the working tree to match the content of
+ * the commit pointed at by HEAD.
*
* @param repo repository to check out (must be non-bare)
* @param opts specifies checkout options (may be NULL)