Sync with 2.49.1
						commit
						e1775c0646
					
				|  | @ -0,0 +1,73 @@ | |||
| Git v2.43.7 Release Notes | ||||
| ========================= | ||||
|  | ||||
| This release includes fixes for CVE-2025-27613, CVE-2025-27614, | ||||
| CVE-2025-46334, CVE-2025-46835, CVE-2025-48384, CVE-2025-48385, and | ||||
| CVE-2025-48386. | ||||
|  | ||||
| Fixes since v2.43.6 | ||||
| ------------------- | ||||
|  | ||||
|  * CVE-2025-27613, Gitk: | ||||
|  | ||||
|    When a user clones an untrusted repository and runs Gitk without | ||||
|    additional command arguments, any writable file can be created and | ||||
|    truncated. The option "Support per-file encoding" must have been | ||||
|    enabled. The operation "Show origin of this line" is affected as | ||||
|    well, regardless of the option being enabled or not. | ||||
|  | ||||
|  * CVE-2025-27614, Gitk: | ||||
|  | ||||
|    A Git repository can be crafted in such a way that a user who has | ||||
|    cloned the repository can be tricked into running any script | ||||
|    supplied by the attacker by invoking `gitk filename`, where | ||||
|    `filename` has a particular structure. | ||||
|  | ||||
|  * CVE-2025-46334, Git GUI (Windows only): | ||||
|  | ||||
|    A malicious repository can ship versions of sh.exe or typical | ||||
|    textconv filter programs such as astextplain. On Windows, path | ||||
|    lookup can find such executables in the worktree. These programs | ||||
|    are invoked when the user selects "Git Bash" or "Browse Files" from | ||||
|    the menu. | ||||
|  | ||||
|  * CVE-2025-46835, Git GUI: | ||||
|  | ||||
|    When a user clones an untrusted repository and is tricked into | ||||
|    editing a file located in a maliciously named directory in the | ||||
|    repository, then Git GUI can create and overwrite any writable | ||||
|    file. | ||||
|  | ||||
|  * CVE-2025-48384, Git: | ||||
|  | ||||
|    When reading a config value, Git strips any trailing carriage | ||||
|    return and line feed (CRLF). When writing a config entry, values | ||||
|    with a trailing CR are not quoted, causing the CR to be lost when | ||||
|    the config is later read.  When initializing a submodule, if the | ||||
|    submodule path contains a trailing CR, the altered path is read | ||||
|    resulting in the submodule being checked out to an incorrect | ||||
|    location. If a symlink exists that points the altered path to the | ||||
|    submodule hooks directory, and the submodule contains an executable | ||||
|    post-checkout hook, the script may be unintentionally executed | ||||
|    after checkout. | ||||
|  | ||||
|  * CVE-2025-48385, Git: | ||||
|  | ||||
|    When cloning a repository Git knows to optionally fetch a bundle | ||||
|    advertised by the remote server, which allows the server-side to | ||||
|    offload parts of the clone to a CDN. The Git client does not | ||||
|    perform sufficient validation of the advertised bundles, which | ||||
|    allows the remote side to perform protocol injection. | ||||
|  | ||||
|    This protocol injection can cause the client to write the fetched | ||||
|    bundle to a location controlled by the adversary. The fetched | ||||
|    content is fully controlled by the server, which can in the worst | ||||
|    case lead to arbitrary code execution. | ||||
|  | ||||
|  * CVE-2025-48386, Git: | ||||
|  | ||||
|    The wincred credential helper uses a static buffer (`target`) as a | ||||
|    unique key for storing and comparing against internal storage. This | ||||
|    credential helper does not properly bounds check the available | ||||
|    space remaining in the buffer before appending to it with | ||||
|    `wcsncat()`, leading to potential buffer overflows. | ||||
|  | @ -0,0 +1,7 @@ | |||
| Git v2.44.4 Release Notes | ||||
| ========================= | ||||
|  | ||||
| This release merges up the fixes that appears in v2.43.7 to address | ||||
| the following CVEs: CVE-2025-27613, CVE-2025-27614, CVE-2025-46334, | ||||
| CVE-2025-46835, CVE-2025-48384, CVE-2025-48385, and CVE-2025-48386. | ||||
| See the release notes for v2.43.7 for details. | ||||
|  | @ -0,0 +1,7 @@ | |||
| Git v2.45.4 Release Notes | ||||
| ========================= | ||||
|  | ||||
| This release merges up the fixes that appears in v2.43.7, and v2.44.4 | ||||
| to address the following CVEs: CVE-2025-27613, CVE-2025-27614, | ||||
| CVE-2025-46334, CVE-2025-46835, CVE-2025-48384, CVE-2025-48385, and | ||||
| CVE-2025-48386. See the release notes for v2.43.7 for details. | ||||
|  | @ -0,0 +1,7 @@ | |||
| Git v2.46.4 Release Notes | ||||
| ========================= | ||||
|  | ||||
| This release merges up the fixes that appears in v2.43.7, v2.44.4, and | ||||
| v2.45.4 to address the following CVEs: CVE-2025-27613, CVE-2025-27614, | ||||
| CVE-2025-46334, CVE-2025-46835, CVE-2025-48384, CVE-2025-48385, and | ||||
| CVE-2025-48386. See the release notes for v2.43.7 for details. | ||||
|  | @ -0,0 +1,8 @@ | |||
| Git v2.47.3 Release Notes | ||||
| ========================= | ||||
|  | ||||
| This release merges up the fixes that appears in v2.43.7, v2.44.4, | ||||
| v2.45.4, and v2.46.4 to address the following CVEs: CVE-2025-27613, | ||||
| CVE-2025-27614, CVE-2025-46334, CVE-2025-46835, CVE-2025-48384, | ||||
| CVE-2025-48385, and CVE-2025-48386. See the release notes for v2.43.7 | ||||
| for details. | ||||
|  | @ -0,0 +1,8 @@ | |||
| Git v2.48.2 Release Notes | ||||
| ========================= | ||||
|  | ||||
| This release merges up the fixes that appears in v2.43.7, v2.44.4, | ||||
| v2.45.4, v2.46.4, and v2.47.3 to address the following CVEs: | ||||
| CVE-2025-27613, CVE-2025-27614, CVE-2025-46334, CVE-2025-46835, | ||||
| CVE-2025-48384, CVE-2025-48385, and CVE-2025-48386. See the release | ||||
| notes for v2.43.7 for details. | ||||
|  | @ -0,0 +1,12 @@ | |||
| Git v2.49.1 Release Notes | ||||
| ========================= | ||||
|  | ||||
| This release merges up the fixes that appear in v2.43.7, v2.44.4, | ||||
| v2.45.4, v2.46.4, v2.47.3, and v2.48.2 to address the following CVEs: | ||||
| CVE-2025-27613, CVE-2025-27614, CVE-2025-46334, CVE-2025-46835, | ||||
| CVE-2025-48384, CVE-2025-48385, and CVE-2025-48386. See the release | ||||
| notes for v2.43.7 for details. | ||||
|  | ||||
| It also contains some updates to various CI bits to work around | ||||
| and/or to adjust to the deprecation of use of Ubuntu 20.04 GitHub | ||||
| Actions CI, updates to to Fedora base image. | ||||
							
								
								
									
										22
									
								
								bundle-uri.c
								
								
								
								
							
							
						
						
									
										22
									
								
								bundle-uri.c
								
								
								
								
							|  | @ -297,6 +297,28 @@ static int download_https_uri_to_file(const char *file, const char *uri) | |||
