Code

Merge branch 'maint'
[git.git] / git-gui / lib / commit.tcl
index f9791f64dbec927726622f3d0a368606a4f13b17..40a710355751836e78b65101592b753266f507ca 100644 (file)
@@ -6,26 +6,26 @@ proc load_last_commit {} {
        global repo_config
 
        if {[llength $PARENT] == 0} {
        global repo_config
 
        if {[llength $PARENT] == 0} {
-               error_popup {There is nothing to amend.
+               error_popup [mc "There is nothing to amend.
 
 You are about to create the initial commit.  There is no commit before this to amend.
 
 You are about to create the initial commit.  There is no commit before this to amend.
-}
+"]
                return
        }
 
        repository_state curType curHEAD curMERGE_HEAD
        if {$curType eq {merge}} {
                return
        }
 
        repository_state curType curHEAD curMERGE_HEAD
        if {$curType eq {merge}} {
-               error_popup {Cannot amend while merging.
+               error_popup [mc "Cannot amend while merging.
 
 You are currently in the middle of a merge that has not been fully completed.  You cannot amend the prior commit unless you first abort the current merge activity.
 
 You are currently in the middle of a merge that has not been fully completed.  You cannot amend the prior commit unless you first abort the current merge activity.
-}
+"]
                return
        }
 
        set msg {}
        set parents [list]
        if {[catch {
                return
        }
 
        set msg {}
        set parents [list]
        if {[catch {
-                       set fd [open "| git cat-file commit $curHEAD" r]
+                       set fd [git_read cat-file commit $curHEAD]
                        fconfigure $fd -encoding binary -translation lf
                        if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
                                set enc utf-8
                        fconfigure $fd -encoding binary -translation lf
                        if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
                                set enc utf-8
@@ -37,11 +37,16 @@ You are currently in the middle of a merge that has not been fully completed.  Y
                                        set enc [string tolower [string range $line 9 end]]
                                }
                        }
                                        set enc [string tolower [string range $line 9 end]]
                                }
                        }
-                       set msg [encoding convertfrom $enc [read $fd]]
-                       set msg [string trim $msg]
+                       set msg [read $fd]
                        close $fd
                        close $fd
+
+                       set enc [tcl_encoding $enc]
+                       if {$enc ne {}} {
+                               set msg [encoding convertfrom $enc $msg]
+                       }
+                       set msg [string trim $msg]
                } err]} {
                } err]} {
-               error_popup "Error loading commit data for amend:\n\n$err"
+               error_popup [strcat [mc "Error loading commit data for amend:"] "\n\n$err"]
                return
        }
 
                return
        }
 
@@ -58,7 +63,7 @@ You are currently in the middle of a merge that has not been fully completed.  Y
        $ui_comm insert end $msg
        $ui_comm edit reset
        $ui_comm edit modified false
        $ui_comm insert end $msg
        $ui_comm edit reset
        $ui_comm edit modified false
-       rescan {set ui_status_value {Ready.}}
+       rescan ui_ready
 }
 
 set GIT_COMMITTER_IDENT {}
 }
 
 set GIT_COMMITTER_IDENT {}
@@ -68,12 +73,12 @@ proc committer_ident {} {
 
        if {$GIT_COMMITTER_IDENT eq {}} {
                if {[catch {set me [git var GIT_COMMITTER_IDENT]} err]} {
 
        if {$GIT_COMMITTER_IDENT eq {}} {
                if {[catch {set me [git var GIT_COMMITTER_IDENT]} err]} {
-                       error_popup "Unable to obtain your identity:\n\n$err"
+                       error_popup [strcat [mc "Unable to obtain your identity:"] "\n\n$err"]
                        return {}
                }
                if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \
                        $me me GIT_COMMITTER_IDENT]} {
                        return {}
                }
                if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \
                        $me me GIT_COMMITTER_IDENT]} {
-                       error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me"
+                       error_popup [strcat [mc "Invalid GIT_COMMITTER_IDENT:"] "\n\n$me"]
                        return {}
                }
        }
                        return {}
                }
        }
