diff --git a/compat/mingw.c b/compat/mingw.c index d131092a6e..0df1a3d5ea 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -5,6 +5,7 @@ #include #include "../strbuf.h" #include "../cache.h" +#include "../run-command.h" unsigned int _CRT_fmode = _O_BINARY; static const int delay[] = { 0, 1, 10, 20, 40 }; @@ -158,6 +159,54 @@ int mingw_mkdir(const char *path, int mode) return ret; } +static int ask_user_yes_no(const char *format, ...) +{ + char answer[5]; + char question[4096]; + const char *retry_hook[] = { NULL, NULL, NULL }; + va_list args; + + if ((retry_hook[0] = getenv("GIT_ASK_YESNO"))) { + + va_start(args, format); + vsnprintf(question, sizeof(question), format, args); + va_end(args); + + retry_hook[1] = question; + return !run_command_v_opt(retry_hook, 0); + } + + if (!isatty(_fileno(stdin))) + return 0; + + while (1) { + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, " (y/n)? "); + + if (fgets(answer, sizeof(answer), stdin)) { + /* remove the newline */ + if (answer[strlen(answer)-2] == '\r') + answer[strlen(answer)-2] = '\0'; + if (answer[strlen(answer)-1] == '\n') + answer[strlen(answer)-1] = '\0'; + } else + return 0; + + if (answer[0] == 'y' && strlen(answer) == 1) + return 1; + if (!strncasecmp(answer, "yes", sizeof(answer))) + return 1; + if (answer[0] == 'n' && strlen(answer) == 1) + return 0; + if (!strncasecmp(answer, "no", sizeof(answer))) + return 0; + fprintf(stderr, "I did not understand your answer: '%s'\n", + answer); + } +} + #undef unlink int mingw_unlink(const char *pathname) { @@ -178,6 +227,35 @@ int mingw_unlink(const char *pathname) Sleep(delay[tries]); tries++; } + while (ret == -1 && errno == EACCES && + ask_user_yes_no("Unlink of file '%s' failed. " + "Should I try again?", pathname)) + ret = unlink(pathname); + return ret; +} + +#undef rmdir +int mingw_rmdir(const char *pathname) +{ + int ret, tries = 0; + + while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) { + if (errno != EACCES) + break; + /* + * We assume that some other process had the source or + * destination file open at the wrong moment and retry. + * In order to give the other process a higher chance to + * complete its operation, we give up our time slice now. + * If we have to retry again, we do sleep a bit. + */ + Sleep(delay[tries]); + tries++; + } + while (ret == -1 && errno == EACCES && + ask_user_yes_no("Deletion of directory '%s' failed. " + "Should I try again?", pathname)) + ret = rmdir(pathname); return ret; } @@ -1277,6 +1355,11 @@ repeat: tries++; goto repeat; } + if (gle == ERROR_ACCESS_DENIED && + ask_user_yes_no("Rename from '%s' to '%s' failed. " + "Should I try again?", pold, pnew)) + goto repeat; + errno = EACCES; return -1; } diff --git a/compat/mingw.h b/compat/mingw.h index f66c83166b..4650d8a882 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -170,6 +170,9 @@ int mingw_mkdir(const char *path, int mode); int mingw_unlink(const char *pathname); #define unlink mingw_unlink +int mingw_rmdir(const char *path); +#define rmdir mingw_rmdir + int mingw_open (const char *filename, int oflags, ...); #define open mingw_open diff --git a/git-gui/Makefile b/git-gui/Makefile index 197b55edf3..7d8b54101d 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -289,6 +289,7 @@ install: all $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1) $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(INSTALL_X0)git-gui--askpass $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(QUIET)$(INSTALL_X0)git-gui--askyesno $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true ifdef GITGUI_WINDOWS_WRAPPER $(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' @@ -307,6 +308,7 @@ uninstall: $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1) $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askpass $(REMOVE_F1) + $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askyesno $(REMOVE_F1) $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true ifdef GITGUI_WINDOWS_WRAPPER $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1) diff --git a/git-gui/git-gui--askyesno b/git-gui/git-gui--askyesno new file mode 100755 index 0000000000..2a6e6fd111 --- /dev/null +++ b/git-gui/git-gui--askyesno @@ -0,0 +1,51 @@ +#!/bin/sh +# Tcl ignores the next line -*- tcl -*- \ +exec wish "$0" -- "$@" + +# This is an implementation of a simple yes no dialog +# which is injected into the git commandline by git gui +# in case a yesno question needs to be answered. + +set NS {} +set use_ttk [package vsatisfies [package provide Tk] 8.5] +if {$use_ttk} { + set NS ttk +} + +if {$argc < 1} { + puts stderr "Usage: $argv0 " + exit 1 +} else { + set prompt [join $argv " "] +} + +${NS}::frame .t +${NS}::label .t.m -text $prompt -justify center -width 40 +.t.m configure -wraplength 400 +pack .t.m -side top -fill x -padx 20 -pady 20 -expand 1 +pack .t -side top -fill x -ipadx 20 -ipady 20 -expand 1 + +${NS}::frame .b +${NS}::frame .b.left -width 200 +${NS}::button .b.yes -text Yes -command yes +${NS}::button .b.no -text No -command no + + +pack .b.left -side left -expand 1 -fill x +pack .b.yes -side left -expand 1 +pack .b.no -side right -expand 1 -ipadx 5 +pack .b -side bottom -fill x -ipadx 20 -ipady 15 + +bind . {exit 0} +bind . {exit 1} + +proc no {} { + exit 1 +} + +proc yes {} { + exit 0 +} + +wm title . "Question?" +tk::PlaceWindow . diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 0e5cc89e6d..395cb2e35f 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -1115,6 +1115,9 @@ set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}] if {![info exists env(SSH_ASKPASS)]} { set env(SSH_ASKPASS) [gitexec git-gui--askpass] } +if {![info exists env(GIT_ASK_YESNO)]} { + set env(GIT_ASK_YESNO) [gitexec git-gui--askyesno] +} ###################################################################### ##