| 	struct strbuf line = STRBUF_INIT; | ||||
| 	int found_get = 0; | ||||
|  | ||||
| 	/* | ||||
| 	 * The protocol we speak with git-remote-https(1) uses a space to | ||||
| 	 * separate between URI and file, so the URI itself must not contain a | ||||
| 	 * space. If it did, an adversary could change the location where the | ||||
| 	 * downloaded file is being written to. | ||||
| 	 * | ||||
| 	 * Similarly, we use newlines to separate commands from one another. | ||||
| 	 * Consequently, neither the URI nor the file must contain a newline or | ||||
| 	 * otherwise an adversary could inject arbitrary commands. | ||||
| 	 * | ||||
| 	 * TODO: Restricting newlines in the target paths may break valid | ||||
| 	 *       usecases, even if those are a bit more on the esoteric side. | ||||
| 	 *       If this ever becomes a problem we should probably think about | ||||
| 	 *       alternatives. One alternative could be to use NUL-delimited | ||||
| 	 *       requests in git-remote-http(1). Another alternative could be | ||||
| 	 *       to use URL quoting. | ||||
| 	 */ | ||||
| 	if (strpbrk(uri, " \n")) | ||||
| 		return error("bundle-uri: URI is malformed: '%s'", file); | ||||
| 	if (strchr(file, '\n')) | ||||
| 		return error("bundle-uri: filename is malformed: '%s'", file); | ||||
|  | ||||
| 	strvec_pushl(&cp.args, "git-remote-https", uri, NULL); | ||||
| 	cp.err = -1; | ||||
| 	cp.in = -1; | ||||
|  |  | |||
							
								
								
									
										2
									
								
								config.c
								
								
								
								
							
							
						
						
									
										2
									
								
								config.c
								
								
								
								
							|  | @ -2940,7 +2940,7 @@ static ssize_t write_pair(int fd, const char *key, const char *value, | |||
| 	if (value[0] == ' ') | ||||
| 		quote = "\""; | ||||
| 	for (i = 0; value[i]; i++) | ||||
| 		if (value[i] == ';' || value[i] == '#') | ||||
| 		if (value[i] == ';' || value[i] == '#' || value[i] == '\r') | ||||
| 			quote = "\""; | ||||
| 	if (i && value[i - 1] == ' ') | ||||
| 		quote = "\""; | ||||
|  |  | |||
|  | @ -39,6 +39,14 @@ static void *xmalloc(size_t size) | |||
| static WCHAR *wusername, *password, *protocol, *host, *path, target[1024], | ||||
| 	*password_expiry_utc, *oauth_refresh_token; | ||||
|  | ||||
| static void target_append(const WCHAR *src) | ||||
| { | ||||
| 	size_t avail = ARRAY_SIZE(target) - wcslen(target) - 1; /* -1 for NUL */ | ||||
| 	if (avail < wcslen(src)) | ||||
| 		die("target buffer overflow"); | ||||
| 	wcsncat(target, src, avail); | ||||
| } | ||||
|  | ||||
| static void write_item(const char *what, LPCWSTR wbuf, int wlen) | ||||
| { | ||||
| 	char *buf; | ||||
|  | @ -330,17 +338,17 @@ int main(int argc, char *argv[]) | |||
|  | ||||
| 	/* prepare 'target', the unique key for the credential */ | ||||
| 	wcscpy(target, L"git:"); | ||||
| 	wcsncat(target, protocol, ARRAY_SIZE(target)); | ||||
| 	wcsncat(target, L"://", ARRAY_SIZE(target)); | ||||
| 	target_append(protocol); | ||||
| 	target_append(L"://"); | ||||
| 	if (wusername) { | ||||
| 		wcsncat(target, wusername, ARRAY_SIZE(target)); | ||||
| 		wcsncat(target, L"@", ARRAY_SIZE(target)); | ||||
| 		target_append(wusername); | ||||
| 		target_append(L"@"); | ||||
| 	} | ||||
| 	if (host) | ||||
| 		wcsncat(target, host, ARRAY_SIZE(target)); | ||||
| 		target_append(host); | ||||
| 	if (path) { | ||||
| 		wcsncat(target, L"/", ARRAY_SIZE(target)); | ||||
| 		wcsncat(target, path, ARRAY_SIZE(target)); | ||||
| 		target_append(L"/"); | ||||
| 		target_append(path); | ||||
| 	} | ||||
|  | ||||
| 	if (!strcmp(argv[1], "get")) | ||||
|  |  | |||
|  | @ -77,29 +77,49 @@ proc is_Cygwin {} { | |||
|  | ||||
| ###################################################################### | ||||
| ## | ||||
| ## PATH lookup | ||||
| ## PATH lookup. Sanitize $PATH, assure exec/open use only that | ||||
|  | ||||
| set _search_path {} | ||||
| proc _which {what args} { | ||||
| 	global env _search_exe _search_path | ||||
| if {[is_Windows]} { | ||||
| 	set _path_sep {;} | ||||
| 	set _search_exe .exe | ||||
| } else { | ||||
| 	set _path_sep {:} | ||||
| 	set _search_exe {} | ||||
| } | ||||
|  | ||||
| 	if {$_search_path eq {}} { | ||||
| 		if {[is_Windows]} { | ||||
| if {[is_Windows]} { | ||||
| 	set gitguidir [file dirname [info script]] | ||||
| 	regsub -all ";" $gitguidir "\\;" gitguidir | ||||
| 	set env(PATH) "$gitguidir;$env(PATH)" | ||||
| 			set _search_path [split $env(PATH) {;}] | ||||
| 			# Skip empty `PATH` elements | ||||
| 			set _search_path [lsearch -all -inline -not -exact \ | ||||
| 				$_search_path ""] | ||||
| 			set _search_exe .exe | ||||
| 		} else { | ||||
| 			set _search_path [split $env(PATH) :] | ||||
| 			set _search_exe {} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 	if {[is_Windows] && [lsearch -exact $args -script] >= 0} { | ||||
| set _search_path {} | ||||
| set _path_seen [dict create] | ||||
| foreach p [split $env(PATH) $_path_sep] { | ||||
| 	# Keep only absolute paths, getting rid of ., empty, etc. | ||||
| 	if {[file pathtype $p] ne {absolute}} { | ||||
| 		continue | ||||
| 	} | ||||
| 	# Keep only the first occurence of any duplicates. | ||||
| 	set norm_p [file normalize $p] | ||||
| 	if {[dict exists $_path_seen $norm_p]} { | ||||
| 		continue | ||||
| 	} | ||||
| 	dict set _path_seen $norm_p 1 | ||||
| 	lappend _search_path $norm_p | ||||
| } | ||||
| unset _path_seen | ||||
|  | ||||
| set env(PATH) [join $_search_path $_path_sep] | ||||
|  | ||||
| if {[is_Windows]} { | ||||
| 	proc _which {what args} { | ||||
| 		global _search_exe _search_path | ||||
|  | ||||
| 		if {[lsearch -exact $args -script] >= 0} { | ||||
| 			set suffix {} | ||||
| 		} elseif {[string match *$_search_exe [string tolower $what]]} { | ||||
| 			# The search string already has the file extension | ||||
| 			set suffix {} | ||||
| 		} else { | ||||
| 			set suffix $_search_exe | ||||
|  | @ -112,9 +132,9 @@ proc _which {what args} { | |||
| 			} | ||||
| 		} | ||||
| 		return {} | ||||
| } | ||||
| 	} | ||||
|  | ||||
| proc sanitize_command_line {command_line from_index} { | ||||
| 	proc sanitize_command_line {command_line from_index} { | ||||
| 		set i $from_index | ||||
| 		while {$i < [llength $command_line]} { | ||||
| 			set cmd [lindex $command_line $i] | ||||
|  | @ -135,13 +155,13 @@ proc sanitize_command_line {command_line from_index} { | |||
| 			} | ||||
| 		} | ||||
| 		return $command_line | ||||
| } | ||||
| 	} | ||||
|  | ||||
| # Override `exec` to avoid unsafe PATH lookup | ||||
| 	# Override `exec` to avoid unsafe PATH lookup | ||||
|  | ||||
| rename exec real_exec | ||||
| 	rename exec real_exec | ||||
|  | ||||
| proc exec {args} { | ||||
| 	proc exec {args} { | ||||
| 		# skip options | ||||
| 		for {set i 0} {$i < [llength $args]} {incr i} { | ||||
| 			set arg [lindex $args $i] | ||||
|  | @ -155,21 +175,80 @@ proc exec {args} { | |||
| 		} | ||||
| 		set args [sanitize_command_line $args $i] | ||||
| 		uplevel 1 real_exec $args | ||||
| } | ||||
| 	} | ||||
|  | ||||
| # Override `open` to avoid unsafe PATH lookup | ||||
| 	# Override `open` to avoid unsafe PATH lookup | ||||
|  | ||||
| rename open real_open | ||||
| 	rename open real_open | ||||
|  | ||||
| proc open {args} { | ||||
| 	proc open {args} { | ||||
| 		set arg0 [lindex $args 0] | ||||
| 		if {[string range $arg0 0 0] eq "|"} { | ||||
| 			set command_line [string trim [string range $arg0 1 end]] | ||||
| 			lset args 0 "| [sanitize_command_line $command_line 0]" | ||||
| 		} | ||||
| 		uplevel 1 real_open $args | ||||
| 	} | ||||
|  | ||||
| } else { | ||||
| 	# On non-Windows platforms, auto_execok, exec, and open are safe, and will | ||||
| 	# use the sanitized search path. But, we need _which for these. | ||||
|  | ||||
| 	proc _which {what args} { | ||||
| 		return [lindex [auto_execok $what] 0] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| # Wrap exec/open to sanitize arguments | ||||
|  | ||||
| # unsafe arguments begin with redirections or the pipe or background operators | ||||
| proc is_arg_unsafe {arg} { | ||||
| 	regexp {^([<|>&]|2>)} $arg | ||||
| } | ||||
|  | ||||
| proc make_arg_safe {arg} { | ||||
| 	if {[is_arg_unsafe $arg]} { | ||||
| 		set arg [file join . $arg] | ||||
| 	} | ||||
| 	return $arg | ||||
| } | ||||
|  | ||||
| proc make_arglist_safe {arglist} { | ||||
| 	set res {} | ||||
| 	foreach arg $arglist { | ||||
| 		lappend res [make_arg_safe $arg] | ||||
| 	} | ||||
| 	return $res | ||||
| } | ||||
|  | ||||
| # executes one command | ||||
| # no redirections or pipelines are possible | ||||
| # cmd is a list that specifies the command and its arguments | ||||
| # calls `exec` and returns its value | ||||
| proc safe_exec {cmd} { | ||||
| 	eval exec [make_arglist_safe $cmd] | ||||
| } | ||||
|  | ||||
| # executes one command in the background | ||||
| # no redirections or pipelines are possible | ||||
| # cmd is a list that specifies the command and its arguments | ||||
| # calls `exec` and returns its value | ||||
| proc safe_exec_bg {cmd} { | ||||
| 	eval exec [make_arglist_safe $cmd] & | ||||
| } | ||||
|  | ||||
| proc safe_open_file {filename flags} { | ||||
| 	# a file name starting with "|" would attempt to run a process | ||||
| 	# but such a file name must be treated as a relative path | ||||
| 	# hide the "|" behind "./" | ||||
| 	if {[string index $filename 0] eq "|"} { | ||||
| 		set filename [file join . $filename] | ||||
| 	} | ||||
| 	open $filename $flags | ||||
| } | ||||
|  | ||||
| # End exec/open wrappers | ||||
|  | ||||
| ###################################################################### | ||||
| ## | ||||
| ## locate our library | ||||
|  | @ -270,11 +349,11 @@ unset oguimsg | |||
|  | ||||
| if {[tk windowingsystem] eq "aqua"} { | ||||
| 	catch { | ||||
| 		exec osascript -e [format { | ||||
| 		safe_exec [list osascript -e [format { | ||||
| 			tell application "System Events" | ||||
| 				set frontmost of processes whose unix id is %d to true | ||||
| 			end tell | ||||
| 		} [pid]] | ||||
| 		} [pid]]] | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | @ -304,15 +383,37 @@ if {$_trace >= 0} { | |||
| # branches). | ||||
| set _last_merged_branch {} | ||||
|  | ||||
| proc shellpath {} { | ||||
| 	global _shellpath env | ||||
| 	if {[string match @@* $_shellpath]} { | ||||
| # for testing, allow unconfigured _shellpath | ||||
| if {[string match @@* $_shellpath]} { | ||||
| 	if {[info exists env(SHELL)]} { | ||||
| 			return $env(SHELL) | ||||
| 		set _shellpath $env(SHELL) | ||||
| 	} else { | ||||
| 			return /bin/sh | ||||
| 		} | ||||
| 		set _shellpath /bin/sh | ||||
| 	} | ||||
| } | ||||
|  | ||||
| if {[is_Windows]} { | ||||
| 	set _shellpath [safe_exec [list cygpath -m $_shellpath]] | ||||
| } | ||||
|  | ||||
| if {![file executable $_shellpath] || \ | ||||
| 	!([file pathtype $_shellpath] eq {absolute})} { | ||||
| 	set errmsg "The defined shell ('$_shellpath') is not usable, \ | ||||
| 		it must be an absolute path to an executable." | ||||
| 	puts stderr $errmsg | ||||
|  | ||||
| 	catch {wm withdraw .} | ||||
| 	tk_messageBox \ | ||||
| 		-icon error \ | ||||
| 		-type ok \ | ||||
| 		-title "git-gui: configuration error" \ | ||||
| 		-message $errmsg | ||||
| 	exit 1 | ||||
| } | ||||
|  | ||||
|  | ||||
| proc shellpath {} { | ||||
| 	global _shellpath | ||||
| 	return $_shellpath | ||||
| } | ||||
|  | ||||
|  | @ -494,7 +595,7 @@ proc _git_cmd {name} { | |||
| 			# Tcl on Windows doesn't know it. | ||||
| 			# | ||||
| 			set p [gitexec git-$name] | ||||
| 			set f [open $p r] | ||||
| 			set f [safe_open_file $p r] | ||||
| 			set s [gets $f] | ||||
| 			close $f | ||||
|  | ||||
|  | @ -524,32 +625,14 @@ proc _git_cmd {name} { | |||
| 	return $v | ||||
| } | ||||
|  | ||||
| # Test a file for a hashbang to identify executable scripts on Windows. | ||||
| proc is_shellscript {filename} { | ||||
| 	if {![file exists $filename]} {return 0} | ||||
| 	set f [open $filename r] | ||||
| 	fconfigure $f -encoding binary | ||||
| 	set magic [read $f 2] | ||||
| 	close $f | ||||
| 	return [expr {$magic eq "#!"}] | ||||
| } | ||||
|  | ||||
| # Run a command connected via pipes on stdout. | ||||
| # Run a shell command connected via pipes on stdout. | ||||
| # This is for use with textconv filters and uses sh -c "..." to allow it to | ||||
| # contain a command with arguments. On windows we must check for shell | ||||
| # scripts specifically otherwise just call the filter command. | ||||
| # contain a command with arguments. We presume this | ||||
| # to be a shellscript that the configured shell (/bin/sh by default) knows | ||||
| # how to run. | ||||
| proc open_cmd_pipe {cmd path} { | ||||
| 	global env | ||||
| 	if {![file executable [shellpath]]} { | ||||
| 		set exe [auto_execok [lindex $cmd 0]] | ||||
| 		if {[is_shellscript [lindex $exe 0]]} { | ||||
| 			set run [linsert [auto_execok sh] end -c "$cmd \"\$0\"" $path] | ||||
| 		} else { | ||||
| 			set run [concat $exe [lrange $cmd 1 end] $path] | ||||
| 		} | ||||
| 	} else { | ||||
| 	set run [list [shellpath] -c "$cmd \"\$0\"" $path] | ||||
| 	} | ||||
| 	set run [make_arglist_safe $run] | ||||
| 	return [open |$run r] | ||||
| } | ||||
|  | ||||
|  | @ -559,7 +642,7 @@ proc _lappend_nice {cmd_var} { | |||
|  | ||||
| 	if {![info exists _nice]} { | ||||
| 		set _nice [_which nice] | ||||
| 		if {[catch {exec $_nice git version}]} { | ||||
| 		if {[catch {safe_exec [list $_nice git version]}]} { | ||||
| 			set _nice {} | ||||
| 		} elseif {[is_Windows] && [file dirname $_nice] ne [file dirname $::_git]} { | ||||
| 			set _nice {} | ||||
|  | @ -571,7 +654,11 @@ proc _lappend_nice {cmd_var} { | |||
| } | ||||
|  | ||||
| proc git {args} { | ||||
| 	set fd [eval [list git_read] $args] | ||||
| 	git_redir $args {} | ||||
| } | ||||
|  | ||||
| proc git_redir {cmd redir} { | ||||
| 	set fd [git_read $cmd $redir] | ||||
| 	fconfigure $fd -translation binary -encoding utf-8 | ||||
| 	set result [string trimright [read $fd] "\n"] | ||||
| 	close $fd | ||||
|  | @ -581,88 +668,47 @@ proc git {args} { | |||
| 	return $result | ||||
| } | ||||
|  | ||||
| proc _open_stdout_stderr {cmd} { | ||||
| 	_trace_exec $cmd | ||||
| proc safe_open_command {cmd {redir {}}} { | ||||
| 	set cmd [make_arglist_safe $cmd] | ||||
| 	_trace_exec [concat $cmd $redir] | ||||
| 	if {[catch { | ||||
| 			set fd [open [concat [list | ] $cmd] r] | ||||
| 		set fd [open [concat [list | ] $cmd $redir] r] | ||||
| 	} err]} { | ||||
| 		if {   [lindex $cmd end] eq {2>@1} | ||||
| 		    && $err eq {can not find channel named "1"} | ||||
| 			} { | ||||
| 			# Older versions of Tcl 8.4 don't have this 2>@1 IO | ||||
| 			# redirect operator.  Fallback to |& cat for those. | ||||
| 			# The command was not actually started, so its safe | ||||
| 			# to try to start it a second time. | ||||
| 			# | ||||
| 			set fd [open [concat \ | ||||
| 				[list | ] \ | ||||
| 				[lrange $cmd 0 end-1] \ | ||||
| 				[list |& cat] \ | ||||
| 				] r] | ||||
| 		} else { | ||||
| 		error $err | ||||
| 	} | ||||
| 	} | ||||
| 	fconfigure $fd -eofchar {} | ||||
| 	return $fd | ||||
| } | ||||
|  | ||||
| proc git_read {args} { | ||||
| 	set opt [list] | ||||
| proc git_read {cmd {redir {}}} { | ||||
| 	set cmdp [_git_cmd [lindex $cmd 0]] | ||||
| 	set cmd [lrange $cmd 1 end] | ||||
|  | ||||
| 	while {1} { | ||||
| 		switch -- [lindex $args 0] { | ||||
| 		--nice { | ||||
| 			_lappend_nice opt | ||||
| 		} | ||||
|  | ||||
| 		--stderr { | ||||
| 			lappend args 2>@1 | ||||
| 		} | ||||
|  | ||||
| 		default { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		set args [lrange $args 1 end] | ||||
| 	} | ||||
|  | ||||
| 	set cmdp [_git_cmd [lindex $args 0]] | ||||
| 	set args [lrange $args 1 end] | ||||
|  | ||||
| 	return [_open_stdout_stderr [concat $opt $cmdp $args]] | ||||
| 	return [safe_open_command [concat $cmdp $cmd] $redir] | ||||
| } | ||||
|  | ||||
| proc git_write {args} { | ||||
| proc git_read_nice {cmd} { | ||||
| 	set opt [list] | ||||
|  | ||||
| 	while {1} { | ||||
| 		switch -- [lindex $args 0] { | ||||
| 		--nice { | ||||
| 	_lappend_nice opt | ||||
| 		} | ||||
|  | ||||
| 		default { | ||||
| 			break | ||||
| 		} | ||||
| 	set cmdp [_git_cmd [lindex $cmd 0]] | ||||
| 	set cmd [lrange $cmd 1 end] | ||||
|  | ||||
| 		} | ||||
| 	return [safe_open_command [concat $opt $cmdp $cmd]] | ||||
| } | ||||
|  | ||||
| 		set args [lrange $args 1 end] | ||||
| 	} | ||||
| proc git_write {cmd} { | ||||
| 	set cmd [make_arglist_safe $cmd] | ||||
| 	set cmdp [_git_cmd [lindex $cmd 0]] | ||||
| 	set cmd [lrange $cmd 1 end] | ||||
|  | ||||
| 	set cmdp [_git_cmd [lindex $args 0]] | ||||
| 	set args [lrange $args 1 end] | ||||
|  | ||||
| 	_trace_exec [concat $opt $cmdp $args] | ||||
| 	return [open [concat [list | ] $opt $cmdp $args] w] | ||||
| 	_trace_exec [concat $cmdp $cmd] | ||||
| 	return [open [concat [list | ] $cmdp $cmd] w] | ||||
| } | ||||
|  | ||||
| proc githook_read {hook_name args} { | ||||
| 	set cmd [concat git hook run --ignore-missing $hook_name -- $args 2>@1] | ||||
| 	return [_open_stdout_stderr $cmd] | ||||
| 	git_read [concat [list hook run --ignore-missing $hook_name --] $args] [list 2>@1] | ||||
| } | ||||
|  | ||||
| proc kill_file_process {fd} { | ||||
|  | @ -670,9 +716,9 @@ proc kill_file_process {fd} { | |||
|  | ||||
| 	catch { | ||||
| 		if {[is_Windows]} { | ||||
| 			exec taskkill /pid $process | ||||
| 			safe_exec [list taskkill /pid $process] | ||||
| 		} else { | ||||
| 			exec kill $process | ||||
| 			safe_exec [list kill $process] | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -698,7 +744,7 @@ proc sq {value} { | |||
| proc load_current_branch {} { | ||||
| 	global current_branch is_detached | ||||
|  | ||||
| 	set fd [open [gitdir HEAD] r] | ||||
| 	set fd [safe_open_file [gitdir HEAD] r] | ||||
| 	fconfigure $fd -translation binary -encoding utf-8 | ||||
| 	if {[gets $fd ref] < 1} { | ||||
| 		set ref {} | ||||
|  | @ -1068,7 +1114,7 @@ You are using [git-version]: | |||
| ## configure our library | ||||
|  | ||||
| set idx [file join $oguilib tclIndex] | ||||
| if {[catch {set fd [open $idx r]} err]} { | ||||
| if {[catch {set fd [safe_open_file $idx r]} err]} { | ||||
| 	catch {wm withdraw .} | ||||
| 	tk_messageBox \ | ||||
| 		-icon error \ | ||||
|  | @ -1106,16 +1152,15 @@ unset -nocomplain idx fd | |||
| ## | ||||
| ## config file parsing | ||||
|  | ||||
| git-version proc _parse_config {arr_name args} { | ||||
| 	>= 1.5.3 { | ||||
| proc _parse_config {arr_name args} { | ||||
| 	upvar $arr_name arr | ||||
| 	array unset arr | ||||
| 	set buf {} | ||||
| 	catch { | ||||
| 			set fd_rc [eval \ | ||||
| 				[list git_read config] \ | ||||
| 		set fd_rc [git_read \ | ||||
| 			[concat config \ | ||||
| 			$args \ | ||||
| 				[list --null --list]] | ||||
| 			--null --list]] | ||||
| 		fconfigure $fd_rc -translation binary -encoding utf-8 | ||||
| 		set buf [read $fd_rc] | ||||
| 		close $fd_rc | ||||
|  | @ -1133,28 +1178,6 @@ git-version proc _parse_config {arr_name args} { | |||
| 			set arr($name) {} | ||||
| 		} | ||||
| 	} | ||||
| 	} | ||||
| 	default { | ||||
| 		upvar $arr_name arr | ||||
| 		array unset arr | ||||
| 		catch { | ||||
| 			set fd_rc [eval [list git_read config --list] $args] | ||||
| 			while {[gets $fd_rc line] >= 0} { | ||||
| 				if {[regexp {^([^=]+)=(.*)$} $line line name value]} { | ||||
| 					if {[is_many_config $name]} { | ||||
| 						lappend arr($name) $value | ||||
| 					} else { | ||||
| 						set arr($name) $value | ||||
| 					} | ||||
| 				} elseif {[regexp {^([^=]+)$} $line line name]} { | ||||
| 					# no value given, but interpreting them as | ||||
| 					# boolean will be handled as true | ||||
| 					set arr($name) {} | ||||
| 				} | ||||
| 			} | ||||
| 			close $fd_rc | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| proc load_config {include_global} { | ||||
|  | @ -1427,7 +1450,7 @@ proc repository_state {ctvar hdvar mhvar} { | |||
| 	set merge_head [gitdir MERGE_HEAD] | ||||
| 	if {[file exists $merge_head]} { | ||||
| 		set ct merge | ||||
| 		set fd_mh [open $merge_head r] | ||||
| 		set fd_mh [safe_open_file $merge_head r] | ||||
| 		while {[gets $fd_mh line] >= 0} { | ||||
| 			lappend mh $line | ||||
| 		} | ||||
|  | @ -1446,7 +1469,7 @@ proc PARENT {} { | |||
| 		return $p | ||||
| 	} | ||||
| 	if {$empty_tree eq {}} { | ||||
| 		set empty_tree [git mktree << {}] | ||||
| 		set empty_tree [git_redir [list mktree] [list << {}]] | ||||
| 	} | ||||
| 	return $empty_tree | ||||
| } | ||||
|  | @ -1505,12 +1528,12 @@ proc rescan {after {honor_trustmtime 1}} { | |||
| 	} else { | ||||
| 		set rescan_active 1 | ||||
| 		ui_status [mc "Refreshing file status..."] | ||||
| 		set fd_rf [git_read update-index \ | ||||
| 		set fd_rf [git_read [list update-index \ | ||||
| 			-q \ | ||||
| 			--unmerged \ | ||||
| 			--ignore-missing \ | ||||
| 			--refresh \ | ||||
| 			] | ||||
| 			]] | ||||
| 		fconfigure $fd_rf -blocking 0 -translation binary | ||||
| 		fileevent $fd_rf readable \ | ||||
| 			[list rescan_stage2 $fd_rf $after] | ||||
|  | @ -1550,11 +1573,11 @@ proc rescan_stage2 {fd after} { | |||
| 	set rescan_active 2 | ||||
| 	ui_status [mc "Scanning for modified files ..."] | ||||
| 	if {[git-version >= "1.7.2"]} { | ||||
| 		set fd_di [git_read diff-index --cached --ignore-submodules=dirty -z [PARENT]] | ||||
| 		set fd_di [git_read [list diff-index --cached --ignore-submodules=dirty -z [PARENT]]] | ||||
| 	} else { | ||||
| 		set fd_di [git_read diff-index --cached -z [PARENT]] | ||||
| 		set fd_di [git_read [list diff-index --cached -z [PARENT]]] | ||||
| 	} | ||||
| 	set fd_df [git_read diff-files -z] | ||||
| 	set fd_df [git_read [list diff-files -z]] | ||||
|  | ||||
| 	fconfigure $fd_di -blocking 0 -translation binary -encoding binary | ||||
| 	fconfigure $fd_df -blocking 0 -translation binary -encoding binary | ||||
|  | @ -1563,7 +1586,7 @@ proc rescan_stage2 {fd after} { | |||
| 	fileevent $fd_df readable [list read_diff_files $fd_df $after] | ||||
|  | ||||
| 	if {[is_config_true gui.displayuntracked]} { | ||||
| 		set fd_lo [eval git_read ls-files --others -z $ls_others] | ||||
| 		set fd_lo [git_read [concat ls-files --others -z $ls_others]] | ||||
| 		fconfigure $fd_lo -blocking 0 -translation binary -encoding binary | ||||
| 		fileevent $fd_lo readable [list read_ls_others $fd_lo $after] | ||||
| 		incr rescan_active | ||||
|  | @ -1575,7 +1598,7 @@ proc load_message {file {encoding {}}} { | |||
|  | ||||
| 	set f [gitdir $file] | ||||
| 	if {[file isfile $f]} { | ||||
| 		if {[catch {set fd [open $f r]}]} { | ||||
| 		if {[catch {set fd [safe_open_file $f r]}]} { | ||||
| 			return 0 | ||||
| 		} | ||||
| 		fconfigure $fd -eofchar {} | ||||
|  | @ -1599,23 +1622,23 @@ proc run_prepare_commit_msg_hook {} { | |||
| 	# it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an | ||||
| 	# empty file but existent file. | ||||
|  | ||||
| 	set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a] | ||||
| 	set fd_pcm [safe_open_file [gitdir PREPARE_COMMIT_MSG] a] | ||||
|  | ||||
| 	if {[file isfile [gitdir MERGE_MSG]]} { | ||||
| 		set pcm_source "merge" | ||||
| 		set fd_mm [open [gitdir MERGE_MSG] r] | ||||
| 		set fd_mm [safe_open_file [gitdir MERGE_MSG] r] | ||||
| 		fconfigure $fd_mm -encoding utf-8 | ||||
| 		puts -nonewline $fd_pcm [read $fd_mm] | ||||
| 		close $fd_mm | ||||
| 	} elseif {[file isfile [gitdir SQUASH_MSG]]} { | ||||
| 		set pcm_source "squash" | ||||
| 		set fd_sm [open [gitdir SQUASH_MSG] r] | ||||
| 		set fd_sm [safe_open_file [gitdir SQUASH_MSG] r] | ||||
| 		fconfigure $fd_sm -encoding utf-8 | ||||
| 		puts -nonewline $fd_pcm [read $fd_sm] | ||||
| 		close $fd_sm | ||||
| 	} elseif {[file isfile [get_config commit.template]]} { | ||||
| 		set pcm_source "template" | ||||
| 		set fd_sm [open [get_config commit.template] r] | ||||
| 		set fd_sm [safe_open_file [get_config commit.template] r] | ||||
| 		fconfigure $fd_sm -encoding utf-8 | ||||
| 		puts -nonewline $fd_pcm [read $fd_sm] | ||||
| 		close $fd_sm | ||||
|  | @ -2205,7 +2228,7 @@ proc do_gitk {revs {is_submodule false}} { | |||
| 			unset env(GIT_DIR) | ||||
| 			unset env(GIT_WORK_TREE) | ||||
| 		} | ||||
| 		eval exec $cmd $revs "--" "--" & | ||||
| 		safe_exec_bg [concat $cmd $revs "--" "--"] | ||||
|  | ||||
| 		set env(GIT_DIR) $_gitdir | ||||
| 		set env(GIT_WORK_TREE) $_gitworktree | ||||
|  | @ -2242,7 +2265,7 @@ proc do_git_gui {} { | |||
| 		set pwd [pwd] | ||||
| 		cd $current_diff_path | ||||
|  | ||||
| 		eval exec $exe gui & | ||||
| 		safe_exec_bg [concat $exe gui] | ||||
|  | ||||
| 		set env(GIT_DIR) $_gitdir | ||||
| 		set env(GIT_WORK_TREE) $_gitworktree | ||||
|  | @ -2273,16 +2296,18 @@ proc get_explorer {} { | |||
|  | ||||
| proc do_explore {} { | ||||
| 	global _gitworktree | ||||
| 	set explorer [get_explorer] | ||||
| 	eval exec $explorer [list [file nativename $_gitworktree]] & | ||||
| 	set cmd [get_explorer] | ||||
| 	lappend cmd [file nativename $_gitworktree] | ||||
| 	safe_exec_bg $cmd | ||||
| } | ||||
|  | ||||
| # Open file relative to the working tree by the default associated app. | ||||
| proc do_file_open {file} { | ||||
| 	global _gitworktree | ||||
| 	set explorer [get_explorer] | ||||
| 	set cmd [get_explorer] | ||||
| 	set full_file_path [file join $_gitworktree $file] | ||||
| 	exec $explorer [file nativename $full_file_path] & | ||||
| 	lappend cmd [file nativename $full_file_path] | ||||
| 	safe_exec_bg $cmd | ||||
| } | ||||
|  | ||||
| set is_quitting 0 | ||||
|  | @ -2316,7 +2341,7 @@ proc do_quit {{rc {1}}} { | |||
| 			if {![string match amend* $commit_type] | ||||
| 				&& $msg ne {}} { | ||||
| 				catch { | ||||
| 					set fd [open $save w] | ||||
| 					set fd [safe_open_file $save w] | ||||
| 					fconfigure $fd -encoding utf-8 | ||||
| 					puts -nonewline $fd $msg | ||||
| 					close $fd | ||||
|  | @ -2760,17 +2785,16 @@ if {![is_bare]} { | |||
|  | ||||
| if {[is_Windows]} { | ||||
| 	# Use /git-bash.exe if available | ||||
| 	set normalized [file normalize $::argv0] | ||||
| 	regsub "/mingw../libexec/git-core/git-gui$" \ | ||||
| 		$normalized "/git-bash.exe" cmdLine | ||||
| 	if {$cmdLine != $normalized && [file exists $cmdLine]} { | ||||
| 		set cmdLine [list "Git Bash" $cmdLine &] | ||||
| 	set _git_bash [safe_exec [list cygpath -m /git-bash.exe]] | ||||
| 	if {[file executable $_git_bash]} { | ||||
| 		set _bash_cmdline [list "Git Bash" $_git_bash] | ||||
| 	} else { | ||||
| 		set cmdLine [list "Git Bash" bash --login -l &] | ||||
| 		set _bash_cmdline [list "Git Bash" bash --login -l] | ||||
| 	} | ||||
| 	.mbar.repository add command \ | ||||
| 		-label [mc "Git Bash"] \ | ||||
| 		-command {eval exec [auto_execok start] $cmdLine} | ||||
| 		-command {safe_exec_bg [concat [list [_which cmd] /c start] $_bash_cmdline]} | ||||
| 	unset _git_bash | ||||
| } | ||||
|  | ||||
| if {[is_Windows] || ![is_bare]} { | ||||
|  | @ -4079,7 +4103,7 @@ if {[winfo exists $ui_comm]} { | |||
| 				} | ||||
| 			} elseif {$m} { | ||||
| 				catch { | ||||
| 					set fd [open [gitdir GITGUI_BCK] w] | ||||
| 					set fd [safe_open_file [gitdir GITGUI_BCK] w] | ||||
| 					fconfigure $fd -encoding utf-8 | ||||
| 					puts -nonewline $fd $msg | ||||
| 					close $fd | ||||
|  |  | |||
|  | @ -481,14 +481,14 @@ method _load {jump} { | |||
| 		if {$do_textconv ne 0} { | ||||
| 			set fd [open_cmd_pipe $textconv $path] | ||||
| 		} else { | ||||
| 			set fd [open $path r] | ||||
| 			set fd [safe_open_file $path r] | ||||
| 		} | ||||
| 		fconfigure $fd -eofchar {} | ||||
| 	} else { | ||||
| 		if {$do_textconv ne 0} { | ||||
| 			set fd [git_read cat-file --textconv "$commit:$path"] | ||||
| 			set fd [git_read [list cat-file --textconv "$commit:$path"]] | ||||
| 		} else { | ||||
| 			set fd [git_read cat-file blob "$commit:$path"] | ||||
| 			set fd [git_read [list cat-file blob "$commit:$path"]] | ||||
| 		} | ||||
| 	} | ||||
| 	fconfigure $fd \ | ||||
|  | @ -617,7 +617,7 @@ method _exec_blame {cur_w cur_d options cur_s} { | |||
| 	} | ||||
|  | ||||
| 	lappend options -- $path | ||||
| 	set fd [eval git_read --nice blame $options] | ||||
| 	set fd [git_read_nice [concat blame $options]] | ||||
| 	fconfigure $fd -blocking 0 -translation lf -encoding utf-8 | ||||
| 	fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d] | ||||
| 	set current_fd $fd | ||||
|  | @ -986,7 +986,7 @@ method _showcommit {cur_w lno} { | |||
| 		if {[catch {set msg $header($cmit,message)}]} { | ||||
| 			set msg {} | ||||
| 			catch { | ||||
| 				set fd [git_read cat-file commit $cmit] | ||||
| 				set fd [git_read [list cat-file commit $cmit]] | ||||
| 				fconfigure $fd -encoding binary -translation lf | ||||
| 				# By default commits are assumed to be in utf-8 | ||||
| 				set enc utf-8 | ||||
|  | @ -1134,7 +1134,7 @@ method _blameparent {} { | |||
| 		} else { | ||||
| 			set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path] | ||||
| 		} | ||||
| 		if {[catch {set fd [eval git_read $diffcmd]} err]} { | ||||
| 		if {[catch {set fd [git_read $diffcmd]} err]} { | ||||
| 			$status_operation stop [mc "Unable to display parent"] | ||||
| 			error_popup [strcat [mc "Error loading diff:"] "\n\n$err"] | ||||
| 			return | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ proc load_all_heads {} { | |||
| 	set rh refs/heads | ||||
| 	set rh_len [expr {[string length $rh] + 1}] | ||||
| 	set all_heads [list] | ||||
| 	set fd [git_read for-each-ref --format=%(refname) $rh] | ||||
| 	set fd [git_read [list for-each-ref --format=%(refname) $rh]] | ||||
| 	fconfigure $fd -translation binary -encoding utf-8 | ||||
| 	while {[gets $fd line] > 0} { | ||||
| 		if {!$some_heads_tracking || ![is_tracking_branch $line]} { | ||||
|  | @ -21,10 +21,10 @@ proc load_all_heads {} { | |||
|  | ||||
| proc load_all_tags {} { | ||||
| 	set all_tags [list] | ||||
| 	set fd [git_read for-each-ref \ | ||||
| 	set fd [git_read [list for-each-ref \ | ||||
| 		--sort=-taggerdate \ | ||||
| 		--format=%(refname) \ | ||||
| 		refs/tags] | ||||
| 		refs/tags]] | ||||
| 	fconfigure $fd -translation binary -encoding utf-8 | ||||
| 	while {[gets $fd line] > 0} { | ||||
| 		if {![regsub ^refs/tags/ $line {} name]} continue | ||||
|  |  | |||
|  | @ -196,7 +196,7 @@ method _ls {tree_id {name {}}} { | |||
| 	lappend browser_stack [list $tree_id $name] | ||||
| 	$w conf -state disabled | ||||
|  | ||||
| 	set fd [git_read ls-tree -z $tree_id] | ||||
| 	set fd [git_read [list ls-tree -z $tree_id]] | ||||
| 	fconfigure $fd -blocking 0 -translation binary -encoding utf-8 | ||||
| 	fileevent $fd readable [cb _read $fd] | ||||
| } | ||||
|  |  | |||
|  | @ -304,12 +304,12 @@ The rescan will be automatically started now. | |||
| 		_readtree $this | ||||
| 	} else { | ||||
| 		ui_status [mc "Refreshing file status..."] | ||||
| 		set fd [git_read update-index \ | ||||
| 		set fd [git_read [list update-index \ | ||||
| 			-q \ | ||||
| 			--unmerged \ | ||||
| 			--ignore-missing \ | ||||
| 			--refresh \ | ||||
| 			] | ||||
| 			]] | ||||
| 		fconfigure $fd -blocking 0 -translation binary | ||||
| 		fileevent $fd readable [cb _refresh_wait $fd] | ||||
| 	} | ||||
|  | @ -345,14 +345,15 @@ method _readtree {} { | |||
| 		[mc "Updating working directory to '%s'..." [_name $this]] \ | ||||
| 		[mc "files checked out"]] | ||||
|  | ||||
| 	set fd [git_read --stderr read-tree \ | ||||
| 	set fd [git_read [list read-tree \ | ||||
| 		-m \ | ||||
| 		-u \ | ||||
| 		-v \ | ||||
| 		--exclude-per-directory=.gitignore \ | ||||
| 		$HEAD \ | ||||
| 		$new_hash \ | ||||
| 		] | ||||
| 		] \ | ||||
| 		[list 2>@1]] | ||||
| 	fconfigure $fd -blocking 0 -translation binary | ||||
| 	fileevent $fd readable [cb _readtree_wait $fd $status_bar_operation] | ||||
| } | ||||
|  | @ -510,18 +511,8 @@ method _update_repo_state {} { | |||
| 	delete_this | ||||
| } | ||||
|  | ||||
| git-version proc _detach_HEAD {log new} { | ||||
| 	>= 1.5.3 { | ||||
| proc _detach_HEAD {log new} { | ||||
| 	git update-ref --no-deref -m $log HEAD $new | ||||
| 	} | ||||
| 	default { | ||||
| 		set p [gitdir HEAD] | ||||
| 		file delete $p | ||||
| 		set fd [open $p w] | ||||
| 		fconfigure $fd -translation lf -encoding utf-8 | ||||
| 		puts $fd $new | ||||
| 		close $fd | ||||
| 	} | ||||
| } | ||||
|  | ||||
| method _confirm_reset {cur} { | ||||
|  | @ -582,7 +573,7 @@ method _confirm_reset {cur} { | |||
| 	pack $w.buttons.cancel -side right -padx 5 | ||||
| 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10 | ||||
|  | ||||
| 	set fd [git_read rev-list --pretty=oneline $cur ^$new_hash] | ||||
| 	set fd [git_read [list rev-list --pretty=oneline $cur ^$new_hash]] | ||||
| 	while {[gets $fd line] > 0} { | ||||
| 		set abbr [string range $line 0 7] | ||||
| 		set subj [string range $line 41 end] | ||||
|  |  | |||
|  | @ -641,8 +641,8 @@ method _do_clone2 {} { | |||
| 			set pwd [pwd] | ||||
| 			if {[catch { | ||||
| 				file mkdir [gitdir objects info] | ||||
| 				set f_in [open [file join $objdir info alternates] r] | ||||
| 				set f_cp [open [gitdir objects info alternates] w] | ||||
| 				set f_in [safe_open_file [file join $objdir info alternates] r] | ||||
| 				set f_cp [safe_open_file [gitdir objects info alternates] w] | ||||
| 				fconfigure $f_in -translation binary -encoding binary | ||||
| 				fconfigure $f_cp -translation binary -encoding binary | ||||
| 				cd $objdir | ||||
|  | @ -727,7 +727,7 @@ method _do_clone2 {} { | |||
| 			[cb _do_clone_tags] | ||||
| 	} | ||||
| 	shared { | ||||
| 		set fd [open [gitdir objects info alternates] w] | ||||
| 		set fd [safe_open_file [gitdir objects info alternates] w] | ||||
| 		fconfigure $fd -translation binary | ||||
| 		puts $fd $objdir | ||||
| 		close $fd | ||||
|  | @ -760,8 +760,8 @@ method _copy_files {objdir tocopy} { | |||
| 	} | ||||
| 	foreach p $tocopy { | ||||
| 		if {[catch { | ||||
| 				set f_in [open [file join $objdir $p] r] | ||||
| 				set f_cp [open [file join .git objects $p] w] | ||||
| 				set f_in [safe_open_file [file join $objdir $p] r] | ||||
| 				set f_cp [safe_open_file [file join .git objects $p] w] | ||||
| 				fconfigure $f_in -translation binary -encoding binary | ||||
| 				fconfigure $f_cp -translation binary -encoding binary | ||||
|  | ||||
|  | @ -818,12 +818,12 @@ method _clone_refs {} { | |||
| 		error_popup [mc "Not a Git repository: %s" [file tail $origin_url]] | ||||
| 		return 0 | ||||
| 	} | ||||
| 	set fd_in [git_read for-each-ref \ | ||||
| 	set fd_in [git_read [list for-each-ref \ | ||||
| 		--tcl \ | ||||
| 		{--format=list %(refname) %(objectname) %(*objectname)}] | ||||
| 		{--format=list %(refname) %(objectname) %(*objectname)}]] | ||||
| 	cd $pwd | ||||
|  | ||||
| 	set fd [open [gitdir packed-refs] w] | ||||
| 	set fd [safe_open_file [gitdir packed-refs] w] | ||||
| 	fconfigure $fd -translation binary | ||||
| 	puts $fd "# pack-refs with: peeled" | ||||
| 	while {[gets $fd_in line] >= 0} { | ||||
|  | @ -877,7 +877,7 @@ method _do_clone_full_end {ok} { | |||
|  | ||||
| 		set HEAD {} | ||||
| 		if {[file exists [gitdir FETCH_HEAD]]} { | ||||
| 			set fd [open [gitdir FETCH_HEAD] r] | ||||
| 			set fd [safe_open_file [gitdir FETCH_HEAD] r] | ||||
| 			while {[gets $fd line] >= 0} { | ||||
| 				if {[regexp "^(.{40})\t\t" $line line HEAD]} { | ||||
| 					break | ||||
|  | @ -953,13 +953,14 @@ method _do_clone_checkout {HEAD} { | |||
| 		[mc "files"]] | ||||
|  | ||||
| 	set readtree_err {} | ||||
| 	set fd [git_read --stderr read-tree \ | ||||
| 	set fd [git_read [list read-tree \ | ||||
| 		-m \ | ||||
| 		-u \ | ||||
| 		-v \ | ||||
| 		HEAD \ | ||||
| 		HEAD \ | ||||
| 		] | ||||
| 		] \ | ||||
| 		[list 2>@1]] | ||||
| 	fconfigure $fd -blocking 0 -translation binary | ||||
| 	fileevent $fd readable [cb _readtree_wait $fd] | ||||
| } | ||||
|  |  | |||
|  | @ -146,14 +146,14 @@ constructor _new {path unmerged_only title} { | |||
| 	append fmt { %(*subject)} | ||||
| 	append fmt {]} | ||||
| 	set all_refn [list] | ||||
| 	set fr_fd [git_read for-each-ref \ | ||||
| 	set fr_fd [git_read [list for-each-ref \ | ||||
| 		--tcl \ | ||||
| 		--sort=-taggerdate \ | ||||
| 		--format=$fmt \ | ||||
| 		refs/heads \ | ||||
| 		refs/remotes \ | ||||
| 		refs/tags \ | ||||
| 		] | ||||
| 		]] | ||||
| 	fconfigure $fr_fd -translation lf -encoding utf-8 | ||||
| 	while {[gets $fr_fd line] > 0} { | ||||
| 		set line [eval $line] | ||||
|  | @ -176,7 +176,7 @@ constructor _new {path unmerged_only title} { | |||
| 	close $fr_fd | ||||
|  | ||||
| 	if {$unmerged_only} { | ||||
| 		set fr_fd [git_read rev-list --all ^$::HEAD] | ||||
| 		set fr_fd [git_read [list rev-list --all ^$::HEAD]] | ||||
| 		while {[gets $fr_fd sha1] > 0} { | ||||
| 			if {[catch {set rlst $cmt_refn($sha1)}]} continue | ||||
| 			foreach refn $rlst { | ||||
|  | @ -579,7 +579,7 @@ method _reflog_last {name} { | |||
|  | ||||
| 	set last {} | ||||
| 	if {[catch {set last [file mtime [gitdir $name]]}] | ||||
| 	&& ![catch {set g [open [gitdir logs $name] r]}]} { | ||||
| 	&& ![catch {set g [safe_open_file [gitdir logs $name] r]}]} { | ||||
| 		fconfigure $g -translation binary | ||||
| 		while {[gets $g line] >= 0} { | ||||
| 			if {[regexp {> ([1-9][0-9]*) } $line line when]} { | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ You are currently in the middle of a merge that has not been fully completed.  Y | |||
| 	if {[catch { | ||||
| 			set name "" | ||||
| 			set email "" | ||||
| 			set fd [git_read cat-file commit $curHEAD] | ||||
| 			set fd [git_read [list cat-file commit $curHEAD]] | ||||
| 			fconfigure $fd -encoding binary -translation lf | ||||
| 			# By default commits are assumed to be in utf-8 | ||||
| 			set enc utf-8 | ||||
|  | @ -236,7 +236,7 @@ A good commit message has the following format: | |||
| 	# -- Build the message file. | ||||
| 	# | ||||
| 	set msg_p [gitdir GITGUI_EDITMSG] | ||||
| 	set msg_wt [open $msg_p w] | ||||
| 	set msg_wt [safe_open_file $msg_p w] | ||||
| 	fconfigure $msg_wt -translation lf | ||||
| 	setup_commit_encoding $msg_wt | ||||
| 	puts $msg_wt $msg | ||||
|  | @ -336,7 +336,7 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} { | |||
|  | ||||
| proc commit_writetree {curHEAD msg_p} { | ||||
| 	ui_status [mc "Committing changes..."] | ||||
| 	set fd_wt [git_read write-tree] | ||||
| 	set fd_wt [git_read [list write-tree]] | ||||
| 	fileevent $fd_wt readable \ | ||||
| 		[list commit_committree $fd_wt $curHEAD $msg_p] | ||||
| } | ||||
|  | @ -361,7 +361,7 @@ proc commit_committree {fd_wt curHEAD msg_p} { | |||
| 	# -- Verify this wasn't an empty change. | ||||
| 	# | ||||
| 	if {$commit_type eq {normal}} { | ||||
| 		set fd_ot [git_read cat-file commit $PARENT] | ||||
| 		set fd_ot [git_read [list cat-file commit $PARENT]] | ||||
| 		fconfigure $fd_ot -encoding binary -translation lf | ||||
| 		set old_tree [gets $fd_ot] | ||||
| 		close $fd_ot | ||||
|  | @ -399,8 +399,8 @@ A rescan will be automatically started now. | |||
| 	foreach p [concat $PARENT $MERGE_HEAD] { | ||||
| 		lappend cmd -p $p | ||||
| 	} | ||||
| 	lappend cmd <$msg_p | ||||
| 	if {[catch {set cmt_id [eval git $cmd]} err]} { | ||||
| 	set msgtxt [list <$msg_p] | ||||
| 	if {[catch {set cmt_id [git_redir $cmd $msgtxt]} err]} { | ||||
| 		catch {file delete $msg_p} | ||||
| 		error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"] | ||||
| 		ui_status [mc "Commit failed."] | ||||
|  | @ -420,7 +420,7 @@ A rescan will be automatically started now. | |||
| 	if {$commit_type ne {normal}} { | ||||
| 		append reflogm " ($commit_type)" | ||||
| 	} | ||||
| 	set msg_fd [open $msg_p r] | ||||
| 	set msg_fd [safe_open_file $msg_p r] | ||||
| 	setup_commit_encoding $msg_fd 1 | ||||
| 	gets $msg_fd subject | ||||
| 	close $msg_fd | ||||
|  |  | |||
|  | @ -92,10 +92,9 @@ method _init {} { | |||
|  | ||||
| method exec {cmd {after {}}} { | ||||
| 	if {[lindex $cmd 0] eq {git}} { | ||||
| 		set fd_f [eval git_read --stderr [lrange $cmd 1 end]] | ||||
| 		set fd_f [git_read [lrange $cmd 1 end] [list 2>@1]] | ||||
| 	} else { | ||||
| 		lappend cmd 2>@1 | ||||
| 		set fd_f [_open_stdout_stderr $cmd] | ||||
| 		set fd_f [safe_open_command $cmd [list 2>@1]] | ||||
| 	} | ||||
| 	fconfigure $fd_f -blocking 0 -translation binary -encoding [encoding system] | ||||
| 	fileevent $fd_f readable [cb _read $fd_f $after] | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
|  | ||||
| proc do_stats {} { | ||||
| 	global use_ttk NS | ||||
| 	set fd [git_read count-objects -v] | ||||
| 	set fd [git_read [list count-objects -v]] | ||||
| 	while {[gets $fd line] > 0} { | ||||
| 		if {[regexp {^([^:]+): (\d+)$} $line _ name value]} { | ||||
| 			set stats($name) $value | ||||
|  |  | |||
|  | @ -191,7 +191,7 @@ proc show_other_diff {path w m cont_info} { | |||
| 					set sz [string length $content] | ||||
| 				} | ||||
| 				file { | ||||
| 					set fd [open $path r] | ||||
| 					set fd [safe_open_file $path r] | ||||
| 					fconfigure $fd \ | ||||
| 						-eofchar {} \ | ||||
| 						-encoding [get_path_encoding $path] | ||||
|  | @ -215,7 +215,7 @@ proc show_other_diff {path w m cont_info} { | |||
| 			$ui_diff insert end \ | ||||
| 				"* [mc "Git Repository (subproject)"]\n" \ | ||||
| 				d_info | ||||
| 		} elseif {![catch {set type [exec file $path]}]} { | ||||
| 		} elseif {![catch {set type [safe_exec [list file $path]]}]} { | ||||
| 			set n [string length $path] | ||||
| 			if {[string equal -length $n $path $type]} { | ||||
| 				set type [string range $type $n end] | ||||
|  | @ -327,7 +327,7 @@ proc start_show_diff {cont_info {add_opts {}}} { | |||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if {[catch {set fd [eval git_read --nice $cmd]} err]} { | ||||
| 	if {[catch {set fd [git_read_nice $cmd]} err]} { | ||||
| 		set diff_active 0 | ||||
| 		unlock_index | ||||
| 		ui_status [mc "Unable to display %s" [escape_path $path]] | ||||
|  | @ -603,7 +603,7 @@ proc apply_or_revert_hunk {x y revert} { | |||
|  | ||||
| 	if {[catch { | ||||
| 		set enc [get_path_encoding $current_diff_path] | ||||
| 		set p [eval git_write $apply_cmd] | ||||
| 		set p [git_write $apply_cmd] | ||||
| 		fconfigure $p -translation binary -encoding $enc | ||||
| 		puts -nonewline $p $wholepatch | ||||
| 		close $p} err]} { | ||||
|  | @ -839,7 +839,7 @@ proc apply_or_revert_range_or_line {x y revert} { | |||
|  | ||||
| 	if {[catch { | ||||
| 		set enc [get_path_encoding $current_diff_path] | ||||
| 		set p [eval git_write $apply_cmd] | ||||
| 		set p [git_write $apply_cmd] | ||||
| 		fconfigure $p -translation binary -encoding $enc | ||||
| 		puts -nonewline $p $current_diff_header | ||||
| 		puts -nonewline $p $wholepatch | ||||
|  | @ -876,7 +876,7 @@ proc undo_last_revert {} { | |||
|  | ||||
| 	if {[catch { | ||||
| 		set enc $last_revert_enc | ||||
| 		set p [eval git_write $apply_cmd] | ||||
| 		set p [git_write $apply_cmd] | ||||
| 		fconfigure $p -translation binary -encoding $enc | ||||
| 		puts -nonewline $p $last_revert | ||||
| 		close $p} err]} { | ||||
|  |  | |||
|  | @ -75,7 +75,7 @@ proc update_indexinfo {msg path_list after} { | |||
| 	if {$batch > 25} {set batch 25} | ||||
|  | ||||
| 	set status_bar_operation [$::main_status start $msg [mc "files"]] | ||||
| 	set fd [git_write update-index -z --index-info] | ||||
| 	set fd [git_write [list update-index -z --index-info]] | ||||
| 	fconfigure $fd \ | ||||
| 		-blocking 0 \ | ||||
| 		-buffering full \ | ||||
|  | @ -144,7 +144,7 @@ proc update_index {msg path_list after} { | |||
| 	if {$batch > 25} {set batch 25} | ||||
|  | ||||
| 	set status_bar_operation [$::main_status start $msg [mc "files"]] | ||||
| 	set fd [git_write update-index --add --remove -z --stdin] | ||||
| 	set fd [git_write [list update-index --add --remove -z --stdin]] | ||||
| 	fconfigure $fd \ | ||||
| 		-blocking 0 \ | ||||
| 		-buffering full \ | ||||
|  | @ -218,13 +218,13 @@ proc checkout_index {msg path_list after capture_error} { | |||
| 	if {$batch > 25} {set batch 25} | ||||
|  | ||||
| 	set status_bar_operation [$::main_status start $msg [mc "files"]] | ||||
| 	set fd [git_write checkout-index \ | ||||
| 	set fd [git_write [list checkout-index \ | ||||
| 		--index \ | ||||
| 		--quiet \ | ||||
| 		--force \ | ||||
| 		-z \ | ||||
| 		--stdin \ | ||||
| 		] | ||||
| 		]] | ||||
| 	fconfigure $fd \ | ||||
| 		-blocking 0 \ | ||||
| 		-buffering full \ | ||||
|  |  | |||
|  | @ -93,7 +93,7 @@ method _start {} { | |||
| 	set spec [$w_rev get_tracking_branch] | ||||
| 	set cmit [$w_rev get_commit] | ||||
|  | ||||
| 	set fh [open [gitdir FETCH_HEAD] w] | ||||
| 	set fh [safe_open_file [gitdir FETCH_HEAD] w] | ||||
| 	fconfigure $fh -translation lf | ||||
| 	if {$spec eq {}} { | ||||
| 		set remote . | ||||
|  | @ -118,7 +118,7 @@ method _start {} { | |||
| 		set cmd [list git] | ||||
| 		lappend cmd merge | ||||
| 		lappend cmd --strategy=recursive | ||||
| 		lappend cmd [git fmt-merge-msg <[gitdir FETCH_HEAD]] | ||||
| 		lappend cmd [git_redir [list fmt-merge-msg] [list <[gitdir FETCH_HEAD]]] | ||||
| 		lappend cmd HEAD | ||||
| 		lappend cmd $name | ||||
| 	} | ||||
|  | @ -239,7 +239,7 @@ Continue with resetting the current changes?"] | |||
| 	} | ||||
|  | ||||
| 	if {[ask_popup $op_question] eq {yes}} { | ||||
| 		set fd [git_read --stderr read-tree --reset -u -v HEAD] | ||||
| 		set fd [git_read [list read-tree --reset -u -v HEAD] [list 2>@1]] | ||||
| 		fconfigure $fd -blocking 0 -translation binary | ||||
| 		set status_bar_operation [$::main_status \ | ||||
| 			start \ | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ proc merge_load_stages {path cont} { | |||
| 	set merge_stages(3) {} | ||||
| 	set merge_stages_buf {} | ||||
|  | ||||
| 	set merge_stages_fd [eval git_read ls-files -u -z -- {$path}] | ||||
| 	set merge_stages_fd [git_read [list ls-files -u -z -- $path]] | ||||
|  | ||||
| 	fconfigure $merge_stages_fd -blocking 0 -translation binary -encoding binary | ||||
| 	fileevent $merge_stages_fd readable [list read_merge_stages $merge_stages_fd $cont] | ||||
|  | @ -310,7 +310,7 @@ proc merge_tool_get_stages {target stages} { | |||
| 	foreach fname $stages { | ||||
| 		if {$merge_stages($i) eq {}} { | ||||
| 			file delete $fname | ||||
| 			catch { close [open $fname w] } | ||||
| 			catch { close [safe_open_file $fname w] } | ||||
| 		} else { | ||||
| 			# A hack to support autocrlf properly | ||||
| 			git checkout-index -f --stage=$i -- $target | ||||
|  | @ -360,9 +360,9 @@ proc merge_tool_start {cmdline target backup stages} { | |||
|  | ||||
| 	# Force redirection to avoid interpreting output on stderr | ||||
| 	# as an error, and launch the tool | ||||
| 	lappend cmdline {2>@1} | ||||
| 	set redir [list {2>@1}] | ||||
|  | ||||
| 	if {[catch { set mtool_fd [_open_stdout_stderr $cmdline] } err]} { | ||||
| 	if {[catch { set mtool_fd [safe_open_command $cmdline $redir] } err]} { | ||||
| 		delete_temp_files $mtool_tmpfiles | ||||
| 		error_popup [mc "Could not start the merge tool:\n\n%s" $err] | ||||
| 		return | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ proc all_tracking_branches {} { | |||
| 	} | ||||
|  | ||||
| 	if {$pat ne {}} { | ||||
| 		set fd [eval git_read for-each-ref --format=%(refname) $cmd] | ||||
| 		set fd [git_read [concat for-each-ref --format=%(refname) $cmd]] | ||||
| 		while {[gets $fd n] > 0} { | ||||
| 			foreach spec $pat { | ||||
| 				set dst [string range [lindex $spec 0] 0 end-2] | ||||
|  | @ -75,7 +75,7 @@ proc load_all_remotes {} { | |||
|  | ||||
| 		foreach name $all_remotes { | ||||
| 			catch { | ||||
| 				set fd [open [file join $rm_dir $name] r] | ||||
| 				set fd [safe_open_file [file join $rm_dir $name] r] | ||||
| 				while {[gets $fd line] >= 0} { | ||||
| 					if {[regexp {^URL:[ 	]*(.+)$} $line line url]} { | ||||
| 						set remote_url($name) $url | ||||
|  | @ -145,7 +145,7 @@ proc add_fetch_entry {r} { | |||
| 		} | ||||
| 	} else { | ||||
| 		catch { | ||||
| 			set fd [open [gitdir remotes $r] r] | ||||
| 			set fd [safe_open_file [gitdir remotes $r] r] | ||||
| 			while {[gets $fd n] >= 0} { | ||||
| 				if {[regexp {^Pull:[ \t]*([^:]+):} $n]} { | ||||
| 					set enable 1 | ||||
|  | @ -182,7 +182,7 @@ proc add_push_entry {r} { | |||
| 		} | ||||
| 	} else { | ||||
| 		catch { | ||||
| 			set fd [open [gitdir remotes $r] r] | ||||
| 			set fd [safe_open_file [gitdir remotes $r] r] | ||||
| 			while {[gets $fd n] >= 0} { | ||||
| 				if {[regexp {^Push:[ \t]*([^:]+):} $n]} { | ||||
| 					set enable 1 | ||||
|  |  | |||
|  | @ -308,7 +308,7 @@ method _load {cache uri} { | |||
| 		set full_list [list] | ||||
| 		set head_cache($cache) [list] | ||||
| 		set full_cache($cache) [list] | ||||
| 		set active_ls [git_read ls-remote $uri] | ||||
| 		set active_ls [git_read [list ls-remote $uri]] | ||||
| 		fconfigure $active_ls \ | ||||
| 			-blocking 0 \ | ||||
| 			-translation lf \ | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ proc do_windows_shortcut {} { | |||
| 			set fn ${fn}.lnk | ||||
| 		} | ||||
| 		# Use git-gui.exe if available (ie: git-for-windows) | ||||
| 		set cmdLine [auto_execok git-gui.exe] | ||||
| 		set cmdLine [list [_which git-gui]] | ||||
| 		if {$cmdLine eq {}} { | ||||
| 			set cmdLine [list [info nameofexecutable] \ | ||||
| 							 [file normalize $::argv0]] | ||||
|  | @ -30,8 +30,8 @@ proc do_cygwin_shortcut {} { | |||
| 	global argv0 _gitworktree oguilib | ||||
|  | ||||
| 	if {[catch { | ||||
| 		set desktop [exec cygpath \ | ||||
| 			--desktop] | ||||
| 		set desktop [safe_exec [list cygpath \ | ||||
| 			--desktop]] | ||||
| 		}]} { | ||||
| 			set desktop . | ||||
| 	} | ||||
|  | @ -50,14 +50,14 @@ proc do_cygwin_shortcut {} { | |||
| 					"CHERE_INVOKING=1 \ | ||||
| 					source /etc/profile; \ | ||||
| 					git gui"} | ||||
| 				exec /bin/mkshortcut.exe \ | ||||
| 				safe_exec [list /bin/mkshortcut.exe \ | ||||
| 					--arguments $shargs \ | ||||
| 					--desc "git-gui on $repodir" \ | ||||
| 					--icon $oguilib/git-gui.ico \ | ||||
| 					--name $fn \ | ||||
| 					--show min \ | ||||
| 					--workingdir $repodir \ | ||||
| 					/bin/sh.exe | ||||
| 					/bin/sh.exe] | ||||
| 			} err]} { | ||||
| 			error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] | ||||
| 		} | ||||
|  | @ -83,7 +83,7 @@ proc do_macosx_app {} { | |||
|  | ||||
| 				file mkdir $MacOS | ||||
|  | ||||
| 				set fd [open [file join $Contents Info.plist] w] | ||||
| 				set fd [safe_open_file [file join $Contents Info.plist] w] | ||||
| 				puts $fd {<?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
|  | @ -108,7 +108,7 @@ proc do_macosx_app {} { | |||
| </plist>} | ||||
| 				close $fd | ||||
|  | ||||
| 				set fd [open $exe w] | ||||
| 				set fd [safe_open_file $exe w] | ||||
| 				puts $fd "#!/bin/sh" | ||||
| 				foreach name [lsort [array names env]] { | ||||
| 					set value $env($name) | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ proc find_ssh_key {} { | |||
| 		~/.ssh/id_rsa.pub ~/.ssh/identity.pub | ||||
| 	} { | ||||
| 		if {[file exists $name]} { | ||||
| 			set fh    [open $name r] | ||||
| 			set fh    [safe_open_file $name r] | ||||
| 			set cont  [read $fh] | ||||
| 			close $fh | ||||
| 			return [list $name $cont] | ||||
|  | @ -83,9 +83,10 @@ proc make_ssh_key {w} { | |||
| 	set sshkey_title [mc "Generating..."] | ||||
| 	$w.header.gen configure -state disabled | ||||
|  | ||||
| 	set cmdline [list sh -c {echo | ssh-keygen -q -t rsa -f ~/.ssh/id_rsa 2>&1}] | ||||
| 	set cmdline [list [shellpath] -c \ | ||||
| 		{echo | ssh-keygen -q -t rsa -f ~/.ssh/id_rsa 2>&1}] | ||||
|  | ||||
| 	if {[catch { set sshkey_fd [_open_stdout_stderr $cmdline] } err]} { | ||||
| 	if {[catch { set sshkey_fd [safe_open_command $cmdline] } err]} { | ||||
| 		error_popup [mc "Could not start ssh-keygen:\n\n%s" $err] | ||||
| 		return | ||||
| 	} | ||||
|  |  | |||
|  | @ -110,14 +110,14 @@ proc tools_exec {fullname} { | |||
|  | ||||
| 	set cmdline $repo_config(guitool.$fullname.cmd) | ||||
| 	if {[is_config_true "guitool.$fullname.noconsole"]} { | ||||
| 		tools_run_silent [list sh -c $cmdline] \ | ||||
| 		tools_run_silent [list [shellpath] -c $cmdline] \ | ||||
| 				 [list tools_complete $fullname {}] | ||||
| 	} else { | ||||
| 		regsub {/} $fullname { / } title | ||||
| 		set w [console::new \ | ||||
| 			[mc "Tool: %s" $title] \ | ||||
| 			[mc "Running: %s" $cmdline]] | ||||
| 		console::exec $w [list sh -c $cmdline] \ | ||||
| 		console::exec $w [list [shellpath] -c $cmdline] \ | ||||
| 				 [list tools_complete $fullname $w] | ||||
| 	} | ||||
|  | ||||
|  | @ -130,8 +130,7 @@ proc tools_exec {fullname} { | |||
| } | ||||
|  | ||||
| proc tools_run_silent {cmd after} { | ||||
| 	lappend cmd 2>@1 | ||||
| 	set fd [_open_stdout_stderr $cmd] | ||||
| 	set fd [safe_open_command $cmd [list 2>@1]] | ||||
|  | ||||
| 	fconfigure $fd -blocking 0 -translation binary | ||||
| 	fileevent $fd readable [list tools_consume_input $fd $after] | ||||
|  |  | |||
|  | @ -2,11 +2,11 @@ | |||
| # Copyright (C) 2007 Shawn Pearce | ||||
|  | ||||
| proc win32_read_lnk {lnk_path} { | ||||
| 	return [exec cscript.exe \ | ||||
| 	return [safe_exec [list cscript.exe \ | ||||
| 		/E:jscript \ | ||||
| 		/nologo \ | ||||
| 		[file join $::oguilib win32_shortcut.js] \ | ||||
| 		$lnk_path] | ||||
| 		$lnk_path]] | ||||
| } | ||||
|  | ||||
| proc win32_create_lnk {lnk_path lnk_exec lnk_dir} { | ||||
|  | @ -15,12 +15,13 @@ proc win32_create_lnk {lnk_path lnk_exec lnk_dir} { | |||
| 	set lnk_args [lrange $lnk_exec 1 end] | ||||
| 	set lnk_exec [lindex $lnk_exec 0] | ||||
|  | ||||
| 	eval [list exec wscript.exe \ | ||||
| 	set cmd [list wscript.exe \ | ||||
| 		/E:jscript \ | ||||
| 		/nologo \ | ||||
| 		[file nativename [file join $oguilib win32_shortcut.js]] \ | ||||
| 		$lnk_path \ | ||||
| 		[file nativename [file join $oguilib git-gui.ico]] \ | ||||
| 		$lnk_dir \ | ||||
| 		$lnk_exec] $lnk_args | ||||
| 		$lnk_exec] | ||||
| 	safe_exec [concat $cmd $lnk_args] | ||||
| } | ||||
|  |  | |||
							
								
								
									
										277
									
								
								gitk-git/gitk
								
								
								
								
							
							
						
						
									
										277
									
								
								gitk-git/gitk
								
								
								
								
							|  | @ -113,6 +113,91 @@ if {[is_Windows]} { | |||
|  | ||||
| # End of safe PATH lookup stuff | ||||
|  | ||||
| # Wrap exec/open to sanitize arguments | ||||
|  | ||||
| # unsafe arguments begin with redirections or the pipe or background operators | ||||
| proc is_arg_unsafe {arg} { | ||||
|     regexp {^([<|>&]|2>)} $arg | ||||
| } | ||||
|  | ||||
| proc make_arg_safe {arg} { | ||||
|     if {[is_arg_unsafe $arg]} { | ||||
|         set arg [file join . $arg] | ||||
|     } | ||||
|     return $arg | ||||
| } | ||||
|  | ||||
| proc make_arglist_safe {arglist} { | ||||
|     set res {} | ||||
|     foreach arg $arglist { | ||||
|         lappend res [make_arg_safe $arg] | ||||
|     } | ||||
|     return $res | ||||
| } | ||||
|  | ||||
| # executes one command | ||||
| # no redirections or pipelines are possible | ||||
| # cmd is a list that specifies the command and its arguments | ||||
| # calls `exec` and returns its value | ||||
| proc safe_exec {cmd} { | ||||
|     eval exec [make_arglist_safe $cmd] | ||||
| } | ||||
|  | ||||
| # executes one command with redirections | ||||
| # no pipelines are possible | ||||
| # cmd is a list that specifies the command and its arguments | ||||
| # redir is a list that specifies redirections (output, background, constant(!) commands) | ||||
| # calls `exec` and returns its value | ||||
| proc safe_exec_redirect {cmd redir} { | ||||
|     eval exec [make_arglist_safe $cmd] $redir | ||||
| } | ||||
|  | ||||
| proc safe_open_file {filename flags} { | ||||
|     # a file name starting with "|" would attempt to run a process | ||||
|     # but such a file name must be treated as a relative path | ||||
|     # hide the "|" behind "./" | ||||
|     if {[string index $filename 0] eq "|"} { | ||||
|         set filename [file join . $filename] | ||||
|     } | ||||
|     open $filename $flags | ||||
| } | ||||
|  | ||||
| # opens a command pipeline for reading | ||||
| # cmd is a list that specifies the command and its arguments | ||||
| # calls `open` and returns the file id | ||||
| proc safe_open_command {cmd} { | ||||
|     open |[make_arglist_safe $cmd] r | ||||
| } | ||||
|  | ||||
| # opens a command pipeline for reading and writing | ||||
| # cmd is a list that specifies the command and its arguments | ||||
| # calls `open` and returns the file id | ||||
| proc safe_open_command_rw {cmd} { | ||||
|     open |[make_arglist_safe $cmd] r+ | ||||
| } | ||||
|  | ||||
| # opens a command pipeline for reading with redirections | ||||
| # cmd is a list that specifies the command and its arguments | ||||
| # redir is a list that specifies redirections | ||||
| # calls `open` and returns the file id | ||||
| proc safe_open_command_redirect {cmd redir} { | ||||
|     set cmd [make_arglist_safe $cmd] | ||||
|     open |[concat $cmd $redir] r | ||||
| } | ||||
|  | ||||
| # opens a pipeline with several commands for reading | ||||
| # cmds is a list of lists, each of which specifies a command and its arguments | ||||
| # calls `open` and returns the file id | ||||
| proc safe_open_pipeline {cmds} { | ||||
|     set cmd {} | ||||
|     foreach subcmd $cmds { | ||||
|         set cmd [concat $cmd | [make_arglist_safe $subcmd]] | ||||
|     } | ||||
|     open $cmd r | ||||
| } | ||||
|  | ||||
| # End exec/open wrappers | ||||
|  | ||||
| proc hasworktree {} { | ||||
|     return [expr {[exec git rev-parse --is-bare-repository] == "false" && | ||||
|                   [exec git rev-parse --is-inside-git-dir] == "false"}] | ||||
|  | @ -238,7 +323,7 @@ proc unmerged_files {files} { | |||
|     set mlist {} | ||||
|     set nr_unmerged 0 | ||||
|     if {[catch { | ||||
|         set fd [open "| git ls-files -u" r] | ||||
|         set fd [safe_open_command {git ls-files -u}] | ||||
|     } err]} { | ||||
|         show_error {} . "[mc "Couldn't get list of unmerged files:"] $err" | ||||
|         exit 1 | ||||
|  | @ -400,7 +485,7 @@ proc parseviewrevs {view revs} { | |||
|     } elseif {[lsearch -exact $revs --all] >= 0} { | ||||
|         lappend revs HEAD | ||||
|     } | ||||
|     if {[catch {set ids [eval exec git rev-parse $revs]} err]} { | ||||
|     if {[catch {set ids [safe_exec [concat git rev-parse $revs]]} err]} { | ||||
|         # we get stdout followed by stderr in $err | ||||
|         # for an unknown rev, git rev-parse echoes it and then errors out | ||||
|         set errlines [split $err "\n"] | ||||
|  | @ -457,16 +542,6 @@ proc parseviewrevs {view revs} { | |||
|     return $ret | ||||
| } | ||||
|  | ||||
| # Escapes a list of filter paths to be passed to git log via stdin. Note that | ||||
| # paths must not be quoted. | ||||
| proc escape_filter_paths {paths} { | ||||
|     set escaped [list] | ||||
|     foreach path $paths { | ||||
|         lappend escaped [string map {\\ \\\\ "\ " "\\\ "} $path] | ||||
|     } | ||||
|     return $escaped | ||||
| } | ||||
|  | ||||
| # Start off a git log process and arrange to read its output | ||||
| proc start_rev_list {view} { | ||||
|     global startmsecs commitidx viewcomplete curview | ||||
|  | @ -488,7 +563,7 @@ proc start_rev_list {view} { | |||
|     set args $viewargs($view) | ||||
|     if {$viewargscmd($view) ne {}} { | ||||
|         if {[catch { | ||||
|             set str [exec sh -c $viewargscmd($view)] | ||||
|             set str [safe_exec [list sh -c $viewargscmd($view)]] | ||||
|         } err]} { | ||||
|             error_popup "[mc "Error executing --argscmd command:"] $err" | ||||
|             return 0 | ||||
|  | @ -526,10 +601,9 @@ proc start_rev_list {view} { | |||
|     } | ||||
|  | ||||
|     if {[catch { | ||||
|         set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \ | ||||
|                         --parents --boundary $args --stdin \ | ||||
|                         "<<[join [concat $revs "--" \ | ||||
|                                 [escape_filter_paths $files]] "\\n"]"] r] | ||||
|         set fd [safe_open_command_redirect [concat git log --no-color -z --pretty=raw $show_notes \ | ||||
|                         --parents --boundary $args --stdin] \ | ||||
|                         [list "<<[join [concat $revs "--" $files] "\n"]"]] | ||||
|     } err]} { | ||||
|         error_popup "[mc "Error executing git log:"] $err" | ||||
|         return 0 | ||||
|  | @ -563,9 +637,9 @@ proc stop_instance {inst} { | |||
|         set pid [pid $fd] | ||||
|  | ||||
|         if {$::tcl_platform(platform) eq {windows}} { | ||||
|             exec taskkill /pid $pid | ||||
|             safe_exec [list taskkill /pid $pid] | ||||
|         } else { | ||||
|             exec kill $pid | ||||
|             safe_exec [list kill $pid] | ||||
|         } | ||||
|     } | ||||
|     catch {close $fd} | ||||
|  | @ -680,11 +754,9 @@ proc updatecommits {} { | |||
|         set args $vorigargs($view) | ||||
|     } | ||||
|     if {[catch { | ||||
|         set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \ | ||||
|                         --parents --boundary $args --stdin \ | ||||
|                         "<<[join [concat $revs "--" \ | ||||
|                                 [escape_filter_paths \ | ||||
|                                         $vfilelimit($view)]] "\\n"]"] r] | ||||
|         set fd [safe_open_command_redirect [concat git log --no-color -z --pretty=raw $show_notes \ | ||||
|                         --parents --boundary $args --stdin] \ | ||||
|                         [list "<<[join [concat $revs "--" $vfilelimit($view)] "\n"]"]] | ||||
|     } err]} { | ||||
|         error_popup "[mc "Error executing git log:"] $err" | ||||
|         return | ||||
|  | @ -1651,8 +1723,8 @@ proc getcommitlines {fd inst view updating}  { | |||
|             # and if we already know about it, using the rewritten | ||||
|             # parent as a substitute parent for $id's children. | ||||
|             if {![catch { | ||||
|                 set rwid [exec git rev-list --first-parent --max-count=1 \ | ||||
|                               $id -- $vfilelimit($view)] | ||||
|                 set rwid [safe_exec [list git rev-list --first-parent --max-count=1 \ | ||||
|                               $id -- $vfilelimit($view)]] | ||||
|             }]} { | ||||
|                 if {$rwid ne {} && [info exists varcid($view,$rwid)]} { | ||||
|                     # use $rwid in place of $id | ||||
|  | @ -1772,7 +1844,7 @@ proc do_readcommit {id} { | |||
|     global tclencoding | ||||
|  | ||||
|     # Invoke git-log to handle automatic encoding conversion | ||||
|     set fd [open [concat | git log --no-color --pretty=raw -1 $id] r] | ||||
|     set fd [safe_open_command [concat git log --no-color --pretty=raw -1 $id]] | ||||
|     # Read the results using i18n.logoutputencoding | ||||
|     fconfigure $fd -translation lf -eofchar {} | ||||
|     if {$tclencoding != {}} { | ||||
|  | @ -1908,7 +1980,7 @@ proc readrefs {} { | |||
|     foreach v {tagids idtags headids idheads otherrefids idotherrefs} { | ||||
|         unset -nocomplain $v | ||||
|     } | ||||
|     set refd [open [list | git show-ref -d] r] | ||||
|     set refd [safe_open_command [list git show-ref -d]] | ||||
|     if {$tclencoding != {}} { | ||||
|         fconfigure $refd -encoding $tclencoding | ||||
|     } | ||||
|  | @ -1956,7 +2028,7 @@ proc readrefs {} { | |||
|     set selectheadid {} | ||||
|     if {$selecthead ne {}} { | ||||
|         catch { | ||||
|             set selectheadid [exec git rev-parse --verify $selecthead] | ||||
|             set selectheadid [safe_exec [list git rev-parse --verify $selecthead]] | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -2220,7 +2292,7 @@ proc makewindow {} { | |||
|             {mc "Reread re&ferences" command rereadrefs} | ||||
|             {mc "&List references" command showrefs -accelerator F2} | ||||
|             {xx "" separator} | ||||
|             {mc "Start git &gui" command {exec git gui &}} | ||||
|             {mc "Start git &gui" command {safe_exec_redirect [list git gui] [list &]}} | ||||
|             {xx "" separator} | ||||
|             {mc "&Quit" command doquit -accelerator Meta1-Q} | ||||
|         }} | ||||
|  | @ -3007,7 +3079,7 @@ proc savestuff {w} { | |||
|     set remove_tmp 0 | ||||
|     if {[catch { | ||||
|         set try_count 0 | ||||
|         while {[catch {set f [open $config_file_tmp {WRONLY CREAT EXCL}]}]} { | ||||
|         while {[catch {set f [safe_open_file $config_file_tmp {WRONLY CREAT EXCL}]}]} { | ||||
|             if {[incr try_count] > 50} { | ||||
|                 error "Unable to write config file: $config_file_tmp exists" | ||||
|             } | ||||
|  | @ -3723,7 +3795,7 @@ proc gitknewtmpdir {} { | |||
|             set tmpdir $gitdir | ||||
|         } | ||||
|         set gitktmpformat [file join $tmpdir ".gitk-tmp.XXXXXX"] | ||||
|         if {[catch {set gitktmpdir [exec mktemp -d $gitktmpformat]}]} { | ||||
|         if {[catch {set gitktmpdir [safe_exec [list mktemp -d $gitktmpformat]]}]} { | ||||
|             set gitktmpdir [file join $gitdir [format ".gitk-tmp.%s" [pid]]] | ||||
|         } | ||||
|         if {[catch {file mkdir $gitktmpdir} err]} { | ||||
|  | @ -3745,7 +3817,7 @@ proc gitknewtmpdir {} { | |||
| proc save_file_from_commit {filename output what} { | ||||
|     global nullfile | ||||
|  | ||||
|     if {[catch {exec git show $filename -- > $output} err]} { | ||||
|     if {[catch {safe_exec_redirect [list git show $filename --] [list > $output]} err]} { | ||||
|         if {[string match "fatal: bad revision *" $err]} { | ||||
|             return $nullfile | ||||
|         } | ||||
|  | @ -3810,7 +3882,7 @@ proc external_diff {} { | |||
|  | ||||
|     if {$difffromfile ne {} && $difftofile ne {}} { | ||||
|         set cmd [list [shellsplit $extdifftool] $difffromfile $difftofile] | ||||
|         if {[catch {set fl [open |$cmd r]} err]} { | ||||
|         if {[catch {set fl [safe_open_command $cmd]} err]} { | ||||
|             file delete -force $diffdir | ||||
|             error_popup "$extdifftool: [mc "command failed:"] $err" | ||||
|         } else { | ||||
|  | @ -3914,7 +3986,7 @@ proc external_blame_diff {} { | |||
| # Find the SHA1 ID of the blob for file $fname in the index | ||||
| # at stage 0 or 2 | ||||
| proc index_sha1 {fname} { | ||||
|     set f [open [list | git ls-files -s $fname] r] | ||||
|     set f [safe_open_command [list git ls-files -s $fname]] | ||||
|     while {[gets $f line] >= 0} { | ||||
|         set info [lindex [split $line "\t"] 0] | ||||
|         set stage [lindex $info 2] | ||||
|  | @ -3974,7 +4046,7 @@ proc external_blame {parent_idx {line {}}} { | |||
|     # being given an absolute path... | ||||
|     set f [make_relative $f] | ||||
|     lappend cmdline $base_commit $f | ||||
|     if {[catch {eval exec $cmdline &} err]} { | ||||
|     if {[catch {safe_exec_redirect $cmdline [list &]} err]} { | ||||
|         error_popup "[mc "git gui blame: command failed:"] $err" | ||||
|     } | ||||
| } | ||||
|  | @ -4002,7 +4074,7 @@ proc show_line_source {} { | |||
|                 # must be a merge in progress... | ||||
|                 if {[catch { | ||||
|                     # get the last line from .git/MERGE_HEAD | ||||
|                     set f [open [file join $gitdir MERGE_HEAD] r] | ||||
|                     set f [safe_open_file [file join $gitdir MERGE_HEAD] r] | ||||
|                     set id [lindex [split [read $f] "\n"] end-1] | ||||
|                     close $f | ||||
|                 } err]} { | ||||
|  | @ -4025,19 +4097,17 @@ proc show_line_source {} { | |||
|         } | ||||
|         set line [lindex $h 1] | ||||
|     } | ||||
|     set blameargs {} | ||||
|     set blamefile [file join $cdup $flist_menu_file] | ||||
|     if {$from_index ne {}} { | ||||
|         lappend blameargs | git cat-file blob $from_index | ||||
|     } | ||||
|     lappend blameargs | git blame -p -L$line,+1 | ||||
|     if {$from_index ne {}} { | ||||
|         lappend blameargs --contents - | ||||
|         set blameargs [list \ | ||||
|             [list git cat-file blob $from_index] \ | ||||
|             [list git blame -p -L$line,+1 --contents - -- $blamefile]] | ||||
|     } else { | ||||
|         lappend blameargs $id | ||||
|         set blameargs [list \ | ||||
|             [list git blame -p -L$line,+1 $id -- $blamefile]] | ||||
|     } | ||||
|     lappend blameargs -- [file join $cdup $flist_menu_file] | ||||
|     if {[catch { | ||||
|         set f [open $blameargs r] | ||||
|         set f [safe_open_pipeline $blameargs] | ||||
|     } err]} { | ||||
|         error_popup [mc "Couldn't start git blame: %s" $err] | ||||
|         return | ||||
|  | @ -4962,8 +5032,8 @@ proc do_file_hl {serial} { | |||
|         # must be "containing:", i.e. we're searching commit info | ||||
|         return | ||||
|     } | ||||
|     set cmd [concat | git diff-tree -r -s --stdin $gdtargs] | ||||
|     set filehighlight [open $cmd r+] | ||||
|     set cmd [concat git diff-tree -r -s --stdin $gdtargs] | ||||
|     set filehighlight [safe_open_command_rw $cmd] | ||||
|     fconfigure $filehighlight -blocking 0 | ||||
|     filerun $filehighlight readfhighlight | ||||
|     set fhl_list {} | ||||
|  | @ -5392,8 +5462,8 @@ proc get_viewmainhead {view} { | |||
|     global viewmainheadid vfilelimit viewinstances mainheadid | ||||
|  | ||||
|     catch { | ||||
|         set rfd [open [concat | git rev-list -1 $mainheadid \ | ||||
|                            -- $vfilelimit($view)] r] | ||||
|         set rfd [safe_open_command [concat git rev-list -1 $mainheadid \ | ||||
|                            -- $vfilelimit($view)]] | ||||
|         set j [reg_instance $rfd] | ||||
|         lappend viewinstances($view) $j | ||||
|         fconfigure $rfd -blocking 0 | ||||
|  | @ -5458,14 +5528,14 @@ proc dodiffindex {} { | |||
|     if {!$showlocalchanges || !$hasworktree} return | ||||
|     incr lserial | ||||
|     if {[package vcompare $git_version "1.7.2"] >= 0} { | ||||
|         set cmd "|git diff-index --cached --ignore-submodules=dirty HEAD" | ||||
|         set cmd "git diff-index --cached --ignore-submodules=dirty HEAD" | ||||
|     } else { | ||||
|         set cmd "|git diff-index --cached HEAD" | ||||
|         set cmd "git diff-index --cached HEAD" | ||||
|     } | ||||
|     if {$vfilelimit($curview) ne {}} { | ||||
|         set cmd [concat $cmd -- $vfilelimit($curview)] | ||||
|     } | ||||
|     set fd [open $cmd r] | ||||
|     set fd [safe_open_command $cmd] | ||||
|     fconfigure $fd -blocking 0 | ||||
|     set i [reg_instance $fd] | ||||
|     filerun $fd [list readdiffindex $fd $lserial $i] | ||||
|  | @ -5490,11 +5560,11 @@ proc readdiffindex {fd serial inst} { | |||
|     } | ||||
|  | ||||
|     # now see if there are any local changes not checked in to the index | ||||
|     set cmd "|git diff-files" | ||||
|     set cmd "git diff-files" | ||||
|     if {$vfilelimit($curview) ne {}} { | ||||
|         set cmd [concat $cmd -- $vfilelimit($curview)] | ||||
|     } | ||||
|     set fd [open $cmd r] | ||||
|     set fd [safe_open_command $cmd] | ||||
|     fconfigure $fd -blocking 0 | ||||
|     set i [reg_instance $fd] | ||||
|     filerun $fd [list readdifffiles $fd $serial $i] | ||||
|  | @ -7283,8 +7353,8 @@ proc browseweb {url} { | |||
|     global web_browser | ||||
|  | ||||
|     if {$web_browser eq {}} return | ||||
|     # Use eval here in case $web_browser is a command plus some arguments | ||||
|     if {[catch {eval exec $web_browser [list $url] &} err]} { | ||||
|     # Use concat here in case $web_browser is a command plus some arguments | ||||
|     if {[catch {safe_exec_redirect [concat $web_browser [list $url]] [list &]} err]} { | ||||
|         error_popup "[mc "Error starting web browser:"] $err" | ||||
|     } | ||||
| } | ||||
|  | @ -7790,13 +7860,13 @@ proc gettree {id} { | |||
|     if {![info exists treefilelist($id)]} { | ||||
|         if {![info exists treepending]} { | ||||
|             if {$id eq $nullid} { | ||||
|                 set cmd [list | git ls-files] | ||||
|                 set cmd [list git ls-files] | ||||
|             } elseif {$id eq $nullid2} { | ||||
|                 set cmd [list | git ls-files --stage -t] | ||||
|                 set cmd [list git ls-files --stage -t] | ||||
|             } else { | ||||
|                 set cmd [list | git ls-tree -r $id] | ||||
|                 set cmd [list git ls-tree -r $id] | ||||
|             } | ||||
|             if {[catch {set gtf [open $cmd r]}]} { | ||||
|             if {[catch {set gtf [safe_open_command $cmd]}]} { | ||||
|                 return | ||||
|             } | ||||
|             set treepending $id | ||||
|  | @ -7860,13 +7930,13 @@ proc showfile {f} { | |||
|         return | ||||
|     } | ||||
|     if {$diffids eq $nullid} { | ||||
|         if {[catch {set bf [open $f r]} err]} { | ||||
|         if {[catch {set bf [safe_open_file $f r]} err]} { | ||||
|             puts "oops, can't read $f: $err" | ||||
|             return | ||||
|         } | ||||
|     } else { | ||||
|         set blob [lindex $treeidlist($diffids) $i] | ||||
|         if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} { | ||||
|         if {[catch {set bf [safe_open_command [concat git cat-file blob $blob]]} err]} { | ||||
|             puts "oops, error reading blob $blob: $err" | ||||
|             return | ||||
|         } | ||||
|  | @ -8016,7 +8086,7 @@ proc diffcmd {ids flags} { | |||
|     if {$i >= 0} { | ||||
|         if {[llength $ids] > 1 && $j < 0} { | ||||
|             # comparing working directory with some specific revision | ||||
|             set cmd [concat | git diff-index $flags] | ||||
|             set cmd [concat git diff-index $flags] | ||||
|             if {$i == 0} { | ||||
|                 lappend cmd -R [lindex $ids 1] | ||||
|             } else { | ||||
|  | @ -8024,7 +8094,7 @@ proc diffcmd {ids flags} { | |||
|             } | ||||
|         } else { | ||||
|             # comparing working directory with index | ||||
|             set cmd [concat | git diff-files $flags] | ||||
|             set cmd [concat git diff-files $flags] | ||||
|             if {$j == 1} { | ||||
|                 lappend cmd -R | ||||
|             } | ||||
|  | @ -8033,7 +8103,7 @@ proc diffcmd {ids flags} { | |||
|         if {[package vcompare $git_version "1.7.2"] >= 0} { | ||||
|             set flags "$flags --ignore-submodules=dirty" | ||||
|         } | ||||
|         set cmd [concat | git diff-index --cached $flags] | ||||
|         set cmd [concat git diff-index --cached $flags] | ||||
|         if {[llength $ids] > 1} { | ||||
|             # comparing index with specific revision | ||||
|             if {$j == 0} { | ||||
|  | @ -8049,7 +8119,7 @@ proc diffcmd {ids flags} { | |||
|         if {$log_showroot} { | ||||
|             lappend flags --root | ||||
|         } | ||||
|         set cmd [concat | git diff-tree -r $flags $ids] | ||||
|         set cmd [concat git diff-tree -r $flags $ids] | ||||
|     } | ||||
|     return $cmd | ||||
| } | ||||
|  | @ -8061,7 +8131,7 @@ proc gettreediffs {ids} { | |||
|     if {$limitdiffs && $vfilelimit($curview) ne {}} { | ||||
|             set cmd [concat $cmd -- $vfilelimit($curview)] | ||||
|     } | ||||
|     if {[catch {set gdtf [open $cmd r]}]} return | ||||
|     if {[catch {set gdtf [safe_open_command $cmd]}]} return | ||||
|  | ||||
|     set treepending $ids | ||||
|     set treediff {} | ||||
|  | @ -8181,7 +8251,7 @@ proc getblobdiffs {ids} { | |||
|     if {$limitdiffs && $vfilelimit($curview) ne {}} { | ||||
|         set cmd [concat $cmd -- $vfilelimit($curview)] | ||||
|     } | ||||
|     if {[catch {set bdf [open $cmd r]} err]} { | ||||
|     if {[catch {set bdf [safe_open_command $cmd]} err]} { | ||||
|         error_popup [mc "Error getting diffs: %s" $err] | ||||
|         return | ||||
|     } | ||||
|  | @ -8899,7 +8969,7 @@ proc gotocommit {} { | |||
|                 set id [lindex $matches 0] | ||||
|             } | ||||
|         } else { | ||||
|             if {[catch {set id [exec git rev-parse --verify $sha1string]}]} { | ||||
|             if {[catch {set id [safe_exec [list git rev-parse --verify $sha1string]]}]} { | ||||
|                 error_popup [mc "Revision %s is not known" $sha1string] | ||||
|                 return | ||||
|             } | ||||
|  | @ -9205,10 +9275,8 @@ proc getpatchid {id} { | |||
|  | ||||
|     if {![info exists patchids($id)]} { | ||||
|         set cmd [diffcmd [list $id] {-p --root}] | ||||
|         # trim off the initial "|" | ||||
|         set cmd [lrange $cmd 1 end] | ||||
|         if {[catch { | ||||
|             set x [eval exec $cmd | git patch-id] | ||||
|             set x [safe_exec_redirect $cmd [list | git patch-id]] | ||||
|             set patchids($id) [lindex $x 0] | ||||
|         }]} { | ||||
|             set patchids($id) "error" | ||||
|  | @ -9304,14 +9372,14 @@ proc diffcommits {a b} { | |||
|     set fna [file join $tmpdir "commit-[string range $a 0 7]"] | ||||
|     set fnb [file join $tmpdir "commit-[string range $b 0 7]"] | ||||
|     if {[catch { | ||||
|         exec git diff-tree -p --pretty $a >$fna | ||||
|         exec git diff-tree -p --pretty $b >$fnb | ||||
|         safe_exec_redirect [list git diff-tree -p --pretty $a] [list >$fna] | ||||
|         safe_exec_redirect [list git diff-tree -p --pretty $b] [list >$fnb] | ||||
|     } err]} { | ||||
|         error_popup [mc "Error writing commit to file: %s" $err] | ||||
|         return | ||||
|     } | ||||
|     if {[catch { | ||||
|         set fd [open "| diff -U$diffcontext $fna $fnb" r] | ||||
|         set fd [safe_open_command "diff -U$diffcontext $fna $fnb"] | ||||
|     } err]} { | ||||
|         error_popup [mc "Error diffing commits: %s" $err] | ||||
|         return | ||||
|  | @ -9451,10 +9519,7 @@ proc mkpatchgo {} { | |||
|     set newid [$patchtop.tosha1 get] | ||||
|     set fname [$patchtop.fname get] | ||||
|     set cmd [diffcmd [list $oldid $newid] -p] | ||||
|     # trim off the initial "|" | ||||
|     set cmd [lrange $cmd 1 end] | ||||
|     lappend cmd >$fname & | ||||
|     if {[catch {eval exec $cmd} err]} { | ||||
|     if {[catch {safe_exec_redirect $cmd [list >$fname &]} err]} { | ||||
|         error_popup "[mc "Error creating patch:"] $err" $patchtop | ||||
|     } | ||||
|     catch {destroy $patchtop} | ||||
|  | @ -9523,9 +9588,9 @@ proc domktag {} { | |||
|     } | ||||
|     if {[catch { | ||||
|         if {$msg != {}} { | ||||
|             exec git tag -a -m $msg $tag $id | ||||
|             safe_exec [list git tag -a -m $msg $tag $id] | ||||
|         } else { | ||||
|             exec git tag $tag $id | ||||
|             safe_exec [list git tag $tag $id] | ||||
|         } | ||||
|     } err]} { | ||||
|         error_popup "[mc "Error creating tag:"] $err" $mktagtop | ||||
|  | @ -9593,7 +9658,7 @@ proc copyreference {} { | |||
|     if {$autosellen < 40} { | ||||
|         lappend cmd --abbrev=$autosellen | ||||
|     } | ||||
|     set reference [eval exec $cmd $rowmenuid] | ||||
|     set reference [safe_exec [concat $cmd $rowmenuid]] | ||||
|  | ||||
|     clipboard clear | ||||
|     clipboard append $reference | ||||
|  | @ -9643,7 +9708,7 @@ proc wrcomgo {} { | |||
|     set id [$wrcomtop.sha1 get] | ||||
|     set cmd "echo $id | [$wrcomtop.cmd get]" | ||||
|     set fname [$wrcomtop.fname get] | ||||
|     if {[catch {exec sh -c $cmd >$fname &} err]} { | ||||
|     if {[catch {safe_exec_redirect [list sh -c $cmd] [list >$fname &]} err]} { | ||||
|         error_popup "[mc "Error writing commit:"] $err" $wrcomtop | ||||
|     } | ||||
|     catch {destroy $wrcomtop} | ||||
|  | @ -9747,7 +9812,7 @@ proc mkbrgo {top} { | |||
|     nowbusy newbranch | ||||
|     update | ||||
|     if {[catch { | ||||
|         eval exec git branch $cmdargs | ||||
|         safe_exec [concat git branch $cmdargs] | ||||
|     } err]} { | ||||
|         notbusy newbranch | ||||
|         error_popup $err | ||||
|  | @ -9788,7 +9853,7 @@ proc mvbrgo {top prevname} { | |||
|     nowbusy renamebranch | ||||
|     update | ||||
|     if {[catch { | ||||
|         eval exec git branch $cmdargs | ||||
|         safe_exec [concat git branch $cmdargs] | ||||
|     } err]} { | ||||
|         notbusy renamebranch | ||||
|         error_popup $err | ||||
|  | @ -9829,7 +9894,7 @@ proc exec_citool {tool_args {baseid {}}} { | |||
|         } | ||||
|     } | ||||
|  | ||||
|     eval exec git citool $tool_args & | ||||
|     safe_exec_redirect [concat git citool $tool_args] [list &] | ||||
|  | ||||
|     array unset env GIT_AUTHOR_* | ||||
|     array set env $save_env | ||||
|  | @ -9852,7 +9917,7 @@ proc cherrypick {} { | |||
|     update | ||||
|     # Unfortunately git-cherry-pick writes stuff to stderr even when | ||||
|     # no error occurs, and exec takes that as an indication of error... | ||||
|     if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} { | ||||
|     if {[catch {safe_exec [list sh -c "git cherry-pick -r $rowmenuid 2>&1"]} err]} { | ||||
|         notbusy cherrypick | ||||
|         if {[regexp -line \ | ||||
|                  {Entry '(.*)' (would be overwritten by merge|not uptodate)} \ | ||||
|  | @ -9914,7 +9979,7 @@ proc revert {} { | |||
|     nowbusy revert [mc "Reverting"] | ||||
|     update | ||||
|  | ||||
|     if [catch {exec git revert --no-edit $rowmenuid} err] { | ||||
|     if [catch {safe_exec [list git revert --no-edit $rowmenuid]} err] { | ||||
|         notbusy revert | ||||
|         if [regexp {files would be overwritten by merge:(\n(( |\t)+[^\n]+\n)+)}\ | ||||
|                 $err match files] { | ||||
|  | @ -9990,8 +10055,8 @@ proc resethead {} { | |||
|     bind $w <Visibility> "grab $w; focus $w" | ||||
|     tkwait window $w | ||||
|     if {!$confirm_ok} return | ||||
|     if {[catch {set fd [open \ | ||||
|             [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} { | ||||
|     if {[catch {set fd [safe_open_command_redirect \ | ||||
|             [list git reset --$resettype $rowmenuid] [list 2>@1]]} err]} { | ||||
|         error_popup $err | ||||
|     } else { | ||||
|         dohidelocalchanges | ||||
|  | @ -10062,7 +10127,7 @@ proc cobranch {} { | |||
|  | ||||
|     # check the tree is clean first?? | ||||
|     set newhead $headmenuhead | ||||
|     set command [list | git checkout] | ||||
|     set command [list git checkout] | ||||
|     if {[string match "remotes/*" $newhead]} { | ||||
|         set remote $newhead | ||||
|         set newhead [string range $newhead [expr [string last / $newhead] + 1] end] | ||||
|  | @ -10076,12 +10141,11 @@ proc cobranch {} { | |||
|     } else { | ||||
|         lappend command $newhead | ||||
|     } | ||||
|     lappend command 2>@1 | ||||
|     nowbusy checkout [mc "Checking out"] | ||||
|     update | ||||
|     dohidelocalchanges | ||||
|     if {[catch { | ||||
|         set fd [open $command r] | ||||
|         set fd [safe_open_command_redirect $command [list 2>@1]] | ||||
|     } err]} { | ||||
|         notbusy checkout | ||||
|         error_popup $err | ||||
|  | @ -10147,7 +10211,7 @@ proc rmbranch {} { | |||
|     } | ||||
|     nowbusy rmbranch | ||||
|     update | ||||
|     if {[catch {exec git branch -D $head} err]} { | ||||
|     if {[catch {safe_exec [list git branch -D $head]} err]} { | ||||
|         notbusy rmbranch | ||||
|         error_popup $err | ||||
|         return | ||||
|  | @ -10338,7 +10402,7 @@ proc getallcommits {} { | |||
|         set cachedarcs 0 | ||||
|         set allccache [file join $gitdir "gitk.cache"] | ||||
|         if {![catch { | ||||
|             set f [open $allccache r] | ||||
|             set f [safe_open_file $allccache r] | ||||
|             set allcwait 1 | ||||
|             getcache $f | ||||
|         }]} return | ||||
|  | @ -10347,7 +10411,7 @@ proc getallcommits {} { | |||
|     if {$allcwait} { | ||||
|         return | ||||
|     } | ||||
|     set cmd [list | git rev-list --parents] | ||||
|     set cmd [list git rev-list --parents] | ||||
|     set allcupdate [expr {$seeds ne {}}] | ||||
|     if {!$allcupdate} { | ||||
|         set ids "--all" | ||||
|  | @ -10375,10 +10439,11 @@ proc getallcommits {} { | |||
|     if {$ids ne {}} { | ||||
|         if {$ids eq "--all"} { | ||||
|             set cmd [concat $cmd "--all"] | ||||
|             set fd [safe_open_command $cmd] | ||||
|         } else { | ||||
|             set cmd [concat $cmd --stdin "<<[join $ids "\\n"]"] | ||||
|             set cmd [concat $cmd --stdin] | ||||
|             set fd [safe_open_command_redirect $cmd [list "<<[join $ids "\n"]"]] | ||||
|         } | ||||
|         set fd [open $cmd r] | ||||
|         fconfigure $fd -blocking 0 | ||||
|         incr allcommits | ||||
|         nowbusy allcommits | ||||
|  | @ -10768,7 +10833,7 @@ proc savecache {} { | |||
|     set cachearc 0 | ||||
|     set cachedarcs $nextarc | ||||
|     catch { | ||||
|         set f [open $allccache w] | ||||
|         set f [safe_open_file $allccache w] | ||||
|         puts $f [list 1 $cachedarcs] | ||||
|         run writecache $f | ||||
|     } | ||||
|  | @ -11471,7 +11536,7 @@ proc add_tag_ctext {tag} { | |||
|  | ||||
|     if {![info exists cached_tagcontent($tag)]} { | ||||
|         catch { | ||||
|             set cached_tagcontent($tag) [exec git cat-file -p $tag] | ||||
|             set cached_tagcontent($tag) [safe_exec [list git cat-file -p $tag]] | ||||
|         } | ||||
|     } | ||||
|     $ctext insert end "[mc "Tag"]: $tag\n" bold | ||||
|  | @ -12382,7 +12447,7 @@ proc gitattr {path attr default} { | |||
|         set r $path_attr_cache($attr,$path) | ||||
|     } else { | ||||
|         set r "unspecified" | ||||
|         if {![catch {set line [exec git check-attr $attr -- $path]}]} { | ||||
|         if {![catch {set line [safe_exec [list git check-attr $attr -- $path]]}]} { | ||||
|             regexp "(.*): $attr: (.*)" $line m f r | ||||
|         } | ||||
|         set path_attr_cache($attr,$path) $r | ||||
|  | @ -12409,7 +12474,7 @@ proc cache_gitattr {attr pathlist} { | |||
|     while {$newlist ne {}} { | ||||
|         set head [lrange $newlist 0 [expr {$lim - 1}]] | ||||
|         set newlist [lrange $newlist $lim end] | ||||
|         if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} { | ||||
|         if {![catch {set rlist [safe_exec [concat git check-attr $attr -- $head]]}]} { | ||||
|             foreach row [split $rlist "\n"] { | ||||
|                 if {[regexp "(.*): $attr: (.*)" $row m path value]} { | ||||
|                     if {[string index $path 0] eq "\""} { | ||||
|  | @ -12461,11 +12526,11 @@ if {[catch {package require Tk 8.4} err]} { | |||
|  | ||||
| # on OSX bring the current Wish process window to front | ||||
| if {[tk windowingsystem] eq "aqua"} { | ||||
|     exec osascript -e [format { | ||||
|     safe_exec [list osascript -e [format { | ||||
|         tell application "System Events" | ||||
|             set frontmost of processes whose unix id is %d to true | ||||
|         end tell | ||||
|     } [pid] ] | ||||
|     } [pid] ]] | ||||
| } | ||||
|  | ||||
| # Unset GIT_TRACE var if set | ||||
|  | @ -12713,7 +12778,7 @@ if {$selecthead eq "HEAD"} { | |||
| if {$i >= [llength $argv] && $revtreeargs ne {}} { | ||||
|     # no -- on command line, but some arguments (other than --argscmd) | ||||
|     if {[catch { | ||||
|         set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs] | ||||
|         set f [safe_exec [concat git rev-parse --no-revs --no-flags $revtreeargs]] | ||||
|         set cmdline_files [split $f "\n"] | ||||
|         set n [llength $cmdline_files] | ||||
|         set revtreeargs [lrange $revtreeargs 0 end-$n] | ||||
|  |  | |||
|  | @ -2851,4 +2851,15 @@ test_expect_success 'writing to stdin is rejected' ' | |||
|  | ||||
| done | ||||
|  | ||||
| test_expect_success 'writing value with trailing CR not stripped on read' ' | ||||
| 	test_when_finished "rm -rf cr-test" && | ||||
|  | ||||
| 	printf "bar\r\n" >expect && | ||||
| 	git init cr-test && | ||||
| 	git -C cr-test config set core.foo $(printf "bar\r") && | ||||
| 	git -C cr-test config get core.foo >actual && | ||||
|  | ||||
| 	test_cmp expect actual | ||||
| ' | ||||
|  | ||||
| test_done | ||||
|  |  | |||
|  | @ -1279,6 +1279,29 @@ test_expect_success 'bundles are downloaded once during fetch --all' ' | |||
| 		trace-mult.txt >bundle-fetches && | ||||
| 	test_line_count = 1 bundle-fetches | ||||
| ' | ||||
|  | ||||
| test_expect_success 'bundles with space in URI are rejected' ' | ||||
| 	test_when_finished "rm -rf busted repo" && | ||||
| 	mkdir -p "$HOME/busted/ /$HOME/repo/.git/objects/bundles" && | ||||
| 	git clone --bundle-uri="$HTTPD_URL/bogus $HOME/busted/" "$HTTPD_URL/smart/fetch.git" repo 2>err && | ||||
| 	test_grep "error: bundle-uri: URI is malformed: " err && | ||||
| 	find busted -type f >files && | ||||
| 	test_must_be_empty files | ||||
| ' | ||||
|  | ||||
| test_expect_success 'bundles with newline in URI are rejected' ' | ||||
| 	test_when_finished "rm -rf busted repo" && | ||||
| 	git clone --bundle-uri="$HTTPD_URL/bogus\nget $HTTPD_URL/bogus $HOME/busted" "$HTTPD_URL/smart/fetch.git" repo 2>err && | ||||
| 	test_grep "error: bundle-uri: URI is malformed: " err && | ||||
| 	test_path_is_missing "$HOME/busted" | ||||
| ' | ||||
|  | ||||
| test_expect_success 'bundles with newline in target path are rejected' ' | ||||
| 	git clone --bundle-uri="$HTTPD_URL/bogus" "$HTTPD_URL/smart/fetch.git" "$(printf "escape\nget $HTTPD_URL/bogus .")" 2>err && | ||||
| 	test_grep "error: bundle-uri: filename is malformed: " err && | ||||
| 	test_path_is_missing escape | ||||
| ' | ||||
|  | ||||
| # Do not add tests here unless they use the HTTP server, as they will | ||||
| # not run unless the HTTP dependencies exist. | ||||
|  | ||||
|  |  | |||
|  | @ -372,4 +372,37 @@ test_expect_success 'checkout -f --recurse-submodules must not use a nested gitd | |||
| 	test_path_is_missing nested_checkout/thing2/.git | ||||
| ' | ||||
|  | ||||
| test_expect_success SYMLINKS,!WINDOWS,!MINGW 'submodule must not checkout into different directory' ' | ||||
| 	test_when_finished "rm -rf sub repo bad-clone" && | ||||
|  | ||||
| 	git init sub && | ||||
| 	write_script sub/post-checkout <<-\EOF && | ||||
| 	touch "$PWD/foo" | ||||
| 	EOF | ||||
| 	git -C sub add post-checkout && | ||||
| 	git -C sub commit -m hook && | ||||
|  | ||||
| 	git init repo && | ||||
| 	git -C repo -c protocol.file.allow=always submodule add "$PWD/sub" sub && | ||||
| 	git -C repo mv sub $(printf "sub\r") && | ||||
|  | ||||
| 	# Ensure config values containing CR are wrapped in quotes. | ||||
| 	git config unset -f repo/.gitmodules submodule.sub.path && | ||||
| 	printf "\tpath = \"sub\r\"\n" >>repo/.gitmodules && | ||||
|  | ||||
| 	git config unset -f repo/.git/modules/sub/config core.worktree && | ||||
| 	{ | ||||
| 		printf "[core]\n" && | ||||
| 		printf "\tworktree = \"../../../sub\r\"\n" | ||||
| 	} >>repo/.git/modules/sub/config && | ||||
|  | ||||
| 	ln -s .git/modules/sub/hooks repo/sub && | ||||
| 	git -C repo add -A && | ||||
| 	git -C repo commit -m submodule && | ||||
|  | ||||
| 	git -c protocol.file.allow=always clone --recurse-submodules repo bad-clone && | ||||
| 	! test -f "$PWD/foo" && | ||||
| 	test -f $(printf "bad-clone/sub\r/post-checkout") | ||||
| ' | ||||
|  | ||||
| test_done | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano