Merge branch 'ap/clone-into-empty'
* ap/clone-into-empty: Allow cloning to an existing empty directory add is_dot_or_dotdot inline function
This commit is contained in:
commit
6af384ce73
@ -357,6 +357,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||||||
struct stat buf;
|
struct stat buf;
|
||||||
const char *repo_name, *repo, *work_tree, *git_dir;
|
const char *repo_name, *repo, *work_tree, *git_dir;
|
||||||
char *path, *dir;
|
char *path, *dir;
|
||||||
|
int dest_exists;
|
||||||
const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
|
const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
|
||||||
struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
|
struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
|
||||||
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
|
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
|
||||||
@ -406,8 +407,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||||||
dir = guess_dir_name(repo_name, is_bundle, option_bare);
|
dir = guess_dir_name(repo_name, is_bundle, option_bare);
|
||||||
strip_trailing_slashes(dir);
|
strip_trailing_slashes(dir);
|
||||||
|
|
||||||
if (!stat(dir, &buf))
|
dest_exists = !stat(dir, &buf);
|
||||||
die("destination directory '%s' already exists.", dir);
|
if (dest_exists && !is_empty_dir(dir))
|
||||||
|
die("destination path '%s' already exists and is not "
|
||||||
|
"an empty directory.", dir);
|
||||||
|
|
||||||
strbuf_addf(&reflog_msg, "clone: from %s", repo);
|
strbuf_addf(&reflog_msg, "clone: from %s", repo);
|
||||||
|
|
||||||
@ -431,7 +434,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||||||
if (safe_create_leading_directories_const(work_tree) < 0)
|
if (safe_create_leading_directories_const(work_tree) < 0)
|
||||||
die("could not create leading directories of '%s': %s",
|
die("could not create leading directories of '%s': %s",
|
||||||
work_tree, strerror(errno));
|
work_tree, strerror(errno));
|
||||||
if (mkdir(work_tree, 0755))
|
if (!dest_exists && mkdir(work_tree, 0755))
|
||||||
die("could not create work tree dir '%s': %s.",
|
die("could not create work tree dir '%s': %s.",
|
||||||
work_tree, strerror(errno));
|
work_tree, strerror(errno));
|
||||||
set_git_work_tree(work_tree);
|
set_git_work_tree(work_tree);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "dir.h"
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
|
|
||||||
@ -21,9 +22,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
|
|||||||
const char *cp;
|
const char *cp;
|
||||||
int bad = 0;
|
int bad = 0;
|
||||||
|
|
||||||
if ((ent->d_name[0] == '.') &&
|
if (is_dot_or_dotdot(ent->d_name))
|
||||||
(ent->d_name[1] == 0 ||
|
|
||||||
((ent->d_name[1] == '.') && (ent->d_name[2] == 0))))
|
|
||||||
continue;
|
continue;
|
||||||
for (cp = ent->d_name; *cp; cp++) {
|
for (cp = ent->d_name; *cp; cp++) {
|
||||||
int ch = *cp;
|
int ch = *cp;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "tree-walk.h"
|
#include "tree-walk.h"
|
||||||
#include "fsck.h"
|
#include "fsck.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
|
#include "dir.h"
|
||||||
|
|
||||||
#define REACHABLE 0x0001
|
#define REACHABLE 0x0001
|
||||||
#define SEEN 0x0002
|
#define SEEN 0x0002
|
||||||
@ -395,19 +396,12 @@ static void fsck_dir(int i, char *path)
|
|||||||
while ((de = readdir(dir)) != NULL) {
|
while ((de = readdir(dir)) != NULL) {
|
||||||
char name[100];
|
char name[100];
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
int len = strlen(de->d_name);
|
|
||||||
|
|
||||||
switch (len) {
|
if (is_dot_or_dotdot(de->d_name))
|
||||||
case 2:
|
|
||||||
if (de->d_name[1] != '.')
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (de->d_name[0] != '.')
|
|
||||||
break;
|
|
||||||
continue;
|
continue;
|
||||||
case 38:
|
if (strlen(de->d_name) == 38) {
|
||||||
sprintf(name, "%02x", i);
|
sprintf(name, "%02x", i);
|
||||||
memcpy(name+2, de->d_name, len+1);
|
memcpy(name+2, de->d_name, 39);
|
||||||
if (get_sha1_hex(name, sha1) < 0)
|
if (get_sha1_hex(name, sha1) < 0)
|
||||||
break;
|
break;
|
||||||
add_sha1_list(sha1, DIRENT_SORT_HINT(de));
|
add_sha1_list(sha1, DIRENT_SORT_HINT(de));
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "reachable.h"
|
#include "reachable.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
|
#include "dir.h"
|
||||||
|
|
||||||
static const char * const prune_usage[] = {
|
static const char * const prune_usage[] = {
|
||||||
"git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
|
"git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
|
||||||
@ -61,19 +62,12 @@ static int prune_dir(int i, char *path)
|
|||||||
while ((de = readdir(dir)) != NULL) {
|
while ((de = readdir(dir)) != NULL) {
|
||||||
char name[100];
|
char name[100];
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
int len = strlen(de->d_name);
|
|
||||||
|
|
||||||
switch (len) {
|
if (is_dot_or_dotdot(de->d_name))
|
||||||
case 2:
|
|
||||||
if (de->d_name[1] != '.')
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (de->d_name[0] != '.')
|
|
||||||
break;
|
|
||||||
continue;
|
continue;
|
||||||
case 38:
|
if (strlen(de->d_name) == 38) {
|
||||||
sprintf(name, "%02x", i);
|
sprintf(name, "%02x", i);
|
||||||
memcpy(name+2, de->d_name, len+1);
|
memcpy(name+2, de->d_name, 39);
|
||||||
if (get_sha1_hex(name, sha1) < 0)
|
if (get_sha1_hex(name, sha1) < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "dir.h"
|
||||||
#include "string-list.h"
|
#include "string-list.h"
|
||||||
#include "rerere.h"
|
#include "rerere.h"
|
||||||
#include "xdiff/xdiff.h"
|
#include "xdiff/xdiff.h"
|
||||||
@ -59,17 +60,15 @@ static void garbage_collect(struct string_list *rr)
|
|||||||
git_config(git_rerere_gc_config, NULL);
|
git_config(git_rerere_gc_config, NULL);
|
||||||
dir = opendir(git_path("rr-cache"));
|
dir = opendir(git_path("rr-cache"));
|
||||||
while ((e = readdir(dir))) {
|
while ((e = readdir(dir))) {
|
||||||
const char *name = e->d_name;
|
if (is_dot_or_dotdot(e->d_name))
|
||||||
if (name[0] == '.' &&
|
|
||||||
(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
|
|
||||||
continue;
|
continue;
|
||||||
then = rerere_created_at(name);
|
then = rerere_created_at(e->d_name);
|
||||||
if (!then)
|
if (!then)
|
||||||
continue;
|
continue;
|
||||||
cutoff = (has_resolution(name)
|
cutoff = (has_resolution(e->d_name)
|
||||||
? cutoff_resolve : cutoff_noresolve);
|
? cutoff_resolve : cutoff_noresolve);
|
||||||
if (then < now - cutoff * 86400)
|
if (then < now - cutoff * 86400)
|
||||||
string_list_append(name, &to_remove);
|
string_list_append(e->d_name, &to_remove);
|
||||||
}
|
}
|
||||||
for (i = 0; i < to_remove.nr; i++)
|
for (i = 0; i < to_remove.nr; i++)
|
||||||
unlink_rr_item(to_remove.items[i].string);
|
unlink_rr_item(to_remove.items[i].string);
|
||||||
|
31
dir.c
31
dir.c
@ -585,10 +585,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
|
|||||||
int len, dtype;
|
int len, dtype;
|
||||||
int exclude;
|
int exclude;
|
||||||
|
|
||||||
if ((de->d_name[0] == '.') &&
|
if (is_dot_or_dotdot(de->d_name) ||
|
||||||
(de->d_name[1] == 0 ||
|
!strcmp(de->d_name, ".git"))
|
||||||
!strcmp(de->d_name + 1, ".") ||
|
|
||||||
!strcmp(de->d_name + 1, "git")))
|
|
||||||
continue;
|
continue;
|
||||||
len = strlen(de->d_name);
|
len = strlen(de->d_name);
|
||||||
/* Ignore overly long pathnames! */
|
/* Ignore overly long pathnames! */
|
||||||
@ -779,6 +777,25 @@ int is_inside_dir(const char *dir)
|
|||||||
return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
|
return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int is_empty_dir(const char *path)
|
||||||
|
{
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
struct dirent *e;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
if (!dir)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while ((e = readdir(dir)) != NULL)
|
||||||
|
if (!is_dot_or_dotdot(e->d_name)) {
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int remove_dir_recursively(struct strbuf *path, int only_empty)
|
int remove_dir_recursively(struct strbuf *path, int only_empty)
|
||||||
{
|
{
|
||||||
DIR *dir = opendir(path->buf);
|
DIR *dir = opendir(path->buf);
|
||||||
@ -793,10 +810,8 @@ int remove_dir_recursively(struct strbuf *path, int only_empty)
|
|||||||
len = path->len;
|
len = path->len;
|
||||||
while ((e = readdir(dir)) != NULL) {
|
while ((e = readdir(dir)) != NULL) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if ((e->d_name[0] == '.') &&
|
if (is_dot_or_dotdot(e->d_name))
|
||||||
((e->d_name[1] == 0) ||
|
continue;
|
||||||
((e->d_name[1] == '.') && e->d_name[2] == 0)))
|
|
||||||
continue; /* "." and ".." */
|
|
||||||
|
|
||||||
strbuf_setlen(path, len);
|
strbuf_setlen(path, len);
|
||||||
strbuf_addstr(path, e->d_name);
|
strbuf_addstr(path, e->d_name);
|
||||||
|
9
dir.h
9
dir.h
@ -77,6 +77,15 @@ extern int file_exists(const char *);
|
|||||||
extern char *get_relative_cwd(char *buffer, int size, const char *dir);
|
extern char *get_relative_cwd(char *buffer, int size, const char *dir);
|
||||||
extern int is_inside_dir(const char *dir);
|
extern int is_inside_dir(const char *dir);
|
||||||
|
|
||||||
|
static inline int is_dot_or_dotdot(const char *name)
|
||||||
|
{
|
||||||
|
return (name[0] == '.' &&
|
||||||
|
(name[1] == '\0' ||
|
||||||
|
(name[1] == '.' && name[2] == '\0')));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int is_empty_dir(const char *dir);
|
||||||
|
|
||||||
extern void setup_standard_excludes(struct dir_struct *dir);
|
extern void setup_standard_excludes(struct dir_struct *dir);
|
||||||
extern int remove_dir_recursively(struct strbuf *path, int only_empty);
|
extern int remove_dir_recursively(struct strbuf *path, int only_empty);
|
||||||
|
|
||||||
|
5
entry.c
5
entry.c
@ -1,5 +1,6 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
|
#include "dir.h"
|
||||||
|
|
||||||
static void create_directories(const char *path, const struct checkout *state)
|
static void create_directories(const char *path, const struct checkout *state)
|
||||||
{
|
{
|
||||||
@ -62,9 +63,7 @@ static void remove_subtree(const char *path)
|
|||||||
*name++ = '/';
|
*name++ = '/';
|
||||||
while ((de = readdir(dir)) != NULL) {
|
while ((de = readdir(dir)) != NULL) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if ((de->d_name[0] == '.') &&
|
if (is_dot_or_dotdot(de->d_name))
|
||||||
((de->d_name[1] == 0) ||
|
|
||||||
((de->d_name[1] == '.') && de->d_name[2] == 0)))
|
|
||||||
continue;
|
continue;
|
||||||
strcpy(name, de->d_name);
|
strcpy(name, de->d_name);
|
||||||
if (lstat(pathbuf, &st))
|
if (lstat(pathbuf, &st))
|
||||||
|
6
remote.c
6
remote.c
@ -4,6 +4,7 @@
|
|||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
|
#include "dir.h"
|
||||||
|
|
||||||
static struct refspec s_tag_refspec = {
|
static struct refspec s_tag_refspec = {
|
||||||
0,
|
0,
|
||||||
@ -634,10 +635,7 @@ static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
|
|||||||
|
|
||||||
static int valid_remote_nick(const char *name)
|
static int valid_remote_nick(const char *name)
|
||||||
{
|
{
|
||||||
if (!name[0] || /* not empty */
|
if (!name[0] || is_dot_or_dotdot(name))
|
||||||
(name[0] == '.' && /* not "." */
|
|
||||||
(!name[1] || /* not ".." */
|
|
||||||
(name[1] == '.' && !name[2]))))
|
|
||||||
return 0;
|
return 0;
|
||||||
return !strchr(name, '/'); /* no slash */
|
return !strchr(name, '/'); /* no slash */
|
||||||
}
|
}
|
||||||
|
@ -125,4 +125,23 @@ test_expect_success 'clone to destination with extra trailing /' '
|
|||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'clone to an existing empty directory' '
|
||||||
|
mkdir target-3 &&
|
||||||
|
git clone src target-3 &&
|
||||||
|
T=$( cd target-3 && git rev-parse HEAD ) &&
|
||||||
|
S=$( cd src && git rev-parse HEAD ) &&
|
||||||
|
test "$T" = "$S"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'clone to an existing non-empty directory' '
|
||||||
|
mkdir target-4 &&
|
||||||
|
>target-4/Fakefile &&
|
||||||
|
test_must_fail git clone src target-4
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'clone to an existing path' '
|
||||||
|
>target-5 &&
|
||||||
|
test_must_fail git clone src target-5
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -50,9 +50,7 @@ static int read_loose_refs(struct strbuf *path, int name_offset,
|
|||||||
memset (&list, 0, sizeof(list));
|
memset (&list, 0, sizeof(list));
|
||||||
|
|
||||||
while ((de = readdir(dir))) {
|
while ((de = readdir(dir))) {
|
||||||
if (de->d_name[0] == '.' && (de->d_name[1] == '\0' ||
|
if (is_dot_or_dotdot(de->d_name))
|
||||||
(de->d_name[1] == '.' &&
|
|
||||||
de->d_name[2] == '\0')))
|
|
||||||
continue;
|
continue;
|
||||||
ALLOC_GROW(list.entries, list.nr + 1, list.alloc);
|
ALLOC_GROW(list.entries, list.nr + 1, list.alloc);
|
||||||
list.entries[list.nr++] = xstrdup(de->d_name);
|
list.entries[list.nr++] = xstrdup(de->d_name);
|
||||||
|
Loading…
Reference in New Issue
Block a user