Commit 4c47a8bc by Russell Belfer

Merge pull request #968 from arrbee/diff-support-typechange

Support TYPECHANGE records in status and adjust checkout accordingly
parents 6012e868 52a61bb8
...@@ -21,22 +21,39 @@ ...@@ -21,22 +21,39 @@
*/ */
GIT_BEGIN_DECL GIT_BEGIN_DECL
enum { /**
* Checkout behavior flags
*
* These flags control what checkout does with files. Pass in a
* combination of these values OR'ed together.
*/
typedef enum {
/** Checkout does not update any files in the working directory. */
GIT_CHECKOUT_DEFAULT = (1 << 0), GIT_CHECKOUT_DEFAULT = (1 << 0),
/** When a file exists and is modified, replace it with new version. */
GIT_CHECKOUT_OVERWRITE_MODIFIED = (1 << 1), GIT_CHECKOUT_OVERWRITE_MODIFIED = (1 << 1),
/** When a file does not exist in the working directory, create it. */
GIT_CHECKOUT_CREATE_MISSING = (1 << 2), GIT_CHECKOUT_CREATE_MISSING = (1 << 2),
/** If an untracked file in found in the working dir, delete it. */
GIT_CHECKOUT_REMOVE_UNTRACKED = (1 << 3), GIT_CHECKOUT_REMOVE_UNTRACKED = (1 << 3),
}; } git_checkout_strategy_t;
/* Use zeros to indicate default settings */ /**
* Checkout options structure
*
* Use zeros to indicate default settings.
*/
typedef struct git_checkout_opts { typedef struct git_checkout_opts {
unsigned int checkout_strategy; /* default: GIT_CHECKOUT_DEFAULT */ unsigned int checkout_strategy; /** default: GIT_CHECKOUT_DEFAULT */
int disable_filters; int disable_filters; /** don't apply filters like CRLF conversion */
int dir_mode; /* default is 0755 */ int dir_mode; /** default is 0755 */
int file_mode; /* default is 0644 */ int file_mode; /** default is 0644 or 0755 as dictated by blob */
int file_open_flags; /* default is O_CREAT | O_TRUNC | O_WRONLY */ int file_open_flags; /** default is O_CREAT | O_TRUNC | O_WRONLY */
/* Optional callback to notify the consumer of files that /** Optional callback to notify the consumer of files that
* haven't be checked out because a modified version of them * haven't be checked out because a modified version of them
* exist in the working directory. * exist in the working directory.
* *
...@@ -51,7 +68,7 @@ typedef struct git_checkout_opts { ...@@ -51,7 +68,7 @@ typedef struct git_checkout_opts {
void *notify_payload; void *notify_payload;
/* when not NULL, arrays of fnmatch pattern specifying /** When not NULL, array of fnmatch patterns specifying
* which paths should be taken into account * which paths should be taken into account
*/ */
git_strarray paths; git_strarray paths;
......
...@@ -34,21 +34,58 @@ GIT_BEGIN_DECL ...@@ -34,21 +34,58 @@ GIT_BEGIN_DECL
* in via the `flags` value in the `git_diff_options`. * in via the `flags` value in the `git_diff_options`.
*/ */
enum { enum {
/** Normal diff, the default */
GIT_DIFF_NORMAL = 0, GIT_DIFF_NORMAL = 0,
/** Reverse the sides of the diff */
GIT_DIFF_REVERSE = (1 << 0), GIT_DIFF_REVERSE = (1 << 0),
/** Treat all files as text, disabling binary attributes & detection */
GIT_DIFF_FORCE_TEXT = (1 << 1), GIT_DIFF_FORCE_TEXT = (1 << 1),
/** Ignore all whitespace */
GIT_DIFF_IGNORE_WHITESPACE = (1 << 2), GIT_DIFF_IGNORE_WHITESPACE = (1 << 2),
/** Ignore changes in amount of whitespace */
GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3), GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
/** Ignore whitespace at end of line */
GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4), GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
/** Exclude submodules from the diff completely */
GIT_DIFF_IGNORE_SUBMODULES = (1 << 5), GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
/** Use the "patience diff" algorithm (currently unimplemented) */
GIT_DIFF_PATIENCE = (1 << 6), GIT_DIFF_PATIENCE = (1 << 6),
/** Include ignored files in the diff list */
GIT_DIFF_INCLUDE_IGNORED = (1 << 7), GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
/** Include untracked files in the diff list */
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8), GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
/** Include unmodified files in the diff list */
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9), GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
/** Even with the GIT_DIFF_INCLUDE_UNTRACKED flag, when an untracked
* directory is found, only a single entry for the directory is added
* to the diff list; with this flag, all files under the directory will
* be included, too.
*/
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10), GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
/** If the pathspec is set in the diff options, this flags means to
* apply it as an exact match instead of as an fnmatch pattern.
*/
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11), GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
/** Use case insensitive filename comparisons */
GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12), GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
/** When generating patch text, include the content of untracked files */
GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13), GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
/** Disable updating of the `binary` flag in delta records. This is
* useful when iterating over a diff if you don't need hunk and data
* callbacks and want to avoid having to load file completely.
*/
GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
/** Normally, a type change between files will be converted into a
* DELETED record for the old and an ADDED record for the new; this
* options enabled the generation of TYPECHANGE delta records.
*/
GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
* generally show as a DELETED blob. This flag tries to correctly
* label blob->tree transitions as TYPECHANGE records with new_file's
* mode set to tree. Note: the tree SHA will not be available.
*/
GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16),
}; };
/** /**
...@@ -85,24 +122,16 @@ typedef struct git_diff_list git_diff_list; ...@@ -85,24 +122,16 @@ typedef struct git_diff_list git_diff_list;
* Flags that can be set for the file on side of a diff. * Flags that can be set for the file on side of a diff.
* *
* Most of the flags are just for internal consumption by libgit2, * Most of the flags are just for internal consumption by libgit2,
* but some of them may be interesting to external users. They are: * but some of them may be interesting to external users.
*
* - VALID_OID - the `oid` value is computed and correct
* - FREE_PATH - the `path` string is separated allocated memory
* - BINARY - this file should be considered binary data
* - NOT_BINARY - this file should be considered text data
* - FREE_DATA - the internal file data is kept in allocated memory
* - UNMAP_DATA - the internal file data is kept in mmap'ed memory
* - NO_DATA - this side of the diff should not be loaded
*/ */
enum { enum {
GIT_DIFF_FILE_VALID_OID = (1 << 0), GIT_DIFF_FILE_VALID_OID = (1 << 0), /** `oid` value is known correct */
GIT_DIFF_FILE_FREE_PATH = (1 << 1), GIT_DIFF_FILE_FREE_PATH = (1 << 1), /** `path` is allocated memory */
GIT_DIFF_FILE_BINARY = (1 << 2), GIT_DIFF_FILE_BINARY = (1 << 2), /** should be considered binary data */
GIT_DIFF_FILE_NOT_BINARY = (1 << 3), GIT_DIFF_FILE_NOT_BINARY = (1 << 3), /** should be considered text data */
GIT_DIFF_FILE_FREE_DATA = (1 << 4), GIT_DIFF_FILE_FREE_DATA = (1 << 4), /** internal file data is allocated */
GIT_DIFF_FILE_UNMAP_DATA = (1 << 5), GIT_DIFF_FILE_UNMAP_DATA = (1 << 5), /** internal file data is mmap'ed */
GIT_DIFF_FILE_NO_DATA = (1 << 6), GIT_DIFF_FILE_NO_DATA = (1 << 6), /** file data should not be loaded */
}; };
/** /**
...@@ -116,7 +145,8 @@ typedef enum { ...@@ -116,7 +145,8 @@ typedef enum {
GIT_DELTA_RENAMED = 4, GIT_DELTA_RENAMED = 4,
GIT_DELTA_COPIED = 5, GIT_DELTA_COPIED = 5,
GIT_DELTA_IGNORED = 6, GIT_DELTA_IGNORED = 6,
GIT_DELTA_UNTRACKED = 7 GIT_DELTA_UNTRACKED = 7,
GIT_DELTA_TYPECHANGE = 8,
} git_delta_t; } git_delta_t;
/** /**
......
...@@ -19,19 +19,22 @@ ...@@ -19,19 +19,22 @@
*/ */
GIT_BEGIN_DECL GIT_BEGIN_DECL
enum { typedef enum {
GIT_STATUS_CURRENT = 0, GIT_STATUS_CURRENT = 0,
GIT_STATUS_INDEX_NEW = (1 << 0), GIT_STATUS_INDEX_NEW = (1u << 0),
GIT_STATUS_INDEX_MODIFIED = (1 << 1), GIT_STATUS_INDEX_MODIFIED = (1u << 1),
GIT_STATUS_INDEX_DELETED = (1 << 2), GIT_STATUS_INDEX_DELETED = (1u << 2),
GIT_STATUS_INDEX_RENAMED = (1u << 3),
GIT_STATUS_INDEX_TYPECHANGE = (1u << 4),
GIT_STATUS_WT_NEW = (1 << 3), GIT_STATUS_WT_NEW = (1u << 7),
GIT_STATUS_WT_MODIFIED = (1 << 4), GIT_STATUS_WT_MODIFIED = (1u << 8),
GIT_STATUS_WT_DELETED = (1 << 5), GIT_STATUS_WT_DELETED = (1u << 9),
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
GIT_STATUS_IGNORED = (1 << 6), GIT_STATUS_IGNORED = (1u << 14),
}; } git_status_t;
/** /**
* Gather file statuses and run a callback for each one. * Gather file statuses and run a callback for each one.
......
...@@ -100,7 +100,7 @@ GIT_EXTERN(git_tree_entry *) git_tree_entry_dup(const git_tree_entry *entry); ...@@ -100,7 +100,7 @@ GIT_EXTERN(git_tree_entry *) git_tree_entry_dup(const git_tree_entry *entry);
* @param tree a previously loaded tree. * @param tree a previously loaded tree.
* @return object identity for the tree. * @return object identity for the tree.
*/ */
GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree); GIT_EXTERN(const git_oid *) git_tree_id(const git_tree *tree);
/** /**
* Get the number of entries listed in a tree * Get the number of entries listed in a tree
...@@ -108,7 +108,7 @@ GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree); ...@@ -108,7 +108,7 @@ GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree);
* @param tree a previously loaded tree. * @param tree a previously loaded tree.
* @return the number of entries in the tree * @return the number of entries in the tree
*/ */
GIT_EXTERN(unsigned int) git_tree_entrycount(git_tree *tree); GIT_EXTERN(unsigned int) git_tree_entrycount(const git_tree *tree);
/** /**
* Lookup a tree entry by its filename * Lookup a tree entry by its filename
......
...@@ -30,6 +30,9 @@ struct checkout_diff_data ...@@ -30,6 +30,9 @@ struct checkout_diff_data
git_indexer_stats *stats; git_indexer_stats *stats;
git_repository *owner; git_repository *owner;
bool can_symlink; bool can_symlink;
bool found_submodules;
bool create_submodules;
int error;
}; };
static int buffer_to_file( static int buffer_to_file(
...@@ -39,18 +42,27 @@ static int buffer_to_file( ...@@ -39,18 +42,27 @@ static int buffer_to_file(
int file_open_flags, int file_open_flags,
mode_t file_mode) mode_t file_mode)
{ {
int fd, error_write, error_close; int fd, error, error_close;
if (git_futils_mkpath2file(path, dir_mode) < 0) if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
return -1; return error;
if ((fd = p_open(path, file_open_flags, file_mode)) < 0) if ((fd = p_open(path, file_open_flags, file_mode)) < 0)
return -1; return fd;
error = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer));
error_write = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer));
error_close = p_close(fd); error_close = p_close(fd);
return error_write ? error_write : error_close; if (!error)
error = error_close;
if (!error &&
(file_mode & 0100) != 0 &&
(error = p_chmod(path, file_mode)) < 0)
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
return error;
} }
static int blob_content_to_file( static int blob_content_to_file(
...@@ -84,7 +96,7 @@ static int blob_content_to_file( ...@@ -84,7 +96,7 @@ static int blob_content_to_file(
return nb_filters; return nb_filters;
if (nb_filters > 0) { if (nb_filters > 0) {
if (git_blob__getbuf(&unfiltered, blob) < 0) if ((error = git_blob__getbuf(&unfiltered, blob)) < 0)
goto cleanup; goto cleanup;
if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0) if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0)
...@@ -111,8 +123,8 @@ static int blob_content_to_link(git_blob *blob, const char *path, bool can_symli ...@@ -111,8 +123,8 @@ static int blob_content_to_link(git_blob *blob, const char *path, bool can_symli
git_buf linktarget = GIT_BUF_INIT; git_buf linktarget = GIT_BUF_INIT;
int error; int error;
if (git_blob__getbuf(&linktarget, blob) < 0) if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
return -1; return error;
if (can_symlink) if (can_symlink)
error = p_symlink(git_buf_cstr(&linktarget), path); error = p_symlink(git_buf_cstr(&linktarget), path);
...@@ -124,110 +136,126 @@ static int blob_content_to_link(git_blob *blob, const char *path, bool can_symli ...@@ -124,110 +136,126 @@ static int blob_content_to_link(git_blob *blob, const char *path, bool can_symli
return error; return error;
} }
static int checkout_submodule(
struct checkout_diff_data *data,
const git_diff_file *file)
{
if (git_futils_mkdir(
file->path, git_repository_workdir(data->owner),
data->checkout_opts->dir_mode, GIT_MKDIR_PATH) < 0)
return -1;
/* TODO: two cases:
* 1 - submodule already checked out, but we need to move the HEAD
* to the new OID, or
* 2 - submodule not checked out and we should recursively check it out
*
* Checkout will not execute a pull request on the submodule, but a
* clone command should probably be able to. Do we need a submodule
* callback option?
*/
return 0;
}
static int checkout_blob( static int checkout_blob(
git_repository *repo, struct checkout_diff_data *data,
const git_oid *blob_oid, const git_diff_file *file)
const char *path,
mode_t filemode,
bool can_symlink,
git_checkout_opts *opts)
{ {
git_blob *blob; git_blob *blob;
int error; int error;
if (git_blob_lookup(&blob, repo, blob_oid) < 0) git_buf_truncate(data->path, data->workdir_len);
return -1; /* Add an error message */ if (git_buf_joinpath(data->path, git_buf_cstr(data->path), file->path) < 0)
return -1;
if ((error = git_blob_lookup(&blob, data->owner, &file->oid)) < 0)
return error;
if (S_ISLNK(filemode)) if (S_ISLNK(file->mode))
error = blob_content_to_link(blob, path, can_symlink); error = blob_content_to_link(
blob, git_buf_cstr(data->path), data->can_symlink);
else else
error = blob_content_to_file(blob, path, filemode, opts); error = blob_content_to_file(
blob, git_buf_cstr(data->path), file->mode, data->checkout_opts);
git_blob_free(blob); git_blob_free(blob);
return error; return error;
} }
static int checkout_diff_fn( static int checkout_remove_the_old(
void *cb_data, void *cb_data, const git_diff_delta *delta, float progress)
const git_diff_delta *delta,
float progress)
{ {
struct checkout_diff_data *data; struct checkout_diff_data *data = cb_data;
int error = -1; git_checkout_opts *opts = data->checkout_opts;
git_checkout_opts *opts;
GIT_UNUSED(progress);
data = (struct checkout_diff_data *)cb_data; data->stats->processed++;
data->stats->processed = (unsigned int)(data->stats->total * progress); if ((delta->status == GIT_DELTA_UNTRACKED &&
(opts->checkout_strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0) ||
git_buf_truncate(data->path, data->workdir_len); (delta->status == GIT_DELTA_TYPECHANGE &&
if (git_buf_joinpath(data->path, git_buf_cstr(data->path), delta->new_file.path) < 0) (opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED) != 0))
return -1; {
data->error = git_futils_rmdir_r(
opts = data->checkout_opts; delta->new_file.path,
git_repository_workdir(data->owner),
switch (delta->status) { GIT_DIRREMOVAL_FILES_AND_DIRS);
case GIT_DELTA_UNTRACKED: }
if (!(opts->checkout_strategy & GIT_CHECKOUT_REMOVE_UNTRACKED))
return 0;
if (!git__suffixcmp(delta->new_file.path, "/"))
error = git_futils_rmdir_r(git_buf_cstr(data->path), GIT_DIRREMOVAL_FILES_AND_DIRS);
else
error = p_unlink(git_buf_cstr(data->path));
break;
case GIT_DELTA_MODIFIED: return data->error;
if (!(opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED)) { }
if ((opts->skipped_notify_cb != NULL) static int checkout_create_the_new(
&& (opts->skipped_notify_cb( void *cb_data, const git_diff_delta *delta, float progress)
delta->new_file.path, {
&delta->old_file.oid, int error = 0;
delta->old_file.mode, struct checkout_diff_data *data = cb_data;
opts->notify_payload))) { git_checkout_opts *opts = data->checkout_opts;
bool do_checkout = false, do_notify = false;
GIT_UNUSED(progress);
data->stats->processed++;
if (delta->status == GIT_DELTA_MODIFIED ||
delta->status == GIT_DELTA_TYPECHANGE)
{
if ((opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED) != 0)
do_checkout = true;
else if (opts->skipped_notify_cb != NULL)
do_notify = !data->create_submodules;
}
else if (delta->status == GIT_DELTA_DELETED &&
(opts->checkout_strategy & GIT_CHECKOUT_CREATE_MISSING) != 0)
do_checkout = true;
if (do_notify) {
if (opts->skipped_notify_cb(
delta->old_file.path, &delta->old_file.oid,
delta->old_file.mode, opts->notify_payload))
{
giterr_clear(); giterr_clear();
return GIT_EUSER; error = GIT_EUSER;
} }
return 0;
} }
if (checkout_blob( if (do_checkout) {
data->owner, bool is_submodule = S_ISGITLINK(delta->old_file.mode);
&delta->old_file.oid,
git_buf_cstr(data->path),
delta->old_file.mode,
data->can_symlink,
opts) < 0)
goto cleanup;
break;
case GIT_DELTA_DELETED:
if (!(opts->checkout_strategy & GIT_CHECKOUT_CREATE_MISSING))
return 0;
if (checkout_blob( if (is_submodule)
data->owner, data->found_submodules = true;
&delta->old_file.oid,
git_buf_cstr(data->path),
delta->old_file.mode,
data->can_symlink,
opts) < 0)
goto cleanup;
break; if (!is_submodule && !data->create_submodules)
error = checkout_blob(data, &delta->old_file);
default: else if (is_submodule && data->create_submodules)
giterr_set(GITERR_INVALID, "Unexpected status (%d) for path '%s'.", delta->status, delta->new_file.path); error = checkout_submodule(data, &delta->old_file);
goto cleanup;
} }
error = 0; if (error)
data->error = error;
cleanup:
return error; return error;
} }
...@@ -279,7 +307,6 @@ int git_checkout_index( ...@@ -279,7 +307,6 @@ int git_checkout_index(
git_checkout_opts *opts, git_checkout_opts *opts,
git_indexer_stats *stats) git_indexer_stats *stats)
{ {
git_index *index = NULL;
git_diff_list *diff = NULL; git_diff_list *diff = NULL;
git_indexer_stats dummy_stats; git_indexer_stats dummy_stats;
...@@ -293,10 +320,13 @@ int git_checkout_index( ...@@ -293,10 +320,13 @@ int git_checkout_index(
assert(repo); assert(repo);
if ((git_repository__ensure_not_bare(repo, "checkout")) < 0) if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
return GIT_EBAREREPO; return error;
diff_opts.flags = GIT_DIFF_INCLUDE_UNTRACKED; diff_opts.flags =
GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_INCLUDE_TYPECHANGE |
GIT_DIFF_SKIP_BINARY_CHECK;
if (opts && opts->paths.count > 0) if (opts && opts->paths.count > 0)
diff_opts.pathspec = opts->paths; diff_opts.pathspec = opts->paths;
...@@ -313,11 +343,8 @@ int git_checkout_index( ...@@ -313,11 +343,8 @@ int git_checkout_index(
stats = &dummy_stats; stats = &dummy_stats;
stats->processed = 0; stats->processed = 0;
/* total based on 3 passes, but it might be 2 if no submodules */
if ((git_repository_index(&index, repo)) < 0) stats->total = (unsigned int)git_diff_num_deltas(diff) * 3;
goto cleanup;
stats->total = git_index_entrycount(index);
memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(data));
...@@ -330,12 +357,36 @@ int git_checkout_index( ...@@ -330,12 +357,36 @@ int git_checkout_index(
if ((error = retrieve_symlink_capabilities(repo, &data.can_symlink)) < 0) if ((error = retrieve_symlink_capabilities(repo, &data.can_symlink)) < 0)
goto cleanup; goto cleanup;
error = git_diff_foreach(diff, &data, checkout_diff_fn, NULL, NULL); /* Checkout is best performed with three passes through the diff.
*
* 1. First do removes, because we iterate in alphabetical order, thus
* a new untracked directory will end up sorted *after* a blob that
* should be checked out with the same name.
* 2. Then checkout all blobs.
* 3. Then checkout all submodules in case a new .gitmodules blob was
* checked out during pass #2.
*/
if (!(error = git_diff_foreach(
diff, &data, checkout_remove_the_old, NULL, NULL)) &&
!(error = git_diff_foreach(
diff, &data, checkout_create_the_new, NULL, NULL)) &&
data.found_submodules)
{
data.create_submodules = true;
error = git_diff_foreach(
diff, &data, checkout_create_the_new, NULL, NULL);
}
stats->processed = stats->total;
cleanup: cleanup:
git_index_free(index); if (error == GIT_EUSER)
error = (data.error != 0) ? data.error : -1;
git_diff_list_free(diff); git_diff_list_free(diff);
git_buf_free(&workdir); git_buf_free(&workdir);
return error; return error;
} }
......
...@@ -330,7 +330,7 @@ static int clone_internal( ...@@ -330,7 +330,7 @@ static int clone_internal(
if ((retcode = setup_remotes_and_fetch(repo, origin_url, fetch_stats)) < 0) { if ((retcode = setup_remotes_and_fetch(repo, origin_url, fetch_stats)) < 0) {
/* Failed to fetch; clean up */ /* Failed to fetch; clean up */
git_repository_free(repo); git_repository_free(repo);
git_futils_rmdir_r(path, GIT_DIRREMOVAL_FILES_AND_DIRS); git_futils_rmdir_r(path, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
} else { } else {
*out = repo; *out = repo;
retcode = 0; retcode = 0;
......
...@@ -291,6 +291,36 @@ static int diff_delta__from_two( ...@@ -291,6 +291,36 @@ static int diff_delta__from_two(
return 0; return 0;
} }
static git_diff_delta *diff_delta__last_for_item(
git_diff_list *diff,
const git_index_entry *item)
{
git_diff_delta *delta = git_vector_last(&diff->deltas);
if (!delta)
return NULL;
switch (delta->status) {
case GIT_DELTA_UNMODIFIED:
case GIT_DELTA_DELETED:
if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0)
return delta;
break;
case GIT_DELTA_ADDED:
if (git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
return delta;
break;
case GIT_DELTA_MODIFIED:
if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0 ||
git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
return delta;
break;
default:
break;
}
return NULL;
}
static char *diff_strdup_prefix(git_pool *pool, const char *prefix) static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
{ {
size_t len = strlen(prefix); size_t len = strlen(prefix);
...@@ -368,6 +398,10 @@ static git_diff_list *git_diff_list_alloc( ...@@ -368,6 +398,10 @@ static git_diff_list *git_diff_list_alloc(
diff->opts.new_prefix = swap; diff->opts.new_prefix = swap;
} }
/* INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
if (diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES)
diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
/* only copy pathspec if it is "interesting" so we can test /* only copy pathspec if it is "interesting" so we can test
* diff->pathspec.length > 0 to know if it is worth calling * diff->pathspec.length > 0 to know if it is worth calling
* fnmatch as we iterate. * fnmatch as we iterate.
...@@ -508,6 +542,7 @@ static int maybe_modified( ...@@ -508,6 +542,7 @@ static int maybe_modified(
git_delta_t status = GIT_DELTA_MODIFIED; git_delta_t status = GIT_DELTA_MODIFIED;
unsigned int omode = oitem->mode; unsigned int omode = oitem->mode;
unsigned int nmode = nitem->mode; unsigned int nmode = nitem->mode;
bool new_is_workdir = (new_iter->type == GIT_ITERATOR_WORKDIR);
GIT_UNUSED(old_iter); GIT_UNUSED(old_iter);
...@@ -515,15 +550,14 @@ static int maybe_modified( ...@@ -515,15 +550,14 @@ static int maybe_modified(
return 0; return 0;
/* on platforms with no symlinks, preserve mode of existing symlinks */ /* on platforms with no symlinks, preserve mode of existing symlinks */
if (S_ISLNK(omode) && S_ISREG(nmode) && if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir &&
!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS) && !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
new_iter->type == GIT_ITERATOR_WORKDIR)
nmode = omode; nmode = omode;
/* on platforms with no execmode, just preserve old mode */ /* on platforms with no execmode, just preserve old mode */
if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) && if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
(nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) && (nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) &&
new_iter->type == GIT_ITERATOR_WORKDIR) new_is_workdir)
nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK); nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
/* support "assume unchanged" (poorly, b/c we still stat everything) */ /* support "assume unchanged" (poorly, b/c we still stat everything) */
...@@ -537,11 +571,15 @@ static int maybe_modified( ...@@ -537,11 +571,15 @@ static int maybe_modified(
/* if basic type of file changed, then split into delete and add */ /* if basic type of file changed, then split into delete and add */
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
if ((diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE) != 0)
status = GIT_DELTA_TYPECHANGE;
else {
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0) diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
return -1; return -1;
return 0; return 0;
} }
}
/* if oids and modes match, then file is unmodified */ /* if oids and modes match, then file is unmodified */
else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 && else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
...@@ -551,9 +589,7 @@ static int maybe_modified( ...@@ -551,9 +589,7 @@ static int maybe_modified(
/* if we have an unknown OID and a workdir iterator, then check some /* if we have an unknown OID and a workdir iterator, then check some
* circumstances that can accelerate things or need special handling * circumstances that can accelerate things or need special handling
*/ */
else if (git_oid_iszero(&nitem->oid) && else if (git_oid_iszero(&nitem->oid) && new_is_workdir) {
new_iter->type == GIT_ITERATOR_WORKDIR)
{
/* TODO: add check against index file st_mtime to avoid racy-git */ /* TODO: add check against index file st_mtime to avoid racy-git */
/* if the stat data looks exactly alike, then assume the same */ /* if the stat data looks exactly alike, then assume the same */
...@@ -588,7 +624,7 @@ static int maybe_modified( ...@@ -588,7 +624,7 @@ static int maybe_modified(
/* grab OID while we are here */ /* grab OID while we are here */
if (git_oid_iszero(&nitem->oid)) { if (git_oid_iszero(&nitem->oid)) {
const git_oid *sm_oid = git_submodule_wd_oid(sub); const git_oid *sm_oid = git_submodule_wd_oid(sub);
if (sub != NULL) { if (sm_oid != NULL) {
git_oid_cpy(&noid, sm_oid); git_oid_cpy(&noid, sm_oid);
use_noid = &noid; use_noid = &noid;
} }
...@@ -600,7 +636,7 @@ static int maybe_modified( ...@@ -600,7 +636,7 @@ static int maybe_modified(
/* if we got here and decided that the files are modified, but we /* if we got here and decided that the files are modified, but we
* haven't calculated the OID of the new item, then calculate it now * haven't calculated the OID of the new item, then calculate it now
*/ */
if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) { if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0) if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
return -1; return -1;
else if (omode == nmode && git_oid_equal(&oitem->oid, &noid)) else if (omode == nmode && git_oid_equal(&oitem->oid, &noid))
...@@ -630,6 +666,24 @@ static int git_index_entry_cmp_icase(const void *a, const void *b) ...@@ -630,6 +666,24 @@ static int git_index_entry_cmp_icase(const void *a, const void *b)
return strcasecmp(entry_a->path, entry_b->path); return strcasecmp(entry_a->path, entry_b->path);
} }
static bool entry_is_prefixed(
const git_index_entry *item,
git_iterator *prefix_iterator,
const git_index_entry *prefix_item)
{
size_t pathlen;
if (!prefix_item ||
ITERATOR_PREFIXCMP(*prefix_iterator, prefix_item->path, item->path))
return false;
pathlen = strlen(item->path);
return (item->path[pathlen - 1] == '/' ||
prefix_item->path[pathlen] == '\0' ||
prefix_item->path[pathlen] == '/');
}
static int diff_from_iterators( static int diff_from_iterators(
git_repository *repo, git_repository *repo,
const git_diff_options *opts, /**< can be NULL for defaults */ const git_diff_options *opts, /**< can be NULL for defaults */
...@@ -679,8 +733,24 @@ static int diff_from_iterators( ...@@ -679,8 +733,24 @@ static int diff_from_iterators(
/* create DELETED records for old items not matched in new */ /* create DELETED records for old items not matched in new */
if (oitem && (!nitem || entry_compare(oitem, nitem) < 0)) { if (oitem && (!nitem || entry_compare(oitem, nitem) < 0)) {
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0)
git_iterator_advance(old_iter, &oitem) < 0) goto fail;
/* if we are generating TYPECHANGE records then check for that
* instead of just generating a DELETE record
*/
if ((diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 &&
entry_is_prefixed(oitem, new_iter, nitem))
{
/* this entry has become a tree! convert to TYPECHANGE */
git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
if (last) {
last->status = GIT_DELTA_TYPECHANGE;
last->new_file.mode = GIT_FILEMODE_TREE;
}
}
if (git_iterator_advance(old_iter, &oitem) < 0)
goto fail; goto fail;
} }
...@@ -702,8 +772,7 @@ static int diff_from_iterators( ...@@ -702,8 +772,7 @@ static int diff_from_iterators(
* directories and it is not under an ignored directory. * directories and it is not under an ignored directory.
*/ */
bool contains_tracked = bool contains_tracked =
(oitem && entry_is_prefixed(nitem, old_iter, oitem);
!ITERATOR_PREFIXCMP(*old_iter, oitem->path, nitem->path));
bool recurse_untracked = bool recurse_untracked =
(delta_type == GIT_DELTA_UNTRACKED && (delta_type == GIT_DELTA_UNTRACKED &&
(diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0); (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0);
...@@ -759,8 +828,25 @@ static int diff_from_iterators( ...@@ -759,8 +828,25 @@ static int diff_from_iterators(
else if (new_iter->type != GIT_ITERATOR_WORKDIR) else if (new_iter->type != GIT_ITERATOR_WORKDIR)
delta_type = GIT_DELTA_ADDED; delta_type = GIT_DELTA_ADDED;
if (diff_delta__from_one(diff, delta_type, nitem) < 0 || if (diff_delta__from_one(diff, delta_type, nitem) < 0)
git_iterator_advance(new_iter, &nitem) < 0) goto fail;
/* if we are generating TYPECHANGE records then check for that
* instead of just generating an ADD/UNTRACKED record
*/
if (delta_type != GIT_DELTA_IGNORED &&
(diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 &&
entry_is_prefixed(nitem, old_iter, oitem))
{
/* this entry was a tree! convert to TYPECHANGE */
git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
if (last) {
last->status = GIT_DELTA_TYPECHANGE;
last->old_file.mode = GIT_FILEMODE_TREE;
}
}
if (git_iterator_advance(new_iter, &nitem) < 0)
goto fail; goto fail;
} }
......
...@@ -28,17 +28,12 @@ enum { ...@@ -28,17 +28,12 @@ enum {
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */ GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
}; };
typedef struct {
git_refcount rc;
git_diff_delta delta;
} git_diff_delta_refcounted;
struct git_diff_list { struct git_diff_list {
git_refcount rc; git_refcount rc;
git_repository *repo; git_repository *repo;
git_diff_options opts; git_diff_options opts;
git_vector pathspec; git_vector pathspec;
git_vector deltas; /* vector of git_diff_delta_refcounted */ git_vector deltas; /* vector of git_diff_delta */
git_pool pool; git_pool pool;
git_iterator_type_t old_src; git_iterator_type_t old_src;
git_iterator_type_t new_src; git_iterator_type_t new_src;
......
...@@ -533,6 +533,11 @@ static int diff_patch_load( ...@@ -533,6 +533,11 @@ static int diff_patch_load(
if (delta->binary == 1) if (delta->binary == 1)
goto cleanup; goto cleanup;
if (!ctxt->hunk_cb &&
!ctxt->data_cb &&
(ctxt->opts->flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0)
goto cleanup;
switch (delta->status) { switch (delta->status) {
case GIT_DELTA_ADDED: case GIT_DELTA_ADDED:
delta->old_file.flags |= GIT_DIFF_FILE_NO_DATA; delta->old_file.flags |= GIT_DIFF_FILE_NO_DATA;
...@@ -698,7 +703,9 @@ static int diff_patch_generate( ...@@ -698,7 +703,9 @@ static int diff_patch_generate(
if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0) if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0)
return 0; return 0;
if (ctxt) if (!ctxt->file_cb && !ctxt->hunk_cb)
return 0;
patch->ctxt = ctxt; patch->ctxt = ctxt;
memset(&xdiff_callback, 0, sizeof(xdiff_callback)); memset(&xdiff_callback, 0, sizeof(xdiff_callback));
...@@ -1360,7 +1367,9 @@ int git_diff_get_patch( ...@@ -1360,7 +1367,9 @@ int git_diff_get_patch(
if (delta_ptr) if (delta_ptr)
*delta_ptr = delta; *delta_ptr = delta;
if (!patch_ptr && delta->binary != -1) if (!patch_ptr &&
(delta->binary != -1 ||
(diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
return 0; return 0;
diff_context_init( diff_context_init(
......
...@@ -326,10 +326,6 @@ static int _rmdir_recurs_foreach(void *opaque, git_buf *path) ...@@ -326,10 +326,6 @@ static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
{ {
git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque; git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
|| removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
|| removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
if (git_path_isdir(path->ptr) == true) { if (git_path_isdir(path->ptr) == true) {
if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0) if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
return -1; return -1;
...@@ -362,15 +358,24 @@ static int _rmdir_recurs_foreach(void *opaque, git_buf *path) ...@@ -362,15 +358,24 @@ static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
return 0; return 0;
} }
int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type) int git_futils_rmdir_r(
const char *path, const char *base, git_directory_removal_type removal_type)
{ {
int error; int error;
git_buf p = GIT_BUF_INIT; 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);
/* 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);
git_buf_free(&fullpath);
error = git_buf_sets(&p, path);
if (!error)
error = _rmdir_recurs_foreach(&removal_type, &p);
git_buf_free(&p);
return error; return error;
} }
......
...@@ -107,15 +107,17 @@ typedef enum { ...@@ -107,15 +107,17 @@ typedef enum {
* Remove path and any files and directories beneath it. * Remove path and any files and directories beneath it.
* *
* @param path Path to to top level directory to process. * @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 * @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 * of empty directories (will fail if any file is found),
* to remove a hierarchy of files and folders, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove * GIT_DIRREMOVAL_FILES_AND_DIRS to remove a hierarchy of
* empty directories (no failure on file encounter). * files and folders,
* GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove empty
* directories (no failure on file encounter).
* *
* @return 0 on success; -1 on error. * @return 0 on success; -1 on error.
*/ */
extern int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type); extern int git_futils_rmdir_r(const char *path, const char *base, git_directory_removal_type removal_type);
/** /**
* Create and open a temporary file with a `_git2_` suffix. * Create and open a temporary file with a `_git2_` suffix.
......
...@@ -82,7 +82,7 @@ int git_iterator_for_nothing(git_iterator **iter) ...@@ -82,7 +82,7 @@ int git_iterator_for_nothing(git_iterator **iter)
typedef struct tree_iterator_frame tree_iterator_frame; typedef struct tree_iterator_frame tree_iterator_frame;
struct tree_iterator_frame { struct tree_iterator_frame {
tree_iterator_frame *next; tree_iterator_frame *next, *prev;
git_tree *tree; git_tree *tree;
char *start; char *start;
unsigned int index; unsigned int index;
...@@ -91,7 +91,7 @@ struct tree_iterator_frame { ...@@ -91,7 +91,7 @@ struct tree_iterator_frame {
typedef struct { typedef struct {
git_iterator base; git_iterator base;
git_repository *repo; git_repository *repo;
tree_iterator_frame *stack; tree_iterator_frame *stack, *tail;
git_index_entry entry; git_index_entry entry;
git_buf path; git_buf path;
bool path_has_filename; bool path_has_filename;
...@@ -119,8 +119,10 @@ static void tree_iterator__pop_frame(tree_iterator *ti) ...@@ -119,8 +119,10 @@ static void tree_iterator__pop_frame(tree_iterator *ti)
{ {
tree_iterator_frame *tf = ti->stack; tree_iterator_frame *tf = ti->stack;
ti->stack = tf->next; ti->stack = tf->next;
if (ti->stack != NULL) /* don't free the initial tree */ if (ti->stack != NULL) {
git_tree_free(tf->tree); git_tree_free(tf->tree); /* don't free the initial tree */
ti->stack->prev = NULL; /* disconnect prev */
}
git__free(tf); git__free(tf);
} }
...@@ -221,6 +223,7 @@ static int tree_iterator__expand_tree(tree_iterator *ti) ...@@ -221,6 +223,7 @@ static int tree_iterator__expand_tree(tree_iterator *ti)
tf->next = ti->stack; tf->next = ti->stack;
ti->stack = tf; ti->stack = tf;
tf->next->prev = tf;
te = tree_iterator__tree_entry(ti); te = tree_iterator__tree_entry(ti);
} }
...@@ -312,7 +315,7 @@ int git_iterator_for_tree_range( ...@@ -312,7 +315,7 @@ int git_iterator_for_tree_range(
ITERATOR_BASE_INIT(ti, tree, TREE); ITERATOR_BASE_INIT(ti, tree, TREE);
ti->repo = repo; ti->repo = repo;
ti->stack = tree_iterator__alloc_frame(tree, ti->base.start); ti->stack = ti->tail = tree_iterator__alloc_frame(tree, ti->base.start);
if ((error = tree_iterator__expand_tree(ti)) < 0) if ((error = tree_iterator__expand_tree(ti)) < 0)
git_iterator_free((git_iterator *)ti); git_iterator_free((git_iterator *)ti);
...@@ -864,6 +867,45 @@ int git_iterator_current_tree_entry( ...@@ -864,6 +867,45 @@ int git_iterator_current_tree_entry(
return 0; return 0;
} }
int git_iterator_current_parent_tree(
git_iterator *iter,
const char *parent_path,
const git_tree **tree_ptr)
{
tree_iterator *ti = (tree_iterator *)iter;
tree_iterator_frame *tf;
const char *scan = parent_path;
if (iter->type != GIT_ITERATOR_TREE || ti->stack == NULL)
goto notfound;
for (tf = ti->tail; tf != NULL; tf = tf->prev) {
const git_tree_entry *te;
if (!*scan) {
*tree_ptr = tf->tree;
return 0;
}
te = git_tree_entry_byindex(tf->tree, tf->index);
if (strncmp(scan, te->filename, te->filename_len) != 0)
goto notfound;
scan += te->filename_len;
if (*scan) {
if (*scan != '/')
goto notfound;
scan++;
}
}
notfound:
*tree_ptr = NULL;
return 0;
}
int git_iterator_current_is_ignored(git_iterator *iter) int git_iterator_current_is_ignored(git_iterator *iter)
{ {
return (iter->type != GIT_ITERATOR_WORKDIR) ? 0 : return (iter->type != GIT_ITERATOR_WORKDIR) ? 0 :
......
...@@ -142,6 +142,9 @@ GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter) ...@@ -142,6 +142,9 @@ GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)
extern int git_iterator_current_tree_entry( extern int git_iterator_current_tree_entry(
git_iterator *iter, const git_tree_entry **tree_entry); git_iterator *iter, const git_tree_entry **tree_entry);
extern int git_iterator_current_parent_tree(
git_iterator *iter, const char *parent_path, const git_tree **tree_ptr);
extern int git_iterator_current_is_ignored(git_iterator *iter); extern int git_iterator_current_is_ignored(git_iterator *iter);
/** /**
......
...@@ -372,7 +372,7 @@ int git_reflog_rename(git_reference *ref, const char *new_name) ...@@ -372,7 +372,7 @@ int git_reflog_rename(git_reference *ref, const char *new_name)
goto cleanup; goto cleanup;
if (git_path_isdir(git_buf_cstr(&new_path)) && if (git_path_isdir(git_buf_cstr(&new_path)) &&
(git_futils_rmdir_r(git_buf_cstr(&new_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0))
goto cleanup; goto cleanup;
if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0)
......
...@@ -280,7 +280,8 @@ static int loose_write(git_reference *ref) ...@@ -280,7 +280,8 @@ static int loose_write(git_reference *ref)
* which name would collide with the reference name * which name would collide with the reference name
*/ */
if (git_path_isdir(git_buf_cstr(&ref_path)) && if (git_path_isdir(git_buf_cstr(&ref_path)) &&
(git_futils_rmdir_r(git_buf_cstr(&ref_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) { git_futils_rmdir_r(git_buf_cstr(&ref_path), NULL,
GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0) {
git_buf_free(&ref_path); git_buf_free(&ref_path);
return -1; return -1;
} }
......
...@@ -25,7 +25,6 @@ static unsigned int index_delta2status(git_delta_t index_status) ...@@ -25,7 +25,6 @@ static unsigned int index_delta2status(git_delta_t index_status)
switch (index_status) { switch (index_status) {
case GIT_DELTA_ADDED: case GIT_DELTA_ADDED:
case GIT_DELTA_COPIED: case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED:
st = GIT_STATUS_INDEX_NEW; st = GIT_STATUS_INDEX_NEW;
break; break;
case GIT_DELTA_DELETED: case GIT_DELTA_DELETED:
...@@ -34,6 +33,12 @@ static unsigned int index_delta2status(git_delta_t index_status) ...@@ -34,6 +33,12 @@ static unsigned int index_delta2status(git_delta_t index_status)
case GIT_DELTA_MODIFIED: case GIT_DELTA_MODIFIED:
st = GIT_STATUS_INDEX_MODIFIED; st = GIT_STATUS_INDEX_MODIFIED;
break; break;
case GIT_DELTA_RENAMED:
st = GIT_STATUS_INDEX_RENAMED;
break;
case GIT_DELTA_TYPECHANGE:
st = GIT_STATUS_INDEX_TYPECHANGE;
break;
default: default:
break; break;
} }
...@@ -47,8 +52,8 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) ...@@ -47,8 +52,8 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
switch (workdir_status) { switch (workdir_status) {
case GIT_DELTA_ADDED: case GIT_DELTA_ADDED:
case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED: case GIT_DELTA_RENAMED:
case GIT_DELTA_COPIED:
case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNTRACKED:
st = GIT_STATUS_WT_NEW; st = GIT_STATUS_WT_NEW;
break; break;
...@@ -61,6 +66,9 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) ...@@ -61,6 +66,9 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
case GIT_DELTA_IGNORED: case GIT_DELTA_IGNORED:
st = GIT_STATUS_IGNORED; st = GIT_STATUS_IGNORED;
break; break;
case GIT_DELTA_TYPECHANGE:
st = GIT_STATUS_WT_TYPECHANGE;
break;
default: default:
break; break;
} }
...@@ -96,6 +104,8 @@ int git_status_foreach_ext( ...@@ -96,6 +104,8 @@ int git_status_foreach_ext(
memset(&diffopt, 0, sizeof(diffopt)); memset(&diffopt, 0, sizeof(diffopt));
memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0) if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED; diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0) if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
......
...@@ -180,9 +180,9 @@ void git_tree__free(git_tree *tree) ...@@ -180,9 +180,9 @@ void git_tree__free(git_tree *tree)
git__free(tree); git__free(tree);
} }
const git_oid *git_tree_id(git_tree *c) const git_oid *git_tree_id(const git_tree *c)
{ {
return git_object_id((git_object *)c); return git_object_id((const git_object *)c);
} }
git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry) git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry)
...@@ -286,7 +286,7 @@ int git_tree__prefix_position(git_tree *tree, const char *path) ...@@ -286,7 +286,7 @@ int git_tree__prefix_position(git_tree *tree, const char *path)
return at_pos; return at_pos;
} }
unsigned int git_tree_entrycount(git_tree *tree) unsigned int git_tree_entrycount(const git_tree *tree)
{ {
assert(tree); assert(tree);
return (unsigned int)tree->entries.length; return (unsigned int)tree->entries.length;
......
#include "clar_libgit2.h"
#include "git2/checkout.h"
#include "path.h"
#include "posix.h"
static git_repository *g_repo = NULL;
static const char *g_typechange_oids[] = {
"79b9f23e85f55ea36a472a902e875bc1121a94cb",
"9bdb75b73836a99e3dbeea640a81de81031fdc29",
"0e7ed140b514b8cae23254cb8656fe1674403aff",
"9d0235c7a7edc0889a18f97a42ee6db9fe688447",
"9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a",
"1b63caae4a5ca96f78e8dfefc376c6a39a142475",
"6eae26c90e8ccc4d16208972119c40635489c6f0",
NULL
};
static bool g_typechange_empty[] = {
true, false, false, false, false, false, true, true
};
void test_checkout_typechange__initialize(void)
{
g_repo = cl_git_sandbox_init("typechanges");
cl_fixture_sandbox("submod2_target");
p_rename("submod2_target/.gitted", "submod2_target/.git");
}
void test_checkout_typechange__cleanup(void)
{
cl_git_sandbox_cleanup();
cl_fixture_cleanup("submod2_target");
}
void test_checkout_typechange__checkout_typechanges(void)
{
int i;
git_object *obj;
git_checkout_opts opts = {0};
opts.checkout_strategy =
GIT_CHECKOUT_REMOVE_UNTRACKED |
GIT_CHECKOUT_CREATE_MISSING |
GIT_CHECKOUT_OVERWRITE_MODIFIED;
for (i = 0; g_typechange_oids[i] != NULL; ++i) {
cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
/* fprintf(stderr, "checking out '%s'\n", g_typechange_oids[i]); */
cl_git_pass(git_checkout_tree(g_repo, obj, &opts, NULL));
git_object_free(obj);
if (!g_typechange_empty[i]) {
cl_assert(git_path_isdir("typechanges"));
cl_assert(git_path_exists("typechanges/a"));
cl_assert(git_path_exists("typechanges/b"));
cl_assert(git_path_exists("typechanges/c"));
cl_assert(git_path_exists("typechanges/d"));
cl_assert(git_path_exists("typechanges/e"));
} else {
cl_assert(git_path_isdir("typechanges"));
cl_assert(!git_path_exists("typechanges/a"));
cl_assert(!git_path_exists("typechanges/b"));
cl_assert(!git_path_exists("typechanges/c"));
cl_assert(!git_path_exists("typechanges/d"));
cl_assert(!git_path_exists("typechanges/e"));
}
}
}
...@@ -41,7 +41,7 @@ void test_core_copy__file_in_dir(void) ...@@ -41,7 +41,7 @@ void test_core_copy__file_in_dir(void)
cl_assert(S_ISREG(st.st_mode)); cl_assert(S_ISREG(st.st_mode));
cl_assert(strlen(content) == (size_t)st.st_size); cl_assert(strlen(content) == (size_t)st.st_size);
cl_git_pass(git_futils_rmdir_r("an_dir", GIT_DIRREMOVAL_FILES_AND_DIRS)); cl_git_pass(git_futils_rmdir_r("an_dir", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_assert(!git_path_isdir("an_dir")); cl_assert(!git_path_isdir("an_dir"));
} }
...@@ -95,7 +95,7 @@ void test_core_copy__tree(void) ...@@ -95,7 +95,7 @@ void test_core_copy__tree(void)
cl_assert(S_ISLNK(st.st_mode)); cl_assert(S_ISLNK(st.st_mode));
#endif #endif
cl_git_pass(git_futils_rmdir_r("t1", GIT_DIRREMOVAL_FILES_AND_DIRS)); cl_git_pass(git_futils_rmdir_r("t1", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_assert(!git_path_isdir("t1")); cl_assert(!git_path_isdir("t1"));
/* copy with empty dirs, no links, yes dotfiles, no overwrite */ /* copy with empty dirs, no links, yes dotfiles, no overwrite */
...@@ -119,8 +119,8 @@ void test_core_copy__tree(void) ...@@ -119,8 +119,8 @@ void test_core_copy__tree(void)
cl_git_fail(git_path_lstat("t2/c/d/l1", &st)); cl_git_fail(git_path_lstat("t2/c/d/l1", &st));
#endif #endif
cl_git_pass(git_futils_rmdir_r("t2", GIT_DIRREMOVAL_FILES_AND_DIRS)); cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_assert(!git_path_isdir("t2")); cl_assert(!git_path_isdir("t2"));
cl_git_pass(git_futils_rmdir_r("src", GIT_DIRREMOVAL_FILES_AND_DIRS)); cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
} }
...@@ -6,11 +6,11 @@ ...@@ -6,11 +6,11 @@
static void cleanup_basic_dirs(void *ref) static void cleanup_basic_dirs(void *ref)
{ {
GIT_UNUSED(ref); GIT_UNUSED(ref);
git_futils_rmdir_r("d0", GIT_DIRREMOVAL_EMPTY_HIERARCHY); git_futils_rmdir_r("d0", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d1", GIT_DIRREMOVAL_EMPTY_HIERARCHY); git_futils_rmdir_r("d1", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d2", GIT_DIRREMOVAL_EMPTY_HIERARCHY); git_futils_rmdir_r("d2", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d3", GIT_DIRREMOVAL_EMPTY_HIERARCHY); git_futils_rmdir_r("d3", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
git_futils_rmdir_r("d4", GIT_DIRREMOVAL_EMPTY_HIERARCHY); git_futils_rmdir_r("d4", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
} }
void test_core_mkdir__basic(void) void test_core_mkdir__basic(void)
...@@ -56,7 +56,7 @@ void test_core_mkdir__basic(void) ...@@ -56,7 +56,7 @@ void test_core_mkdir__basic(void)
static void cleanup_basedir(void *ref) static void cleanup_basedir(void *ref)
{ {
GIT_UNUSED(ref); GIT_UNUSED(ref);
git_futils_rmdir_r("base", GIT_DIRREMOVAL_EMPTY_HIERARCHY); git_futils_rmdir_r("base", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
} }
void test_core_mkdir__with_base(void) void test_core_mkdir__with_base(void)
...@@ -108,7 +108,7 @@ static void cleanup_chmod_root(void *ref) ...@@ -108,7 +108,7 @@ static void cleanup_chmod_root(void *ref)
git__free(mode); git__free(mode);
} }
git_futils_rmdir_r("r", GIT_DIRREMOVAL_EMPTY_HIERARCHY); git_futils_rmdir_r("r", NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY);
} }
static void check_mode(mode_t expected, mode_t actual) static void check_mode(mode_t expected, mode_t actual)
......
...@@ -30,7 +30,7 @@ void test_core_rmdir__initialize(void) ...@@ -30,7 +30,7 @@ void test_core_rmdir__initialize(void)
/* make sure empty dir can be deleted recusively */ /* make sure empty dir can be deleted recusively */
void test_core_rmdir__delete_recursive(void) void test_core_rmdir__delete_recursive(void)
{ {
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_EMPTY_HIERARCHY)); cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY));
} }
/* make sure non-empty dir cannot be deleted recusively */ /* make sure non-empty dir cannot be deleted recusively */
...@@ -42,10 +42,10 @@ void test_core_rmdir__fail_to_delete_non_empty_dir(void) ...@@ -42,10 +42,10 @@ void test_core_rmdir__fail_to_delete_non_empty_dir(void)
cl_git_mkfile(git_buf_cstr(&file), "dummy"); cl_git_mkfile(git_buf_cstr(&file), "dummy");
cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_EMPTY_HIERARCHY)); cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY));
cl_must_pass(p_unlink(file.ptr)); cl_must_pass(p_unlink(file.ptr));
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_EMPTY_HIERARCHY)); cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_EMPTY_HIERARCHY));
git_buf_free(&file); git_buf_free(&file);
} }
...@@ -58,10 +58,10 @@ void test_core_rmdir__can_skip__non_empty_dir(void) ...@@ -58,10 +58,10 @@ void test_core_rmdir__can_skip__non_empty_dir(void)
cl_git_mkfile(git_buf_cstr(&file), "dummy"); cl_git_mkfile(git_buf_cstr(&file), "dummy");
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS)); cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS));
cl_assert(git_path_exists(git_buf_cstr(&file)) == true); cl_assert(git_path_exists(git_buf_cstr(&file)) == true);
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_FILES_AND_DIRS)); cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
cl_assert(git_path_exists(empty_tmp_dir) == false); cl_assert(git_path_exists(empty_tmp_dir) == false);
git_buf_free(&file); git_buf_free(&file);
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "diff_helpers.h" #include "diff_helpers.h"
#include "iterator.h" #include "iterator.h"
#include "tree.h"
void test_diff_iterator__initialize(void) void test_diff_iterator__initialize(void)
{ {
...@@ -237,6 +238,103 @@ void test_diff_iterator__tree_range_empty_2(void) ...@@ -237,6 +238,103 @@ void test_diff_iterator__tree_range_empty_2(void)
NULL, ".aaa_empty_before", 0, NULL); NULL, ".aaa_empty_before", 0, NULL);
} }
static void check_tree_entry(
git_iterator *i,
const char *oid,
const char *oid_p,
const char *oid_pp,
const char *oid_ppp)
{
const git_index_entry *ie;
const git_tree_entry *te;
const git_tree *tree;
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_iterator_current_tree_entry(i, &te));
cl_assert(te);
cl_assert(git_oid_streq(&te->oid, oid) == 0);
cl_git_pass(git_iterator_current(i, &ie));
cl_git_pass(git_buf_sets(&path, ie->path));
if (oid_p) {
git_buf_rtruncate_at_char(&path, '/');
cl_git_pass(git_iterator_current_parent_tree(i, path.ptr, &tree));
cl_assert(tree);
cl_assert(git_oid_streq(git_tree_id(tree), oid_p) == 0);
}
if (oid_pp) {
git_buf_rtruncate_at_char(&path, '/');
cl_git_pass(git_iterator_current_parent_tree(i, path.ptr, &tree));
cl_assert(tree);
cl_assert(git_oid_streq(git_tree_id(tree), oid_pp) == 0);
}
if (oid_ppp) {
git_buf_rtruncate_at_char(&path, '/');
cl_git_pass(git_iterator_current_parent_tree(i, path.ptr, &tree));
cl_assert(tree);
cl_assert(git_oid_streq(git_tree_id(tree), oid_ppp) == 0);
}
git_buf_free(&path);
}
void test_diff_iterator__tree_special_functions(void)
{
git_tree *t;
git_iterator *i;
const git_index_entry *entry;
git_repository *repo = cl_git_sandbox_init("attr");
int cases = 0;
const char *rootoid = "ce39a97a7fb1fa90bcf5e711249c1e507476ae0e";
t = resolve_commit_oid_to_tree(
repo, "24fa9a9fc4e202313e24b648087495441dab432b");
cl_assert(t != NULL);
cl_git_pass(git_iterator_for_tree_range(&i, repo, t, NULL, NULL));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
if (strcmp(entry->path, "sub/file") == 0) {
cases++;
check_tree_entry(
i, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
"ecb97df2a174987475ac816e3847fc8e9f6c596b",
rootoid, NULL);
}
else if (strcmp(entry->path, "sub/sub/subsub.txt") == 0) {
cases++;
check_tree_entry(
i, "9e5bdc47d6a80f2be0ea3049ad74231b94609242",
"4e49ba8c5b6c32ff28cd9dcb60be34df50fcc485",
"ecb97df2a174987475ac816e3847fc8e9f6c596b", rootoid);
}
else if (strcmp(entry->path, "subdir/.gitattributes") == 0) {
cases++;
check_tree_entry(
i, "99eae476896f4907224978b88e5ecaa6c5bb67a9",
"9fb40b6675dde60b5697afceae91b66d908c02d9",
rootoid, NULL);
}
else if (strcmp(entry->path, "subdir2/subdir2_test1") == 0) {
cases++;
check_tree_entry(
i, "dccada462d3df8ac6de596fb8c896aba9344f941",
"2929de282ce999e95183aedac6451d3384559c4b",
rootoid, NULL);
}
cl_git_pass(git_iterator_advance(i, &entry));
}
cl_assert_equal_i(4, cases);
git_iterator_free(i);
git_tree_free(t);
}
/* -- INDEX ITERATOR TESTS -- */ /* -- INDEX ITERATOR TESTS -- */
static void index_iterator_test( static void index_iterator_test(
......
...@@ -49,7 +49,7 @@ void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_absolut ...@@ -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); assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk);
git_buf_free(&full_path); git_buf_free(&full_path);
cl_must_pass(git_futils_rmdir_r(ELSEWHERE, GIT_DIRREMOVAL_FILES_AND_DIRS)); cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
} }
void test_object_blob_write__can_create_a_blob_in_a_bare_repo_from_a_absolute_filepath(void) 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 ...@@ -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); assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk);
git_buf_free(&full_path); git_buf_free(&full_path);
cl_must_pass(git_futils_rmdir_r(ELSEWHERE, GIT_DIRREMOVAL_FILES_AND_DIRS)); cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
} }
...@@ -135,7 +135,7 @@ void test_repo_discover__0(void) ...@@ -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, ceiling_dirs, sub_repository_path);
ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, 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, GIT_DIRREMOVAL_FILES_AND_DIRS)); cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
git_repository_free(repo); git_repository_free(repo);
git_buf_free(&ceiling_dirs_buf); git_buf_free(&ceiling_dirs_buf);
} }
......
...@@ -7,7 +7,7 @@ void test_repo_open__cleanup(void) ...@@ -7,7 +7,7 @@ void test_repo_open__cleanup(void)
cl_git_sandbox_cleanup(); cl_git_sandbox_cleanup();
if (git_path_isdir("alternate")) if (git_path_isdir("alternate"))
git_futils_rmdir_r("alternate", GIT_DIRREMOVAL_FILES_AND_DIRS); git_futils_rmdir_r("alternate", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
} }
void test_repo_open__bare_empty_repo(void) void test_repo_open__bare_empty_repo(void)
...@@ -202,8 +202,8 @@ void test_repo_open__bad_gitlinks(void) ...@@ -202,8 +202,8 @@ void test_repo_open__bad_gitlinks(void)
cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL)); cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
} }
git_futils_rmdir_r("invalid", GIT_DIRREMOVAL_FILES_AND_DIRS); git_futils_rmdir_r("invalid", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
git_futils_rmdir_r("invalid2", GIT_DIRREMOVAL_FILES_AND_DIRS); git_futils_rmdir_r("invalid2", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
} }
#ifdef GIT_WIN32 #ifdef GIT_WIN32
......
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
[submodule "e"]
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
[submodule "d"]
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
[submodule "b"]
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
Unnamed repository; edit this file 'description' to name the repository.
# 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]
# *~
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
worktree = ../../../b
ignorecase = true
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
[branch "master"]
remote = origin
merge = refs/heads/master
Unnamed repository; edit this file 'description' to name the repository.
# 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]
# *~
# pack-refs with: peeled
480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
worktree = ../../../d
ignorecase = true
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
[branch "master"]
remote = origin
merge = refs/heads/master
Unnamed repository; edit this file 'description' to name the repository.
# 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]
# *~
# pack-refs with: peeled
480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
worktree = ../../../e
ignorecase = true
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
[branch "master"]
remote = origin
merge = refs/heads/master
Unnamed repository; edit this file 'description' to name the repository.
# 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]
# *~
# pack-refs with: peeled
480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
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