]> asedeno.scripts.mit.edu Git - git.git/blobdiff - lib/blame.tcl
git-gui: Fix the blame window shape.
[git.git] / lib / blame.tcl
index c8975cfecdbe5dd4cf00a458d5b07a6ff9840f8c..221313c76849c57875525b8ed1acc6f56c83bd7d 100644 (file)
@@ -58,7 +58,7 @@ field tooltip_t         {} ; # Text widget in $tooltip_wm
 field tooltip_timer     {} ; # Current timer event for our tooltip
 field tooltip_commit    {} ; # Commit(s) in tooltip
 
-constructor new {i_commit i_path} {
+constructor new {i_commit i_path i_jump} {
        global cursor_ptr
        variable active_color
        variable group_colors
@@ -69,6 +69,8 @@ constructor new {i_commit i_path} {
        make_toplevel top w
        wm title $top [append "[appname] ([reponame]): " [mc "File Viewer"]]
 
+       set font_w [font measure font_diff "0"]
+
        frame $w.header -background gold
        label $w.header.commit_l \
                -text [mc "Commit:"] \
@@ -114,9 +116,9 @@ constructor new {i_commit i_path} {
        pack $w_path -fill x -side right
        pack $w.header.path_l -side right
 
-       panedwindow $w.file_pane -orient vertical
-       frame $w.file_pane.out
-       frame $w.file_pane.cm
+       panedwindow $w.file_pane -orient vertical -borderwidth 0 -sashwidth 3
+       frame $w.file_pane.out -relief flat -borderwidth 1
+       frame $w.file_pane.cm -relief sunken -borderwidth 1
        $w.file_pane add $w.file_pane.out \
                -sticky nsew \
                -minsize 100 \
@@ -256,12 +258,22 @@ constructor new {i_commit i_path} {
        $w.ctxm add command \
                -label [mc "Copy Commit"] \
                -command [cb _copycommit]
+       $w.ctxm add separator
+       menu $w.ctxm.enc
+       build_encoding_menu $w.ctxm.enc [cb _setencoding]
+       $w.ctxm add cascade \
+               -label [mc "Encoding"] \
+               -menu $w.ctxm.enc
        $w.ctxm add command \
                -label [mc "Do Full Copy Detection"] \
                -command [cb _fullcopyblame]
+       $w.ctxm add separator
        $w.ctxm add command \
                -label [mc "Show History Context"] \
                -command [cb _gitkcommit]
+       $w.ctxm add command \
+               -label [mc "Blame Parent Commit"] \
+               -command [cb _blameparent]
 
        foreach i $w_columns {
                for {set g 0} {$g < [llength $group_colors]} {incr g} {
@@ -318,9 +330,14 @@ constructor new {i_commit i_path} {
 
        set req_w [winfo reqwidth  $top]
        set req_h [winfo reqheight $top]
-       set scr_h [expr {[winfo screenheight $top] - 100}]
-       if {$req_w < 600} {set req_w 600}
+       set scr_w [expr {[winfo screenwidth $top] - 40}]
+       set scr_h [expr {[winfo screenheight $top] - 120}]
+       set opt_w [expr {$font_w * (80 + 5*3 + 3)}]
+       if {$req_w < $opt_w} {set req_w $opt_w}
+       if {$req_w > $scr_w} {set req_w $scr_w}
+       set opt_h [expr {$req_w*4/3}]
        if {$req_h < $scr_h} {set req_h $scr_h}
+       if {$req_h > $opt_h} {set req_h $opt_h}
        set g "${req_w}x${req_h}"
        wm geometry $top $g
        update
@@ -328,14 +345,14 @@ constructor new {i_commit i_path} {
        set old_height [winfo height $w.file_pane]
        $w.file_pane sash place 0 \
                [lindex [$w.file_pane sash coord 0] 0] \
-               [expr {int($old_height * 0.70)}]
+               [expr {int($old_height * 0.80)}]
        bind $w.file_pane <Configure> \
        "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}"
 
        wm protocol $top WM_DELETE_WINDOW "destroy $top"
        bind $top <Destroy> [cb _kill]
 
-       _load $this {}
+       _load $this $i_jump
 }
 
 method _kill {} {
@@ -396,7 +413,10 @@ method _load {jump} {
        } else {
                set fd [git_read cat-file blob "$commit:$path"]
        }
-       fconfigure $fd -blocking 0 -translation lf -encoding binary
+       fconfigure $fd \
+               -blocking 0 \
+               -translation lf \
+               -encoding [get_path_encoding $path]
        fileevent $fd readable [cb _read_file $fd $jump]
        set current_fd $fd
 }
@@ -505,7 +525,7 @@ method _exec_blame {cur_w cur_d options cur_s} {
        }
        lappend options -- $path
        set fd [eval git_read --nice blame $options]
-       fconfigure $fd -blocking 0 -translation lf -encoding binary
+       fconfigure $fd -blocking 0 -translation lf -encoding utf-8
        fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
        set current_fd $fd
        set blame_lines 0
@@ -785,24 +805,42 @@ method _click {cur_w pos} {
        _showcommit $this $cur_w $lno
 }
 
+method _setencoding {enc} {
+       force_path_encoding $path $enc
+       _load $this [list \
+               $highlight_column \
+               $highlight_line \
+               [lindex [$w_file xview] 0] \
+               [lindex [$w_file yview] 0] \
+               ]
+}
+
 method _load_commit {cur_w cur_d pos} {
        upvar #0 $cur_d line_data
        set lno [lindex [split [$cur_w index $pos] .] 0]
        set dat [lindex $line_data $lno]
        if {$dat ne {}} {
-               lappend history [list \
-                       $commit $path \
-                       $highlight_column \
-                       $highlight_line \
-                       [lindex [$w_file xview] 0] \
-                       [lindex [$w_file yview] 0] \
-                       ]
-               set commit [lindex $dat 0]
-               set path   [lindex $dat 1]
-               _load $this [list [lindex $dat 2]]
+               _load_new_commit $this  \
+                       [lindex $dat 0] \
+                       [lindex $dat 1] \
+                       [list [lindex $dat 2]]
        }
 }
 
+method _load_new_commit {new_commit new_path jump} {
+       lappend history [list \
+               $commit $path \
+               $highlight_column \
+               $highlight_line \
+               [lindex [$w_file xview] 0] \
+               [lindex [$w_file yview] 0] \
+               ]
+
+       set commit $new_commit
+       set path   $new_path
+       _load $this $jump
+}
+
 method _showcommit {cur_w lno} {
        global repo_config
        variable active_color
@@ -870,12 +908,6 @@ method _showcommit {cur_w lno} {
                                set enc [tcl_encoding $enc]
                                if {$enc ne {}} {
                                        set msg [encoding convertfrom $enc $msg]
-                                       set author_name [encoding convertfrom $enc $author_name]
-                                       set committer_name [encoding convertfrom $enc $committer_name]
-                                       set header($cmit,author) $author_name
-                                       set header($cmit,committer) $committer_name
-                                       set header($cmit,summary) \
-                                       [encoding convertfrom $enc $header($cmit,summary)]
                                }
                                set msg [string trim $msg]
                        }
@@ -931,9 +963,20 @@ method _format_offset_date {base offset} {
 }
 
 method _gitkcommit {} {
+       global nullid
+
        set dat [_get_click_amov_info $this]
        if {$dat ne {}} {
                set cmit [lindex $dat 0]
+
+               # If the line belongs to the working copy, use HEAD instead
+               if {$cmit eq $nullid} {
+                       if {[catch {set cmit [git rev-parse --verify HEAD]} err]} {
+                               error_popup [strcat [mc "Cannot find HEAD commit:"] "\n\n$err"]
+                               return;
+                       }
+               }
+
                set radius [get_config gui.blamehistoryctx]
                set cmdline [list --select-commit=$cmit]
 
@@ -969,6 +1012,92 @@ method _gitkcommit {} {
        }
 }
 
+method _blameparent {} {
+       global nullid
+
+       set dat [_get_click_amov_info $this]
+       if {$dat ne {}} {
+               set cmit [lindex $dat 0]
+               set new_path [lindex $dat 1]
+
+               # Allow using Blame Parent on lines modified in the working copy
+               if {$cmit eq $nullid} {
+                       set parent_ref "HEAD"
+               } else {
+                       set parent_ref "$cmit^"
+               }
+               if {[catch {set cparent [git rev-parse --verify $parent_ref]} err]} {
+                       error_popup [strcat [mc "Cannot find parent commit:"] "\n\n$err"]
+                       return;
+               }
+
+               _kill $this
+
+               # Generate a diff between the commit and its parent,
+               # and use the hunks to update the line number.
+               # Request zero context to simplify calculations.
+               if {$cmit eq $nullid} {
+                       set diffcmd [list diff-index --unified=0 $cparent -- $new_path]
+               } else {
+                       set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path]
+               }
+               if {[catch {set fd [eval git_read $diffcmd]} err]} {
+                       $status stop [mc "Unable to display parent"]
+                       error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
+                       return
+               }
+
+               set r_orig_line [lindex $dat 2]
+
+               fconfigure $fd \
+                       -blocking 0 \
+                       -encoding binary \
+                       -translation binary
+               fileevent $fd readable [cb _read_diff_load_commit \
+                       $fd $cparent $new_path $r_orig_line]
+               set current_fd $fd
+       }
+}
+
+method _read_diff_load_commit {fd cparent new_path tline} {
+       if {$fd ne $current_fd} {
+               catch {close $fd}
+               return
+       }
+
+       while {[gets $fd line] >= 0} {
+               if {[regexp {^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@} $line line \
+                       old_line osz old_size new_line nsz new_size]} {
+
+                       if {$osz eq {}} { set old_size 1 }
+                       if {$nsz eq {}} { set new_size 1 }
+
+                       if {$new_line <= $tline} {
+                               if {[expr {$new_line + $new_size}] > $tline} {
+                                       # Target line within the hunk
+                                       set line_shift [expr {
+                                               ($new_size-$old_size)*($tline-$new_line)/$new_size
+                                               }]
+                               } else {
+                                       set line_shift [expr {$new_size-$old_size}]
+                               }
+
+                               set r_orig_line [expr {$r_orig_line - $line_shift}]
+                       }
+               }
+       }
+
+       if {[eof $fd]} {
+               close $fd;
+               set current_fd {}
+
+               _load_new_commit $this  \
+                       $cparent        \
+                       $new_path       \
+                       [list $r_orig_line]
+       }
+} ifdeleted { catch {close $fd} }
+
 method _show_tooltip {cur_w pos} {
        if {$tooltip_wm ne {}} {
                _open_tooltip $this $cur_w