Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 75b8a67

Browse files
committed
MinGW: Allow passing the symlink target type from index information.
Required for msysgit which needs to know whether the target of a symbolic link is a directory or file. Signed-off-by: Michael Geddes <[email protected]>
1 parent 82cc187 commit 75b8a67

File tree

2 files changed

+134
-1
lines changed

2 files changed

+134
-1
lines changed

entry.c

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,126 @@ static int streaming_write_entry(const struct cache_entry *ce, char *path,
136136
return result;
137137
}
138138

139+
/*
140+
* Does 'match' match the given name?
141+
* A match is found if
142+
*
143+
* (1) the 'match' string is leading directory of 'name', or
144+
* (2) the 'match' string is exactly the same as 'name'.
145+
*
146+
* and the return value tells which case it was.
147+
*
148+
* It returns 0 when there is no match.
149+
*
150+
* Preserved and simplified from dir.c for use here (without glob special matching)
151+
*/
152+
static int match_one(const char *match, const char *name, int namelen)
153+
{
154+
int matchlen;
155+
156+
/* If the match was just the prefix, we matched */
157+
if (!*match)
158+
return MATCHED_RECURSIVELY;
159+
160+
if (ignore_case) {
161+
for (;;) {
162+
unsigned char c1 = tolower(*match);
163+
unsigned char c2 = tolower(*name);
164+
if (c1 == '\0' )
165+
break;
166+
if (c1 != c2)
167+
return 0;
168+
match++;
169+
name++;
170+
namelen--;
171+
}
172+
/* We don't match the matchstring exactly, */
173+
matchlen = strlen(match);
174+
if (strncmp_icase(match, name, matchlen))
175+
return 0;
176+
} else {
177+
for (;;) {
178+
unsigned char c1 = *match;
179+
unsigned char c2 = *name;
180+
if (c1 == '\0' )
181+
break;
182+
if (c1 != c2)
183+
return 0;
184+
match++;
185+
name++;
186+
namelen--;
187+
}
188+
/* We don't match the matchstring exactly, */
189+
matchlen = strlen(match);
190+
if (strncmp(match, name, matchlen))
191+
return 0;
192+
}
193+
194+
if (namelen == matchlen)
195+
return MATCHED_EXACTLY;
196+
if (match[matchlen-1] == '/' || name[matchlen] == '/')
197+
return MATCHED_RECURSIVELY;
198+
return 0;
199+
}
200+
201+
static enum git_target_type get_symlink_type(const char *filepath, const char *symlinkpath)
202+
{
203+
/* For certain O/S and file-systems, symlinks need to know before-hand whether it
204+
* is a directory or a file being pointed to.
205+
*
206+
* This allows us to use index information for relative paths that lie
207+
* within the working directory.
208+
*
209+
* This function is not interested in interrogating the file-system.
210+
*/
211+
char *sanitized;
212+
const char *fpos, *last;
213+
enum git_target_type ret;
214+
int len, pos;
215+
216+
/* This is an absolute path, so git doesn't know.
217+
*/
218+
if (is_absolute_path(symlinkpath))
219+
return GIT_TARGET_UNKNOWN;
220+
221+
/* Work on a sanitized version of the path that can be
222+
* matched against the index.
223+
*/
224+
last = NULL;
225+
for (fpos = filepath; *fpos; ++fpos)
226+
if (is_dir_sep(*fpos))
227+
last = fpos;
228+
229+
if (last) {
230+
len = (1+last-filepath);
231+
sanitized = xmalloc(len + strlen(symlinkpath)+1);
232+
memcpy(sanitized, filepath, 1+last-filepath);
233+
} else {
234+
len = 0;
235+
sanitized = xmalloc(strlen(symlinkpath)+1);
236+
}
237+
strcpy(sanitized+len, symlinkpath);
238+
239+
ret = GIT_TARGET_UNKNOWN;
240+
if (!normalize_path_copy(sanitized, sanitized)) {
241+
for (pos = 0; pos < active_nr; pos++) {
242+
struct cache_entry *ce = active_cache[pos];
243+
switch (match_one(sanitized, ce->name, ce_namelen(ce))) {
244+
case MATCHED_EXACTLY:
245+
case MATCHED_FNMATCH:
246+
ret = GIT_TARGET_ISFILE;
247+
break;
248+
case MATCHED_RECURSIVELY:
249+
ret = GIT_TARGET_ISDIR;
250+
break;
251+
}
252+
}
253+
}
254+
255+
free(sanitized);
256+
return ret;
257+
}
258+
139259
static int write_entry(struct cache_entry *ce,
140260
char *path, const struct checkout *state, int to_tempfile)
141261
{
@@ -165,7 +285,10 @@ static int write_entry(struct cache_entry *ce,
165285
path, sha1_to_hex(ce->sha1));
166286

167287
if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) {
168-
ret = symlink(new, path);
288+
/* Note that symlink_with_type is a macro, and that for filesystems that
289+
* don't care, get_symlink_type will not be called.
290+
*/
291+
ret = symlink_with_type(new, path, get_symlink_type(path, new));
169292
free(new);
170293
if (ret)
171294
return error("unable to create symlink %s (%s)",

git-compat-util.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@
8585
#define _NETBSD_SOURCE 1
8686
#define _SGI_SOURCE 1
8787

88+
/* default is not to pass type - mingw needs this */
89+
#define symlink_with_type(a,b,c) symlink((a),(b))
90+
91+
/* Used for 'Target Type' Parameter for symlink_with_type */
92+
enum git_target_type {
93+
GIT_TARGET_UNKNOWN,
94+
GIT_TARGET_ISFILE,
95+
GIT_TARGET_ISDIR
96+
};
97+
8898
#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
8999
# if defined (_MSC_VER) && !defined(_WIN32_WINNT)
90100
# define _WIN32_WINNT 0x0502

0 commit comments

Comments
 (0)