mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-05 20:33:41 +00:00
Merge pull request #844 from arrbee/init-extended
Add git_repository_init_ext for power initters
This commit is contained in:
commit
c920e16232
@ -80,15 +80,16 @@ GIT_EXTERN(int) git_config_find_global(char *global_config_path, size_t length);
|
||||
GIT_EXTERN(int) git_config_find_system(char *system_config_path, size_t length);
|
||||
|
||||
/**
|
||||
* Open the global configuration file
|
||||
* Open the global and system configuration files
|
||||
*
|
||||
* Utility wrapper that calls `git_config_find_global`
|
||||
* and opens the located file, if it exists.
|
||||
* Utility wrapper that finds the global and system configuration files
|
||||
* and opens them into a single prioritized config object that can be
|
||||
* used when accessing default config data outside a repository.
|
||||
*
|
||||
* @param out Pointer to store the config instance
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_open_global(git_config **out);
|
||||
GIT_EXTERN(int) git_config_open_default(git_config **out);
|
||||
|
||||
/**
|
||||
* Create a configuration file backend for ondisk files
|
||||
|
@ -83,6 +83,18 @@ GIT_EXTERN(int) git_repository_discover(
|
||||
int across_fs,
|
||||
const char *ceiling_dirs);
|
||||
|
||||
/**
|
||||
* Option flags for `git_repository_open_ext`.
|
||||
*
|
||||
* * GIT_REPOSITORY_OPEN_NO_SEARCH - Only open the repository if it can be
|
||||
* immediately found in the start_path. Do not walk up from the
|
||||
* start_path looking at parent directories.
|
||||
* * GIT_REPOSITORY_OPEN_CROSS_FS - Unless this flag is set, open will not
|
||||
* continue searching across filesystem boundaries (i.e. when `st_dev`
|
||||
* changes from the `stat` system call). (E.g. Searching in a user's home
|
||||
* directory "/home/user/source/" will not return "/.git/" as the found
|
||||
* repo if "/" is a different filesystem than "/home".)
|
||||
*/
|
||||
enum {
|
||||
GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
|
||||
GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
|
||||
@ -90,6 +102,20 @@ enum {
|
||||
|
||||
/**
|
||||
* Find and open a repository with extended controls.
|
||||
*
|
||||
* @param repo_out Pointer to the repo which will be opened. This can
|
||||
* actually be NULL if you only want to use the error code to
|
||||
* see if a repo at this path could be opened.
|
||||
* @param start_path Path to open as git repository. If the flags
|
||||
* permit "searching", then this can be a path to a subdirectory
|
||||
* inside the working directory of the repository.
|
||||
* @param flags A combination of the GIT_REPOSITORY_OPEN flags above.
|
||||
* @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR delimited list of path
|
||||
* prefixes at which the search for a containing repository should
|
||||
* terminate.
|
||||
* @return 0 on success, GIT_ENOTFOUND if no repository could be found,
|
||||
* or -1 if there was a repository but open failed for some reason
|
||||
* (such as repo corruption or system errors).
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_open_ext(
|
||||
git_repository **repo,
|
||||
@ -118,13 +144,127 @@ GIT_EXTERN(void) git_repository_free(git_repository *repo);
|
||||
*
|
||||
* @param repo_out pointer to the repo which will be created or reinitialized
|
||||
* @param path the path to the repository
|
||||
* @param is_bare if true, a Git repository without a working directory is created
|
||||
* at the pointed path. If false, provided path will be considered as the working
|
||||
* directory into which the .git directory will be created.
|
||||
* @param is_bare if true, a Git repository without a working directory is
|
||||
* created at the pointed path. If false, provided path will be
|
||||
* considered as the working directory into which the .git directory
|
||||
* will be created.
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare);
|
||||
GIT_EXTERN(int) git_repository_init(
|
||||
git_repository **repo_out,
|
||||
const char *path,
|
||||
unsigned is_bare);
|
||||
|
||||
/**
|
||||
* Option flags for `git_repository_init_ext`.
|
||||
*
|
||||
* These flags configure extra behaviors to `git_repository_init_ext`.
|
||||
* In every case, the default behavior is the zero value (i.e. flag is
|
||||
* not set). Just OR the flag values together for the `flags` parameter
|
||||
* when initializing a new repo. Details of individual values are:
|
||||
*
|
||||
* * BARE - Create a bare repository with no working directory.
|
||||
* * NO_REINIT - Return an EEXISTS error if the repo_path appears to
|
||||
* already be an git repository.
|
||||
* * NO_DOTGIT_DIR - Normally a "/.git/" will be appended to the repo
|
||||
* path for non-bare repos (if it is not already there), but
|
||||
* passing this flag prevents that behavior.
|
||||
* * MKDIR - Make the repo_path (and workdir_path) as needed. Init is
|
||||
* always willing to create the ".git" directory even without this
|
||||
* flag. This flag tells init to create the trailing component of
|
||||
* the repo and workdir paths as needed.
|
||||
* * MKPATH - Recursively make all components of the repo and workdir
|
||||
* paths as necessary.
|
||||
* * EXTERNAL_TEMPLATE - libgit2 normally uses internal templates to
|
||||
* initialize a new repo. This flags enables external templates,
|
||||
* looking the "template_path" from the options if set, or the
|
||||
* `init.templatedir` global config if not, or falling back on
|
||||
* "/usr/share/git-core/templates" if it exists.
|
||||
*/
|
||||
enum {
|
||||
GIT_REPOSITORY_INIT_BARE = (1u << 0),
|
||||
GIT_REPOSITORY_INIT_NO_REINIT = (1u << 1),
|
||||
GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = (1u << 2),
|
||||
GIT_REPOSITORY_INIT_MKDIR = (1u << 3),
|
||||
GIT_REPOSITORY_INIT_MKPATH = (1u << 4),
|
||||
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
|
||||
};
|
||||
|
||||
/**
|
||||
* Mode options for `git_repository_init_ext`.
|
||||
*
|
||||
* Set the mode field of the `git_repository_init_options` structure
|
||||
* either to the custom mode that you would like, or to one of the
|
||||
* following modes:
|
||||
*
|
||||
* * SHARED_UMASK - Use permissions configured by umask - the default.
|
||||
* * SHARED_GROUP - Use "--shared=group" behavior, chmod'ing the new repo
|
||||
* to be group writable and "g+sx" for sticky group assignment.
|
||||
* * SHARED_ALL - Use "--shared=all" behavior, adding world readability.
|
||||
* * Anything else - Set to custom value.
|
||||
*/
|
||||
enum {
|
||||
GIT_REPOSITORY_INIT_SHARED_UMASK = 0,
|
||||
GIT_REPOSITORY_INIT_SHARED_GROUP = 0002775,
|
||||
GIT_REPOSITORY_INIT_SHARED_ALL = 0002777,
|
||||
};
|
||||
|
||||
/**
|
||||
* Extended options structure for `git_repository_init_ext`.
|
||||
*
|
||||
* This contains extra options for `git_repository_init_ext` that enable
|
||||
* additional initialization features. The fields are:
|
||||
*
|
||||
* * flags - Combination of GIT_REPOSITORY_INIT flags above.
|
||||
* * mode - Set to one of the standard GIT_REPOSITORY_INIT_SHARED_...
|
||||
* constants above, or to a custom value that you would like.
|
||||
* * workdir_path - The path to the working dir or NULL for default (i.e.
|
||||
* repo_path parent on non-bare repos). IF THIS IS RELATIVE PATH,
|
||||
* IT WILL BE EVALUATED RELATIVE TO THE REPO_PATH. If this is not
|
||||
* the "natural" working directory, a .git gitlink file will be
|
||||
* created here linking to the repo_path.
|
||||
* * description - If set, this will be used to initialize the "description"
|
||||
* file in the repository, instead of using the template content.
|
||||
* * template_path - When GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE is set,
|
||||
* this contains the path to use for the template directory. If
|
||||
* this is NULL, the config or default directory options will be
|
||||
* used instead.
|
||||
* * initial_head - The name of the head to point HEAD at. If NULL, then
|
||||
* this will be treated as "master" and the HEAD ref will be set
|
||||
* to "refs/heads/master". If this begins with "refs/" it will be
|
||||
* used verbatim; otherwise "refs/heads/" will be prefixed.
|
||||
* * origin_url - If this is non-NULL, then after the rest of the
|
||||
* repository initialization is completed, an "origin" remote
|
||||
* will be added pointing to this URL.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t flags;
|
||||
uint32_t mode;
|
||||
const char *workdir_path;
|
||||
const char *description;
|
||||
const char *template_path;
|
||||
const char *initial_head;
|
||||
const char *origin_url;
|
||||
} git_repository_init_options;
|
||||
|
||||
/**
|
||||
* Create a new Git repository in the given folder with extended controls.
|
||||
*
|
||||
* This will initialize a new git repository (creating the repo_path
|
||||
* if requested by flags) and working directory as needed. It will
|
||||
* auto-detect the case sensitivity of the file system and if the
|
||||
* file system supports file mode bits correctly.
|
||||
*
|
||||
* @param repo_out Pointer to the repo which will be created or reinitialized.
|
||||
* @param repo_path The path to the repository.
|
||||
* @param opts Pointer to git_repository_init_options struct.
|
||||
* @return 0 or an error code on failure.
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_init_ext(
|
||||
git_repository **repo_out,
|
||||
const char *repo_path,
|
||||
git_repository_init_options *opts);
|
||||
|
||||
/**
|
||||
* Retrieve and resolve the reference pointed at by HEAD.
|
||||
@ -326,6 +466,11 @@ GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index
|
||||
*
|
||||
* Use this function to get the contents of this file. Don't forget to
|
||||
* remove the file after you create the commit.
|
||||
*
|
||||
* @param buffer Buffer to write data into or NULL to just read required size
|
||||
* @param len Length of buffer in bytes
|
||||
* @param repo Repository to read prepared message from
|
||||
* @return Bytes written to buffer, GIT_ENOTFOUND if no message, or -1 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_message(char *buffer, size_t len, git_repository *repo);
|
||||
|
||||
|
@ -250,18 +250,15 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
|
||||
int git_attr_path__init(
|
||||
git_attr_path *info, const char *path, const char *base)
|
||||
{
|
||||
ssize_t root;
|
||||
|
||||
/* build full path as best we can */
|
||||
git_buf_init(&info->full, 0);
|
||||
|
||||
if (base != NULL && git_path_root(path) < 0) {
|
||||
if (git_buf_joinpath(&info->full, base, path) < 0)
|
||||
return -1;
|
||||
info->path = info->full.ptr + strlen(base);
|
||||
} else {
|
||||
if (git_buf_sets(&info->full, path) < 0)
|
||||
return -1;
|
||||
info->path = info->full.ptr;
|
||||
}
|
||||
if (git_path_join_unrooted(&info->full, path, base, &root) < 0)
|
||||
return -1;
|
||||
|
||||
info->path = info->full.ptr + root;
|
||||
|
||||
/* remove trailing slashes */
|
||||
while (info->full.size > 0) {
|
||||
|
25
src/config.c
25
src/config.c
@ -501,17 +501,28 @@ int git_config_find_system(char *system_config_path, size_t length)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_open_global(git_config **out)
|
||||
int git_config_open_default(git_config **out)
|
||||
{
|
||||
int error;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
git_config *cfg = NULL;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
if ((error = git_config_find_global_r(&path)) < 0)
|
||||
return error;
|
||||
error = git_config_new(&cfg);
|
||||
|
||||
error = git_config_open_ondisk(out, git_buf_cstr(&path));
|
||||
git_buf_free(&path);
|
||||
if (!error && !git_config_find_global_r(&buf))
|
||||
error = git_config_add_file_ondisk(cfg, buf.ptr, 2);
|
||||
|
||||
if (!error && !git_config_find_system_r(&buf))
|
||||
error = git_config_add_file_ondisk(cfg, buf.ptr, 1);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
if (error && cfg) {
|
||||
git_config_free(cfg);
|
||||
cfg = NULL;
|
||||
}
|
||||
|
||||
*out = cfg;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
330
src/fileops.c
330
src/fileops.c
@ -10,19 +10,8 @@
|
||||
|
||||
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
|
||||
{
|
||||
int result = 0;
|
||||
git_buf target_folder = GIT_BUF_INIT;
|
||||
|
||||
if (git_path_dirname_r(&target_folder, file_path) < 0)
|
||||
return -1;
|
||||
|
||||
/* Does the containing folder exist? */
|
||||
if (git_path_isdir(target_folder.ptr) == false)
|
||||
/* Let's create the tree structure */
|
||||
result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
|
||||
|
||||
git_buf_free(&target_folder);
|
||||
return result;
|
||||
return git_futils_mkdir(
|
||||
file_path, NULL, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST);
|
||||
}
|
||||
|
||||
int git_futils_mktmp(git_buf *path_out, const char *filename)
|
||||
@ -239,66 +228,92 @@ void git_futils_mmap_free(git_map *out)
|
||||
p_munmap(out);
|
||||
}
|
||||
|
||||
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
|
||||
int git_futils_mkdir(
|
||||
const char *path,
|
||||
const char *base,
|
||||
mode_t mode,
|
||||
uint32_t flags)
|
||||
{
|
||||
git_buf make_path = GIT_BUF_INIT;
|
||||
size_t start = 0;
|
||||
char *pp, *sp;
|
||||
bool failed = false;
|
||||
ssize_t root = 0;
|
||||
char lastch, *tail;
|
||||
|
||||
if (base != NULL) {
|
||||
/*
|
||||
* when a base is being provided, it is supposed to already exist.
|
||||
* Therefore, no attempt is being made to recursively create this leading path
|
||||
* segment. It's just skipped. */
|
||||
start = strlen(base);
|
||||
if (git_buf_joinpath(&make_path, base, path) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
int root_path_offset;
|
||||
/* build path and find "root" where we should start calling mkdir */
|
||||
if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_buf_puts(&make_path, path) < 0)
|
||||
return -1;
|
||||
|
||||
root_path_offset = git_path_root(make_path.ptr);
|
||||
if (root_path_offset > 0) {
|
||||
/*
|
||||
* On Windows, will skip the drive name (eg. C: or D:)
|
||||
* or the leading part of a network path (eg. //computer_name ) */
|
||||
start = root_path_offset;
|
||||
}
|
||||
if (make_path.size == 0) {
|
||||
giterr_set(GITERR_OS, "Attempt to create empty path");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pp = make_path.ptr + start;
|
||||
|
||||
while (!failed && (sp = strchr(pp, '/')) != NULL) {
|
||||
if (sp != pp && git_path_isdir(make_path.ptr) == false) {
|
||||
*sp = 0;
|
||||
|
||||
/* Do not choke while trying to recreate an existing directory */
|
||||
if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
|
||||
failed = true;
|
||||
|
||||
*sp = '/';
|
||||
}
|
||||
|
||||
pp = sp + 1;
|
||||
/* remove trailing slashes on path */
|
||||
while (make_path.ptr[make_path.size - 1] == '/') {
|
||||
make_path.size--;
|
||||
make_path.ptr[make_path.size] = '\0';
|
||||
}
|
||||
|
||||
if (*pp != '\0' && !failed) {
|
||||
if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
|
||||
failed = true;
|
||||
/* if we are not supposed to made the last element, truncate it */
|
||||
if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
|
||||
git_buf_rtruncate_at_char(&make_path, '/');
|
||||
|
||||
/* if we are not supposed to make the whole path, reset root */
|
||||
if ((flags & GIT_MKDIR_PATH) == 0)
|
||||
root = git_buf_rfind(&make_path, '/');
|
||||
|
||||
/* clip root to make_path length */
|
||||
if (root >= (ssize_t)make_path.size)
|
||||
root = (ssize_t)make_path.size - 1;
|
||||
if (root < 0)
|
||||
root = 0;
|
||||
|
||||
tail = & make_path.ptr[root];
|
||||
|
||||
while (*tail) {
|
||||
/* advance tail to include next path component */
|
||||
while (*tail == '/')
|
||||
tail++;
|
||||
while (*tail && *tail != '/')
|
||||
tail++;
|
||||
|
||||
/* truncate path at next component */
|
||||
lastch = *tail;
|
||||
*tail = '\0';
|
||||
|
||||
/* make directory */
|
||||
if (p_mkdir(make_path.ptr, mode) < 0 &&
|
||||
(errno != EEXIST || (flags & GIT_MKDIR_EXCL) != 0))
|
||||
{
|
||||
giterr_set(GITERR_OS, "Failed to make directory '%s'",
|
||||
make_path.ptr);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* chmod if requested */
|
||||
if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
|
||||
((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0'))
|
||||
{
|
||||
if (p_chmod(make_path.ptr, mode) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
|
||||
make_path.ptr);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
*tail = lastch;
|
||||
}
|
||||
|
||||
git_buf_free(&make_path);
|
||||
|
||||
if (failed) {
|
||||
giterr_set(GITERR_OS,
|
||||
"Failed to create directory structure at '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
git_buf_free(&make_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
|
||||
{
|
||||
return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
|
||||
}
|
||||
|
||||
static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
|
||||
@ -495,3 +510,196 @@ int git_futils_fake_symlink(const char *old, const char *new)
|
||||
}
|
||||
return retcode;
|
||||
}
|
||||
|
||||
static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done)
|
||||
{
|
||||
int error = 0;
|
||||
char buffer[4096];
|
||||
ssize_t len = 0;
|
||||
|
||||
while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0)
|
||||
/* p_write() does not have the same semantics as write(). It loops
|
||||
* internally and will return 0 when it has completed writing.
|
||||
*/
|
||||
error = p_write(ofd, buffer, len);
|
||||
|
||||
if (len < 0) {
|
||||
giterr_set(GITERR_OS, "Read error while copying file");
|
||||
error = (int)len;
|
||||
}
|
||||
|
||||
if (close_fd_when_done) {
|
||||
p_close(ifd);
|
||||
p_close(ofd);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_futils_cp(const char *from, const char *to, mode_t filemode)
|
||||
{
|
||||
int ifd, ofd;
|
||||
|
||||
if ((ifd = git_futils_open_ro(from)) < 0)
|
||||
return ifd;
|
||||
|
||||
if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
|
||||
if (errno == ENOENT || errno == ENOTDIR)
|
||||
ofd = GIT_ENOTFOUND;
|
||||
giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
|
||||
p_close(ifd);
|
||||
return ofd;
|
||||
}
|
||||
|
||||
return cp_by_fd(ifd, ofd, true);
|
||||
}
|
||||
|
||||
static int cp_link(const char *from, const char *to, size_t link_size)
|
||||
{
|
||||
int error = 0;
|
||||
ssize_t read_len;
|
||||
char *link_data = git__malloc(link_size + 1);
|
||||
GITERR_CHECK_ALLOC(link_data);
|
||||
|
||||
read_len = p_readlink(from, link_data, link_size);
|
||||
if (read_len != (ssize_t)link_size) {
|
||||
giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from);
|
||||
error = -1;
|
||||
}
|
||||
else {
|
||||
link_data[read_len] = '\0';
|
||||
|
||||
if (p_symlink(link_data, to) < 0) {
|
||||
giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'",
|
||||
link_data, to);
|
||||
error = -1;
|
||||
}
|
||||
}
|
||||
|
||||
git__free(link_data);
|
||||
return error;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *to_root;
|
||||
git_buf to;
|
||||
ssize_t from_prefix;
|
||||
uint32_t flags;
|
||||
uint32_t mkdir_flags;
|
||||
mode_t dirmode;
|
||||
} cp_r_info;
|
||||
|
||||
static int _cp_r_callback(void *ref, git_buf *from)
|
||||
{
|
||||
cp_r_info *info = ref;
|
||||
struct stat from_st, to_st;
|
||||
bool exists = false;
|
||||
|
||||
if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 &&
|
||||
from->ptr[git_path_basename_offset(from)] == '.')
|
||||
return 0;
|
||||
|
||||
if (git_buf_joinpath(
|
||||
&info->to, info->to_root, from->ptr + info->from_prefix) < 0)
|
||||
return -1;
|
||||
|
||||
if (p_lstat(info->to.ptr, &to_st) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
giterr_set(GITERR_OS,
|
||||
"Could not access %s while copying files", info->to.ptr);
|
||||
return -1;
|
||||
}
|
||||
} else
|
||||
exists = true;
|
||||
|
||||
if (git_path_lstat(from->ptr, &from_st) < 0)
|
||||
return -1;
|
||||
|
||||
if (S_ISDIR(from_st.st_mode)) {
|
||||
int error = 0;
|
||||
mode_t oldmode = info->dirmode;
|
||||
|
||||
/* if we are not chmod'ing, then overwrite dirmode */
|
||||
if ((info->flags & GIT_CPDIR_CHMOD) == 0)
|
||||
info->dirmode = from_st.st_mode;
|
||||
|
||||
/* make directory now if CREATE_EMPTY_DIRS is requested and needed */
|
||||
if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
|
||||
error = git_futils_mkdir(
|
||||
info->to.ptr, NULL, info->dirmode, info->mkdir_flags);
|
||||
|
||||
/* recurse onto target directory */
|
||||
if (!exists || S_ISDIR(to_st.st_mode))
|
||||
error = git_path_direach(from, _cp_r_callback, info);
|
||||
|
||||
if (oldmode != 0)
|
||||
info->dirmode = oldmode;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
if ((info->flags & GIT_CPDIR_OVERWRITE) == 0)
|
||||
return 0;
|
||||
|
||||
if (p_unlink(info->to.ptr) < 0) {
|
||||
giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
|
||||
info->to.ptr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Done if this isn't a regular file or a symlink */
|
||||
if (!S_ISREG(from_st.st_mode) &&
|
||||
(!S_ISLNK(from_st.st_mode) ||
|
||||
(info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0))
|
||||
return 0;
|
||||
|
||||
/* Make container directory on demand if needed */
|
||||
if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
|
||||
git_futils_mkdir(
|
||||
info->to.ptr, NULL, info->dirmode, info->mkdir_flags) < 0)
|
||||
return -1;
|
||||
|
||||
/* make symlink or regular file */
|
||||
if (S_ISLNK(from_st.st_mode))
|
||||
return cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
|
||||
else
|
||||
return git_futils_cp(from->ptr, info->to.ptr, from_st.st_mode);
|
||||
}
|
||||
|
||||
int git_futils_cp_r(
|
||||
const char *from,
|
||||
const char *to,
|
||||
uint32_t flags,
|
||||
mode_t dirmode)
|
||||
{
|
||||
int error;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
cp_r_info info;
|
||||
|
||||
if (git_buf_sets(&path, from) < 0)
|
||||
return -1;
|
||||
|
||||
info.to_root = to;
|
||||
info.flags = flags;
|
||||
info.dirmode = dirmode;
|
||||
info.from_prefix = path.size;
|
||||
git_buf_init(&info.to, 0);
|
||||
|
||||
/* precalculate mkdir flags */
|
||||
if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
|
||||
info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
|
||||
if ((flags & GIT_CPDIR_CHMOD) != 0)
|
||||
info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
|
||||
} else {
|
||||
info.mkdir_flags =
|
||||
((flags & GIT_CPDIR_CHMOD) != 0) ? GIT_MKDIR_CHMOD : 0;
|
||||
}
|
||||
|
||||
error = _cp_r_callback(&info, &path);
|
||||
|
||||
git_buf_free(&path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -50,11 +50,46 @@ extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmo
|
||||
/**
|
||||
* Create a path recursively
|
||||
*
|
||||
* If a base parameter is being passed, it's expected to be valued with a path pointing to an already
|
||||
* exisiting directory.
|
||||
* If a base parameter is being passed, it's expected to be valued with a
|
||||
* path pointing to an already existing directory.
|
||||
*/
|
||||
extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);
|
||||
|
||||
/**
|
||||
* Flags to pass to `git_futils_mkdir`.
|
||||
*
|
||||
* * GIT_MKDIR_EXCL is "exclusive" - i.e. generate an error if dir exists.
|
||||
* * GIT_MKDIR_PATH says to make all components in the path.
|
||||
* * GIT_MKDIR_CHMOD says to chmod the final directory entry after creation
|
||||
* * GIT_MKDIR_CHMOD_PATH says to chmod each directory component in the path
|
||||
* * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path
|
||||
*
|
||||
* Note that the chmod options will be executed even if the directory already
|
||||
* exists, unless GIT_MKDIR_EXCL is given.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_MKDIR_EXCL = 1,
|
||||
GIT_MKDIR_PATH = 2,
|
||||
GIT_MKDIR_CHMOD = 4,
|
||||
GIT_MKDIR_CHMOD_PATH = 8,
|
||||
GIT_MKDIR_SKIP_LAST = 16
|
||||
} git_futils_mkdir_flags;
|
||||
|
||||
/**
|
||||
* Create a directory or entire path.
|
||||
*
|
||||
* This makes a directory (and the entire path leading up to it if requested),
|
||||
* and optionally chmods the directory immediately after (or each part of the
|
||||
* path if requested).
|
||||
*
|
||||
* @param path The path to create.
|
||||
* @param base Root for relative path. These directories will never be made.
|
||||
* @param mode The mode to use for created directories.
|
||||
* @param flags Combination of the mkdir flags above.
|
||||
* @return 0 on success, else error code
|
||||
*/
|
||||
extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Create all the folders required to contain
|
||||
* the full path of a file
|
||||
@ -94,6 +129,45 @@ extern int git_futils_mktmp(git_buf *path_out, const char *filename);
|
||||
*/
|
||||
extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
|
||||
|
||||
/**
|
||||
* Copy a file
|
||||
*
|
||||
* The filemode will be used for the newly created file.
|
||||
*/
|
||||
extern int git_futils_cp(
|
||||
const char *from,
|
||||
const char *to,
|
||||
mode_t filemode);
|
||||
|
||||
/**
|
||||
* Flags that can be passed to `git_futils_cp_r`.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_CPDIR_CREATE_EMPTY_DIRS = 1,
|
||||
GIT_CPDIR_COPY_SYMLINKS = 2,
|
||||
GIT_CPDIR_COPY_DOTFILES = 4,
|
||||
GIT_CPDIR_OVERWRITE = 8,
|
||||
GIT_CPDIR_CHMOD = 16
|
||||
} git_futils_cpdir_flags;
|
||||
|
||||
/**
|
||||
* Copy a directory tree.
|
||||
*
|
||||
* This copies directories and files from one root to another. You can
|
||||
* pass a combinationof GIT_CPDIR flags as defined above.
|
||||
*
|
||||
* If you pass the CHMOD flag, then the dirmode will be applied to all
|
||||
* directories that are created during the copy, overiding the natural
|
||||
* permissions. If you do not pass the CHMOD flag, then the dirmode
|
||||
* will actually be copied from the source files and the `dirmode` arg
|
||||
* will be ignored.
|
||||
*/
|
||||
extern int git_futils_cp_r(
|
||||
const char *from,
|
||||
const char *to,
|
||||
uint32_t flags,
|
||||
mode_t dirmode);
|
||||
|
||||
/**
|
||||
* Open a file readonly and set error if needed.
|
||||
*/
|
||||
|
@ -82,5 +82,5 @@ int git_message_prettify(char *message_out, size_t buffer_size, const char *mess
|
||||
|
||||
done:
|
||||
git_buf_free(&buf);
|
||||
return out_size;
|
||||
return (int)out_size;
|
||||
}
|
||||
|
46
src/path.c
46
src/path.c
@ -147,6 +147,20 @@ char *git_path_basename(const char *path)
|
||||
return basename;
|
||||
}
|
||||
|
||||
size_t git_path_basename_offset(git_buf *buffer)
|
||||
{
|
||||
ssize_t slash;
|
||||
|
||||
if (!buffer || buffer->size <= 0)
|
||||
return 0;
|
||||
|
||||
slash = git_buf_rfind_next(buffer, '/');
|
||||
|
||||
if (slash >= 0 && buffer->ptr[slash] == '/')
|
||||
return (size_t)(slash + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *git_path_topdir(const char *path)
|
||||
{
|
||||
@ -193,6 +207,31 @@ int git_path_root(const char *path)
|
||||
return -1; /* Not a real error - signals that path is not rooted */
|
||||
}
|
||||
|
||||
int git_path_join_unrooted(
|
||||
git_buf *path_out, const char *path, const char *base, ssize_t *root_at)
|
||||
{
|
||||
int error, root;
|
||||
|
||||
assert(path && path_out);
|
||||
|
||||
root = git_path_root(path);
|
||||
|
||||
if (base != NULL && root < 0) {
|
||||
error = git_buf_joinpath(path_out, base, path);
|
||||
|
||||
if (root_at)
|
||||
*root_at = (ssize_t)strlen(base);
|
||||
}
|
||||
else {
|
||||
error = git_buf_sets(path_out, path);
|
||||
|
||||
if (root_at)
|
||||
*root_at = (root < 0) ? 0 : (ssize_t)root;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_path_prettify(git_buf *path_out, const char *path, const char *base)
|
||||
{
|
||||
char buf[GIT_PATH_MAX];
|
||||
@ -502,12 +541,7 @@ bool git_path_contains_file(git_buf *base, const char *file)
|
||||
|
||||
int git_path_find_dir(git_buf *dir, const char *path, const char *base)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (base != NULL && git_path_root(path) < 0)
|
||||
error = git_buf_joinpath(dir, base, path);
|
||||
else
|
||||
error = git_buf_sets(dir, path);
|
||||
int error = git_path_join_unrooted(dir, path, base, NULL);
|
||||
|
||||
if (!error) {
|
||||
char buf[GIT_PATH_MAX];
|
||||
|
14
src/path.h
14
src/path.h
@ -58,6 +58,11 @@ extern int git_path_dirname_r(git_buf *buffer, const char *path);
|
||||
extern char *git_path_basename(const char *path);
|
||||
extern int git_path_basename_r(git_buf *buffer, const char *path);
|
||||
|
||||
/* Return the offset of the start of the basename. Unlike the other
|
||||
* basename functions, this returns 0 if the path is empty.
|
||||
*/
|
||||
extern size_t git_path_basename_offset(git_buf *buffer);
|
||||
|
||||
extern const char *git_path_topdir(const char *path);
|
||||
|
||||
/**
|
||||
@ -185,6 +190,15 @@ extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
|
||||
*/
|
||||
extern bool git_path_contains_file(git_buf *dir, const char *file);
|
||||
|
||||
/**
|
||||
* Prepend base to unrooted path or just copy path over.
|
||||
*
|
||||
* This will optionally return the index into the path where the "root"
|
||||
* is, either the end of the base directory prefix or the path root.
|
||||
*/
|
||||
extern int git_path_join_unrooted(
|
||||
git_buf *path_out, const char *path, const char *base, ssize_t *root_at);
|
||||
|
||||
/**
|
||||
* Clean up path, prepending base if it is not already rooted.
|
||||
*/
|
||||
|
@ -11,8 +11,15 @@
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef S_IFGITLINK
|
||||
#define S_IFGITLINK 0160000
|
||||
#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
|
||||
#endif
|
||||
|
||||
/* if S_ISGID is not defined, then don't try to set it */
|
||||
#ifndef S_ISGID
|
||||
#define S_ISGID 0
|
||||
#endif
|
||||
|
||||
#if !defined(O_BINARY)
|
||||
#define O_BINARY 0
|
||||
|
58
src/repo_template.h
Normal file
58
src/repo_template.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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_repo_template_h__
|
||||
#define INCLUDE_repo_template_h__
|
||||
|
||||
#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
|
||||
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
|
||||
|
||||
#define GIT_HOOKS_DIR "hooks/"
|
||||
#define GIT_HOOKS_DIR_MODE 0755
|
||||
|
||||
#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
|
||||
#define GIT_HOOKS_README_MODE 0755
|
||||
#define GIT_HOOKS_README_CONTENT \
|
||||
"#!/bin/sh\n"\
|
||||
"#\n"\
|
||||
"# Place appropriately named executable hook scripts into this directory\n"\
|
||||
"# to intercept various actions that git takes. See `git help hooks` for\n"\
|
||||
"# more information.\n"
|
||||
|
||||
#define GIT_INFO_DIR "info/"
|
||||
#define GIT_INFO_DIR_MODE 0755
|
||||
|
||||
#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
|
||||
#define GIT_INFO_EXCLUDE_MODE 0644
|
||||
#define GIT_INFO_EXCLUDE_CONTENT \
|
||||
"# File patterns to ignore; see `git help ignore` for more information.\n"\
|
||||
"# Lines that start with '#' are comments.\n"
|
||||
|
||||
#define GIT_DESC_FILE "description"
|
||||
#define GIT_DESC_MODE 0644
|
||||
#define GIT_DESC_CONTENT \
|
||||
"Unnamed repository; edit this file 'description' to name the repository.\n"
|
||||
|
||||
typedef struct {
|
||||
const char *path;
|
||||
mode_t mode;
|
||||
const char *content;
|
||||
} repo_template_item;
|
||||
|
||||
static repo_template_item repo_template[] = {
|
||||
{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/info/' */
|
||||
{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/pack/' */
|
||||
{ GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/heads/' */
|
||||
{ GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/tags/' */
|
||||
{ GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE, NULL }, /* '/hooks/' */
|
||||
{ GIT_INFO_DIR, GIT_INFO_DIR_MODE, NULL }, /* '/info/' */
|
||||
{ GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
|
||||
{ GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
|
||||
{ GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
|
||||
{ NULL, 0, NULL }
|
||||
};
|
||||
|
||||
#endif
|
576
src/repository.c
576
src/repository.c
@ -18,15 +18,14 @@
|
||||
#include "config.h"
|
||||
#include "refs.h"
|
||||
|
||||
#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
|
||||
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
|
||||
|
||||
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
|
||||
|
||||
#define GIT_BRANCH_MASTER "master"
|
||||
|
||||
#define GIT_REPO_VERSION 0
|
||||
|
||||
#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
|
||||
|
||||
static void drop_odb(git_repository *repo)
|
||||
{
|
||||
if (repo->_odb != NULL) {
|
||||
@ -238,16 +237,17 @@ static int read_gitfile(git_buf *path_out, const char *file_path)
|
||||
|
||||
git_buf_rtrim(&file);
|
||||
|
||||
if (file.size <= prefix_len ||
|
||||
memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
|
||||
if (git_buf_len(&file) <= prefix_len ||
|
||||
memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
|
||||
{
|
||||
giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);
|
||||
error = -1;
|
||||
}
|
||||
else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
|
||||
const char *gitlink = ((const char *)file.ptr) + prefix_len;
|
||||
const char *gitlink = git_buf_cstr(&file) + prefix_len;
|
||||
while (*gitlink && git__isspace(*gitlink)) gitlink++;
|
||||
error = git_path_prettify_dir(path_out, gitlink, path_out->ptr);
|
||||
error = git_path_prettify_dir(
|
||||
path_out, gitlink, git_buf_cstr(path_out));
|
||||
}
|
||||
|
||||
git_buf_free(&file);
|
||||
@ -359,9 +359,11 @@ int git_repository_open_ext(
|
||||
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
|
||||
git_repository *repo;
|
||||
|
||||
*repo_ptr = NULL;
|
||||
if (repo_ptr)
|
||||
*repo_ptr = NULL;
|
||||
|
||||
if ((error = find_repo(&path, &parent, start_path, flags, ceiling_dirs)) < 0)
|
||||
error = find_repo(&path, &parent, start_path, flags, ceiling_dirs);
|
||||
if (error < 0 || !repo_ptr)
|
||||
return error;
|
||||
|
||||
repo = repository_alloc();
|
||||
@ -632,19 +634,35 @@ static int check_repositoryformatversion(git_config *config)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int repo_init_createhead(const char *git_dir)
|
||||
static int repo_init_create_head(const char *git_dir, const char *ref_name)
|
||||
{
|
||||
git_buf ref_path = GIT_BUF_INIT;
|
||||
git_filebuf ref = GIT_FILEBUF_INIT;
|
||||
const char *fmt;
|
||||
|
||||
if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
|
||||
git_filebuf_open(&ref, ref_path.ptr, 0) < 0 ||
|
||||
git_filebuf_printf(&ref, "ref: refs/heads/master\n") < 0 ||
|
||||
git_filebuf_open(&ref, ref_path.ptr, 0) < 0)
|
||||
goto fail;
|
||||
|
||||
if (!ref_name)
|
||||
ref_name = GIT_BRANCH_MASTER;
|
||||
|
||||
if (git__prefixcmp(ref_name, "refs/") == 0)
|
||||
fmt = "ref: %s\n";
|
||||
else
|
||||
fmt = "ref: refs/heads/%s\n";
|
||||
|
||||
if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
|
||||
git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
|
||||
return -1;
|
||||
goto fail;
|
||||
|
||||
git_buf_free(&ref_path);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
git_buf_free(&ref_path);
|
||||
git_filebuf_cleanup(&ref);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool is_chmod_supported(const char *file_path)
|
||||
@ -665,6 +683,7 @@ static bool is_chmod_supported(const char *file_path)
|
||||
return false;
|
||||
|
||||
_is_supported = (st1.st_mode != st2.st_mode);
|
||||
|
||||
return _is_supported;
|
||||
}
|
||||
|
||||
@ -686,19 +705,45 @@ cleanup:
|
||||
return _is_insensitive;
|
||||
}
|
||||
|
||||
static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
|
||||
static bool are_symlinks_supported(const char *wd_path)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
int fd;
|
||||
struct stat st;
|
||||
static int _symlinks_supported = -1;
|
||||
|
||||
if (_symlinks_supported > -1)
|
||||
return _symlinks_supported;
|
||||
|
||||
if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
|
||||
p_close(fd) < 0 ||
|
||||
p_unlink(path.ptr) < 0 ||
|
||||
p_symlink("testing", path.ptr) < 0 ||
|
||||
p_lstat(path.ptr, &st) < 0)
|
||||
_symlinks_supported = false;
|
||||
else
|
||||
_symlinks_supported = (S_ISLNK(st.st_mode) != 0);
|
||||
|
||||
(void)p_unlink(path.ptr);
|
||||
git_buf_free(&path);
|
||||
|
||||
return _symlinks_supported;
|
||||
}
|
||||
|
||||
static int repo_init_config(
|
||||
const char *repo_dir,
|
||||
const char *work_dir,
|
||||
git_repository_init_options *opts)
|
||||
{
|
||||
int error = 0;
|
||||
git_buf cfg_path = GIT_BUF_INIT;
|
||||
git_config *config = NULL;
|
||||
|
||||
#define SET_REPO_CONFIG(type, name, val) {\
|
||||
if (git_config_set_##type(config, name, val) < 0) { \
|
||||
git_buf_free(&cfg_path); \
|
||||
git_config_free(config); \
|
||||
return -1; } \
|
||||
}
|
||||
#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
|
||||
if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
|
||||
goto cleanup; } while (0)
|
||||
|
||||
if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
|
||||
if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
|
||||
@ -706,58 +751,61 @@ static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_reinit && check_repositoryformatversion(config) < 0) {
|
||||
git_buf_free(&cfg_path);
|
||||
git_config_free(config);
|
||||
return -1;
|
||||
}
|
||||
if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
|
||||
(error = check_repositoryformatversion(config)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
SET_REPO_CONFIG(bool, "core.bare", is_bare);
|
||||
SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
|
||||
SET_REPO_CONFIG(bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
|
||||
|
||||
if (!is_bare)
|
||||
SET_REPO_CONFIG(
|
||||
bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
|
||||
SET_REPO_CONFIG(
|
||||
int32, "core.repositoryformatversion", GIT_REPO_VERSION);
|
||||
SET_REPO_CONFIG(
|
||||
bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
|
||||
|
||||
if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
|
||||
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
|
||||
|
||||
if (!is_reinit && is_filesystem_case_insensitive(git_dir))
|
||||
SET_REPO_CONFIG(bool, "core.ignorecase", true);
|
||||
/* TODO: what other defaults? */
|
||||
if (!are_symlinks_supported(work_dir))
|
||||
SET_REPO_CONFIG(bool, "core.symlinks", false);
|
||||
|
||||
if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
|
||||
SET_REPO_CONFIG(string, "core.worktree", work_dir);
|
||||
}
|
||||
else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
|
||||
if ((error = git_config_delete(config, "core.worktree")) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
if (!are_symlinks_supported(repo_dir))
|
||||
SET_REPO_CONFIG(bool, "core.symlinks", false);
|
||||
}
|
||||
|
||||
if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
|
||||
is_filesystem_case_insensitive(repo_dir))
|
||||
SET_REPO_CONFIG(bool, "core.ignorecase", true);
|
||||
|
||||
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
|
||||
SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
|
||||
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
|
||||
}
|
||||
else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
|
||||
SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
|
||||
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&cfg_path);
|
||||
git_config_free(config);
|
||||
return 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#define GIT_HOOKS_DIR "hooks/"
|
||||
#define GIT_HOOKS_DIR_MODE 0755
|
||||
|
||||
#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
|
||||
#define GIT_HOOKS_README_MODE 0755
|
||||
#define GIT_HOOKS_README_CONTENT \
|
||||
"#!/bin/sh\n"\
|
||||
"#\n"\
|
||||
"# Place appropriately named executable hook scripts into this directory\n"\
|
||||
"# to intercept various actions that git takes. See `git help hooks` for\n"\
|
||||
"# more information.\n"
|
||||
|
||||
#define GIT_INFO_DIR "info/"
|
||||
#define GIT_INFO_DIR_MODE 0755
|
||||
|
||||
#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
|
||||
#define GIT_INFO_EXCLUDE_MODE 0644
|
||||
#define GIT_INFO_EXCLUDE_CONTENT \
|
||||
"# File patterns to ignore; see `git help ignore` for more information.\n"\
|
||||
"# Lines that start with '#' are comments.\n"
|
||||
|
||||
#define GIT_DESC_FILE "description"
|
||||
#define GIT_DESC_MODE 0644
|
||||
#define GIT_DESC_CONTENT "Unnamed repository; edit this file 'description' to name the repository.\n"
|
||||
|
||||
static int repo_write_template(
|
||||
const char *git_dir,
|
||||
bool allow_overwrite,
|
||||
const char *file,
|
||||
mode_t mode,
|
||||
bool hidden,
|
||||
const char *content)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
@ -781,6 +829,15 @@ static int repo_write_template(
|
||||
else if (errno != EEXIST)
|
||||
error = fd;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
if (!error && hidden) {
|
||||
if (p_hide_directory__w32(path.ptr) < 0)
|
||||
error = -1;
|
||||
}
|
||||
#else
|
||||
GIT_UNUSED(hidden);
|
||||
#endif
|
||||
|
||||
git_buf_free(&path);
|
||||
|
||||
if (error)
|
||||
@ -790,86 +847,316 @@ static int repo_write_template(
|
||||
return error;
|
||||
}
|
||||
|
||||
static int repo_init_structure(const char *git_dir, int is_bare)
|
||||
static int repo_write_gitlink(
|
||||
const char *in_dir, const char *to_repo)
|
||||
{
|
||||
int i;
|
||||
struct { const char *dir; mode_t mode; } dirs[] = {
|
||||
{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */
|
||||
{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */
|
||||
{ GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/heads/' */
|
||||
{ GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/tags/' */
|
||||
{ GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE }, /* '/hooks/' */
|
||||
{ GIT_INFO_DIR, GIT_INFO_DIR_MODE }, /* '/info/' */
|
||||
{ NULL, 0 }
|
||||
};
|
||||
struct { const char *file; mode_t mode; const char *content; } tmpl[] = {
|
||||
{ GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
|
||||
{ GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
|
||||
{ GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
|
||||
{ NULL, 0, NULL }
|
||||
};
|
||||
int error;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
struct stat st;
|
||||
|
||||
/* Make the base directory */
|
||||
if (git_futils_mkdir_r(git_dir, NULL, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE) < 0)
|
||||
git_path_dirname_r(&buf, to_repo);
|
||||
git_path_to_dir(&buf);
|
||||
if (git_buf_oom(&buf))
|
||||
return -1;
|
||||
|
||||
/* Hides the ".git" directory */
|
||||
if (!is_bare) {
|
||||
/* don't write gitlink to natural workdir */
|
||||
if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
|
||||
strcmp(in_dir, buf.ptr) == 0)
|
||||
{
|
||||
error = GIT_PASSTHROUGH;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((error = git_buf_joinpath(&buf, in_dir, DOT_GIT)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
|
||||
giterr_set(GITERR_REPOSITORY,
|
||||
"Cannot overwrite gitlink file into path '%s'", in_dir);
|
||||
error = GIT_EEXISTS;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
git_buf_clear(&buf);
|
||||
|
||||
error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
|
||||
|
||||
if (!error)
|
||||
error = repo_write_template(in_dir, true, DOT_GIT, 0644, true, buf.ptr);
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
static mode_t pick_dir_mode(git_repository_init_options *opts)
|
||||
{
|
||||
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
|
||||
return 0755;
|
||||
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
|
||||
return (0775 | S_ISGID);
|
||||
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
|
||||
return (0777 | S_ISGID);
|
||||
return opts->mode;
|
||||
}
|
||||
|
||||
#include "repo_template.h"
|
||||
|
||||
static int repo_init_structure(
|
||||
const char *repo_dir,
|
||||
const char *work_dir,
|
||||
git_repository_init_options *opts)
|
||||
{
|
||||
int error = 0;
|
||||
repo_template_item *tpl;
|
||||
bool external_tpl =
|
||||
((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
|
||||
mode_t dmode = pick_dir_mode(opts);
|
||||
|
||||
/* Hide the ".git" directory */
|
||||
if ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0) {
|
||||
#ifdef GIT_WIN32
|
||||
if (p_hide_directory__w32(git_dir) < 0) {
|
||||
if (p_hide_directory__w32(repo_dir) < 0) {
|
||||
giterr_set(GITERR_REPOSITORY,
|
||||
"Failed to mark Git repository folder as hidden");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Make subdirectories as needed */
|
||||
for (i = 0; dirs[i].dir != NULL; ++i) {
|
||||
if (git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode) < 0)
|
||||
/* Create .git gitlink if appropriate */
|
||||
else if ((opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0) {
|
||||
if (repo_write_gitlink(work_dir, repo_dir) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make template files as needed */
|
||||
for (i = 0; tmpl[i].file != NULL; ++i) {
|
||||
if (repo_write_template(
|
||||
git_dir, false, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0)
|
||||
return -1;
|
||||
/* Copy external template if requested */
|
||||
if (external_tpl) {
|
||||
git_config *cfg;
|
||||
const char *tdir;
|
||||
|
||||
if (opts->template_path)
|
||||
tdir = opts->template_path;
|
||||
else if ((error = git_config_open_default(&cfg)) < 0)
|
||||
return error;
|
||||
else {
|
||||
error = git_config_get_string(&tdir, cfg, "init.templatedir");
|
||||
|
||||
git_config_free(cfg);
|
||||
|
||||
if (error && error != GIT_ENOTFOUND)
|
||||
return error;
|
||||
|
||||
giterr_clear();
|
||||
tdir = GIT_TEMPLATE_DIR;
|
||||
}
|
||||
|
||||
error = git_futils_cp_r(tdir, repo_dir,
|
||||
GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD, dmode);
|
||||
|
||||
if (error < 0) {
|
||||
if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
|
||||
return error;
|
||||
|
||||
/* if template was default, ignore error and use internal */
|
||||
giterr_clear();
|
||||
external_tpl = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* Copy internal template
|
||||
* - always ensure existence of dirs
|
||||
* - only create files if no external template was specified
|
||||
*/
|
||||
for (tpl = repo_template; !error && tpl->path; ++tpl) {
|
||||
if (!tpl->content)
|
||||
error = git_futils_mkdir(
|
||||
tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
|
||||
else if (!external_tpl) {
|
||||
const char *content = tpl->content;
|
||||
|
||||
if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0)
|
||||
content = opts->description;
|
||||
|
||||
error = repo_write_template(
|
||||
repo_dir, false, tpl->path, tpl->mode, false, content);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
|
||||
static int repo_init_directories(
|
||||
git_buf *repo_path,
|
||||
git_buf *wd_path,
|
||||
const char *given_repo,
|
||||
git_repository_init_options *opts)
|
||||
{
|
||||
git_buf repository_path = GIT_BUF_INIT;
|
||||
bool is_reinit;
|
||||
int result = -1;
|
||||
int error = 0;
|
||||
bool add_dotgit, has_dotgit, natural_wd;
|
||||
mode_t dirmode;
|
||||
|
||||
assert(repo_out && path);
|
||||
/* set up repo path */
|
||||
|
||||
if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)
|
||||
goto cleanup;
|
||||
add_dotgit =
|
||||
(opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
|
||||
(opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
|
||||
git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
|
||||
git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
|
||||
|
||||
is_reinit = git_path_isdir(repository_path.ptr) && valid_repository_path(&repository_path);
|
||||
if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
|
||||
return -1;
|
||||
|
||||
if (is_reinit) {
|
||||
/* TODO: reinitialize the templates */
|
||||
has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
|
||||
if (has_dotgit)
|
||||
opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;
|
||||
|
||||
if (repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0)
|
||||
goto cleanup;
|
||||
/* set up workdir path */
|
||||
|
||||
} else if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||
|
||||
repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0 ||
|
||||
repo_init_createhead(repository_path.ptr) < 0) {
|
||||
goto cleanup;
|
||||
if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0) {
|
||||
if (opts->workdir_path) {
|
||||
if (git_path_join_unrooted(
|
||||
wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
|
||||
return -1;
|
||||
} else if (has_dotgit) {
|
||||
if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
giterr_set(GITERR_REPOSITORY, "Cannot pick working directory"
|
||||
" for non-bare repository that isn't a '.git' directory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (git_path_to_dir(wd_path) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
git_buf_clear(wd_path);
|
||||
}
|
||||
|
||||
result = git_repository_open(repo_out, repository_path.ptr);
|
||||
natural_wd =
|
||||
has_dotgit &&
|
||||
wd_path->size > 0 &&
|
||||
wd_path->size + strlen(GIT_DIR) == repo_path->size &&
|
||||
memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0;
|
||||
if (natural_wd)
|
||||
opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD;
|
||||
|
||||
/* create directories as needed / requested */
|
||||
|
||||
dirmode = pick_dir_mode(opts);
|
||||
|
||||
if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 && has_dotgit) {
|
||||
git_buf p = GIT_BUF_INIT;
|
||||
if ((error = git_path_dirname_r(&p, repo_path->ptr)) >= 0)
|
||||
error = git_futils_mkdir(p.ptr, NULL, dirmode, 0);
|
||||
git_buf_free(&p);
|
||||
}
|
||||
|
||||
if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
|
||||
(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
|
||||
has_dotgit)
|
||||
{
|
||||
uint32_t mkflag = GIT_MKDIR_CHMOD;
|
||||
if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
|
||||
mkflag |= GIT_MKDIR_PATH;
|
||||
error = git_futils_mkdir(repo_path->ptr, NULL, dirmode, mkflag);
|
||||
}
|
||||
|
||||
if (wd_path->size > 0 &&
|
||||
!natural_wd &&
|
||||
((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
|
||||
(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0))
|
||||
error = git_futils_mkdir(wd_path->ptr, NULL, dirmode & ~S_ISGID,
|
||||
(opts->flags & GIT_REPOSITORY_INIT_MKPATH) ? GIT_MKDIR_PATH : 0);
|
||||
|
||||
/* prettify both directories now that they are created */
|
||||
|
||||
if (!error) {
|
||||
error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL);
|
||||
|
||||
if (!error && wd_path->size > 0)
|
||||
error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int repo_init_create_origin(git_repository *repo, const char *url)
|
||||
{
|
||||
int error;
|
||||
git_remote *remote;
|
||||
|
||||
if (!(error = git_remote_add(&remote, repo, "origin", url))) {
|
||||
error = git_remote_save(remote);
|
||||
git_remote_free(remote);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_init(
|
||||
git_repository **repo_out, const char *path, unsigned is_bare)
|
||||
{
|
||||
git_repository_init_options opts;
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */
|
||||
if (is_bare)
|
||||
opts.flags |= GIT_REPOSITORY_INIT_BARE;
|
||||
|
||||
return git_repository_init_ext(repo_out, path, &opts);
|
||||
}
|
||||
|
||||
int git_repository_init_ext(
|
||||
git_repository **repo_out,
|
||||
const char *given_repo,
|
||||
git_repository_init_options *opts)
|
||||
{
|
||||
int error;
|
||||
git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;
|
||||
|
||||
assert(repo_out && given_repo && opts);
|
||||
|
||||
error = repo_init_directories(&repo_path, &wd_path, given_repo, opts);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (valid_repository_path(&repo_path)) {
|
||||
|
||||
if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
|
||||
giterr_set(GITERR_REPOSITORY,
|
||||
"Attempt to reinitialize '%s'", given_repo);
|
||||
error = GIT_EEXISTS;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
|
||||
|
||||
error = repo_init_config(
|
||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
|
||||
|
||||
/* TODO: reinitialize the templates */
|
||||
}
|
||||
else {
|
||||
if (!(error = repo_init_structure(
|
||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
|
||||
!(error = repo_init_config(
|
||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
|
||||
error = repo_init_create_head(
|
||||
git_buf_cstr(&repo_path), opts->initial_head);
|
||||
}
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_repository_open(repo_out, git_buf_cstr(&repo_path));
|
||||
|
||||
if (!error && opts->origin_url)
|
||||
error = repo_init_create_origin(*repo_out, opts->origin_url);
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&repository_path);
|
||||
return result;
|
||||
git_buf_free(&repo_path);
|
||||
git_buf_free(&wd_path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_head_detached(git_repository *repo)
|
||||
@ -965,43 +1252,6 @@ const char *git_repository_workdir(git_repository *repo)
|
||||
return repo->workdir;
|
||||
}
|
||||
|
||||
static int write_gitlink(
|
||||
const char *in_dir, const char *to_repo)
|
||||
{
|
||||
int error;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
struct stat st;
|
||||
|
||||
if (git_path_dirname_r(&buf, to_repo) < 0 ||
|
||||
git_path_to_dir(&buf) < 0)
|
||||
return -1;
|
||||
|
||||
/* don't write gitlink to natural workdir */
|
||||
if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
|
||||
strcmp(in_dir, buf.ptr) == 0)
|
||||
return GIT_PASSTHROUGH;
|
||||
|
||||
if (git_buf_joinpath(&buf, in_dir, DOT_GIT) < 0)
|
||||
return -1;
|
||||
|
||||
if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
|
||||
giterr_set(GITERR_REPOSITORY,
|
||||
"Cannot overwrite gitlink file into path '%s'", in_dir);
|
||||
return GIT_EEXISTS;
|
||||
}
|
||||
|
||||
git_buf_clear(&buf);
|
||||
|
||||
if (git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo) < 0)
|
||||
return -1;
|
||||
|
||||
error = repo_write_template(in_dir, true, DOT_GIT, 0644, buf.ptr);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_set_workdir(
|
||||
git_repository *repo, const char *workdir, int update_gitlink)
|
||||
{
|
||||
@ -1022,7 +1272,7 @@ int git_repository_set_workdir(
|
||||
if (git_repository_config__weakptr(&config, repo) < 0)
|
||||
return -1;
|
||||
|
||||
error = write_gitlink(path.ptr, git_repository_path(repo));
|
||||
error = repo_write_gitlink(path.ptr, git_repository_path(repo));
|
||||
|
||||
/* passthrough error means gitlink is unnecessary */
|
||||
if (error == GIT_PASSTHROUGH)
|
||||
@ -1078,39 +1328,27 @@ int git_repository_message(char *buffer, size_t len, git_repository *repo)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
|
||||
struct stat st;
|
||||
ssize_t size;
|
||||
int error;
|
||||
|
||||
if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0)
|
||||
return -1;
|
||||
|
||||
error = p_stat(git_buf_cstr(&path), &st);
|
||||
if (error < 0) {
|
||||
if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
|
||||
if (errno == ENOENT)
|
||||
error = GIT_ENOTFOUND;
|
||||
|
||||
git_buf_free(&path);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (buffer == NULL) {
|
||||
git_buf_free(&path);
|
||||
return (int)st.st_size;
|
||||
else if (buffer != NULL) {
|
||||
error = git_futils_readbuffer(&buf, git_buf_cstr(&path));
|
||||
git_buf_copy_cstr(buffer, len, &buf);
|
||||
}
|
||||
|
||||
if (git_futils_readbuffer(&buf, git_buf_cstr(&path)) < 0)
|
||||
goto on_error;
|
||||
|
||||
memcpy(buffer, git_buf_cstr(&buf), len);
|
||||
size = git_buf_len(&buf);
|
||||
|
||||
git_buf_free(&path);
|
||||
git_buf_free(&buf);
|
||||
return size;
|
||||
|
||||
on_error:
|
||||
git_buf_free(&path);
|
||||
return -1;
|
||||
if (!error)
|
||||
error = (int)st.st_size + 1; /* add 1 for NUL byte */
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_message_remove(git_repository *repo)
|
||||
|
@ -68,6 +68,13 @@ typedef enum {
|
||||
GIT_EOL_DEFAULT = GIT_EOL_NATIVE
|
||||
} git_cvar_value;
|
||||
|
||||
/* internal repository init flags */
|
||||
enum {
|
||||
GIT_REPOSITORY_INIT__HAS_DOTGIT = (1u << 16),
|
||||
GIT_REPOSITORY_INIT__NATURAL_WD = (1u << 17),
|
||||
GIT_REPOSITORY_INIT__IS_REINIT = (1u << 18),
|
||||
};
|
||||
|
||||
/** Base git object for inheritance */
|
||||
struct git_object {
|
||||
git_cached_obj cached;
|
||||
@ -75,6 +82,7 @@ struct git_object {
|
||||
git_otype type;
|
||||
};
|
||||
|
||||
/** Internal structure for repository object */
|
||||
struct git_repository {
|
||||
git_odb *_odb;
|
||||
git_config *_config;
|
||||
@ -94,8 +102,7 @@ struct git_repository {
|
||||
git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
|
||||
};
|
||||
|
||||
/* fully free the object; internal method, do not
|
||||
* export */
|
||||
/* fully free the object; internal method, DO NOT EXPORT */
|
||||
void git_object__free(void *object);
|
||||
|
||||
GIT_INLINE(int) git_object__dup(git_object **dest, git_object *source)
|
||||
|
@ -233,7 +233,7 @@ static int http_recv_cb(gitno_buffer *buf)
|
||||
if (t->error < 0)
|
||||
return t->error;
|
||||
|
||||
return buf->offset - old_len;
|
||||
return (int)(buf->offset - old_len);
|
||||
}
|
||||
|
||||
/* Set up the gitno_buffer so calling gitno_recv() grabs data from the HTTP response */
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
#define p_lstat(p,b) lstat(p,b)
|
||||
#define p_readlink(a, b, c) readlink(a, b, c)
|
||||
#define p_symlink(o,n) symlink(o, n)
|
||||
#define p_link(o,n) link(o, n)
|
||||
#define p_symlink(o,n) symlink(o,n)
|
||||
#define p_unlink(p) unlink(p)
|
||||
#define p_mkdir(p,m) mkdir(p, m)
|
||||
#define p_fsync(fd) fsync(fd)
|
||||
|
@ -33,7 +33,7 @@ static void test_file_contents(const char *path, const char *expectedcontents)
|
||||
actuallen = p_read(fd, buffer, 1024);
|
||||
cl_git_pass(p_close(fd));
|
||||
|
||||
cl_assert_equal_i(actuallen, expectedlen);
|
||||
cl_assert_equal_sz(actuallen, expectedlen);
|
||||
cl_assert_equal_s(buffer, expectedcontents);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,8 @@
|
||||
*/
|
||||
#define cl_git_fail(expr) cl_must_fail(expr)
|
||||
|
||||
#define cl_assert_equal_sz(sz1,sz2) cl_assert((sz1) == (sz2))
|
||||
|
||||
/*
|
||||
* Some utility macros for building long strings
|
||||
*/
|
||||
|
@ -665,7 +665,7 @@ static void assert_unescape(char *expected, char *to_unescape) {
|
||||
cl_git_pass(git_buf_sets(&buf, to_unescape));
|
||||
git_buf_unescape(&buf);
|
||||
cl_assert_equal_s(expected, buf.ptr);
|
||||
cl_assert_equal_i(strlen(expected), buf.size);
|
||||
cl_assert_equal_sz(strlen(expected), buf.size);
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
126
tests-clar/core/copy.c
Normal file
126
tests-clar/core/copy.c
Normal file
@ -0,0 +1,126 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "fileops.h"
|
||||
#include "path.h"
|
||||
#include "posix.h"
|
||||
|
||||
void test_core_copy__file(void)
|
||||
{
|
||||
struct stat st;
|
||||
const char *content = "This is some stuff to copy\n";
|
||||
|
||||
cl_git_mkfile("copy_me", content);
|
||||
|
||||
cl_git_pass(git_futils_cp("copy_me", "copy_me_two", 0664));
|
||||
|
||||
cl_git_pass(git_path_lstat("copy_me_two", &st));
|
||||
cl_assert(S_ISREG(st.st_mode));
|
||||
cl_assert(strlen(content) == (size_t)st.st_size);
|
||||
|
||||
cl_git_pass(p_unlink("copy_me_two"));
|
||||
cl_git_pass(p_unlink("copy_me"));
|
||||
}
|
||||
|
||||
void test_core_copy__file_in_dir(void)
|
||||
{
|
||||
struct stat st;
|
||||
const char *content = "This is some other stuff to copy\n";
|
||||
|
||||
cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", NULL, 0775, GIT_MKDIR_PATH));
|
||||
cl_git_mkfile("an_dir/in_a_dir/copy_me", content);
|
||||
cl_assert(git_path_isdir("an_dir"));
|
||||
|
||||
cl_git_pass(git_futils_mkpath2file
|
||||
("an_dir/second_dir/and_more/copy_me_two", 0775));
|
||||
|
||||
cl_git_pass(git_futils_cp
|
||||
("an_dir/in_a_dir/copy_me",
|
||||
"an_dir/second_dir/and_more/copy_me_two",
|
||||
0664));
|
||||
|
||||
cl_git_pass(git_path_lstat("an_dir/second_dir/and_more/copy_me_two", &st));
|
||||
cl_assert(S_ISREG(st.st_mode));
|
||||
cl_assert(strlen(content) == (size_t)st.st_size);
|
||||
|
||||
cl_git_pass(git_futils_rmdir_r("an_dir", GIT_DIRREMOVAL_FILES_AND_DIRS));
|
||||
cl_assert(!git_path_isdir("an_dir"));
|
||||
}
|
||||
|
||||
void test_core_copy__tree(void)
|
||||
{
|
||||
struct stat st;
|
||||
const char *content = "File content\n";
|
||||
|
||||
cl_git_pass(git_futils_mkdir("src/b", NULL, 0775, GIT_MKDIR_PATH));
|
||||
cl_git_pass(git_futils_mkdir("src/c/d", NULL, 0775, GIT_MKDIR_PATH));
|
||||
cl_git_pass(git_futils_mkdir("src/c/e", NULL, 0775, GIT_MKDIR_PATH));
|
||||
|
||||
cl_git_mkfile("src/f1", content);
|
||||
cl_git_mkfile("src/b/f2", content);
|
||||
cl_git_mkfile("src/c/f3", content);
|
||||
cl_git_mkfile("src/c/d/f4", content);
|
||||
cl_git_mkfile("src/c/d/.f5", content);
|
||||
|
||||
#ifndef GIT_WIN32
|
||||
cl_assert(p_symlink("../../b/f2", "src/c/d/l1") == 0);
|
||||
#endif
|
||||
|
||||
cl_assert(git_path_isdir("src"));
|
||||
cl_assert(git_path_isdir("src/b"));
|
||||
cl_assert(git_path_isdir("src/c/d"));
|
||||
cl_assert(git_path_isfile("src/c/d/f4"));
|
||||
|
||||
/* copy with no empty dirs, yes links, no dotfiles, no overwrite */
|
||||
|
||||
cl_git_pass(
|
||||
git_futils_cp_r("src", "t1", GIT_CPDIR_COPY_SYMLINKS, 0) );
|
||||
|
||||
cl_assert(git_path_isdir("t1"));
|
||||
cl_assert(git_path_isdir("t1/b"));
|
||||
cl_assert(git_path_isdir("t1/c"));
|
||||
cl_assert(git_path_isdir("t1/c/d"));
|
||||
cl_assert(!git_path_isdir("t1/c/e"));
|
||||
|
||||
cl_assert(git_path_isfile("t1/f1"));
|
||||
cl_assert(git_path_isfile("t1/b/f2"));
|
||||
cl_assert(git_path_isfile("t1/c/f3"));
|
||||
cl_assert(git_path_isfile("t1/c/d/f4"));
|
||||
cl_assert(!git_path_isfile("t1/c/d/.f5"));
|
||||
|
||||
cl_git_pass(git_path_lstat("t1/c/f3", &st));
|
||||
cl_assert(S_ISREG(st.st_mode));
|
||||
cl_assert(strlen(content) == (size_t)st.st_size);
|
||||
|
||||
#ifndef GIT_WIN32
|
||||
cl_git_pass(git_path_lstat("t1/c/d/l1", &st));
|
||||
cl_assert(S_ISLNK(st.st_mode));
|
||||
#endif
|
||||
|
||||
cl_git_pass(git_futils_rmdir_r("t1", GIT_DIRREMOVAL_FILES_AND_DIRS));
|
||||
cl_assert(!git_path_isdir("t1"));
|
||||
|
||||
/* copy with empty dirs, no links, yes dotfiles, no overwrite */
|
||||
|
||||
cl_git_pass(
|
||||
git_futils_cp_r("src", "t2", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_COPY_DOTFILES, 0) );
|
||||
|
||||
cl_assert(git_path_isdir("t2"));
|
||||
cl_assert(git_path_isdir("t2/b"));
|
||||
cl_assert(git_path_isdir("t2/c"));
|
||||
cl_assert(git_path_isdir("t2/c/d"));
|
||||
cl_assert(git_path_isdir("t2/c/e"));
|
||||
|
||||
cl_assert(git_path_isfile("t2/f1"));
|
||||
cl_assert(git_path_isfile("t2/b/f2"));
|
||||
cl_assert(git_path_isfile("t2/c/f3"));
|
||||
cl_assert(git_path_isfile("t2/c/d/f4"));
|
||||
cl_assert(git_path_isfile("t2/c/d/.f5"));
|
||||
|
||||
#ifndef GIT_WIN32
|
||||
cl_git_fail(git_path_lstat("t2/c/d/l1", &st));
|
||||
#endif
|
||||
|
||||
cl_git_pass(git_futils_rmdir_r("t2", GIT_DIRREMOVAL_FILES_AND_DIRS));
|
||||
cl_assert(!git_path_isdir("t2"));
|
||||
|
||||
cl_git_pass(git_futils_rmdir_r("src", GIT_DIRREMOVAL_FILES_AND_DIRS));
|
||||
}
|
172
tests-clar/core/mkdir.c
Normal file
172
tests-clar/core/mkdir.c
Normal file
@ -0,0 +1,172 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "fileops.h"
|
||||
#include "path.h"
|
||||
#include "posix.h"
|
||||
|
||||
static void cleanup_basic_dirs(void *ref)
|
||||
{
|
||||
GIT_UNUSED(ref);
|
||||
git_futils_rmdir_r("d0", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
|
||||
git_futils_rmdir_r("d1", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
|
||||
git_futils_rmdir_r("d2", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
|
||||
git_futils_rmdir_r("d3", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
|
||||
git_futils_rmdir_r("d4", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
|
||||
}
|
||||
|
||||
void test_core_mkdir__basic(void)
|
||||
{
|
||||
cl_set_cleanup(cleanup_basic_dirs, NULL);
|
||||
|
||||
/* make a directory */
|
||||
cl_assert(!git_path_isdir("d0"));
|
||||
cl_git_pass(git_futils_mkdir("d0", NULL, 0755, 0));
|
||||
cl_assert(git_path_isdir("d0"));
|
||||
|
||||
/* make a path */
|
||||
cl_assert(!git_path_isdir("d1"));
|
||||
cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", NULL, 0755, GIT_MKDIR_PATH));
|
||||
cl_assert(git_path_isdir("d1"));
|
||||
cl_assert(git_path_isdir("d1/d1.1"));
|
||||
cl_assert(git_path_isdir("d1/d1.1/d1.2"));
|
||||
|
||||
/* make a dir exclusively */
|
||||
cl_assert(!git_path_isdir("d2"));
|
||||
cl_git_pass(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
|
||||
cl_assert(git_path_isdir("d2"));
|
||||
|
||||
/* make exclusive failure */
|
||||
cl_git_fail(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
|
||||
|
||||
/* make a path exclusively */
|
||||
cl_assert(!git_path_isdir("d3"));
|
||||
cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
|
||||
cl_assert(git_path_isdir("d3"));
|
||||
cl_assert(git_path_isdir("d3/d3.1/d3.2"));
|
||||
|
||||
/* make exclusive path failure */
|
||||
cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
|
||||
/* ??? Should EXCL only apply to the last item in the path? */
|
||||
|
||||
/* path with trailing slash? */
|
||||
cl_assert(!git_path_isdir("d4"));
|
||||
cl_git_pass(git_futils_mkdir("d4/d4.1/", NULL, 0755, GIT_MKDIR_PATH));
|
||||
cl_assert(git_path_isdir("d4/d4.1"));
|
||||
}
|
||||
|
||||
static void cleanup_basedir(void *ref)
|
||||
{
|
||||
GIT_UNUSED(ref);
|
||||
git_futils_rmdir_r("base", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
|
||||
}
|
||||
|
||||
void test_core_mkdir__with_base(void)
|
||||
{
|
||||
#define BASEDIR "base/dir/here"
|
||||
|
||||
cl_set_cleanup(cleanup_basedir, NULL);
|
||||
|
||||
cl_git_pass(git_futils_mkdir(BASEDIR, NULL, 0755, GIT_MKDIR_PATH));
|
||||
|
||||
cl_git_pass(git_futils_mkdir("a", BASEDIR, 0755, 0));
|
||||
cl_assert(git_path_isdir(BASEDIR "/a"));
|
||||
|
||||
cl_git_pass(git_futils_mkdir("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH));
|
||||
cl_assert(git_path_isdir(BASEDIR "/b/b1/b2"));
|
||||
|
||||
/* exclusive with existing base */
|
||||
cl_git_pass(git_futils_mkdir("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
|
||||
|
||||
/* fail: exclusive with duplicated suffix */
|
||||
cl_git_fail(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
|
||||
|
||||
/* fail: exclusive with any duplicated component */
|
||||
cl_git_fail(git_futils_mkdir("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
|
||||
|
||||
/* success: exclusive without path */
|
||||
cl_git_pass(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL));
|
||||
|
||||
/* path with shorter base and existing dirs */
|
||||
cl_git_pass(git_futils_mkdir("dir/here/d/", "base", 0755, GIT_MKDIR_PATH));
|
||||
cl_assert(git_path_isdir("base/dir/here/d"));
|
||||
|
||||
/* fail: path with shorter base and existing dirs */
|
||||
cl_git_fail(git_futils_mkdir("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
|
||||
|
||||
/* fail: base with missing components */
|
||||
cl_git_fail(git_futils_mkdir("f/", "base/missing", 0755, GIT_MKDIR_PATH));
|
||||
|
||||
/* success: shift missing component to path */
|
||||
cl_git_pass(git_futils_mkdir("missing/f/", "base/", 0755, GIT_MKDIR_PATH));
|
||||
}
|
||||
|
||||
static void cleanup_chmod_root(void *ref)
|
||||
{
|
||||
mode_t *mode = ref;
|
||||
|
||||
if (*mode != 0) {
|
||||
(void)p_umask(*mode);
|
||||
git__free(mode);
|
||||
}
|
||||
|
||||
git_futils_rmdir_r("r", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
|
||||
}
|
||||
|
||||
void test_core_mkdir__chmods(void)
|
||||
{
|
||||
struct stat st;
|
||||
mode_t *old = git__malloc(sizeof(mode_t));
|
||||
*old = p_umask(022);
|
||||
|
||||
cl_set_cleanup(cleanup_chmod_root, old);
|
||||
|
||||
cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
|
||||
|
||||
cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
|
||||
|
||||
cl_git_pass(git_path_lstat("r/mode", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0755);
|
||||
cl_git_pass(git_path_lstat("r/mode/is", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0755);
|
||||
cl_git_pass(git_path_lstat("r/mode/is/important", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0755);
|
||||
|
||||
cl_git_pass(git_futils_mkdir("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
|
||||
|
||||
cl_git_pass(git_path_lstat("r/mode2", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0755);
|
||||
cl_git_pass(git_path_lstat("r/mode2/is2", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0755);
|
||||
cl_git_pass(git_path_lstat("r/mode2/is2/important2", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0777);
|
||||
|
||||
cl_git_pass(git_futils_mkdir("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
|
||||
|
||||
cl_git_pass(git_path_lstat("r/mode3", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0777);
|
||||
cl_git_pass(git_path_lstat("r/mode3/is3", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0777);
|
||||
cl_git_pass(git_path_lstat("r/mode3/is3/important3", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0777);
|
||||
|
||||
/* test that we chmod existing dir */
|
||||
|
||||
cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
|
||||
|
||||
cl_git_pass(git_path_lstat("r/mode", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0755);
|
||||
cl_git_pass(git_path_lstat("r/mode/is", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0755);
|
||||
cl_git_pass(git_path_lstat("r/mode/is/important", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0777);
|
||||
|
||||
/* test that we chmod even existing dirs if CHMOD_PATH is set */
|
||||
|
||||
cl_git_pass(git_futils_mkdir("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
|
||||
|
||||
cl_git_pass(git_path_lstat("r/mode2", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0777);
|
||||
cl_git_pass(git_path_lstat("r/mode2/is2", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0777);
|
||||
cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st));
|
||||
cl_assert((st.st_mode & 0777) == 0777);
|
||||
}
|
@ -36,7 +36,7 @@ void test_refs_list__all(void)
|
||||
/* We have exactly 9 refs in total if we include the packed ones:
|
||||
* there is a reference that exists both in the packfile and as
|
||||
* loose, but we only list it once */
|
||||
cl_assert_equal_i(ref_list.count, 10);
|
||||
cl_assert_equal_i((int)ref_list.count, 10);
|
||||
|
||||
git_strarray_free(&ref_list);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "fileops.h"
|
||||
#include "repository.h"
|
||||
#include "config.h"
|
||||
#include "path.h"
|
||||
|
||||
enum repo_mode {
|
||||
STANDARD_REPOSITORY = 0,
|
||||
@ -83,7 +84,7 @@ void test_repo_init__bare_repo_escaping_current_workdir(void)
|
||||
git_buf path_current_workdir = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL));
|
||||
|
||||
|
||||
cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c"));
|
||||
cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), NULL, GIT_DIR_MODE));
|
||||
|
||||
@ -295,3 +296,82 @@ void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
|
||||
git_repository_free(_repo);
|
||||
assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
|
||||
}
|
||||
|
||||
void test_repo_init__extended_0(void)
|
||||
{
|
||||
git_repository_init_options opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
/* without MKDIR this should fail */
|
||||
cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts));
|
||||
|
||||
/* make the directory first, then it should succeed */
|
||||
cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
|
||||
cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts));
|
||||
|
||||
cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/"));
|
||||
cl_assert(!git__suffixcmp(git_repository_path(_repo), "/extended/.git/"));
|
||||
cl_assert(!git_repository_is_bare(_repo));
|
||||
cl_assert(git_repository_is_empty(_repo));
|
||||
|
||||
cleanup_repository("extended");
|
||||
}
|
||||
|
||||
void test_repo_init__extended_1(void)
|
||||
{
|
||||
git_reference *ref;
|
||||
git_remote *remote;
|
||||
struct stat st;
|
||||
git_repository_init_options opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
opts.flags = GIT_REPOSITORY_INIT_MKPATH |
|
||||
GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
|
||||
opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
|
||||
opts.workdir_path = "../c_wd";
|
||||
opts.description = "Awesomest test repository evah";
|
||||
opts.initial_head = "development";
|
||||
opts.origin_url = "https://github.com/libgit2/libgit2.git";
|
||||
|
||||
cl_git_pass(git_repository_init_ext(&_repo, "root/b/c.git", &opts));
|
||||
|
||||
cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/c_wd/"));
|
||||
cl_assert(!git__suffixcmp(git_repository_path(_repo), "/c.git/"));
|
||||
cl_assert(git_path_isfile("root/b/c_wd/.git"));
|
||||
cl_assert(!git_repository_is_bare(_repo));
|
||||
/* repo will not be counted as empty because we set head to "development" */
|
||||
cl_assert(!git_repository_is_empty(_repo));
|
||||
|
||||
cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
|
||||
cl_assert(S_ISDIR(st.st_mode));
|
||||
cl_assert((S_ISGID & st.st_mode) == S_ISGID);
|
||||
|
||||
cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
|
||||
cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
|
||||
cl_assert_equal_s("refs/heads/development", git_reference_target(ref));
|
||||
git_reference_free(ref);
|
||||
|
||||
cl_git_pass(git_remote_load(&remote, _repo, "origin"));
|
||||
cl_assert_equal_s("origin", git_remote_name(remote));
|
||||
cl_assert_equal_s(opts.origin_url, git_remote_url(remote));
|
||||
git_remote_free(remote);
|
||||
|
||||
git_repository_free(_repo);
|
||||
cl_fixture_cleanup("root");
|
||||
}
|
||||
|
||||
void test_repo_init__extended_with_template(void)
|
||||
{
|
||||
git_repository_init_options opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE;
|
||||
opts.template_path = cl_fixture("template");
|
||||
|
||||
cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts));
|
||||
|
||||
cl_assert(git_repository_is_bare(_repo));
|
||||
cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/"));
|
||||
|
||||
cleanup_repository("templated.git");
|
||||
}
|
||||
|
2
tests-clar/resources/template/branches/.gitignore
vendored
Normal file
2
tests-clar/resources/template/branches/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# This file should not be copied, nor should the
|
||||
# containing directory, since it is effectively "empty"
|
1
tests-clar/resources/template/description
Normal file
1
tests-clar/resources/template/description
Normal file
@ -0,0 +1 @@
|
||||
Unnamed repository; edit this file 'description' to name the repository.
|
15
tests-clar/resources/template/hooks/applypatch-msg.sample
Executable file
15
tests-clar/resources/template/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+"$@"}
|
||||
:
|
24
tests-clar/resources/template/hooks/commit-msg.sample
Executable file
24
tests-clar/resources/template/hooks/commit-msg.sample
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to check the commit log message.
|
||||
# Called by "git commit" with one argument, the name of the file
|
||||
# that has the commit 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 "commit-msg".
|
||||
|
||||
# Uncomment the below to add a Signed-off-by line to the message.
|
||||
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
||||
# hook is more suited to it.
|
||||
#
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
||||
|
||||
# This example catches duplicate Signed-off-by lines.
|
||||
|
||||
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
||||
echo >&2 Duplicate Signed-off-by lines.
|
||||
exit 1
|
||||
}
|
8
tests-clar/resources/template/hooks/post-commit.sample
Executable file
8
tests-clar/resources/template/hooks/post-commit.sample
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script that is called after a successful
|
||||
# commit is made.
|
||||
#
|
||||
# To enable this hook, rename this file to "post-commit".
|
||||
|
||||
: Nothing
|
15
tests-clar/resources/template/hooks/post-receive.sample
Executable file
15
tests-clar/resources/template/hooks/post-receive.sample
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script for the "post-receive" event.
|
||||
#
|
||||
# The "post-receive" script is run after receive-pack has accepted a pack
|
||||
# and the repository has been updated. It is passed arguments in through
|
||||
# stdin in the form
|
||||
# <oldrev> <newrev> <refname>
|
||||
# For example:
|
||||
# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
|
||||
#
|
||||
# see contrib/hooks/ for a sample, or uncomment the next line and
|
||||
# rename the file to "post-receive".
|
||||
|
||||
#. /usr/share/doc/git-core/contrib/hooks/post-receive-email
|
8
tests-clar/resources/template/hooks/post-update.sample
Executable file
8
tests-clar/resources/template/hooks/post-update.sample
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to prepare a packed repository for use over
|
||||
# dumb transports.
|
||||
#
|
||||
# To enable this hook, rename this file to "post-update".
|
||||
|
||||
exec git update-server-info
|
14
tests-clar/resources/template/hooks/pre-applypatch.sample
Executable file
14
tests-clar/resources/template/hooks/pre-applypatch.sample
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to verify what is about to be committed
|
||||
# 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.
|
||||
#
|
||||
# To enable this hook, rename this file to "pre-applypatch".
|
||||
|
||||
. git-sh-setup
|
||||
test -x "$GIT_DIR/hooks/pre-commit" &&
|
||||
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
|
||||
:
|
46
tests-clar/resources/template/hooks/pre-commit.sample
Executable file
46
tests-clar/resources/template/hooks/pre-commit.sample
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to verify what is about to be committed.
|
||||
# Called by "git commit" with no arguments. The hook should
|
||||
# exit with non-zero status after issuing an appropriate message if
|
||||
# it wants to stop the commit.
|
||||
#
|
||||
# To enable this hook, rename this file to "pre-commit".
|
||||
|
||||
if git rev-parse --verify HEAD >/dev/null 2>&1
|
||||
then
|
||||
against=HEAD
|
||||
else
|
||||
# Initial commit: diff against an empty tree object
|
||||
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||
fi
|
||||
|
||||
# If you want to allow non-ascii filenames set this variable to true.
|
||||
allownonascii=$(git config hooks.allownonascii)
|
||||
|
||||
# Cross platform projects tend to avoid non-ascii filenames; prevent
|
||||
# them from being added to the repository. We exploit the fact that the
|
||||
# printable range starts at the space character and ends with tilde.
|
||||
if [ "$allownonascii" != "true" ] &&
|
||||
# Note that the use of brackets around a tr range is ok here, (it's
|
||||
# even required, for portability to Solaris 10's /usr/bin/tr), since
|
||||
# the square bracket bytes happen to fall in the designated range.
|
||||
test "$(git diff --cached --name-only --diff-filter=A -z $against |
|
||||
LC_ALL=C tr -d '[ -~]\0')"
|
||||
then
|
||||
echo "Error: Attempt to add a non-ascii file name."
|
||||
echo
|
||||
echo "This can cause problems if you want to work"
|
||||
echo "with people on other platforms."
|
||||
echo
|
||||
echo "To be portable it is advisable to rename the file ..."
|
||||
echo
|
||||
echo "If you know what you are doing you can disable this"
|
||||
echo "check using:"
|
||||
echo
|
||||
echo " git config hooks.allownonascii true"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec git diff-index --check --cached $against --
|
169
tests-clar/resources/template/hooks/pre-rebase.sample
Executable file
169
tests-clar/resources/template/hooks/pre-rebase.sample
Executable file
@ -0,0 +1,169 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006, 2008 Junio C Hamano
|
||||
#
|
||||
# The "pre-rebase" hook is run just before "git rebase" starts doing
|
||||
# its job, and can prevent the command from running by exiting with
|
||||
# non-zero status.
|
||||
#
|
||||
# The hook is called with the following parameters:
|
||||
#
|
||||
# $1 -- the upstream the series was forked from.
|
||||
# $2 -- the branch being rebased (or empty when rebasing the current branch).
|
||||
#
|
||||
# This sample shows how to prevent topic branches that are already
|
||||
# merged to 'next' branch from getting rebased, because allowing it
|
||||
# would result in rebasing already published history.
|
||||
|
||||
publish=next
|
||||
basebranch="$1"
|
||||
if test "$#" = 2
|
||||
then
|
||||
topic="refs/heads/$2"
|
||||
else
|
||||
topic=`git symbolic-ref HEAD` ||
|
||||
exit 0 ;# we do not interrupt rebasing detached HEAD
|
||||
fi
|
||||
|
||||
case "$topic" in
|
||||
refs/heads/??/*)
|
||||
;;
|
||||
*)
|
||||
exit 0 ;# we do not interrupt others.
|
||||
;;
|
||||
esac
|
||||
|
||||
# Now we are dealing with a topic branch being rebased
|
||||
# on top of master. Is it OK to rebase it?
|
||||
|
||||
# Does the topic really exist?
|
||||
git show-ref -q "$topic" || {
|
||||
echo >&2 "No such branch $topic"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Is topic fully merged to master?
|
||||
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
|
||||
if test -z "$not_in_master"
|
||||
then
|
||||
echo >&2 "$topic is fully merged to master; better remove it."
|
||||
exit 1 ;# we could allow it, but there is no point.
|
||||
fi
|
||||
|
||||
# Is topic ever merged to next? If so you should not be rebasing it.
|
||||
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
|
||||
only_next_2=`git rev-list ^master ${publish} | sort`
|
||||
if test "$only_next_1" = "$only_next_2"
|
||||
then
|
||||
not_in_topic=`git rev-list "^$topic" master`
|
||||
if test -z "$not_in_topic"
|
||||
then
|
||||
echo >&2 "$topic is already up-to-date with master"
|
||||
exit 1 ;# we could allow it, but there is no point.
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
|
||||
/usr/bin/perl -e '
|
||||
my $topic = $ARGV[0];
|
||||
my $msg = "* $topic has commits already merged to public branch:\n";
|
||||
my (%not_in_next) = map {
|
||||
/^([0-9a-f]+) /;
|
||||
($1 => 1);
|
||||
} split(/\n/, $ARGV[1]);
|
||||
for my $elem (map {
|
||||
/^([0-9a-f]+) (.*)$/;
|
||||
[$1 => $2];
|
||||
} split(/\n/, $ARGV[2])) {
|
||||
if (!exists $not_in_next{$elem->[0]}) {
|
||||
if ($msg) {
|
||||
print STDERR $msg;
|
||||
undef $msg;
|
||||
}
|
||||
print STDERR " $elem->[1]\n";
|
||||
}
|
||||
}
|
||||
' "$topic" "$not_in_next" "$not_in_master"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
################################################################
|
||||
|
||||
This sample hook safeguards topic branches that have been
|
||||
published from being rewound.
|
||||
|
||||
The workflow assumed here is:
|
||||
|
||||
* Once a topic branch forks from "master", "master" is never
|
||||
merged into it again (either directly or indirectly).
|
||||
|
||||
* Once a topic branch is fully cooked and merged into "master",
|
||||
it is deleted. If you need to build on top of it to correct
|
||||
earlier mistakes, a new topic branch is created by forking at
|
||||
the tip of the "master". This is not strictly necessary, but
|
||||
it makes it easier to keep your history simple.
|
||||
|
||||
* Whenever you need to test or publish your changes to topic
|
||||
branches, merge them into "next" branch.
|
||||
|
||||
The script, being an example, hardcodes the publish branch name
|
||||
to be "next", but it is trivial to make it configurable via
|
||||
$GIT_DIR/config mechanism.
|
||||
|
||||
With this workflow, you would want to know:
|
||||
|
||||
(1) ... if a topic branch has ever been merged to "next". Young
|
||||
topic branches can have stupid mistakes you would rather
|
||||
clean up before publishing, and things that have not been
|
||||
merged into other branches can be easily rebased without
|
||||
affecting other people. But once it is published, you would
|
||||
not want to rewind it.
|
||||
|
||||
(2) ... if a topic branch has been fully merged to "master".
|
||||
Then you can delete it. More importantly, you should not
|
||||
build on top of it -- other people may already want to
|
||||
change things related to the topic as patches against your
|
||||
"master", so if you need further changes, it is better to
|
||||
fork the topic (perhaps with the same name) afresh from the
|
||||
tip of "master".
|
||||
|
||||
Let's look at this example:
|
||||
|
||||
o---o---o---o---o---o---o---o---o---o "next"
|
||||
/ / / /
|
||||
/ a---a---b A / /
|
||||
/ / / /
|
||||
/ / c---c---c---c B /
|
||||
/ / / \ /
|
||||
/ / / b---b C \ /
|
||||
/ / / / \ /
|
||||
---o---o---o---o---o---o---o---o---o---o---o "master"
|
||||
|
||||
|
||||
A, B and C are topic branches.
|
||||
|
||||
* A has one fix since it was merged up to "next".
|
||||
|
||||
* B has finished. It has been fully merged up to "master" and "next",
|
||||
and is ready to be deleted.
|
||||
|
||||
* C has not merged to "next" at all.
|
||||
|
||||
We would want to allow C to be rebased, refuse A, and encourage
|
||||
B to be deleted.
|
||||
|
||||
To compute (1):
|
||||
|
||||
git rev-list ^master ^topic next
|
||||
git rev-list ^master next
|
||||
|
||||
if these match, topic has not merged in next at all.
|
||||
|
||||
To compute (2):
|
||||
|
||||
git rev-list master..topic
|
||||
|
||||
if this is empty, it is fully merged to "master".
|
36
tests-clar/resources/template/hooks/prepare-commit-msg.sample
Executable file
36
tests-clar/resources/template/hooks/prepare-commit-msg.sample
Executable file
@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to prepare the commit log message.
|
||||
# Called by "git commit" with the name of the file that has the
|
||||
# commit message, followed by the description of the commit
|
||||
# message's source. The hook's purpose is to edit the commit
|
||||
# message file. If the hook fails with a non-zero status,
|
||||
# the commit is aborted.
|
||||
#
|
||||
# To enable this hook, rename this file to "prepare-commit-msg".
|
||||
|
||||
# This hook includes three examples. The first comments out the
|
||||
# "Conflicts:" part of a merge commit.
|
||||
#
|
||||
# The second includes the output of "git diff --name-status -r"
|
||||
# into the message, just before the "git status" output. It is
|
||||
# commented because it doesn't cope with --amend or with squashed
|
||||
# commits.
|
||||
#
|
||||
# The third example adds a Signed-off-by line to the message, that can
|
||||
# still be edited. This is rarely a good idea.
|
||||
|
||||
case "$2,$3" in
|
||||
merge,)
|
||||
/usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
|
||||
|
||||
# ,|template,)
|
||||
# /usr/bin/perl -i.bak -pe '
|
||||
# print "\n" . `git diff --cached --name-status -r`
|
||||
# if /^#/ && $first++ == 0' "$1" ;;
|
||||
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
128
tests-clar/resources/template/hooks/update.sample
Executable file
128
tests-clar/resources/template/hooks/update.sample
Executable file
@ -0,0 +1,128 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to blocks unannotated tags from entering.
|
||||
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
|
||||
#
|
||||
# To enable this hook, rename this file to "update".
|
||||
#
|
||||
# Config
|
||||
# ------
|
||||
# hooks.allowunannotated
|
||||
# This boolean sets whether unannotated tags will be allowed into the
|
||||
# repository. By default they won't be.
|
||||
# hooks.allowdeletetag
|
||||
# This boolean sets whether deleting tags will be allowed in the
|
||||
# repository. By default they won't be.
|
||||
# hooks.allowmodifytag
|
||||
# This boolean sets whether a tag may be modified after creation. By default
|
||||
# it won't be.
|
||||
# hooks.allowdeletebranch
|
||||
# This boolean sets whether deleting branches will be allowed in the
|
||||
# repository. By default they won't be.
|
||||
# hooks.denycreatebranch
|
||||
# This boolean sets whether remotely creating branches will be denied
|
||||
# in the repository. By default this is allowed.
|
||||
#
|
||||
|
||||
# --- Command line
|
||||
refname="$1"
|
||||
oldrev="$2"
|
||||
newrev="$3"
|
||||
|
||||
# --- Safety check
|
||||
if [ -z "$GIT_DIR" ]; then
|
||||
echo "Don't run this script from the command line." >&2
|
||||
echo " (if you want, you could supply GIT_DIR then run" >&2
|
||||
echo " $0 <ref> <oldrev> <newrev>)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
|
||||
echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Config
|
||||
allowunannotated=$(git config --bool hooks.allowunannotated)
|
||||
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
|
||||
denycreatebranch=$(git config --bool hooks.denycreatebranch)
|
||||
allowdeletetag=$(git config --bool hooks.allowdeletetag)
|
||||
allowmodifytag=$(git config --bool hooks.allowmodifytag)
|
||||
|
||||
# check for no description
|
||||
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
|
||||
case "$projectdesc" in
|
||||
"Unnamed repository"* | "")
|
||||
echo "*** Project description file hasn't been set" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Check types
|
||||
# if $newrev is 0000...0000, it's a commit to delete a ref.
|
||||
zero="0000000000000000000000000000000000000000"
|
||||
if [ "$newrev" = "$zero" ]; then
|
||||
newrev_type=delete
|
||||
else
|
||||
newrev_type=$(git cat-file -t $newrev)
|
||||
fi
|
||||
|
||||
case "$refname","$newrev_type" in
|
||||
refs/tags/*,commit)
|
||||
# un-annotated tag
|
||||
short_refname=${refname##refs/tags/}
|
||||
if [ "$allowunannotated" != "true" ]; then
|
||||
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
|
||||
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/tags/*,delete)
|
||||
# delete tag
|
||||
if [ "$allowdeletetag" != "true" ]; then
|
||||
echo "*** Deleting a tag is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/tags/*,tag)
|
||||
# annotated tag
|
||||
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
|
||||
then
|
||||
echo "*** Tag '$refname' already exists." >&2
|
||||
echo "*** Modifying a tag is not allowed in this repository." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*,commit)
|
||||
# branch
|
||||
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
|
||||
echo "*** Creating a branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*,delete)
|
||||
# delete branch
|
||||
if [ "$allowdeletebranch" != "true" ]; then
|
||||
echo "*** Deleting a branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/remotes/*,commit)
|
||||
# tracking branch
|
||||
;;
|
||||
refs/remotes/*,delete)
|
||||
# delete tracking branch
|
||||
if [ "$allowdeletebranch" != "true" ]; then
|
||||
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Anything else (is there anything else?)
|
||||
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Finished
|
||||
exit 0
|
6
tests-clar/resources/template/info/exclude
Normal file
6
tests-clar/resources/template/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]
|
||||
# *~
|
@ -44,7 +44,7 @@ static const unsigned int entry_statuses0[] = {
|
||||
GIT_STATUS_WT_NEW,
|
||||
};
|
||||
|
||||
static const size_t entry_count0 = 16;
|
||||
static const int entry_count0 = 16;
|
||||
|
||||
/* entries for a copy of tests/resources/status with all content
|
||||
* deleted from the working directory
|
||||
@ -86,7 +86,7 @@ static const unsigned int entry_statuses2[] = {
|
||||
GIT_STATUS_WT_DELETED,
|
||||
};
|
||||
|
||||
static const size_t entry_count2 = 15;
|
||||
static const int entry_count2 = 15;
|
||||
|
||||
/* entries for a copy of tests/resources/status with some mods */
|
||||
|
||||
@ -140,7 +140,7 @@ static const unsigned int entry_statuses3[] = {
|
||||
GIT_STATUS_WT_NEW,
|
||||
};
|
||||
|
||||
static const size_t entry_count3 = 22;
|
||||
static const int entry_count3 = 22;
|
||||
|
||||
|
||||
/* entries for a copy of tests/resources/status with some mods
|
||||
@ -199,4 +199,4 @@ static const unsigned int entry_statuses4[] = {
|
||||
GIT_STATUS_WT_NEW,
|
||||
};
|
||||
|
||||
static const size_t entry_count4 = 23;
|
||||
static const int entry_count4 = 23;
|
||||
|
@ -2,12 +2,12 @@
|
||||
#define INCLUDE_cl_status_helpers_h__
|
||||
|
||||
typedef struct {
|
||||
size_t wrong_status_flags_count;
|
||||
size_t wrong_sorted_path;
|
||||
size_t entry_count;
|
||||
int wrong_status_flags_count;
|
||||
int wrong_sorted_path;
|
||||
int entry_count;
|
||||
const unsigned int* expected_statuses;
|
||||
const char** expected_paths;
|
||||
size_t expected_entry_count;
|
||||
int expected_entry_count;
|
||||
} status_entry_counts;
|
||||
|
||||
/* cb_status__normal takes payload of "status_entry_counts *" */
|
||||
|
@ -683,7 +683,7 @@ static unsigned int filemode_statuses[] = {
|
||||
GIT_STATUS_WT_NEW
|
||||
};
|
||||
|
||||
static const size_t filemode_count = 8;
|
||||
static const int filemode_count = 8;
|
||||
|
||||
void test_status_worktree__filemode_changes(void)
|
||||
{
|
||||
@ -697,7 +697,7 @@ void test_status_worktree__filemode_changes(void)
|
||||
if (cl_is_chmod_supported())
|
||||
cl_git_pass(git_config_set_bool(cfg, "core.filemode", true));
|
||||
else {
|
||||
unsigned int i;
|
||||
int i;
|
||||
cl_git_pass(git_config_set_bool(cfg, "core.filemode", false));
|
||||
|
||||
/* won't trust filesystem mode diffs, so these will appear unchanged */
|
||||
|
Loading…
Reference in New Issue
Block a user