@@ -108,12 +113,12 @@ proc create_new_commit {} {
        $ui_comm delete 0.0 end
        $ui_comm edit reset
        $ui_comm edit modified false
        $ui_comm delete 0.0 end
        $ui_comm edit reset
        $ui_comm edit modified false
-       rescan {set ui_status_value {Ready.}}
+       rescan ui_ready
 }
 
 proc commit_tree {} {
        global HEAD commit_type file_states ui_comm repo_config
 }
 
 proc commit_tree {} {
        global HEAD commit_type file_states ui_comm repo_config
-       global ui_status_value pch_error
+       global pch_error
 
        if {[committer_ident] eq {}} return
        if {![lock_index update]} return
 
        if {[committer_ident] eq {}} return
        if {![lock_index update]} return
@@ -125,14 +130,14 @@ proc commit_tree {} {
                && $curType eq {normal}
                && $curHEAD eq $HEAD} {
        } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
                && $curType eq {normal}
                && $curHEAD eq $HEAD} {
        } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
-               info_popup {Last scanned state does not match repository state.
+               info_popup [mc "Last scanned state does not match repository state.
 
 Another Git program has modified this repository since the last scan.  A rescan must be performed before another commit can be created.
 
 The rescan will be automatically started now.
 
 Another Git program has modified this repository since the last scan.  A rescan must be performed before another commit can be created.
 
 The rescan will be automatically started now.
-}
+"]
                unlock_index
                unlock_index
-               rescan {set ui_status_value {Ready.}}
+               rescan ui_ready
                return
        }
 
                return
        }
 
@@ -146,26 +151,26 @@ The rescan will be automatically started now.
                D? -
                M? {set files_ready 1}
                U? {
                D? -
                M? {set files_ready 1}
                U? {
-                       error_popup "Unmerged files cannot be committed.
+                       error_popup [mc "Unmerged files cannot be committed.
 
 
-File [short_path $path] has merge conflicts.  You must resolve them and add the file before committing.
-"
+File %s has merge conflicts.  You must resolve them and stage the file before committing.
+" [short_path $path]]
                        unlock_index
                        return
                }
                default {
                        unlock_index
                        return
                }
                default {
-                       error_popup "Unknown file state [lindex $s 0] detected.
+                       error_popup [mc "Unknown file state %s detected.
 
 
-File [short_path $path] cannot be committed by this program.
-"
+File %s cannot be committed by this program.
+" [lindex $s 0] [short_path $path]]
                }
                }
        }
        if {!$files_ready && ![string match *merge $curType]} {
                }
                }
        }
        if {!$files_ready && ![string match *merge $curType]} {
-               info_popup {No changes to commit.
+               info_popup [mc "No changes to commit.
 
 
-You must add at least 1 file before you can commit.
-}
+You must stage at least 1 file before you can commit.
+"]
                unlock_index
                return
        }
                unlock_index
                return
        }
