Skip to content

Commit b72f5d4

Browse files
author
Vicent Martí
committed
Merge pull request libgit2#1369 from arrbee/repo-init-template-hooks
More tests (and fixes) for initializing repo from template
2 parents 3d74702 + 18f0826 commit b72f5d4

File tree

9 files changed

+332
-97
lines changed

9 files changed

+332
-97
lines changed

src/fileops.c

Lines changed: 80 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ int git_futils_mkdir(
272272
}
273273

274274
/* if we are not supposed to made the last element, truncate it */
275+
if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
276+
git_buf_rtruncate_at_char(&make_path, '/');
277+
flags |= GIT_MKDIR_SKIP_LAST;
278+
}
275279
if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
276280
git_buf_rtruncate_at_char(&make_path, '/');
277281

@@ -303,34 +307,34 @@ int git_futils_mkdir(
303307
int already_exists = 0;
304308

305309
switch (errno) {
306-
case EEXIST:
307-
if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
308-
!git_path_isdir(make_path.ptr)) {
309-
giterr_set(
310-
GITERR_OS, "Existing path is not a directory '%s'",
311-
make_path.ptr);
312-
error = GIT_ENOTFOUND;
313-
goto fail;
314-
}
315-
316-
already_exists = 1;
317-
break;
318-
case ENOSYS:
319-
/* Solaris can generate this error if you try to mkdir
320-
* a path which is already a mount point. In that case,
321-
* the path does already exist; but it's not implied by
322-
* the definition of the error, so let's recheck */
323-
if (git_path_isdir(make_path.ptr)) {
324-
already_exists = 1;
325-
break;
326-
}
327-
328-
/* Fall through */
329-
errno = ENOSYS;
330-
default:
331-
giterr_set(GITERR_OS, "Failed to make directory '%s'",
310+
case EEXIST:
311+
if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
312+
!git_path_isdir(make_path.ptr)) {
313+
giterr_set(
314+
GITERR_OS, "Existing path is not a directory '%s'",
332315
make_path.ptr);
316+
error = GIT_ENOTFOUND;
333317
goto fail;
318+
}
319+
320+
already_exists = 1;
321+
break;
322+
case ENOSYS:
323+
/* Solaris can generate this error if you try to mkdir
324+
* a path which is already a mount point. In that case,
325+
* the path does already exist; but it's not implied by
326+
* the definition of the error, so let's recheck */
327+
if (git_path_isdir(make_path.ptr)) {
328+
already_exists = 1;
329+
break;
330+
}
331+
332+
/* Fall through */
333+
errno = ENOSYS;
334+
default:
335+
giterr_set(GITERR_OS, "Failed to make directory '%s'",
336+
make_path.ptr);
337+
goto fail;
334338
}
335339

