Skip to content

Commit

Permalink
Introduce helper to create symlinks that knows about index_state
Browse files Browse the repository at this point in the history
On Windows, symbolic links actually have a type depending on the target:
it can be a file or a directory.

In certain circumstances, this poses problems, e.g. when a symbolic link
is supposed to point into a submodule that is not checked out, so there
is no way for Git to auto-detect the type.

To help with that, we will add support over the course of the next
commits to specify that symlink type via the Git attributes. This
requires an index_state, though, something that Git for Windows'
`symlink()` replacement cannot know about because the function signature
is defined by the POSIX standard and not ours to change.

So let's introduce a helper function to create symbolic links that
*does* know about the index_state.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
  • Loading branch information
dscho committed Sep 18, 2024
1 parent 41f035f commit ceee7f1
Show file tree
Hide file tree
Showing 9 changed files with 20 additions and 9 deletions.
2 changes: 1 addition & 1 deletion apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -4440,7 +4440,7 @@ static int try_create_file(struct apply_state *state, const char *path,
/* Although buf:size is counted string, it also is NUL
* terminated.
*/
return !!symlink(buf, path);
return !!create_symlink(state && state->repo ? state->repo->index : NULL, buf, path);

fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
if (fd < 0)
Expand Down
2 changes: 1 addition & 1 deletion builtin/difftool.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
add_path(&wtdir, wtdir_len, dst_path);
if (symlinks) {
if (symlink(wtdir.buf, rdir.buf)) {
if (create_symlink(lstate.istate, wtdir.buf, rdir.buf)) {
ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf);
goto finish;
}
Expand Down
2 changes: 1 addition & 1 deletion compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -2943,7 +2943,7 @@ int link(const char *oldpath, const char *newpath)
return 0;
}

int symlink(const char *target, const char *link)
int mingw_create_symlink(struct index_state *index, const char *target, const char *link)
{
wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
int len;
Expand Down
4 changes: 3 additions & 1 deletion compat/mingw.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,10 @@ int setitimer(int type, struct itimerval *in, struct itimerval *out);
int sigaction(int sig, struct sigaction *in, struct sigaction *out);
int link(const char *oldpath, const char *newpath);
int uname(struct utsname *buf);
int symlink(const char *target, const char *link);
int readlink(const char *path, char *buf, size_t bufsiz);
struct index_state;
int mingw_create_symlink(struct index_state *index, const char *target, const char *link);
#define create_symlink mingw_create_symlink

/*
* replacements of existing functions
Expand Down
2 changes: 1 addition & 1 deletion entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
if (!has_symlinks || to_tempfile)
goto write_file_entry;

ret = symlink(new_blob, path);
ret = create_symlink(state->istate, new_blob, path);
free(new_blob);
if (ret)
return error_errno("unable to create symlink %s", path);
Expand Down
9 changes: 9 additions & 0 deletions git-compat-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,15 @@ static inline int git_has_dir_sep(const char *path)
#define is_mount_point is_mount_point_via_stat
#endif

#ifndef create_symlink
struct index_state;
static inline int git_create_symlink(struct index_state *index, const char *target, const char *link)
{
return symlink(target, link);
}
#define create_symlink git_create_symlink
#endif

#ifndef query_user_email
#define query_user_email() NULL
#endif
Expand Down
2 changes: 1 addition & 1 deletion merge-recursive.c
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ static int update_file_flags(struct merge_options *opt,
char *lnk = xmemdupz(buf, size);
safe_create_leading_directories_const(path);
unlink(path);
if (symlink(lnk, path))
if (create_symlink(&opt->priv->orig_index, lnk, path))
ret = err(opt, _("failed to symlink '%s': %s"),
path, strerror(errno));
free(lnk);
Expand Down
2 changes: 1 addition & 1 deletion refs/files-backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1942,7 +1942,7 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
#ifndef NO_SYMLINK_HEAD
char *ref_path = get_locked_file_path(&lock->lk);
unlink(ref_path);
ret = symlink(target, ref_path);
ret = create_symlink(NULL, target, ref_path);
free(ref_path);

if (ret)
Expand Down
4 changes: 2 additions & 2 deletions setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -2021,7 +2021,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
if (strbuf_readlink(&lnk, template_path->buf,
st_template.st_size) < 0)
die_errno(_("cannot readlink '%s'"), template_path->buf);
if (symlink(lnk.buf, path->buf))
if (create_symlink(NULL, lnk.buf, path->buf))
die_errno(_("cannot symlink '%s' '%s'"),
lnk.buf, path->buf);
strbuf_release(&lnk);
Expand Down Expand Up @@ -2268,7 +2268,7 @@ static int create_default_files(const char *template_path,
path = git_path_buf(&buf, "tXXXXXX");
if (!close(xmkstemp(path)) &&
!unlink(path) &&
!symlink("testing", path) &&
!create_symlink(NULL, "testing", path) &&
!lstat(path, &st1) &&
S_ISLNK(st1.st_mode))
unlink(path); /* good */
Expand Down

0 comments on commit ceee7f1

Please sign in to comment.