@@ -175,57 +180,64 @@ You must add at least 1 file before you can commit.
        set msg [string trim [$ui_comm get 1.0 end]]
        regsub -all -line {[ \t\r]+$} $msg {} msg
        if {$msg eq {}} {
        set msg [string trim [$ui_comm get 1.0 end]]
        regsub -all -line {[ \t\r]+$} $msg {} msg
        if {$msg eq {}} {
-               error_popup {Please supply a commit message.
+               error_popup [mc "Please supply a commit message.
 
 A good commit message has the following format:
 
 
 A good commit message has the following format:
 
-- First line: Describe in one sentance what you did.
+- First line: Describe in one sentence what you did.
 - Second line: Blank
 - Remaining lines: Describe why this change is good.
 - Second line: Blank
 - Remaining lines: Describe why this change is good.
-}
+"]
                unlock_index
                return
        }
 
                unlock_index
                return
        }
 
-       # -- Run the pre-commit hook.
+       # -- Build the message file.
        #
        #
-       set pchook [gitdir hooks pre-commit]
+       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
 
 
-       # On Cygwin [file executable] might lie so we need to ask
-       # the shell if the hook is executable.  Yes that's annoying.
+       # -- Run the pre-commit hook.
        #
        #
-       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 {
-               commit_writetree $curHEAD $msg
+       set fd_ph [githook_read pre-commit]
+       if {$fd_ph eq {}} {
+               commit_commitmsg $curHEAD $msg_p
                return
        }
 
                return
        }
 
-       set ui_status_value {Calling pre-commit hook...}
+       ui_status [mc "Calling pre-commit hook..."]
        set pch_error {}
        set pch_error {}
-       set fd_ph [open "| $pchook" r]
-       fconfigure $fd_ph -blocking 0 -translation binary
+       fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
        fileevent $fd_ph readable \
        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} {
-       global pch_error ui_status_value
+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}]} {
 
        append pch_error [read $fd_ph]
        fconfigure $fd_ph -blocking 1
        if {[eof $fd_ph]} {
                if {[catch {close $fd_ph}]} {
-                       set ui_status_value {Commit declined by pre-commit hook.}
+                       catch {file delete $msg_p}
+                       ui_status [mc "Commit declined by pre-commit hook."]
                        hook_failed_popup pre-commit $pch_error
                        unlock_index
                } else {
                        hook_failed_popup pre-commit $pch_error
                        unlock_index
                } else {
-                       commit_writetree $curHEAD $msg
+                       commit_commitmsg $curHEAD $msg_p
                }
                set pch_error {}
                return
                }
                set pch_error {}
                return
@@ -233,26 +245,63 @@ proc commit_prehook_wait {fd_ph curHEAD msg} {
        fconfigure $fd_ph -blocking 0
 }
 
        fconfigure $fd_ph -blocking 0
 }
 
-proc commit_writetree {curHEAD msg} {
-       global ui_status_value
+proc commit_commitmsg {curHEAD msg_p} {
+       global pch_error
+
+       # -- Run the commit-msg hook.
+       #
+       set fd_ph [githook_read commit-msg $msg_p]
+       if {$fd_ph eq {}} {
+               commit_writetree $curHEAD $msg_p
+               return
+       }
+
+       ui_status [mc "Calling commit-msg hook..."]
+       set pch_error {}
+       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 [mc "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
+}
 
 
-       set ui_status_value {Committing changes...}
-       set fd_wt [open "| git write-tree" r]
+proc commit_writetree {curHEAD msg_p} {
+       ui_status [mc "Committing changes..."]
+       set fd_wt [git_read write-tree]
        fileevent $fd_wt readable \
        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 HEAD PARENT MERGE_HEAD commit_type
-       global all_heads current_branch
-       global ui_status_value ui_comm selected_commit_type
+       global current_branch
+       global ui_comm selected_commit_type
        global file_states selected_paths rescan_active
        global repo_config
 
        gets $fd_wt tree_id
        global file_states selected_paths rescan_active
        global repo_config
 
        gets $fd_wt tree_id
-       if {$tree_id eq {} || [catch {close $fd_wt} err]} {
-               error_popup "write-tree failed:\n\n$err"
-               set ui_status_value {Commit failed.}
+       if {[catch {close $fd_wt} err]} {
+               catch {file delete $msg_p}
+               error_popup [strcat [mc "write-tree failed:"] "\n\n$err"]
+               ui_status [mc "Commit failed."]
                unlock_index
                return
        }
                unlock_index
                return
        }
@@ -260,31 +309,32 @@ proc commit_committree {fd_wt curHEAD msg} {
        # -- Verify this wasn't an empty change.
        #
        if {$commit_type eq {normal}} {
        # -- Verify this wasn't an empty change.
        #
        if {$commit_type eq {normal}} {
-               set old_tree [git rev-parse "$PARENT^{tree}"]
+               set fd_ot [git_read cat-file commit $PARENT]
+               fconfigure $fd_ot -encoding binary -translation lf
+               set old_tree [gets $fd_ot]
+               close $fd_ot
+
+               if {[string equal -length 5 {tree } $old_tree]
+                       && [string length $old_tree] == 45} {
+                       set old_tree [string range $old_tree 5 end]
+               } else {
+                       error [mc "Commit %s appears to be corrupt" $PARENT]
+               }
+
                if {$tree_id eq $old_tree} {
                if {$tree_id eq $old_tree} {
-                       info_popup {No changes to commit.
+                       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.
 
 A rescan will be automatically started now.
 
 No files were modified by this commit and it was not a merge commit.
 
 A rescan will be automatically started now.
-}
+"]
                        unlock_index
                        unlock_index
-                       rescan {set ui_status_value {No changes to commit.}}
+                       rescan {ui_status [mc "No changes to commit."]}
                        return
                }
        }
 
                        return
                }
        }
 
-       # -- Build the message.
-       #
-       set msg_p [gitdir COMMIT_EDITMSG]
-       set msg_wt [open $msg_p w]
-       if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
-               set enc utf-8
-       }
-       fconfigure $msg_wt -encoding binary -translation binary
-       puts -nonewline $msg_wt [encoding convertto $enc $msg]
-       close $msg_wt
-
        # -- Create the commit.
        #
        set cmd [list commit-tree $tree_id]
        # -- Create the commit.
        #
        set cmd [list commit-tree $tree_id]
@@ -293,8 +343,9 @@ A rescan will be automatically started now.
        }
        lappend cmd <$msg_p
        if {[catch {set cmt_id [eval git $cmd]} err]} {
        }
        lappend cmd <$msg_p
        if {[catch {set cmt_id [eval git $cmd]} err]} {
-               error_popup "commit-tree failed:\n\n$err"
-               set ui_status_value {Commit failed.}
+               catch {file delete $msg_p}
+               error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"]
+               ui_status [mc "Commit failed."]
                unlock_index
                return
        }
                unlock_index
                return
        }
@@ -305,18 +356,16 @@ A rescan will be automatically started now.
        if {$commit_type ne {normal}} {
                append reflogm " ($commit_type)"
        }
        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]} {
        append reflogm {: } $subject
        if {[catch {
                        git update-ref -m $reflogm HEAD $cmt_id $curHEAD
                } err]} {
-               error_popup "update-ref failed:\n\n$err"
-               set ui_status_value {Commit failed.}
+               catch {file delete $msg_p}
+               error_popup [strcat [mc "update-ref failed:"] "\n\n$err"]
+               ui_status [mc "Commit failed."]
                unlock_index
                return
        }
                unlock_index
                return
        }
