Commit aa1c3b58 by Vicent Martí

Merge pull request #1016 from arrbee/fix-checkout-dir-removal

Update checkout with new strategies & behavior
parents 26227474 757b4065
......@@ -2,7 +2,8 @@ default: all
CC = gcc
CFLAGS += -g
CFLAGS += -I../../include -L../../build -L../.. -lgit2 -lpthread
CFLAGS += -I../../include
LDFLAGS += -L../../build -L../.. -lgit2 -lpthread
OBJECTS = \
git2.o \
......@@ -12,4 +13,4 @@ OBJECTS = \
index-pack.o
all: $(OBJECTS)
$(CC) $(CFLAGS) -o git2 $(OBJECTS)
$(CC) $(CFLAGS) $(LDFLAGS) -o git2 $(OBJECTS)
......@@ -22,11 +22,13 @@ static void print_progress(const progress_data *pd)
? (100 * pd->completed_steps) / pd->total_steps
: 0.f;
int kbytes = pd->fetch_progress.received_bytes / 1024;
printf("net %3d%% (%4d kb, %5d/%5d) / idx %3d%% (%5d/%5d) / chk %3d%% (%4lu/%4lu) %s\n",
printf("net %3d%% (%4d kb, %5d/%5d) / idx %3d%% (%5d/%5d) / chk %3d%% (%4" PRIuZ "/%4" PRIuZ ") %s\n",
network_percent, kbytes,
pd->fetch_progress.received_objects, pd->fetch_progress.total_objects,
index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects,
checkout_percent, pd->completed_steps, pd->total_steps,
checkout_percent,
pd->completed_steps, pd->total_steps,
pd->path);
}
......@@ -47,13 +49,15 @@ static void checkout_progress(const char *path, size_t cur, size_t tot, void *pa
int do_clone(git_repository *repo, int argc, char **argv)
{
progress_data pd = {0};
progress_data pd;
git_repository *cloned_repo = NULL;
git_checkout_opts checkout_opts = {0};
git_checkout_opts checkout_opts;
const char *url = argv[1];
const char *path = argv[2];
int error;
(void)repo; // unused
// Validate args
if (argc < 3) {
printf ("USAGE: %s <url> <path>\n", argv[0]);
......@@ -61,8 +65,10 @@ int do_clone(git_repository *repo, int argc, char **argv)
}
// Set up options
checkout_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
memset(&checkout_opts, 0, sizeof(checkout_opts));
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
checkout_opts.progress_cb = checkout_progress;
memset(&pd, 0, sizeof(pd));
checkout_opts.progress_payload = &pd;
// Do the clone
......
......@@ -12,4 +12,13 @@ int fetch(git_repository *repo, int argc, char **argv);
int index_pack(git_repository *repo, int argc, char **argv);
int do_clone(git_repository *repo, int argc, char **argv);
#ifndef PRIuZ
/* Define the printf format specifer to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
# define PRIuZ "Iu"
#else
# define PRIuZ "zu"
#endif
#endif
#endif /* __COMMON_H__ */
......@@ -103,7 +103,7 @@ int fetch(git_repository *repo, int argc, char **argv)
usleep(10000);
if (stats->total_objects > 0)
printf("Received %d/%d objects (%d) in %d bytes\r",
printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
stats->received_objects, stats->total_objects,
stats->indexed_objects, stats->received_bytes);
} while (!data.finished);
......
......@@ -25,20 +25,121 @@ GIT_BEGIN_DECL
* Checkout behavior flags
*
* These flags control what checkout does with files. Pass in a
* combination of these values OR'ed together.
* combination of these values OR'ed together. If you just pass zero
* (i.e. no flags), then you are effectively doing a "dry run" where no
* files will be modified.
*
* Checkout groups the working directory content into 3 classes of files:
* (1) files that don't need a change, and files that do need a change
* that either (2) we are allowed to modifed or (3) we are not. The flags
* you pass in will decide which files we are allowed to modify.
*
* By default, checkout is not allowed to modify any files. Anything
* needing a change would be considered a conflict.
*
* GIT_CHECKOUT_UPDATE_UNMODIFIED means that checkout is allowed to update
* any file where the working directory content matches the HEAD
* (e.g. either the files match or the file is absent in both places).
*
* GIT_CHECKOUT_UPDATE_MISSING means checkout can create a missing file
* that exists in the index and does not exist in the working directory.
* This is usually desirable for initial checkout, etc. Technically, the
* missing file differs from the HEAD, which is why this is separate.
*
* GIT_CHECKOUT_UPDATE_MODIFIED means checkout is allowed to update files
* where the working directory does not match the HEAD so long as the file
* actually exists in the HEAD. This option implies UPDATE_UNMODIFIED.
*
* GIT_CHECKOUT_UPDATE_UNTRACKED means checkout is allowed to update files
* even if there is a working directory version that does not exist in the
* HEAD (i.e. the file was independently created in the workdir). This
* implies UPDATE_UNMODIFIED | UPDATE_MISSING (but *not* UPDATE_MODIFIED).
*
*
* On top of these three basic strategies, there are some modifiers
* options that can be applied:
*
* If any files need update but are disallowed by the strategy, normally
* checkout calls the conflict callback (if given) and then aborts.
* GIT_CHECKOUT_ALLOW_CONFLICTS means it is okay to update the files that
* are allowed by the strategy even if there are conflicts. The conflict
* callbacks are still made, but non-conflicting files will be updated.
*
* Any unmerged entries in the index are automatically considered conflicts.
* If you want to proceed anyhow and just skip unmerged entries, you can use
* GIT_CHECKOUT_SKIP_UNMERGED which is less dangerous than just allowing all
* conflicts. Alternatively, use GIT_CHECKOUT_USE_OURS to proceed and
* checkout the stage 2 ("ours") version. GIT_CHECKOUT_USE_THEIRS means to
* proceed and use the stage 3 ("theirs") version.
*
* GIT_CHECKOUT_UPDATE_ONLY means that update is not allowed to create new
* files or delete old ones, only update existing content. With this
* flag, files that needs to be created or deleted are not conflicts -
* they are just skipped. This also skips typechanges to existing files
* (because the old would have to be removed).
*
* GIT_CHECKOUT_REMOVE_UNTRACKED means that files in the working directory
* that are untracked (and not ignored) will be removed altogether. These
* untracked files (that do not shadow index entries) are not considered
* conflicts and would normally be ignored.
*
*
* Checkout is "semi-atomic" as in it will go through the work to be done
* before making any changes and if may decide to abort if there are
* conflicts, or you can use the conflict callback to explicitly abort the
* action before any updates are made. Despite this, if a second process
* is modifying the filesystem while checkout is running, it can't
* guarantee that the choices is makes while initially examining the
* filesystem are still going to be correct as it applies them.
*/
typedef enum {
/** Checkout does not update any files in the working directory. */
GIT_CHECKOUT_DEFAULT = (1 << 0),
GIT_CHECKOUT_DEFAULT = 0, /** default is a dry run, no actual updates */
/** When a file exists and is modified, replace it with new version. */
GIT_CHECKOUT_OVERWRITE_MODIFIED = (1 << 1),
/** Allow update of entries where working dir matches HEAD. */
GIT_CHECKOUT_UPDATE_UNMODIFIED = (1u << 0),
/** When a file does not exist in the working directory, create it. */
GIT_CHECKOUT_CREATE_MISSING = (1 << 2),
/** Allow update of entries where working dir does not have file. */
GIT_CHECKOUT_UPDATE_MISSING = (1u << 1),
/** Allow safe updates that cannot overwrite uncommited data */
GIT_CHECKOUT_SAFE =
(GIT_CHECKOUT_UPDATE_UNMODIFIED | GIT_CHECKOUT_UPDATE_MISSING),
/** Allow update of entries in working dir that are modified from HEAD. */
GIT_CHECKOUT_UPDATE_MODIFIED = (1u << 2),
/** Update existing untracked files that are now present in the index. */
GIT_CHECKOUT_UPDATE_UNTRACKED = (1u << 3),
/** Allow all updates to force working directory to look like index */
GIT_CHECKOUT_FORCE =
(GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_MODIFIED | GIT_CHECKOUT_UPDATE_UNTRACKED),
/** Allow checkout to make updates even if conflicts are found */
GIT_CHECKOUT_ALLOW_CONFLICTS = (1u << 4),
/** Remove untracked files not in index (that are not ignored) */
GIT_CHECKOUT_REMOVE_UNTRACKED = (1u << 5),
/** Only update existing files, don't create new ones */
GIT_CHECKOUT_UPDATE_ONLY = (1u << 6),
/**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/
/** Allow checkout to skip unmerged files (NOT IMPLEMENTED) */
GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
/** For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED) */
GIT_CHECKOUT_USE_OURS = (1u << 11),
/** For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED) */
GIT_CHECKOUT_USE_THEIRS = (1u << 12),
/** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
/** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1u << 17),
/** If an untracked file in found in the working dir, delete it. */
GIT_CHECKOUT_REMOVE_UNTRACKED = (1 << 3),
} git_checkout_strategy_t;
/**
......@@ -47,36 +148,36 @@ typedef enum {
* Use zeros to indicate default settings.
*/
typedef struct git_checkout_opts {
unsigned int checkout_strategy; /** default: GIT_CHECKOUT_DEFAULT */
unsigned int checkout_strategy; /** default will be a dry run */
int disable_filters; /** don't apply filters like CRLF conversion */
int dir_mode; /** default is 0755 */
int file_mode; /** default is 0644 or 0755 as dictated by blob */
int file_open_flags; /** default is O_CREAT | O_TRUNC | O_WRONLY */
/** Optional callback to notify the consumer of files that
* haven't be checked out because a modified version of them
* exist in the working directory.
*
* When provided, this callback will be invoked when the flag
* GIT_CHECKOUT_OVERWRITE_MODIFIED isn't part of the checkout strategy.
/** Optional callback made on files where the index differs from the
* working directory but the rules do not allow update. Return a
* non-zero value to abort the checkout. All such callbacks will be
* made before any changes are made to the working directory.
*/
int (* skipped_notify_cb)(
const char *skipped_file,
const git_oid *blob_oid,
int file_mode,
int (*conflict_cb)(
const char *conflicting_path,
const git_oid *index_oid,
unsigned int index_mode,
unsigned int wd_mode,
void *payload);
void *notify_payload;
void *conflict_payload;
/* Optional callback to notify the consumer of checkout progress. */
void (* progress_cb)(
void (*progress_cb)(
const char *path,
size_t completed_steps,
size_t total_steps,
void *payload);
void *progress_payload;
/** When not NULL, array of fnmatch patterns specifying
* which paths should be taken into account
/** When not zeroed out, array of fnmatch patterns specifying which
* paths should be taken into account, otherwise all files.
*/
git_strarray paths;
} git_checkout_opts;
......
......@@ -60,6 +60,7 @@ typedef enum {
GITERR_SUBMODULE,
GITERR_THREAD,
GITERR_STASH,
GITERR_CHECKOUT,
} git_error_t;
/**
......
......@@ -7,6 +7,7 @@
#ifndef INCLUDE_attr_file_h__
#define INCLUDE_attr_file_h__
#include "git2/oid.h"
#include "git2/attr.h"
#include "vector.h"
#include "pool.h"
......
......@@ -344,7 +344,7 @@ static int clone_internal(
fetch_progress_cb, fetch_progress_payload)) < 0) {
/* Failed to fetch; clean up */
git_repository_free(repo);
git_futils_rmdir_r(path, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES);
} else {
*out = repo;
retcode = 0;
......
......@@ -41,6 +41,11 @@ struct git_diff_list {
git_iterator_type_t old_src;
git_iterator_type_t new_src;
uint32_t diffcaps;
int (*strcomp)(const char *, const char *);
int (*strncomp)(const char *, const char *, size_t);
int (*pfxcomp)(const char *str, const char *pfx);
int (*entrycomp)(const void *a, const void *b);
};
extern void git_diff__cleanup_modes(
......@@ -53,5 +58,8 @@ extern int git_diff_delta__cmp(const void *a, const void *b);
extern bool git_diff_delta__should_skip(
const git_diff_options *opts, const git_diff_delta *delta);
extern int git_diff__oid_for_file(
git_repository *, const char *, uint16_t, git_off_t, git_oid *);
#endif
......@@ -120,7 +120,7 @@ static int diff_delta_is_binary_by_attr(
return -1;
mirror_new = (delta->new_file.path == delta->old_file.path ||
strcmp(delta->new_file.path, delta->old_file.path) == 0);
ctxt->diff->strcomp(delta->new_file.path, delta->old_file.path) == 0);
if (mirror_new)
delta->new_file.flags |= (delta->old_file.flags & KNOWN_BINARY_FLAGS);
else
......@@ -1002,7 +1002,7 @@ static int print_compact(
git_buf_clear(pi->buf);
if (delta->old_file.path != delta->new_file.path &&
strcmp(delta->old_file.path,delta->new_file.path) != 0)
pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0)
git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
else if (delta->old_file.mode != delta->new_file.mode &&
......@@ -1573,3 +1573,58 @@ int git_diff_patch_to_str(
return error;
}
int git_diff__paired_foreach(
git_diff_list *idx2head,
git_diff_list *wd2idx,
int (*cb)(void *cbref, git_diff_delta *i2h, git_diff_delta *w2i),
void *cbref)
{
int cmp;
git_diff_delta *i2h, *w2i;
size_t i, j, i_max, j_max;
bool icase = false;
i_max = idx2head ? idx2head->deltas.length : 0;
j_max = wd2idx ? wd2idx->deltas.length : 0;
if (idx2head && wd2idx &&
(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) ||
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)))
{
/* Then use the ignore-case sorter... */
icase = true;
/* and assert that both are ignore-case sorted. If this function
* ever needs to support merge joining result sets that are not sorted
* by the same function, then it will need to be extended to do a spool
* and sort on one of the results before merge joining */
assert(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) &&
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE));
}
for (i = 0, j = 0; i < i_max || j < j_max; ) {
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
cmp = !w2i ? -1 : !i2h ? 1 :
STRCMP_CASESELECT(icase, i2h->old_file.path, w2i->old_file.path);
if (cmp < 0) {
if (cb(cbref, i2h, NULL))
return GIT_EUSER;
i++;
} else if (cmp > 0) {
if (cb(cbref, NULL, w2i))
return GIT_EUSER;
j++;
} else {
if (cb(cbref, i2h, w2i))
return GIT_EUSER;
i++; j++;
}
}
return 0;
}
......@@ -83,4 +83,10 @@ typedef struct {
uint32_t diffed : 1;
} diff_delta_context;
extern int git_diff__paired_foreach(
git_diff_list *idx2head,
git_diff_list *wd2idx,
int (*cb)(void *cbref, git_diff_delta *i2h, git_diff_delta *w2i),
void *cbref);
#endif
......@@ -14,7 +14,8 @@
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
return git_futils_mkdir(
file_path, NULL, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST);
file_path, NULL, mode,
GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
}
int git_futils_mktmp(git_buf *path_out, const char *filename)
......@@ -250,6 +251,7 @@ int git_futils_mkdir(
mode_t mode,
uint32_t flags)
{
int error = -1;
git_buf make_path = GIT_BUF_INIT;
ssize_t root = 0;
char lastch, *tail;
......@@ -297,13 +299,29 @@ int git_futils_mkdir(
*tail = '\0';
/* make directory */
if (p_mkdir(make_path.ptr, mode) < 0 &&
(errno != EEXIST || (flags & GIT_MKDIR_EXCL) != 0))
{
if (p_mkdir(make_path.ptr, mode) < 0) {
if (errno == EEXIST) {
if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0) {
if (!git_path_isdir(make_path.ptr)) {
giterr_set(
GITERR_OS, "Existing path is not a directory '%s'",
make_path.ptr);
error = GIT_ENOTFOUND;
goto fail;
}
}
if ((flags & GIT_MKDIR_EXCL) != 0) {
giterr_set(GITERR_OS, "Directory already exists '%s'",
make_path.ptr);
error = GIT_EEXISTS;
goto fail;
}
} else {
giterr_set(GITERR_OS, "Failed to make directory '%s'",
make_path.ptr);
goto fail;
}
}
/* chmod if requested */
if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
......@@ -324,7 +342,7 @@ int git_futils_mkdir(
fail:
git_buf_free(&make_path);
return -1;
return error;
}
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
......@@ -332,57 +350,145 @@ 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)
typedef struct {
const char *base;
uint32_t flags;
int error;
} futils__rmdir_data;
static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
{
git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
if (filemsg)
giterr_set(GITERR_OS, "Could not remove directory. File '%s' %s",
path, filemsg);
else
giterr_set(GITERR_OS, "Could not remove directory '%s'", path);
if (git_path_isdir(path->ptr) == true) {
if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
return -1;
}
if (p_rmdir(path->ptr) < 0) {
if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
return 0;
static int futils__rm_first_parent(git_buf *path, const char *ceiling)
{
int error = GIT_ENOTFOUND;
struct stat st;
giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
return -1;
while (error == GIT_ENOTFOUND) {
git_buf_rtruncate_at_char(path, '/');
if (!path->size || git__prefixcmp(path->ptr, ceiling) != 0)
error = 0;
else if (p_lstat(path->ptr, &st) == 0) {
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
error = p_unlink(path->ptr);
else if (!S_ISDIR(st.st_mode))
error = -1; /* fail to remove non-regular file */
} else if (errno != ENOTDIR)
error = -1;
}
return 0;
if (error)
futils__error_cannot_rmdir(path->ptr, "cannot remove parent");
return error;
}
static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
{
struct stat st;
futils__rmdir_data *data = opaque;
if ((data->error = p_lstat(path->ptr, &st)) < 0) {
if (errno == ENOENT)
data->error = 0;
else if (errno == ENOTDIR) {
/* asked to remove a/b/c/d/e and a/b is a normal file */
if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
data->error = futils__rm_first_parent(path, data->base);
else
futils__error_cannot_rmdir(
path->ptr, "parent is not directory");
}
else
futils__error_cannot_rmdir(path->ptr, "cannot access");
}
if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {
if (p_unlink(path->ptr) < 0) {
giterr_set(GITERR_OS, "Could not remove directory. File '%s' cannot be removed", path->ptr);
return -1;
else if (S_ISDIR(st.st_mode)) {
int error = git_path_direach(path, futils__rmdir_recurs_foreach, data);
if (error < 0)
return (error == GIT_EUSER) ? data->error : error;
data->error = p_rmdir(path->ptr);
if (data->error < 0) {
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
(errno == ENOTEMPTY || errno == EEXIST))
data->error = 0;
else
futils__error_cannot_rmdir(path->ptr, NULL);
}
}
return 0;
else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
data->error = p_unlink(path->ptr);
if (data->error < 0)
futils__error_cannot_rmdir(path->ptr, "cannot be removed");
}
if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
return -1;
else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
data->error = futils__error_cannot_rmdir(path->ptr, "still present");
return data->error;
}
static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
{
int error = p_rmdir(path->ptr);
GIT_UNUSED(opaque);
if (error) {
int en = errno;
if (en == ENOENT || en == ENOTDIR) {
giterr_clear();
error = 0;
} else if (en == ENOTEMPTY || en == EEXIST) {
giterr_clear();
error = GIT_ITEROVER;
} else {
futils__error_cannot_rmdir(path->ptr, NULL);
}
}
return 0;
return error;
}
int git_futils_rmdir_r(
const char *path, const char *base, git_directory_removal_type removal_type)
const char *path, const char *base, uint32_t flags)
{
int error;
git_buf fullpath = GIT_BUF_INIT;
assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
|| removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
|| removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
futils__rmdir_data data;
/* build path and find "root" where we should start calling mkdir */
if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
return -1;
error = _rmdir_recurs_foreach(&removal_type, &fullpath);
data.base = base ? base : "";
data.flags = flags;
data.error = 0;
error = futils__rmdir_recurs_foreach(&data, &fullpath);
/* remove now-empty parents if requested */
if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) {
error = git_path_walk_up(
&fullpath, base, futils__rmdir_empty_parent, &data);
if (error == GIT_ITEROVER)
error = 0;
}
git_buf_free(&fullpath);
......
......@@ -65,6 +65,7 @@ extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t m
* * 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
* * GIT_MKDIR_VERIFY_DIR says confirm final item is a dir, not just EEXIST
*
* Note that the chmod options will be executed even if the directory already
* exists, unless GIT_MKDIR_EXCL is given.
......@@ -74,7 +75,8 @@ typedef enum {
GIT_MKDIR_PATH = 2,
GIT_MKDIR_CHMOD = 4,
GIT_MKDIR_CHMOD_PATH = 8,
GIT_MKDIR_SKIP_LAST = 16
GIT_MKDIR_SKIP_LAST = 16,
GIT_MKDIR_VERIFY_DIR = 32,
} git_futils_mkdir_flags;
/**
......@@ -98,27 +100,40 @@ extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uin
*/
extern int git_futils_mkpath2file(const char *path, const mode_t mode);
/**
* Flags to pass to `git_futils_rmdir_r`.
*
* * GIT_RMDIR_EMPTY_HIERARCHY - the default; remove hierarchy of empty
* dirs and generate error if any files are found.
* * GIT_RMDIR_REMOVE_FILES - attempt to remove files in the hierarchy.
* * GIT_RMDIR_SKIP_NONEMPTY - skip non-empty directories with no error.
* * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base
* if removing this item leaves them empty
* * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
*
* The old values translate into the new as follows:
*
* * GIT_DIRREMOVAL_EMPTY_HIERARCHY == GIT_RMDIR_EMPTY_HIERARCHY
* * GIT_DIRREMOVAL_FILES_AND_DIRS ~= GIT_RMDIR_REMOVE_FILES
* * GIT_DIRREMOVAL_ONLY_EMPTY_DIRS == GIT_RMDIR_SKIP_NONEMPTY
*/
typedef enum {
GIT_DIRREMOVAL_EMPTY_HIERARCHY = 0,
GIT_DIRREMOVAL_FILES_AND_DIRS = 1,
GIT_DIRREMOVAL_ONLY_EMPTY_DIRS = 2,
} git_directory_removal_type;
GIT_RMDIR_EMPTY_HIERARCHY = 0,
GIT_RMDIR_REMOVE_FILES = (1 << 0),
GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
} git_futils_rmdir_flags;
/**
* Remove path and any files and directories beneath it.
*
* @param path Path to to top level directory to process.
* @param base Root for relative path.
* @param removal_type GIT_DIRREMOVAL_EMPTY_HIERARCHY to remove a hierarchy
* of empty directories (will fail if any file is found),
* GIT_DIRREMOVAL_FILES_AND_DIRS to remove a hierarchy of
* files and folders,
* GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove empty
* directories (no failure on file encounter).
*
* @param flags Combination of git_futils_rmdir_flags values
* @return 0 on success; -1 on error.
*/
extern int git_futils_rmdir_r(const char *path, const char *base, git_directory_removal_type removal_type);
extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
/**
* Create and open a temporary file with a `_git2_` suffix.
......
......@@ -13,6 +13,8 @@
#include "tree.h"
#include "tree-cache.h"
#include "hash.h"
#include "iterator.h"
#include "pathspec.h"
#include "git2/odb.h"
#include "git2/oid.h"
#include "git2/blob.h"
......@@ -403,7 +405,7 @@ int git_index_read(git_index *index)
{
int error = 0, updated;
git_buf buffer = GIT_BUF_INIT;
git_futils_filestamp stamp;
git_futils_filestamp stamp = {0};
if (!index->index_file_path)
return create_index_error(-1,
......@@ -513,7 +515,7 @@ git_index_entry *git_index_get_bypath(git_index *index, const char *path, int st
return git_index_get_byindex(index, pos);
}
void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)
void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
{
entry->ctime.seconds = (git_time_t)st->st_ctime;
entry->mtime.seconds = (git_time_t)st->st_mtime;
......@@ -527,6 +529,22 @@ void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)
entry->file_size = st->st_size;
}
int git_index_entry__cmp(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcmp(entry_a->path, entry_b->path);
}
int git_index_entry__cmp_icase(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcasecmp(entry_a->path, entry_b->path);
}
static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
{
git_index_entry *entry = NULL;
......@@ -568,7 +586,7 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
entry = git__calloc(1, sizeof(git_index_entry));
GITERR_CHECK_ALLOC(entry);
git_index__init_entry_from_stat(&st, entry);
git_index_entry__init_from_stat(entry, &st);
entry->oid = oid;
entry->path = git__strdup(rel_path);
......@@ -1589,3 +1607,54 @@ git_repository *git_index_owner(const git_index *index)
{
return INDEX_OWNER(index);
}
int git_index_read_tree_match(
git_index *index, git_tree *tree, git_strarray *strspec)
{
#if 0
git_iterator *iter = NULL;
const git_index_entry *entry;
char *pfx = NULL;
git_vector pathspec = GIT_VECTOR_INIT;
git_pool pathpool = GIT_POOL_INIT_STRINGPOOL;
#endif
if (!git_pathspec_is_interesting(strspec))
return git_index_read_tree(index, tree);
return git_index_read_tree(index, tree);
#if 0
/* The following loads the matches into the index, but doesn't
* erase obsoleted entries (e.g. you load a blob at "a/b" which
* should obsolete a blob at "a/b/c/d" since b is no longer a tree)
*/
if (git_pathspec_init(&pathspec, strspec, &pathpool) < 0)
return -1;
pfx = git_pathspec_prefix(strspec);
if ((error = git_iterator_for_tree_range(
&iter, INDEX_OWNER(index), tree, pfx, pfx)) < 0 ||
(error = git_iterator_current(iter, &entry)) < 0)
goto cleanup;
while (entry != NULL) {
if (git_pathspec_match_path(&pathspec, entry->path, false, false) &&
(error = git_index_add(index, entry)) < 0)
goto cleanup;
if ((error = git_iterator_advance(iter, &entry)) < 0)
goto cleanup;
}
cleanup:
git_iterator_free(iter);
git_pathspec_free(&pathspec);
git_pool_clear(&pathpool);
git__free(pfx);
return error;
#endif
}
......@@ -41,8 +41,14 @@ struct git_index {
git_vector_cmp reuc_search;
};
extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st);
extern unsigned int git_index__prefix_position(git_index *index, const char *path);
extern int git_index_entry__cmp(const void *a, const void *b);
extern int git_index_entry__cmp_icase(const void *a, const void *b);
extern int git_index_read_tree_match(
git_index *index, git_tree *tree, git_strarray *strspec);
#endif
......@@ -641,26 +641,23 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
wi->entry.path = ps->path;
/* skip over .git entry */
/* skip over .git entries */
if (STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT "/") == 0 ||
STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT) == 0)
return workdir_iterator__advance((git_iterator *)wi, NULL);
/* if there is an error processing the entry, treat as ignored */
wi->is_ignored = 1;
wi->is_ignored = -1;
git_index__init_entry_from_stat(&ps->st, &wi->entry);
git_index_entry__init_from_stat(&wi->entry, &ps->st);
/* need different mode here to keep directories during iteration */
wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
/* if this is a file type we don't handle, treat as ignored */
if (wi->entry.mode == 0)
if (wi->entry.mode == 0) {
wi->is_ignored = 1;
return 0;
/* okay, we are far enough along to look up real ignore rule */
if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
return 0; /* if error, ignore it and ignore file */
}
/* detect submodules */
if (S_ISDIR(wi->entry.mode)) {
......@@ -908,8 +905,18 @@ notfound:
int git_iterator_current_is_ignored(git_iterator *iter)
{
return (iter->type != GIT_ITERATOR_WORKDIR) ? 0 :
((workdir_iterator *)iter)->is_ignored;
workdir_iterator *wi = (workdir_iterator *)iter;
if (iter->type != GIT_ITERATOR_WORKDIR)
return 0;
if (wi->is_ignored != -1)
return wi->is_ignored;
if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
wi->is_ignored = 1;
return wi->is_ignored;
}
int git_iterator_advance_into_directory(
......
......@@ -73,16 +73,16 @@ static int packbuilder_config(git_packbuilder *pb)
{
git_config *config;
int ret;
int64_t val;
if (git_repository_config__weakptr(&config, pb->repo) < 0)
return -1;
#define config_get(key, dst, default) \
ret = git_config_get_int64((int64_t *)&dst, config, key); \
if (ret == GIT_ENOTFOUND) \
dst = default; \
else if (ret < 0) \
return -1;
#define config_get(KEY,DST,DFLT) do { \
ret = git_config_get_int64(&val, config, KEY); \
if (!ret) (DST) = val; \
else if (ret == GIT_ENOTFOUND) (DST) = (DFLT); \
else if (ret < 0) return -1; } while (0)
config_get("pack.deltaCacheSize", pb->max_delta_cache_size,
GIT_PACK_DELTA_CACHE_SIZE);
......
......@@ -382,9 +382,10 @@ int git_path_walk_up(
iter.asize = path->asize;
while (scan >= stop) {
if ((error = cb(data, &iter)) < 0)
break;
error = cb(data, &iter);
iter.ptr[scan] = oldc;
if (error < 0)
break;
scan = git_buf_rfind_next(&iter, '/');
if (scan >= 0) {
scan++;
......
/*
* Copyright (C) 2009-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.
*/
#include "pathspec.h"
#include "attr_file.h"
/* what is the common non-wildcard prefix for all items in the pathspec */
char *git_pathspec_prefix(const git_strarray *pathspec)
{
git_buf prefix = GIT_BUF_INIT;
const char *scan;
if (!pathspec || !pathspec->count ||
git_buf_common_prefix(&prefix, pathspec) < 0)
return NULL;
/* diff prefix will only be leading non-wildcards */
for (scan = prefix.ptr; *scan; ++scan) {
if (git__iswildcard(*scan) &&
(scan == prefix.ptr || (*(scan - 1) != '\\')))
break;
}
git_buf_truncate(&prefix, scan - prefix.ptr);
if (prefix.size <= 0) {
git_buf_free(&prefix);
return NULL;
}
git_buf_unescape(&prefix);
return git_buf_detach(&prefix);
}
/* is there anything in the spec that needs to be filtered on */
bool git_pathspec_is_interesting(const git_strarray *pathspec)
{
const char *str;
if (pathspec == NULL || pathspec->count == 0)
return false;
if (pathspec->count > 1)
return true;
str = pathspec->strings[0];
if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))
return false;
return true;
}
/* build a vector of fnmatch patterns to evaluate efficiently */
int git_pathspec_init(
git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
{
size_t i;
memset(vspec, 0, sizeof(*vspec));
if (!git_pathspec_is_interesting(strspec))
return 0;
if (git_vector_init(vspec, strspec->count, NULL) < 0)
return -1;
for (i = 0; i < strspec->count; ++i) {
int ret;
const char *pattern = strspec->strings[i];
git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
if (!match)
return -1;
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
if (ret == GIT_ENOTFOUND) {
git__free(match);
continue;
} else if (ret < 0)
return ret;
if (git_vector_insert(vspec, match) < 0)
return -1;
}
return 0;
}
/* free data from the pathspec vector */
void git_pathspec_free(git_vector *vspec)
{
git_attr_fnmatch *match;
unsigned int i;
git_vector_foreach(vspec, i, match) {
git__free(match);
vspec->contents[i] = NULL;
}
git_vector_free(vspec);
}
/* match a path against the vectorized pathspec */
bool git_pathspec_match_path(
git_vector *vspec, const char *path, bool disable_fnmatch, bool casefold)
{
unsigned int i;
git_attr_fnmatch *match;
int fnmatch_flags = 0;
int (*use_strcmp)(const char *, const char *);
int (*use_strncmp)(const char *, const char *, size_t);
if (!vspec || !vspec->length)
return true;
if (disable_fnmatch)
fnmatch_flags = -1;
else if (casefold)
fnmatch_flags = FNM_CASEFOLD;
if (casefold) {
use_strcmp = strcasecmp;
use_strncmp = strncasecmp;
} else {
use_strcmp = strcmp;
use_strncmp = strncmp;
}
git_vector_foreach(vspec, i, match) {
int result = use_strcmp(match->pattern, path) ? FNM_NOMATCH : 0;
if (fnmatch_flags >= 0 && result == FNM_NOMATCH)
result = p_fnmatch(match->pattern, path, fnmatch_flags);
/* if we didn't match, look for exact dirname prefix match */
if (result == FNM_NOMATCH &&
(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
use_strncmp(path, match->pattern, match->length) == 0 &&
path[match->length] == '/')
result = 0;
if (result == 0)
return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
}
return false;
}
/*
* Copyright (C) 2011-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_pathspec_h__
#define INCLUDE_pathspec_h__
#include "common.h"
#include "buffer.h"
#include "vector.h"
#include "pool.h"
/* what is the common non-wildcard prefix for all items in the pathspec */
extern char *git_pathspec_prefix(const git_strarray *pathspec);
/* is there anything in the spec that needs to be filtered on */
extern bool git_pathspec_is_interesting(const git_strarray *pathspec);
/* build a vector of fnmatch patterns to evaluate efficiently */
extern int git_pathspec_init(
git_vector *vspec, const git_strarray *strspec, git_pool *strpool);
/* free data from the pathspec vector */
extern void git_pathspec_free(git_vector *vspec);
/* match a path against the vectorized pathspec */
extern bool git_pathspec_match_path(
git_vector *vspec, const char *path, bool disable_fnmatch, bool casefold);
#endif
......@@ -378,7 +378,7 @@ int git_reflog_rename(git_reference *ref, const char *new_name)
goto cleanup;
if (git_path_isdir(git_buf_cstr(&new_path)) &&
(git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0))
(git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0))
goto cleanup;
if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0)
......
......@@ -274,18 +274,15 @@ static int loose_write(git_reference *ref)
git_buf ref_path = GIT_BUF_INIT;
struct stat st;
if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
return -1;
/* Remove a possibly existing empty directory hierarchy
* which name would collide with the reference name
*/
if (git_path_isdir(git_buf_cstr(&ref_path)) &&
git_futils_rmdir_r(git_buf_cstr(&ref_path), NULL,
GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0) {
git_buf_free(&ref_path);
if (git_futils_rmdir_r(ref->name, ref->owner->path_repository,
GIT_RMDIR_SKIP_NONEMPTY) < 0)
return -1;
if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
return -1;
}
if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
git_buf_free(&ref_path);
......
......@@ -137,10 +137,7 @@ int git_reset(
}
memset(&opts, 0, sizeof(opts));
opts.checkout_strategy =
GIT_CHECKOUT_CREATE_MISSING
| GIT_CHECKOUT_OVERWRITE_MODIFIED
| GIT_CHECKOUT_REMOVE_UNTRACKED;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
if (git_checkout_index(repo, &opts) < 0) {
giterr_set(GITERR_INDEX, "%s - Failed to checkout the index.", ERROR_MSG);
......
......@@ -502,7 +502,7 @@ static int reset_index_and_workdir(
memset(&opts, 0, sizeof(git_checkout_opts));
opts.checkout_strategy =
GIT_CHECKOUT_CREATE_MISSING | GIT_CHECKOUT_OVERWRITE_MODIFIED;
GIT_CHECKOUT_UPDATE_MODIFIED | GIT_CHECKOUT_UPDATE_UNTRACKED;
if (remove_untracked)
opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
......
......@@ -17,6 +17,7 @@
#include "git2/diff.h"
#include "diff.h"
#include "diff_output.h"
static unsigned int index_delta2status(git_delta_t index_status)
{
......@@ -76,21 +77,43 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
return st;
}
typedef struct {
int (*cb)(const char *, unsigned int, void *);
void *cbdata;
} status_user_callback;
static int status_invoke_cb(
void *cbref, git_diff_delta *i2h, git_diff_delta *w2i)
{
status_user_callback *usercb = cbref;
const char *path = NULL;
unsigned int status = 0;
if (w2i) {
path = w2i->old_file.path;
status |= workdir_delta2status(w2i->status);
}
if (i2h) {
path = i2h->old_file.path;
status |= index_delta2status(i2h->status);
}
return usercb->cb(path, status, usercb->cbdata);
}
int git_status_foreach_ext(
git_repository *repo,
const git_status_options *opts,
int (*cb)(const char *, unsigned int, void *),
void *cbdata)
{
int err = 0, cmp;
int err = 0;
git_diff_options diffopt;
git_diff_list *idx2head = NULL, *wd2idx = NULL;
git_tree *head = NULL;
git_status_show_t show =
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
git_diff_delta *i2h, *w2i;
size_t i, j, i_max, j_max;
bool ignore_case = false;
status_user_callback usercb;
assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
......@@ -126,55 +149,19 @@ int git_status_foreach_ext(
(err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
goto cleanup;
usercb.cb = cb;
usercb.cbdata = cbdata;
if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
for (i = 0; !err && i < idx2head->deltas.length; i++) {
i2h = GIT_VECTOR_GET(&idx2head->deltas, i);
if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
err = GIT_EUSER;
}
if ((err = git_diff__paired_foreach(
idx2head, NULL, status_invoke_cb, &usercb)) < 0)
goto cleanup;
git_diff_list_free(idx2head);
idx2head = NULL;
}
i_max = idx2head ? idx2head->deltas.length : 0;
j_max = wd2idx ? wd2idx->deltas.length : 0;
if (idx2head && wd2idx &&
(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) ||
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)))
{
/* Then use the ignore-case sorter... */
ignore_case = true;
/* and assert that both are ignore-case sorted. If this function
* ever needs to support merge joining result sets that are not sorted
* by the same function, then it will need to be extended to do a spool
* and sort on one of the results before merge joining */
assert(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) &&
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE));
}
for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
cmp = !w2i ? -1 : !i2h ? 1 : STRCMP_CASESELECT(ignore_case, i2h->old_file.path, w2i->old_file.path);
if (cmp < 0) {
if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
err = GIT_EUSER;
i++;
} else if (cmp > 0) {
if (cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata))
err = GIT_EUSER;
j++;
} else {
if (cb(i2h->old_file.path, index_delta2status(i2h->status) |
workdir_delta2status(w2i->status), cbdata))
err = GIT_EUSER;
i++; j++;
}
}
err = git_diff__paired_foreach(idx2head, wd2idx, status_invoke_cb, &usercb);
cleanup:
git_tree_free(head);
......
......@@ -371,7 +371,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
memset(&entry, 0, sizeof(entry));
entry.path = sm->path;
git_index__init_entry_from_stat(&st, &entry);
git_index_entry__init_from_stat(&entry, &st);
/* calling git_submodule_open will have set sm->wd_oid if possible */
if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
......
......@@ -628,6 +628,8 @@ int git_smart_subtransport_http(git_smart_subtransport **out,
{
http_subtransport *t;
(void)flags;
if (!out)
return -1;
......
......@@ -81,6 +81,11 @@ extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__prefixcmp_icase(const char *str, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix);
GIT_INLINE(int) git__signum(int val)
{
return ((val > 0) - (val < 0));
}
extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);
extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base);
......
......@@ -26,7 +26,7 @@ void test_checkout_index__initialize(void)
git_tree *tree;
memset(&g_opts, 0, sizeof(g_opts));
g_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
g_repo = cl_git_sandbox_init("testrepo");
......@@ -78,7 +78,6 @@ void test_checkout_index__can_create_missing_files(void)
cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
g_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/README", "hey there\n");
......@@ -94,7 +93,7 @@ void test_checkout_index__can_remove_untracked_files(void)
cl_assert_equal_i(true, git_path_isdir("./testrepo/dir/subdir/subsubdir"));
g_opts.checkout_strategy = GIT_CHECKOUT_REMOVE_UNTRACKED;
g_opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
cl_assert_equal_i(false, git_path_isdir("./testrepo/dir"));
......@@ -203,7 +202,11 @@ void test_checkout_index__donot_overwrite_modified_file_by_default(void)
{
cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
g_opts.checkout_strategy = 0;
/* set this up to not return an error code on conflicts, but it
* still will not have permission to overwrite anything...
*/
g_opts.checkout_strategy = GIT_CHECKOUT_ALLOW_CONFLICTS;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/new.txt", "This isn't what's stored!");
......@@ -213,7 +216,8 @@ void test_checkout_index__can_overwrite_modified_file(void)
{
cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
g_opts.checkout_strategy = GIT_CHECKOUT_OVERWRITE_MODIFIED;
g_opts.checkout_strategy |= GIT_CHECKOUT_UPDATE_MODIFIED;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/new.txt", "my new file\n");
......@@ -282,28 +286,30 @@ void test_checkout_index__options_open_flags(void)
g_opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
g_opts.checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED;
g_opts.checkout_strategy |= GIT_CHECKOUT_UPDATE_MODIFIED;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
}
struct notify_data {
struct conflict_data {
const char *file;
const char *sha;
};
static int notify_cb(
const char *skipped_file,
static int conflict_cb(
const char *conflict_file,
const git_oid *blob_oid,
int file_mode,
unsigned int index_mode,
unsigned int wd_mode,
void *payload)
{
struct notify_data *expectations = (struct notify_data *)payload;
struct conflict_data *expectations = (struct conflict_data *)payload;
GIT_UNUSED(file_mode);
GIT_UNUSED(index_mode);
GIT_UNUSED(wd_mode);
cl_assert_equal_s(expectations->file, skipped_file);
cl_assert_equal_s(expectations->file, conflict_file);
cl_assert_equal_i(0, git_oid_streq(blob_oid, expectations->sha));
return 0;
......@@ -311,7 +317,7 @@ static int notify_cb(
void test_checkout_index__can_notify_of_skipped_files(void)
{
struct notify_data data;
struct conflict_data data;
cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
......@@ -324,22 +330,24 @@ void test_checkout_index__can_notify_of_skipped_files(void)
data.file = "new.txt";
data.sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd";
g_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
g_opts.skipped_notify_cb = notify_cb;
g_opts.notify_payload = &data;
g_opts.checkout_strategy |= GIT_CHECKOUT_ALLOW_CONFLICTS;
g_opts.conflict_cb = conflict_cb;
g_opts.conflict_payload = &data;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
}
static int dont_notify_cb(
const char *skipped_file,
static int dont_conflict_cb(
const char *conflict_file,
const git_oid *blob_oid,
int file_mode,
unsigned int index_mode,
unsigned int wd_mode,
void *payload)
{
GIT_UNUSED(skipped_file);
GIT_UNUSED(conflict_file);
GIT_UNUSED(blob_oid);
GIT_UNUSED(file_mode);
GIT_UNUSED(index_mode);
GIT_UNUSED(wd_mode);
GIT_UNUSED(payload);
cl_assert(false);
......@@ -354,9 +362,9 @@ void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");
g_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
g_opts.skipped_notify_cb = dont_notify_cb;
g_opts.notify_payload = NULL;
g_opts.checkout_strategy |= GIT_CHECKOUT_ALLOW_CONFLICTS;
g_opts.conflict_cb = dont_conflict_cb;
g_opts.conflict_payload = NULL;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
}
......@@ -377,3 +385,44 @@ void test_checkout_index__calls_progress_callback(void)
cl_git_pass(git_checkout_index(g_repo, &g_opts));
cl_assert_equal_i(was_called, true);
}
void test_checkout_index__can_overcome_name_clashes(void)
{
git_index *index;
cl_git_pass(git_repository_index(&index, g_repo));
git_index_clear(index);
cl_git_mkfile("./testrepo/path0", "content\r\n");
cl_git_pass(p_mkdir("./testrepo/path1", 0777));
cl_git_mkfile("./testrepo/path1/file1", "content\r\n");
cl_git_pass(git_index_add_from_workdir(index, "path0"));
cl_git_pass(git_index_add_from_workdir(index, "path1/file1"));
cl_git_pass(p_unlink("./testrepo/path0"));
cl_git_pass(git_futils_rmdir_r(
"./testrepo/path1", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_mkfile("./testrepo/path1", "content\r\n");
cl_git_pass(p_mkdir("./testrepo/path0", 0777));
cl_git_mkfile("./testrepo/path0/file0", "content\r\n");
cl_assert(git_path_isfile("./testrepo/path1"));
cl_assert(git_path_isfile("./testrepo/path0/file0"));
g_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
cl_assert(git_path_isfile("./testrepo/path1"));
cl_assert(git_path_isfile("./testrepo/path0/file0"));
g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_index(g_repo, &g_opts));
cl_assert(git_path_isfile("./testrepo/path0"));
cl_assert(git_path_isfile("./testrepo/path1/file1"));
git_index_free(index);
}
......@@ -12,7 +12,7 @@ void test_checkout_tree__initialize(void)
g_repo = cl_git_sandbox_init("testrepo");
memset(&g_opts, 0, sizeof(g_opts));
g_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
}
void test_checkout_tree__cleanup(void)
......@@ -74,10 +74,13 @@ static void progress(const char *path, size_t cur, size_t tot, void *payload)
void test_checkout_tree__calls_progress_callback(void)
{
bool was_called = 0;
g_opts.progress_cb = progress;
g_opts.progress_payload = &was_called;
cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
cl_assert_equal_i(was_called, true);
}
......@@ -40,10 +40,12 @@ void test_checkout_typechange__checkout_typechanges(void)
git_object *obj;
git_checkout_opts opts = {0};
opts.checkout_strategy =
GIT_CHECKOUT_REMOVE_UNTRACKED |
GIT_CHECKOUT_CREATE_MISSING |
GIT_CHECKOUT_OVERWRITE_MODIFIED;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
/* if you don't include GIT_CHECKOUT_REMOVE_UNTRACKED then on the final
* checkout which is supposed to remove all the files, we will not
* actually remove them!
*/
for (i = 0; g_typechange_oids[i] != NULL; ++i) {
cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
......@@ -51,6 +53,9 @@ void test_checkout_typechange__checkout_typechanges(void)
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
cl_git_pass(
git_repository_set_head_detached(g_repo, git_object_id(obj)));
git_object_free(obj);
if (!g_typechange_empty[i]) {
......
......@@ -113,7 +113,7 @@ void test_clone_network__can_checkout_a_cloned_repo(void)
bool checkout_progress_cb_was_called = false,
fetch_progress_cb_was_called = false;
opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
opts.checkout_strategy = GIT_CHECKOUT_SAFE;
opts.progress_cb = &checkout_progress;
opts.progress_payload = &checkout_progress_cb_was_called;
......
......@@ -41,7 +41,7 @@ void test_core_copy__file_in_dir(void)
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", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r("an_dir", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_isdir("an_dir"));
}
......@@ -95,7 +95,7 @@ void test_core_copy__tree(void)
cl_assert(S_ISLNK(st.st_mode));
#endif
cl_git_pass(git_futils_rmdir_r("t1", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r("t1", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_isdir("t1"));
/* copy with empty dirs, no links, yes dotfiles, no overwrite */
......@@ -119,8 +119,8 @@ void test_core_copy__tree(void)
cl_git_fail(git_path_lstat("t2/c/d/l1", &st));
#endif
cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_isdir("t2"));
cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES));
}
......@@ -6,11 +6,11 @@
static void cleanup_basic_dirs(void *ref)
{
GIT_UNUSED(ref);
git_futils_rmdir_r("d0", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d1", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d2", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d3", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d4", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d0", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
git_futils_rmdir_r("d1", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
git_futils_rmdir_r("d2", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
git_futils_rmdir_r("d3", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
}
void test_core_mkdir__basic(void)
......@@ -56,7 +56,7 @@ void test_core_mkdir__basic(void)
static void cleanup_basedir(void *ref)
{
GIT_UNUSED(ref);
git_futils_rmdir_r("base", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("base", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
}
void test_core_mkdir__with_base(void)
......@@ -108,7 +108,7 @@ static void cleanup_chmod_root(void *ref)
git__free(mode);
}
git_futils_rmdir_r("r", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
}
static void check_mode(mode_t expected, mode_t actual)
......
......@@ -30,7 +30,7 @@ void test_core_rmdir__initialize(void)
/* make sure empty dir can be deleted recusively */
void test_core_rmdir__delete_recursive(void)
{
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY));
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
}
/* make sure non-empty dir cannot be deleted recusively */
......@@ -42,15 +42,15 @@ void test_core_rmdir__fail_to_delete_non_empty_dir(void)
cl_git_mkfile(git_buf_cstr(&file), "dummy");
cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY));
cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
cl_must_pass(p_unlink(file.ptr));
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY));
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
git_buf_free(&file);
}
void test_core_rmdir__can_skip__non_empty_dir(void)
void test_core_rmdir__can_skip_non_empty_dir(void)
{
git_buf file = GIT_BUF_INIT;
......@@ -58,11 +58,41 @@ void test_core_rmdir__can_skip__non_empty_dir(void)
cl_git_mkfile(git_buf_cstr(&file), "dummy");
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS));
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_SKIP_NONEMPTY));
cl_assert(git_path_exists(git_buf_cstr(&file)) == true);
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(git_path_exists(empty_tmp_dir) == false);
git_buf_free(&file);
}
void test_core_rmdir__can_remove_empty_parents(void)
{
git_buf file = GIT_BUF_INIT;
cl_git_pass(
git_buf_joinpath(&file, empty_tmp_dir, "/one/two_two/three/file.txt"));
cl_git_mkfile(git_buf_cstr(&file), "dummy");
cl_assert(git_path_isfile(git_buf_cstr(&file)));
cl_git_pass(git_futils_rmdir_r("one/two_two/three/file.txt", empty_tmp_dir,
GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_EMPTY_PARENTS));
cl_assert(!git_path_exists(git_buf_cstr(&file)));
git_buf_rtruncate_at_char(&file, '/'); /* three (only contained file.txt) */
cl_assert(!git_path_exists(git_buf_cstr(&file)));
git_buf_rtruncate_at_char(&file, '/'); /* two_two (only contained three) */
cl_assert(!git_path_exists(git_buf_cstr(&file)));
git_buf_rtruncate_at_char(&file, '/'); /* one (contained two_one also) */
cl_assert(git_path_exists(git_buf_cstr(&file)));
cl_assert(git_path_exists(empty_tmp_dir) == true);
git_buf_free(&file);
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
}
......@@ -49,7 +49,7 @@ void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_absolut
assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk);
git_buf_free(&full_path);
cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_RMDIR_REMOVE_FILES));
}
void test_object_blob_write__can_create_a_blob_in_a_bare_repo_from_a_absolute_filepath(void)
......@@ -65,5 +65,5 @@ void test_object_blob_write__can_create_a_blob_in_a_bare_repo_from_a_absolute_fi
assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk);
git_buf_free(&full_path);
cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_RMDIR_REMOVE_FILES));
}
......@@ -135,7 +135,7 @@ void test_repo_discover__0(void)
ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path);
ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path);
cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_RMDIR_REMOVE_FILES));
git_repository_free(repo);
git_buf_free(&ceiling_dirs_buf);
}
......
......@@ -7,7 +7,7 @@ void test_repo_open__cleanup(void)
cl_git_sandbox_cleanup();
if (git_path_isdir("alternate"))
git_futils_rmdir_r("alternate", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES);
}
void test_repo_open__bare_empty_repo(void)
......@@ -202,8 +202,8 @@ void test_repo_open__bad_gitlinks(void)
cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
}
git_futils_rmdir_r("invalid", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
git_futils_rmdir_r("invalid2", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
git_futils_rmdir_r("invalid", NULL, GIT_RMDIR_REMOVE_FILES);
git_futils_rmdir_r("invalid2", NULL, GIT_RMDIR_REMOVE_FILES);
}
#ifdef GIT_WIN32
......
......@@ -19,30 +19,56 @@ void test_reset_hard__cleanup(void)
cl_git_sandbox_cleanup();
}
void test_reset_hard__resetting_culls_empty_directories(void)
void test_reset_hard__resetting_reverts_modified_files(void)
{
git_buf subdir_path = GIT_BUF_INIT;
git_buf subfile_path = GIT_BUF_INIT;
git_buf newdir_path = GIT_BUF_INIT;
git_buf path = GIT_BUF_INIT, content = GIT_BUF_INIT;
int i;
static const char *files[4] = {
"current_file",
"modified_file",
"staged_new_file",
"staged_changes_modified_file" };
static const char *before[4] = {
"current_file\n",
"modified_file\nmodified_file\n",
"staged_new_file\n",
"staged_changes_modified_file\nstaged_changes_modified_file\nstaged_changes_modified_file\n"
};
static const char *after[4] = {
"current_file\n",
"modified_file\n",
/* wrong value because reset is still slightly incorrect */
"staged_new_file\n",
/* right value: NULL, */
"staged_changes_modified_file\n"
};
const char *wd = git_repository_workdir(repo);
cl_assert(wd);
for (i = 0; i < 4; ++i) {
cl_git_pass(git_buf_joinpath(&path, wd, files[i]));
cl_git_pass(git_futils_readbuffer(&content, path.ptr));
cl_assert_equal_s(before[i], content.ptr);
}
retrieve_target_from_oid(
&target, repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
cl_git_pass(git_buf_joinpath(&newdir_path, git_repository_workdir(repo), "newdir/"));
cl_git_pass(git_buf_joinpath(&subfile_path, git_buf_cstr(&newdir_path), "with/nested/file.txt"));
cl_git_pass(git_futils_mkpath2file(git_buf_cstr(&subfile_path), 0755));
cl_git_mkfile(git_buf_cstr(&subfile_path), "all anew...\n");
cl_git_pass(git_buf_joinpath(&subdir_path, git_repository_workdir(repo), "subdir/"));
cl_assert(git_path_isdir(git_buf_cstr(&subdir_path)) == true);
retrieve_target_from_oid(&target, repo, "0017bd4ab1ec30440b17bae1680cff124ab5f1f6");
cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
cl_assert(git_path_isdir(git_buf_cstr(&subdir_path)) == false);
cl_assert(git_path_isdir(git_buf_cstr(&newdir_path)) == false);
git_buf_free(&subdir_path);
git_buf_free(&subfile_path);
git_buf_free(&newdir_path);
for (i = 0; i < 4; ++i) {
cl_git_pass(git_buf_joinpath(&path, wd, files[i]));
if (after[i]) {
cl_git_pass(git_futils_readbuffer(&content, path.ptr));
cl_assert_equal_s(after[i], content.ptr);
} else {
cl_assert(!git_path_exists(path.ptr));
}
}
git_buf_free(&content);
git_buf_free(&path);
}
void test_reset_hard__cannot_reset_in_a_bare_repository(void)
......
......@@ -15,7 +15,7 @@ void test_stash_drop__cleanup(void)
{
git_signature_free(signature);
git_repository_free(repo);
cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
}
void test_stash_drop__cannot_drop_from_an_empty_stash(void)
......
......@@ -30,7 +30,7 @@ void test_stash_foreach__cleanup(void)
{
git_signature_free(signature);
git_repository_free(repo);
cl_git_pass(git_futils_rmdir_r(REPO_NAME, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(REPO_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
}
static int callback_cb(
......
......@@ -27,7 +27,7 @@ void test_stash_save__cleanup(void)
{
git_signature_free(signature);
git_repository_free(repo);
cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
}
static void assert_object_oid(const char* revision, const char* expected_oid, git_otype type)
......
......@@ -16,6 +16,7 @@ void commit_staged_files(
cl_git_pass(git_index_write_tree(&tree_oid, index));
cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
cl_git_pass(git_commit_create_v(
commit_oid,
repo,
......
......@@ -71,7 +71,7 @@ static int remove_file_cb(void *data, git_buf *file)
return 0;
if (git_path_isdir(filename))
cl_git_pass(git_futils_rmdir_r(filename, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(filename, NULL, GIT_RMDIR_REMOVE_FILES));
else
cl_git_pass(p_unlink(git_buf_cstr(file)));
......@@ -314,7 +314,7 @@ void test_status_worktree__issue_592_3(void)
repo = cl_git_sandbox_init("issue_592");
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c"));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
......@@ -344,7 +344,7 @@ void test_status_worktree__issue_592_5(void)
repo = cl_git_sandbox_init("issue_592");
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t"));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(p_mkdir(git_buf_cstr(&path), 0777));
cl_git_pass(git_status_foreach(repo, cb_status__check_592, NULL));
......
......@@ -50,7 +50,7 @@ void test_submodule_status__ignore_none(void)
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_fail(git_submodule_lookup(&sm, g_repo, "not_submodule"));
......@@ -135,7 +135,7 @@ void test_submodule_status__ignore_untracked(void)
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
......@@ -195,7 +195,7 @@ void test_submodule_status__ignore_dirty(void)
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
......@@ -255,7 +255,7 @@ void test_submodule_status__ignore_all(void)
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment