From fb0ca475c6ab02eeeb8ca7f2c0558d64301a8800 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 20 Jan 2008 14:11:52 -0500 Subject: [PATCH 1/4] 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 --- lib/commit.tcl | 114 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 31 deletions(-) diff --git a/lib/commit.tcl b/lib/commit.tcl index 1c0586c409..73e18bf982 100644 --- a/lib/commit.tcl +++ b/lib/commit.tcl @@ -192,6 +192,24 @@ A good commit message has the following format: 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] @@ -207,7 +225,7 @@ A good commit message has the following format: } elseif {[file executable $pchook]} { set pchook [list $pchook |& cat] } else { - commit_writetree $curHEAD $msg + commit_commitmsg $curHEAD $msg_p return } @@ -216,21 +234,22 @@ A good commit message has the following format: 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 @@ -238,14 +257,64 @@ proc commit_prehook_wait {fd_ph curHEAD msg} { 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 @@ -254,6 +323,7 @@ proc commit_committree {fd_wt curHEAD msg} { 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 @@ -276,6 +346,7 @@ proc commit_committree {fd_wt curHEAD msg} { } 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. @@ -288,24 +359,6 @@ A rescan will be automatically started now. } } - # -- 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] @@ -314,6 +367,7 @@ A rescan will be automatically started now. } 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 @@ -326,16 +380,14 @@ A rescan will be automatically started now. 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 From c87238e19de70c1066e606df53f4f20f19621acd Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 20 Jan 2008 14:43:38 -0500 Subject: [PATCH 2/4] git-gui: Correct window title for hook failure dialogs During i18n translation work this message was partially broken by using "append" instead of "strcat" to join the two different parts of the message together. Signed-off-by: Shawn O. Pearce --- lib/error.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/error.tcl b/lib/error.tcl index 13565b7ab0..712d217553 100644 --- a/lib/error.tcl +++ b/lib/error.tcl @@ -99,6 +99,6 @@ proc hook_failed_popup {hook msg} { bind $w "grab $w; focus $w" bind $w "destroy $w" - wm title $w [append "[appname] ([reponame]): " [mc "error"]] + wm title $w [strcat "[appname] ([reponame]): " [mc "error"]] tkwait window $w } From ed76cb70f47225fc1a2ba4209b38b89be71adeb6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 20 Jan 2008 14:46:59 -0500 Subject: [PATCH 3/4] git-gui: Consolidate hook execution code into a single function The code we use to test if a hook is executable or not differs on Cygwin from the normal POSIX case. Rather then repeating that for all three hooks we call in our commit code path we can place the common logic into a global procedure and invoke it when necessary. This also lets us get rid of the ugly "|& cat" we were using before as we can now rely on the Tcl 8.4 feature of "2>@1" or fallback to the "|& cat" when necessary. The post-commit hook is now run through the same API, but its outcome does not influence the commit status. As a result we now show any of the errors from the post-commit hook in a dialog window, instead of on the user's tty that was used to launch git-gui. This resolves a long standing bug related to not getting errors out of the post-commit hook when launched under git-gui. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 28 ++++++++++++++++++++++ lib/commit.tcl | 65 ++++++++++++++++++++------------------------------ lib/error.tcl | 16 +++++++------ 3 files changed, 63 insertions(+), 46 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index fcb2ab2fb7..f42e461fd4 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -438,6 +438,34 @@ proc git_write {args} { return [open [concat $opt $cmdp $args] w] } +proc githook_read {hook_name args} { + set pchook [gitdir hooks $hook_name] + lappend args 2>@1 + + # 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]} { + upvar #0 _sh interp + if {![info exists interp]} { + set interp [_which sh] + } + if {$interp eq {}} { + error "hook execution requires sh (not in PATH)" + } + + set scr {if test -x "$1";then exec "$@";fi} + set sh_c [list | $interp -c $scr $interp $pchook] + return [_open_stdout_stderr [concat $sh_c $args]] + } + + if {[file executable $pchook]} { + return [_open_stdout_stderr [concat [list | $pchook] $args]] + } + + return {} +} + proc sq {value} { regsub -all ' $value "'\\''" value return "'$value'" diff --git a/lib/commit.tcl b/lib/commit.tcl index 73e18bf982..947b201c32 100644 --- a/lib/commit.tcl +++ b/lib/commit.tcl @@ -212,26 +212,14 @@ A good commit message has the following format: # -- Run the pre-commit hook. # - set pchook [gitdir hooks pre-commit] - - # 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\" 2>&1;" \ - "fi"]] - } elseif {[file executable $pchook]} { - set pchook [list $pchook |& cat] - } else { + set fd_ph [githook_read pre-commit] + if {$fd_ph eq {}} { commit_commitmsg $curHEAD $msg_p return } ui_status {Calling pre-commit hook...} set pch_error {} - 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_p] @@ -262,26 +250,14 @@ proc commit_commitmsg {curHEAD msg_p} { # -- 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 { + set fd_ph [githook_read commit-msg $msg_p] + if {$fd_ph eq {}} { 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] @@ -415,17 +391,13 @@ A rescan will be automatically started now. # -- Run the post-commit hook. # - set pchook [gitdir hooks post-commit] - if {[is_Cygwin] && [file isfile $pchook]} { - set pchook [list sh -c [concat \ - "if test -x \"$pchook\";" \ - "then exec \"$pchook\";" \ - "fi"]] - } elseif {![file executable $pchook]} { - set pchook {} - } - if {$pchook ne {}} { - catch {exec $pchook &} + set fd_ph [githook_read post-commit] + if {$fd_ph ne {}} { + upvar #0 pch_error$cmt_id pc_err + set pc_err {} + fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} + fileevent $fd_ph readable \ + [list commit_postcommit_wait $fd_ph $cmt_id] } $ui_comm delete 0.0 end @@ -481,3 +453,18 @@ A rescan will be automatically started now. reshow_diff ui_status [mc "Created commit %s: %s" [string range $cmt_id 0 7] $subject] } + +proc commit_postcommit_wait {fd_ph cmt_id} { + upvar #0 pch_error$cmt_id pch_error + + append pch_error [read $fd_ph] + fconfigure $fd_ph -blocking 1 + if {[eof $fd_ph]} { + if {[catch {close $fd_ph}]} { + hook_failed_popup post-commit $pch_error 0 + } + unset pch_error + return + } + fconfigure $fd_ph -blocking 0 +} diff --git a/lib/error.tcl b/lib/error.tcl index 712d217553..0fdd7531da 100644 --- a/lib/error.tcl +++ b/lib/error.tcl @@ -62,7 +62,7 @@ proc ask_popup {msg} { eval $cmd } -proc hook_failed_popup {hook msg} { +proc hook_failed_popup {hook msg {is_fatal 1}} { set w .hookfail toplevel $w @@ -77,14 +77,16 @@ proc hook_failed_popup {hook msg} { -width 80 -height 10 \ -font font_diff \ -yscrollcommand [list $w.m.sby set] - label $w.m.l2 \ - -text [mc "You must correct the above errors before committing."] \ - -anchor w \ - -justify left \ - -font font_uibold scrollbar $w.m.sby -command [list $w.m.t yview] pack $w.m.l1 -side top -fill x - pack $w.m.l2 -side bottom -fill x + if {$is_fatal} { + label $w.m.l2 \ + -text [mc "You must correct the above errors before committing."] \ + -anchor w \ + -justify left \ + -font font_uibold + pack $w.m.l2 -side bottom -fill x + } pack $w.m.sby -side right -fill y pack $w.m.t -side left -fill both -expand 1 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10 From 6caaf2daf0ea50ff99009b94f24252b8fa747e64 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 20 Jan 2008 23:03:23 -0500 Subject: [PATCH 4/4] git-gui: Correct encoding of glossary/fr.po to UTF-8 Junio noticed this was incorrectly added in ISO-8859-1 but it should be in UTF-8 (as the headers claim UTF-8, and our convention is to use only UTF-8). Signed-off-by: Shawn O. Pearce --- po/glossary/fr.po | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/po/glossary/fr.po b/po/glossary/fr.po index bb2feaf137..27c006abb2 100644 --- a/po/glossary/fr.po +++ b/po/glossary/fr.po @@ -34,7 +34,7 @@ msgstr "branche" #. "" msgid "branch [verb]" -msgstr "créer une branche" +msgstr "créer une branche" #. "" msgid "checkout [noun]" @@ -58,7 +58,7 @@ msgstr "commiter" #. "" msgid "diff [noun]" -msgstr "différence" +msgstr "différence" #. "" msgid "diff [verb]" @@ -70,11 +70,11 @@ msgstr "fusion par avance rapide" #. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too." msgid "fetch" -msgstr "récupérer" +msgstr "récupérer" #. "A collection of files. The index is a stored version of your working tree." msgid "index (in git-gui: staging area)" -msgstr "pré-commit" +msgstr "pré-commit" #. "A successful merge results in the creation of a new commit representing the result of the merge." msgid "merge [noun]" @@ -106,15 +106,15 @@ msgstr "refaire" #. "An other repository ('remote'). One might have a set of remotes whose branches one tracks." msgid "remote" -msgstr "référentiel distant" +msgstr "référentiel distant" #. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)" msgid "repository" -msgstr "référentiel" +msgstr "référentiel" #. "" msgid "reset" -msgstr "réinitialiser" +msgstr "réinitialiser" #. "" msgid "revert" @@ -122,7 +122,7 @@ msgstr "inverser" #. "A particular state of files and directories which was stored in the object database." msgid "revision" -msgstr "révision" +msgstr "révision" #. "" msgid "sign off" @@ -130,11 +130,11 @@ msgstr "signer" #. "" msgid "staging area" -msgstr "pré-commit" +msgstr "pré-commit" #. "" msgid "status" -msgstr "état" +msgstr "état" #. "A ref pointing to a tag or commit object" msgid "tag [noun]" @@ -150,15 +150,15 @@ msgstr "branche de suivi" #. "" msgid "undo" -msgstr "défaire" +msgstr "défaire" #. "" msgid "update" -msgstr "mise à jour" +msgstr "mise à jour" #. "" msgid "verify" -msgstr "vérifier" +msgstr "vérifier" #. "The tree of actual checked out files." msgid "working copy, working tree"