diff options
author | Russell Belfer <rb@github.com> | 2012-08-02 13:00:58 -0700 |
---|---|---|
committer | Russell Belfer <rb@github.com> | 2012-08-24 11:00:26 -0700 |
commit | aa13bf05c84f10f364ce35c5d4f989337b36e043 (patch) | |
tree | ca5877bd006a6bfdcd9aad5587e36e89523d6cd2 /include/git2/submodule.h | |
parent | decff7b4c13939e5f00d51aea4176fc543d73ede (diff) | |
download | libgit2-aa13bf05c84f10f364ce35c5d4f989337b36e043.tar.gz |
Major submodule rewrite
This replaces the old submodule API with a new extended API that
supports most of the things that can be done with `git submodule`.
Diffstat (limited to 'include/git2/submodule.h')
-rw-r--r-- | include/git2/submodule.h | 465 |
1 files changed, 420 insertions, 45 deletions
diff --git a/include/git2/submodule.h b/include/git2/submodule.h index f65911a3..6cd66465 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -20,54 +20,169 @@ */ GIT_BEGIN_DECL +/** + * Opaque structure representing a submodule. + * + * Submodule support in libgit2 builds a list of known submodules and keeps + * it in the repository. The list is built from the .gitmodules file, the + * .git/config file, the index, and the HEAD tree. Items in the working + * directory that look like submodules (i.e. a git repo) but are not + * mentioned in those places won't be tracked. + */ +typedef struct git_submodule git_submodule; + +/** + * Values that could be specified for the update rule of a submodule. + * + * Use the DEFAULT value if you have altered the update value via + * `git_submodule_set_update()` and wish to reset to the original default. + */ typedef enum { + GIT_SUBMODULE_UPDATE_DEFAULT = -1, GIT_SUBMODULE_UPDATE_CHECKOUT = 0, GIT_SUBMODULE_UPDATE_REBASE = 1, - GIT_SUBMODULE_UPDATE_MERGE = 2 + GIT_SUBMODULE_UPDATE_MERGE = 2, + GIT_SUBMODULE_UPDATE_NONE = 3 } git_submodule_update_t; +/** + * Values that could be specified for how closely to examine the + * working directory when getting submodule status. + * + * Use the DEFUALT value if you have altered the ignore value via + * `git_submodule_set_ignore()` and wish to reset to the original value. + */ typedef enum { - GIT_SUBMODULE_IGNORE_ALL = 0, /* never dirty */ - GIT_SUBMODULE_IGNORE_DIRTY = 1, /* only dirty if HEAD moved */ - GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */ - GIT_SUBMODULE_IGNORE_NONE = 3 /* any change or untracked == dirty */ + GIT_SUBMODULE_IGNORE_DEFAULT = -1, /* reset to default */ + GIT_SUBMODULE_IGNORE_NONE = 0, /* any change or untracked == dirty */ + GIT_SUBMODULE_IGNORE_UNTRACKED = 1, /* dirty if tracked files change */ + GIT_SUBMODULE_IGNORE_DIRTY = 2, /* only dirty if HEAD moved */ + GIT_SUBMODULE_IGNORE_ALL = 3 /* never dirty */ } git_submodule_ignore_t; /** - * Description of submodule + * Status values for submodules. + * + * One of these values will be returned for the submodule in the index + * relative to the HEAD tree, and one will be returned for the submodule in + * the working directory relative to the index. The value can be extracted + * from the actual submodule status return value using one of the macros + * below (see GIT_SUBMODULE_INDEX_STATUS and GIT_SUBMODULE_WD_STATUS). + */ +enum { + GIT_SUBMODULE_STATUS_CLEAN = 0, + GIT_SUBMODULE_STATUS_ADDED = 1, + GIT_SUBMODULE_STATUS_REMOVED = 2, + GIT_SUBMODULE_STATUS_REMOVED_TYPE_CHANGE = 3, + GIT_SUBMODULE_STATUS_MODIFIED = 4, + GIT_SUBMODULE_STATUS_MODIFIED_AHEAD = 5, + GIT_SUBMODULE_STATUS_MODIFIED_BEHIND = 6 +}; + +/** + * Return codes for submodule status. + * + * A combination of these flags (and shifted values of the + * GIT_SUBMODULE_STATUS codes above) will be returned to describe the status + * of a submodule. + * + * Submodule info is contained in 4 places: the HEAD tree, the index, config + * files (both .git/config and .gitmodules), and the working directory. Any + * or all of those places might be missing information about the submodule + * depending on what state the repo is in. + * + * When you ask for submodule status, we consider all four places and return + * a combination of the flags below. Also, we also compare HEAD to index to + * workdir, and return a relative status code (see above) for the + * comparisons. Use the GIT_SUBMODULE_INDEX_STATUS() and + * GIT_SUBMODULE_WD_STATUS() macros to extract these status codes from the + * results. As an example, if the submodule exists in the HEAD and does not + * exist in the index, then using GIT_SUBMODULE_INDEX_STATUS(st) will return + * GIT_SUBMODULE_STATUS_REMOVED. + * + * The ignore settings for the submodule will control how much status info + * you get about the working directory. For example, with ignore ALL, the + * workdir will always show as clean. With any ignore level below NONE, + * you will never get the WD_HAS_UNTRACKED value back. + * + * The other SUBMODULE_STATUS values you might see are: + * + * - IN_HEAD means submodule exists in HEAD tree + * - IN_INDEX means submodule exists in index + * - IN_CONFIG means submodule exists in config + * - IN_WD means submodule exists in workdir and looks like a submodule + * - WD_CHECKED_OUT means submodule in workdir has .git content + * - WD_HAS_UNTRACKED means workdir contains untracked files. This would + * only ever be returned for ignore value GIT_SUBMODULE_IGNORE_NONE. + * - WD_MISSING_COMMITS means workdir repo is out of date and does not + * contain the SHAs from either the index or the HEAD tree + */ +#define GIT_SUBMODULE_STATUS_IN_HEAD (1u << 0) +#define GIT_SUBMODULE_STATUS_IN_INDEX (1u << 1) +#define GIT_SUBMODULE_STATUS_IN_CONFIG (1u << 2) +#define GIT_SUBMODULE_STATUS_IN_WD (1u << 3) +#define GIT_SUBMODULE_STATUS_INDEX_DATA_OFFSET (4) +#define GIT_SUBMODULE_STATUS_WD_DATA_OFFSET (7) +#define GIT_SUBMODULE_STATUS_WD_CHECKED_OUT (1u << 10) +#define GIT_SUBMODULE_STATUS_WD_HAS_UNTRACKED (1u << 11) +#define GIT_SUBMODULE_STATUS_WD_MISSING_COMMITS (1u << 12) + +/** + * Extract submodule status value for index from status mask. + */ +#define GIT_SUBMODULE_INDEX_STATUS(s) \ + (((s) >> GIT_SUBMODULE_STATUS_INDEX_DATA_OFFSET) & 0x07) + +/** + * Extract submodule status value for working directory from status mask. + */ +#define GIT_SUBMODULE_WD_STATUS(s) \ + (((s) >> GIT_SUBMODULE_STATUS_WD_DATA_OFFSET) & 0x07) + +/** + * Lookup submodule information by name or path. + * + * Given either the submodule name or path (they are usually the same), this + * returns a structure describing the submodule. + * + * There are two expected error scenarios: * - * This record describes a submodule found in a repository. There - * should be an entry for every submodule found in the HEAD and for - * every submodule described in .gitmodules. The fields are as follows: + * - The submodule is not mentioned in the HEAD, the index, and the config, + * but does "exist" in the working directory (i.e. there is a subdirectory + * that is a valid self-contained git repo). In this case, this function + * returns GIT_EEXISTS to indicate the the submodule exists but not in a + * state where a git_submodule can be instantiated. + * - The submodule is not mentioned in the HEAD, index, or config and the + * working directory doesn't contain a value git repo at that path. + * There may or may not be anything else at that path, but nothing that + * looks like a submodule. In this case, this returns GIT_ENOTFOUND. * - * - `name` is the name of the submodule from .gitmodules. - * - `path` is the path to the submodule from the repo working directory. - * It is almost always the same as `name`. - * - `url` is the url for the submodule. - * - `oid` is the HEAD SHA1 for the submodule. - * - `update` is a value from above - see gitmodules(5) update. - * - `ignore` is a value from above - see gitmodules(5) ignore. - * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules. - * - `refcount` is for internal use. + * The submodule object is owned by the containing repo and will be freed + * when the repo is freed. The caller need not free the submodule. * - * If the submodule has been added to .gitmodules but not yet git added, - * then the `oid` will be zero. If the submodule has been deleted, but - * the delete has not been committed yet, then the `oid` will be set, but - * the `url` will be NULL. + * @param submodule Pointer to submodule description object pointer.. + * @param repo The repository. + * @param name The name of the submodule. Trailing slashes will be ignored. + * @return 0 on success, GIT_ENOTFOUND if submodule does not exist, + * GIT_EEXISTS if submodule exists in working directory only, -1 on + * other errors. */ -typedef struct { - char *name; - char *path; - char *url; - git_oid oid; /* sha1 of submodule HEAD ref or zero if not committed */ - git_submodule_update_t update; - git_submodule_ignore_t ignore; - int fetch_recurse; - int refcount; -} git_submodule; +GIT_EXTERN(int) git_submodule_lookup( + git_submodule **submodule, + git_repository *repo, + const char *name); /** - * Iterate over all submodules of a repository. + * Iterate over all tracked submodules of a repository. + * + * See the note on `git_submodule` above. This iterates over the tracked + * submodules as decribed therein. + * + * If you are concerned about items in the working directory that look like + * submodules but are not tracked, the diff API will generate a diff record + * for workdir items that look like submodules but are not tracked, showing + * them as added in the workdir. Also, the status API will treat the entire + * subdirectory of a contained git repo as a single GIT_STATUS_WT_NEW item. * * @param repo The repository * @param callback Function to be called with the name of each submodule. @@ -77,26 +192,286 @@ typedef struct { */ GIT_EXTERN(int) git_submodule_foreach( git_repository *repo, - int (*callback)(const char *name, void *payload), + int (*callback)(git_submodule *sm, const char *name, void *payload), void *payload); /** - * Lookup submodule information by name or path. + * Set up a new git submodule for checkout. * - * Given either the submodule name or path (they are usually the same), - * this returns a structure describing the submodule. If the submodule - * does not exist, this will return GIT_ENOTFOUND and set the submodule - * pointer to NULL. + * This does "git submodule add" up to the fetch and checkout of the + * submodule contents. It preps a new submodule, creates an entry in + * .gitmodules and creates an empty initialized repository either at the + * given path in the working directory or in .git/modules with a gitlink + * from the working directory to the new repo. * - * @param submodule Pointer to submodule description object pointer.. - * @param repo The repository. - * @param name The name of the submodule. Trailing slashes will be ignored. - * @return 0 on success, GIT_ENOTFOUND if submodule does not exist, -1 on error + * To fully emulate "git submodule add" call this function, then open the + * submodule repo and perform the clone step as needed. Lastly, call + * `git_submodule_add_finalize` to wrap up adding the new submodule and + * .gitmodules to the index to be ready to commit. + * + * @param submodule The newly created submodule ready to open for clone + * @param repo Superproject repository to contain the new submodule + * @param url URL for the submodules remote + * @param path Path at which the submodule should be created + * @param use_gitlink Should workdir contain a gitlink to the repo in + * .git/modules vs. repo directly in workdir. + * @return 0 on success, GIT_EEXISTS if submodule already exists, + * -1 on other errors. */ -GIT_EXTERN(int) git_submodule_lookup( +GIT_EXTERN(int) git_submodule_add_setup( git_submodule **submodule, git_repository *repo, - const char *name); + const char *url, + const char *path, + int use_gitlink); + +/** + * Resolve the setup of a new git submodule. + * + * This should be called on a submodule once you have called add setup + * and done the clone of the submodule. This adds the .gitmodules file + * and the newly cloned submodule to the index to be ready to be committed + * (but doesn't actually do the commit). + */ +GIT_EXTERN(int) git_submodule_add_finalize(git_submodule *submodule); + +/** + * Add current submodule HEAD commit to index of superproject. + */ +GIT_EXTERN(int) git_submodule_add_to_index(git_submodule *submodule); + +/** + * Write submodule settings to .gitmodules file. + * + * This commits any in-memory changes to the submodule to the gitmodules + * file on disk. You may also be interested in `git_submodule_init` which + * writes submodule info to ".git/config" (which is better for local changes + * to submodule settings) and/or `git_submodule_sync` which writes settings + * about remotes to the actual submodule repository. + * + * @param submodule The submodule to write. + * @return 0 on success, <0 on failure. + */ +GIT_EXTERN(int) git_submodule_save(git_submodule *submodule); + +/** + * Get the containing repository for a submodule. + * + * This returns a pointer to the repository that contains the submodule. + * This is a just a reference to the repository that was passed to the + * original `git_submodule_lookup` call, so if that repository has been + * freed, then this may be a dangling reference. + * + * @param submodule Pointer to submodule object + * @return Pointer to `git_repository` + */ +GIT_EXTERN(git_repository *) git_submodule_owner(git_submodule *submodule); + +/** + * Get the name of submodule. + * + * @param submodule Pointer to submodule object + * @return Pointer to the submodule name + */ +GIT_EXTERN(const char *) git_submodule_name(git_submodule *submodule); + +/** + * Get the path to the submodule. + * + * The path is almost always the same as the submodule name, but the + * two are actually not required to match. + * + * @param submodule Pointer to submodule object + * @return Pointer to the submodule path + */ +GIT_EXTERN(const char *) git_submodule_path(git_submodule *submodule); + +/** + * Get the URL for the submodule. + * + * @param submodule Pointer to submodule object + * @return Pointer to the submodule url + */ +GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule); + +/** + * Set the URL for the submodule. + * + * This sets the URL in memory for the submodule. This will be used for + * any following submodule actions while this submodule data is in memory. + * + * After calling this, you may wish to call `git_submodule_save` to write + * the changes back to the ".gitmodules" file and `git_submodule_sync` to + * write the changes to the checked out submodule repository. + * + * @param submodule Pointer to the submodule object + * @param url URL that should be used for the submodule + * @return 0 on success, <0 on failure + */ +GIT_EXTERN(int) git_submodule_set_url(git_submodule *submodule, const char *url); + +/** + * Get the OID for the submodule in the index. + * + * @param submodule Pointer to submodule object + * @return Pointer to git_oid or NULL if submodule is not in index. + */ +GIT_EXTERN(const git_oid *) git_submodule_index_oid(git_submodule *submodule); + +/** + * Get the OID for the submodule in the current HEAD tree. + * + * @param submodule Pointer to submodule object + * @return Pointer to git_oid or NULL if submodule is not in the HEAD. + */ +GIT_EXTERN(const git_oid *) git_submodule_head_oid(git_submodule *submodule); + +/** + * Get the OID for the submodule in the current working directory. + * + * This returns the OID that corresponds to looking up 'HEAD' in the checked + * out submodule. If there are pending changes in the index or anything + * else, this won't notice that. You should call `git_submodule_status` for + * a more complete picture about the state of the working directory. + * + * @param submodule Pointer to submodule object + * @return Pointer to git_oid or NULL if submodule is not checked out. + */ +GIT_EXTERN(const git_oid *) git_submodule_wd_oid(git_submodule *submodule); + +/** + * Get the ignore rule for the submodule. + * + * There are four ignore values: + * + * - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents + * of the submodule from a clean checkout to be dirty, including the + * addition of untracked files. This is the default if unspecified. + * - **GIT_SUBMODULE_IGNORE_UNTRACKED** examines the contents of the + * working tree (i.e. call `git_status_foreach` on the submodule) but + * UNTRACKED files will not count as making the submodule dirty. + * - **GIT_SUBMODULE_IGNORE_DIRTY** means to only check if the HEAD of the + * submodule has moved for status. This is fast since it does not need to + * scan the working tree of the submodule at all. + * - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo. + * The working directory will be consider clean so long as there is a + * checked out version present. + */ +GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore( + git_submodule *submodule); + +/** + * Set the ignore rule for the submodule. + * + * This sets the ignore rule in memory for the submodule. This will be used + * for any following actions (such as `git_submodule_status`) while the + * submodule is in memory. You should call `git_submodule_save` if you want + * to persist the new ignore role. + * + * Calling this again with GIT_SUBMODULE_IGNORE_DEFAULT or calling + * `git_submodule_reload` will revert the rule to the value that was in the + * original config. + * + * @return old value for ignore + */ +GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore( + git_submodule *submodule, + git_submodule_ignore_t ignore); + +/** + * Get the update rule for the submodule. + */ +GIT_EXTERN(git_submodule_update_t) git_submodule_update( + git_submodule *submodule); + +/** + * Set the update rule for the submodule. + * + * This sets the update rule in memory for the submodule. You should call + * `git_submodule_save` if you want to persist the new update rule. + * + * Calling this again with GIT_SUBMODULE_UPDATE_DEFAULT or calling + * `git_submodule_reload` will revert the rule to the value that was in the + * original config. + * + * @return old value for update + */ +GIT_EXTERN(git_submodule_update_t) git_submodule_set_update( + git_submodule *submodule, + git_submodule_update_t update); + +/** + * Copy submodule info into ".git/config" file. + * + * Just like "git submodule init", this copies information about the + * submodule into ".git/config". You can use the accessor functions + * above to alter the in-memory git_submodule object and control what + * is written to the config, overriding what is in .gitmodules. + * + * @param submodule The submodule to write into the superproject config + * @param overwrite By default, existing entries will not be overwritten, + * but setting this to true forces them to be updated. + * @return 0 on success, <0 on failure. + */ +GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite); + +/** + * Copy submodule remote info into submodule repo. + * + * This copies the information about the submodules URL into the checked out + * submodule config, acting like "git submodule sync". This is useful if + * you have altered the URL for the submodule (or it has been altered by a + * fetch of upstream changes) and you need to update your local repo. + */ +GIT_EXTERN(int) git_submodule_sync(git_submodule *submodule); + +/** + * Open the repository for a submodule. + * + * This is a newly opened repository object. The caller is responsible for + * calling `git_repository_free` on it when done. Multiple calls to this + * function will return distinct `git_repository` objects. This will only + * work if the submodule is checked out into the working directory. + * + * @param subrepo Pointer to the submodule repo which was opened + * @param submodule Submodule to be opened + * @return 0 on success, <0 if submodule repo could not be opened. + */ +GIT_EXTERN(int) git_submodule_open( + git_repository **repo, + git_submodule *submodule); + +/** + * Reread submodule info from config, index, and HEAD. + * + * Call this to reread cached submodule information for this submodule if + * you have reason to believe that it has changed. + */ +GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule); + +/** + * Reread all submodule info. + * + * Call this to reload all cached submodule information for the repo. + */ +GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo); + +/** + * Get the status for a submodule. + * + * This looks at a submodule and tries to determine the status. It + * will return a combination of the `GIT_SUBMODULE_STATUS` values above. + * How deeply it examines the working directory to do this will depend + * on the `git_submodule_ignore_t` value for the submodule (which can be + * overridden with `git_submodule_set_ignore()`). + * + * @param status Combination of GIT_SUBMODULE_STATUS values from above. + * @param submodule Submodule for which to get status + * @return 0 on success, <0 on error + */ +GIT_EXTERN(int) git_submodule_status( + unsigned int *status, + git_submodule *submodule); /** @} */ GIT_END_DECL |