mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-21 18:03:54 +00:00
Merge pull request #852 from arrbee/submodule-extensions
Submodule extensions
This commit is contained in:
commit
09fad50696
@ -391,6 +391,21 @@ GIT_EXTERN(int) git_diff_print_patch(
|
|||||||
void *cb_data,
|
void *cb_data,
|
||||||
git_diff_data_fn print_cb);
|
git_diff_data_fn print_cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query how many diff records are there in a diff list.
|
||||||
|
*
|
||||||
|
* You can optionally pass in a `git_delta_t` value if you want a count
|
||||||
|
* of just entries that match that delta type, or pass -1 for all delta
|
||||||
|
* records.
|
||||||
|
*
|
||||||
|
* @param diff A git_diff_list generated by one of the above functions
|
||||||
|
* @param delta_t A git_delta_t value to filter the count, or -1 for all records
|
||||||
|
* @return Count of number of deltas matching delta_t type
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_diff_entrycount(
|
||||||
|
git_diff_list *diff,
|
||||||
|
int delta_t);
|
||||||
|
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ typedef enum {
|
|||||||
GITERR_TREE,
|
GITERR_TREE,
|
||||||
GITERR_INDEXER,
|
GITERR_INDEXER,
|
||||||
GITERR_SSL,
|
GITERR_SSL,
|
||||||
|
GITERR_SUBMODULE,
|
||||||
} git_error_t;
|
} git_error_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,6 +185,8 @@ GIT_EXTERN(int) git_oid_streq(const git_oid *a, const char *str);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check is an oid is all zeros.
|
* Check is an oid is all zeros.
|
||||||
|
*
|
||||||
|
* @return 1 if all zeros, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_oid_iszero(const git_oid *a);
|
GIT_EXTERN(int) git_oid_iszero(const git_oid *a);
|
||||||
|
|
||||||
|
@ -20,54 +20,153 @@
|
|||||||
*/
|
*/
|
||||||
GIT_BEGIN_DECL
|
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 {
|
typedef enum {
|
||||||
|
GIT_SUBMODULE_UPDATE_DEFAULT = -1,
|
||||||
GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
|
GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
|
||||||
GIT_SUBMODULE_UPDATE_REBASE = 1,
|
GIT_SUBMODULE_UPDATE_REBASE = 1,
|
||||||
GIT_SUBMODULE_UPDATE_MERGE = 2
|
GIT_SUBMODULE_UPDATE_MERGE = 2,
|
||||||
|
GIT_SUBMODULE_UPDATE_NONE = 3
|
||||||
} git_submodule_update_t;
|
} 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 {
|
typedef enum {
|
||||||
GIT_SUBMODULE_IGNORE_ALL = 0, /* never dirty */
|
GIT_SUBMODULE_IGNORE_DEFAULT = -1, /* reset to default */
|
||||||
GIT_SUBMODULE_IGNORE_DIRTY = 1, /* only dirty if HEAD moved */
|
GIT_SUBMODULE_IGNORE_NONE = 0, /* any change or untracked == dirty */
|
||||||
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
|
GIT_SUBMODULE_IGNORE_UNTRACKED = 1, /* dirty if tracked files change */
|
||||||
GIT_SUBMODULE_IGNORE_NONE = 3 /* any change or untracked == dirty */
|
GIT_SUBMODULE_IGNORE_DIRTY = 2, /* only dirty if HEAD moved */
|
||||||
|
GIT_SUBMODULE_IGNORE_ALL = 3 /* never dirty */
|
||||||
} git_submodule_ignore_t;
|
} git_submodule_ignore_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of submodule
|
* Return codes for submodule status.
|
||||||
*
|
*
|
||||||
* This record describes a submodule found in a repository. There
|
* A combination of these flags will be returned to describe the status of a
|
||||||
* should be an entry for every submodule found in the HEAD and for
|
* submodule. Depending on the "ignore" property of the submodule, some of
|
||||||
* every submodule described in .gitmodules. The fields are as follows:
|
* the flags may never be returned because they indicate changes that are
|
||||||
|
* supposed to be ignored.
|
||||||
*
|
*
|
||||||
* - `name` is the name of the submodule from .gitmodules.
|
* Submodule info is contained in 4 places: the HEAD tree, the index, config
|
||||||
* - `path` is the path to the submodule from the repo working directory.
|
* files (both .git/config and .gitmodules), and the working directory. Any
|
||||||
* It is almost always the same as `name`.
|
* or all of those places might be missing information about the submodule
|
||||||
* - `url` is the url for the submodule.
|
* depending on what state the repo is in. We consider all four places to
|
||||||
* - `oid` is the HEAD SHA1 for the submodule.
|
* build the combination of status flags.
|
||||||
* - `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.
|
|
||||||
*
|
*
|
||||||
* If the submodule has been added to .gitmodules but not yet git added,
|
* There are four values that are not really status, but give basic info
|
||||||
* then the `oid` will be zero. If the submodule has been deleted, but
|
* about what sources of submodule data are available. These will be
|
||||||
* the delete has not been committed yet, then the `oid` will be set, but
|
* returned even if ignore is set to "ALL".
|
||||||
* the `url` will be NULL.
|
*
|
||||||
|
* * IN_HEAD - superproject head contains submodule
|
||||||
|
* * IN_INDEX - superproject index contains submodule
|
||||||
|
* * IN_CONFIG - superproject gitmodules has submodule
|
||||||
|
* * IN_WD - superproject workdir has submodule
|
||||||
|
*
|
||||||
|
* The following values will be returned so long as ignore is not "ALL".
|
||||||
|
*
|
||||||
|
* * INDEX_ADDED - in index, not in head
|
||||||
|
* * INDEX_DELETED - in head, not in index
|
||||||
|
* * INDEX_MODIFIED - index and head don't match
|
||||||
|
* * WD_UNINITIALIZED - workdir contains empty directory
|
||||||
|
* * WD_ADDED - in workdir, not index
|
||||||
|
* * WD_DELETED - in index, not workdir
|
||||||
|
* * WD_MODIFIED - index and workdir head don't match
|
||||||
|
*
|
||||||
|
* The following can only be returned if ignore is "NONE" or "UNTRACKED".
|
||||||
|
*
|
||||||
|
* * WD_INDEX_MODIFIED - submodule workdir index is dirty
|
||||||
|
* * WD_WD_MODIFIED - submodule workdir has modified files
|
||||||
|
*
|
||||||
|
* Lastly, the following will only be returned for ignore "NONE".
|
||||||
|
*
|
||||||
|
* * WD_UNTRACKED - wd contains untracked files
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef enum {
|
||||||
char *name;
|
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
||||||
char *path;
|
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
||||||
char *url;
|
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
||||||
git_oid oid; /* sha1 of submodule HEAD ref or zero if not committed */
|
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
||||||
git_submodule_update_t update;
|
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
||||||
git_submodule_ignore_t ignore;
|
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
||||||
int fetch_recurse;
|
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
||||||
int refcount;
|
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
||||||
} git_submodule;
|
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
||||||
|
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
||||||
|
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
||||||
|
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
||||||
|
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
||||||
|
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||||
|
} git_submodule_status_t;
|
||||||
|
|
||||||
|
#define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
|
||||||
|
(((S) & ~(GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | \
|
||||||
|
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD)) == 0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate over all submodules of a repository.
|
* 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:
|
||||||
|
*
|
||||||
|
* - 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_submodule_lookup(
|
||||||
|
git_submodule **submodule,
|
||||||
|
git_repository *repo,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 repo The repository
|
||||||
* @param callback Function to be called with the name of each submodule.
|
* @param callback Function to be called with the name of each submodule.
|
||||||
@ -77,26 +176,297 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_submodule_foreach(
|
GIT_EXTERN(int) git_submodule_foreach(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
int (*callback)(const char *name, void *payload),
|
int (*callback)(git_submodule *sm, const char *name, void *payload),
|
||||||
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 does "git submodule add" up to the fetch and checkout of the
|
||||||
* this returns a structure describing the submodule. If the submodule
|
* submodule contents. It preps a new submodule, creates an entry in
|
||||||
* does not exist, this will return GIT_ENOTFOUND and set the submodule
|
* .gitmodules and creates an empty initialized repository either at the
|
||||||
* pointer to NULL.
|
* 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..
|
* To fully emulate "git submodule add" call this function, then open the
|
||||||
* @param repo The repository.
|
* submodule repo and perform the clone step as needed. Lastly, call
|
||||||
* @param name The name of the submodule. Trailing slashes will be ignored.
|
* `git_submodule_add_finalize()` to wrap up adding the new submodule and
|
||||||
* @return 0 on success, GIT_ENOTFOUND if submodule does not exist, -1 on error
|
* .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_submodule **submodule,
|
||||||
git_repository *repo,
|
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).
|
||||||
|
*
|
||||||
|
* @param submodule The submodule to finish adding.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_submodule_add_finalize(git_submodule *submodule);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add current submodule HEAD commit to index of superproject.
|
||||||
|
*
|
||||||
|
* @param submodule The submodule to add to the index
|
||||||
|
* @param write_index Boolean if this should immediately write the index
|
||||||
|
* file. If you pass this as false, you will have to get the
|
||||||
|
* git_index and explicitly call `git_index_write()` on it to
|
||||||
|
* save the change.
|
||||||
|
* @return 0 on success, <0 on failure
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_submodule_add_to_index(
|
||||||
|
git_submodule *submodule,
|
||||||
|
int write_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* set either temporarily or permanently with `git_submodule_set_ignore()`.
|
||||||
|
*
|
||||||
|
* @param status Combination of `GIT_SUBMODULE_STATUS` flags
|
||||||
|
* @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
|
GIT_END_DECL
|
||||||
|
37
src/buffer.c
37
src/buffer.c
@ -144,31 +144,40 @@ int git_buf_puts(git_buf *buf, const char *string)
|
|||||||
int git_buf_puts_escaped(
|
int git_buf_puts_escaped(
|
||||||
git_buf *buf, const char *string, const char *esc_chars, const char *esc_with)
|
git_buf *buf, const char *string, const char *esc_chars, const char *esc_with)
|
||||||
{
|
{
|
||||||
const char *scan = string;
|
const char *scan;
|
||||||
size_t total = 0, esc_with_len = strlen(esc_with);
|
size_t total = 0, esc_len = strlen(esc_with), count;
|
||||||
|
|
||||||
while (*scan) {
|
if (!string)
|
||||||
size_t count = strcspn(scan, esc_chars);
|
return 0;
|
||||||
total += count + 1 + esc_with_len;
|
|
||||||
scan += count + 1;
|
for (scan = string; *scan; ) {
|
||||||
|
/* count run of non-escaped characters */
|
||||||
|
count = strcspn(scan, esc_chars);
|
||||||
|
total += count;
|
||||||
|
scan += count;
|
||||||
|
/* count run of escaped characters */
|
||||||
|
count = strspn(scan, esc_chars);
|
||||||
|
total += count * (esc_len + 1);
|
||||||
|
scan += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
ENSURE_SIZE(buf, buf->size + total + 1);
|
ENSURE_SIZE(buf, buf->size + total + 1);
|
||||||
|
|
||||||
for (scan = string; *scan; ) {
|
for (scan = string; *scan; ) {
|
||||||
size_t count = strcspn(scan, esc_chars);
|
count = strcspn(scan, esc_chars);
|
||||||
|
|
||||||
memmove(buf->ptr + buf->size, scan, count);
|
memmove(buf->ptr + buf->size, scan, count);
|
||||||
scan += count;
|
scan += count;
|
||||||
buf->size += count;
|
buf->size += count;
|
||||||
|
|
||||||
if (*scan) {
|
for (count = strspn(scan, esc_chars); count > 0; --count) {
|
||||||
memmove(buf->ptr + buf->size, esc_with, esc_with_len);
|
/* copy escape sequence */
|
||||||
buf->size += esc_with_len;
|
memmove(buf->ptr + buf->size, esc_with, esc_len);
|
||||||
|
buf->size += esc_len;
|
||||||
memmove(buf->ptr + buf->size, scan, 1);
|
/* copy character to be escaped */
|
||||||
scan += 1;
|
buf->ptr[buf->size] = *scan;
|
||||||
buf->size += 1;
|
buf->size++;
|
||||||
|
scan++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ typedef struct tree_walk_data
|
|||||||
git_checkout_opts *opts;
|
git_checkout_opts *opts;
|
||||||
git_repository *repo;
|
git_repository *repo;
|
||||||
git_odb *odb;
|
git_odb *odb;
|
||||||
bool do_symlinks;
|
bool no_symlinks;
|
||||||
} tree_walk_data;
|
} tree_walk_data;
|
||||||
|
|
||||||
|
|
||||||
@ -48,9 +48,9 @@ static int blob_contents_to_link(tree_walk_data *data, git_buf *fnbuf,
|
|||||||
/* Create the link */
|
/* Create the link */
|
||||||
const char *new = git_buf_cstr(&linktarget),
|
const char *new = git_buf_cstr(&linktarget),
|
||||||
*old = git_buf_cstr(fnbuf);
|
*old = git_buf_cstr(fnbuf);
|
||||||
retcode = data->do_symlinks
|
retcode = data->no_symlinks
|
||||||
? p_symlink(new, old)
|
? git_futils_fake_symlink(new, old)
|
||||||
: git_futils_fake_symlink(new, old);
|
: p_symlink(new, old);
|
||||||
}
|
}
|
||||||
git_buf_free(&linktarget);
|
git_buf_free(&linktarget);
|
||||||
git_blob_free(blob);
|
git_blob_free(blob);
|
||||||
@ -176,13 +176,14 @@ int git_checkout_head(git_repository *repo, git_checkout_opts *opts, git_indexer
|
|||||||
return GIT_ERROR;
|
return GIT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&payload, 0, sizeof(payload));
|
||||||
|
|
||||||
/* Determine if symlinks should be handled */
|
/* Determine if symlinks should be handled */
|
||||||
if (!git_repository_config(&cfg, repo)) {
|
if (!git_repository_config__weakptr(&cfg, repo)) {
|
||||||
int temp = true;
|
int temp = true;
|
||||||
if (!git_config_get_bool(&temp, cfg, "core.symlinks")) {
|
if (!git_config_get_bool(&temp, cfg, "core.symlinks")) {
|
||||||
payload.do_symlinks = !!temp;
|
payload.no_symlinks = !temp;
|
||||||
}
|
}
|
||||||
git_config_free(cfg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stats->total = stats->processed = 0;
|
stats->total = stats->processed = 0;
|
||||||
|
@ -195,7 +195,7 @@ static int file_foreach(
|
|||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
diskfile_backend *b = (diskfile_backend *)backend;
|
diskfile_backend *b = (diskfile_backend *)backend;
|
||||||
cvar_t *var;
|
cvar_t *var, *next_var;
|
||||||
const char *key;
|
const char *key;
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
@ -212,7 +212,9 @@ static int file_foreach(
|
|||||||
}
|
}
|
||||||
|
|
||||||
git_strmap_foreach(b->values, key, var,
|
git_strmap_foreach(b->values, key, var,
|
||||||
for (; var != NULL; var = CVAR_LIST_NEXT(var)) {
|
for (; var != NULL; var = next_var) {
|
||||||
|
next_var = CVAR_LIST_NEXT(var);
|
||||||
|
|
||||||
/* skip non-matching keys if regexp was provided */
|
/* skip non-matching keys if regexp was provided */
|
||||||
if (regexp && regexec(®ex, key, 0, NULL, 0) != 0)
|
if (regexp && regexec(®ex, key, 0, NULL, 0) != 0)
|
||||||
continue;
|
continue;
|
||||||
@ -253,11 +255,17 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
|
|||||||
char *tmp = NULL;
|
char *tmp = NULL;
|
||||||
|
|
||||||
git__free(key);
|
git__free(key);
|
||||||
|
|
||||||
if (existing->next != NULL) {
|
if (existing->next != NULL) {
|
||||||
giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
|
giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* don't update if old and new values already match */
|
||||||
|
if ((!existing->value && !value) ||
|
||||||
|
(existing->value && value && !strcmp(existing->value, value)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
tmp = git__strdup(value);
|
tmp = git__strdup(value);
|
||||||
GITERR_CHECK_ALLOC(tmp);
|
GITERR_CHECK_ALLOC(tmp);
|
||||||
|
@ -19,12 +19,24 @@ GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
|
|||||||
cfg->free(cfg);
|
cfg->free(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GIT_INLINE(int) git_config_file_get_string(
|
||||||
|
const char **out, git_config_file *cfg, const char *name)
|
||||||
|
{
|
||||||
|
return cfg->get(cfg, name, out);
|
||||||
|
}
|
||||||
|
|
||||||
GIT_INLINE(int) git_config_file_set_string(
|
GIT_INLINE(int) git_config_file_set_string(
|
||||||
git_config_file *cfg, const char *name, const char *value)
|
git_config_file *cfg, const char *name, const char *value)
|
||||||
{
|
{
|
||||||
return cfg->set(cfg, name, value);
|
return cfg->set(cfg, name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GIT_INLINE(int) git_config_file_delete(
|
||||||
|
git_config_file *cfg, const char *name)
|
||||||
|
{
|
||||||
|
return cfg->del(cfg, name);
|
||||||
|
}
|
||||||
|
|
||||||
GIT_INLINE(int) git_config_file_foreach(
|
GIT_INLINE(int) git_config_file_foreach(
|
||||||
git_config_file *cfg,
|
git_config_file *cfg,
|
||||||
int (*fn)(const char *key, const char *value, void *data),
|
int (*fn)(const char *key, const char *value, void *data),
|
||||||
|
@ -530,7 +530,7 @@ static int maybe_modified(
|
|||||||
status = GIT_DELTA_UNMODIFIED;
|
status = GIT_DELTA_UNMODIFIED;
|
||||||
else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
|
else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
|
else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
|
||||||
status = GIT_DELTA_UNMODIFIED;
|
status = GIT_DELTA_UNMODIFIED;
|
||||||
else {
|
else {
|
||||||
/* TODO: support other GIT_SUBMODULE_IGNORE values */
|
/* TODO: support other GIT_SUBMODULE_IGNORE values */
|
||||||
|
@ -718,6 +718,25 @@ int git_diff_print_patch(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_diff_entrycount(git_diff_list *diff, int delta_t)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
unsigned int i;
|
||||||
|
git_diff_delta *delta;
|
||||||
|
|
||||||
|
assert(diff);
|
||||||
|
|
||||||
|
if (delta_t < 0)
|
||||||
|
return diff->deltas.length;
|
||||||
|
|
||||||
|
git_vector_foreach(&diff->deltas, i, delta) {
|
||||||
|
if (delta->status == (git_delta_t)delta_t)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
int git_diff_blobs(
|
int git_diff_blobs(
|
||||||
git_blob *old_blob,
|
git_blob *old_blob,
|
||||||
git_blob *new_blob,
|
git_blob *new_blob,
|
||||||
|
@ -110,6 +110,11 @@ void giterr_set_regex(const regex_t *regex, int error_code)
|
|||||||
void giterr_clear(void)
|
void giterr_clear(void)
|
||||||
{
|
{
|
||||||
GIT_GLOBAL->last_error = NULL;
|
GIT_GLOBAL->last_error = NULL;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
#ifdef GIT_WIN32
|
||||||
|
SetLastError(0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const git_error *giterr_last(void)
|
const git_error *giterr_last(void)
|
||||||
|
@ -50,6 +50,7 @@ static int lock_file(git_filebuf *file, int flags)
|
|||||||
if (flags & GIT_FILEBUF_FORCE)
|
if (flags & GIT_FILEBUF_FORCE)
|
||||||
p_unlink(file->path_lock);
|
p_unlink(file->path_lock);
|
||||||
else {
|
else {
|
||||||
|
giterr_clear(); /* actual OS error code just confuses */
|
||||||
giterr_set(GITERR_OS,
|
giterr_set(GITERR_OS,
|
||||||
"Failed to lock file '%s' for writing", file->path_lock);
|
"Failed to lock file '%s' for writing", file->path_lock);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -525,7 +525,9 @@ static int workdir_iterator__advance(
|
|||||||
while ((wf = wi->stack) != NULL) {
|
while ((wf = wi->stack) != NULL) {
|
||||||
next = git_vector_get(&wf->entries, ++wf->index);
|
next = git_vector_get(&wf->entries, ++wf->index);
|
||||||
if (next != NULL) {
|
if (next != NULL) {
|
||||||
if (strcmp(next->path, DOT_GIT "/") == 0)
|
/* match git's behavior of ignoring anything named ".git" */
|
||||||
|
if (strcmp(next->path, DOT_GIT "/") == 0 ||
|
||||||
|
strcmp(next->path, DOT_GIT) == 0)
|
||||||
continue;
|
continue;
|
||||||
/* else found a good entry */
|
/* else found a good entry */
|
||||||
break;
|
break;
|
||||||
@ -607,8 +609,8 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
|
|||||||
|
|
||||||
wi->entry.path = ps->path;
|
wi->entry.path = ps->path;
|
||||||
|
|
||||||
/* skip over .git directory */
|
/* skip over .git entry */
|
||||||
if (strcmp(ps->path, DOT_GIT "/") == 0)
|
if (strcmp(ps->path, DOT_GIT "/") == 0 || strcmp(ps->path, DOT_GIT) == 0)
|
||||||
return workdir_iterator__advance((git_iterator *)wi, NULL);
|
return workdir_iterator__advance((git_iterator *)wi, NULL);
|
||||||
|
|
||||||
/* if there is an error processing the entry, treat as ignored */
|
/* if there is an error processing the entry, treat as ignored */
|
||||||
@ -629,15 +631,10 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
|
|||||||
|
|
||||||
/* detect submodules */
|
/* detect submodules */
|
||||||
if (S_ISDIR(wi->entry.mode)) {
|
if (S_ISDIR(wi->entry.mode)) {
|
||||||
bool is_submodule = git_path_contains(&wi->path, DOT_GIT);
|
|
||||||
|
|
||||||
/* if there is no .git, still check submodules data */
|
|
||||||
if (!is_submodule) {
|
|
||||||
int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
|
int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
|
||||||
is_submodule = (res == 0);
|
bool is_submodule = (res == 0);
|
||||||
if (res == GIT_ENOTFOUND)
|
if (res == GIT_ENOTFOUND)
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
}
|
|
||||||
|
|
||||||
/* if submodule, mark as GITLINK and remove trailing slash */
|
/* if submodule, mark as GITLINK and remove trailing slash */
|
||||||
if (is_submodule) {
|
if (is_submodule) {
|
||||||
|
@ -146,8 +146,13 @@ static int load_workdir(git_repository *repo, git_buf *parent_path)
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
error = git_config_get_string(&worktree, config, "core.worktree");
|
error = git_config_get_string(&worktree, config, "core.worktree");
|
||||||
if (!error && worktree != NULL)
|
if (!error && worktree != NULL) {
|
||||||
repo->workdir = git__strdup(worktree);
|
error = git_path_prettify_dir(
|
||||||
|
&worktree_buf, worktree, repo->path_repository);
|
||||||
|
if (error < 0)
|
||||||
|
return error;
|
||||||
|
repo->workdir = git_buf_detach(&worktree_buf);
|
||||||
|
}
|
||||||
else if (error != GIT_ENOTFOUND)
|
else if (error != GIT_ENOTFOUND)
|
||||||
return error;
|
return error;
|
||||||
else {
|
else {
|
||||||
|
1653
src/submodule.c
1653
src/submodule.c
File diff suppressed because it is too large
Load Diff
102
src/submodule.h
Normal file
102
src/submodule.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 the libgit2 contributors
|
||||||
|
*
|
||||||
|
* 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_submodule_h__
|
||||||
|
#define INCLUDE_submodule_h__
|
||||||
|
|
||||||
|
/* Notes:
|
||||||
|
*
|
||||||
|
* Submodule information can be in four places: the index, the config files
|
||||||
|
* (both .git/config and .gitmodules), the HEAD tree, and the working
|
||||||
|
* directory.
|
||||||
|
*
|
||||||
|
* In the index:
|
||||||
|
* - submodule is found by path
|
||||||
|
* - may be missing, present, or of the wrong type
|
||||||
|
* - will have an oid if present
|
||||||
|
*
|
||||||
|
* In the HEAD tree:
|
||||||
|
* - submodule is found by path
|
||||||
|
* - may be missing, present, or of the wrong type
|
||||||
|
* - will have an oid if present
|
||||||
|
*
|
||||||
|
* In the config files:
|
||||||
|
* - submodule is found by submodule "name" which is usually the path
|
||||||
|
* - may be missing or present
|
||||||
|
* - will have a name, path, url, and other properties
|
||||||
|
*
|
||||||
|
* In the working directory:
|
||||||
|
* - submodule is found by path
|
||||||
|
* - may be missing, an empty directory, a checked out directory,
|
||||||
|
* or of the wrong type
|
||||||
|
* - if checked out, will have a HEAD oid
|
||||||
|
* - if checked out, will have git history that can be used to compare oids
|
||||||
|
* - if checked out, may have modified files and/or untracked files
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of submodule
|
||||||
|
*
|
||||||
|
* This record describes a submodule found in a repository. There should be
|
||||||
|
* an entry for every submodule found in the HEAD and index, and for every
|
||||||
|
* submodule described in .gitmodules. The fields are as follows:
|
||||||
|
*
|
||||||
|
* - `owner` is the git_repository containing this submodule
|
||||||
|
* - `name` is the name of the submodule from .gitmodules.
|
||||||
|
* - `path` is the path to the submodule from the repo root. It is almost
|
||||||
|
* always the same as `name`.
|
||||||
|
* - `url` is the url for the submodule.
|
||||||
|
* - `tree_oid` is the SHA1 for the submodule path in the repo HEAD.
|
||||||
|
* - `index_oid` is the SHA1 for the submodule recorded in the index.
|
||||||
|
* - `workdir_oid` is the SHA1 for the HEAD of the checked out submodule.
|
||||||
|
* - `update` is a git_submodule_update_t value - see gitmodules(5) update.
|
||||||
|
* - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore.
|
||||||
|
* - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
|
||||||
|
* - `refcount` tracks how many hashmap entries there are for this submodule.
|
||||||
|
* It only comes into play if the name and path of the submodule differ.
|
||||||
|
* - `flags` is for internal use, tracking where this submodule has been
|
||||||
|
* found (head, index, config, workdir) and other misc info about it.
|
||||||
|
*
|
||||||
|
* If the submodule has been added to .gitmodules but not yet git added,
|
||||||
|
* then the `index_oid` will be valid and zero. If the submodule has been
|
||||||
|
* deleted, but the delete has not been committed yet, then the `index_oid`
|
||||||
|
* will be set, but the `url` will be NULL.
|
||||||
|
*/
|
||||||
|
struct git_submodule {
|
||||||
|
git_repository *owner;
|
||||||
|
char *name;
|
||||||
|
char *path; /* important: may point to same string data as "name" */
|
||||||
|
char *url;
|
||||||
|
uint32_t flags;
|
||||||
|
git_oid head_oid;
|
||||||
|
git_oid index_oid;
|
||||||
|
git_oid wd_oid;
|
||||||
|
/* information from config */
|
||||||
|
git_submodule_update_t update;
|
||||||
|
git_submodule_update_t update_default;
|
||||||
|
git_submodule_ignore_t ignore;
|
||||||
|
git_submodule_ignore_t ignore_default;
|
||||||
|
int fetch_recurse;
|
||||||
|
/* internal information */
|
||||||
|
int refcount;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
|
||||||
|
enum {
|
||||||
|
GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20),
|
||||||
|
GIT_SUBMODULE_STATUS__HEAD_OID_VALID = (1u << 21),
|
||||||
|
GIT_SUBMODULE_STATUS__INDEX_OID_VALID = (1u << 22),
|
||||||
|
GIT_SUBMODULE_STATUS__WD_OID_VALID = (1u << 23),
|
||||||
|
GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE = (1u << 24),
|
||||||
|
GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE = (1u << 25),
|
||||||
|
GIT_SUBMODULE_STATUS__WD_NOT_SUBMODULE = (1u << 26),
|
||||||
|
GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27),
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
|
||||||
|
((S) & ~(0xFFFFFFFFu << 20))
|
||||||
|
|
||||||
|
#endif
|
1
tests-clar/resources/submod2/.gitted/HEAD
Normal file
1
tests-clar/resources/submod2/.gitted/HEAD
Normal file
@ -0,0 +1 @@
|
|||||||
|
ref: refs/heads/master
|
20
tests-clar/resources/submod2/.gitted/config
Normal file
20
tests-clar/resources/submod2/.gitted/config
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = false
|
||||||
|
logallrefupdates = true
|
||||||
|
ignorecase = true
|
||||||
|
[submodule "sm_missing_commits"]
|
||||||
|
url = ../submod2_target
|
||||||
|
[submodule "sm_unchanged"]
|
||||||
|
url = ../submod2_target
|
||||||
|
[submodule "sm_changed_file"]
|
||||||
|
url = ../submod2_target
|
||||||
|
[submodule "sm_changed_index"]
|
||||||
|
url = ../submod2_target
|
||||||
|
[submodule "sm_changed_head"]
|
||||||
|
url = ../submod2_target
|
||||||
|
[submodule "sm_changed_untracked_file"]
|
||||||
|
url = ../submod2_target
|
||||||
|
[submodule "sm_added_and_uncommited"]
|
||||||
|
url = ../submod2_target
|
1
tests-clar/resources/submod2/.gitted/description
Normal file
1
tests-clar/resources/submod2/.gitted/description
Normal file
@ -0,0 +1 @@
|
|||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
15
tests-clar/resources/submod2/.gitted/hooks/applypatch-msg.sample
Executable file
15
tests-clar/resources/submod2/.gitted/hooks/applypatch-msg.sample
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to check the commit log message taken by
|
||||||
|
# applypatch from an e-mail message.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an
|
||||||
|
# appropriate message if it wants to stop the commit. The hook is
|
||||||
|
# allowed to edit the commit message file.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "applypatch-msg".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
test -x "$GIT_DIR/hooks/commit-msg" &&
|
||||||
|
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
||||||
|
:
|
BIN
tests-clar/resources/submod2/.gitted/index
Normal file
BIN
tests-clar/resources/submod2/.gitted/index
Normal file
Binary file not shown.
6
tests-clar/resources/submod2/.gitted/info/exclude
Normal file
6
tests-clar/resources/submod2/.gitted/info/exclude
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
4
tests-clar/resources/submod2/.gitted/logs/HEAD
Normal file
4
tests-clar/resources/submod2/.gitted/logs/HEAD
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer <rb@github.com> 1342559771 -0700 commit (initial): Initial commit
|
||||||
|
14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer <rb@github.com> 1342559831 -0700 commit: Adding a submodule
|
||||||
|
a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer <rb@github.com> 1342560036 -0700 commit: Updating submodule
|
||||||
|
5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer <rb@github.com> 1342560288 -0700 commit: Adding a bunch more test content
|
@ -0,0 +1,4 @@
|
|||||||
|
0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer <rb@github.com> 1342559771 -0700 commit (initial): Initial commit
|
||||||
|
14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer <rb@github.com> 1342559831 -0700 commit: Adding a submodule
|
||||||
|
a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer <rb@github.com> 1342560036 -0700 commit: Updating submodule
|
||||||
|
5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer <rb@github.com> 1342560288 -0700 commit: Adding a bunch more test content
|
@ -0,0 +1 @@
|
|||||||
|
ref: refs/heads/master
|
@ -0,0 +1,13 @@
|
|||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = false
|
||||||
|
logallrefupdates = true
|
||||||
|
worktree = ../../../sm_added_and_uncommited
|
||||||
|
ignorecase = true
|
||||||
|
[remote "origin"]
|
||||||
|
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||||
|
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
||||||
|
[branch "master"]
|
||||||
|
remote = origin
|
||||||
|
merge = refs/heads/master
|
@ -0,0 +1 @@
|
|||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to check the commit log message taken by
|
||||||
|
# applypatch from an e-mail message.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an
|
||||||
|
# appropriate message if it wants to stop the commit. The hook is
|
||||||
|
# allowed to edit the commit message file.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "applypatch-msg".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
test -x "$GIT_DIR/hooks/commit-msg" &&
|
||||||
|
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
||||||
|
:
|
Binary file not shown.
@ -0,0 +1,6 @@
|
|||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
@ -0,0 +1 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
@ -0,0 +1 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
@ -0,0 +1 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
x•<>Q
|
||||||
|
!EϋvoΕΣy*Ρ_Ών@Ηg#h‚£ϋOhύ^Ξ9w«¥¤<0E>κSoΜ€f1*²<>Α[”<18>‰¬§θIc Τ¤<CEA4>μκ¤p£οµΑkηΞ‘\›ΏΏSί‡Ώlµά@.¤΄^<5E>QpF‹(ζ:<3A>ϊD<CF8A>5Εσ“zr~ ρen8
|
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
x-Ë1Â0Faæžâߨ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£<E280A6>'©î`)”.Ø-1¨x
|
||||||
|
u„xãòt(+
|
@ -0,0 +1,2 @@
|
|||||||
|
x
α
…0<>)ÞŠ?=
¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr
”
|
||||||
|
ïqJWñ°7<EFBFBD>¾B<<3C>ÉáöfÙìK8#Q1C-‘"eª·Ì«£Š°ð>¼'@
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
# pack-refs with: peeled
|
||||||
|
480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
|
@ -0,0 +1 @@
|
|||||||
|
480095882d281ed676fe5b863569520e54a7d5c0
|
@ -0,0 +1 @@
|
|||||||
|
ref: refs/remotes/origin/master
|
@ -0,0 +1 @@
|
|||||||
|
ref: refs/heads/master
|
@ -0,0 +1,13 @@
|
|||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = false
|
||||||
|
logallrefupdates = true
|
||||||
|
worktree = ../../../sm_changed_file
|
||||||
|
ignorecase = true
|
||||||
|
[remote "origin"]
|
||||||
|
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||||
|
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
||||||
|
[branch "master"]
|
||||||
|
remote = origin
|
||||||
|
merge = refs/heads/master
|
@ -0,0 +1 @@
|
|||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to check the commit log message taken by
|
||||||
|
# applypatch from an e-mail message.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an
|
||||||
|
# appropriate message if it wants to stop the commit. The hook is
|
||||||
|
# allowed to edit the commit message file.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "applypatch-msg".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
test -x "$GIT_DIR/hooks/commit-msg" &&
|
||||||
|
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
||||||
|
:
|
Binary file not shown.
@ -0,0 +1,6 @@
|
|||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
@ -0,0 +1 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
@ -0,0 +1 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
@ -0,0 +1 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
x•<>Q
|
||||||
|
!EϋvoΕΣy*Ρ_Ών@Ηg#h‚£ϋOhύ^Ξ9w«¥¤<0E>κSoΜ€f1*²<>Α[”<18>‰¬§θIc Τ¤<CEA4>μκ¤p£οµΑkηΞ‘\›ΏΏSί‡Ώlµά@.¤΄^<5E>QpF‹(ζ:<3A>ϊD<CF8A>5Εσ“zr~ ρen8
|
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
x-Ë1Â0Faæžâߨ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£<E280A6>'©î`)”.Ø-1¨x
|
||||||
|
u„xãòt(+
|
@ -0,0 +1,2 @@
|
|||||||
|
x
α
…0<>)ÞŠ?=
¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr
”
|
||||||
|
ïqJWñ°7<EFBFBD>¾B<<3C>ÉáöfÙìK8#Q1C-‘"eª·Ì«£Š°ð>¼'@
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
# pack-refs with: peeled
|
||||||
|
480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
|
@ -0,0 +1 @@
|
|||||||
|
480095882d281ed676fe5b863569520e54a7d5c0
|
@ -0,0 +1 @@
|
|||||||
|
ref: refs/remotes/origin/master
|
@ -0,0 +1 @@
|
|||||||
|
Making a change in a submodule
|
@ -0,0 +1 @@
|
|||||||
|
ref: refs/heads/master
|
@ -0,0 +1,13 @@
|
|||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = false
|
||||||
|
logallrefupdates = true
|
||||||
|
worktree = ../../../sm_changed_head
|
||||||
|
ignorecase = true
|
||||||
|
[remote "origin"]
|
||||||
|
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||||
|
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
||||||
|
[branch "master"]
|
||||||
|
remote = origin
|
||||||
|
merge = refs/heads/master
|
@ -0,0 +1 @@
|
|||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to check the commit log message taken by
|
||||||
|
# applypatch from an e-mail message.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an
|
||||||
|
# appropriate message if it wants to stop the commit. The hook is
|
||||||
|
# allowed to edit the commit message file.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "applypatch-msg".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
test -x "$GIT_DIR/hooks/commit-msg" &&
|
||||||
|
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
||||||
|
:
|
Binary file not shown.
@ -0,0 +1,6 @@
|
|||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
@ -0,0 +1,2 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
||||||
|
480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer <rb@github.com> 1342560431 -0700 commit: Making a change in a submodule
|
@ -0,0 +1,2 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
||||||
|
480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer <rb@github.com> 1342560431 -0700 commit: Making a change in a submodule
|
@ -0,0 +1 @@
|
|||||||
|
0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@
|
|||||||
|
x•ŽKj!E3vµ<>„jµüŔ#<Ţ<“ě@ęéO°u˙q™.çÂ)×ql
´‰oŠ€÷sFa#Čv‰ŤÓĹ)g#{':ŞßTĺl`b¤4ë0 ;ďfˇár‘4
|
||||||
|
Ůä™
|
||||||
|
ŞÔŰzUřî÷-ű/Ůg©đ¨ůąlmíůŁ\Ç'LĆjrhÍďčŐXG_ęźę+ýlç ĘšÎE`;ß=÷]ÔŢJç
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
x•<>Q
|
||||||
|
!EϋvoΕΣy*Ρ_Ών@Ηg#h‚£ϋOhύ^Ξ9w«¥¤<0E>κSoΜ€f1*²<>Α[”<18>‰¬§θIc Τ¤<CEA4>μκ¤p£οµΑkηΞ‘\›ΏΏSί‡Ώlµά@.¤΄^<5E>QpF‹(ζ:<3A>ϊD<CF8A>5Εσ“zr~ ρen8
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
x-Ë1Â0Faæžâߨ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£<E280A6>'©î`)”.Ø-1¨x
|
||||||
|
u„xãòt(+
|
@ -0,0 +1,2 @@
|
|||||||
|
x
α
…0<>)ÞŠ?=
¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr
”
|
||||||
|
ïqJWñ°7<EFBFBD>¾B<<3C>ÉáöfÙìK8#Q1C-‘"eª·Ì«£Š°ð>¼'@
|
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
xMM;
|
||||||
|
1µÎ)Þ ÁZPÐÞÆr²3kÉl²En¿ƒl!¼æýc±ˆóõrz§Üà ,¹º¡çe +Ú<16>lEZxuPY…x QC³*ðf·uLácfR3ŠÍT0'Ò¯øjƒŠ°ð~G¦^s1Šèb<>2z’ƒÿùVkî]Ü5<·ûv¨'>ã
|
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
# pack-refs with: peeled
|
||||||
|
480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
|
@ -0,0 +1 @@
|
|||||||
|
3d9386c507f6b093471a3e324085657a3c2b4247
|
@ -0,0 +1 @@
|
|||||||
|
ref: refs/remotes/origin/master
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user