@@ -331,39 +380,36 @@ A rescan will be automatically started now.
 
        # -- Let rerere do its thing.
        #
 
        # -- Let rerere do its thing.
        #
-       if {[file isdirectory [gitdir rr-cache]]} {
+       if {[get_config rerere.enabled] eq {}} {
+               set rerere [file isdirectory [gitdir rr-cache]]
+       } else {
+               set rerere [is_config_true rerere.enabled]
+       }
+       if {$rerere} {
                catch {git rerere}
        }
 
        # -- Run the post-commit hook.
        #
                catch {git rerere}
        }
 
        # -- 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
        $ui_comm edit reset
        $ui_comm edit modified false
        }
 
        $ui_comm delete 0.0 end
        $ui_comm edit reset
        $ui_comm edit modified false
+       if {$::GITGUI_BCK_exists} {
+               catch {file delete [gitdir GITGUI_BCK]}
+               set ::GITGUI_BCK_exists 0
+       }
 
        if {[is_enabled singlecommit]} do_quit
 
 
        if {[is_enabled singlecommit]} do_quit
 
-       # -- Make sure our current branch exists.
-       #
-       if {$commit_type eq {initial}} {
-               lappend all_heads $current_branch
-               set all_heads [lsort -unique $all_heads]
-               populate_branch_menu
-       }
-
        # -- Update in memory status
        #
        set selected_commit_type new
        # -- Update in memory status
        #
        set selected_commit_type new
@@ -405,6 +451,20 @@ A rescan will be automatically started now.
        display_all_files
        unlock_index
        reshow_diff
        display_all_files
        unlock_index
        reshow_diff
-       set ui_status_value \
-               "Created commit [string range $cmt_id 0 7]: $subject"
+       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
 }
 }