Anonymous avatar Anonymous committed fb0ca47

git-gui: Honor the standard commit-msg hook

Under core Git the git-commit tool will invoke the commit-msg hook
if it exists and is executable to the user running git-commit. As
a hook it has some limited value as it cannot alter the commit, but
it can modify the message the user is attempting to commit. It is
also able to examine the message to ensure it conforms to some local
standards/conventions.

Since the hook takes the name of a temporary file holding the message
as its only parameter we need to move the code that creates the temp
file up earlier in our commit code path, and then pass through that
file name to the latest stage (where we call git-commit-tree). We let
the hook alter the file as it sees fit and we don't bother to look at
its content again until the commit succeeded and we need the subject
for the reflog update.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>;

Comments (0)

Files changed (1)

 		return
 	}
 
+	# -- Build the message file.
+	#
+	set msg_p [gitdir GITGUI_EDITMSG]
+	set msg_wt [open $msg_p w]
+	fconfigure $msg_wt -translation lf
+	if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
+		set enc utf-8
+	}
+	set use_enc [tcl_encoding $enc]
+	if {$use_enc ne {}} {
+		fconfigure $msg_wt -encoding $use_enc
+	} else {
+		puts stderr [mc "warning: Tcl does not support encoding '%s'." $enc]
+		fconfigure $msg_wt -encoding utf-8
+	}
+	puts $msg_wt $msg
+	close $msg_wt
+
 	# -- Run the pre-commit hook.
 	#
 	set pchook [gitdir hooks pre-commit]
 	} elseif {[file executable $pchook]} {
 		set pchook [list $pchook |& cat]
 	} else {
-		commit_writetree $curHEAD $msg
+		commit_commitmsg $curHEAD $msg_p
 		return
 	}
 
 	set fd_ph [open "| $pchook" r]
 	fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
 	fileevent $fd_ph readable \
-		[list commit_prehook_wait $fd_ph $curHEAD $msg]
+		[list commit_prehook_wait $fd_ph $curHEAD $msg_p]
 }
 
-proc commit_prehook_wait {fd_ph curHEAD msg} {
+proc commit_prehook_wait {fd_ph curHEAD msg_p} {
 	global pch_error
 
 	append pch_error [read $fd_ph]
 	fconfigure $fd_ph -blocking 1
 	if {[eof $fd_ph]} {
 		if {[catch {close $fd_ph}]} {
+			catch {file delete $msg_p}
 			ui_status {Commit declined by pre-commit hook.}
 			hook_failed_popup pre-commit $pch_error
 			unlock_index
 		} else {
-			commit_writetree $curHEAD $msg
+			commit_commitmsg $curHEAD $msg_p
 		}
 		set pch_error {}
 		return
 	fconfigure $fd_ph -blocking 0
 }
 
-proc commit_writetree {curHEAD msg} {
+proc commit_commitmsg {curHEAD msg_p} {
+	global pch_error
+
+	# -- Run the commit-msg hook.
+	#
+	set pchook [gitdir hooks commit-msg]
+
+	# On Cygwin [file executable] might lie so we need to ask
+	# the shell if the hook is executable.  Yes that's annoying.
+	#
+	if {[is_Cygwin] && [file isfile $pchook]} {
+		set pchook [list sh -c [concat \
+			"if test -x \"$pchook\";" \
+			"then exec \"$pchook\" \"$msg_p\" 2>&1;" \
+			"fi"]]
+	} elseif {[file executable $pchook]} {
+		set pchook [list $pchook $msg_p |& cat]
+	} else {
+		commit_writetree $curHEAD $msg_p
+		return
+	}
+
+	ui_status {Calling commit-msg hook...}
+	set pch_error {}
+	set fd_ph [open "| $pchook" r]
+	fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
+	fileevent $fd_ph readable \
+		[list commit_commitmsg_wait $fd_ph $curHEAD $msg_p]
+}
+
+proc commit_commitmsg_wait {fd_ph curHEAD msg_p} {
+	global pch_error
+
+	append pch_error [read $fd_ph]
+	fconfigure $fd_ph -blocking 1
+	if {[eof $fd_ph]} {
+		if {[catch {close $fd_ph}]} {
+			catch {file delete $msg_p}
+			ui_status {Commit declined by commit-msg hook.}
+			hook_failed_popup commit-msg $pch_error
+			unlock_index
+		} else {
+			commit_writetree $curHEAD $msg_p
+		}
+		set pch_error {}
+		return
+	}
+	fconfigure $fd_ph -blocking 0
+}
+
+proc commit_writetree {curHEAD msg_p} {
 	ui_status {Committing changes...}
 	set fd_wt [git_read write-tree]
 	fileevent $fd_wt readable \
-		[list commit_committree $fd_wt $curHEAD $msg]
+		[list commit_committree $fd_wt $curHEAD $msg_p]
 }
 
-proc commit_committree {fd_wt curHEAD msg} {
+proc commit_committree {fd_wt curHEAD msg_p} {
 	global HEAD PARENT MERGE_HEAD commit_type
 	global current_branch
 	global ui_comm selected_commit_type
 
 	gets $fd_wt tree_id
 	if {[catch {close $fd_wt} err]} {
+		catch {file delete $msg_p}
 		error_popup [strcat [mc "write-tree failed:"] "\n\n$err"]
 		ui_status {Commit failed.}
 		unlock_index
 		}
 
 		if {$tree_id eq $old_tree} {
+			catch {file delete $msg_p}
 			info_popup [mc "No changes to commit.
 
 No files were modified by this commit and it was not a merge commit.
 		}
 	}
 
-	# -- Build the message.
-	#
-	set msg_p [gitdir COMMIT_EDITMSG]
-	set msg_wt [open $msg_p w]
-	fconfigure $msg_wt -translation lf
-	if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
-		set enc utf-8
-	}
-	set use_enc [tcl_encoding $enc]
-	if {$use_enc ne {}} {
-		fconfigure $msg_wt -encoding $use_enc
-	} else {
-		puts stderr [mc "warning: Tcl does not support encoding '%s'." $enc]
-		fconfigure $msg_wt -encoding utf-8
-	}
-	puts $msg_wt $msg
-	close $msg_wt
-
 	# -- Create the commit.
 	#
 	set cmd [list commit-tree $tree_id]
 	}
 	lappend cmd <$msg_p
 	if {[catch {set cmt_id [eval git $cmd]} err]} {
+		catch {file delete $msg_p}
 		error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"]
 		ui_status {Commit failed.}
 		unlock_index
 	if {$commit_type ne {normal}} {
 		append reflogm " ($commit_type)"
 	}
-	set i [string first "\n" $msg]
-	if {$i >= 0} {
-		set subject [string range $msg 0 [expr {$i - 1}]]
-	} else {
-		set subject $msg
-	}
+	set msg_fd [open $msg_p r]
+	gets $msg_fd subject
+	close $msg_fd
 	append reflogm {: } $subject
 	if {[catch {
 			git update-ref -m $reflogm HEAD $cmt_id $curHEAD
 		} err]} {
+		catch {file delete $msg_p}
 		error_popup [strcat [mc "update-ref failed:"] "\n\n$err"]
 		ui_status {Commit failed.}
 		unlock_index
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.