Skip to content

Commit c9b7840

Browse files
hvoigtgitster
authored andcommitted
mingw: make failures to unlink or move raise a question
On Windows in case a program is accessing a file unlink or move operations may fail. To give the user a chance to correct this we simply wait until the user asks us to retry or fail. This is useful because of the following use case which seem to happen rarely but when it does it is a mess: After making some changes the user realizes that he was on the incorrect branch. When trying to change the branch some file is still in use by some other process and git stops in the middle of changing branches. Now the user has lots of files with changes mixed with his own. This is especially confusing on repositories that contain lots of files. Although the recent implementation of automatic retry makes this scenario much more unlikely lets provide a fallback as a last resort. Thanks to Albert Dvornik for disabling the question if users can't see it. If the stdout of the command is connected to a terminal but the stderr has been redirected, the odds are good that the user can't see any question we print out to stderr. This will result in a "mysterious hang" while the app is waiting for user input. It seems better to be conservative, and avoid asking for input whenever the stderr is not a terminal, just like we do for stdin. Signed-off-by: Heiko Voigt <hvoigt@hvoigt.net> Signed-off-by: Albert Dvornik <dvornik+git@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 19e1254 commit c9b7840

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

compat/mingw.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "win32.h"
33
#include <conio.h>
44
#include "../strbuf.h"
5+
#include "../run-command.h"
56

67
static const int delay[] = { 0, 1, 10, 20, 40 };
78

@@ -129,6 +130,74 @@ static inline int is_file_in_use_error(DWORD errcode)
129130
return 0;
130131
}
131132

133+
static int read_yes_no_answer(void)
134+
{
135+
char answer[1024];
136+
137+
if (fgets(answer, sizeof(answer), stdin)) {
138+
size_t answer_len = strlen(answer);
139+
int got_full_line = 0, c;
140+
141+
/* remove the newline */
142+
if (answer_len >= 2 && answer[answer_len-2] == '\r') {
143+
answer[answer_len-2] = '\0';
144+
got_full_line = 1;
145+
} else if (answer_len >= 1 && answer[answer_len-1] == '\n') {
146+
answer[answer_len-1] = '\0';
147+
got_full_line = 1;
148+
}
149+
/* flush the buffer in case we did not get the full line */
150+
if (!got_full_line)
151+
while ((c = getchar()) != EOF && c != '\n')
152+
;
153+
} else
154+
/* we could not read, return the
155+
* default answer which is no */
156+
return 0;
157+
158+
if (tolower(answer[0]) == 'y' && !answer[1])
159+
return 1;
160+
if (!strncasecmp(answer, "yes", sizeof(answer)))
161+
return 1;
162+
if (tolower(answer[0]) == 'n' && !answer[1])
163+
return 0;
164+
if (!strncasecmp(answer, "no", sizeof(answer)))
165+
return 0;
166+
167+
/* did not find an answer we understand */
168+
return -1;
169+
}
170+
171+
static int ask_yes_no_if_possible(const char *format, ...)
172+
{
173+
char question[4096];
174+
const char *retry_hook[] = { NULL, NULL, NULL };
175+
va_list args;
176+
177+
va_start(args, format);
178+
vsnprintf(question, sizeof(question), format, args);
179+
va_end(args);
180+
181+
if ((retry_hook[0] = getenv("GIT_ASK_YESNO"))) {
182+
retry_hook[1] = question;
183+
return !run_command_v_opt(retry_hook, 0);
184+
}
185+
186+
if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))
187+
return 0;
188+
189+
while (1) {
190+
int answer;
191+
fprintf(stderr, "%s (y/n) ", question);
192+
193+
if ((answer = read_yes_no_answer()) >= 0)
194+
return answer;
195+
196+
fprintf(stderr, "Sorry, I did not understand your answer. "
197+
"Please type 'y' or 'n'\n");
198+
}
199+
}
200+
132201
#undef unlink
133202
int mingw_unlink(const char *pathname)
134203
{
@@ -149,6 +218,10 @@ int mingw_unlink(const char *pathname)
149218
Sleep(delay[tries]);
150219
tries++;
151220
}
221+
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
222+
ask_yes_no_if_possible("Unlink of file '%s' failed. "
223+
"Should I try again?", pathname))
224+
ret = unlink(pathname);
152225
return ret;
153226
}
154227

@@ -1326,6 +1399,11 @@ int mingw_rename(const char *pold, const char *pnew)
13261399
tries++;
13271400
goto repeat;
13281401
}
1402+
if (gle == ERROR_ACCESS_DENIED &&
1403+
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
1404+
"Should I try again?", pold, pnew))
1405+
goto repeat;
1406+
13291407
errno = EACCES;
13301408
return -1;
13311409
}

0 commit comments

Comments
 (0)