336340
if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) {
@@ -714,8 +718,33 @@ typedef struct {
714718
mode_t dirmode;
715719
} cp_r_info;
716720

721+
#define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
722+
723+
static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
724+
{
725+
int error = 0;
726+
727+
/* create root directory the first time we need to create a directory */
728+
if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
729+
error = git_futils_mkdir(
730+
info->to_root, NULL, info->dirmode,
731+
(info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
732+
733+
info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
734+
}
735+
736+
/* create directory with root as base to prevent excess chmods */
737+
if (!error)
738+
error = git_futils_mkdir(
739+
from->ptr + info->from_prefix, info->to_root,
740+
info->dirmode, info->mkdir_flags);
741+
742+
return error;
743+
}
744+
717745
static int _cp_r_callback(void *ref, git_buf *from)
718746
{
747+
int error = 0;
719748
cp_r_info *info = ref;
720749
struct stat from_st, to_st;
721750
bool exists = false;
@@ -737,24 +766,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
737766
} else
738767
exists = true;
739768

740-
if (git_path_lstat(from->ptr, &from_st) < 0)
741-
return -1;
769+
if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
770+
return error;
742771

743772
if (S_ISDIR(from_st.st_mode)) {
744-
int error = 0;
745773
mode_t oldmode = info->dirmode;
746774

747775
/* if we are not chmod'ing, then overwrite dirmode */
748-
if ((info->flags & GIT_CPDIR_CHMOD) == 0)
776+
if ((info->flags & GIT_CPDIR_CHMOD_DIRS) == 0)
749777
info->dirmode = from_st.st_mode;
750778

751779
/* make directory now if CREATE_EMPTY_DIRS is requested and needed */
752780
if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
753-
error = git_futils_mkdir(
754-
info->to.ptr, NULL, info->dirmode, info->mkdir_flags);
781+
error = _cp_r_mkdir(info, from);
755782

756783
/* recurse onto target directory */
757-
if (!exists || S_ISDIR(to_st.st_mode))
784+
if (!error && (!exists || S_ISDIR(to_st.st_mode)))
758785
error = git_path_direach(from, _cp_r_callback, info);
759786

760787
if (oldmode != 0)
@@ -782,15 +809,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
782809

783810
/* Make container directory on demand if needed */
784811
if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
785-
git_futils_mkdir(
786-
info->to.ptr, NULL, info->dirmode, info->mkdir_flags) < 0)
787-
return -1;
812+
(error = _cp_r_mkdir(info, from)) < 0)
813+
return error;
788814

789815
/* make symlink or regular file */
790816
if (S_ISLNK(from_st.st_mode))
791-
return cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
792-
else
793-
return git_futils_cp(from->ptr, info->to.ptr, from_st.st_mode);
817+
error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
818+
else {
819+
mode_t usemode = from_st.st_mode;
820+
821+
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
822+
usemode = (usemode & 0111) ? 0777 : 0666;
823+
824+
error = git_futils_cp(from->ptr, info->to.ptr, usemode);
825+
}
826+
827+
return error;
794828
}
795829

796830
int git_futils_cp_r(
@@ -803,7 +837,7 @@ int git_futils_cp_r(
803837
git_buf path = GIT_BUF_INIT;
804838
cp_r_info info;
805839

806-
if (git_buf_sets(&path, from) < 0)
840+
if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */
807841
return -1;
808842

809843
info.to_root = to;
@@ -814,12 +848,16 @@ int git_futils_cp_r(
814848

815849
/* precalculate mkdir flags */
816850
if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
851+
/* if not creating empty dirs, then use mkdir to create the path on
852+
* demand right before files are copied.
853+
*/
817854
info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
818-
if ((flags & GIT_CPDIR_CHMOD) != 0)
855+
if ((flags & GIT_CPDIR_CHMOD_DIRS) != 0)
819856
info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
820857
} else {
858+
/* otherwise, we will do simple mkdir as directories are encountered */
821859
info.mkdir_flags =
822-
((flags & GIT_CPDIR_CHMOD) != 0) ? GIT_MKDIR_CHMOD : 0;
860+
((flags & GIT_CPDIR_CHMOD_DIRS) != 0) ? GIT_MKDIR_CHMOD : 0;
823861
}
824862

825863
error = _cp_r_callback(&info, &path);

src/fileops.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t m
6565
* * GIT_MKDIR_CHMOD says to chmod the final directory entry after creation
6666
* * GIT_MKDIR_CHMOD_PATH says to chmod each directory component in the path
6767
* * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path
68+
* * GIT_MKDIR_SKIP_LAST2 says to leave off the last 2 elements of the path
6869
* * GIT_MKDIR_VERIFY_DIR says confirm final item is a dir, not just EEXIST
6970
*
7071
* Note that the chmod options will be executed even if the directory already
@@ -76,7 +77,8 @@ typedef enum {
7677
GIT_MKDIR_CHMOD = 4,
7778
GIT_MKDIR_CHMOD_PATH = 8,
7879
GIT_MKDIR_SKIP_LAST = 16,
79-
GIT_MKDIR_VERIFY_DIR = 32,
80+
GIT_MKDIR_SKIP_LAST2 = 32,
81+
GIT_MKDIR_VERIFY_DIR = 64,
8082
} git_futils_mkdir_flags;
8183

8284
/**
@@ -168,13 +170,26 @@ extern int git_futils_cp(
168170

169171
/**
170172
* Flags that can be passed to `git_futils_cp_r`.
173+
*
174+
* - GIT_CPDIR_CREATE_EMPTY_DIRS: create directories even if there are no
175+
* files under them (otherwise directories will only be created lazily
176+
* when a file inside them is copied).
177+
* - GIT_CPDIR_COPY_SYMLINKS: copy symlinks, otherwise they are ignored.
178+
* - GIT_CPDIR_COPY_DOTFILES: copy files with leading '.', otherwise ignored.
179+
* - GIT_CPDIR_OVERWRITE: overwrite pre-existing files with source content,
180+
* otherwise they are silently skipped.
181+
* - GIT_CPDIR_CHMOD_DIRS: explicitly chmod directories to `dirmode`
182+
* - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the
183+
* source file to the target; with this flag, always use 0666 (or 0777 if
184+
* source has exec bits set) for target.
171185
*/
172186
typedef enum {
173-
GIT_CPDIR_CREATE_EMPTY_DIRS = 1,
174-
GIT_CPDIR_COPY_SYMLINKS = 2,
175-
GIT_CPDIR_COPY_DOTFILES = 4,
176-
GIT_CPDIR_OVERWRITE = 8,
177-
GIT_CPDIR_CHMOD = 16
187+
GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
188+
GIT_CPDIR_COPY_SYMLINKS = (1u << 1),
189+
GIT_CPDIR_COPY_DOTFILES = (1u << 2),
190+
GIT_CPDIR_OVERWRITE = (1u << 3),
191+
GIT_CPDIR_CHMOD_DIRS = (1u << 4),
192+
GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
178193
} git_futils_cpdir_flags;
179194

180195
/**

src/refs.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,8 @@ static int ensure_segment_validity(const char *name)
15991599
{
16001600
const char *current = name;
16011601
char prev = '\0';
1602-
int lock_len = strlen(GIT_FILELOCK_EXTENSION);
1602+
const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
1603+
int segment_len;
16031604

16041605
if (*current == '.')
16051606
return -1; /* Refname starts with "." */
@@ -1620,12 +1621,14 @@ static int ensure_segment_validity(const char *name)
16201621
prev = *current;
16211622
}
16221623

1624+
segment_len = (int)(current - name);
1625+
16231626
/* A refname component can not end with ".lock" */
1624-
if (current - name >= lock_len &&
1627+
if (segment_len >= lock_len &&
16251628
!memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
16261629
return -1;
16271630

1628-
return (int)(current - name);
1631+
return segment_len;
16291632
}
16301633

16311634
static bool is_all_caps_and_underscore(const char *name, size_t len)
@@ -1700,7 +1703,7 @@ int git_reference__normalize_name(
17001703
/* No empty segment is allowed when not normalizing */
17011704
if (segment_len == 0 && !normalize)
17021705
goto cleanup;
1703-
1706+
17041707
if (current[segment_len] == '\0')
17051708
break;
17061709

src/repo_template.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
1212

1313
#define GIT_HOOKS_DIR "hooks/"
14-
#define GIT_HOOKS_DIR_MODE 0755
14+
#define GIT_HOOKS_DIR_MODE 0777
1515

1616
#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
17-
#define GIT_HOOKS_README_MODE 0755
17+
#define GIT_HOOKS_README_MODE 0777
1818
#define GIT_HOOKS_README_CONTENT \
1919
"#!/bin/sh\n"\
2020
"#\n"\
@@ -23,16 +23,16 @@
2323
"# more information.\n"
2424

2525
#define GIT_INFO_DIR "info/"
26-
#define GIT_INFO_DIR_MODE 0755
26+
#define GIT_INFO_DIR_MODE 0777
2727

2828
#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
29-
#define GIT_INFO_EXCLUDE_MODE 0644
29+
#define GIT_INFO_EXCLUDE_MODE 0666
3030
#define GIT_INFO_EXCLUDE_CONTENT \
3131
"# File patterns to ignore; see `git help ignore` for more information.\n"\
3232
"# Lines that start with '#' are comments.\n"
3333

3434
#define GIT_DESC_FILE "description"
35-
#define GIT_DESC_MODE 0644
35+
#define GIT_DESC_MODE 0666
3636
#define GIT_DESC_CONTENT \
3737
"Unnamed repository; edit this file 'description' to name the repository.\n"
3838

0 commit comments

Comments
 (0)