From cb07fc2a29c86d1bc11f5415368f778d25d3d20a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 14:20:27 -0500 Subject: git-gui: Initial revision. This is based on Paul Mackerras' gitool prototype which he offered up to the community earlier in 2006. Its mostly however a rewrite from scratch of a Tcl/Tk based graphical interface for Git and the most common commands users might need to perform. Currently it can display the status of the current repository, and not much else. Signed-off-by: Shawn O. Pearce --- git-gui | 764 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 764 insertions(+) create mode 100755 git-gui diff --git a/git-gui b/git-gui new file mode 100755 index 0000000000..dfa3000260 --- /dev/null +++ b/git-gui @@ -0,0 +1,764 @@ +#!/bin/sh +# Tcl ignores the next line -*- tcl -*- \ +exec wish "$0" -- "$@" + +# Copyright (C) 2006 Shawn Pearce, Paul Mackerras. All rights reserved. +# This program is free software; it may be used, copied, modified +# and distributed under the terms of the GNU General Public Licence, +# either version 2, or (at your option) any later version. + + +###################################################################### +## +## status + +set status_active 0 + +proc update_status {} { + global gitdir HEAD commit_type + global ui_index ui_other ui_status_value + global status_active file_states + + if {$status_active > 0} return + + array unset file_states + set ui_status_value {Refreshing file status...} + foreach w [list $ui_index $ui_other] { + $w conf -state normal + $w delete 0.0 end + $w conf -state disabled + } + + if {[catch {set HEAD [exec git rev-parse --verify HEAD]}]} { + set commit_type initial + } else { + set commit_type normal + } + + set ls_others [list | git ls-files --others -z \ + --exclude-per-directory=.gitignore] + set info_exclude [file join $gitdir info exclude] + if {[file readable $info_exclude]} { + lappend ls_others "--exclude-from=$info_exclude" + } + + set fd_di [open "| git diff-index --cached -z $HEAD" r] + set fd_df [open "| git diff-files -z" r] + set fd_lo [open $ls_others r] + set status_active 3 + + fconfigure $fd_di -blocking 0 -translation binary + fconfigure $fd_df -blocking 0 -translation binary + fconfigure $fd_lo -blocking 0 -translation binary + fileevent $fd_di readable [list read_diff_index $fd_di] + fileevent $fd_df readable [list read_diff_files $fd_df] + fileevent $fd_lo readable [list read_ls_others $fd_lo] +} + +proc read_diff_index {fd} { + global buf_rdi + + append buf_rdi [read $fd] + set pck [split $buf_rdi "\0"] + set buf_rdi [lindex $pck end] + foreach {m p} [lrange $pck 0 end-1] { + if {$m != {} && $p != {}} { + display_file $p [string index $m end]_ + } + } + status_eof $fd buf_rdi +} + +proc read_diff_files {fd} { + global buf_rdf + + append buf_rdf [read $fd] + set pck [split $buf_rdf "\0"] + set buf_rdf [lindex $pck end] + foreach {m p} [lrange $pck 0 end-1] { + if {$m != {} && $p != {}} { + display_file $p _[string index $m end] + } + } + status_eof $fd buf_rdf +} + +proc read_ls_others {fd} { + global buf_rlo + + append buf_rlo [read $fd] + set pck [split $buf_rlo "\0"] + set buf_rlo [lindex $pck end] + foreach p [lrange $pck 0 end-1] { + display_file $p _O + } + status_eof $fd buf_rlo +} + +proc status_eof {fd buf} { + global status_active $buf + global ui_fname_value ui_status_value + + if {[eof $fd]} { + set $buf {} + close $fd + if {[incr status_active -1] == 0} { + set ui_status_value {Ready.} + if {$ui_fname_value != {}} { + show_diff $ui_fname_value + } + } + } +} + +###################################################################### +## +## diff + +set diff_active 0 + +proc clear_diff {} { + global ui_diff ui_fname_value ui_fstatus_value + + $ui_diff conf -state normal + $ui_diff delete 0.0 end + $ui_diff conf -state disabled + set ui_fname_value {} + set ui_fstatus_value {} +} + +proc show_diff {path} { + global file_states HEAD status_active diff_3way diff_active + global ui_diff ui_fname_value ui_fstatus_value ui_status_value + + if {$status_active > 0} return + if {$diff_active} return + + clear_diff + set s $file_states($path) + set m [lindex $s 0] + set diff_3way 0 + set diff_active 1 + set ui_fname_value $path + set ui_fstatus_value [mapdesc $m $path] + set ui_status_value "Loading diff of $path..." + + set cmd [list | git diff-index -p $HEAD -- $path] + switch $m { + AM { + } + MM { + set cmd [list | git diff-index -p -c $HEAD $path] + } + _O { + if {[catch { + set fd [open $path r] + set content [read $fd] + close $fd + } err ]} { + set ui_status_value "Unable to display $path" + error_popup "Error loading file:\n$err" + return + } + $ui_diff conf -state normal + $ui_diff insert end $content + $ui_diff conf -state disabled + return + } + } + + if {[catch {set fd [open $cmd r]} err]} { + set ui_status_value "Unable to display $path" + error_popup "Error loading diff:\n$err" + return + } + + fconfigure $fd -blocking 0 + fileevent $fd readable [list read_diff $fd] +} + +proc read_diff {fd} { + global ui_diff ui_status_value diff_3way diff_active + + while {[gets $fd line] >= 0} { + if {[string match index* $line]} { + if {[string first , $line] >= 0} { + set diff_3way 1 + } + } + + $ui_diff conf -state normal + if {!$diff_3way} { + set x [string index $line 0] + switch -- $x { + "@" {set tags da} + "+" {set tags dp} + "-" {set tags dm} + default {set tags {}} + } + } else { + set x [string range $line 0 1] + switch -- $x { + default {set tags {}} + "@@" {set tags da} + "++" {set tags dp; set x " +"} + " +" {set tags {di bold}; set x "++"} + "+ " {set tags dni; set x "-+"} + "--" {set tags dm; set x " -"} + " -" {set tags {dm bold}; set x "--"} + "- " {set tags di; set x "+-"} + default {set tags {}} + } + set line [string replace $line 0 1 $x] + } + $ui_diff insert end $line $tags + $ui_diff insert end "\n" + $ui_diff conf -state disabled + } + + if {[eof $fd]} { + close $fd + set diff_active 0 + set ui_status_value {Ready.} + } +} + +###################################################################### +## +## ui helpers + +proc mapcol {state path} { + global all_cols + + if {[catch {set r $all_cols($state)}]} { + puts "error: no column for state={$state} $path" + return o + } + return $r +} + +proc mapicon {state path} { + global all_icons + + if {[catch {set r $all_icons($state)}]} { + puts "error: no icon for state={$state} $path" + return file_plain + } + return $r +} + +proc mapdesc {state path} { + global all_descs + + if {[catch {set r $all_descs($state)}]} { + puts "error: no desc for state={$state} $path" + return $state + } + return $r +} + +proc bsearch {w path} { + set hi [expr [lindex [split [$w index end] .] 0] - 2] + if {$hi == 0} { + return -1 + } + set lo 0 + while {$lo < $hi} { + set mi [expr [expr $lo + $hi] / 2] + set ti [expr $mi + 1] + set cmp [string compare [$w get $ti.1 $ti.end] $path] + if {$cmp < 0} { + set lo $ti + } elseif {$cmp == 0} { + return $mi + } else { + set hi $mi + } + } + return -[expr $lo + 1] +} + +proc merge_state {path state} { + global file_states + + if {[array names file_states -exact $path] == {}} { + set o __ + set s [list $o none none] + } else { + set s $file_states($path) + set o [lindex $s 0] + } + + set m [lindex $s 0] + if {[string index $state 0] == "_"} { + set state [string index $m 0][string index $state 1] + } elseif {[string index $state 0] == "*"} { + set state _[string index $state 1] + } + + if {[string index $state 1] == "_"} { + set state [string index $state 0][string index $m 1] + } elseif {[string index $state 1] == "*"} { + set state [string index $state 0]_ + } + + set file_states($path) [lreplace $s 0 0 $state] + return $o +} + +proc display_file {path state} { + global ui_index ui_other file_states + + set old_m [merge_state $path $state] + set s $file_states($path) + set m [lindex $s 0] + + if {[mapcol $m $path] == "o"} { + set ii 1 + set ai 2 + set iw $ui_index + set aw $ui_other + } else { + set ii 2 + set ai 1 + set iw $ui_other + set aw $ui_index + } + + set d [lindex $s $ii] + if {$d != "none"} { + set lno [bsearch $iw $path] + if {$lno >= 0} { + incr lno + $iw conf -state normal + $iw delete $lno.0 [expr $lno + 1].0 + $iw conf -state disabled + set s [lreplace $s $ii $ii none] + } + } + + set d [lindex $s $ai] + if {$d == "none"} { + set lno [expr abs([bsearch $aw $path] + 1) + 1] + $aw conf -state normal + set ico [$aw image create $lno.0 \ + -align center -padx 5 -pady 1 \ + -image [mapicon $m $path]] + $aw insert $lno.1 "$path\n" + $aw conf -state disabled + set file_states($path) [lreplace $s $ai $ai [list $ico]] + } elseif {[mapicon $m $path] != [mapicon $old_m $path]} { + set ico [lindex $d 0] + $aw image conf $ico -image [mapicon $m $path] + } +} + +proc toggle_mode {path} { + global file_states + + set s $file_states($path) + set m [lindex $s 0] + + switch -- $m { + AM - + _O { + set new A* + set cmd [list exec git update-index --add $path] + } + MM { + set new M* + set cmd [list exec git update-index $path] + } + _D { + set new D* + set cmd [list exec git update-index --remove $path] + } + default { + return + } + } + + if {[catch {eval $cmd} err]} { + error_popup "Error processing file:\n$err" + return + } + display_file $path $new +} + +###################################################################### +## +## icons + +set filemask { +#define mask_width 14 +#define mask_height 15 +static unsigned char mask_bits[] = { + 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, + 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, + 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f}; +} + +image create bitmap file_plain -background white -foreground black -data { +#define plain_width 14 +#define plain_height 15 +static unsigned char plain_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10, + 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, + 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_mod -background white -foreground blue -data { +#define mod_width 14 +#define mod_height 15 +static unsigned char mod_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_tick -background white -foreground "#007000" -data { +#define file_tick_width 14 +#define file_tick_height 15 +static unsigned char file_tick_bits[] = { + 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16, + 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10, + 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_parttick -background white -foreground "#005050" -data { +#define parttick_width 14 +#define parttick_height 15 +static unsigned char parttick_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, + 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10, + 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_question -background white -foreground black -data { +#define file_question_width 14 +#define file_question_height 15 +static unsigned char file_question_bits[] = { + 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13, + 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10, + 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_removed -background white -foreground red -data { +#define file_removed_width 14 +#define file_removed_height 15 +static unsigned char file_removed_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10, + 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13, + 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_merge -background white -foreground blue -data { +#define file_merge_width 14 +#define file_merge_height 15 +static unsigned char file_merge_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10, + 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +foreach i { + {__ i "Unmodified" plain} + {_M i "Modified" mod} + {M_ i "Checked in" tick} + {MM i "Partially checked in" parttick} + + {_O o "Untracked" plain} + {A_ o "Added" tick} + {AM o "Partially added" parttick} + + {_D i "Missing" question} + {D_ i "Removed" removed} + {DD i "Removed" removed} + {DO i "Partially removed" removed} + + {UM i "Merge conflicts" merge} + {U_ i "Merge conflicts" merge} + } { + set all_cols([lindex $i 0]) [lindex $i 1] + set all_descs([lindex $i 0]) [lindex $i 2] + set all_icons([lindex $i 0]) file_[lindex $i 3] +} +unset filemask i + +###################################################################### +## +## util + +proc error_popup {msg} { + set w .error + toplevel $w + wm transient $w . + show_msg $w $w $msg +} + +proc show_msg {w top msg} { + message $w.m -text $msg -justify center -aspect 400 + pack $w.m -side top -fill x -padx 20 -pady 20 + button $w.ok -text OK -command "destroy $top" + pack $w.ok -side bottom -fill x + bind $top "grab $top; focus $top" + bind $top "destroy $top" + tkwait window $top +} + +###################################################################### +## +## ui commands + +proc do_gitk {} { + global tcl_platform + + if {$tcl_platform(platform) == "windows"} { + exec sh -c gitk & + } else { + exec gitk & + } +} + +proc do_quit {} { + destroy . +} + +proc do_rescan {} { + update_status +} + +# shift == 1: left click +# 3: right click +proc click {w x y shift wx wy} { + set pos [split [$w index @$x,$y] .] + set lno [lindex $pos 0] + set col [lindex $pos 1] + set path [$w get $lno.1 $lno.end] + if {$path == {}} return + + if {$col > 0 && $shift == 1} { + show_diff $path + } +} + +proc unclick {w x y} { + set pos [split [$w index @$x,$y] .] + set lno [lindex $pos 0] + set col [lindex $pos 1] + set path [$w get $lno.1 $lno.end] + if {$path == {}} return + + if {$col == 0} { + toggle_mode $path + } +} + +###################################################################### +## +## ui init + +set mainfont {Helvetica 10} +set difffont {Courier 10} +set maincursor [. cget -cursor] + +# -- Menu Bar +menu .mbar -tearoff 0 +.mbar add cascade -label Project -menu .mbar.project +.mbar add cascade -label Commit -menu .mbar.commit +.mbar add cascade -label Fetch -menu .mbar.fetch +.mbar add cascade -label Pull -menu .mbar.pull +. configure -menu .mbar + +# -- Project Menu +menu .mbar.project +.mbar.project add command -label Visulize \ + -command do_gitk \ + -font $mainfont +.mbar.project add command -label Quit \ + -command do_quit \ + -font $mainfont + +# -- Commit Menu +menu .mbar.commit +.mbar.commit add command -label Rescan \ + -command do_rescan \ + -font $mainfont + +# -- Fetch Menu +menu .mbar.fetch + +# -- Pull Menu +menu .mbar.pull + +# -- Main Window Layout +panedwindow .vpane -orient vertical +panedwindow .vpane.files -orient horizontal +.vpane add .vpane.files -sticky nsew +pack .vpane -anchor n -side top -fill both -expand 1 + +# -- Index File List +set ui_index .vpane.files.index.list +frame .vpane.files.index -height 100 -width 400 +label .vpane.files.index.title -text {Modified Files} \ + -background green \ + -font $mainfont +text $ui_index -background white -borderwidth 0 \ + -width 40 -height 10 \ + -font $mainfont \ + -yscrollcommand {.vpane.files.index.sb set} \ + -cursor $maincursor \ + -state disabled +scrollbar .vpane.files.index.sb -command [list $ui_index yview] +pack .vpane.files.index.title -side top -fill x +pack .vpane.files.index.sb -side right -fill y +pack $ui_index -side left -fill both -expand 1 +.vpane.files add .vpane.files.index -sticky nsew + +# -- Other (Add) File List +set ui_other .vpane.files.other.list +frame .vpane.files.other -height 100 -width 100 +label .vpane.files.other.title -text {Untracked Files} \ + -background red \ + -font $mainfont +text $ui_other -background white -borderwidth 0 \ + -width 40 -height 10 \ + -font $mainfont \ + -yscrollcommand {.vpane.files.other.sb set} \ + -cursor $maincursor \ + -state disabled +scrollbar .vpane.files.other.sb -command [list $ui_other yview] +pack .vpane.files.other.title -side top -fill x +pack .vpane.files.other.sb -side right -fill y +pack $ui_other -side left -fill both -expand 1 +.vpane.files add .vpane.files.other -sticky nsew + +# -- Diff Header +set ui_fname_value {} +set ui_fstatus_value {} +frame .vpane.diff -height 100 -width 100 +frame .vpane.diff.header +label .vpane.diff.header.l1 -text {File:} -font $mainfont +label .vpane.diff.header.l2 -textvariable ui_fname_value \ + -anchor w \ + -justify left \ + -font $mainfont +label .vpane.diff.header.l3 -text {Status:} -font $mainfont +label .vpane.diff.header.l4 -textvariable ui_fstatus_value \ + -width 20 \ + -anchor w \ + -justify left \ + -font $mainfont +pack .vpane.diff.header.l1 -side left +pack .vpane.diff.header.l2 -side left -fill x +pack .vpane.diff.header.l4 -side right +pack .vpane.diff.header.l3 -side right + +# -- Diff Body +frame .vpane.diff.body +set ui_diff .vpane.diff.body.t +text $ui_diff -background white -borderwidth 0 \ + -width 40 -height 20 \ + -font $difffont \ + -xscrollcommand {.vpane.diff.body.sbx set} \ + -yscrollcommand {.vpane.diff.body.sby set} \ + -cursor $maincursor \ + -state disabled +scrollbar .vpane.diff.body.sbx -orient horizontal \ + -command [list $ui_diff xview] +scrollbar .vpane.diff.body.sby -orient vertical \ + -command [list $ui_diff yview] +pack .vpane.diff.body.sbx -side bottom -fill x +pack .vpane.diff.body.sby -side right -fill y +pack $ui_diff -side left -fill both -expand 1 +pack .vpane.diff.header -side top -fill x +pack .vpane.diff.body -side bottom -fill both -expand 1 +.vpane add .vpane.diff -stick nsew + +$ui_diff tag conf dm -foreground red +$ui_diff tag conf dp -foreground blue +$ui_diff tag conf da -font [concat $difffont bold] +$ui_diff tag conf di -foreground "#00a000" +$ui_diff tag conf dni -foreground "#a000a0" +$ui_diff tag conf bold -font [concat $difffont bold] + +# -- Commit Area +frame .vpane.commarea -height 50 +.vpane add .vpane.commarea -stick nsew + +# -- Commit Area Buttons +frame .vpane.commarea.buttons +label .vpane.commarea.buttons.l -text {} \ + -anchor w \ + -justify left \ + -font $mainfont +pack .vpane.commarea.buttons.l -side top -fill x +button .vpane.commarea.buttons.rescan -text {Rescan} \ + -command do_rescan \ + -font $mainfont +pack .vpane.commarea.buttons.rescan -side top -fill x +button .vpane.commarea.buttons.ciall -text {Check-in All} \ + -command do_checkin_all \ + -font $mainfont +pack .vpane.commarea.buttons.ciall -side top -fill x +button .vpane.commarea.buttons.commit -text {Commit} \ + -command do_commit \ + -font $mainfont +pack .vpane.commarea.buttons.commit -side top -fill x +pack .vpane.commarea.buttons -side left -fill y + +# -- Commit Message Buffer +frame .vpane.commarea.buffer +set ui_comm .vpane.commarea.buffer.t +label .vpane.commarea.buffer.l -text {Commit Message:} \ + -anchor w \ + -justify left \ + -font $mainfont +text $ui_comm -background white -borderwidth 1 \ + -relief sunken \ + -width 75 -height 10 -wrap none \ + -font $difffont \ + -yscrollcommand {.vpane.commarea.buffer.sby set} \ + -cursor $maincursor +scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview] +pack .vpane.commarea.buffer.l -side top -fill x +pack .vpane.commarea.buffer.sby -side right -fill y +pack $ui_comm -side left -fill y +pack .vpane.commarea.buffer -side left -fill y + +# -- Status Bar +set ui_status_value {Initializing...} +label .status -textvariable ui_status_value \ + -anchor w \ + -justify left \ + -borderwidth 1 \ + -relief sunken \ + -font $mainfont +pack .status -anchor w -side bottom -fill x + +# -- Key Bindings +bind . do_quit +bind . do_rescan +bind . do_rescan +bind . do_rescan +bind . do_quit +bind . do_quit +foreach i [list $ui_index $ui_other] { + bind $i {click %W %x %y 1 %X %Y; break} + bind $i {click %W %x %y 3 %X %Y; break} + bind $i {unclick %W %x %y; break} +} +unset i + +###################################################################### +## +## main + +if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { + show_msg {} . "Cannot find the git directory: $err" + exit 1 +} + +wm title . "git-ui ([file normalize [file dirname $gitdir]])" +focus -force $ui_comm +update_status -- cgit v1.2.3 From 131f503b7262b001eae434182feb08cb5e6fa4be Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 16:07:32 -0500 Subject: git-gui: Additional early feature development. * Run refresh before diff-index. * Load saved commit message during rescan. * Save current commit message (if any) during quit. * Add Signed-off-by line to commit buffer. * Batch update-index invocations through --stdin. * Better highlight which file is in the diff viewer. * Key bindings for signoff, check-in all and commit. * Improved formatting of status table within source. Signed-off-by: Shawn O. Pearce --- git-gui | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 214 insertions(+), 56 deletions(-) diff --git a/git-gui b/git-gui index dfa3000260..d74509a20b 100755 --- a/git-gui +++ b/git-gui @@ -13,16 +13,30 @@ exec wish "$0" -- "$@" ## status set status_active 0 +set diff_active 0 +set checkin_active 0 +set update_index_fd {} + +proc is_busy {} { + global status_active diff_active checkin_active update_index_fd + + if {$status_active > 0 + || $diff_active + || $checkin_active + || $update_index_fd != {}} { + return 1 + } + return 0 +} proc update_status {} { global gitdir HEAD commit_type - global ui_index ui_other ui_status_value + global ui_index ui_other ui_status_value ui_comm global status_active file_states - if {$status_active > 0} return + if {[is_busy]} return array unset file_states - set ui_status_value {Refreshing file status...} foreach w [list $ui_index $ui_other] { $w conf -state normal $w delete 0.0 end @@ -35,6 +49,31 @@ proc update_status {} { set commit_type normal } + if {![$ui_comm edit modified] + || [string trim [$ui_comm get 0.0 end]] == {}} { + if {[load_message GITGUI_MSG]} { + } elseif {[load_message MERGE_MSG]} { + } elseif {[load_message SQUASH_MSG]} { + } + $ui_comm edit modified false + } + + set status_active 1 + set ui_status_value {Refreshing file status...} + set fd_rf [open "| git update-index -q --unmerged --refresh" r] + fconfigure $fd_rf -blocking 0 -translation binary + fileevent $fd_rf readable [list read_refresh $fd_rf] +} + +proc read_refresh {fd} { + global gitdir HEAD commit_type + global ui_index ui_other ui_status_value ui_comm + global status_active file_states + + read $fd + if {![eof $fd]} return + close $fd + set ls_others [list | git ls-files --others -z \ --exclude-per-directory=.gitignore] set info_exclude [file join $gitdir info exclude] @@ -42,10 +81,11 @@ proc update_status {} { lappend ls_others "--exclude-from=$info_exclude" } + set status_active 3 + set ui_status_value {Scanning for modified files ...} set fd_di [open "| git diff-index --cached -z $HEAD" r] set fd_df [open "| git diff-files -z" r] set fd_lo [open $ls_others r] - set status_active 3 fconfigure $fd_di -blocking 0 -translation binary fconfigure $fd_df -blocking 0 -translation binary @@ -55,6 +95,23 @@ proc update_status {} { fileevent $fd_lo readable [list read_ls_others $fd_lo] } +proc load_message {file} { + global gitdir ui_comm + + set f [file join $gitdir $file] + if {[file exists $f]} { + if {[catch {set fd [open $f r]}]} { + return 0 + } + set content [read $fd] + close $fd + $ui_comm delete 0.0 end + $ui_comm insert end $content + return 1 + } + return 0 +} + proc read_diff_index {fd} { global buf_rdi @@ -115,8 +172,6 @@ proc status_eof {fd buf} { ## ## diff -set diff_active 0 - proc clear_diff {} { global ui_diff ui_fname_value ui_fstatus_value @@ -128,11 +183,10 @@ proc clear_diff {} { } proc show_diff {path} { - global file_states HEAD status_active diff_3way diff_active + global file_states HEAD diff_3way diff_active global ui_diff ui_fname_value ui_fstatus_value ui_status_value - if {$status_active > 0} return - if {$diff_active} return + if {[is_busy]} return clear_diff set s $file_states($path) @@ -156,6 +210,7 @@ proc show_diff {path} { set content [read $fd] close $fd } err ]} { + set diff_active 0 set ui_status_value "Unable to display $path" error_popup "Error loading file:\n$err" return @@ -168,12 +223,13 @@ proc show_diff {path} { } if {[catch {set fd [open $cmd r]} err]} { + set diff_active 0 set ui_status_value "Unable to display $path" error_popup "Error loading diff:\n$err" return } - fconfigure $fd -blocking 0 + fconfigure $fd -blocking 0 -translation binary fileevent $fd readable [list read_diff $fd] } @@ -353,6 +409,32 @@ proc display_file {path state} { } } +proc with_update_index {body} { + global update_index_fd + + if {$update_index_fd == {}} { + set update_index_fd [open \ + "| git update-index --add --remove -z --stdin" \ + w] + fconfigure $update_index_fd -translation binary + uplevel 1 $body + close $update_index_fd + set update_index_fd {} + } else { + uplevel 1 $body + } +} + +proc update_index {path} { + global update_index_fd + + if {$update_index_fd == {}} { + error {not in with_update_index} + } else { + puts -nonewline $update_index_fd "$path\0" + } +} + proc toggle_mode {path} { global file_states @@ -361,27 +443,14 @@ proc toggle_mode {path} { switch -- $m { AM - - _O { - set new A* - set cmd [list exec git update-index --add $path] - } - MM { - set new M* - set cmd [list exec git update-index $path] - } - _D { - set new D* - set cmd [list exec git update-index --remove $path] - } - default { - return - } + _O {set new A*} + _M - + MM {set new M*} + _D {set new D*} + default {return} } - if {[catch {eval $cmd} err]} { - error_popup "Error processing file:\n$err" - return - } + with_update_index {update_index $path} display_file $path $new } @@ -416,10 +485,10 @@ static unsigned char mod_bits[] = { 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; } -maskdata $filemask -image create bitmap file_tick -background white -foreground "#007000" -data { -#define file_tick_width 14 -#define file_tick_height 15 -static unsigned char file_tick_bits[] = { +image create bitmap file_fulltick -background white -foreground "#007000" -data { +#define file_fulltick_width 14 +#define file_fulltick_height 15 +static unsigned char file_fulltick_bits[] = { 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16, 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f}; @@ -461,27 +530,31 @@ static unsigned char file_merge_bits[] = { 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; } -maskdata $filemask +set max_status_desc 0 foreach i { - {__ i "Unmodified" plain} - {_M i "Modified" mod} - {M_ i "Checked in" tick} - {MM i "Partially checked in" parttick} - - {_O o "Untracked" plain} - {A_ o "Added" tick} - {AM o "Partially added" parttick} - - {_D i "Missing" question} - {D_ i "Removed" removed} - {DD i "Removed" removed} - {DO i "Partially removed" removed} - - {UM i "Merge conflicts" merge} - {U_ i "Merge conflicts" merge} + {__ i plain "Unmodified"} + {_M i mod "Modified"} + {M_ i fulltick "Checked in"} + {MM i parttick "Partially checked in"} + + {_O o plain "Untracked"} + {A_ o fulltick "Added"} + {AM o parttick "Partially added"} + + {_D i question "Missing"} + {D_ i removed "Removed"} + {DD i removed "Removed"} + {DO i removed "Removed (still exists)"} + + {UM i merge "Merge conflicts"} + {U_ i merge "Merge conflicts"} } { + if {$max_status_desc < [string length [lindex $i 3]]} { + set max_status_desc [string length [lindex $i 3]] + } set all_cols([lindex $i 0]) [lindex $i 1] - set all_descs([lindex $i 0]) [lindex $i 2] - set all_icons([lindex $i 0]) file_[lindex $i 3] + set all_icons([lindex $i 0]) file_[lindex $i 2] + set all_descs([lindex $i 0]) [lindex $i 3] } unset filemask i @@ -521,6 +594,20 @@ proc do_gitk {} { } proc do_quit {} { + global gitdir ui_comm + + set save [file join $gitdir GITGUI_MSG] + if {[$ui_comm edit modified] + && [string trim [$ui_comm get 0.0 end]] != {}} { + catch { + set fd [open $save w] + puts $fd [string trim [$ui_comm get 0.0 end]] + close $fd + } + } elseif {[file exists $save]} { + file delete $save + } + destroy . } @@ -528,9 +615,52 @@ proc do_rescan {} { update_status } +proc do_checkin_all {} { + global checkin_active ui_status_value + + if {[is_busy]} return + + set checkin_active 1 + set ui_status_value {Checking in all files...} + after 1 { + with_update_index { + foreach path [array names file_states] { + set s $file_states($path) + set m [lindex $s 0] + switch -- $m { + AM - + MM - + _M - + _D {toggle_mode $path} + } + } + } + set checkin_active 0 + set ui_status_value {Ready.} + } +} + +proc do_signoff {} { + global ui_comm + + catch { + set me [exec git var GIT_COMMITTER_IDENT] + if {[regexp {(.*) [0-9]+ [-+0-9]+$} $me me name]} { + set str "Signed-off-by: $name" + if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} { + $ui_comm insert end "\n" + $ui_comm insert end $str + $ui_comm see end + } + } + } +} + # shift == 1: left click # 3: right click proc click {w x y shift wx wy} { + global ui_index ui_other + set pos [split [$w index @$x,$y] .] set lno [lindex $pos 0] set col [lindex $pos 1] @@ -538,6 +668,9 @@ proc click {w x y shift wx wy} { if {$path == {}} return if {$col > 0 && $shift == 1} { + $ui_index tag remove in_diff 0.0 end + $ui_other tag remove in_diff 0.0 end + $w tag add in_diff $lno.0 [expr $lno + 1].0 show_diff $path } } @@ -549,7 +682,7 @@ proc unclick {w x y} { set path [$w get $lno.1 $lno.end] if {$path == {}} return - if {$col == 0} { + if {$col == 0 && ![is_busy]} { toggle_mode $path } } @@ -584,6 +717,15 @@ menu .mbar.commit .mbar.commit add command -label Rescan \ -command do_rescan \ -font $mainfont +.mbar.commit add command -label {Check-in All Files} \ + -command do_checkin_all \ + -font $mainfont +.mbar.commit add command -label {Sign Off} \ + -command do_signoff \ + -font $mainfont +.mbar.commit add command -label Commit \ + -command do_commit \ + -font $mainfont # -- Fetch Menu menu .mbar.fetch @@ -633,10 +775,13 @@ pack .vpane.files.other.sb -side right -fill y pack $ui_other -side left -fill both -expand 1 .vpane.files add .vpane.files.other -sticky nsew +$ui_index tag conf in_diff -font [concat $mainfont bold] +$ui_other tag conf in_diff -font [concat $mainfont bold] + # -- Diff Header set ui_fname_value {} set ui_fstatus_value {} -frame .vpane.diff -height 100 -width 100 +frame .vpane.diff -height 50 -width 400 frame .vpane.diff.header label .vpane.diff.header.l1 -text {File:} -font $mainfont label .vpane.diff.header.l2 -textvariable ui_fname_value \ @@ -645,7 +790,7 @@ label .vpane.diff.header.l2 -textvariable ui_fname_value \ -font $mainfont label .vpane.diff.header.l3 -text {Status:} -font $mainfont label .vpane.diff.header.l4 -textvariable ui_fstatus_value \ - -width 20 \ + -width $max_status_desc \ -anchor w \ -justify left \ -font $mainfont @@ -658,7 +803,7 @@ pack .vpane.diff.header.l3 -side right frame .vpane.diff.body set ui_diff .vpane.diff.body.t text $ui_diff -background white -borderwidth 0 \ - -width 40 -height 20 \ + -width 80 -height 15 \ -font $difffont \ -xscrollcommand {.vpane.diff.body.sbx set} \ -yscrollcommand {.vpane.diff.body.sby set} \ @@ -693,19 +838,27 @@ label .vpane.commarea.buttons.l -text {} \ -justify left \ -font $mainfont pack .vpane.commarea.buttons.l -side top -fill x +pack .vpane.commarea.buttons -side left -fill y + button .vpane.commarea.buttons.rescan -text {Rescan} \ -command do_rescan \ -font $mainfont pack .vpane.commarea.buttons.rescan -side top -fill x + button .vpane.commarea.buttons.ciall -text {Check-in All} \ -command do_checkin_all \ -font $mainfont pack .vpane.commarea.buttons.ciall -side top -fill x + +button .vpane.commarea.buttons.signoff -text {Sign Off} \ + -command do_signoff \ + -font $mainfont +pack .vpane.commarea.buttons.signoff -side top -fill x + button .vpane.commarea.buttons.commit -text {Commit} \ -command do_commit \ -font $mainfont pack .vpane.commarea.buttons.commit -side top -fill x -pack .vpane.commarea.buttons -side left -fill y # -- Commit Message Buffer frame .vpane.commarea.buffer @@ -741,6 +894,11 @@ bind . do_quit bind . do_rescan bind . do_rescan bind . do_rescan +bind . do_signoff +bind . do_signoff +bind . do_checkin_all +bind . do_checkin_all +bind . do_commit bind . do_quit bind . do_quit foreach i [list $ui_index $ui_other] { -- cgit v1.2.3 From 6f6eed286f6a056fab1f887871ba5287f609e1f1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 18:22:19 -0500 Subject: git-gui: Fixed UI layout problems on Windows. Signed-off-by: Shawn O. Pearce --- git-gui | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/git-gui b/git-gui index d74509a20b..3ee32e105c 100755 --- a/git-gui +++ b/git-gui @@ -229,7 +229,7 @@ proc show_diff {path} { return } - fconfigure $fd -blocking 0 -translation binary + fconfigure $fd -blocking 0 -translation auto fileevent $fd readable [list read_diff $fd] } @@ -237,6 +237,10 @@ proc read_diff {fd} { global ui_diff ui_status_value diff_3way diff_active while {[gets $fd line] >= 0} { + if {[string match {diff --git *} $line]} continue + if {[string match {diff --combined *} $line]} continue + if {[string match {--- *} $line]} continue + if {[string match {+++ *} $line]} continue if {[string match index* $line]} { if {[string first , $line] >= 0} { set diff_3way 1 @@ -584,8 +588,9 @@ proc show_msg {w top msg} { ## ui commands proc do_gitk {} { - global tcl_platform + global tcl_platform ui_status_value + set ui_status_value "Please wait... Starting gitk..." if {$tcl_platform(platform) == "windows"} { exec sh -c gitk & } else { @@ -705,7 +710,7 @@ menu .mbar -tearoff 0 # -- Project Menu menu .mbar.project -.mbar.project add command -label Visulize \ +.mbar.project add command -label Visualize \ -command do_gitk \ -font $mainfont .mbar.project add command -label Quit \ @@ -736,7 +741,7 @@ menu .mbar.pull # -- Main Window Layout panedwindow .vpane -orient vertical panedwindow .vpane.files -orient horizontal -.vpane add .vpane.files -sticky nsew +.vpane add .vpane.files -sticky nsew -height 100 -width 400 pack .vpane -anchor n -side top -fill both -expand 1 # -- Index File List @@ -781,7 +786,7 @@ $ui_other tag conf in_diff -font [concat $mainfont bold] # -- Diff Header set ui_fname_value {} set ui_fstatus_value {} -frame .vpane.diff -height 50 -width 400 +frame .vpane.diff -height 200 -width 400 frame .vpane.diff.header label .vpane.diff.header.l1 -text {File:} -font $mainfont label .vpane.diff.header.l2 -textvariable ui_fname_value \ @@ -803,7 +808,7 @@ pack .vpane.diff.header.l3 -side right frame .vpane.diff.body set ui_diff .vpane.diff.body.t text $ui_diff -background white -borderwidth 0 \ - -width 80 -height 15 \ + -width 80 -height 15 -wrap none \ -font $difffont \ -xscrollcommand {.vpane.diff.body.sbx set} \ -yscrollcommand {.vpane.diff.body.sby set} \ @@ -828,7 +833,7 @@ $ui_diff tag conf dni -foreground "#a000a0" $ui_diff tag conf bold -font [concat $difffont bold] # -- Commit Area -frame .vpane.commarea -height 50 +frame .vpane.commarea -height 150 .vpane add .vpane.commarea -stick nsew # -- Commit Area Buttons -- cgit v1.2.3 From e210e67451f22f97c1476d6b78b6fa7fdd5817f9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 19:12:58 -0500 Subject: git-gui: Corrected keyboard bindings on Windows, improved state management. When we are refreshing from the index or updating the index we shouldn't let the user cause other index based operations to occur as these would likely conflict with the currently running operations possibly causing some index changes to be lost. Signed-off-by: Shawn O. Pearce --- git-gui | 99 +++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 24 deletions(-) diff --git a/git-gui b/git-gui index 3ee32e105c..168062e67b 100755 --- a/git-gui +++ b/git-gui @@ -7,34 +7,53 @@ exec wish "$0" -- "$@" # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. - ###################################################################### ## -## status +## task management set status_active 0 set diff_active 0 set checkin_active 0 set update_index_fd {} -proc is_busy {} { - global status_active diff_active checkin_active update_index_fd +set disable_on_lock [list] +set index_lock_type none + +proc lock_index {type} { + global index_lock_type disable_on_lock - if {$status_active > 0 - || $diff_active - || $checkin_active - || $update_index_fd != {}} { + if {$index_lock_type == {none}} { + set index_lock_type $type + foreach w $disable_on_lock { + uplevel #0 $w disabled + } + return 1 + } elseif {$index_lock_type == {begin-update} && $type == {update}} { + set index_lock_type $type return 1 } return 0 } +proc unlock_index {} { + global index_lock_type disable_on_lock + + set index_lock_type none + foreach w $disable_on_lock { + uplevel #0 $w normal + } +} + +###################################################################### +## +## status + proc update_status {} { global gitdir HEAD commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states - if {[is_busy]} return + if {$status_active || ![lock_index read]} return array unset file_states foreach w [list $ui_index $ui_other] { @@ -160,6 +179,7 @@ proc status_eof {fd buf} { set $buf {} close $fd if {[incr status_active -1] == 0} { + unlock_index set ui_status_value {Ready.} if {$ui_fname_value != {}} { show_diff $ui_fname_value @@ -186,7 +206,7 @@ proc show_diff {path} { global file_states HEAD diff_3way diff_active global ui_diff ui_fname_value ui_fstatus_value ui_status_value - if {[is_busy]} return + if {$diff_active || ![lock_index read]} return clear_diff set s $file_states($path) @@ -211,6 +231,7 @@ proc show_diff {path} { close $fd } err ]} { set diff_active 0 + unlock_index set ui_status_value "Unable to display $path" error_popup "Error loading file:\n$err" return @@ -224,6 +245,7 @@ proc show_diff {path} { if {[catch {set fd [open $cmd r]} err]} { set diff_active 0 + unlock_index set ui_status_value "Unable to display $path" error_popup "Error loading diff:\n$err" return @@ -279,6 +301,7 @@ proc read_diff {fd} { if {[eof $fd]} { close $fd set diff_active 0 + unlock_index set ui_status_value {Ready.} } } @@ -417,6 +440,7 @@ proc with_update_index {body} { global update_index_fd if {$update_index_fd == {}} { + if {![lock_index update]} return set update_index_fd [open \ "| git update-index --add --remove -z --stdin" \ w] @@ -424,6 +448,7 @@ proc with_update_index {body} { uplevel 1 $body close $update_index_fd set update_index_fd {} + unlock_index } else { uplevel 1 $body } @@ -587,10 +612,17 @@ proc show_msg {w top msg} { ## ## ui commands +set starting_gitk_msg {Please wait... Starting gitk...} proc do_gitk {} { - global tcl_platform ui_status_value + global tcl_platform ui_status_value starting_gitk_msg + + set ui_status_value $starting_gitk_msg + after 5000 { + if {$ui_status_value == $starting_gitk_msg} { + set ui_status_value {Ready.} + } + } - set ui_status_value "Please wait... Starting gitk..." if {$tcl_platform(platform) == "windows"} { exec sh -c gitk & } else { @@ -623,7 +655,7 @@ proc do_rescan {} { proc do_checkin_all {} { global checkin_active ui_status_value - if {[is_busy]} return + if {$checkin_active || ![lock_index begin-update]} return set checkin_active 1 set ui_status_value {Checking in all files...} @@ -687,7 +719,7 @@ proc unclick {w x y} { set path [$w get $lno.1 $lno.end] if {$path == {}} return - if {$col == 0 && ![is_busy]} { + if {$col == 0} { toggle_mode $path } } @@ -700,6 +732,11 @@ set mainfont {Helvetica 10} set difffont {Courier 10} set maincursor [. cget -cursor] +switch -- $tcl_platform(platform) { +windows {set M1B Control; set M1T Ctrl} +default {set M1B M1; set M1T M1} +} + # -- Menu Bar menu .mbar -tearoff 0 .mbar add cascade -label Project -menu .mbar.project @@ -715,22 +752,33 @@ menu .mbar.project -font $mainfont .mbar.project add command -label Quit \ -command do_quit \ + -accelerator $M1T-Q \ -font $mainfont # -- Commit Menu menu .mbar.commit .mbar.commit add command -label Rescan \ -command do_rescan \ + -accelerator F5 \ -font $mainfont +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Check-in All Files} \ -command do_checkin_all \ + -accelerator $M1T-U \ -font $mainfont +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Sign Off} \ -command do_signoff \ + -accelerator $M1T-S \ -font $mainfont .mbar.commit add command -label Commit \ -command do_commit \ + -accelerator $M1T-Return \ -font $mainfont +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] # -- Fetch Menu menu .mbar.fetch @@ -849,11 +897,13 @@ button .vpane.commarea.buttons.rescan -text {Rescan} \ -command do_rescan \ -font $mainfont pack .vpane.commarea.buttons.rescan -side top -fill x +lappend disable_on_lock {.vpane.commarea.buttons.rescan conf -state} button .vpane.commarea.buttons.ciall -text {Check-in All} \ -command do_checkin_all \ -font $mainfont pack .vpane.commarea.buttons.ciall -side top -fill x +lappend disable_on_lock {.vpane.commarea.buttons.ciall conf -state} button .vpane.commarea.buttons.signoff -text {Sign Off} \ -command do_signoff \ @@ -864,6 +914,7 @@ button .vpane.commarea.buttons.commit -text {Commit} \ -command do_commit \ -font $mainfont pack .vpane.commarea.buttons.commit -side top -fill x +lappend disable_on_lock {.vpane.commarea.buttons.commit conf -state} # -- Commit Message Buffer frame .vpane.commarea.buffer @@ -897,21 +948,21 @@ pack .status -anchor w -side bottom -fill x # -- Key Bindings bind . do_quit bind . do_rescan -bind . do_rescan -bind . do_rescan -bind . do_signoff -bind . do_signoff -bind . do_checkin_all -bind . do_checkin_all -bind . do_commit -bind . do_quit -bind . do_quit +bind . <$M1B-Key-r> do_rescan +bind . <$M1B-Key-R> do_rescan +bind . <$M1B-Key-s> do_signoff +bind . <$M1B-Key-S> do_signoff +bind . <$M1B-Key-u> do_checkin_all +bind . <$M1B-Key-U> do_checkin_all +bind . <$M1B-Key-Return> do_commit +bind . <$M1B-Key-q> do_quit +bind . <$M1B-Key-Q> do_quit foreach i [list $ui_index $ui_other] { bind $i {click %W %x %y 1 %X %Y; break} bind $i {click %W %x %y 3 %X %Y; break} bind $i {unclick %W %x %y; break} } -unset i +unset i M1B M1T ###################################################################### ## -- cgit v1.2.3 From 6e27d826c807153a4773f197e5056d66a6a809c0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 20:03:36 -0500 Subject: git-gui: Verify we should actually perform a commit when asked to do so. A user shouldn't perform a commit if any of the following are true: * The repository state has changed since the last rescan. * There are no files updated in the index to commit. * There are unmerged stages still in the index. * The commit message has not been provided. * The pre-commit hook is executable and declined. Signed-off-by: Shawn O. Pearce --- git-gui | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index 168062e67b..0b941c3ffb 100755 --- a/git-gui +++ b/git-gui @@ -599,15 +599,62 @@ proc error_popup {msg} { } proc show_msg {w top msg} { - message $w.m -text $msg -justify center -aspect 400 + global gitdir + + message $w.m -text $msg -justify left -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 button $w.ok -text OK -command "destroy $top" - pack $w.ok -side bottom -fill x + pack $w.ok -side bottom bind $top "grab $top; focus $top" bind $top "destroy $top" + wm title $top "error: git-ui ([file normalize [file dirname $gitdir]])" tkwait window $top } +proc hook_failed_popup {hook msg} { + global gitdir mainfont difffont + + set w .hookfail + toplevel $w + wm transient $w . + + frame $w.m + label $w.m.l1 -text "$hook hook failed:" \ + -anchor w \ + -justify left \ + -font [concat $mainfont bold] + text $w.m.t \ + -background white -borderwidth 1 \ + -relief sunken \ + -width 80 -height 10 \ + -font $difffont \ + -yscrollcommand [list $w.m.sby set] + label $w.m.l2 \ + -text {You must correct the above errors before committing.} \ + -anchor w \ + -justify left \ + -font [concat $mainfont bold] + 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 + 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 + + $w.m.t insert 1.0 $msg + $w.m.t conf -state disabled + + button $w.ok -text OK \ + -width 15 \ + -command "destroy $w" + pack $w.ok -side bottom + + bind $w "grab $w; focus $w" + bind $w "destroy $w" + wm title $w "error: git-ui ([file normalize [file dirname $gitdir]])" + tkwait window $w +} + ###################################################################### ## ## ui commands @@ -693,6 +740,94 @@ proc do_signoff {} { } } +proc do_commit {} { + global tcl_platform HEAD gitdir commit_type file_states + global ui_comm + + # -- Our in memory state should match the repository. + # + if {[catch {set curHEAD [exec git rev-parse --verify HEAD]}]} { + set cur_type initial + } else { + set cur_type normal + } + if {$commit_type != $commit_type || $HEAD != $curHEAD} { + error_popup {Last scanned state does not match repository state. + +Its highly likely that another Git program modified the +repository since our last scan. A rescan is required +before committing. +} + update_status + return + } + + # -- At least one file should differ in the index. + # + set files_ready 0 + foreach path [array names file_states] { + set s $file_states($path) + switch -glob -- [lindex $s 0] { + _* {continue} + A* - + D* - + M* {set files_ready 1; break} + U* { + error_popup "Unmerged files cannot be committed. + +File $path has merge conflicts. +You must resolve them and check the file in before committing. +" + return + } + default { + error_popup "Unknown file state [lindex $s 0] detected. + +File $path cannot be committed by this program. +" + } + } + } + if {!$files_ready} { + error_popup {No checked-in files to commit. + +You must check-in at least 1 file before you can commit. +} + return + } + + # -- A message is required. + # + set msg [string trim [$ui_comm get 1.0 end]] + if {$msg == {}} { + error_popup {Please supply a commit message. + +A good commit message has the following format: + +- First line: Describe in one sentance what you did. +- Second line: Blank +- Remaining lines: Describe why this change is good. +} + return + } + + # -- Ask the pre-commit hook for the go-ahead. + # + set pchook [file join $gitdir hooks pre-commit] + if {$tcl_platform(platform) == {windows} && [file exists $pchook]} { + set pchook [list sh -c \ + "if test -x \"$pchook\"; then exec \"$pchook\"; fi"] + } elseif {[file executable $pchook]} { + set pchook [list $pchook] + } else { + set pchook {} + } + if {$pchook != {} && [catch {eval exec $pchook} err]} { + hook_failed_popup pre-commit $err + return + } +} + # shift == 1: left click # 3: right click proc click {w x y shift wx wy} { -- cgit v1.2.3 From ec6b424abb8d0a5c6399bcffdfde19aa47f16d18 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 20:50:59 -0500 Subject: git-gui: Finished commit implementation. We can now commit any type of commit (initial, normal or merge) using the same techniques as git-commit.sh does for these types of things. If invoked as git-citool we run exit immediately after the commit was finished. If invoked as git-gui then we stay running. Also fixed a bug which caused the commit message buffer to be lost when the application shutdown and restarted. Signed-off-by: Shawn O. Pearce --- git-citool | 1 + git-gui | 339 ++++++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 238 insertions(+), 102 deletions(-) create mode 120000 git-citool diff --git a/git-citool b/git-citool new file mode 120000 index 0000000000..b5f620fd09 --- /dev/null +++ b/git-citool @@ -0,0 +1 @@ +git-gui \ No newline at end of file diff --git a/git-gui b/git-gui index 0b941c3ffb..0e9e519636 100755 --- a/git-gui +++ b/git-gui @@ -11,9 +11,11 @@ exec wish "$0" -- "$@" ## ## task management +set single_commit 0 set status_active 0 set diff_active 0 set checkin_active 0 +set commit_active 0 set update_index_fd {} set disable_on_lock [list] @@ -48,13 +50,27 @@ proc unlock_index {} { ## ## status +proc repository_state {hdvar ctvar} { + global gitdir + upvar $hdvar hd $ctvar ct + + if {[catch {set hd [exec git rev-parse --verify HEAD]}]} { + set ct initial + } elseif {[file exists [file join $gitdir MERGE_HEAD]]} { + set ct merge + } else { + set ct normal + } +} + proc update_status {} { - global gitdir HEAD commit_type + global HEAD commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states if {$status_active || ![lock_index read]} return + repository_state HEAD commit_type array unset file_states foreach w [list $ui_index $ui_other] { $w conf -state normal @@ -62,12 +78,6 @@ proc update_status {} { $w conf -state disabled } - if {[catch {set HEAD [exec git rev-parse --verify HEAD]}]} { - set commit_type initial - } else { - set commit_type normal - } - if {![$ui_comm edit modified] || [string trim [$ui_comm get 0.0 end]] == {}} { if {[load_message GITGUI_MSG]} { @@ -306,6 +316,207 @@ proc read_diff {fd} { } } +###################################################################### +## +## commit + +proc commit_tree {} { + global tcl_platform HEAD gitdir commit_type file_states + global commit_active ui_status_value + global ui_comm + + if {$commit_active || ![lock_index update]} return + + # -- Our in memory state should match the repository. + # + repository_state curHEAD cur_type + if {$commit_type != $cur_type || $HEAD != $curHEAD} { + error_popup {Last scanned state does not match repository state. + +Its highly likely that another Git program modified the +repository since our last scan. A rescan is required +before committing. +} + unlock_index + update_status + return + } + + # -- At least one file should differ in the index. + # + set files_ready 0 + foreach path [array names file_states] { + set s $file_states($path) + switch -glob -- [lindex $s 0] { + _* {continue} + A* - + D* - + M* {set files_ready 1; break} + U* { + error_popup "Unmerged files cannot be committed. + +File $path has merge conflicts. +You must resolve them and check the file in before committing. +" + unlock_index + return + } + default { + error_popup "Unknown file state [lindex $s 0] detected. + +File $path cannot be committed by this program. +" + } + } + } + if {!$files_ready} { + error_popup {No checked-in files to commit. + +You must check-in at least 1 file before you can commit. +} + unlock_index + return + } + + # -- A message is required. + # + set msg [string trim [$ui_comm get 1.0 end]] + if {$msg == {}} { + error_popup {Please supply a commit message. + +A good commit message has the following format: + +- First line: Describe in one sentance what you did. +- Second line: Blank +- Remaining lines: Describe why this change is good. +} + unlock_index + return + } + + # -- Ask the pre-commit hook for the go-ahead. + # + set pchook [file join $gitdir hooks pre-commit] + if {$tcl_platform(platform) == {windows} && [file exists $pchook]} { + set pchook [list sh -c \ + "if test -x \"$pchook\"; then exec \"$pchook\"; fi"] + } elseif {[file executable $pchook]} { + set pchook [list $pchook] + } else { + set pchook {} + } + if {$pchook != {} && [catch {eval exec $pchook} err]} { + hook_failed_popup pre-commit $err + unlock_index + return + } + + # -- Write the tree in the background. + # + set commit_active 1 + set ui_status_value {Committing changes...} + + set fd_wt [open "| git write-tree" r] + fileevent $fd_wt readable \ + [list commit_stage2 $fd_wt $curHEAD $msg] +} + +proc commit_stage2 {fd_wt curHEAD msg} { + global single_commit gitdir HEAD commit_type + global commit_active ui_status_value comm_ui + + gets $fd_wt tree_id + close $fd_wt + + if {$tree_id == {}} { + error_popup "write-tree failed" + set commit_active 0 + set ui_status_value {Commit failed.} + unlock_index + return + } + + # -- Create the commit. + # + set cmd [list git commit-tree $tree_id] + if {$commit_type != {initial}} { + lappend cmd -p $HEAD + } + if {$commit_type == {merge}} { + if {[catch { + set fd_mh [open [file join $gitdir MERGE_HEAD] r] + while {[gets $fd_mh merge_head] > 0} { + lappend -p $merge_head + } + close $fd_mh + } err]} { + error_popup "Loading MERGE_HEADs failed:\n$err" + set commit_active 0 + set ui_status_value {Commit failed.} + unlock_index + return + } + } + if {$commit_type == {initial}} { + # git commit-tree writes to stderr during initial commit. + lappend cmd 2>/dev/null + } + lappend cmd << $msg + if {[catch {set cmt_id [eval exec $cmd]} err]} { + error_popup "commit-tree failed:\n$err" + set commit_active 0 + set ui_status_value {Commit failed.} + unlock_index + return + } + + # -- Update the HEAD ref. + # + set reflogm commit + if {$commit_type != {normal}} { + append reflogm " ($commit_type)" + } + set i [string first "\n" $msg] + if {$i >= 0} { + append reflogm {: } [string range $msg 0 [expr $i - 1]] + } else { + append reflogm {: } $msg + } + set cmd [list git update-ref \ + -m $reflogm \ + HEAD $cmt_id $curHEAD] + if {[catch {eval exec $cmd} err]} { + error_popup "update-ref failed:\n$err" + set commit_active 0 + set ui_status_value {Commit failed.} + unlock_index + return + } + + # -- Cleanup after ourselves. + # + catch {file delete [file join $gitdir MERGE_HEAD]} + catch {file delete [file join $gitdir MERGE_MSG]} + catch {file delete [file join $gitdir SQUASH_MSG]} + catch {file delete [file join $gitdir GITGUI_MSG]} + + # -- Let rerere do its thing. + # + if {[file isdirectory [file join $gitdir rr-cache]]} { + catch {exec git rerere} + } + + $comm_ui delete 0.0 end + $comm_ui edit modified false + + if {$single_commit} do_quit + + set commit_active 0 + set ui_status_value "Changes committed as $cmt_id." + unlock_index + update_status +} + ###################################################################### ## ## ui helpers @@ -599,20 +810,22 @@ proc error_popup {msg} { } proc show_msg {w top msg} { - global gitdir + global gitdir appname message $w.m -text $msg -justify left -aspect 400 - pack $w.m -side top -fill x -padx 20 -pady 20 - button $w.ok -text OK -command "destroy $top" + pack $w.m -side top -fill x -padx 5 -pady 10 + button $w.ok -text OK \ + -width 15 \ + -command "destroy $top" pack $w.ok -side bottom bind $top "grab $top; focus $top" bind $top "destroy $top" - wm title $top "error: git-ui ([file normalize [file dirname $gitdir]])" + wm title $top "error: $appname ([file normalize [file dirname $gitdir]])" tkwait window $top } proc hook_failed_popup {hook msg} { - global gitdir mainfont difffont + global gitdir mainfont difffont appname set w .hookfail toplevel $w @@ -651,7 +864,7 @@ proc hook_failed_popup {hook msg} { bind $w "grab $w; focus $w" bind $w "destroy $w" - wm title $w "error: git-ui ([file normalize [file dirname $gitdir]])" + wm title $w "error: $appname ([file normalize [file dirname $gitdir]])" tkwait window $w } @@ -681,14 +894,14 @@ proc do_quit {} { global gitdir ui_comm set save [file join $gitdir GITGUI_MSG] - if {[$ui_comm edit modified] - && [string trim [$ui_comm get 0.0 end]] != {}} { + set msg [string trim [$ui_comm get 0.0 end]] + if {[$ui_comm edit modified] && $msg != {}} { catch { set fd [open $save w] puts $fd [string trim [$ui_comm get 0.0 end]] close $fd } - } elseif {[file exists $save]} { + } elseif {$msg == {} && [file exists $save]} { file delete $save } @@ -741,91 +954,7 @@ proc do_signoff {} { } proc do_commit {} { - global tcl_platform HEAD gitdir commit_type file_states - global ui_comm - - # -- Our in memory state should match the repository. - # - if {[catch {set curHEAD [exec git rev-parse --verify HEAD]}]} { - set cur_type initial - } else { - set cur_type normal - } - if {$commit_type != $commit_type || $HEAD != $curHEAD} { - error_popup {Last scanned state does not match repository state. - -Its highly likely that another Git program modified the -repository since our last scan. A rescan is required -before committing. -} - update_status - return - } - - # -- At least one file should differ in the index. - # - set files_ready 0 - foreach path [array names file_states] { - set s $file_states($path) - switch -glob -- [lindex $s 0] { - _* {continue} - A* - - D* - - M* {set files_ready 1; break} - U* { - error_popup "Unmerged files cannot be committed. - -File $path has merge conflicts. -You must resolve them and check the file in before committing. -" - return - } - default { - error_popup "Unknown file state [lindex $s 0] detected. - -File $path cannot be committed by this program. -" - } - } - } - if {!$files_ready} { - error_popup {No checked-in files to commit. - -You must check-in at least 1 file before you can commit. -} - return - } - - # -- A message is required. - # - set msg [string trim [$ui_comm get 1.0 end]] - if {$msg == {}} { - error_popup {Please supply a commit message. - -A good commit message has the following format: - -- First line: Describe in one sentance what you did. -- Second line: Blank -- Remaining lines: Describe why this change is good. -} - return - } - - # -- Ask the pre-commit hook for the go-ahead. - # - set pchook [file join $gitdir hooks pre-commit] - if {$tcl_platform(platform) == {windows} && [file exists $pchook]} { - set pchook [list sh -c \ - "if test -x \"$pchook\"; then exec \"$pchook\"; fi"] - } elseif {[file executable $pchook]} { - set pchook [list $pchook] - } else { - set pchook {} - } - if {$pchook != {} && [catch {eval exec $pchook} err]} { - hook_failed_popup pre-commit $err - return - } + commit_tree } # shift == 1: left click @@ -1081,6 +1210,7 @@ label .status -textvariable ui_status_value \ pack .status -anchor w -side bottom -fill x # -- Key Bindings +bind $ui_comm <$M1B-Key-Return> {do_commit;break} bind . do_quit bind . do_rescan bind . <$M1B-Key-r> do_rescan @@ -1108,6 +1238,11 @@ if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { exit 1 } -wm title . "git-ui ([file normalize [file dirname $gitdir]])" +set appname [lindex [file split $argv0] end] +if {$appname == {git-citool}} { + set single_commit 1 +} + +wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm update_status -- cgit v1.2.3 From e57ca85e113437649ec908ce48fc43a659d650f8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 21:34:10 -0500 Subject: git-gui: Implemented amended commits. Also fixed a bug related that caused a crash if the file currently in the diff viewer is no longer modified after the commit. Signed-off-by: Shawn O. Pearce --- git-gui | 160 +++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 119 insertions(+), 41 deletions(-) diff --git a/git-gui b/git-gui index 0e9e519636..bb89da4332 100755 --- a/git-gui +++ b/git-gui @@ -21,6 +21,10 @@ set update_index_fd {} set disable_on_lock [list] set index_lock_type none +set HEAD {} +set PARENT {} +set commit_type {} + proc lock_index {type} { global index_lock_type disable_on_lock @@ -63,14 +67,23 @@ proc repository_state {hdvar ctvar} { } } -proc update_status {} { - global HEAD commit_type +proc update_status {{final Ready.}} { + global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states if {$status_active || ![lock_index read]} return - repository_state HEAD commit_type + repository_state new_HEAD new_type + if {$commit_type == {amend} + && $new_type == {normal} + && $new_HEAD == $HEAD} { + } else { + set HEAD $new_HEAD + set PARENT $new_HEAD + set commit_type $new_type + } + array unset file_states foreach w [list $ui_index $ui_other] { $w conf -state normal @@ -91,11 +104,11 @@ proc update_status {} { set ui_status_value {Refreshing file status...} set fd_rf [open "| git update-index -q --unmerged --refresh" r] fconfigure $fd_rf -blocking 0 -translation binary - fileevent $fd_rf readable [list read_refresh $fd_rf] + fileevent $fd_rf readable [list read_refresh $fd_rf $final] } -proc read_refresh {fd} { - global gitdir HEAD commit_type +proc read_refresh {fd final} { + global gitdir PARENT commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states @@ -112,27 +125,27 @@ proc read_refresh {fd} { set status_active 3 set ui_status_value {Scanning for modified files ...} - set fd_di [open "| git diff-index --cached -z $HEAD" r] + set fd_di [open "| git diff-index --cached -z $PARENT" r] set fd_df [open "| git diff-files -z" r] set fd_lo [open $ls_others r] fconfigure $fd_di -blocking 0 -translation binary fconfigure $fd_df -blocking 0 -translation binary fconfigure $fd_lo -blocking 0 -translation binary - fileevent $fd_di readable [list read_diff_index $fd_di] - fileevent $fd_df readable [list read_diff_files $fd_df] - fileevent $fd_lo readable [list read_ls_others $fd_lo] + fileevent $fd_di readable [list read_diff_index $fd_di $final] + fileevent $fd_df readable [list read_diff_files $fd_df $final] + fileevent $fd_lo readable [list read_ls_others $fd_lo $final] } proc load_message {file} { global gitdir ui_comm set f [file join $gitdir $file] - if {[file exists $f]} { + if {[file isfile $f]} { if {[catch {set fd [open $f r]}]} { return 0 } - set content [read $fd] + set content [string trim [read $fd]] close $fd $ui_comm delete 0.0 end $ui_comm insert end $content @@ -141,7 +154,7 @@ proc load_message {file} { return 0 } -proc read_diff_index {fd} { +proc read_diff_index {fd final} { global buf_rdi append buf_rdi [read $fd] @@ -152,10 +165,10 @@ proc read_diff_index {fd} { display_file $p [string index $m end]_ } } - status_eof $fd buf_rdi + status_eof $fd buf_rdi $final } -proc read_diff_files {fd} { +proc read_diff_files {fd final} { global buf_rdf append buf_rdf [read $fd] @@ -166,10 +179,10 @@ proc read_diff_files {fd} { display_file $p _[string index $m end] } } - status_eof $fd buf_rdf + status_eof $fd buf_rdf $final } -proc read_ls_others {fd} { +proc read_ls_others {fd final} { global buf_rlo append buf_rlo [read $fd] @@ -178,21 +191,25 @@ proc read_ls_others {fd} { foreach p [lrange $pck 0 end-1] { display_file $p _O } - status_eof $fd buf_rlo + status_eof $fd buf_rlo $final } -proc status_eof {fd buf} { +proc status_eof {fd buf final} { global status_active $buf - global ui_fname_value ui_status_value + global ui_fname_value ui_status_value file_states if {[eof $fd]} { set $buf {} close $fd if {[incr status_active -1] == 0} { unlock_index - set ui_status_value {Ready.} - if {$ui_fname_value != {}} { + + set ui_status_value $final + if {$ui_fname_value != {} && [array names file_states \ + -exact $ui_fname_value] != {}} { show_diff $ui_fname_value + } else { + clear_diff } } } @@ -213,7 +230,7 @@ proc clear_diff {} { } proc show_diff {path} { - global file_states HEAD diff_3way diff_active + global file_states PARENT diff_3way diff_active global ui_diff ui_fname_value ui_fstatus_value ui_status_value if {$diff_active || ![lock_index read]} return @@ -227,12 +244,12 @@ proc show_diff {path} { set ui_fstatus_value [mapdesc $m $path] set ui_status_value "Loading diff of $path..." - set cmd [list | git diff-index -p $HEAD -- $path] + set cmd [list | git diff-index -p $PARENT -- $path] switch $m { AM { } MM { - set cmd [list | git diff-index -p -c $HEAD $path] + set cmd [list | git diff-index -p -c $PARENT $path] } _O { if {[catch { @@ -320,6 +337,51 @@ proc read_diff {fd} { ## ## commit +proc load_last_commit {} { + global HEAD PARENT commit_type ui_comm + + if {$commit_type == {amend}} return + if {$commit_type != {normal}} { + error_popup "Can't amend a $commit_type commit." + return + } + + set msg {} + set parent {} + set parent_count 0 + if {[catch { + set fd [open "| git cat-file commit $HEAD" r] + while {[gets $fd line] > 0} { + if {[string match {parent *} $line]} { + set parent [string range $line 7 end] + incr parent_count + } + } + set msg [string trim [read $fd]] + close $fd + } err]} { + error_popup "Error loading commit data for amend:\n$err" + return + } + + if {$parent_count == 0} { + set commit_type amend + set HEAD {} + set PARENT {} + update_status + } elseif {$parent_count == 1} { + set commit_type amend + set PARENT $parent + $ui_comm delete 0.0 end + $ui_comm insert end $msg + $ui_comm edit modified false + update_status + } else { + error_popup {You can't amend a merge commit.} + return + } +} + proc commit_tree {} { global tcl_platform HEAD gitdir commit_type file_states global commit_active ui_status_value @@ -330,7 +392,10 @@ proc commit_tree {} { # -- Our in memory state should match the repository. # repository_state curHEAD cur_type - if {$commit_type != $cur_type || $HEAD != $curHEAD} { + if {$commit_type == {amend} + && $cur_type == {normal} + && $curHEAD == $HEAD} { + } elseif {$commit_type != $cur_type || $HEAD != $curHEAD} { error_popup {Last scanned state does not match repository state. Its highly likely that another Git program modified the @@ -397,7 +462,7 @@ A good commit message has the following format: # -- Ask the pre-commit hook for the go-ahead. # set pchook [file join $gitdir hooks pre-commit] - if {$tcl_platform(platform) == {windows} && [file exists $pchook]} { + if {$tcl_platform(platform) == {windows} && [file isfile $pchook]} { set pchook [list sh -c \ "if test -x \"$pchook\"; then exec \"$pchook\"; fi"] } elseif {[file executable $pchook]} { @@ -422,8 +487,8 @@ A good commit message has the following format: } proc commit_stage2 {fd_wt curHEAD msg} { - global single_commit gitdir HEAD commit_type - global commit_active ui_status_value comm_ui + global single_commit gitdir PARENT commit_type + global commit_active ui_status_value ui_comm gets $fd_wt tree_id close $fd_wt @@ -439,8 +504,8 @@ proc commit_stage2 {fd_wt curHEAD msg} { # -- Create the commit. # set cmd [list git commit-tree $tree_id] - if {$commit_type != {initial}} { - lappend cmd -p $HEAD + if {$PARENT != {}} { + lappend cmd -p $PARENT } if {$commit_type == {merge}} { if {[catch { @@ -457,7 +522,7 @@ proc commit_stage2 {fd_wt curHEAD msg} { return } } - if {$commit_type == {initial}} { + if {$PARENT == {}} { # git commit-tree writes to stderr during initial commit. lappend cmd 2>/dev/null } @@ -482,9 +547,7 @@ proc commit_stage2 {fd_wt curHEAD msg} { } else { append reflogm {: } $msg } - set cmd [list git update-ref \ - -m $reflogm \ - HEAD $cmt_id $curHEAD] + set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD] if {[catch {eval exec $cmd} err]} { error_popup "update-ref failed:\n$err" set commit_active 0 @@ -506,15 +569,15 @@ proc commit_stage2 {fd_wt curHEAD msg} { catch {exec git rerere} } - $comm_ui delete 0.0 end - $comm_ui edit modified false + $ui_comm delete 0.0 end + $ui_comm edit modified false if {$single_commit} do_quit + set commit_type {} set commit_active 0 - set ui_status_value "Changes committed as $cmt_id." unlock_index - update_status + update_status "Changes committed as $cmt_id." } ###################################################################### @@ -877,7 +940,7 @@ proc do_gitk {} { global tcl_platform ui_status_value starting_gitk_msg set ui_status_value $starting_gitk_msg - after 5000 { + after 10000 { if {$ui_status_value == $starting_gitk_msg} { set ui_status_value {Ready.} } @@ -953,6 +1016,10 @@ proc do_signoff {} { } } +proc do_amend_last {} { + load_last_commit +} + proc do_commit {} { commit_tree } @@ -1027,6 +1094,11 @@ menu .mbar.commit -font $mainfont lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] +.mbar.commit add command -label {Amend Last Commit} \ + -command do_amend_last \ + -font $mainfont +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Check-in All Files} \ -command do_checkin_all \ -accelerator $M1T-U \ @@ -1145,7 +1217,7 @@ $ui_diff tag conf dni -foreground "#a000a0" $ui_diff tag conf bold -font [concat $difffont bold] # -- Commit Area -frame .vpane.commarea -height 150 +frame .vpane.commarea -height 170 .vpane add .vpane.commarea -stick nsew # -- Commit Area Buttons @@ -1163,6 +1235,12 @@ button .vpane.commarea.buttons.rescan -text {Rescan} \ pack .vpane.commarea.buttons.rescan -side top -fill x lappend disable_on_lock {.vpane.commarea.buttons.rescan conf -state} +button .vpane.commarea.buttons.amend -text {Amend Last} \ + -command do_amend_last \ + -font $mainfont +pack .vpane.commarea.buttons.amend -side top -fill x +lappend disable_on_lock {.vpane.commarea.buttons.amend conf -state} + button .vpane.commarea.buttons.ciall -text {Check-in All} \ -command do_checkin_all \ -font $mainfont -- cgit v1.2.3 From bd1e2b4028e183f7b4d564aad69c3eae8bdb661a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 22:03:05 -0500 Subject: git-gui: Misc. nit type of bug fixes. * Make sure we are in the top level working directory. This way we can access files using their repository path. * Reload the diff viewer if the current file's status has changed; as the diff may now be different. * Correctly handle the 'AD' file state: added but now gone from the working directory. Signed-off-by: Shawn O. Pearce --- git-gui | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/git-gui b/git-gui index bb89da4332..8ad3f2d033 100755 --- a/git-gui +++ b/git-gui @@ -266,6 +266,9 @@ proc show_diff {path} { $ui_diff conf -state normal $ui_diff insert end $content $ui_diff conf -state disabled + set diff_active 0 + unlock_index + set ui_status_value {Ready.} return } } @@ -482,8 +485,7 @@ A good commit message has the following format: set ui_status_value {Committing changes...} set fd_wt [open "| git write-tree" r] - fileevent $fd_wt readable \ - [list commit_stage2 $fd_wt $curHEAD $msg] + fileevent $fd_wt readable [list commit_stage2 $fd_wt $curHEAD $msg] } proc commit_stage2 {fd_wt curHEAD msg} { @@ -510,8 +512,8 @@ proc commit_stage2 {fd_wt curHEAD msg} { if {$commit_type == {merge}} { if {[catch { set fd_mh [open [file join $gitdir MERGE_HEAD] r] - while {[gets $fd_mh merge_head] > 0} { - lappend -p $merge_head + while {[gets $fd_mh merge_head] >= 0} { + lappend cmd -p $merge_head } close $fd_mh } err]} { @@ -576,6 +578,8 @@ proc commit_stage2 {fd_wt curHEAD msg} { set commit_type {} set commit_active 0 + set HEAD $cmt_id + set PARENT $cmt_id unlock_index update_status "Changes committed as $cmt_id." } @@ -739,7 +743,7 @@ proc update_index {path} { } proc toggle_mode {path} { - global file_states + global file_states ui_fname_value set s $file_states($path) set m [lindex $s 0] @@ -749,12 +753,16 @@ proc toggle_mode {path} { _O {set new A*} _M - MM {set new M*} + AD - _D {set new D*} default {return} } with_update_index {update_index $path} display_file $path $new + if {$ui_fname_value == $path} { + show_diff $path + } } ###################################################################### @@ -843,6 +851,7 @@ foreach i { {_O o plain "Untracked"} {A_ o fulltick "Added"} {AM o parttick "Partially added"} + {AD o question "Added (but now gone)"} {_D i question "Missing"} {D_ i removed "Removed"} @@ -1261,10 +1270,18 @@ lappend disable_on_lock {.vpane.commarea.buttons.commit conf -state} # -- Commit Message Buffer frame .vpane.commarea.buffer set ui_comm .vpane.commarea.buffer.t -label .vpane.commarea.buffer.l -text {Commit Message:} \ +set ui_coml .vpane.commarea.buffer.l +label $ui_coml -text {Commit Message:} \ -anchor w \ -justify left \ -font $mainfont +trace add variable commit_type write {uplevel #0 { + switch -glob $commit_type \ + initial {$ui_coml conf -text {Initial Commit Message:}} \ + amend {$ui_coml conf -text {Amended Commit Message:}} \ + merge {$ui_coml conf -text {Merge Commit Message:}} \ + * {$ui_coml conf -text {Commit Message:}} +}} text $ui_comm -background white -borderwidth 1 \ -relief sunken \ -width 75 -height 10 -wrap none \ @@ -1272,7 +1289,7 @@ text $ui_comm -background white -borderwidth 1 \ -yscrollcommand {.vpane.commarea.buffer.sby set} \ -cursor $maincursor scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview] -pack .vpane.commarea.buffer.l -side top -fill x +pack $ui_coml -side top -fill x pack .vpane.commarea.buffer.sby -side right -fill y pack $ui_comm -side left -fill y pack .vpane.commarea.buffer -side left -fill y @@ -1315,6 +1332,11 @@ if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { show_msg {} . "Cannot find the git directory: $err" exit 1 } +set cdup [exec git rev-parse --show-cdup] +if {$cdup != ""} { + cd $cdup +} +unset cdup set appname [lindex [file split $argv0] end] if {$appname == {git-citool}} { -- cgit v1.2.3 From 8c0ce436826f01e17cba90ebcaf6483ad3b8c984 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 23:13:23 -0500 Subject: git-gui: Started construction of fetch and push operations. Signed-off-by: Shawn O. Pearce --- git-gui | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/git-gui b/git-gui index 8ad3f2d033..a43fb2f662 100755 --- a/git-gui +++ b/git-gui @@ -584,6 +584,34 @@ proc commit_stage2 {fd_wt curHEAD msg} { update_status "Changes committed as $cmt_id." } +###################################################################### +## +## fetch pull push + +proc fetch_from {remote} { + set w [new_console "fetch $remote" \ + "Fetching new changes from $remote"] + set cmd [list | git fetch] + lappend -v + lappend cmd $remote + lappend cmd |& cat + set fd_f [open $cmd r] + fconfigure $fd_f -blocking 0 -translation auto + fileevent $fd_f readable [list console_read $w $fd_f] +} + +proc push_to {remote} { + set w [new_console "push $remote" \ + "Pushing changes to $remote"] + set cmd [list | git push] + lappend -v + lappend cmd $remote + lappend cmd |& cat + set fd_f [open $cmd r] + fconfigure $fd_f -blocking 0 -translation auto + fileevent $fd_f readable [list console_read $w $fd_f] +} + ###################################################################### ## ## ui helpers @@ -765,6 +793,41 @@ proc toggle_mode {path} { } } +###################################################################### +## +## config (fetch push pull) + +proc load_all_remotes {} { + global gitdir all_remotes + + set all_remotes [list] + set rm_dir [file join $gitdir remotes] + if {[file isdirectory $rm_dir]} { + set all_remotes [concat $all_remotes \ + [glob -types f -tails -directory $rm_dir * *]] + } + + set fd_rc [open "| git repo-config --list" r] + while {[gets $fd_rc line] >= 0} { + if {[regexp ^remote\.(.*)\.url= $line line name]} { + lappend all_remotes $name + } + } + close $fd_rc + + set all_remotes [lsort -unique $all_remotes] +} + +proc populate_remote_menu {m pfx op} { + global gitdir all_remotes mainfont + + foreach remote $all_remotes { + $m add command -label "$pfx $remote..." \ + -command [list $op $remote] \ + -font $mainfont + } +} + ###################################################################### ## ## icons @@ -888,6 +951,7 @@ proc show_msg {w top msg} { pack $w.m -side top -fill x -padx 5 -pady 10 button $w.ok -text OK \ -width 15 \ + -font $mainfont \ -command "destroy $top" pack $w.ok -side bottom bind $top "grab $top; focus $top" @@ -931,6 +995,7 @@ proc hook_failed_popup {hook msg} { button $w.ok -text OK \ -width 15 \ + -font $mainfont \ -command "destroy $w" pack $w.ok -side bottom @@ -940,6 +1005,58 @@ proc hook_failed_popup {hook msg} { tkwait window $w } +set next_console_id 0 + +proc new_console {short_title long_title} { + global next_console_id gitdir appname mainfont difffont + + set w .console[incr next_console_id] + toplevel $w + frame $w.m + label $w.m.l1 -text "$long_title:" \ + -anchor w \ + -justify left \ + -font [concat $mainfont bold] + text $w.m.t \ + -background white -borderwidth 1 \ + -relief sunken \ + -width 80 -height 10 \ + -font $difffont \ + -state disabled \ + -yscrollcommand [list $w.m.sby set] + scrollbar $w.m.sby -command [list $w.m.t yview] + pack $w.m.l1 -side top -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 + + button $w.ok -text {OK} \ + -width 15 \ + -font $mainfont \ + -state disabled \ + -command "destroy $w" + pack $w.ok -side bottom + + bind $w "focus $w" + bind $w break + wm title $w "$appname ([file dirname [file normalize [file dirname $gitdir]]]): $short_title" + return $w +} + +proc console_read {w fd} { + $w.m.t conf -state normal + while {[gets $fd line] >= 0} { + $w.m.t insert end $line + $w.m.t insert end "\n" + } + $w.m.t conf -state disabled + + if {[eof $fd]} { + close $fd + $w.ok conf -state normal + } +} + ###################################################################### ## ## ui commands @@ -1083,6 +1200,7 @@ menu .mbar -tearoff 0 .mbar add cascade -label Commit -menu .mbar.commit .mbar add cascade -label Fetch -menu .mbar.fetch .mbar add cascade -label Pull -menu .mbar.pull +.mbar add cascade -label Push -menu .mbar.push . configure -menu .mbar # -- Project Menu @@ -1131,6 +1249,9 @@ menu .mbar.fetch # -- Pull Menu menu .mbar.pull +# -- Push Menu +menu .mbar.push + # -- Main Window Layout panedwindow .vpane -orient vertical panedwindow .vpane.files -orient horizontal @@ -1345,4 +1466,7 @@ if {$appname == {git-citool}} { wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm +load_all_remotes +populate_remote_menu .mbar.fetch From fetch_from +populate_remote_menu .mbar.push To push_to update_status -- cgit v1.2.3 From cc4b1c0213ad2d99121135125b8c2ea630bebe3d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 6 Nov 2006 23:47:05 -0500 Subject: git-gui: Worked around environment variable problems on Windows. Apparently the Cygwin tclsh/wish executables don't pass the environment that they inherited onto any children that they invoke. This causes a problem for some users during 'git fetch' or 'git push' as critical environment variables like GIT_SSH and SSH_AUTH_SOCK aren't available to the git processes. So we work around this by forcing sh to start a login shell, thus reloading the user's environment, then cd to the current directory, and finally start the requested process. Of course this won't correctly handle any transient environment variables that were inherited but were not supplied by the user's login shell. Signed-off-by: Shawn O. Pearce --- git-gui | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/git-gui b/git-gui index a43fb2f662..be361dec17 100755 --- a/git-gui +++ b/git-gui @@ -92,7 +92,7 @@ proc update_status {{final Ready.}} { } if {![$ui_comm edit modified] - || [string trim [$ui_comm get 0.0 end]] == {}} { + || [string trim [$ui_comm get 0.0 end]] == {}} { if {[load_message GITGUI_MSG]} { } elseif {[load_message MERGE_MSG]} { } elseif {[load_message SQUASH_MSG]} { @@ -591,25 +591,19 @@ proc commit_stage2 {fd_wt curHEAD msg} { proc fetch_from {remote} { set w [new_console "fetch $remote" \ "Fetching new changes from $remote"] - set cmd [list | git fetch] - lappend -v + set cmd [list git fetch] + lappend cmd -v lappend cmd $remote - lappend cmd |& cat - set fd_f [open $cmd r] - fconfigure $fd_f -blocking 0 -translation auto - fileevent $fd_f readable [list console_read $w $fd_f] + console_exec $w $cmd } proc push_to {remote} { set w [new_console "push $remote" \ "Pushing changes to $remote"] - set cmd [list | git push] + set cmd [list git push] lappend -v lappend cmd $remote - lappend cmd |& cat - set fd_f [open $cmd r] - fconfigure $fd_f -blocking 0 -translation auto - fileevent $fd_f readable [list console_read $w $fd_f] + console_exec $w $cmd } ###################################################################### @@ -1043,6 +1037,26 @@ proc new_console {short_title long_title} { return $w } +proc console_exec {w cmd} { + global tcl_platform + + # -- Windows tosses the enviroment when we exec our child. + # But most users need that so we have to relogin. :-( + # + if {$tcl_platform(platform) == {windows}} { + set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"] + } + + # -- Tcl won't let us redirect both stdout and stderr to + # the same pipe. So pass it through cat... + # + set cmd [concat | $cmd |& cat] + + set fd_f [open $cmd r] + fconfigure $fd_f -blocking 0 -translation auto + fileevent $fd_f readable [list console_read $w $fd_f] +} + proc console_read {w fd} { $w.m.t conf -state normal while {[gets $fd line] >= 0} { @@ -1050,6 +1064,7 @@ proc console_read {w fd} { $w.m.t insert end "\n" } $w.m.t conf -state disabled + $w.m.t see end if {[eof $fd]} { close $fd @@ -1062,6 +1077,7 @@ proc console_read {w fd} { ## ui commands set starting_gitk_msg {Please wait... Starting gitk...} + proc do_gitk {} { global tcl_platform ui_status_value starting_gitk_msg @@ -1072,7 +1088,7 @@ proc do_gitk {} { } } - if {$tcl_platform(platform) == "windows"} { + if {$tcl_platform(platform) == {windows}} { exec sh -c gitk & } else { exec gitk & -- cgit v1.2.3 From b8ce6f0ec8a275bf9e32ff7666d00a88154fd50b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 00:20:56 -0500 Subject: git-gui: Reorganized startup procedure to ensure gitdir is right. Because we cd after getting the cdup value from Git we can't try to get the gitdir until after we perform the cd, as usually the gitdir is relative to the current working directory. Signed-off-by: Shawn O. Pearce --- git-gui | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index be361dec17..87dcbaef4c 100755 --- a/git-gui +++ b/git-gui @@ -939,7 +939,7 @@ proc error_popup {msg} { } proc show_msg {w top msg} { - global gitdir appname + global gitdir appname mainfont message $w.m -text $msg -justify left -aspect 400 pack $w.m -side top -fill x -padx 5 -pady 10 @@ -1465,17 +1465,23 @@ unset i M1B M1T ## ## main -if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { +set appname [lindex [file split $argv0] end] +set gitdir {} + +if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} { show_msg {} . "Cannot find the git directory: $err" exit 1 } -set cdup [exec git rev-parse --show-cdup] if {$cdup != ""} { cd $cdup } unset cdup -set appname [lindex [file split $argv0] end] +if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { + show_msg {} . "Cannot find the git directory: $err" + exit 1 +} + if {$appname == {git-citool}} { set single_commit 1 } -- cgit v1.2.3 From 661448922fd55b907449962d35d3fdb92397ce9d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 00:53:39 -0500 Subject: git-gui: Fix menu item accelerator display on Mac OS X. Apparently accelerators really only work correctly for function keys (F1-F12) and "Cmd-q". Apparently wish on Mac OS X reports itself as unix and the OS is Darwin, this makes it a little difficult to be sure we are running under Aqua. Signed-off-by: Shawn O. Pearce --- git-gui | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index 87dcbaef4c..0bbb0064f0 100755 --- a/git-gui +++ b/git-gui @@ -1205,9 +1205,10 @@ set mainfont {Helvetica 10} set difffont {Courier 10} set maincursor [. cget -cursor] -switch -- $tcl_platform(platform) { -windows {set M1B Control; set M1T Ctrl} -default {set M1B M1; set M1T M1} +switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" { +windows,* {set M1B Control; set M1T Ctrl} +unix,Darwin {set M1B M1; set M1T Cmd} +default {set M1B M1; set M1T M1} } # -- Menu Bar -- cgit v1.2.3 From ee3dc9354d23b1262fb9c71c648435e0ccf01d7e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 02:18:18 -0500 Subject: git-gui: Correctly handle CR vs. LF within the console of fetch. Because the remote end is likely to send us progress meters by resetting each line with a CR (and no LF) we should display those meters by replacing the last line of text with the next line, just like a normal xterm would do. This makes the output of fetch look about the same as if we ran it from within an xterm. Signed-off-by: Shawn O. Pearce --- git-gui | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/git-gui b/git-gui index 0bbb0064f0..2645cd5d1a 100755 --- a/git-gui +++ b/git-gui @@ -1002,9 +1002,11 @@ proc hook_failed_popup {hook msg} { set next_console_id 0 proc new_console {short_title long_title} { - global next_console_id gitdir appname mainfont difffont + global next_console_id console_cr + global gitdir appname mainfont difffont set w .console[incr next_console_id] + set console_cr($w) 1.0 toplevel $w frame $w.m label $w.m.l1 -text "$long_title:" \ @@ -1024,7 +1026,7 @@ proc new_console {short_title long_title} { pack $w.m.t -side left -fill both -expand 1 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10 - button $w.ok -text {OK} \ + button $w.ok -text {Running...} \ -width 15 \ -font $mainfont \ -state disabled \ @@ -1053,22 +1055,44 @@ proc console_exec {w cmd} { set cmd [concat | $cmd |& cat] set fd_f [open $cmd r] - fconfigure $fd_f -blocking 0 -translation auto + fconfigure $fd_f -blocking 0 -translation binary fileevent $fd_f readable [list console_read $w $fd_f] } proc console_read {w fd} { + global console_cr + $w.m.t conf -state normal - while {[gets $fd line] >= 0} { - $w.m.t insert end $line - $w.m.t insert end "\n" + set buf [read $fd] + set c 0 + set n [string length $buf] + while {$c < $n} { + set cr [string first "\r" $buf $c] + set lf [string first "\n" $buf $c] + if {$cr < 0} {set cr [expr $n + 1]} + if {$lf < 0} {set lf [expr $n + 1]} + + if {$lf < $cr} { + $w.m.t insert end [string range $buf $c $lf] + set console_cr($w) [$w.m.t index {end -1c}] + set c $lf + incr c + } else { + $w.m.t delete $console_cr($w) end + $w.m.t insert end "\n" + $w.m.t insert end [string range $buf $c $cr] + set c $cr + incr c + } } $w.m.t conf -state disabled $w.m.t see end if {[eof $fd]} { close $fd + $w.ok conf -text Close $w.ok conf -state normal + array unset console_cr $w } } -- cgit v1.2.3 From 07123f4002efdb881f6ce08c5c4411e17e34e141 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 02:57:46 -0500 Subject: git-gui: Check for fetch or push command failure and denote it. Signed-off-by: Shawn O. Pearce --- git-gui | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/git-gui b/git-gui index 2645cd5d1a..0cd85e3c92 100755 --- a/git-gui +++ b/git-gui @@ -1020,8 +1020,12 @@ proc new_console {short_title long_title} { -font $difffont \ -state disabled \ -yscrollcommand [list $w.m.sby set] + label $w.m.s -anchor w \ + -justify left \ + -font [concat $mainfont bold] scrollbar $w.m.sby -command [list $w.m.t yview] pack $w.m.l1 -side top -fill x + pack $w.m.s -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 @@ -1088,12 +1092,19 @@ proc console_read {w fd} { $w.m.t conf -state disabled $w.m.t see end + fconfigure $fd -blocking 1 if {[eof $fd]} { - close $fd + if {[catch {close $fd}]} { + $w.m.s conf -background red -text {Error: Command Failed} + } else { + $w.m.s conf -background green -text {Success} + } $w.ok conf -text Close $w.ok conf -state normal array unset console_cr $w + return } + fconfigure $fd -blocking 0 } ###################################################################### @@ -1468,17 +1479,19 @@ pack .status -anchor w -side bottom -fill x # -- Key Bindings bind $ui_comm <$M1B-Key-Return> {do_commit;break} -bind . do_quit -bind . do_rescan -bind . <$M1B-Key-r> do_rescan -bind . <$M1B-Key-R> do_rescan -bind . <$M1B-Key-s> do_signoff -bind . <$M1B-Key-S> do_signoff -bind . <$M1B-Key-u> do_checkin_all -bind . <$M1B-Key-U> do_checkin_all -bind . <$M1B-Key-Return> do_commit -bind . <$M1B-Key-q> do_quit -bind . <$M1B-Key-Q> do_quit +bind . do_quit +bind all do_rescan +bind all <$M1B-Key-r> do_rescan +bind all <$M1B-Key-R> do_rescan +bind . <$M1B-Key-s> do_signoff +bind . <$M1B-Key-S> do_signoff +bind . <$M1B-Key-u> do_checkin_all +bind . <$M1B-Key-U> do_checkin_all +bind . <$M1B-Key-Return> do_commit +bind all <$M1B-Key-q> do_quit +bind all <$M1B-Key-Q> do_quit +bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} +bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} foreach i [list $ui_index $ui_other] { bind $i {click %W %x %y 1 %X %Y; break} bind $i {click %W %x %y 3 %X %Y; break} -- cgit v1.2.3 From d47ae541cef50f5fbfaec195597cf1cebc2840f5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 03:00:20 -0500 Subject: git-gui: Don't complain if no .git/remotes exist. The user might be using the new style config syntax remote.name.url rather than the older standalone remote file. Signed-off-by: Shawn O. Pearce --- git-gui | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index 0cd85e3c92..b8e7c89586 100755 --- a/git-gui +++ b/git-gui @@ -797,8 +797,11 @@ proc load_all_remotes {} { set all_remotes [list] set rm_dir [file join $gitdir remotes] if {[file isdirectory $rm_dir]} { - set all_remotes [concat $all_remotes \ - [glob -types f -tails -directory $rm_dir * *]] + set all_remotes [concat $all_remotes [glob \ + -types f \ + -tails \ + -nocomplain \ + -directory $rm_dir *]] } set fd_rc [open "| git repo-config --list" r] -- cgit v1.2.3 From 393ec6f01ce38eb731d12557c8f596c36044cf02 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 03:18:34 -0500 Subject: git-gui: Added current TODO list. Signed-off-by: Shawn O. Pearce --- TODO | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000000..b05495847b --- /dev/null +++ b/TODO @@ -0,0 +1,32 @@ +Items outstanding: + + * Checkout $PARENT version to working directory, overwriting current + version. ($PARENT is HEAD, except when amending). + + * Update index with $PARENT version but leave working directory + alone. + + * Populate the pull menu with local branches. + + * Populate the pull menu with default merge branch from each remote. + + * Make use of the new default merge data stored in repo-config. + + * Indicate what the current branch is. + + * Checkout or create a different local branch. + + * Delete a local branch. + + * Store user preferences (like font, window layout) in global + repo-config. + + * Better organize fetch/push/pull console windows. + + * Clone UI (to download a new repository). + + * Remotes editor (for .git/config format only). + + * Show a shortlog of the last couple of commits in the main window, + to give the user warm fuzzy feelings that we have their data + saved. -- cgit v1.2.3 From 3d9d029bde946d7a230ce85460c53686e10bdfe0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 03:30:26 -0500 Subject: git-gui: Last minute idea about fetch shortcuts. Signed-off-by: Shawn O. Pearce --- TODO | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TODO b/TODO index b05495847b..b425da30e2 100644 --- a/TODO +++ b/TODO @@ -21,6 +21,12 @@ Items outstanding: * Store user preferences (like font, window layout) in global repo-config. + * Allow user to define keyboard shortcuts for frequently used fetch + or merge operations. Or maybe just define a keyboard shortcut + for default fetch/default merge of current branch is enough; + but I do know a few users who merge a couple of common branches + also into the same branch so one default isn't quite enough. + * Better organize fetch/push/pull console windows. * Clone UI (to download a new repository). -- cgit v1.2.3 From 37af79d10d980418eaeca953c4ba1c08a85d37e9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 04:19:49 -0500 Subject: git-gui: Automatically reopen any console closed by the user. If the user closes a console and we get more ouptut for it then we will get a Tcl error in the readable event handle for the file channel. Since this loses the actual output and is quite unfriendly to the end user instead reopen any console which the user closed prior to the additional output arriving. Signed-off-by: Shawn O. Pearce --- git-gui | 74 ++++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/git-gui b/git-gui index b8e7c89586..adc89474a7 100755 --- a/git-gui +++ b/git-gui @@ -1005,14 +1005,20 @@ proc hook_failed_popup {hook msg} { set next_console_id 0 proc new_console {short_title long_title} { - global next_console_id console_cr + global next_console_id console_data + set w .console[incr next_console_id] + set console_data($w) [list $short_title $long_title] + return [console_init $w] +} + +proc console_init {w} { + global console_cr console_data global gitdir appname mainfont difffont - set w .console[incr next_console_id] set console_cr($w) 1.0 toplevel $w frame $w.m - label $w.m.l1 -text "$long_title:" \ + label $w.m.l1 -text "[lindex $console_data($w) 1]:" \ -anchor w \ -justify left \ -font [concat $mainfont bold] @@ -1041,8 +1047,7 @@ proc new_console {short_title long_title} { pack $w.ok -side bottom bind $w "focus $w" - bind $w break - wm title $w "$appname ([file dirname [file normalize [file dirname $gitdir]]]): $short_title" + wm title $w "$appname ([file dirname [file normalize [file dirname $gitdir]]]): [lindex $console_data($w) 0]" return $w } @@ -1067,44 +1072,51 @@ proc console_exec {w cmd} { } proc console_read {w fd} { - global console_cr + global console_cr console_data - $w.m.t conf -state normal set buf [read $fd] - set c 0 - set n [string length $buf] - while {$c < $n} { - set cr [string first "\r" $buf $c] - set lf [string first "\n" $buf $c] - if {$cr < 0} {set cr [expr $n + 1]} - if {$lf < 0} {set lf [expr $n + 1]} - - if {$lf < $cr} { - $w.m.t insert end [string range $buf $c $lf] - set console_cr($w) [$w.m.t index {end -1c}] - set c $lf - incr c - } else { - $w.m.t delete $console_cr($w) end - $w.m.t insert end "\n" - $w.m.t insert end [string range $buf $c $cr] - set c $cr - incr c + if {$buf != {}} { + if {![winfo exists $w]} {console_init $w} + $w.m.t conf -state normal + set c 0 + set n [string length $buf] + while {$c < $n} { + set cr [string first "\r" $buf $c] + set lf [string first "\n" $buf $c] + if {$cr < 0} {set cr [expr $n + 1]} + if {$lf < 0} {set lf [expr $n + 1]} + + if {$lf < $cr} { + $w.m.t insert end [string range $buf $c $lf] + set console_cr($w) [$w.m.t index {end -1c}] + set c $lf + incr c + } else { + $w.m.t delete $console_cr($w) end + $w.m.t insert end "\n" + $w.m.t insert end [string range $buf $c $cr] + set c $cr + incr c + } } + $w.m.t conf -state disabled + $w.m.t see end } - $w.m.t conf -state disabled - $w.m.t see end fconfigure $fd -blocking 1 if {[eof $fd]} { if {[catch {close $fd}]} { + if {![winfo exists $w]} {console_init $w} $w.m.s conf -background red -text {Error: Command Failed} - } else { + $w.ok conf -text Close + $w.ok conf -state normal + } elseif {[winfo exists $w]} { $w.m.s conf -background green -text {Success} + $w.ok conf -text Close + $w.ok conf -state normal } - $w.ok conf -text Close - $w.ok conf -state normal array unset console_cr $w + array unset console_data $w return } fconfigure $fd -blocking 0 -- cgit v1.2.3 From 0d4f3eb5f330a53300e630c1ffe4ea1fa61f5dd9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 04:26:02 -0500 Subject: git-gui: Cache all repo-config data in an array. We're likely going to need key/value pairs from the repo-config beyond just remote.*.url, so cache them all up front into a Tcl array where we have fast access to them without needing to refork a repo-config --list process. Signed-off-by: Shawn O. Pearce --- git-gui | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/git-gui b/git-gui index adc89474a7..de67aa2a68 100755 --- a/git-gui +++ b/git-gui @@ -791,8 +791,23 @@ proc toggle_mode {path} { ## ## config (fetch push pull) +proc load_repo_config {} { + global repo_config + + array unset repo_config + catch { + set fd_rc [open "| git repo-config --list" r] + while {[gets $fd_rc line] >= 0} { + if {[regexp {^([^=]+)=(.*)$} $line line name value]} { + lappend repo_config($name) $value + } + } + close $fd_rc + } +} + proc load_all_remotes {} { - global gitdir all_remotes + global gitdir all_remotes repo_config set all_remotes [list] set rm_dir [file join $gitdir remotes] @@ -804,13 +819,11 @@ proc load_all_remotes {} { -directory $rm_dir *]] } - set fd_rc [open "| git repo-config --list" r] - while {[gets $fd_rc line] >= 0} { - if {[regexp ^remote\.(.*)\.url= $line line name]} { + foreach line [array names repo_config remote.*.url] { + if {[regexp ^remote\.(.*)\.url\$ $line line name]} { lappend all_remotes $name } } - close $fd_rc set all_remotes [lsort -unique $all_remotes] } @@ -1541,6 +1554,7 @@ if {$appname == {git-citool}} { wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm +load_repo_config load_all_remotes populate_remote_menu .mbar.fetch From fetch_from populate_remote_menu .mbar.push To push_to -- cgit v1.2.3 From d33ba5fa809be1714c7f31f128a7a72461a96d96 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 05:02:15 -0500 Subject: git-gui: Added support for pulling from default branch of a remote. We now create one menu entry per remote listing the first Pull: or fetch entry associated with that remote as the branch to pull into the current branch. This is actually quite incorrect as we should be using the default remote branch name listed in branch..merge for a new-style remote described in the config file. But its a good default to get started with. Signed-off-by: Shawn O. Pearce --- git-gui | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/git-gui b/git-gui index de67aa2a68..4aa035a6c4 100755 --- a/git-gui +++ b/git-gui @@ -592,16 +592,31 @@ proc fetch_from {remote} { set w [new_console "fetch $remote" \ "Fetching new changes from $remote"] set cmd [list git fetch] - lappend cmd -v lappend cmd $remote console_exec $w $cmd } +proc pull_remote {remote branch} { + set w [new_console "pull $remote $branch" \ + "Pulling new changes from branch $branch in $remote"] + set cmd [list git pull] + lappend cmd $remote + lappend cmd $branch + console_exec $w $cmd [list post_pull_remote $remote $branch] +} + +proc post_pull_remote {remote branch success} { + if {$success} { + update_status "Successfully pulled $branch from $remote." + } else { + update_status "Conflicts detected while pulling $branch from $remote." + } +} + proc push_to {remote} { set w [new_console "push $remote" \ "Pushing changes to $remote"] set cmd [list git push] - lappend -v lappend cmd $remote console_exec $w $cmd } @@ -829,7 +844,7 @@ proc load_all_remotes {} { } proc populate_remote_menu {m pfx op} { - global gitdir all_remotes mainfont + global all_remotes mainfont foreach remote $all_remotes { $m add command -label "$pfx $remote..." \ @@ -838,6 +853,40 @@ proc populate_remote_menu {m pfx op} { } } +proc populate_pull_menu {m} { + global gitdir repo_config all_remotes mainfont + + foreach remote $all_remotes { + set rb {} + if {[array get repo_config remote.$remote.url] != {}} { + if {[array get repo_config remote.$remote.fetch] != {}} { + regexp {^([^:]+):} \ + [lindex $repo_config(remote.$remote.fetch) 0] \ + line rb + } + } else { + catch { + set fd [open [file join $gitdir remotes $remote] r] + while {[gets $fd line] >= 0} { + if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} { + break + } + } + close $fd + } + } + + set rb_short $rb + regsub ^refs/heads/ $rb {} rb_short + if {$rb_short != {}} { + $m add command \ + -label "Branch $rb_short from $remote..." \ + -command [list pull_remote $remote $rb] \ + -font $mainfont + } + } +} + ###################################################################### ## ## icons @@ -966,7 +1015,9 @@ proc show_msg {w top msg} { pack $w.ok -side bottom bind $top "grab $top; focus $top" bind $top "destroy $top" - wm title $top "error: $appname ([file normalize [file dirname $gitdir]])" + wm title $w "$appname ([lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end]): error" tkwait window $top } @@ -1011,7 +1062,9 @@ proc hook_failed_popup {hook msg} { bind $w "grab $w; focus $w" bind $w "destroy $w" - wm title $w "error: $appname ([file normalize [file dirname $gitdir]])" + wm title $w "$appname ([lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end]): error" tkwait window $w } @@ -1060,11 +1113,13 @@ proc console_init {w} { pack $w.ok -side bottom bind $w "focus $w" - wm title $w "$appname ([file dirname [file normalize [file dirname $gitdir]]]): [lindex $console_data($w) 0]" + wm title $w "$appname ([lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end]): [lindex $console_data($w) 0]" return $w } -proc console_exec {w cmd} { +proc console_exec {w cmd {after {}}} { global tcl_platform # -- Windows tosses the enviroment when we exec our child. @@ -1081,10 +1136,10 @@ proc console_exec {w cmd} { set fd_f [open $cmd r] fconfigure $fd_f -blocking 0 -translation binary - fileevent $fd_f readable [list console_read $w $fd_f] + fileevent $fd_f readable [list console_read $w $fd_f $after] } -proc console_read {w fd} { +proc console_read {w fd after} { global console_cr console_data set buf [read $fd] @@ -1123,13 +1178,18 @@ proc console_read {w fd} { $w.m.s conf -background red -text {Error: Command Failed} $w.ok conf -text Close $w.ok conf -state normal + set ok 0 } elseif {[winfo exists $w]} { $w.m.s conf -background green -text {Success} $w.ok conf -text Close $w.ok conf -state normal + set ok 1 } array unset console_cr $w array unset console_data $w + if {$after != {}} { + uplevel #0 $after $ok + } return } fconfigure $fd -blocking 0 @@ -1558,4 +1618,5 @@ load_repo_config load_all_remotes populate_remote_menu .mbar.fetch From fetch_from populate_remote_menu .mbar.push To push_to +populate_pull_menu .mbar.pull update_status -- cgit v1.2.3 From c5437df168379f33c7148c60b232b5c2887dd537 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 05:06:40 -0500 Subject: git-gui: Updated TODO list now that pull is starting to work. Signed-off-by: Shawn O. Pearce --- TODO | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index b425da30e2..136e87849c 100644 --- a/TODO +++ b/TODO @@ -6,9 +6,11 @@ Items outstanding: * Update index with $PARENT version but leave working directory alone. - * Populate the pull menu with local branches. + * Add all new files in one shot (e.g. "git add ."). + + * Add file to .gitignore or info/excludes. - * Populate the pull menu with default merge branch from each remote. + * Populate the pull menu with local branches. * Make use of the new default merge data stored in repo-config. -- cgit v1.2.3 From 868c8752451c0b75502a0134068f0cfe055eb7ad Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 18:34:09 -0500 Subject: git-gui: Corrected diff-index/diff-files protocol parsing errors. When we were receiving a lot of output from diff-index we split records at the wrong locations and started using the file status information (mode and SHA1s) as path names, and then proceeded to try to use part of the path names as status data. This caused all sorts of havoc. So I rewrote the parsing implementation to scan for the pair of null bytes along the buffer and stop scanning (waiting for more data) if both can't be found during this event. This seems to work well under high load (like when processing 6,983 added files). Signed-off-by: Shawn O. Pearce --- git-gui | 59 +++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/git-gui b/git-gui index 4aa035a6c4..48e1c5601f 100755 --- a/git-gui +++ b/git-gui @@ -111,6 +111,7 @@ proc read_refresh {fd final} { global gitdir PARENT commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states + global buf_rdi buf_rdf buf_rlo read $fd if {![eof $fd]} return @@ -123,6 +124,10 @@ proc read_refresh {fd final} { lappend ls_others "--exclude-from=$info_exclude" } + set buf_rdi {} + set buf_rdf {} + set buf_rlo {} + set status_active 3 set ui_status_value {Scanning for modified files ...} set fd_di [open "| git diff-index --cached -z $PARENT" r] @@ -158,13 +163,28 @@ proc read_diff_index {fd final} { global buf_rdi append buf_rdi [read $fd] - set pck [split $buf_rdi "\0"] - set buf_rdi [lindex $pck end] - foreach {m p} [lrange $pck 0 end-1] { - if {$m != {} && $p != {}} { - display_file $p [string index $m end]_ - } + set c 0 + set n [string length $buf_rdi] + while {$c < $n} { + set z1 [string first "\0" $buf_rdi $c] + if {$z1 == -1} break + incr z1 + set z2 [string first "\0" $buf_rdi $z1] + if {$z2 == -1} break + + set c $z2 + incr z2 -1 + display_file \ + [string range $buf_rdi $z1 $z2] \ + [string index $buf_rdi [expr $z1 - 2]]_ + incr c + } + if {$c < $n} { + set buf_rdi [string range $buf_rdi $c end] + } else { + set buf_rdi {} } + status_eof $fd buf_rdi $final } @@ -172,13 +192,28 @@ proc read_diff_files {fd final} { global buf_rdf append buf_rdf [read $fd] - set pck [split $buf_rdf "\0"] - set buf_rdf [lindex $pck end] - foreach {m p} [lrange $pck 0 end-1] { - if {$m != {} && $p != {}} { - display_file $p _[string index $m end] - } + set c 0 + set n [string length $buf_rdf] + while {$c < $n} { + set z1 [string first "\0" $buf_rdf $c] + if {$z1 == -1} break + incr z1 + set z2 [string first "\0" $buf_rdf $z1] + if {$z2 == -1} break + + set c $z2 + incr z2 -1 + display_file \ + [string range $buf_rdf $z1 $z2] \ + _[string index $buf_rdf [expr $z1 - 2]] + incr c + } + if {$c < $n} { + set buf_rdf [string range $buf_rdf $c end] + } else { + set buf_rdf {} } + status_eof $fd buf_rdf $final } -- cgit v1.2.3 From 93f654df7ec9a509e9362e06821c2af0b2bacc82 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 19:30:54 -0500 Subject: git-gui: Performance improvements for large file sets. Loading 6900 newly added files required about 90 seconds on one system. This is just far too long to perform a "status" type of operation. git-status on the same system completes in just 8.2 seconds if it is redirected to /dev/null. Most of our performance improvement comes from moving all of the UI updating out of the main fileevent handlers for the status process. Instead we are only updating the file_states array and then only doing the UI update when all states are known and have been finally determined. The rescan execution is now down to almost 30 seconds for the same case, a good (but not really all that impressive) improvement. Signed-off-by: Shawn O. Pearce --- git-gui | 107 ++++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/git-gui b/git-gui index 48e1c5601f..ed745ee52e 100755 --- a/git-gui +++ b/git-gui @@ -236,10 +236,13 @@ proc status_eof {fd buf final} { if {[eof $fd]} { set $buf {} close $fd + if {[incr status_active -1] == 0} { unlock_index set ui_status_value $final + display_all_files + if {$ui_fname_value != {} && [array names file_states \ -exact $ui_fname_value] != {}} { show_diff $ui_fname_value @@ -711,79 +714,103 @@ proc bsearch {w path} { return -[expr $lo + 1] } +set next_icon_id 0 + proc merge_state {path state} { - global file_states + global file_states next_icon_id if {[array names file_states -exact $path] == {}} { - set o __ - set s [list $o none none] + set m __ + set s [list $m icon[incr next_icon_id]] } else { set s $file_states($path) - set o [lindex $s 0] + set m [lindex $s 0] } - set m [lindex $s 0] - if {[string index $state 0] == "_"} { + if {[string index $state 0] == {_}} { set state [string index $m 0][string index $state 1] - } elseif {[string index $state 0] == "*"} { + } elseif {[string index $state 0] == {*}} { set state _[string index $state 1] } - if {[string index $state 1] == "_"} { + if {[string index $state 1] == {_}} { set state [string index $state 0][string index $m 1] - } elseif {[string index $state 1] == "*"} { + } elseif {[string index $state 1] == {*}} { set state [string index $state 0]_ } set file_states($path) [lreplace $s 0 0 $state] - return $o + return $m } proc display_file {path state} { - global ui_index ui_other file_states + global ui_index ui_other file_states status_active set old_m [merge_state $path $state] + if {$status_active} return + set s $file_states($path) - set m [lindex $s 0] + set new_m [lindex $s 0] + set new_col [mapcol $new_m $path] + set new_ico [mapicon $new_m $path] - if {[mapcol $m $path] == "o"} { - set ii 1 - set ai 2 - set iw $ui_index - set aw $ui_other + if {$new_col == {o}} { + set old_w $ui_index + set new_w $ui_other } else { - set ii 2 - set ai 1 - set iw $ui_other - set aw $ui_index + set old_w $ui_other + set new_w $ui_index } - set d [lindex $s $ii] - if {$d != "none"} { - set lno [bsearch $iw $path] + if {$new_col != [mapcol $old_m $path]} { + set lno [bsearch $old_w $path] if {$lno >= 0} { incr lno - $iw conf -state normal - $iw delete $lno.0 [expr $lno + 1].0 - $iw conf -state disabled - set s [lreplace $s $ii $ii none] + $old_w conf -state normal + $old_w delete $lno.0 [expr $lno + 1].0 + $old_w conf -state disabled } + + set lno [expr abs([bsearch $new_w $path] + 1) + 1] + $new_w conf -state normal + $new_w image create $lno.0 \ + -align center -padx 5 -pady 1 \ + -name [lindex $s 1] \ + -image [mapicon $m $path] + $new_w insert $lno.1 "$path\n" + $new_w conf -state disabled + } elseif {$new_icon != [mapicon $old_m $path]} { + $new_w conf -state normal + $new_w image conf [lindex $s 1] -image $new_icon + $new_w conf -state disabled } +} - set d [lindex $s $ai] - if {$d == "none"} { - set lno [expr abs([bsearch $aw $path] + 1) + 1] - $aw conf -state normal - set ico [$aw image create $lno.0 \ +proc display_all_files {} { + global ui_index ui_other file_states + + $ui_index conf -state normal + $ui_other conf -state normal + + foreach path [lsort [array names file_states]] { + set s $file_states($path) + set m [lindex $s 0] + + if {[mapcol $m $path] == {o}} { + set aw $ui_other + } else { + set aw $ui_index + } + + $aw image create end \ -align center -padx 5 -pady 1 \ - -image [mapicon $m $path]] - $aw insert $lno.1 "$path\n" - $aw conf -state disabled - set file_states($path) [lreplace $s $ai $ai [list $ico]] - } elseif {[mapicon $m $path] != [mapicon $old_m $path]} { - set ico [lindex $d 0] - $aw image conf $ico -image [mapicon $m $path] + -name [lindex $s 1] \ + -image [mapicon $m $path] + $aw insert end "$path\n" } + + $ui_index conf -state disabled + $ui_other conf -state disabled } proc with_update_index {body} { -- cgit v1.2.3 From 6b29267542546b443fd885b2ed6dc896680b09ed Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 19:58:37 -0500 Subject: git-gui: More performance improvements to rescan logic. Removed as much as possible from the merge_state proc, which is where we spent most of our time before UI update. This change makes our running time match that of git status, except that we then need about 7 additional seconds to draw 6900 files on screen. Apparently the [array names a -exact $v] operator in Tcl is O(n) rather than O(1), which is really quite disappointing given that each array can only have one entry for a given value. Switching to a lookup with a catch (whose error we ignore) runs in O(1) time and bought us most of that improvement. Signed-off-by: Shawn O. Pearce --- git-gui | 87 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/git-gui b/git-gui index ed745ee52e..1833fa86ce 100755 --- a/git-gui +++ b/git-gui @@ -70,7 +70,7 @@ proc repository_state {hdvar ctvar} { proc update_status {{final Ready.}} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm - global status_active file_states + global status_active file_states status_start if {$status_active || ![lock_index read]} return @@ -84,6 +84,7 @@ proc update_status {{final Ready.}} { set commit_type $new_type } + set status_start [clock seconds] array unset file_states foreach w [list $ui_index $ui_other] { $w conf -state normal @@ -230,7 +231,7 @@ proc read_ls_others {fd final} { } proc status_eof {fd buf final} { - global status_active $buf + global status_active status_start $buf global ui_fname_value ui_status_value file_states if {[eof $fd]} { @@ -240,8 +241,12 @@ proc status_eof {fd buf final} { if {[incr status_active -1] == 0} { unlock_index - set ui_status_value $final + set e1 [clock seconds] display_all_files + set e2 [clock seconds] +puts "TIME [expr $e1 - $status_start] + [expr $e2 - $e1] = [expr $e2 - $status_start]" + + set ui_status_value $final if {$ui_fname_value != {} && [array names file_states \ -exact $ui_fname_value] != {}} { @@ -664,11 +669,11 @@ proc push_to {remote} { ## ui helpers proc mapcol {state path} { - global all_cols + global all_cols ui_other if {[catch {set r $all_cols($state)}]} { puts "error: no column for state={$state} $path" - return o + return $ui_other } return $r } @@ -716,31 +721,34 @@ proc bsearch {w path} { set next_icon_id 0 -proc merge_state {path state} { +proc merge_state {path new_state} { global file_states next_icon_id - if {[array names file_states -exact $path] == {}} { - set m __ - set s [list $m icon[incr next_icon_id]] + set s0 [string index $new_state 0] + set s1 [string index $new_state 1] + + if {[catch {set info $file_states($path)}]} { + set state __ + set icon n[incr next_icon_id] } else { - set s $file_states($path) - set m [lindex $s 0] + set state [lindex $info 0] + set icon [lindex $info 1] } - if {[string index $state 0] == {_}} { - set state [string index $m 0][string index $state 1] - } elseif {[string index $state 0] == {*}} { - set state _[string index $state 1] + if {$s0 == {_}} { + set s0 [string index $state 0] + } elseif {$s0 == {*}} { + set s0 _ } - if {[string index $state 1] == {_}} { - set state [string index $state 0][string index $m 1] - } elseif {[string index $state 1] == {*}} { - set state [string index $state 0]_ + if {$s1 == {_}} { + set s1 [string index $state 1] + } elseif {$s1 == {*}} { + set s1 _ } - set file_states($path) [lreplace $s 0 0 $state] - return $m + set file_states($path) [list $s0$s1 $icon] + return $state } proc display_file {path state} { @@ -750,19 +758,12 @@ proc display_file {path state} { if {$status_active} return set s $file_states($path) + set old_w [mapcol $old_m $path] + set new_w [mapcol $new_m $path] set new_m [lindex $s 0] - set new_col [mapcol $new_m $path] set new_ico [mapicon $new_m $path] - if {$new_col == {o}} { - set old_w $ui_index - set new_w $ui_other - } else { - set old_w $ui_other - set new_w $ui_index - } - - if {$new_col != [mapcol $old_m $path]} { + if {$new_w != $old_w} { set lno [bsearch $old_w $path] if {$lno >= 0} { incr lno @@ -795,18 +796,12 @@ proc display_all_files {} { foreach path [lsort [array names file_states]] { set s $file_states($path) set m [lindex $s 0] - - if {[mapcol $m $path] == {o}} { - set aw $ui_other - } else { - set aw $ui_index - } - - $aw image create end \ + set w [mapcol $m $path] + $w image create end \ -align center -padx 5 -pady 1 \ -name [lindex $s 1] \ -image [mapicon $m $path] - $aw insert end "$path\n" + $w insert end "$path\n" } $ui_index conf -state disabled @@ -1025,6 +1020,8 @@ static unsigned char file_merge_bits[] = { 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; } -maskdata $filemask +set ui_index .vpane.files.index.list +set ui_other .vpane.files.other.list set max_status_desc 0 foreach i { {__ i plain "Unmodified"} @@ -1035,7 +1032,7 @@ foreach i { {_O o plain "Untracked"} {A_ o fulltick "Added"} {AM o parttick "Partially added"} - {AD o question "Added (but now gone)"} + {AD o question "Added (but now gone)"} {_D i question "Missing"} {D_ i removed "Removed"} @@ -1048,7 +1045,11 @@ foreach i { if {$max_status_desc < [string length [lindex $i 3]]} { set max_status_desc [string length [lindex $i 3]] } - set all_cols([lindex $i 0]) [lindex $i 1] + if {[lindex $i 1] == {i}} { + set all_cols([lindex $i 0]) $ui_index + } else { + set all_cols([lindex $i 0]) $ui_other + } set all_icons([lindex $i 0]) file_[lindex $i 2] set all_descs([lindex $i 0]) [lindex $i 3] } @@ -1461,7 +1462,6 @@ panedwindow .vpane.files -orient horizontal pack .vpane -anchor n -side top -fill both -expand 1 # -- Index File List -set ui_index .vpane.files.index.list frame .vpane.files.index -height 100 -width 400 label .vpane.files.index.title -text {Modified Files} \ -background green \ @@ -1479,7 +1479,6 @@ pack $ui_index -side left -fill both -expand 1 .vpane.files add .vpane.files.index -sticky nsew # -- Other (Add) File List -set ui_other .vpane.files.other.list frame .vpane.files.other -height 100 -width 100 label .vpane.files.other.title -text {Untracked Files} \ -background red \ -- cgit v1.2.3 From 0fb8f9ce05bf65cc9c22f078c4a6dc7dec3ec27a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 20:27:46 -0500 Subject: git-gui: Flip commit message buffer and diff area. Since Tk will only supply new space gained from growing the top level to the bottom/right most widget within a panedwindow and most users will be growing a git-gui main window for the purposes of seeing more of the currently shown diff, flipping the order around makes Tk do what the user wants by default. Of course because we also removed the paned window from the commit buffer area it is now impossible to increase the visible space for the commit message. But I don't see this as a huge concern right now as its actually very awkward to try and balance three paned window dividers within the same top level window. We could always add it back if users really want to expand the commit buffer and see more. I also corrected a number of bugs that I accidentally introduced in the last commit. Signed-off-by: Shawn O. Pearce --- git-gui | 179 +++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 91 insertions(+), 88 deletions(-) diff --git a/git-gui b/git-gui index 1833fa86ce..83f713535f 100755 --- a/git-gui +++ b/git-gui @@ -70,7 +70,7 @@ proc repository_state {hdvar ctvar} { proc update_status {{final Ready.}} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm - global status_active file_states status_start + global status_active file_states if {$status_active || ![lock_index read]} return @@ -84,7 +84,6 @@ proc update_status {{final Ready.}} { set commit_type $new_type } - set status_start [clock seconds] array unset file_states foreach w [list $ui_index $ui_other] { $w conf -state normal @@ -231,7 +230,7 @@ proc read_ls_others {fd final} { } proc status_eof {fd buf final} { - global status_active status_start $buf + global status_active $buf global ui_fname_value ui_status_value file_states if {[eof $fd]} { @@ -241,11 +240,7 @@ proc status_eof {fd buf final} { if {[incr status_active -1] == 0} { unlock_index - set e1 [clock seconds] display_all_files - set e2 [clock seconds] -puts "TIME [expr $e1 - $status_start] + [expr $e2 - $e1] = [expr $e2 - $status_start]" - set ui_status_value $final if {$ui_fname_value != {} && [array names file_states \ @@ -758,10 +753,10 @@ proc display_file {path state} { if {$status_active} return set s $file_states($path) - set old_w [mapcol $old_m $path] - set new_w [mapcol $new_m $path] set new_m [lindex $s 0] - set new_ico [mapicon $new_m $path] + set new_w [mapcol $new_m $path] + set old_w [mapcol $old_m $path] + set new_icon [mapicon $new_m $path] if {$new_w != $old_w} { set lno [bsearch $old_w $path] @@ -1498,101 +1493,56 @@ pack $ui_other -side left -fill both -expand 1 $ui_index tag conf in_diff -font [concat $mainfont bold] $ui_other tag conf in_diff -font [concat $mainfont bold] -# -- Diff Header -set ui_fname_value {} -set ui_fstatus_value {} -frame .vpane.diff -height 200 -width 400 -frame .vpane.diff.header -label .vpane.diff.header.l1 -text {File:} -font $mainfont -label .vpane.diff.header.l2 -textvariable ui_fname_value \ - -anchor w \ - -justify left \ - -font $mainfont -label .vpane.diff.header.l3 -text {Status:} -font $mainfont -label .vpane.diff.header.l4 -textvariable ui_fstatus_value \ - -width $max_status_desc \ - -anchor w \ - -justify left \ - -font $mainfont -pack .vpane.diff.header.l1 -side left -pack .vpane.diff.header.l2 -side left -fill x -pack .vpane.diff.header.l4 -side right -pack .vpane.diff.header.l3 -side right - -# -- Diff Body -frame .vpane.diff.body -set ui_diff .vpane.diff.body.t -text $ui_diff -background white -borderwidth 0 \ - -width 80 -height 15 -wrap none \ - -font $difffont \ - -xscrollcommand {.vpane.diff.body.sbx set} \ - -yscrollcommand {.vpane.diff.body.sby set} \ - -cursor $maincursor \ - -state disabled -scrollbar .vpane.diff.body.sbx -orient horizontal \ - -command [list $ui_diff xview] -scrollbar .vpane.diff.body.sby -orient vertical \ - -command [list $ui_diff yview] -pack .vpane.diff.body.sbx -side bottom -fill x -pack .vpane.diff.body.sby -side right -fill y -pack $ui_diff -side left -fill both -expand 1 -pack .vpane.diff.header -side top -fill x -pack .vpane.diff.body -side bottom -fill both -expand 1 -.vpane add .vpane.diff -stick nsew - -$ui_diff tag conf dm -foreground red -$ui_diff tag conf dp -foreground blue -$ui_diff tag conf da -font [concat $difffont bold] -$ui_diff tag conf di -foreground "#00a000" -$ui_diff tag conf dni -foreground "#a000a0" -$ui_diff tag conf bold -font [concat $difffont bold] - -# -- Commit Area -frame .vpane.commarea -height 170 -.vpane add .vpane.commarea -stick nsew +# -- Diff and Commit Area +frame .vpane.lower -height 400 -width 400 +frame .vpane.lower.commarea +frame .vpane.lower.diff -relief sunken -borderwidth 1 +pack .vpane.lower.commarea -side top -fill x +pack .vpane.lower.diff -side bottom -fill both -expand 1 +.vpane add .vpane.lower -stick nsew # -- Commit Area Buttons -frame .vpane.commarea.buttons -label .vpane.commarea.buttons.l -text {} \ +frame .vpane.lower.commarea.buttons +label .vpane.lower.commarea.buttons.l -text {} \ -anchor w \ -justify left \ -font $mainfont -pack .vpane.commarea.buttons.l -side top -fill x -pack .vpane.commarea.buttons -side left -fill y +pack .vpane.lower.commarea.buttons.l -side top -fill x +pack .vpane.lower.commarea.buttons -side left -fill y -button .vpane.commarea.buttons.rescan -text {Rescan} \ +button .vpane.lower.commarea.buttons.rescan -text {Rescan} \ -command do_rescan \ -font $mainfont -pack .vpane.commarea.buttons.rescan -side top -fill x -lappend disable_on_lock {.vpane.commarea.buttons.rescan conf -state} +pack .vpane.lower.commarea.buttons.rescan -side top -fill x +lappend disable_on_lock {.vpane.lower.commarea.buttons.rescan conf -state} -button .vpane.commarea.buttons.amend -text {Amend Last} \ +button .vpane.lower.commarea.buttons.amend -text {Amend Last} \ -command do_amend_last \ -font $mainfont -pack .vpane.commarea.buttons.amend -side top -fill x -lappend disable_on_lock {.vpane.commarea.buttons.amend conf -state} +pack .vpane.lower.commarea.buttons.amend -side top -fill x +lappend disable_on_lock {.vpane.lower.commarea.buttons.amend conf -state} -button .vpane.commarea.buttons.ciall -text {Check-in All} \ +button .vpane.lower.commarea.buttons.ciall -text {Check-in All} \ -command do_checkin_all \ -font $mainfont -pack .vpane.commarea.buttons.ciall -side top -fill x -lappend disable_on_lock {.vpane.commarea.buttons.ciall conf -state} +pack .vpane.lower.commarea.buttons.ciall -side top -fill x +lappend disable_on_lock {.vpane.lower.commarea.buttons.ciall conf -state} -button .vpane.commarea.buttons.signoff -text {Sign Off} \ +button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \ -command do_signoff \ -font $mainfont -pack .vpane.commarea.buttons.signoff -side top -fill x +pack .vpane.lower.commarea.buttons.signoff -side top -fill x -button .vpane.commarea.buttons.commit -text {Commit} \ +button .vpane.lower.commarea.buttons.commit -text {Commit} \ -command do_commit \ -font $mainfont -pack .vpane.commarea.buttons.commit -side top -fill x -lappend disable_on_lock {.vpane.commarea.buttons.commit conf -state} +pack .vpane.lower.commarea.buttons.commit -side top -fill x +lappend disable_on_lock {.vpane.lower.commarea.buttons.commit conf -state} # -- Commit Message Buffer -frame .vpane.commarea.buffer -set ui_comm .vpane.commarea.buffer.t -set ui_coml .vpane.commarea.buffer.l +frame .vpane.lower.commarea.buffer +set ui_comm .vpane.lower.commarea.buffer.t +set ui_coml .vpane.lower.commarea.buffer.l label $ui_coml -text {Commit Message:} \ -anchor w \ -justify left \ @@ -1606,15 +1556,68 @@ trace add variable commit_type write {uplevel #0 { }} text $ui_comm -background white -borderwidth 1 \ -relief sunken \ - -width 75 -height 10 -wrap none \ + -width 75 -height 9 -wrap none \ -font $difffont \ - -yscrollcommand {.vpane.commarea.buffer.sby set} \ + -yscrollcommand {.vpane.lower.commarea.buffer.sby set} \ -cursor $maincursor -scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview] +scrollbar .vpane.lower.commarea.buffer.sby -command [list $ui_comm yview] pack $ui_coml -side top -fill x -pack .vpane.commarea.buffer.sby -side right -fill y +pack .vpane.lower.commarea.buffer.sby -side right -fill y pack $ui_comm -side left -fill y -pack .vpane.commarea.buffer -side left -fill y +pack .vpane.lower.commarea.buffer -side left -fill y + +# -- Diff Header +set ui_fname_value {} +set ui_fstatus_value {} +frame .vpane.lower.diff.header -background orange +label .vpane.lower.diff.header.l1 -text {File:} \ + -background orange \ + -font $mainfont +label .vpane.lower.diff.header.l2 -textvariable ui_fname_value \ + -background orange \ + -anchor w \ + -justify left \ + -font $mainfont +label .vpane.lower.diff.header.l3 -text {Status:} \ + -background orange \ + -font $mainfont +label .vpane.lower.diff.header.l4 -textvariable ui_fstatus_value \ + -background orange \ + -width $max_status_desc \ + -anchor w \ + -justify left \ + -font $mainfont +pack .vpane.lower.diff.header.l1 -side left +pack .vpane.lower.diff.header.l2 -side left -fill x +pack .vpane.lower.diff.header.l4 -side right +pack .vpane.lower.diff.header.l3 -side right + +# -- Diff Body +frame .vpane.lower.diff.body +set ui_diff .vpane.lower.diff.body.t +text $ui_diff -background white -borderwidth 0 \ + -width 80 -height 15 -wrap none \ + -font $difffont \ + -xscrollcommand {.vpane.lower.diff.body.sbx set} \ + -yscrollcommand {.vpane.lower.diff.body.sby set} \ + -cursor $maincursor \ + -state disabled +scrollbar .vpane.lower.diff.body.sbx -orient horizontal \ + -command [list $ui_diff xview] +scrollbar .vpane.lower.diff.body.sby -orient vertical \ + -command [list $ui_diff yview] +pack .vpane.lower.diff.body.sbx -side bottom -fill x +pack .vpane.lower.diff.body.sby -side right -fill y +pack $ui_diff -side left -fill both -expand 1 +pack .vpane.lower.diff.header -side top -fill x +pack .vpane.lower.diff.body -side bottom -fill both -expand 1 + +$ui_diff tag conf dm -foreground red +$ui_diff tag conf dp -foreground blue +$ui_diff tag conf da -font [concat $difffont bold] +$ui_diff tag conf di -foreground "#00a000" +$ui_diff tag conf dni -foreground "#a000a0" +$ui_diff tag conf bold -font [concat $difffont bold] # -- Status Bar set ui_status_value {Initializing...} -- cgit v1.2.3 From d1536c488e2f410c9f622356e29d9a5870d2403f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 20:40:35 -0500 Subject: git-gui: Added repack database menu option, to invoke git repack. Most users of git-gui probably shouldn't be invoking git repack directly; instead we should be looking at how many loose objects they have and how many active packs they have and making the decision for them. But that's more work to code, and there's always going to be discussion about what is the right default threshold and how do we know that the user is willing to do the repack when we decide its time, etc. So instead we'll just keep it simple and offer up the menu option. Unfortunately right now we get now progress indication back from git-pack-objects as its being invoked not through a tty, which makes it disable progress output and the git-repack.sh wrapper won't let us pass through --progress. Signed-off-by: Shawn O. Pearce --- git-gui | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/git-gui b/git-gui index 83f713535f..eb7329c218 100755 --- a/git-gui +++ b/git-gui @@ -1276,6 +1276,14 @@ proc do_gitk {} { } } +proc do_repack {} { + set w [new_console "repack" "Repacking the object database"] + set cmd [list git repack] + lappend cmd -a + lappend cmd -d + console_exec $w $cmd +} + proc do_quit {} { global gitdir ui_comm @@ -1406,6 +1414,9 @@ menu .mbar.project .mbar.project add command -label Visualize \ -command do_gitk \ -font $mainfont +.mbar.project add command -label {Repack Database} \ + -command do_repack \ + -font $mainfont .mbar.project add command -label Quit \ -command do_quit \ -accelerator $M1T-Q \ -- cgit v1.2.3 From e534f3a88676fe0a08eb20359e1a43c6aa7dfe84 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 21:27:29 -0500 Subject: git-gui: Allow the user to disable update-index --refresh during rescan. On very large projects (~1000 files) on Windows systems the update-index --refresh stage of the rescan process takes a nontrival amount of time. If the user is generally very careful with their file modification such that the modification timestamp of the file differs only when the content also differs then we can skip this somewhat expensive step and go right to the diff-index and diff-files processes. We save the user's prefernce in the current repository if they modify the setting during a git-gui session, but we also load it through our dump of repo-config --list so the user could move it to their ~/.gitconfig file if they wanted it globally disabled. We still keep update-index --refresh enabled by default however, as most users will probably want it. Signed-off-by: Shawn O. Pearce --- git-gui | 51 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/git-gui b/git-gui index eb7329c218..8562983d36 100755 --- a/git-gui +++ b/git-gui @@ -71,6 +71,7 @@ proc update_status {{final Ready.}} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states + global cfg_trust_mtime if {$status_active || ![lock_index read]} return @@ -100,22 +101,28 @@ proc update_status {{final Ready.}} { $ui_comm edit modified false } - set status_active 1 - set ui_status_value {Refreshing file status...} - set fd_rf [open "| git update-index -q --unmerged --refresh" r] - fconfigure $fd_rf -blocking 0 -translation binary - fileevent $fd_rf readable [list read_refresh $fd_rf $final] + if {$cfg_trust_mtime == {true}} { + update_status_stage2 {} $final + } else { + set status_active 1 + set ui_status_value {Refreshing file status...} + set fd_rf [open "| git update-index -q --unmerged --refresh" r] + fconfigure $fd_rf -blocking 0 -translation binary + fileevent $fd_rf readable [list update_status_stage2 $fd_rf $final] + } } -proc read_refresh {fd final} { +proc update_status_stage2 {fd final} { global gitdir PARENT commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states global buf_rdi buf_rdf buf_rlo - read $fd - if {![eof $fd]} return - close $fd + if {$fd != {}} { + read $fd + if {![eof $fd]} return + close $fd + } set ls_others [list | git ls-files --others -z \ --exclude-per-directory=.gitignore] @@ -860,6 +867,7 @@ proc toggle_mode {path} { proc load_repo_config {} { global repo_config + global cfg_trust_mtime array unset repo_config catch { @@ -871,6 +879,22 @@ proc load_repo_config {} { } close $fd_rc } + + if {[catch {set cfg_trust_mtime $repo_config(gui.trustmtime)}]} { + set cfg_trust_mtime false + } +} + +proc save_my_config {} { + global repo_config + global cfg_trust_mtime + + if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} { + set rc_trustMTime false + } + if {$cfg_trust_mtime != $rc_trustMTime} { + exec git repo-config gui.trustMTime $cfg_trust_mtime + } } proc load_all_remotes {} { @@ -1299,6 +1323,7 @@ proc do_quit {} { file delete $save } + save_my_config destroy . } @@ -1407,6 +1432,7 @@ menu .mbar -tearoff 0 .mbar add cascade -label Fetch -menu .mbar.fetch .mbar add cascade -label Pull -menu .mbar.pull .mbar add cascade -label Push -menu .mbar.push +.mbar add cascade -label Options -menu .mbar.options . configure -menu .mbar # -- Project Menu @@ -1461,6 +1487,13 @@ menu .mbar.pull # -- Push Menu menu .mbar.push +# -- Options Menu +menu .mbar.options +.mbar.options add checkbutton -label {Trust File Modification Timestamp} \ + -offvalue false \ + -onvalue true \ + -variable cfg_trust_mtime + # -- Main Window Layout panedwindow .vpane -orient vertical panedwindow .vpane.files -orient horizontal -- cgit v1.2.3 From 988b8a7d63ff52f252bb4c517d9f05c3952aa728 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 21:30:46 -0500 Subject: git-gui: Grab the index lock while running pull. The user must not modify the index while a git pull operation is running, doing so might cause problems for the merge driver and specific strategy being used. Normally on the command line people are just really good and don't try to run index altering operations while they are also running a pull. But in a slick GUI like git-gui we can't trust the user quite as much. Signed-off-by: Shawn O. Pearce --- git-gui | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git-gui b/git-gui index 8562983d36..2d82152b36 100755 --- a/git-gui +++ b/git-gui @@ -642,6 +642,7 @@ proc fetch_from {remote} { } proc pull_remote {remote branch} { + if {![lock_index update]} return set w [new_console "pull $remote $branch" \ "Pulling new changes from branch $branch in $remote"] set cmd [list git pull] @@ -651,6 +652,7 @@ proc pull_remote {remote branch} { } proc post_pull_remote {remote branch success} { + unlock_index if {$success} { update_status "Successfully pulled $branch from $remote." } else { -- cgit v1.2.3 From 2bc5b3487e47dc6caa8d543fc50a944e80651c59 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 21:38:22 -0500 Subject: git-gui: Pluralize timestamps within the options menu. Signed-off-by: Shawn O. Pearce --- git-gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 2d82152b36..465c2f388b 100755 --- a/git-gui +++ b/git-gui @@ -1491,7 +1491,7 @@ menu .mbar.push # -- Options Menu menu .mbar.options -.mbar.options add checkbutton -label {Trust File Modification Timestamp} \ +.mbar.options add checkbutton -label {Trust File Modification Timestamps} \ -offvalue false \ -onvalue true \ -variable cfg_trust_mtime -- cgit v1.2.3 From 0a462d67761b8178f09e23ef85a9298d1e19a2eb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 21:43:16 -0500 Subject: git-gui: Disable pull menu items when the index is locked. If we have the index locked then no pull command is allowed to proceed (as it would fail to get the index lock itself). So disable the pull menu items when we are doing any index based operations. Signed-off-by: Shawn O. Pearce --- git-gui | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 465c2f388b..4041aaacd9 100755 --- a/git-gui +++ b/git-gui @@ -932,7 +932,7 @@ proc populate_remote_menu {m pfx op} { } proc populate_pull_menu {m} { - global gitdir repo_config all_remotes mainfont + global gitdir repo_config all_remotes mainfont disable_on_lock foreach remote $all_remotes { set rb {} @@ -961,6 +961,8 @@ proc populate_pull_menu {m} { -label "Branch $rb_short from $remote..." \ -command [list pull_remote $remote $rb] \ -font $mainfont + lappend disable_on_lock \ + [list $m entryconf [$m index last] -state] } } } -- cgit v1.2.3 From ec39d83a55124660d03a744e0e7f67072a833950 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 22:00:38 -0500 Subject: git-gui: Don't let the user pull into an uncommitted working directory. If there are modified files present in the working directory then we should not let the user perform a pull as it may fail due to the modified files being uncommitted but needing to be merged at the file level. Yes there are many cases where a merge will complete successfully even though there are modified or untracked files sitting in the working directory. But users generally shouldn't be attempting merges like that, and if they are they probably are advanced enough to just use the command line and bypass this little safety check. We also no longer run a rescan after a successful pull has completed. Usually this is unnecessary as a successful pull won't leave modified files laying around. Instead we just update our HEAD and PARENT values with the new commit, if there is one. Unfortunately this does let the user get into an insane state as there are bugs in core Git's git-pull and git-merge programs where the exit status is sent back as a 0 rather than non-0 when a failure is detected. Signed-off-by: Shawn O. Pearce --- git-gui | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 4041aaacd9..d405008d2c 100755 --- a/git-gui +++ b/git-gui @@ -642,7 +642,40 @@ proc fetch_from {remote} { } proc pull_remote {remote branch} { + global HEAD commit_type + global file_states + if {![lock_index update]} return + + # -- Our in memory state should match the repository. + # + repository_state curHEAD cur_type + if {$commit_type != $cur_type || $HEAD != $curHEAD} { + error_popup {Last scanned state does not match repository state. + +Its highly likely that another Git program modified the +repository since our last scan. A rescan is required +before a pull can be started. +} + unlock_index + update_status + return + } + + # -- No differences should exist before a pull. + # + if {[array size file_states] != 0} { + error_popup {Uncommitted but modified files are present. + +You should not perform a pull with unmodified files in your working +directory as Git would be unable to recover from an incorrect merge. + +Commit or throw away all changes before starting a pull operation. +} + unlock_index + return + } + set w [new_console "pull $remote $branch" \ "Pulling new changes from branch $branch in $remote"] set cmd [list git pull] @@ -652,9 +685,14 @@ proc pull_remote {remote branch} { } proc post_pull_remote {remote branch success} { + global HEAD PARENT commit_type + global ui_status_value + unlock_index if {$success} { - update_status "Successfully pulled $branch from $remote." + repository_state HEAD commit_type + set PARENT $HEAD + set $ui_status_value {Ready.} } else { update_status "Conflicts detected while pulling $branch from $remote." } -- cgit v1.2.3 From bfe4c924da3853956d1b476afaa5f04429695223 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 22:03:03 -0500 Subject: git-gui: Update TODO list. Signed-off-by: Shawn O. Pearce --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index 136e87849c..0b1fa2ca59 100644 --- a/TODO +++ b/TODO @@ -10,6 +10,8 @@ Items outstanding: * Add file to .gitignore or info/excludes. + * Make initial commits work (currently it crashes the UI). + * Populate the pull menu with local branches. * Make use of the new default merge data stored in repo-config. -- cgit v1.2.3 From e4ee9af4946557370cfe18efdfa46c265a10639f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 7 Nov 2006 22:09:55 -0500 Subject: git-gui: Bug fix for bad variable reference in display_file. When a file jumps between the file lists due to its state changing we crashed thanks to a stale variable reference within the procedure as we tried to setup the new icon. Signed-off-by: Shawn O. Pearce --- git-gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui b/git-gui index d405008d2c..515cbb9dfb 100755 --- a/git-gui +++ b/git-gui @@ -819,7 +819,7 @@ proc display_file {path state} { $new_w image create $lno.0 \ -align center -padx 5 -pady 1 \ -name [lindex $s 1] \ - -image [mapicon $m $path] + -image $new_icon $new_w insert $lno.1 "$path\n" $new_w conf -state disabled } elseif {$new_icon != [mapicon $old_m $path]} { -- cgit v1.2.3 From 7fe7e733fafbab0373ee0d8fc35b9e284a598ee4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 8 Nov 2006 22:48:34 -0500 Subject: git-gui: Changed term 'check-in' to 'include'. At least one user was confused by the term 'check-in'; he thought that clicking that button would commit just that one file, but he wanted to include all modified files into his next commit. Since git doesn't really have a check-in concept this really was poor language to use. Git does have an update-index concept but that is a little too low level to show to the user. So instead we now talk about including files in a commit. Signed-off-by: Shawn O. Pearce --- git-gui | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/git-gui b/git-gui index 515cbb9dfb..c57d713144 100755 --- a/git-gui +++ b/git-gui @@ -14,7 +14,7 @@ exec wish "$0" -- "$@" set single_commit 0 set status_active 0 set diff_active 0 -set checkin_active 0 +set update_active 0 set commit_active 0 set update_index_fd {} @@ -469,7 +469,7 @@ before committing. error_popup "Unmerged files cannot be committed. File $path has merge conflicts. -You must resolve them and check the file in before committing. +You must resolve them and include the file before committing. " unlock_index return @@ -483,9 +483,9 @@ File $path cannot be committed by this program. } } if {!$files_ready} { - error_popup {No checked-in files to commit. + error_popup {No included files to commit. -You must check-in at least 1 file before you can commit. +You must include at least 1 file before you can commit. } unlock_index return @@ -1088,7 +1088,7 @@ foreach i { {__ i plain "Unmodified"} {_M i mod "Modified"} {M_ i fulltick "Checked in"} - {MM i parttick "Partially checked in"} + {MM i parttick "Partially included"} {_O o plain "Untracked"} {A_ o fulltick "Added"} @@ -1373,13 +1373,13 @@ proc do_rescan {} { update_status } -proc do_checkin_all {} { - global checkin_active ui_status_value +proc do_include_all {} { + global update_active ui_status_value - if {$checkin_active || ![lock_index begin-update]} return + if {$update_active || ![lock_index begin-update]} return - set checkin_active 1 - set ui_status_value {Checking in all files...} + set update_active 1 + set ui_status_value {Including all modified files...} after 1 { with_update_index { foreach path [array names file_states] { @@ -1393,7 +1393,7 @@ proc do_checkin_all {} { } } } - set checkin_active 0 + set update_active 0 set ui_status_value {Ready.} } } @@ -1503,8 +1503,8 @@ lappend disable_on_lock \ -font $mainfont lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Check-in All Files} \ - -command do_checkin_all \ +.mbar.commit add command -label {Include All Files} \ + -command do_include_all \ -accelerator $M1T-U \ -font $mainfont lappend disable_on_lock \ @@ -1608,11 +1608,11 @@ button .vpane.lower.commarea.buttons.amend -text {Amend Last} \ pack .vpane.lower.commarea.buttons.amend -side top -fill x lappend disable_on_lock {.vpane.lower.commarea.buttons.amend conf -state} -button .vpane.lower.commarea.buttons.ciall -text {Check-in All} \ - -command do_checkin_all \ +button .vpane.lower.commarea.buttons.incall -text {Include All} \ + -command do_include_all \ -font $mainfont -pack .vpane.lower.commarea.buttons.ciall -side top -fill x -lappend disable_on_lock {.vpane.lower.commarea.buttons.ciall conf -state} +pack .vpane.lower.commarea.buttons.incall -side top -fill x +lappend disable_on_lock {.vpane.lower.commarea.buttons.incall conf -state} button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \ -command do_signoff \ @@ -1723,8 +1723,8 @@ bind all <$M1B-Key-r> do_rescan bind all <$M1B-Key-R> do_rescan bind . <$M1B-Key-s> do_signoff bind . <$M1B-Key-S> do_signoff -bind . <$M1B-Key-u> do_checkin_all -bind . <$M1B-Key-U> do_checkin_all +bind . <$M1B-Key-u> do_include_all +bind . <$M1B-Key-U> do_include_all bind . <$M1B-Key-Return> do_commit bind all <$M1B-Key-q> do_quit bind all <$M1B-Key-Q> do_quit -- cgit v1.2.3 From d4ab2035ca64ccb380c42a73f1ce0aefe1669a39 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 8 Nov 2006 22:51:09 -0500 Subject: git-gui: Show only the abbreviated SHA1 after committing. There's really no great reason to show the entire commit object id within the GUI, especially if the user is unable to copy and paste it into another interface such as gitk or a terminal window. So we'll just show them the first 8 digits and hope that is unique within their repository. Signed-off-by: Shawn O. Pearce --- git-gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui b/git-gui index c57d713144..1b1ffee5ea 100755 --- a/git-gui +++ b/git-gui @@ -626,7 +626,7 @@ proc commit_stage2 {fd_wt curHEAD msg} { set HEAD $cmt_id set PARENT $cmt_id unlock_index - update_status "Changes committed as $cmt_id." + update_status "Changes committed as [string range $cmt_id 0 7]." } ###################################################################### -- cgit v1.2.3 From 97bf01c46526a680fb04ac88cb0ecaefbd3b4d7f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 8 Nov 2006 23:05:46 -0500 Subject: git-gui: Cache the GIT_COMMITTER_IDENT value on first sign-off. Caching the Signed-Off-By line isn't very important (as its not performance critical). The major improvement here is that we now report an error to the user if we can't obtain their name from git-var. Signed-off-by: Shawn O. Pearce --- git-gui | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/git-gui b/git-gui index 1b1ffee5ea..ad3aa0727c 100755 --- a/git-gui +++ b/git-gui @@ -1398,19 +1398,28 @@ proc do_include_all {} { } } +set GIT_COMMITTER_IDENT {} + proc do_signoff {} { - global ui_comm + global ui_comm GIT_COMMITTER_IDENT - catch { - set me [exec git var GIT_COMMITTER_IDENT] - if {[regexp {(.*) [0-9]+ [-+0-9]+$} $me me name]} { - set str "Signed-off-by: $name" - if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} { - $ui_comm insert end "\n" - $ui_comm insert end $str - $ui_comm see end - } + if {$GIT_COMMITTER_IDENT == {}} { + if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} { + error_popup "Unable to obtain your identity:\n$err" + return } + if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \ + $me me GIT_COMMITTER_IDENT]} { + error_popup "Invalid GIT_COMMITTER_IDENT:\n$me" + return + } + } + + set str "Signed-off-by: $GIT_COMMITTER_IDENT" + if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} { + $ui_comm insert end "\n" + $ui_comm insert end $str + $ui_comm see end } } -- cgit v1.2.3 From 2d19516db4a6807e817879e6d30fd3137a4a7817 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 8 Nov 2006 23:42:51 -0500 Subject: git-gui: Save window geometry to .git/config during exit. I started to find it very annoying that my test application kept opening at the wrong location on my desktop, so now we save the basic window geometry and sash positions into the config file as gui.geometry. Signed-off-by: Shawn O. Pearce --- git-gui | 149 ++++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 88 insertions(+), 61 deletions(-) diff --git a/git-gui b/git-gui index ad3aa0727c..aa73aa3deb 100755 --- a/git-gui +++ b/git-gui @@ -7,6 +7,86 @@ exec wish "$0" -- "$@" # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. +###################################################################### +## +## config + +proc load_repo_config {} { + global repo_config + global cfg_trust_mtime + + array unset repo_config + catch { + set fd_rc [open "| git repo-config --list" r] + while {[gets $fd_rc line] >= 0} { + if {[regexp {^([^=]+)=(.*)$} $line line name value]} { + lappend repo_config($name) $value + } + } + close $fd_rc + } + + if {[catch {set cfg_trust_mtime \ + [lindex $repo_config(gui.trustmtime) 0] + }]} { + set cfg_trust_mtime false + } +} + +proc save_my_config {} { + global repo_config + global cfg_trust_mtime + + if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} { + set rc_trustMTime [list false] + } + if {$cfg_trust_mtime != [lindex $rc_trustMTime 0]} { + exec git repo-config gui.trustMTime $cfg_trust_mtime + set repo_config(gui.trustmtime) [list $cfg_trust_mtime] + } + + set cfg_geometry [list \ + [wm geometry .] \ + [.vpane sash coord 0] \ + [.vpane.files sash coord 0] \ + ] + if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { + set rc_geometry [list [list]] + } + if {$cfg_geometry != [lindex $rc_geometry 0]} { + exec git repo-config gui.geometry $cfg_geometry + set repo_config(gui.geometry) [list $cfg_geometry] + } +} + +###################################################################### +## +## repository setup + +set appname [lindex [file split $argv0] end] +set gitdir {} +set GIT_COMMITTER_IDENT {} + +if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} { + show_msg {} . "Cannot find the git directory: $err" + exit 1 +} +if {$cdup != ""} { + cd $cdup +} +unset cdup + +if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { + show_msg {} . "Cannot find the git directory: $err" + exit 1 +} + +if {$appname == {git-citool}} { + set single_commit 1 +} + +load_repo_config + ###################################################################### ## ## task management @@ -903,39 +983,7 @@ proc toggle_mode {path} { ###################################################################### ## -## config (fetch push pull) - -proc load_repo_config {} { - global repo_config - global cfg_trust_mtime - - array unset repo_config - catch { - set fd_rc [open "| git repo-config --list" r] - while {[gets $fd_rc line] >= 0} { - if {[regexp {^([^=]+)=(.*)$} $line line name value]} { - lappend repo_config($name) $value - } - } - close $fd_rc - } - - if {[catch {set cfg_trust_mtime $repo_config(gui.trustmtime)}]} { - set cfg_trust_mtime false - } -} - -proc save_my_config {} { - global repo_config - global cfg_trust_mtime - - if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} { - set rc_trustMTime false - } - if {$cfg_trust_mtime != $rc_trustMTime} { - exec git repo-config gui.trustMTime $cfg_trust_mtime - } -} +## remote management proc load_all_remotes {} { global gitdir all_remotes repo_config @@ -1398,8 +1446,6 @@ proc do_include_all {} { } } -set GIT_COMMITTER_IDENT {} - proc do_signoff {} { global ui_comm GIT_COMMITTER_IDENT @@ -1724,6 +1770,13 @@ label .status -textvariable ui_status_value \ -font $mainfont pack .status -anchor w -side bottom -fill x +# -- Load geometry +catch { +wm geometry . [lindex $repo_config(gui.geometry) 0 0] +eval .vpane sash place 0 [lindex $repo_config(gui.geometry) 0 1] +eval .vpane.files sash place 0 [lindex $repo_config(gui.geometry) 0 2] +} + # -- Key Bindings bind $ui_comm <$M1B-Key-Return> {do_commit;break} bind . do_quit @@ -1746,34 +1799,8 @@ foreach i [list $ui_index $ui_other] { } unset i M1B M1T -###################################################################### -## -## main - -set appname [lindex [file split $argv0] end] -set gitdir {} - -if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} { - show_msg {} . "Cannot find the git directory: $err" - exit 1 -} -if {$cdup != ""} { - cd $cdup -} -unset cdup - -if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { - show_msg {} . "Cannot find the git directory: $err" - exit 1 -} - -if {$appname == {git-citool}} { - set single_commit 1 -} - wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm -load_repo_config load_all_remotes populate_remote_menu .mbar.fetch From fetch_from populate_remote_menu .mbar.push To push_to -- cgit v1.2.3 From 49b86f010c3f2d6576a8fba45c5c6b9a23f76fa0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 15:16:01 -0500 Subject: git-gui: Change accelerator for "Include All" to M1-I. Now that we call the update-index all files action "Include All" it makes more sense to make this M1-I (so Control-I or Command-I depending on platform) than M1-U, which stood for update but is somewhat confusing to users. Signed-off-by: Shawn O. Pearce --- git-gui | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index aa73aa3deb..20c36c2d2e 100755 --- a/git-gui +++ b/git-gui @@ -1560,7 +1560,7 @@ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Include All Files} \ -command do_include_all \ - -accelerator $M1T-U \ + -accelerator $M1T-I \ -font $mainfont lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] @@ -1779,14 +1779,17 @@ eval .vpane.files sash place 0 [lindex $repo_config(gui.geometry) 0 2] # -- Key Bindings bind $ui_comm <$M1B-Key-Return> {do_commit;break} +bind $ui_comm <$M1B-Key-i> {do_include_all;break} +bind $ui_comm <$M1B-Key-I> {do_include_all;break} + bind . do_quit bind all do_rescan bind all <$M1B-Key-r> do_rescan bind all <$M1B-Key-R> do_rescan bind . <$M1B-Key-s> do_signoff bind . <$M1B-Key-S> do_signoff -bind . <$M1B-Key-u> do_include_all -bind . <$M1B-Key-U> do_include_all +bind . <$M1B-Key-i> do_include_all +bind . <$M1B-Key-I> do_include_all bind . <$M1B-Key-Return> do_commit bind all <$M1B-Key-q> do_quit bind all <$M1B-Key-Q> do_quit -- cgit v1.2.3 From 9861671de26bf7cd41f591bd2099ac299349f284 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 15:51:41 -0500 Subject: git-gui: Created edit menu and basic editing bindings. Users have come to expect basic editing features within their applications, such as cut/copy/paste/undo/redo/select-all. I found these features to be lacking in git-gui so now we have them. I also added basic keyboard bindings for the diff viewing area so that the arrow keys move around single units (lines or columns) and the M1-X/C keys will copy the selected text and the M1-A key will select the entire diff. Signed-off-by: Shawn O. Pearce --- git-gui | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 20c36c2d2e..640519c204 100755 --- a/git-gui +++ b/git-gui @@ -1525,6 +1525,7 @@ default {set M1B M1; set M1T M1} # -- Menu Bar menu .mbar -tearoff 0 .mbar add cascade -label Project -menu .mbar.project +.mbar add cascade -label Edit -menu .mbar.edit .mbar add cascade -label Commit -menu .mbar.commit .mbar add cascade -label Fetch -menu .mbar.fetch .mbar add cascade -label Pull -menu .mbar.pull @@ -1545,6 +1546,40 @@ menu .mbar.project -accelerator $M1T-Q \ -font $mainfont +# -- Edit Menu +# +menu .mbar.edit +.mbar.edit add command -label Undo \ + -command {catch {[focus] edit undo}} \ + -accelerator $M1T-Z \ + -font $mainfont +.mbar.edit add command -label Redo \ + -command {catch {[focus] edit redo}} \ + -accelerator $M1T-Y \ + -font $mainfont +.mbar.edit add separator +.mbar.edit add command -label Cut \ + -command {catch {tk_textCut [focus]}} \ + -accelerator $M1T-X \ + -font $mainfont +.mbar.edit add command -label Copy \ + -command {catch {tk_textCopy [focus]}} \ + -accelerator $M1T-C \ + -font $mainfont +.mbar.edit add command -label Paste \ + -command {catch {tk_textPaste [focus]; [focus] see insert}} \ + -accelerator $M1T-V \ + -font $mainfont +.mbar.edit add command -label Delete \ + -command {catch {[focus] delete sel.first sel.last}} \ + -accelerator Del \ + -font $mainfont +.mbar.edit add separator +.mbar.edit add command -label {Select All} \ + -command {catch {[focus] tag add sel 0.0 end}} \ + -accelerator $M1T-A \ + -font $mainfont + # -- Commit Menu menu .mbar.commit .mbar.commit add command -label Rescan \ @@ -1586,7 +1621,8 @@ menu .mbar.push # -- Options Menu menu .mbar.options -.mbar.options add checkbutton -label {Trust File Modification Timestamps} \ +.mbar.options add checkbutton \ + -label {Trust File Modification Timestamps} \ -offvalue false \ -onvalue true \ -variable cfg_trust_mtime @@ -1696,6 +1732,8 @@ trace add variable commit_type write {uplevel #0 { * {$ui_coml conf -text {Commit Message:}} }} text $ui_comm -background white -borderwidth 1 \ + -undo true \ + -autoseparators true \ -relief sunken \ -width 75 -height 9 -wrap none \ -font $difffont \ @@ -1781,6 +1819,27 @@ eval .vpane.files sash place 0 [lindex $repo_config(gui.geometry) 0 2] bind $ui_comm <$M1B-Key-Return> {do_commit;break} bind $ui_comm <$M1B-Key-i> {do_include_all;break} bind $ui_comm <$M1B-Key-I> {do_include_all;break} +bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break} +bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break} +bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break} +bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break} +bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break} +bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break} +bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break} +bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break} + +bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break} +bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break} +bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break} +bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break} +bind $ui_diff <$M1B-Key-v> {break} +bind $ui_diff <$M1B-Key-V> {break} +bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break} +bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break} +bind $ui_diff {%W yview scroll -1 units} +bind $ui_diff {%W yview scroll 1 units} +bind $ui_diff {%W xview scroll -1 units} +bind $ui_diff {%W xview scroll 1 units} bind . do_quit bind all do_rescan -- cgit v1.2.3 From b2c6fcf197fd09a6fedb39e4d8d13e6fdc2df4d4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 16:16:25 -0500 Subject: git-gui: Clear undo/redo stack when loading a message file from disk. If we load a message file (e.g. MERGE_MSG) or we have just finished making a commit and are clearing out the commit buffer we should also clear out the undo/redo stack associated with that buffer. The prior undo/redo stack has no associated with the new content and therefore is not useful to the user. Also modified the sign-off operation to perform the entire update in a single undo/redo operation, allowing the user to undo the signoff in case they didn't actually want to do that. I also noticed what may be a crash on Windows related to the up and down arrow keys navigating within the diff viewer. Since I got back no stack trace (just an application exit with a loss of the commit message) I suspect that the binding to scroll the text widget crashed with an error and the wish process just terminated. So now we catch (and ignore) any sort of error related to the arrow keys in the diff viewer. Signed-off-by: Shawn O. Pearce --- git-gui | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/git-gui b/git-gui index 640519c204..540d56397a 100755 --- a/git-gui +++ b/git-gui @@ -179,6 +179,7 @@ proc update_status {{final Ready.}} { } elseif {[load_message SQUASH_MSG]} { } $ui_comm edit modified false + $ui_comm edit reset } if {$cfg_trust_mtime == {true}} { @@ -503,6 +504,7 @@ proc load_last_commit {} { $ui_comm delete 0.0 end $ui_comm insert end $msg $ui_comm edit modified false + $ui_comm edit reset update_status } else { error_popup {You can't amend a merge commit.} @@ -698,6 +700,7 @@ proc commit_stage2 {fd_wt curHEAD msg} { $ui_comm delete 0.0 end $ui_comm edit modified false + $ui_comm edit reset if {$single_commit} do_quit @@ -1463,8 +1466,9 @@ proc do_signoff {} { set str "Signed-off-by: $GIT_COMMITTER_IDENT" if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} { - $ui_comm insert end "\n" - $ui_comm insert end $str + $ui_comm edit separator + $ui_comm insert end "\n$str" + $ui_comm edit separator $ui_comm see end } } @@ -1733,6 +1737,7 @@ trace add variable commit_type write {uplevel #0 { }} text $ui_comm -background white -borderwidth 1 \ -undo true \ + -maxundo 20 \ -autoseparators true \ -relief sunken \ -width 75 -height 9 -wrap none \ @@ -1836,10 +1841,10 @@ bind $ui_diff <$M1B-Key-v> {break} bind $ui_diff <$M1B-Key-V> {break} bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break} bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break} -bind $ui_diff {%W yview scroll -1 units} -bind $ui_diff {%W yview scroll 1 units} -bind $ui_diff {%W xview scroll -1 units} -bind $ui_diff {%W xview scroll 1 units} +bind $ui_diff {catch {%W yview scroll -1 units};break} +bind $ui_diff {catch {%W yview scroll 1 units};break} +bind $ui_diff {catch {%W xview scroll -1 units};break} +bind $ui_diff {catch {%W xview scroll 1 units};break} bind . do_quit bind all do_rescan -- cgit v1.2.3 From 7d9e1d5e8a398a9ed3b782e7c2b49d1dcb5f53f7 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 17:46:04 -0500 Subject: git-gui: Updated TODO list now that geometry is stored. Signed-off-by: Shawn O. Pearce --- TODO | 3 --- 1 file changed, 3 deletions(-) diff --git a/TODO b/TODO index 0b1fa2ca59..bb40a13bee 100644 --- a/TODO +++ b/TODO @@ -22,9 +22,6 @@ Items outstanding: * Delete a local branch. - * Store user preferences (like font, window layout) in global - repo-config. - * Allow user to define keyboard shortcuts for frequently used fetch or merge operations. Or maybe just define a keyboard shortcut for default fetch/default merge of current branch is enough; -- cgit v1.2.3 From 03e4ec5364f955f5e1af0775166c679ed60f7fb4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 17:52:16 -0500 Subject: git-gui: Always indicate the file in the diff viewer. When we did a rescan to update the file lists we lost the tag which indicated which file was currently in the diff viewer. This occurs because we delete every line from the two file list boxes (thus removing the tag) and then redisplay the diff in the diff viewer but then fail to restore the tag in the file list. Now we restore that tag by searching for the file in the file lists and adding the tag back when the diff viewer displays something. We also no longer obtain the file path directly from the file list text box. Instead we now keep two Tcl lists, one for each file list, holding the file names in sorted order. These lists can be searched with the native [lsearch -sorted] operation (which should be faster than our crude bsearch) or can be quickly accessed by index to return the file path. This should help make things safer should we ever be given a file name which contains an LF within it (as that would span two lines in the file list, not one). Signed-off-by: Shawn O. Pearce --- git-gui | 72 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/git-gui b/git-gui index 540d56397a..f36594eea0 100755 --- a/git-gui +++ b/git-gui @@ -150,7 +150,7 @@ proc repository_state {hdvar ctvar} { proc update_status {{final Ready.}} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm - global status_active file_states + global status_active file_states file_lists global cfg_trust_mtime if {$status_active || ![lock_index read]} return @@ -166,6 +166,7 @@ proc update_status {{final Ready.}} { } array unset file_states + array unset file_lists foreach w [list $ui_index $ui_other] { $w conf -state normal $w delete 0.0 end @@ -196,7 +197,7 @@ proc update_status {{final Ready.}} { proc update_status_stage2 {fd final} { global gitdir PARENT commit_type global ui_index ui_other ui_status_value ui_comm - global status_active file_states + global status_active global buf_rdi buf_rdf buf_rlo if {$fd != {}} { @@ -346,22 +347,40 @@ proc status_eof {fd buf final} { ## diff proc clear_diff {} { - global ui_diff ui_fname_value ui_fstatus_value + global ui_diff ui_fname_value ui_fstatus_value ui_index ui_other $ui_diff conf -state normal $ui_diff delete 0.0 end $ui_diff conf -state disabled + set ui_fname_value {} set ui_fstatus_value {} + + $ui_index tag remove in_diff 0.0 end + $ui_other tag remove in_diff 0.0 end } -proc show_diff {path} { - global file_states PARENT diff_3way diff_active +proc show_diff {path {w {}} {lno {}}} { + global file_states file_lists + global PARENT diff_3way diff_active global ui_diff ui_fname_value ui_fstatus_value ui_status_value if {$diff_active || ![lock_index read]} return clear_diff + if {$w == {} || $lno == {}} { + foreach w [array names file_lists] { + set lno [lsearch -sorted $file_lists($w) $path] + if {$lno >= 0} { + incr lno + break + } + } + } + if {$w != {} && $lno >= 1} { + $w tag add in_diff $lno.0 [expr $lno + 1].0 + } + set s $file_states($path) set m [lindex $s 0] set diff_3way 0 @@ -823,27 +842,6 @@ proc mapdesc {state path} { return $r } -proc bsearch {w path} { - set hi [expr [lindex [split [$w index end] .] 0] - 2] - if {$hi == 0} { - return -1 - } - set lo 0 - while {$lo < $hi} { - set mi [expr [expr $lo + $hi] / 2] - set ti [expr $mi + 1] - set cmp [string compare [$w get $ti.1 $ti.end] $path] - if {$cmp < 0} { - set lo $ti - } elseif {$cmp == 0} { - return $mi - } else { - set hi $mi - } - } - return -[expr $lo + 1] -} - set next_icon_id 0 proc merge_state {path new_state} { @@ -877,7 +875,8 @@ proc merge_state {path new_state} { } proc display_file {path state} { - global ui_index ui_other file_states status_active + global ui_index ui_other + global file_states file_lists status_active set old_m [merge_state $path $state] if {$status_active} return @@ -889,7 +888,7 @@ proc display_file {path state} { set new_icon [mapicon $new_m $path] if {$new_w != $old_w} { - set lno [bsearch $old_w $path] + set lno [lsearch -sorted $file_lists($old_w) $path] if {$lno >= 0} { incr lno $old_w conf -state normal @@ -897,7 +896,10 @@ proc display_file {path state} { $old_w conf -state disabled } - set lno [expr abs([bsearch $new_w $path] + 1) + 1] + lappend file_lists($new_w) $path + set file_lists($new_w) [lsort $file_lists($new_w)] + set lno [lsearch -sorted $file_lists($new_w) $path] + incr lno $new_w conf -state normal $new_w image create $lno.0 \ -align center -padx 5 -pady 1 \ @@ -913,7 +915,7 @@ proc display_file {path state} { } proc display_all_files {} { - global ui_index ui_other file_states + global ui_index ui_other file_states file_lists $ui_index conf -state normal $ui_other conf -state normal @@ -922,6 +924,7 @@ proc display_all_files {} { set s $file_states($path) set m [lindex $s 0] set w [mapcol $m $path] + lappend file_lists($w) $path $w image create end \ -align center -padx 5 -pady 1 \ -name [lindex $s 1] \ @@ -1484,19 +1487,16 @@ proc do_commit {} { # shift == 1: left click # 3: right click proc click {w x y shift wx wy} { - global ui_index ui_other + global ui_index ui_other file_lists set pos [split [$w index @$x,$y] .] set lno [lindex $pos 0] set col [lindex $pos 1] - set path [$w get $lno.1 $lno.end] + set path [lindex $file_lists($w) [expr $lno - 1]] if {$path == {}} return if {$col > 0 && $shift == 1} { - $ui_index tag remove in_diff 0.0 end - $ui_other tag remove in_diff 0.0 end - $w tag add in_diff $lno.0 [expr $lno + 1].0 - show_diff $path + show_diff $path $w $lno } } -- cgit v1.2.3 From 68e009dec44a023efd6f6fcd61c1583b23b04f32 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 17:59:34 -0500 Subject: git-gui: Correctly handle files containing LF in their name. If we are given a file whose path name contains an LF (\n) we now escape it by inserting the common escape string \n instead of the LF character whenever we display the name in the UI. This way the text fields don't start to span multiple lines just to display one file, and it keeps the line numbers correct within the file lists. Signed-off-by: Shawn O. Pearce --- git-gui | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/git-gui b/git-gui index f36594eea0..ab64684c91 100755 --- a/git-gui +++ b/git-gui @@ -385,9 +385,9 @@ proc show_diff {path {w {}} {lno {}}} { set m [lindex $s 0] set diff_3way 0 set diff_active 1 - set ui_fname_value $path + set ui_fname_value [escape_path $path] set ui_fstatus_value [mapdesc $m $path] - set ui_status_value "Loading diff of $path..." + set ui_status_value "Loading diff of [escape_path $path]..." set cmd [list | git diff-index -p $PARENT -- $path] switch $m { @@ -404,7 +404,7 @@ proc show_diff {path {w {}} {lno {}}} { } err ]} { set diff_active 0 unlock_index - set ui_status_value "Unable to display $path" + set ui_status_value "Unable to display [escape_path $path]" error_popup "Error loading file:\n$err" return } @@ -421,7 +421,7 @@ proc show_diff {path {w {}} {lno {}}} { if {[catch {set fd [open $cmd r]} err]} { set diff_active 0 unlock_index - set ui_status_value "Unable to display $path" + set ui_status_value "Unable to display [escape_path $path]" error_popup "Error loading diff:\n$err" return } @@ -569,7 +569,7 @@ before committing. U* { error_popup "Unmerged files cannot be committed. -File $path has merge conflicts. +File [escape_path $path] has merge conflicts. You must resolve them and include the file before committing. " unlock_index @@ -578,7 +578,7 @@ You must resolve them and include the file before committing. default { error_popup "Unknown file state [lindex $s 0] detected. -File $path cannot be committed by this program. +File [escape_path $path] cannot be committed by this program. " } } @@ -842,6 +842,11 @@ proc mapdesc {state path} { return $r } +proc escape_path {path} { + regsub -all "\n" $path "\\n" path + return $path +} + set next_icon_id 0 proc merge_state {path new_state} { @@ -905,7 +910,7 @@ proc display_file {path state} { -align center -padx 5 -pady 1 \ -name [lindex $s 1] \ -image $new_icon - $new_w insert $lno.1 "$path\n" + $new_w insert $lno.1 "[escape_path $path]\n" $new_w conf -state disabled } elseif {$new_icon != [mapicon $old_m $path]} { $new_w conf -state normal @@ -929,7 +934,7 @@ proc display_all_files {} { -align center -padx 5 -pady 1 \ -name [lindex $s 1] \ -image [mapicon $m $path] - $w insert end "$path\n" + $w insert end "[escape_path $path]\n" } $ui_index conf -state disabled -- cgit v1.2.3 From 7f1df79bb7bad696d1d8965da3937edeb1cfe9f6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 18:38:00 -0500 Subject: git-gui: Efficiently update the UI after committing. When we commit we know that whatever was in the index went as part of the commit. Since we generally assume that the user does not update the index except through our user interface we can be reasonably certain that any file which was marked as A/M/D in the index will have had that A/M/D state changed to an _ (not different) by the commit. We can use this knowledge to update the user interface post commit by simply updating the index part of the file state of all files whose index state was A/M/D to _ and then removing any file memory any which wound up with a final state of __ (not different anywhere). Finally we redraw the file lists and update the diff view. Signed-off-by: Shawn O. Pearce --- git-gui | 85 +++++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/git-gui b/git-gui index ab64684c91..2c4e97236b 100755 --- a/git-gui +++ b/git-gui @@ -150,7 +150,7 @@ proc repository_state {hdvar ctvar} { proc update_status {{final Ready.}} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm - global status_active file_states file_lists + global status_active file_states global cfg_trust_mtime if {$status_active || ![lock_index read]} return @@ -166,12 +166,6 @@ proc update_status {{final Ready.}} { } array unset file_states - array unset file_lists - foreach w [list $ui_index $ui_other] { - $w conf -state normal - $w delete 0.0 end - $w conf -state disabled - } if {![$ui_comm edit modified] || [string trim [$ui_comm get 0.0 end]] == {}} { @@ -319,25 +313,18 @@ proc read_ls_others {fd final} { } proc status_eof {fd buf final} { - global status_active $buf - global ui_fname_value ui_status_value file_states + global status_active ui_status_value + upvar $buf to_clear if {[eof $fd]} { - set $buf {} + set to_clear {} close $fd if {[incr status_active -1] == 0} { - unlock_index - display_all_files + unlock_index + reshow_diff set ui_status_value $final - - if {$ui_fname_value != {} && [array names file_states \ - -exact $ui_fname_value] != {}} { - show_diff $ui_fname_value - } else { - clear_diff - } } } } @@ -360,6 +347,17 @@ proc clear_diff {} { $ui_other tag remove in_diff 0.0 end } +proc reshow_diff {} { + global ui_fname_value ui_status_value file_states + + if {$ui_fname_value != {} && [array names file_states \ + -exact $ui_fname_value] != {}} { + show_diff $ui_fname_value + } else { + clear_diff + } +} + proc show_diff {path {w {}} {lno {}}} { global file_states file_lists global PARENT diff_3way diff_active @@ -562,11 +560,11 @@ before committing. foreach path [array names file_states] { set s $file_states($path) switch -glob -- [lindex $s 0] { - _* {continue} - A* - - D* - - M* {set files_ready 1; break} - U* { + _? {continue} + A? - + D? - + M? {set files_ready 1; break} + U? { error_popup "Unmerged files cannot be committed. File [escape_path $path] has merge conflicts. @@ -635,8 +633,9 @@ A good commit message has the following format: } proc commit_stage2 {fd_wt curHEAD msg} { - global single_commit gitdir PARENT commit_type + global single_commit gitdir HEAD PARENT commit_type global commit_active ui_status_value ui_comm + global file_states gets $fd_wt tree_id close $fd_wt @@ -663,7 +662,7 @@ proc commit_stage2 {fd_wt curHEAD msg} { } close $fd_mh } err]} { - error_popup "Loading MERGE_HEADs failed:\n$err" + error_popup "Loading MERGE_HEAD failed:\n$err" set commit_active 0 set ui_status_value {Commit failed.} unlock_index @@ -723,12 +722,34 @@ proc commit_stage2 {fd_wt curHEAD msg} { if {$single_commit} do_quit - set commit_type {} + # -- Update status without invoking any git commands. + # set commit_active 0 + set commit_type normal set HEAD $cmt_id set PARENT $cmt_id + + foreach path [array names file_states] { + set s $file_states($path) + set m [lindex $s 0] + switch -glob -- $m { + A? - + M? - + D? {set m _[string index $m 1]} + } + + if {$m == {__}} { + unset file_states($path) + } else { + lset file_states($path) 0 $m + } + } + + display_all_files unlock_index - update_status "Changes committed as [string range $cmt_id 0 7]." + reshow_diff + set ui_status_value \ + "Changes committed as [string range $cmt_id 0 7]." } ###################################################################### @@ -925,6 +946,10 @@ proc display_all_files {} { $ui_index conf -state normal $ui_other conf -state normal + $ui_index delete 0.0 end + $ui_other delete 0.0 end + + array unset file_lists foreach path [lsort [array names file_states]] { set s $file_states($path) set m [lindex $s 0] @@ -1506,10 +1531,12 @@ proc click {w x y shift wx wy} { } proc unclick {w x y} { + global file_lists + set pos [split [$w index @$x,$y] .] set lno [lindex $pos 0] set col [lindex $pos 1] - set path [$w get $lno.1 $lno.end] + set path [lindex $file_lists($w) [expr $lno - 1]] if {$path == {}} return if {$col == 0} { -- cgit v1.2.3 From 73ad179bbbba56c053045c555b7e5f1882380533 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 18:42:42 -0500 Subject: git-gui: Use catch rather than array names to check file. When we reshow the current diff file it can be faster to just fetch the value from the file_states array than it is to ask for all paths whose name exactly matches the one we want to show. This is because [array names -exact] is O(n) in the number of files. Signed-off-by: Shawn O. Pearce --- git-gui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index 2c4e97236b..e16fcf7346 100755 --- a/git-gui +++ b/git-gui @@ -350,11 +350,11 @@ proc clear_diff {} { proc reshow_diff {} { global ui_fname_value ui_status_value file_states - if {$ui_fname_value != {} && [array names file_states \ - -exact $ui_fname_value] != {}} { - show_diff $ui_fname_value - } else { + if {$ui_fname_value == {} + || [catch {set s $file_states($ui_fname_value)}]} { clear_diff + } else { + show_diff $ui_fname_value } } -- cgit v1.2.3 From 3963678da9e78e87fa9b57f2f3ad75ba1c70e589 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 18:46:52 -0500 Subject: git-gui: Rename difffont/mainfont variables. I found difffont to be a very awkward varible name, due to the use of three f's in a row. So use easier to read variable names. Signed-off-by: Shawn O. Pearce --- git-gui | 110 ++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/git-gui b/git-gui index e16fcf7346..e30a114439 100755 --- a/git-gui +++ b/git-gui @@ -1044,17 +1044,17 @@ proc load_all_remotes {} { } proc populate_remote_menu {m pfx op} { - global all_remotes mainfont + global all_remotes font_ui foreach remote $all_remotes { $m add command -label "$pfx $remote..." \ -command [list $op $remote] \ - -font $mainfont + -font $font_ui } } proc populate_pull_menu {m} { - global gitdir repo_config all_remotes mainfont disable_on_lock + global gitdir repo_config all_remotes font_ui disable_on_lock foreach remote $all_remotes { set rb {} @@ -1082,7 +1082,7 @@ proc populate_pull_menu {m} { $m add command \ -label "Branch $rb_short from $remote..." \ -command [list pull_remote $remote $rb] \ - -font $mainfont + -font $font_ui lappend disable_on_lock \ [list $m entryconf [$m index last] -state] } @@ -1212,13 +1212,13 @@ proc error_popup {msg} { } proc show_msg {w top msg} { - global gitdir appname mainfont + global gitdir appname font_ui message $w.m -text $msg -justify left -aspect 400 pack $w.m -side top -fill x -padx 5 -pady 10 button $w.ok -text OK \ -width 15 \ - -font $mainfont \ + -font $font_ui \ -command "destroy $top" pack $w.ok -side bottom bind $top "grab $top; focus $top" @@ -1230,7 +1230,7 @@ proc show_msg {w top msg} { } proc hook_failed_popup {hook msg} { - global gitdir mainfont difffont appname + global gitdir font_ui font_diff appname set w .hookfail toplevel $w @@ -1240,18 +1240,18 @@ proc hook_failed_popup {hook msg} { label $w.m.l1 -text "$hook hook failed:" \ -anchor w \ -justify left \ - -font [concat $mainfont bold] + -font [concat $font_ui bold] text $w.m.t \ -background white -borderwidth 1 \ -relief sunken \ -width 80 -height 10 \ - -font $difffont \ + -font $font_diff \ -yscrollcommand [list $w.m.sby set] label $w.m.l2 \ -text {You must correct the above errors before committing.} \ -anchor w \ -justify left \ - -font [concat $mainfont bold] + -font [concat $font_ui bold] 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 @@ -1264,7 +1264,7 @@ proc hook_failed_popup {hook msg} { button $w.ok -text OK \ -width 15 \ - -font $mainfont \ + -font $font_ui \ -command "destroy $w" pack $w.ok -side bottom @@ -1287,7 +1287,7 @@ proc new_console {short_title long_title} { proc console_init {w} { global console_cr console_data - global gitdir appname mainfont difffont + global gitdir appname font_ui font_diff set console_cr($w) 1.0 toplevel $w @@ -1295,17 +1295,17 @@ proc console_init {w} { label $w.m.l1 -text "[lindex $console_data($w) 1]:" \ -anchor w \ -justify left \ - -font [concat $mainfont bold] + -font [concat $font_ui bold] text $w.m.t \ -background white -borderwidth 1 \ -relief sunken \ -width 80 -height 10 \ - -font $difffont \ + -font $font_diff \ -state disabled \ -yscrollcommand [list $w.m.sby set] label $w.m.s -anchor w \ -justify left \ - -font [concat $mainfont bold] + -font [concat $font_ui bold] scrollbar $w.m.sby -command [list $w.m.t yview] pack $w.m.l1 -side top -fill x pack $w.m.s -side bottom -fill x @@ -1315,7 +1315,7 @@ proc console_init {w} { button $w.ok -text {Running...} \ -width 15 \ - -font $mainfont \ + -font $font_ui \ -state disabled \ -command "destroy $w" pack $w.ok -side bottom @@ -1548,8 +1548,8 @@ proc unclick {w x y} { ## ## ui init -set mainfont {Helvetica 10} -set difffont {Courier 10} +set font_ui {Helvetica 10} +set font_diff {Courier 10} set maincursor [. cget -cursor] switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" { @@ -1573,14 +1573,14 @@ menu .mbar -tearoff 0 menu .mbar.project .mbar.project add command -label Visualize \ -command do_gitk \ - -font $mainfont + -font $font_ui .mbar.project add command -label {Repack Database} \ -command do_repack \ - -font $mainfont + -font $font_ui .mbar.project add command -label Quit \ -command do_quit \ -accelerator $M1T-Q \ - -font $mainfont + -font $font_ui # -- Edit Menu # @@ -1588,61 +1588,61 @@ menu .mbar.edit .mbar.edit add command -label Undo \ -command {catch {[focus] edit undo}} \ -accelerator $M1T-Z \ - -font $mainfont + -font $font_ui .mbar.edit add command -label Redo \ -command {catch {[focus] edit redo}} \ -accelerator $M1T-Y \ - -font $mainfont + -font $font_ui .mbar.edit add separator .mbar.edit add command -label Cut \ -command {catch {tk_textCut [focus]}} \ -accelerator $M1T-X \ - -font $mainfont + -font $font_ui .mbar.edit add command -label Copy \ -command {catch {tk_textCopy [focus]}} \ -accelerator $M1T-C \ - -font $mainfont + -font $font_ui .mbar.edit add command -label Paste \ -command {catch {tk_textPaste [focus]; [focus] see insert}} \ -accelerator $M1T-V \ - -font $mainfont + -font $font_ui .mbar.edit add command -label Delete \ -command {catch {[focus] delete sel.first sel.last}} \ -accelerator Del \ - -font $mainfont + -font $font_ui .mbar.edit add separator .mbar.edit add command -label {Select All} \ -command {catch {[focus] tag add sel 0.0 end}} \ -accelerator $M1T-A \ - -font $mainfont + -font $font_ui # -- Commit Menu menu .mbar.commit .mbar.commit add command -label Rescan \ -command do_rescan \ -accelerator F5 \ - -font $mainfont + -font $font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Amend Last Commit} \ -command do_amend_last \ - -font $mainfont + -font $font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Include All Files} \ -command do_include_all \ -accelerator $M1T-I \ - -font $mainfont + -font $font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Sign Off} \ -command do_signoff \ -accelerator $M1T-S \ - -font $mainfont + -font $font_ui .mbar.commit add command -label Commit \ -command do_commit \ -accelerator $M1T-Return \ - -font $mainfont + -font $font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] @@ -1673,10 +1673,10 @@ pack .vpane -anchor n -side top -fill both -expand 1 frame .vpane.files.index -height 100 -width 400 label .vpane.files.index.title -text {Modified Files} \ -background green \ - -font $mainfont + -font $font_ui text $ui_index -background white -borderwidth 0 \ -width 40 -height 10 \ - -font $mainfont \ + -font $font_ui \ -yscrollcommand {.vpane.files.index.sb set} \ -cursor $maincursor \ -state disabled @@ -1690,10 +1690,10 @@ pack $ui_index -side left -fill both -expand 1 frame .vpane.files.other -height 100 -width 100 label .vpane.files.other.title -text {Untracked Files} \ -background red \ - -font $mainfont + -font $font_ui text $ui_other -background white -borderwidth 0 \ -width 40 -height 10 \ - -font $mainfont \ + -font $font_ui \ -yscrollcommand {.vpane.files.other.sb set} \ -cursor $maincursor \ -state disabled @@ -1703,8 +1703,8 @@ pack .vpane.files.other.sb -side right -fill y pack $ui_other -side left -fill both -expand 1 .vpane.files add .vpane.files.other -sticky nsew -$ui_index tag conf in_diff -font [concat $mainfont bold] -$ui_other tag conf in_diff -font [concat $mainfont bold] +$ui_index tag conf in_diff -font [concat $font_ui bold] +$ui_other tag conf in_diff -font [concat $font_ui bold] # -- Diff and Commit Area frame .vpane.lower -height 400 -width 400 @@ -1719,36 +1719,36 @@ frame .vpane.lower.commarea.buttons label .vpane.lower.commarea.buttons.l -text {} \ -anchor w \ -justify left \ - -font $mainfont + -font $font_ui pack .vpane.lower.commarea.buttons.l -side top -fill x pack .vpane.lower.commarea.buttons -side left -fill y button .vpane.lower.commarea.buttons.rescan -text {Rescan} \ -command do_rescan \ - -font $mainfont + -font $font_ui pack .vpane.lower.commarea.buttons.rescan -side top -fill x lappend disable_on_lock {.vpane.lower.commarea.buttons.rescan conf -state} button .vpane.lower.commarea.buttons.amend -text {Amend Last} \ -command do_amend_last \ - -font $mainfont + -font $font_ui pack .vpane.lower.commarea.buttons.amend -side top -fill x lappend disable_on_lock {.vpane.lower.commarea.buttons.amend conf -state} button .vpane.lower.commarea.buttons.incall -text {Include All} \ -command do_include_all \ - -font $mainfont + -font $font_ui pack .vpane.lower.commarea.buttons.incall -side top -fill x lappend disable_on_lock {.vpane.lower.commarea.buttons.incall conf -state} button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \ -command do_signoff \ - -font $mainfont + -font $font_ui pack .vpane.lower.commarea.buttons.signoff -side top -fill x button .vpane.lower.commarea.buttons.commit -text {Commit} \ -command do_commit \ - -font $mainfont + -font $font_ui pack .vpane.lower.commarea.buttons.commit -side top -fill x lappend disable_on_lock {.vpane.lower.commarea.buttons.commit conf -state} @@ -1759,7 +1759,7 @@ set ui_coml .vpane.lower.commarea.buffer.l label $ui_coml -text {Commit Message:} \ -anchor w \ -justify left \ - -font $mainfont + -font $font_ui trace add variable commit_type write {uplevel #0 { switch -glob $commit_type \ initial {$ui_coml conf -text {Initial Commit Message:}} \ @@ -1773,7 +1773,7 @@ text $ui_comm -background white -borderwidth 1 \ -autoseparators true \ -relief sunken \ -width 75 -height 9 -wrap none \ - -font $difffont \ + -font $font_diff \ -yscrollcommand {.vpane.lower.commarea.buffer.sby set} \ -cursor $maincursor scrollbar .vpane.lower.commarea.buffer.sby -command [list $ui_comm yview] @@ -1788,21 +1788,21 @@ set ui_fstatus_value {} frame .vpane.lower.diff.header -background orange label .vpane.lower.diff.header.l1 -text {File:} \ -background orange \ - -font $mainfont + -font $font_ui label .vpane.lower.diff.header.l2 -textvariable ui_fname_value \ -background orange \ -anchor w \ -justify left \ - -font $mainfont + -font $font_ui label .vpane.lower.diff.header.l3 -text {Status:} \ -background orange \ - -font $mainfont + -font $font_ui label .vpane.lower.diff.header.l4 -textvariable ui_fstatus_value \ -background orange \ -width $max_status_desc \ -anchor w \ -justify left \ - -font $mainfont + -font $font_ui pack .vpane.lower.diff.header.l1 -side left pack .vpane.lower.diff.header.l2 -side left -fill x pack .vpane.lower.diff.header.l4 -side right @@ -1813,7 +1813,7 @@ frame .vpane.lower.diff.body set ui_diff .vpane.lower.diff.body.t text $ui_diff -background white -borderwidth 0 \ -width 80 -height 15 -wrap none \ - -font $difffont \ + -font $font_diff \ -xscrollcommand {.vpane.lower.diff.body.sbx set} \ -yscrollcommand {.vpane.lower.diff.body.sby set} \ -cursor $maincursor \ @@ -1830,10 +1830,10 @@ pack .vpane.lower.diff.body -side bottom -fill both -expand 1 $ui_diff tag conf dm -foreground red $ui_diff tag conf dp -foreground blue -$ui_diff tag conf da -font [concat $difffont bold] +$ui_diff tag conf da -font [concat $font_diff bold] $ui_diff tag conf di -foreground "#00a000" $ui_diff tag conf dni -foreground "#a000a0" -$ui_diff tag conf bold -font [concat $difffont bold] +$ui_diff tag conf bold -font [concat $font_diff bold] # -- Status Bar set ui_status_value {Initializing...} @@ -1842,7 +1842,7 @@ label .status -textvariable ui_status_value \ -justify left \ -borderwidth 1 \ -relief sunken \ - -font $mainfont + -font $font_ui pack .status -anchor w -side bottom -fill x # -- Load geometry -- cgit v1.2.3 From da5239dcab0e8f899a4e6bfb34b574f6286b9dbb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 19:03:06 -0500 Subject: git-gui: Use native tk_messageBox for errors. Rather than drawing our own toplevel for error messages we really should just use the the native tk_messageBox command to display any error messages. Major benefits for doing so are: - automatically centers over main window; - less code required on our part in git-gui; - includes a nifty error icon on most systems; - better fits the look-and-feel of the operating system. Signed-off-by: Shawn O. Pearce --- git-gui | 56 +++++++++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/git-gui b/git-gui index e30a114439..81fe38f006 100755 --- a/git-gui +++ b/git-gui @@ -7,6 +7,9 @@ exec wish "$0" -- "$@" # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. +set appname [lindex [file split $argv0] end] +set gitdir {} + ###################################################################### ## ## config @@ -59,16 +62,30 @@ proc save_my_config {} { } } +proc error_popup {msg} { + global gitdir appname + + set title $appname + if {$gitdir != {}} { + append title { (} + append title [lindex \ + [file split [file normalize [file dirname $gitdir]]] \ + end] + append title {)} + } + tk_messageBox -parent . \ + -icon error \ + -type ok \ + -title "$title: error" \ + -message $msg +} + ###################################################################### ## ## repository setup -set appname [lindex [file split $argv0] end] -set gitdir {} -set GIT_COMMITTER_IDENT {} - if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} { - show_msg {} . "Cannot find the git directory: $err" + error_popup "Cannot find the git directory:\n$err" exit 1 } if {$cdup != ""} { @@ -77,7 +94,7 @@ if {$cdup != ""} { unset cdup if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { - show_msg {} . "Cannot find the git directory: $err" + error_popup "Cannot find the git directory:\n$err" exit 1 } @@ -1204,31 +1221,6 @@ unset filemask i ## ## util -proc error_popup {msg} { - set w .error - toplevel $w - wm transient $w . - show_msg $w $w $msg -} - -proc show_msg {w top msg} { - global gitdir appname font_ui - - message $w.m -text $msg -justify left -aspect 400 - pack $w.m -side top -fill x -padx 5 -pady 10 - button $w.ok -text OK \ - -width 15 \ - -font $font_ui \ - -command "destroy $top" - pack $w.ok -side bottom - bind $top "grab $top; focus $top" - bind $top "destroy $top" - wm title $w "$appname ([lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end]): error" - tkwait window $top -} - proc hook_failed_popup {hook msg} { global gitdir font_ui font_diff appname @@ -1482,6 +1474,8 @@ proc do_include_all {} { } } +set GIT_COMMITTER_IDENT {} + proc do_signoff {} { global ui_comm GIT_COMMITTER_IDENT -- cgit v1.2.3 From 44be340e4d8d39475e86d3e00cec31fec0d0f6c1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 19:10:10 -0500 Subject: git-gui: Cleaned up error message formatting. Added an extra blank line between the first line of each error message and the rest of the message, as usually the rest of the message is coming from Tcl or is the stderr output of a git command we tried to invoke. This makes it easier to read the output (if any). Signed-off-by: Shawn O. Pearce --- git-gui | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/git-gui b/git-gui index 81fe38f006..4449c7d67e 100755 --- a/git-gui +++ b/git-gui @@ -73,7 +73,8 @@ proc error_popup {msg} { end] append title {)} } - tk_messageBox -parent . \ + tk_messageBox \ + -parent . \ -icon error \ -type ok \ -title "$title: error" \ @@ -84,8 +85,10 @@ proc error_popup {msg} { ## ## repository setup -if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} { - error_popup "Cannot find the git directory:\n$err" +if { [catch {set cdup [exec git rev-parse --show-cdup]} err] + || [catch {set gitdir [exec git rev-parse --git-dir]} err]} { + catch {wm withdraw .} + error_popup "Cannot find the git directory:\n\n$err" exit 1 } if {$cdup != ""} { @@ -93,11 +96,6 @@ if {$cdup != ""} { } unset cdup -if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} { - error_popup "Cannot find the git directory:\n$err" - exit 1 -} - if {$appname == {git-citool}} { set single_commit 1 } @@ -420,7 +418,7 @@ proc show_diff {path {w {}} {lno {}}} { set diff_active 0 unlock_index set ui_status_value "Unable to display [escape_path $path]" - error_popup "Error loading file:\n$err" + error_popup "Error loading file:\n\n$err" return } $ui_diff conf -state normal @@ -437,7 +435,7 @@ proc show_diff {path {w {}} {lno {}}} { set diff_active 0 unlock_index set ui_status_value "Unable to display [escape_path $path]" - error_popup "Error loading diff:\n$err" + error_popup "Error loading diff:\n\n$err" return } @@ -523,7 +521,7 @@ proc load_last_commit {} { set msg [string trim [read $fd]] close $fd } err]} { - error_popup "Error loading commit data for amend:\n$err" + error_popup "Error loading commit data for amend:\n\n$err" return } @@ -655,10 +653,8 @@ proc commit_stage2 {fd_wt curHEAD msg} { global file_states gets $fd_wt tree_id - close $fd_wt - - if {$tree_id == {}} { - error_popup "write-tree failed" + if {$tree_id == {} || [catch {close $fd_wt} err]} { + error_popup "write-tree failed:\n\n$err" set commit_active 0 set ui_status_value {Commit failed.} unlock_index @@ -679,7 +675,7 @@ proc commit_stage2 {fd_wt curHEAD msg} { } close $fd_mh } err]} { - error_popup "Loading MERGE_HEAD failed:\n$err" + error_popup "Loading MERGE_HEAD failed:\n\n$err" set commit_active 0 set ui_status_value {Commit failed.} unlock_index @@ -692,7 +688,7 @@ proc commit_stage2 {fd_wt curHEAD msg} { } lappend cmd << $msg if {[catch {set cmt_id [eval exec $cmd]} err]} { - error_popup "commit-tree failed:\n$err" + error_popup "commit-tree failed:\n\n$err" set commit_active 0 set ui_status_value {Commit failed.} unlock_index @@ -713,7 +709,7 @@ proc commit_stage2 {fd_wt curHEAD msg} { } set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD] if {[catch {eval exec $cmd} err]} { - error_popup "update-ref failed:\n$err" + error_popup "update-ref failed:\n\n$err" set commit_active 0 set ui_status_value {Commit failed.} unlock_index @@ -1481,12 +1477,12 @@ proc do_signoff {} { if {$GIT_COMMITTER_IDENT == {}} { if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} { - error_popup "Unable to obtain your identity:\n$err" + error_popup "Unable to obtain your identity:\n\n$err" return } if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \ $me me GIT_COMMITTER_IDENT]} { - error_popup "Invalid GIT_COMMITTER_IDENT:\n$me" + error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me" return } } -- cgit v1.2.3 From c4fe7728529fd9f3dc2c413ce889d359732cd3a4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 19:32:24 -0500 Subject: git-gui: Simplified format of geometry configuration. The gui.geometry config value was starting to contain the odd string \\{ as part of its value due to the way the Tcl lists were being supplied to git repo-config. Now we write out only three values: the overall window geomtry, the y position of the horizontal sash, and the x position of the vertical sash. All other data is skipped, which makes the gui.geometry value simpler. While debugging this I noticed that the save_my_config procedure was being invoked multiple times during exit due to do_quit getting invoked over and over again. So now we set a flag in do_quit and don't perform any of our "at exit" type of logic if we've already been through the do_quit procedure once. Signed-off-by: Shawn O. Pearce --- git-gui | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/git-gui b/git-gui index 4449c7d67e..ce49c38987 100755 --- a/git-gui +++ b/git-gui @@ -48,11 +48,9 @@ proc save_my_config {} { set repo_config(gui.trustmtime) [list $cfg_trust_mtime] } - set cfg_geometry [list \ - [wm geometry .] \ - [.vpane sash coord 0] \ - [.vpane.files sash coord 0] \ - ] + set cfg_geometry [wm geometry .] + append cfg_geometry " [lindex [.vpane sash coord 0] 1]" + append cfg_geometry " [lindex [.vpane.files sash coord 0] 0]" if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { set rc_geometry [list [list]] } @@ -1422,8 +1420,13 @@ proc do_repack {} { console_exec $w $cmd } +set quitting 0 + proc do_quit {} { - global gitdir ui_comm + global gitdir ui_comm quitting + + if {$quitting} return + set quitting 1 set save [file join $gitdir GITGUI_MSG] set msg [string trim [$ui_comm get 0.0 end]] @@ -1837,10 +1840,16 @@ pack .status -anchor w -side bottom -fill x # -- Load geometry catch { -wm geometry . [lindex $repo_config(gui.geometry) 0 0] -eval .vpane sash place 0 [lindex $repo_config(gui.geometry) 0 1] -eval .vpane.files sash place 0 [lindex $repo_config(gui.geometry) 0 2] -} +set gm [lindex $repo_config(gui.geometry) 0] +wm geometry . [lindex $gm 0] +.vpane sash place 0 \ + [lindex [.vpane sash coord 0] 0] \ + [lindex $gm 1] +.vpane.files sash place 0 \ + [lindex $gm 2] \ + [lindex [.vpane.files sash coord 0] 1] +} +unset gm # -- Key Bindings bind $ui_comm <$M1B-Key-Return> {do_commit;break} -- cgit v1.2.3 From 390adaeafbefc49a88d06c3a2ad68bc00fe0c614 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 19:40:33 -0500 Subject: git-gui: Misc. formatting cleanups. A number of lines were line wrapping in a rather ugly way when opened in vim with line numbers enabled, so I split most of these lines over two lines using a sensible wrapping policy. Signed-off-by: Shawn O. Pearce --- git-gui | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/git-gui b/git-gui index ce49c38987..f8c51590b0 100755 --- a/git-gui +++ b/git-gui @@ -197,7 +197,8 @@ proc update_status {{final Ready.}} { set ui_status_value {Refreshing file status...} set fd_rf [open "| git update-index -q --unmerged --refresh" r] fconfigure $fd_rf -blocking 0 -translation binary - fileevent $fd_rf readable [list update_status_stage2 $fd_rf $final] + fileevent $fd_rf readable \ + [list update_status_stage2 $fd_rf $final] } } @@ -402,8 +403,6 @@ proc show_diff {path {w {}} {lno {}}} { set cmd [list | git diff-index -p $PARENT -- $path] switch $m { - AM { - } MM { set cmd [list | git diff-index -p -c $PARENT $path] } @@ -828,7 +827,8 @@ proc post_pull_remote {remote branch success} { set PARENT $HEAD set $ui_status_value {Ready.} } else { - update_status "Conflicts detected while pulling $branch from $remote." + update_status \ + "Conflicts detected while pulling $branch from $remote." } } @@ -1548,7 +1548,7 @@ set maincursor [. cget -cursor] switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" { windows,* {set M1B Control; set M1T Ctrl} unix,Darwin {set M1B M1; set M1T Cmd} -default {set M1B M1; set M1T M1} +* {set M1B M1; set M1T M1} } # -- Menu Bar @@ -1720,19 +1720,22 @@ button .vpane.lower.commarea.buttons.rescan -text {Rescan} \ -command do_rescan \ -font $font_ui pack .vpane.lower.commarea.buttons.rescan -side top -fill x -lappend disable_on_lock {.vpane.lower.commarea.buttons.rescan conf -state} +lappend disable_on_lock \ + {.vpane.lower.commarea.buttons.rescan conf -state} button .vpane.lower.commarea.buttons.amend -text {Amend Last} \ -command do_amend_last \ -font $font_ui pack .vpane.lower.commarea.buttons.amend -side top -fill x -lappend disable_on_lock {.vpane.lower.commarea.buttons.amend conf -state} +lappend disable_on_lock \ + {.vpane.lower.commarea.buttons.amend conf -state} button .vpane.lower.commarea.buttons.incall -text {Include All} \ -command do_include_all \ -font $font_ui pack .vpane.lower.commarea.buttons.incall -side top -fill x -lappend disable_on_lock {.vpane.lower.commarea.buttons.incall conf -state} +lappend disable_on_lock \ + {.vpane.lower.commarea.buttons.incall conf -state} button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \ -command do_signoff \ @@ -1743,7 +1746,8 @@ button .vpane.lower.commarea.buttons.commit -text {Commit} \ -command do_commit \ -font $font_ui pack .vpane.lower.commarea.buttons.commit -side top -fill x -lappend disable_on_lock {.vpane.lower.commarea.buttons.commit conf -state} +lappend disable_on_lock \ + {.vpane.lower.commarea.buttons.commit conf -state} # -- Commit Message Buffer frame .vpane.lower.commarea.buffer @@ -1769,7 +1773,8 @@ text $ui_comm -background white -borderwidth 1 \ -font $font_diff \ -yscrollcommand {.vpane.lower.commarea.buffer.sby set} \ -cursor $maincursor -scrollbar .vpane.lower.commarea.buffer.sby -command [list $ui_comm yview] +scrollbar .vpane.lower.commarea.buffer.sby \ + -command [list $ui_comm yview] pack $ui_coml -side top -fill x pack .vpane.lower.commarea.buffer.sby -side right -fill y pack $ui_comm -side left -fill y @@ -1848,8 +1853,8 @@ wm geometry . [lindex $gm 0] .vpane.files sash place 0 \ [lindex $gm 2] \ [lindex [.vpane.files sash coord 0] 1] -} unset gm +} # -- Key Bindings bind $ui_comm <$M1B-Key-Return> {do_commit;break} -- cgit v1.2.3 From 62aac80b133de4fa004d90d27a97f574aec9d02d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 20:00:35 -0500 Subject: git-gui: Misc. bug fixes for mouse click crashes. Make sure the file_lists array has both elements set at all times, otherwise we get Tcl errors during mouse clicks in the file list areas due to the list not being defined. Also added M1-A as a keyboard binding within the console window text area. This lets users select all text easily and copy it to the clipboard. Signed-off-by: Shawn O. Pearce --- git-gui | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index f8c51590b0..b82e6e629f 100755 --- a/git-gui +++ b/git-gui @@ -960,7 +960,9 @@ proc display_all_files {} { $ui_index delete 0.0 end $ui_other delete 0.0 end - array unset file_lists + set file_lists($ui_index) [list] + set file_lists($ui_other) [list] + foreach path [lsort [array names file_states]] { set s $file_states($path) set m [lindex $s 0] @@ -1273,7 +1275,7 @@ proc new_console {short_title long_title} { proc console_init {w} { global console_cr console_data - global gitdir appname font_ui font_diff + global gitdir appname font_ui font_diff M1B set console_cr($w) 1.0 toplevel $w @@ -1306,6 +1308,8 @@ proc console_init {w} { -command "destroy $w" pack $w.ok -side bottom + bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" + bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break" bind $w "focus $w" wm title $w "$appname ([lindex [file split \ [file normalize [file dirname $gitdir]]] \ @@ -1900,7 +1904,10 @@ foreach i [list $ui_index $ui_other] { bind $i {click %W %x %y 3 %X %Y; break} bind $i {unclick %W %x %y; break} } -unset i M1B M1T +unset i + +set file_lists($ui_index) [list] +set file_lists($ui_other) [list] wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm -- cgit v1.2.3 From 0e794311833fd4d6f0c3204a5449372284318bda Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 20:24:23 -0500 Subject: git-gui: Added context menus for consoles and commit message buffer. This change adds a context menu to the commit message buffer providing fast access to the contents of the Edit menu, and to the console text buffer, providing easy ways to copy selections of the buffer or the entire buffer. Signed-off-by: Shawn O. Pearce --- git-gui | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/git-gui b/git-gui index b82e6e629f..c2b5e56838 100755 --- a/git-gui +++ b/git-gui @@ -1301,6 +1301,21 @@ proc console_init {w} { pack $w.m.t -side left -fill both -expand 1 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10 + menu $w.ctxm -tearoff 0 + $w.ctxm add command -label "Copy" \ + -font $font_ui \ + -command "tk_textCopy $w.m.t" + $w.ctxm add command -label "Select All" \ + -font $font_ui \ + -command "$w.m.t tag add sel 0.0 end" + $w.ctxm add command -label "Copy All" \ + -font $font_ui \ + -command " + $w.m.t tag add sel 0.0 end + tk_textCopy $w.m.t + $w.m.t tag remove sel 0.0 end + " + button $w.ok -text {Running...} \ -width 15 \ -font $font_ui \ @@ -1308,6 +1323,7 @@ proc console_init {w} { -command "destroy $w" pack $w.ok -side bottom + bind $w.m.t "tk_popup $w.ctxm %X %Y" bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break" bind $w "focus $w" @@ -1784,6 +1800,38 @@ pack .vpane.lower.commarea.buffer.sby -side right -fill y pack $ui_comm -side left -fill y pack .vpane.lower.commarea.buffer -side left -fill y +# -- Commit Message Buffer Context Menu +# +menu $ui_comm.ctxm -tearoff 0 +$ui_comm.ctxm add command -label "Cut" \ + -font $font_ui \ + -command "tk_textCut $ui_comm" +$ui_comm.ctxm add command -label "Copy" \ + -font $font_ui \ + -command "tk_textCopy $ui_comm" +$ui_comm.ctxm add command -label "Paste" \ + -font $font_ui \ + -command "tk_textPaste $ui_comm" +$ui_comm.ctxm add command -label "Delete" \ + -font $font_ui \ + -command "$ui_comm delete sel.first sel.last" +$ui_comm.ctxm add separator +$ui_comm.ctxm add command -label "Select All" \ + -font $font_ui \ + -command "$ui_comm tag add sel 0.0 end" +$ui_comm.ctxm add command -label "Copy All" \ + -font $font_ui \ + -command " + $ui_comm tag add sel 0.0 end + tk_textCopy $ui_comm + $ui_comm tag remove sel 0.0 end + " +$ui_comm.ctxm add separator +$ui_comm.ctxm add command -label "Sign Off" \ + -font $font_ui \ + -command do_signoff +bind $ui_comm "tk_popup $ui_comm.ctxm %X %Y" + # -- Diff Header set ui_fname_value {} set ui_fstatus_value {} @@ -1837,6 +1885,24 @@ $ui_diff tag conf di -foreground "#00a000" $ui_diff tag conf dni -foreground "#a000a0" $ui_diff tag conf bold -font [concat $font_diff bold] +# -- Diff Body Context Menu +# +menu $ui_diff.ctxm -tearoff 0 +$ui_diff.ctxm add command -label "Copy" \ + -font $font_ui \ + -command "tk_textCopy $ui_diff" +$ui_diff.ctxm add command -label "Select All" \ + -font $font_ui \ + -command "$ui_diff tag add sel 0.0 end" +$ui_diff.ctxm add command -label "Copy All" \ + -font $font_ui \ + -command " + $ui_diff tag add sel 0.0 end + tk_textCopy $ui_diff + $ui_diff tag remove sel 0.0 end + " +bind $ui_diff "tk_popup $ui_diff.ctxm %X %Y" + # -- Status Bar set ui_status_value {Initializing...} label .status -textvariable ui_status_value \ -- cgit v1.2.3 From 6c6dd01a041e628ce6efb583ea865d1a694ff7b2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 20:33:30 -0500 Subject: git-gui: Fix mouse cursor behavior when in widgets. The mouse cursor (at least on Windows) seemed to be picking up the cursor from the sash controls and then never resetting itself back to the standard text cursor (the I-beam) when it was over a text area that the user can edit (like the commit buffer) or over a text area the user can copy from (like the diff viewer). So now we always set the cursor to left_ptr (which according to the Tk documentation should be available everywhere) and only for the two text areas which we use to list file names, as the user clicks in these but is not permitted to select text. Signed-off-by: Shawn O. Pearce --- git-gui | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/git-gui b/git-gui index c2b5e56838..9a259c0dcb 100755 --- a/git-gui +++ b/git-gui @@ -1563,7 +1563,7 @@ proc unclick {w x y} { set font_ui {Helvetica 10} set font_diff {Courier 10} -set maincursor [. cget -cursor] +set cursor_ptr left_ptr switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" { windows,* {set M1B Control; set M1T Ctrl} @@ -1690,8 +1690,8 @@ label .vpane.files.index.title -text {Modified Files} \ text $ui_index -background white -borderwidth 0 \ -width 40 -height 10 \ -font $font_ui \ + -cursor $cursor_ptr \ -yscrollcommand {.vpane.files.index.sb set} \ - -cursor $maincursor \ -state disabled scrollbar .vpane.files.index.sb -command [list $ui_index yview] pack .vpane.files.index.title -side top -fill x @@ -1707,8 +1707,8 @@ label .vpane.files.other.title -text {Untracked Files} \ text $ui_other -background white -borderwidth 0 \ -width 40 -height 10 \ -font $font_ui \ + -cursor $cursor_ptr \ -yscrollcommand {.vpane.files.other.sb set} \ - -cursor $maincursor \ -state disabled scrollbar .vpane.files.other.sb -command [list $ui_other yview] pack .vpane.files.other.title -side top -fill x @@ -1791,8 +1791,7 @@ text $ui_comm -background white -borderwidth 1 \ -relief sunken \ -width 75 -height 9 -wrap none \ -font $font_diff \ - -yscrollcommand {.vpane.lower.commarea.buffer.sby set} \ - -cursor $maincursor + -yscrollcommand {.vpane.lower.commarea.buffer.sby set} scrollbar .vpane.lower.commarea.buffer.sby \ -command [list $ui_comm yview] pack $ui_coml -side top -fill x @@ -1866,7 +1865,6 @@ text $ui_diff -background white -borderwidth 0 \ -font $font_diff \ -xscrollcommand {.vpane.lower.diff.body.sbx set} \ -yscrollcommand {.vpane.lower.diff.body.sby set} \ - -cursor $maincursor \ -state disabled scrollbar .vpane.lower.diff.body.sbx -orient horizontal \ -command [list $ui_diff xview] -- cgit v1.2.3 From 1daf1d0c81c7c2623867f7884c8cbc5ff0a10d30 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 20:44:03 -0500 Subject: git-gui: Teach sign off to be more intelligent. When we sign off on a commit we want to add a blank line between whatever is in the commit buffer and the new Signed-off-by line, unless there already is a Signed-off-by (or Acked-by) tag at the end of the buffer already. This change makes us do the right thing more often. Signed-off-by: Shawn O. Pearce --- git-gui | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index 9a259c0dcb..8a42c97cb1 100755 --- a/git-gui +++ b/git-gui @@ -1510,10 +1510,15 @@ proc do_signoff {} { } } - set str "Signed-off-by: $GIT_COMMITTER_IDENT" - if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} { + set sob "Signed-off-by: $GIT_COMMITTER_IDENT" + set last [$ui_comm get {end -1c linestart} {end -1c}] + if {$last != $sob} { $ui_comm edit separator - $ui_comm insert end "\n$str" + if {$last != {} + && ![regexp {^[A-Z][A-Za-z]*-[A-Za-z-]+: *} $last]} { + $ui_comm insert end "\n" + } + $ui_comm insert end "\n$sob" $ui_comm edit separator $ui_comm see end } -- cgit v1.2.3 From 058803f400d8bbd72aa8b8584e9a6e93dbd17d54 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 20:45:52 -0500 Subject: git-gui: Corrected font used for options menu items. Signed-off-by: Shawn O. Pearce --- git-gui | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui b/git-gui index 8a42c97cb1..008eeb4460 100755 --- a/git-gui +++ b/git-gui @@ -1677,6 +1677,7 @@ menu .mbar.push menu .mbar.options .mbar.options add checkbutton \ -label {Trust File Modification Timestamps} \ + -font $font_ui \ -offvalue false \ -onvalue true \ -variable cfg_trust_mtime -- cgit v1.2.3 From f019c96add051aa9265c3dba8940c29f307aae81 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 21:02:37 -0500 Subject: git-gui: Honor system font and let user configure fonts. Rather than hardcoding our fonts to something that I thought was reasonable, guess font_ui off the font used by the system in the menu bar. This way the application conforms by default to whatever the user's desktop is setup to. We also now let the user supply font configuration through their repository configuration as gui.fontui (the overall UI font) and gui.fontdiff (the font used for the diff viewer). Signed-off-by: Shawn O. Pearce --- git-gui | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index 008eeb4460..26df169c18 100755 --- a/git-gui +++ b/git-gui @@ -1566,9 +1566,16 @@ proc unclick {w x y} { ## ## ui init -set font_ui {Helvetica 10} -set font_diff {Courier 10} -set cursor_ptr left_ptr +set font_ui {} +set font_diff {} +set cursor_ptr {} +menu .mbar -tearoff 0 +catch {set font_ui [lindex $repo_config(gui.fontui) 0]} +catch {set font_diff [lindex $repo_config(gui.fontdiff) 0]} +if {$font_ui == {}} {catch {set font_ui [.mbar cget -font]}} +if {$font_ui == {}} {set font_ui {Helvetica 10}} +if {$font_diff == {}} {set font_diff {Courier 10}} +if {$cursor_ptr == {}} {set cursor_ptr left_ptr} switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" { windows,* {set M1B Control; set M1T Ctrl} @@ -1577,7 +1584,6 @@ unix,Darwin {set M1B M1; set M1T Cmd} } # -- Menu Bar -menu .mbar -tearoff 0 .mbar add cascade -label Project -menu .mbar.project .mbar add cascade -label Edit -menu .mbar.edit .mbar add cascade -label Commit -menu .mbar.commit -- cgit v1.2.3 From 2c26e6f5279286aa844a54363d877acffb4a4310 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 21:14:28 -0500 Subject: git-gui: Allow the user to change the diff viewer font size. Because the diff area is one of the most important areas to be able to read users should be able to increase or decrease the size of the font used within that area. Currently we save that back to the global configuration, even if it may have originated from the local repository configuration. This is probably going to be considered to be a bug by at least one user who wants some sort of different font within a given repository, but I'm just going to ignore the problem for now. Signed-off-by: Shawn O. Pearce --- git-gui | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 26df169c18..fdb1dce88d 100755 --- a/git-gui +++ b/git-gui @@ -39,6 +39,7 @@ proc load_repo_config {} { proc save_my_config {} { global repo_config global cfg_trust_mtime + global font_diff if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} { set rc_trustMTime [list false] @@ -48,6 +49,14 @@ proc save_my_config {} { set repo_config(gui.trustmtime) [list $cfg_trust_mtime] } + if {[catch {set rc_fontdiff $repo_config(gui.fontdiff)}]} { + set rc_fontdiff [list {Courier 10}] + } + if {$font_diff != [lindex $rc_fontdiff 0]} { + exec git repo-config --global gui.fontDiff $font_diff + set repo_config(gui.fontdiff) [list $font_diff] + } + set cfg_geometry [wm geometry .] append cfg_geometry " [lindex [.vpane sash coord 0] 1]" append cfg_geometry " [lindex [.vpane.files sash coord 0] 0]" @@ -1890,9 +1899,9 @@ pack .vpane.lower.diff.body -side bottom -fill both -expand 1 $ui_diff tag conf dm -foreground red $ui_diff tag conf dp -foreground blue -$ui_diff tag conf da -font [concat $font_diff bold] $ui_diff tag conf di -foreground "#00a000" $ui_diff tag conf dni -foreground "#a000a0" +$ui_diff tag conf da -font [concat $font_diff bold] $ui_diff tag conf bold -font [concat $font_diff bold] # -- Diff Body Context Menu @@ -1911,6 +1920,23 @@ $ui_diff.ctxm add command -label "Copy All" \ tk_textCopy $ui_diff $ui_diff tag remove sel 0.0 end " +$ui_diff.ctxm add separator +$ui_diff.ctxm add command -label "Decrease Font Size" \ + -font $font_ui \ + -command { + lset font_diff 1 [expr [lindex $font_diff 1] - 1] + $ui_diff configure -font $font_diff + $ui_diff tag conf da -font [concat $font_diff bold] + $ui_diff tag conf bold -font [concat $font_diff bold] + } +$ui_diff.ctxm add command -label "Increase Font Size" \ + -font $font_ui \ + -command { + lset font_diff 1 [expr [lindex $font_diff 1] + 1] + $ui_diff configure -font $font_diff + $ui_diff tag conf da -font [concat $font_diff bold] + $ui_diff tag conf bold -font [concat $font_diff bold] + } bind $ui_diff "tk_popup $ui_diff.ctxm %X %Y" # -- Status Bar -- cgit v1.2.3 From 16403d0b1f9d17bec6bce488356ec8bf84cace48 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 11 Nov 2006 21:52:06 -0500 Subject: git-gui: Refresh a file if it has an empty diff. When the user has enabled the Trust File Modification Timestamp option then we may display a file as being modified yet that file may not have a difference. When the user clicks on that file we wind up displaying an empty diff viewer, which makes no sense to the user. So instead if we get an empty diff and the user has this option enabled and the file's current state is _M (no change in index but the working file appears modified) then run a quick update-index on just that file and remove it from the list of modified files. We also give the user a quick dialog stating we are removing it, and why. Usually I don't run into this situation when I have the Trust File Modification Timestamp option enabled, so its not that annoying to have a dialog pop open to remind me why there are no differences. Signed-off-by: Shawn O. Pearce --- git-gui | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index fdb1dce88d..ae87879bbb 100755 --- a/git-gui +++ b/git-gui @@ -88,6 +88,25 @@ proc error_popup {msg} { -message $msg } +proc info_popup {msg} { + global gitdir appname + + set title $appname + if {$gitdir != {}} { + append title { (} + append title [lindex \ + [file split [file normalize [file dirname $gitdir]]] \ + end] + append title {)} + } + tk_messageBox \ + -parent . \ + -icon error \ + -type ok \ + -title $title \ + -message $msg +} + ###################################################################### ## ## repository setup @@ -204,7 +223,12 @@ proc update_status {{final Ready.}} { } else { set status_active 1 set ui_status_value {Refreshing file status...} - set fd_rf [open "| git update-index -q --unmerged --refresh" r] + set cmd [list git update-index] + lappend cmd -q + lappend cmd --unmerged + lappend cmd --ignore-missing + lappend cmd --refresh + set fd_rf [open "| $cmd" r] fconfigure $fd_rf -blocking 0 -translation binary fileevent $fd_rf readable \ [list update_status_stage2 $fd_rf $final] @@ -381,6 +405,42 @@ proc reshow_diff {} { } } +proc handle_empty_diff {} { + global ui_fname_value file_states file_lists + + set path $ui_fname_value + set s $file_states($path) + if {[lindex $s 0] != {_M}} return + + info_popup "No differences detected. + +[short_path $path] has no changes. + +The modification date of this file was updated by another +application and you currently have the Trust File Modification +Timestamps feature enabled, so Git did not automatically detect +that there are no content differences in this file. + +This file will now be removed from the modified files list, to +prevent possible confusion. +" + if {[catch {exec git update-index -- $path} err]} { + error_popup "Failed to refresh index:\n\n$err" + } + + clear_diff + set old_w [mapcol [lindex $file_states($path) 0] $path] + set lno [lsearch -sorted $file_lists($old_w) $path] + if {$lno >= 0} { + set file_lists($old_w) \ + [lreplace $file_lists($old_w) $lno $lno] + incr lno + $old_w conf -state normal + $old_w delete $lno.0 [expr $lno + 1].0 + $old_w conf -state disabled + } +} + proc show_diff {path {w {}} {lno {}}} { global file_states file_lists global PARENT diff_3way diff_active @@ -451,6 +511,7 @@ proc show_diff {path {w {}} {lno {}}} { proc read_diff {fd} { global ui_diff ui_status_value diff_3way diff_active + global cfg_trust_mtime while {[gets $fd line] >= 0} { if {[string match {diff --git *} $line]} continue @@ -497,6 +558,10 @@ proc read_diff {fd} { set diff_active 0 unlock_index set ui_status_value {Ready.} + + if {$cfg_trust_mtime && [$ui_diff index end] == {2.0}} { + handle_empty_diff + } } } @@ -588,7 +653,7 @@ before committing. U? { error_popup "Unmerged files cannot be committed. -File [escape_path $path] has merge conflicts. +File [short_path $path] has merge conflicts. You must resolve them and include the file before committing. " unlock_index @@ -597,7 +662,7 @@ You must resolve them and include the file before committing. default { error_popup "Unknown file state [lindex $s 0] detected. -File [escape_path $path] cannot be committed by this program. +File [short_path $path] cannot be committed by this program. " } } @@ -888,6 +953,10 @@ proc escape_path {path} { return $path } +proc short_path {path} { + return [escape_path [lindex [file split $path] end]] +} + set next_icon_id 0 proc merge_state {path new_state} { @@ -921,7 +990,6 @@ proc merge_state {path new_state} { } proc display_file {path state} { - global ui_index ui_other global file_states file_lists status_active set old_m [merge_state $path $state] -- cgit v1.2.3 From b4946930fa0f7aa538e33f1d799beffb7e10e7a9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 00:40:38 -0500 Subject: git-gui: Make use of the Tk font system rather than faking it. The native Tk font system is actually quite powerful and has the nice property that modifications to a named font are immediately reflected throughout all widgets currently displayed. This really saves us from needing to write all of the reconfigure code as part of our font display. I also fixed the way we detect and apply the system font on the main UI widgets as although it worked on a Windows 2000 system it does not work at all on my Mac OS 10.4 system. Signed-off-by: Shawn O. Pearce --- git-gui | 192 +++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 92 insertions(+), 100 deletions(-) diff --git a/git-gui b/git-gui index ae87879bbb..3cbe3e7f72 100755 --- a/git-gui +++ b/git-gui @@ -39,7 +39,6 @@ proc load_repo_config {} { proc save_my_config {} { global repo_config global cfg_trust_mtime - global font_diff if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} { set rc_trustMTime [list false] @@ -49,14 +48,6 @@ proc save_my_config {} { set repo_config(gui.trustmtime) [list $cfg_trust_mtime] } - if {[catch {set rc_fontdiff $repo_config(gui.fontdiff)}]} { - set rc_fontdiff [list {Courier 10}] - } - if {$font_diff != [lindex $rc_fontdiff 0]} { - exec git repo-config --global gui.fontDiff $font_diff - set repo_config(gui.fontdiff) [list $font_diff] - } - set cfg_geometry [wm geometry .] append cfg_geometry " [lindex [.vpane sash coord 0] 1]" append cfg_geometry " [lindex [.vpane.files sash coord 0] 0]" @@ -1134,17 +1125,17 @@ proc load_all_remotes {} { } proc populate_remote_menu {m pfx op} { - global all_remotes font_ui + global all_remotes foreach remote $all_remotes { $m add command -label "$pfx $remote..." \ -command [list $op $remote] \ - -font $font_ui + -font font_ui } } proc populate_pull_menu {m} { - global gitdir repo_config all_remotes font_ui disable_on_lock + global gitdir repo_config all_remotes disable_on_lock foreach remote $all_remotes { set rb {} @@ -1172,7 +1163,7 @@ proc populate_pull_menu {m} { $m add command \ -label "Branch $rb_short from $remote..." \ -command [list pull_remote $remote $rb] \ - -font $font_ui + -font font_ui lappend disable_on_lock \ [list $m entryconf [$m index last] -state] } @@ -1294,8 +1285,15 @@ unset filemask i ## ## util +proc incr_font_size {font {amt 1}} { + set sz [font configure $font -size] + incr sz $amt + font configure $font -size $sz + font configure ${font}bold -size $sz +} + proc hook_failed_popup {hook msg} { - global gitdir font_ui font_diff appname + global gitdir appname set w .hookfail toplevel $w @@ -1305,18 +1303,18 @@ proc hook_failed_popup {hook msg} { label $w.m.l1 -text "$hook hook failed:" \ -anchor w \ -justify left \ - -font [concat $font_ui bold] + -font font_uibold text $w.m.t \ -background white -borderwidth 1 \ -relief sunken \ -width 80 -height 10 \ - -font $font_diff \ + -font font_diff \ -yscrollcommand [list $w.m.sby set] label $w.m.l2 \ -text {You must correct the above errors before committing.} \ -anchor w \ -justify left \ - -font [concat $font_ui bold] + -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 @@ -1329,7 +1327,7 @@ proc hook_failed_popup {hook msg} { button $w.ok -text OK \ -width 15 \ - -font $font_ui \ + -font font_ui \ -command "destroy $w" pack $w.ok -side bottom @@ -1352,7 +1350,7 @@ proc new_console {short_title long_title} { proc console_init {w} { global console_cr console_data - global gitdir appname font_ui font_diff M1B + global gitdir appname M1B set console_cr($w) 1.0 toplevel $w @@ -1360,17 +1358,17 @@ proc console_init {w} { label $w.m.l1 -text "[lindex $console_data($w) 1]:" \ -anchor w \ -justify left \ - -font [concat $font_ui bold] + -font font_uibold text $w.m.t \ -background white -borderwidth 1 \ -relief sunken \ -width 80 -height 10 \ - -font $font_diff \ + -font font_diff \ -state disabled \ -yscrollcommand [list $w.m.sby set] label $w.m.s -anchor w \ -justify left \ - -font [concat $font_ui bold] + -font font_uibold scrollbar $w.m.sby -command [list $w.m.t yview] pack $w.m.l1 -side top -fill x pack $w.m.s -side bottom -fill x @@ -1380,13 +1378,13 @@ proc console_init {w} { menu $w.ctxm -tearoff 0 $w.ctxm add command -label "Copy" \ - -font $font_ui \ + -font font_ui \ -command "tk_textCopy $w.m.t" $w.ctxm add command -label "Select All" \ - -font $font_ui \ + -font font_ui \ -command "$w.m.t tag add sel 0.0 end" $w.ctxm add command -label "Copy All" \ - -font $font_ui \ + -font font_ui \ -command " $w.m.t tag add sel 0.0 end tk_textCopy $w.m.t @@ -1395,7 +1393,7 @@ proc console_init {w} { button $w.ok -text {Running...} \ -width 15 \ - -font $font_ui \ + -font font_ui \ -state disabled \ -command "destroy $w" pack $w.ok -side bottom @@ -1643,16 +1641,19 @@ proc unclick {w x y} { ## ## ui init -set font_ui {} -set font_diff {} -set cursor_ptr {} -menu .mbar -tearoff 0 -catch {set font_ui [lindex $repo_config(gui.fontui) 0]} -catch {set font_diff [lindex $repo_config(gui.fontdiff) 0]} -if {$font_ui == {}} {catch {set font_ui [.mbar cget -font]}} -if {$font_ui == {}} {set font_ui {Helvetica 10}} -if {$font_diff == {}} {set font_diff {Courier 10}} -if {$cursor_ptr == {}} {set cursor_ptr left_ptr} +set cursor_ptr left_ptr +font create font_diff -family Courier -size 10 +font create font_ui +catch { + label .dummy + eval font configure font_ui [font actual [.dummy cget -font]] + destroy .dummy +} + +eval font create font_uibold [font configure font_ui] +font configure font_uibold -weight bold +eval font create font_diffbold [font configure font_diff] +font configure font_diffbold -weight bold switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" { windows,* {set M1B Control; set M1T Ctrl} @@ -1661,6 +1662,7 @@ unix,Darwin {set M1B M1; set M1T Cmd} } # -- Menu Bar +menu .mbar -tearoff 0 .mbar add cascade -label Project -menu .mbar.project .mbar add cascade -label Edit -menu .mbar.edit .mbar add cascade -label Commit -menu .mbar.commit @@ -1674,14 +1676,14 @@ unix,Darwin {set M1B M1; set M1T Cmd} menu .mbar.project .mbar.project add command -label Visualize \ -command do_gitk \ - -font $font_ui + -font font_ui .mbar.project add command -label {Repack Database} \ -command do_repack \ - -font $font_ui + -font font_ui .mbar.project add command -label Quit \ -command do_quit \ -accelerator $M1T-Q \ - -font $font_ui + -font font_ui # -- Edit Menu # @@ -1689,61 +1691,61 @@ menu .mbar.edit .mbar.edit add command -label Undo \ -command {catch {[focus] edit undo}} \ -accelerator $M1T-Z \ - -font $font_ui + -font font_ui .mbar.edit add command -label Redo \ -command {catch {[focus] edit redo}} \ -accelerator $M1T-Y \ - -font $font_ui + -font font_ui .mbar.edit add separator .mbar.edit add command -label Cut \ -command {catch {tk_textCut [focus]}} \ -accelerator $M1T-X \ - -font $font_ui + -font font_ui .mbar.edit add command -label Copy \ -command {catch {tk_textCopy [focus]}} \ -accelerator $M1T-C \ - -font $font_ui + -font font_ui .mbar.edit add command -label Paste \ -command {catch {tk_textPaste [focus]; [focus] see insert}} \ -accelerator $M1T-V \ - -font $font_ui + -font font_ui .mbar.edit add command -label Delete \ -command {catch {[focus] delete sel.first sel.last}} \ -accelerator Del \ - -font $font_ui + -font font_ui .mbar.edit add separator .mbar.edit add command -label {Select All} \ -command {catch {[focus] tag add sel 0.0 end}} \ -accelerator $M1T-A \ - -font $font_ui + -font font_ui # -- Commit Menu menu .mbar.commit .mbar.commit add command -label Rescan \ -command do_rescan \ -accelerator F5 \ - -font $font_ui + -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Amend Last Commit} \ -command do_amend_last \ - -font $font_ui + -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Include All Files} \ -command do_include_all \ -accelerator $M1T-I \ - -font $font_ui + -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Sign Off} \ -command do_signoff \ -accelerator $M1T-S \ - -font $font_ui + -font font_ui .mbar.commit add command -label Commit \ -command do_commit \ -accelerator $M1T-Return \ - -font $font_ui + -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] @@ -1760,7 +1762,7 @@ menu .mbar.push menu .mbar.options .mbar.options add checkbutton \ -label {Trust File Modification Timestamps} \ - -font $font_ui \ + -font font_ui \ -offvalue false \ -onvalue true \ -variable cfg_trust_mtime @@ -1775,10 +1777,10 @@ pack .vpane -anchor n -side top -fill both -expand 1 frame .vpane.files.index -height 100 -width 400 label .vpane.files.index.title -text {Modified Files} \ -background green \ - -font $font_ui + -font font_ui text $ui_index -background white -borderwidth 0 \ -width 40 -height 10 \ - -font $font_ui \ + -font font_ui \ -cursor $cursor_ptr \ -yscrollcommand {.vpane.files.index.sb set} \ -state disabled @@ -1792,10 +1794,10 @@ pack $ui_index -side left -fill both -expand 1 frame .vpane.files.other -height 100 -width 100 label .vpane.files.other.title -text {Untracked Files} \ -background red \ - -font $font_ui + -font font_ui text $ui_other -background white -borderwidth 0 \ -width 40 -height 10 \ - -font $font_ui \ + -font font_ui \ -cursor $cursor_ptr \ -yscrollcommand {.vpane.files.other.sb set} \ -state disabled @@ -1805,8 +1807,8 @@ pack .vpane.files.other.sb -side right -fill y pack $ui_other -side left -fill both -expand 1 .vpane.files add .vpane.files.other -sticky nsew -$ui_index tag conf in_diff -font [concat $font_ui bold] -$ui_other tag conf in_diff -font [concat $font_ui bold] +$ui_index tag conf in_diff -font font_uibold +$ui_other tag conf in_diff -font font_uibold # -- Diff and Commit Area frame .vpane.lower -height 400 -width 400 @@ -1821,39 +1823,39 @@ frame .vpane.lower.commarea.buttons label .vpane.lower.commarea.buttons.l -text {} \ -anchor w \ -justify left \ - -font $font_ui + -font font_ui pack .vpane.lower.commarea.buttons.l -side top -fill x pack .vpane.lower.commarea.buttons -side left -fill y button .vpane.lower.commarea.buttons.rescan -text {Rescan} \ -command do_rescan \ - -font $font_ui + -font font_ui pack .vpane.lower.commarea.buttons.rescan -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.rescan conf -state} button .vpane.lower.commarea.buttons.amend -text {Amend Last} \ -command do_amend_last \ - -font $font_ui + -font font_ui pack .vpane.lower.commarea.buttons.amend -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.amend conf -state} button .vpane.lower.commarea.buttons.incall -text {Include All} \ -command do_include_all \ - -font $font_ui + -font font_ui pack .vpane.lower.commarea.buttons.incall -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.incall conf -state} button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \ -command do_signoff \ - -font $font_ui + -font font_ui pack .vpane.lower.commarea.buttons.signoff -side top -fill x button .vpane.lower.commarea.buttons.commit -text {Commit} \ -command do_commit \ - -font $font_ui + -font font_ui pack .vpane.lower.commarea.buttons.commit -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.commit conf -state} @@ -1865,7 +1867,7 @@ set ui_coml .vpane.lower.commarea.buffer.l label $ui_coml -text {Commit Message:} \ -anchor w \ -justify left \ - -font $font_ui + -font font_ui trace add variable commit_type write {uplevel #0 { switch -glob $commit_type \ initial {$ui_coml conf -text {Initial Commit Message:}} \ @@ -1879,7 +1881,7 @@ text $ui_comm -background white -borderwidth 1 \ -autoseparators true \ -relief sunken \ -width 75 -height 9 -wrap none \ - -font $font_diff \ + -font font_diff \ -yscrollcommand {.vpane.lower.commarea.buffer.sby set} scrollbar .vpane.lower.commarea.buffer.sby \ -command [list $ui_comm yview] @@ -1892,23 +1894,23 @@ pack .vpane.lower.commarea.buffer -side left -fill y # menu $ui_comm.ctxm -tearoff 0 $ui_comm.ctxm add command -label "Cut" \ - -font $font_ui \ + -font font_ui \ -command "tk_textCut $ui_comm" $ui_comm.ctxm add command -label "Copy" \ - -font $font_ui \ + -font font_ui \ -command "tk_textCopy $ui_comm" $ui_comm.ctxm add command -label "Paste" \ - -font $font_ui \ + -font font_ui \ -command "tk_textPaste $ui_comm" $ui_comm.ctxm add command -label "Delete" \ - -font $font_ui \ + -font font_ui \ -command "$ui_comm delete sel.first sel.last" $ui_comm.ctxm add separator $ui_comm.ctxm add command -label "Select All" \ - -font $font_ui \ + -font font_ui \ -command "$ui_comm tag add sel 0.0 end" $ui_comm.ctxm add command -label "Copy All" \ - -font $font_ui \ + -font font_ui \ -command " $ui_comm tag add sel 0.0 end tk_textCopy $ui_comm @@ -1916,7 +1918,7 @@ $ui_comm.ctxm add command -label "Copy All" \ " $ui_comm.ctxm add separator $ui_comm.ctxm add command -label "Sign Off" \ - -font $font_ui \ + -font font_ui \ -command do_signoff bind $ui_comm "tk_popup $ui_comm.ctxm %X %Y" @@ -1926,21 +1928,21 @@ set ui_fstatus_value {} frame .vpane.lower.diff.header -background orange label .vpane.lower.diff.header.l1 -text {File:} \ -background orange \ - -font $font_ui + -font font_ui label .vpane.lower.diff.header.l2 -textvariable ui_fname_value \ -background orange \ -anchor w \ -justify left \ - -font $font_ui + -font font_ui label .vpane.lower.diff.header.l3 -text {Status:} \ -background orange \ - -font $font_ui + -font font_ui label .vpane.lower.diff.header.l4 -textvariable ui_fstatus_value \ -background orange \ -width $max_status_desc \ -anchor w \ -justify left \ - -font $font_ui + -font font_ui pack .vpane.lower.diff.header.l1 -side left pack .vpane.lower.diff.header.l2 -side left -fill x pack .vpane.lower.diff.header.l4 -side right @@ -1951,7 +1953,7 @@ frame .vpane.lower.diff.body set ui_diff .vpane.lower.diff.body.t text $ui_diff -background white -borderwidth 0 \ -width 80 -height 15 -wrap none \ - -font $font_diff \ + -font font_diff \ -xscrollcommand {.vpane.lower.diff.body.sbx set} \ -yscrollcommand {.vpane.lower.diff.body.sby set} \ -state disabled @@ -1967,22 +1969,22 @@ pack .vpane.lower.diff.body -side bottom -fill both -expand 1 $ui_diff tag conf dm -foreground red $ui_diff tag conf dp -foreground blue -$ui_diff tag conf di -foreground "#00a000" -$ui_diff tag conf dni -foreground "#a000a0" -$ui_diff tag conf da -font [concat $font_diff bold] -$ui_diff tag conf bold -font [concat $font_diff bold] +$ui_diff tag conf di -foreground {#00a000} +$ui_diff tag conf dni -foreground {#a000a0} +$ui_diff tag conf da -font font_diffbold +$ui_diff tag conf bold -font font_diffbold # -- Diff Body Context Menu # menu $ui_diff.ctxm -tearoff 0 $ui_diff.ctxm add command -label "Copy" \ - -font $font_ui \ + -font font_ui \ -command "tk_textCopy $ui_diff" $ui_diff.ctxm add command -label "Select All" \ - -font $font_ui \ + -font font_ui \ -command "$ui_diff tag add sel 0.0 end" $ui_diff.ctxm add command -label "Copy All" \ - -font $font_ui \ + -font font_ui \ -command " $ui_diff tag add sel 0.0 end tk_textCopy $ui_diff @@ -1990,21 +1992,11 @@ $ui_diff.ctxm add command -label "Copy All" \ " $ui_diff.ctxm add separator $ui_diff.ctxm add command -label "Decrease Font Size" \ - -font $font_ui \ - -command { - lset font_diff 1 [expr [lindex $font_diff 1] - 1] - $ui_diff configure -font $font_diff - $ui_diff tag conf da -font [concat $font_diff bold] - $ui_diff tag conf bold -font [concat $font_diff bold] - } + -font font_ui \ + -command {incr_font_size font_diff -1} $ui_diff.ctxm add command -label "Increase Font Size" \ - -font $font_ui \ - -command { - lset font_diff 1 [expr [lindex $font_diff 1] + 1] - $ui_diff configure -font $font_diff - $ui_diff tag conf da -font [concat $font_diff bold] - $ui_diff tag conf bold -font [concat $font_diff bold] - } + -font font_ui \ + -command {incr_font_size font_diff 1} bind $ui_diff "tk_popup $ui_diff.ctxm %X %Y" # -- Status Bar @@ -2014,7 +2006,7 @@ label .status -textvariable ui_status_value \ -justify left \ -borderwidth 1 \ -relief sunken \ - -font $font_ui + -font font_ui pack .status -anchor w -side bottom -fill x # -- Load geometry -- cgit v1.2.3 From 16fccd7a1111c1fca6ce973ddaff690188e742d0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 02:22:21 -0500 Subject: git-gui: Improve right click context menu binding on all platforms. Apparently doesn't work on my single button PowerBook mouse under Mac OS X. I'm guessing this is because Tk is stealing every event and doesn't realize that Control-Button-1 is actually supposed to invoke the context menu on this platform. So now we have a utility procedure is_MacOSX to guess if we are running on a Mac OS X system, and if so setup Control-Button-1 to also activate what Button-3 should have. This does mean that I need to stay away from using Control-Button-1 as a binding in any other context. Of course we should use $M1B for that, which is M1 (aka Command) on Mac OS X so that shouldn't prove to be a problem. Signed-off-by: Shawn O. Pearce --- git-gui | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/git-gui b/git-gui index 3cbe3e7f72..040581e829 100755 --- a/git-gui +++ b/git-gui @@ -1285,6 +1285,23 @@ unset filemask i ## ## util +proc is_MacOSX {} { + global tcl_platform tk_library + if {$tcl_platform(platform) == {unix} + && $tcl_platform(os) == {Darwin} + && [string match /Library/Frameworks/* $tk_library]} { + return 1 + } + return 0 +} + +proc bind_button3 {w cmd} { + bind $w $cmd + if {[is_MacOSX]} { + bind $w $cmd + } +} + proc incr_font_size {font {amt 1}} { set sz [font configure $font -size] incr sz $amt @@ -1398,7 +1415,7 @@ proc console_init {w} { -command "destroy $w" pack $w.ok -side bottom - bind $w.m.t "tk_popup $w.ctxm %X %Y" + bind_button3 $w.m.t "tk_popup $w.ctxm %X %Y" bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break" bind $w "focus $w" @@ -1655,10 +1672,14 @@ font configure font_uibold -weight bold eval font create font_diffbold [font configure font_diff] font configure font_diffbold -weight bold -switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" { -windows,* {set M1B Control; set M1T Ctrl} -unix,Darwin {set M1B M1; set M1T Cmd} -* {set M1B M1; set M1T M1} +set M1B M1 +set M1T M1 +if {$tcl_platform(platform) == {windows}} { + set M1B Control + set M1T Ctrl +} elseif {[is_MacOSX]} { + set M1B M1 + set M1T Cmd } # -- Menu Bar @@ -1920,7 +1941,7 @@ $ui_comm.ctxm add separator $ui_comm.ctxm add command -label "Sign Off" \ -font font_ui \ -command do_signoff -bind $ui_comm "tk_popup $ui_comm.ctxm %X %Y" +bind_button3 $ui_comm "tk_popup $ui_comm.ctxm %X %Y" # -- Diff Header set ui_fname_value {} @@ -1997,7 +2018,7 @@ $ui_diff.ctxm add command -label "Decrease Font Size" \ $ui_diff.ctxm add command -label "Increase Font Size" \ -font font_ui \ -command {incr_font_size font_diff 1} -bind $ui_diff "tk_popup $ui_diff.ctxm %X %Y" +bind_button3 $ui_diff "tk_popup $ui_diff.ctxm %X %Y" # -- Status Bar set ui_status_value {Initializing...} @@ -2063,8 +2084,8 @@ bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} foreach i [list $ui_index $ui_other] { bind $i {click %W %x %y 1 %X %Y; break} - bind $i {click %W %x %y 3 %X %Y; break} bind $i {unclick %W %x %y; break} + bind_button3 $i {click %W %x %y 3 %X %Y; break} } unset i -- cgit v1.2.3 From b5834d70fe970ccf955378d4f6e60a51a94bbaaf Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 02:27:28 -0500 Subject: git-gui: Rename quitting global to is_quitting. This is a boolean value; naming it as such is a good thing. Signed-off-by: Shawn O. Pearce --- git-gui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index 040581e829..73227d594a 100755 --- a/git-gui +++ b/git-gui @@ -1532,13 +1532,13 @@ proc do_repack {} { console_exec $w $cmd } -set quitting 0 +set is_quitting 0 proc do_quit {} { - global gitdir ui_comm quitting + global gitdir ui_comm is_quitting - if {$quitting} return - set quitting 1 + if {$is_quitting} return + set is_quitting 1 set save [file join $gitdir GITGUI_MSG] set msg [string trim [$ui_comm get 0.0 end]] -- cgit v1.2.3 From 00f949fbd831bda29dc909baf4a21d00a7c2c119 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 02:30:02 -0500 Subject: git-gui: Use arrow cursor rather than left_ptr. Arrow is available on all Tk platforms and is mapped to the native system cursor on Windows and Mac OS X. Consequently its the better cursor choice as it should match whatever the system has configured for the standard pointing thingy. Signed-off-by: Shawn O. Pearce --- git-gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 73227d594a..29877e4150 100755 --- a/git-gui +++ b/git-gui @@ -1658,7 +1658,7 @@ proc unclick {w x y} { ## ## ui init -set cursor_ptr left_ptr +set cursor_ptr arrow font create font_diff -family Courier -size 10 font create font_ui catch { -- cgit v1.2.3 From 51f4d16b1d8e2e709f28a4e52b951236950cb1d5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 03:47:00 -0500 Subject: git-gui: Refactor options menu into an options dialog. I decided that the options menu was going to turn into a mess after a while as I start to add additional features to git-gui. The better approach would be to create a dialog that lets the user edit the options, including their --global options. We also wisely let the user press Cancel (or destroy the window) to abort any sort of option editing session, without the options being changed. Signed-off-by: Shawn O. Pearce --- git-gui | 204 +++++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 158 insertions(+), 46 deletions(-) diff --git a/git-gui b/git-gui index 29877e4150..520ec1efff 100755 --- a/git-gui +++ b/git-gui @@ -14,49 +14,90 @@ set gitdir {} ## ## config -proc load_repo_config {} { - global repo_config - global cfg_trust_mtime +set default_config(gui.trustmtime) false + +proc is_many_config {name} { + switch -glob -- $name { + remote.*.fetch - + remote.*.push + {return 1} + * + {return 0} + } +} +proc load_config {} { + global repo_config global_config default_config + + array unset global_config array unset repo_config + catch { + set fd_rc [open "| git repo-config --global --list" r] + while {[gets $fd_rc line] >= 0} { + if {[regexp {^([^=]+)=(.*)$} $line line name value]} { + if {[is_many_config $name]} { + lappend global_config($name) $value + } else { + set global_config($name) $value + } + } + } + close $fd_rc + } catch { set fd_rc [open "| git repo-config --list" r] while {[gets $fd_rc line] >= 0} { if {[regexp {^([^=]+)=(.*)$} $line line name value]} { - lappend repo_config($name) $value + if {[is_many_config $name]} { + lappend repo_config($name) $value + } else { + set repo_config($name) $value + } } } close $fd_rc } - if {[catch {set cfg_trust_mtime \ - [lindex $repo_config(gui.trustmtime) 0] - }]} { - set cfg_trust_mtime false + foreach name [array names default_config] { + if {[catch {set v $global_config($name)}]} { + set global_config($name) $default_config($name) + } + if {[catch {set v $repo_config($name)}]} { + set repo_config($name) $default_config($name) + } } } -proc save_my_config {} { - global repo_config - global cfg_trust_mtime +proc save_config {} { + global repo_config global_config default_config + global repo_config_new global_config_new - if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} { - set rc_trustMTime [list false] - } - if {$cfg_trust_mtime != [lindex $rc_trustMTime 0]} { - exec git repo-config gui.trustMTime $cfg_trust_mtime - set repo_config(gui.trustmtime) [list $cfg_trust_mtime] + foreach name [array names global_config_new] { + set value $global_config_new($name) + if {$value != $global_config($name)} { + if {$value == $default_config($name)} { + catch {exec git repo-config --global --unset $name} + } else { + catch {exec git repo-config --global $name $value} + } + set global_config($name) $value + if {$value == $repo_config($name)} { + catch {exec git repo-config --unset $name} + set repo_config($name) $value + } + } } - set cfg_geometry [wm geometry .] - append cfg_geometry " [lindex [.vpane sash coord 0] 1]" - append cfg_geometry " [lindex [.vpane.files sash coord 0] 0]" - if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { - set rc_geometry [list [list]] - } - if {$cfg_geometry != [lindex $rc_geometry 0]} { - exec git repo-config gui.geometry $cfg_geometry - set repo_config(gui.geometry) [list $cfg_geometry] + foreach name [array names repo_config_new] { + set value $repo_config_new($name) + if {$value != $repo_config($name)} { + if {$value == $global_config($name)} { + catch {exec git repo-config --unset $name} + } else { + catch {exec git repo-config $name $value} + } + set repo_config($name) $value + } } } @@ -117,7 +158,7 @@ if {$appname == {git-citool}} { set single_commit 1 } -load_repo_config +load_config ###################################################################### ## @@ -183,7 +224,7 @@ proc update_status {{final Ready.}} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states - global cfg_trust_mtime + global repo_config if {$status_active || ![lock_index read]} return @@ -209,7 +250,7 @@ proc update_status {{final Ready.}} { $ui_comm edit reset } - if {$cfg_trust_mtime == {true}} { + if {$repo_config(gui.trustmtime) == {true}} { update_status_stage2 {} $final } else { set status_active 1 @@ -409,7 +450,7 @@ proc handle_empty_diff {} { The modification date of this file was updated by another application and you currently have the Trust File Modification -Timestamps feature enabled, so Git did not automatically detect +Timestamps option enabled, so Git did not automatically detect that there are no content differences in this file. This file will now be removed from the modified files list, to @@ -502,7 +543,7 @@ proc show_diff {path {w {}} {lno {}}} { proc read_diff {fd} { global ui_diff ui_status_value diff_3way diff_active - global cfg_trust_mtime + global repo_config while {[gets $fd line] >= 0} { if {[string match {diff --git *} $line]} continue @@ -550,7 +591,8 @@ proc read_diff {fd} { unlock_index set ui_status_value {Ready.} - if {$cfg_trust_mtime && [$ui_diff index end] == {2.0}} { + if {$repo_config(gui.trustmtime) == {true} + && [$ui_diff index end] == {2.0}} { handle_empty_diff } } @@ -1314,7 +1356,6 @@ proc hook_failed_popup {hook msg} { set w .hookfail toplevel $w - wm transient $w . frame $w.m label $w.m.l1 -text "$hook hook failed:" \ @@ -1535,11 +1576,13 @@ proc do_repack {} { set is_quitting 0 proc do_quit {} { - global gitdir ui_comm is_quitting + global gitdir ui_comm is_quitting repo_config if {$is_quitting} return set is_quitting 1 + # -- Stash our current commit buffer. + # set save [file join $gitdir GITGUI_MSG] set msg [string trim [$ui_comm get 0.0 end]] if {[$ui_comm edit modified] && $msg != {}} { @@ -1552,7 +1595,19 @@ proc do_quit {} { file delete $save } - save_my_config + # -- Stash our current window geometry into this repository. + # + set cfg_geometry [list] + lappend cfg_geometry [wm geometry .] + lappend cfg_geometry [lindex [.vpane sash coord 0] 1] + lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0] + if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { + set rc_geometry {} + } + if {$cfg_geometry != $rc_geometry} { + catch {exec git repo-config gui.geometry $cfg_geometry} + } + destroy . } @@ -1624,6 +1679,69 @@ proc do_commit {} { commit_tree } +proc do_options {} { + global appname gitdir + global repo_config global_config + global repo_config_new global_config_new + + load_config + array unset repo_config_new + array unset global_config_new + foreach name [array names repo_config] { + set repo_config_new($name) $repo_config($name) + } + foreach name [array names global_config] { + set global_config_new($name) $global_config($name) + } + + set w .options_editor + toplevel $w + + label $w.header -text "$appname Options" \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.save -text Save \ + -font font_ui \ + -command "save_config; destroy $w" + pack $w.buttons.save -side right + button $w.buttons.cancel -text {Cancel} \ + -font font_ui \ + -command "destroy $w" + pack $w.buttons.cancel -side right + pack $w.buttons -side bottom -anchor e -pady 10 -padx 10 + + labelframe $w.repo -text {This Repository} \ + -relief raised -borderwidth 2 + labelframe $w.global -text {Global (All Repositories)} \ + -relief raised -borderwidth 2 + pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 + pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 + + foreach option { + {trustmtime {Trust File Modification Timestamps}} + } { + set name [lindex $option 0] + set text [lindex $option 1] + foreach f {repo global} { + checkbutton $w.$f.$name -text $text \ + -variable ${f}_config_new(gui.$name) \ + -onvalue true \ + -offvalue false \ + -font font_ui + pack $w.$f.$name -side top -anchor w + } + } + + bind $w "grab $w; focus $w" + bind $w "destroy $w" + wm title $w "$appname ([lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end]): Options" + tkwait window $w +} + # shift == 1: left click # 3: right click proc click {w x y shift wx wy} { @@ -1690,7 +1808,6 @@ menu .mbar -tearoff 0 .mbar add cascade -label Fetch -menu .mbar.fetch .mbar add cascade -label Pull -menu .mbar.pull .mbar add cascade -label Push -menu .mbar.push -.mbar add cascade -label Options -menu .mbar.options . configure -menu .mbar # -- Project Menu @@ -1739,6 +1856,10 @@ menu .mbar.edit -command {catch {[focus] tag add sel 0.0 end}} \ -accelerator $M1T-A \ -font font_ui +.mbar.edit add separator +.mbar.edit add command -label {Options...} \ + -command do_options \ + -font font_ui # -- Commit Menu menu .mbar.commit @@ -1779,15 +1900,6 @@ menu .mbar.pull # -- Push Menu menu .mbar.push -# -- Options Menu -menu .mbar.options -.mbar.options add checkbutton \ - -label {Trust File Modification Timestamps} \ - -font font_ui \ - -offvalue false \ - -onvalue true \ - -variable cfg_trust_mtime - # -- Main Window Layout panedwindow .vpane -orient vertical panedwindow .vpane.files -orient horizontal @@ -2032,7 +2144,7 @@ pack .status -anchor w -side bottom -fill x # -- Load geometry catch { -set gm [lindex $repo_config(gui.geometry) 0] +set gm $repo_config(gui.geometry) wm geometry . [lindex $gm 0] .vpane sash place 0 \ [lindex [.vpane sash coord 0] 0] \ -- cgit v1.2.3 From 92148d8091d148e219b88e4d555a386ffa78f575 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 05:27:00 -0500 Subject: git-gui: Allow the user to manipulate the fonts from the options panel. This turned out to take a lot more time than I thought it would take; but now users can edit the main UI font and the diff/fixed with font by changing both the family name and/or the point size of the text. We save the complete Tk font specification to the user's ~/.gitconfig file upon saving options. This is probably more verbose than it needs to be as there are many useless options recorded (e.g. -overstrike 0) that a user won't really want to use in this application. Signed-off-by: Shawn O. Pearce --- git-gui | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 124 insertions(+), 16 deletions(-) diff --git a/git-gui b/git-gui index 520ec1efff..931a959307 100755 --- a/git-gui +++ b/git-gui @@ -14,8 +14,6 @@ set gitdir {} ## ## config -set default_config(gui.trustmtime) false - proc is_many_config {name} { switch -glob -- $name { remote.*.fetch - @@ -69,10 +67,25 @@ proc load_config {} { } proc save_config {} { - global repo_config global_config default_config + global default_config font_descs + global repo_config global_config global repo_config_new global_config_new - foreach name [array names global_config_new] { + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + font configure $font \ + -family $global_config_new(gui.$font^^family) \ + -size $global_config_new(gui.$font^^size) + font configure ${font}bold \ + -family $global_config_new(gui.$font^^family) \ + -size $global_config_new(gui.$font^^size) + set global_config_new(gui.$name) [font configure $font] + unset global_config_new(gui.$font^^family) + unset global_config_new(gui.$font^^size) + } + + foreach name [array names default_config] { set value $global_config_new($name) if {$value != $global_config($name)} { if {$value == $default_config($name)} { @@ -88,7 +101,7 @@ proc save_config {} { } } - foreach name [array names repo_config_new] { + foreach name [array names default_config] { set value $repo_config_new($name) if {$value != $repo_config($name)} { if {$value == $global_config($name)} { @@ -158,8 +171,6 @@ if {$appname == {git-citool}} { set single_commit 1 } -load_config - ###################################################################### ## ## task management @@ -1680,7 +1691,7 @@ proc do_commit {} { } proc do_options {} { - global appname gitdir + global appname gitdir font_descs global repo_config global_config global repo_config_new global_config_new @@ -1702,19 +1713,25 @@ proc do_options {} { pack $w.header -side top -fill x frame $w.buttons + button $w.buttons.restore -text {Restore Defaults} \ + -font font_ui \ + -command do_restore_defaults + pack $w.buttons.restore -side left button $w.buttons.save -text Save \ -font font_ui \ - -command "save_config; destroy $w" + -command [list do_save_config $w] pack $w.buttons.save -side right button $w.buttons.cancel -text {Cancel} \ -font font_ui \ - -command "destroy $w" + -command [list destroy $w] pack $w.buttons.cancel -side right - pack $w.buttons -side bottom -anchor e -pady 10 -padx 10 + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 labelframe $w.repo -text {This Repository} \ + -font font_ui \ -relief raised -borderwidth 2 labelframe $w.global -text {Global (All Repositories)} \ + -font font_ui \ -relief raised -borderwidth 2 pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 @@ -1734,6 +1751,33 @@ proc do_options {} { } } + set all_fonts [lsort [font families]] + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + set text [lindex $option 2] + + set global_config_new(gui.$font^^family) \ + [font configure $font -family] + set global_config_new(gui.$font^^size) \ + [font configure $font -size] + + frame $w.global.$name + label $w.global.$name.l -text "$text:" -font font_ui + pack $w.global.$name.l -side left -anchor w -fill x + eval tk_optionMenu $w.global.$name.family \ + global_config_new(gui.$font^^family) \ + $all_fonts + spinbox $w.global.$name.size \ + -textvariable global_config_new(gui.$font^^size) \ + -from 2 -to 80 -increment 1 \ + -width 3 \ + -font font_ui + pack $w.global.$name.size -side right -anchor e + pack $w.global.$name.family -side right -anchor e + pack $w.global.$name -side top -anchor w -fill x + } + bind $w "grab $w; focus $w" bind $w "destroy $w" wm title $w "$appname ([lindex [file split \ @@ -1742,6 +1786,38 @@ proc do_options {} { tkwait window $w } +proc do_restore_defaults {} { + global font_descs default_config + global repo_config_new global_config_new + + foreach name [array names default_config] { + set repo_config_new($name) $default_config($name) + set global_config_new($name) $default_config($name) + } + + foreach option $font_descs { + set name [lindex $option 0] + set repo_config($name) $default_config(gui.$name) + } + apply_config + + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + set global_config_new(gui.$font^^family) \ + [font configure $font -family] + set global_config_new(gui.$font^^size) \ + [font configure $font -size] + } +} + +proc do_save_config {w} { + if {[catch {save_config} err]} { + error_popup "Failed to completely save options:\n\n$err" + } + destroy $w +} + # shift == 1: left click # 3: right click proc click {w x y shift wx wy} { @@ -1774,7 +1850,7 @@ proc unclick {w x y} { ###################################################################### ## -## ui init +## config defaults set cursor_ptr arrow font create font_diff -family Courier -size 10 @@ -1785,10 +1861,8 @@ catch { destroy .dummy } -eval font create font_uibold [font configure font_ui] -font configure font_uibold -weight bold -eval font create font_diffbold [font configure font_diff] -font configure font_diffbold -weight bold +font create font_uibold +font create font_diffbold set M1B M1 set M1T M1 @@ -1800,6 +1874,40 @@ if {$tcl_platform(platform) == {windows}} { set M1T Cmd } +proc apply_config {} { + global repo_config font_descs + + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + if {[catch { + foreach {cn cv} $repo_config(gui.$name) { + font configure $font $cn $cv + } + } err]} { + error_popup "Invalid font specified in gui.$name:\n\n$err" + } + foreach {cn cv} [font configure $font] { + font configure ${font}bold $cn $cv + } + font configure ${font}bold -weight bold + } +} + +set default_config(gui.trustmtime) false +set default_config(gui.fontui) [font configure font_ui] +set default_config(gui.fontdiff) [font configure font_diff] +set font_descs { + {fontui font_ui {Main Font}} + {fontdiff font_diff {Diff/Console Font}} +} +load_config +apply_config + +###################################################################### +## +## ui construction + # -- Menu Bar menu .mbar -tearoff 0 .mbar add cascade -label Project -menu .mbar.project -- cgit v1.2.3 From 74e6b12f5831a8c93a5d3c90118a73e908d0680a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 06:35:14 -0500 Subject: git-gui: Supply progress feedback when running update-index. The git-update-index process can take a while to process a large number of files; for example my laptop would probably need almost an hour to chug through 20,000 modified files. In these incredibly large cases the user should be given at least some feedback to let them know the application is still working on their behalf, even if it won't them do anything else (as the index is locked). Signed-off-by: Shawn O. Pearce --- git-gui | 143 +++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 79 insertions(+), 64 deletions(-) diff --git a/git-gui b/git-gui index 931a959307..456f53fdc0 100755 --- a/git-gui +++ b/git-gui @@ -178,9 +178,7 @@ if {$appname == {git-citool}} { set single_commit 0 set status_active 0 set diff_active 0 -set update_active 0 set commit_active 0 -set update_index_fd {} set disable_on_lock [list] set index_lock_type none @@ -1100,55 +1098,73 @@ proc display_all_files {} { $ui_other conf -state disabled } -proc with_update_index {body} { - global update_index_fd +proc update_index {pathList} { + global update_index_cp ui_status_value - if {$update_index_fd == {}} { - if {![lock_index update]} return - set update_index_fd [open \ - "| git update-index --add --remove -z --stdin" \ - w] - fconfigure $update_index_fd -translation binary - uplevel 1 $body - close $update_index_fd - set update_index_fd {} - unlock_index - } else { - uplevel 1 $body - } -} + if {![lock_index update]} return -proc update_index {path} { - global update_index_fd + set update_index_cp 0 + set totalCnt [llength $pathList] + set batch [expr {int($totalCnt * .01) + 1}] + if {$batch > 25} {set batch 25} + + set ui_status_value "Including files ... 0/$totalCnt 0%" + set ui_status_value [format \ + "Including files ... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + 0.0] + set fd [open "| git update-index --add --remove -z --stdin" w] + fconfigure $fd -blocking 0 -translation binary + fileevent $fd writable [list \ + write_update_index \ + $fd \ + $pathList \ + $totalCnt \ + $batch \ + ] +} + +proc write_update_index {fd pathList totalCnt batch} { + global update_index_cp ui_status_value + global file_states ui_fname_value - if {$update_index_fd == {}} { - error {not in with_update_index} - } else { - puts -nonewline $update_index_fd "$path\0" + if {$update_index_cp >= $totalCnt} { + close $fd + unlock_index + set ui_status_value {Ready.} + return } -} -proc toggle_mode {path} { - global file_states ui_fname_value - - set s $file_states($path) - set m [lindex $s 0] + for {set i $batch} \ + {$update_index_cp < $totalCnt && $i > 0} \ + {incr i -1} { + set path [lindex $pathList $update_index_cp] + incr update_index_cp + + switch -- [lindex $file_states($path) 0] { + AM - + _O {set new A*} + _M - + MM {set new M*} + AD - + _D {set new D*} + default {continue} + } - switch -- $m { - AM - - _O {set new A*} - _M - - MM {set new M*} - AD - - _D {set new D*} - default {return} + puts -nonewline $fd $path + puts -nonewline $fd "\0" + display_file $path $new + if {$ui_fname_value == $path} { + show_diff $path + } } - with_update_index {update_index $path} - display_file $path $new - if {$ui_fname_value == $path} { - show_diff $path - } + set ui_status_value [format \ + "Including files ... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + [expr {100.0 * $update_index_cp / $totalCnt}]] } ###################################################################### @@ -1627,27 +1643,25 @@ proc do_rescan {} { } proc do_include_all {} { - global update_active ui_status_value - - if {$update_active || ![lock_index begin-update]} return - - set update_active 1 - set ui_status_value {Including all modified files...} - after 1 { - with_update_index { - foreach path [array names file_states] { - set s $file_states($path) - set m [lindex $s 0] - switch -- $m { - AM - - MM - - _M - - _D {toggle_mode $path} - } - } + global file_states + + if {![lock_index begin-update]} return + + set pathList [list] + foreach path [array names file_states] { + set s $file_states($path) + set m [lindex $s 0] + switch -- $m { + AM - + MM - + _M - + _D {lappend pathList $path} } - set update_active 0 - set ui_status_value {Ready.} + } + if {$pathList == {}} { + unlock_index + } else { + update_index $pathList } } @@ -1844,7 +1858,7 @@ proc unclick {w x y} { if {$path == {}} return if {$col == 0} { - toggle_mode $path + update_index [list $path] } } @@ -2318,4 +2332,5 @@ load_all_remotes populate_remote_menu .mbar.fetch From fetch_from populate_remote_menu .mbar.push To push_to populate_pull_menu .mbar.pull +tkwait visibility . update_status -- cgit v1.2.3 From e01b42211cad31b1262d152b7e2561cb8bf218ed Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 06:46:26 -0500 Subject: git-gui: Minor options dialog UI cleanups. Display the name of "this" repository rather than the quite ambiguous string "This". The idea is that seeing the name of the directory the repository is stored in should help jog the user's memory about what they are setting options for. Also place the options dialog immediately over the git-gui main window when it gets opened. This way the user isn't scrolling very far away to gain access to the window. At least on my Mac OS X system not doing this makes the options dialog open rather far away, thus requiring lots of mouse activity to reach it. Signed-off-by: Shawn O. Pearce --- git-gui | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index 456f53fdc0..580110e629 100755 --- a/git-gui +++ b/git-gui @@ -1718,9 +1718,13 @@ proc do_options {} { foreach name [array names global_config] { set global_config_new($name) $global_config($name) } + set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] set w .options_editor toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" label $w.header -text "$appname Options" \ -font font_uibold @@ -1741,7 +1745,7 @@ proc do_options {} { pack $w.buttons.cancel -side right pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - labelframe $w.repo -text {This Repository} \ + labelframe $w.repo -text "$reponame Repository" \ -font font_ui \ -relief raised -borderwidth 2 labelframe $w.global -text {Global (All Repositories)} \ @@ -1794,9 +1798,7 @@ proc do_options {} { bind $w "grab $w; focus $w" bind $w "destroy $w" - wm title $w "$appname ([lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end]): Options" + wm title $w "$appname ($reponame): Options" tkwait window $w } -- cgit v1.2.3 From 8009dcdc8d9bee0b5aab1f6e860a834ffbb0b08f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 06:53:56 -0500 Subject: git-gui: Added Options... menu item to end of diff context menu. Since the font name can only be chosen from within the options dialog giving the user fast access to this dialog from within a context menu that already talks about increasing and decreasing the font size may help users to locate the font name setting as well. Signed-off-by: Shawn O. Pearce --- git-gui | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 580110e629..f959cb6706 100755 --- a/git-gui +++ b/git-gui @@ -2068,7 +2068,7 @@ $ui_index tag conf in_diff -font font_uibold $ui_other tag conf in_diff -font font_uibold # -- Diff and Commit Area -frame .vpane.lower -height 400 -width 400 +frame .vpane.lower -height 300 -width 400 frame .vpane.lower.commarea frame .vpane.lower.diff -relief sunken -borderwidth 1 pack .vpane.lower.commarea -side top -fill x @@ -2254,6 +2254,9 @@ $ui_diff.ctxm add command -label "Decrease Font Size" \ $ui_diff.ctxm add command -label "Increase Font Size" \ -font font_ui \ -command {incr_font_size font_diff 1} +$ui_diff.ctxm add command -label {Options...} \ + -font font_ui \ + -command do_options bind_button3 $ui_diff "tk_popup $ui_diff.ctxm %X %Y" # -- Status Bar -- cgit v1.2.3 From 4af2c384eaae62300765e205c705c7741dd7dd31 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 15:04:15 -0500 Subject: git-gui: Use 'after 1' to post UI rather than tkwait. The tkwait visibility command and Windows doesn't seem to realize the window is visible, consequently we are never finishing our initialization by calling update_status. Signed-off-by: Shawn O. Pearce --- git-gui | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/git-gui b/git-gui index f959cb6706..2138d2d2e1 100755 --- a/git-gui +++ b/git-gui @@ -2337,5 +2337,4 @@ load_all_remotes populate_remote_menu .mbar.fetch From fetch_from populate_remote_menu .mbar.push To push_to populate_pull_menu .mbar.pull -tkwait visibility . -update_status +after 1 update_status -- cgit v1.2.3 From 7b64d0b7d62ec0eb6e8b37f0be2e62f2a719de16 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 15:45:35 -0500 Subject: git-gui: Correct bugs in font config handling. Apparently Tcl is being helpful on Windows during exec and is throwing a \ in front of every { it finds in the string. I'm guessing they think the value might be read by another Tcl program? Anyway, Git faithfully stores the \{ sequence and sends it back that way to Tcl, at which point Tcl parses the list wrong and starts to break it in the middle of any element which contains spaces. Therefore a list such as: -family {Times New Roman} gets broken up into the pairs: {-family \{Times} {New Roman} which is very incorrect. So now we replace all { and } with "", at which point Tcl doesn't throw \ in front of the " on the way out to Git yet it reads it correctly as a list on the way back in. I also found and fixed a bug in the way we restored the fonts when the user presses Restore Defaults in the options dialog. Signed-off-by: Shawn O. Pearce --- git-gui | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index 2138d2d2e1..e59d225cb6 100755 --- a/git-gui +++ b/git-gui @@ -91,7 +91,8 @@ proc save_config {} { if {$value == $default_config($name)} { catch {exec git repo-config --global --unset $name} } else { - catch {exec git repo-config --global $name $value} + regsub -all "\[{}\]" $value {"} value + exec git repo-config --global $name $value } set global_config($name) $value if {$value == $repo_config($name)} { @@ -107,7 +108,8 @@ proc save_config {} { if {$value == $global_config($name)} { catch {exec git repo-config --unset $name} } else { - catch {exec git repo-config $name $value} + regsub -all "\[{}\]" $value {"} value + exec git repo-config $name $value } set repo_config($name) $value } @@ -1803,7 +1805,7 @@ proc do_options {} { } proc do_restore_defaults {} { - global font_descs default_config + global font_descs default_config repo_config global repo_config_new global_config_new foreach name [array names default_config] { @@ -1813,7 +1815,7 @@ proc do_restore_defaults {} { foreach option $font_descs { set name [lindex $option 0] - set repo_config($name) $default_config(gui.$name) + set repo_config(gui.$name) $default_config(gui.$name) } apply_config -- cgit v1.2.3 From 4ccdab028263dfdeb0adf9764466bb3a43265395 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 16:20:36 -0500 Subject: git-gui: Hide non-commit related commands when invoked as git-citool. If the user is invoking us as git-citool then they want to perform a single commit and exit quickly. Since we are about to be a very short lived process we should do what we can to avoid spending CPU time setting up menus which the user will never use, like the fetch/push/pull menus. Signed-off-by: Shawn O. Pearce --- git-gui | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/git-gui b/git-gui index e59d225cb6..249b2c894a 100755 --- a/git-gui +++ b/git-gui @@ -169,6 +169,7 @@ if {$cdup != ""} { } unset cdup +set single_commit 0 if {$appname == {git-citool}} { set single_commit 1 } @@ -177,7 +178,6 @@ if {$appname == {git-citool}} { ## ## task management -set single_commit 0 set status_active 0 set diff_active 0 set commit_active 0 @@ -1931,9 +1931,11 @@ menu .mbar -tearoff 0 .mbar add cascade -label Project -menu .mbar.project .mbar add cascade -label Edit -menu .mbar.edit .mbar add cascade -label Commit -menu .mbar.commit -.mbar add cascade -label Fetch -menu .mbar.fetch -.mbar add cascade -label Pull -menu .mbar.pull -.mbar add cascade -label Push -menu .mbar.push +if {!$single_commit} { + .mbar add cascade -label Fetch -menu .mbar.fetch + .mbar add cascade -label Pull -menu .mbar.pull + .mbar add cascade -label Push -menu .mbar.push +} . configure -menu .mbar # -- Project Menu @@ -1941,9 +1943,11 @@ menu .mbar.project .mbar.project add command -label Visualize \ -command do_gitk \ -font font_ui -.mbar.project add command -label {Repack Database} \ - -command do_repack \ - -font font_ui +if {!$single_commit} { + .mbar.project add command -label {Repack Database} \ + -command do_repack \ + -font font_ui +} .mbar.project add command -label Quit \ -command do_quit \ -accelerator $M1T-Q \ @@ -2017,14 +2021,16 @@ lappend disable_on_lock \ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -# -- Fetch Menu -menu .mbar.fetch +if {!$single_commit} { + # -- Fetch Menu + menu .mbar.fetch -# -- Pull Menu -menu .mbar.pull + # -- Pull Menu + menu .mbar.pull -# -- Push Menu -menu .mbar.push + # -- Push Menu + menu .mbar.push +} # -- Main Window Layout panedwindow .vpane -orient vertical @@ -2335,8 +2341,10 @@ set file_lists($ui_other) [list] wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm -load_all_remotes -populate_remote_menu .mbar.fetch From fetch_from -populate_remote_menu .mbar.push To push_to -populate_pull_menu .mbar.pull +if {!$single_commit} { + load_all_remotes + populate_remote_menu .mbar.fetch From fetch_from + populate_remote_menu .mbar.push To push_to + populate_pull_menu .mbar.pull +} after 1 update_status -- cgit v1.2.3 From 6bbd1cb95aede2991d0748d2a54088f2c1291602 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 16:24:52 -0500 Subject: git-gui: Don't load the global options unless necessary. Since git-repo-config will supply us a union of both the global and the local repository configuration data when we invoke it during startup there is no reason to go get the global configuration with an extra call to repo-config unless the user is trying to view & edit all options in the options dialog. Since skipping this extra repo-config invocation save us a little bit of time its nice to be able to avoid it when we are invoked as git-citool and won't be running very long. Signed-off-by: Shawn O. Pearce --- git-gui | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/git-gui b/git-gui index 249b2c894a..fbb3090ed1 100755 --- a/git-gui +++ b/git-gui @@ -24,24 +24,27 @@ proc is_many_config {name} { } } -proc load_config {} { +proc load_config {include_global} { global repo_config global_config default_config array unset global_config - array unset repo_config - catch { - set fd_rc [open "| git repo-config --global --list" r] - while {[gets $fd_rc line] >= 0} { - if {[regexp {^([^=]+)=(.*)$} $line line name value]} { - if {[is_many_config $name]} { - lappend global_config($name) $value - } else { - set global_config($name) $value + if {$include_global} { + catch { + set fd_rc [open "| git repo-config --global --list" r] + while {[gets $fd_rc line] >= 0} { + if {[regexp {^([^=]+)=(.*)$} $line line name value]} { + if {[is_many_config $name]} { + lappend global_config($name) $value + } else { + set global_config($name) $value + } } } + close $fd_rc } - close $fd_rc } + + array unset repo_config catch { set fd_rc [open "| git repo-config --list" r] while {[gets $fd_rc line] >= 0} { @@ -1711,7 +1714,7 @@ proc do_options {} { global repo_config global_config global repo_config_new global_config_new - load_config + load_config 1 array unset repo_config_new array unset global_config_new foreach name [array names repo_config] { @@ -1919,7 +1922,7 @@ set font_descs { {fontui font_ui {Main Font}} {fontdiff font_diff {Diff/Console Font}} } -load_config +load_config 0 apply_config ###################################################################### -- cgit v1.2.3 From ebf336b9422302ec89bce14b83017060d7ccd3e6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 16:53:19 -0500 Subject: git-gui: Allow the user to disable diff stat summary during pull. Because the pull diffstat summary can take as long as the pull itself some users may just choose to disable the summary and save themselves an extra few seconds during each pull. This is especially true if the user really doesn't care about the other files being modified, as due to their project organizational structure they aren't really responsible for their content. This adds an option to the options panel which lets the user disable the diffstat summary (and thus we pass --no-summary to git-pull) but there does appear to be a bug in the config saving code where we did not set the local repo config differently from the global config. Signed-off-by: Shawn O. Pearce --- git-gui | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index fbb3090ed1..761ce7551f 100755 --- a/git-gui +++ b/git-gui @@ -896,8 +896,7 @@ proc fetch_from {remote} { } proc pull_remote {remote branch} { - global HEAD commit_type - global file_states + global HEAD commit_type file_states repo_config if {![lock_index update]} return @@ -933,6 +932,9 @@ Commit or throw away all changes before starting a pull operation. set w [new_console "pull $remote $branch" \ "Pulling new changes from branch $branch in $remote"] set cmd [list git pull] + if {$repo_config(gui.pullsummary) == {false}} { + lappend cmd --no-summary + } lappend cmd $remote lappend cmd $branch console_exec $w $cmd [list post_pull_remote $remote $branch] @@ -1760,6 +1762,7 @@ proc do_options {} { pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 foreach option { + {pullsummary {Show Pull Summary}} {trustmtime {Trust File Modification Timestamps}} } { set name [lindex $option 0] @@ -1916,6 +1919,7 @@ proc apply_config {} { } set default_config(gui.trustmtime) false +set default_config(gui.pullsummary) true set default_config(gui.fontui) [font configure font_ui] set default_config(gui.fontdiff) [font configure font_diff] set font_descs { -- cgit v1.2.3 From 4658b56fce4e8b7c4489ae7b0fe9ecf1e236b70b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 17:58:08 -0500 Subject: git-gui: Run the pre-commit hook in the background. I started to notice on Windows that commits took a lot longer to get going than on my Mac OS X system. The real reason is the repositories that I'm testing with on Windows all enabled the standard pre-commit hook while my test repository on Mac OS X doesn't have it executable (so its not running). So the Windows repositories are spending this lag time running that hook. Now we run the pre-commit hook in the background, allowing the UI to update and tell the user we are busy doing things. Signed-off-by: Shawn O. Pearce --- git-gui | 55 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/git-gui b/git-gui index 761ce7551f..bee17de4dd 100755 --- a/git-gui +++ b/git-gui @@ -664,8 +664,8 @@ proc load_last_commit {} { proc commit_tree {} { global tcl_platform HEAD gitdir commit_type file_states - global commit_active ui_status_value - global ui_comm + global commit_active pch_error + global ui_status_value ui_comm if {$commit_active || ![lock_index update]} return @@ -739,33 +739,64 @@ A good commit message has the following format: return } + set commit_active 1 + # -- Ask the pre-commit hook for the go-ahead. # set pchook [file join $gitdir hooks pre-commit] if {$tcl_platform(platform) == {windows} && [file isfile $pchook]} { - set pchook [list sh -c \ - "if test -x \"$pchook\"; then exec \"$pchook\"; fi"] + set pchook [list sh -c [concat \ + "if test -x \"$pchook\";" \ + "then exec \"$pchook\" 2>&1;" \ + "fi"]] } elseif {[file executable $pchook]} { - set pchook [list $pchook] + set pchook [list $pchook |& cat] } else { set pchook {} } - if {$pchook != {} && [catch {eval exec $pchook} err]} { - hook_failed_popup pre-commit $err - unlock_index + if {$pchook != {}} { + set ui_status_value {Calling pre-commit hook...} + set pch_error {} + set fd_ph [open "| $pchook" r] + fconfigure $fd_ph -blocking 0 -translation binary + fileevent $fd_ph readable \ + [list commit_stage1 $fd_ph $curHEAD $msg] + } else { + commit_stage2 $curHEAD $msg + } +} + +proc commit_stage1 {fd_ph curHEAD msg} { + global commit_active pch_error ui_status_value + + 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.} + hook_failed_popup pre-commit $pch_error + unlock_index + set commit_active 0 + set pch_error {} + return + } + commit_stage2 $curHEAD $msg return } + fconfigure $fd_ph -blocking 0 +} + +proc commit_stage2 {curHEAD msg} { + global ui_status_value # -- Write the tree in the background. # - set commit_active 1 set ui_status_value {Committing changes...} - set fd_wt [open "| git write-tree" r] - fileevent $fd_wt readable [list commit_stage2 $fd_wt $curHEAD $msg] + fileevent $fd_wt readable [list commit_stage3 $fd_wt $curHEAD $msg] } -proc commit_stage2 {fd_wt curHEAD msg} { +proc commit_stage3 {fd_wt curHEAD msg} { global single_commit gitdir HEAD PARENT commit_type global commit_active ui_status_value ui_comm global file_states -- cgit v1.2.3 From 333b0c74b33fe54f0489813950345edbeb62c0b5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 18:03:19 -0500 Subject: git-gui: Remove the commit_active global variable. We were originally trying to use $commit_active to tell us if there was a commit currently in progress, just so we didn't attempt to start a second (parallel) one by mistake. But really the index lock handles this for us as it won't let us lock the index if it is already locked for update. So this can't happen. Signed-off-by: Shawn O. Pearce --- git-gui | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/git-gui b/git-gui index bee17de4dd..3ccf0a41de 100755 --- a/git-gui +++ b/git-gui @@ -183,7 +183,6 @@ if {$appname == {git-citool}} { set status_active 0 set diff_active 0 -set commit_active 0 set disable_on_lock [list] set index_lock_type none @@ -664,10 +663,10 @@ proc load_last_commit {} { proc commit_tree {} { global tcl_platform HEAD gitdir commit_type file_states - global commit_active pch_error + global pch_error global ui_status_value ui_comm - if {$commit_active || ![lock_index update]} return + if {![lock_index update]} return # -- Our in memory state should match the repository. # @@ -739,8 +738,6 @@ A good commit message has the following format: return } - set commit_active 1 - # -- Ask the pre-commit hook for the go-ahead. # set pchook [file join $gitdir hooks pre-commit] @@ -767,7 +764,7 @@ A good commit message has the following format: } proc commit_stage1 {fd_ph curHEAD msg} { - global commit_active pch_error ui_status_value + global pch_error ui_status_value append pch_error [read $fd_ph] fconfigure $fd_ph -blocking 1 @@ -776,14 +773,13 @@ proc commit_stage1 {fd_ph curHEAD msg} { set ui_status_value {Commit declined by pre-commit hook.} hook_failed_popup pre-commit $pch_error unlock_index - set commit_active 0 - set pch_error {} - return + } else { + commit_stage2 $curHEAD $msg } - commit_stage2 $curHEAD $msg - return + set pch_error {} + } else { + fconfigure $fd_ph -blocking 0 } - fconfigure $fd_ph -blocking 0 } proc commit_stage2 {curHEAD msg} { @@ -798,13 +794,12 @@ proc commit_stage2 {curHEAD msg} { proc commit_stage3 {fd_wt curHEAD msg} { global single_commit gitdir HEAD PARENT commit_type - global commit_active ui_status_value ui_comm + global ui_status_value ui_comm global file_states gets $fd_wt tree_id if {$tree_id == {} || [catch {close $fd_wt} err]} { error_popup "write-tree failed:\n\n$err" - set commit_active 0 set ui_status_value {Commit failed.} unlock_index return @@ -825,7 +820,6 @@ proc commit_stage3 {fd_wt curHEAD msg} { close $fd_mh } err]} { error_popup "Loading MERGE_HEAD failed:\n\n$err" - set commit_active 0 set ui_status_value {Commit failed.} unlock_index return @@ -838,7 +832,6 @@ proc commit_stage3 {fd_wt curHEAD msg} { lappend cmd << $msg if {[catch {set cmt_id [eval exec $cmd]} err]} { error_popup "commit-tree failed:\n\n$err" - set commit_active 0 set ui_status_value {Commit failed.} unlock_index return @@ -859,7 +852,6 @@ proc commit_stage3 {fd_wt curHEAD msg} { set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD] if {[catch {eval exec $cmd} err]} { error_popup "update-ref failed:\n\n$err" - set commit_active 0 set ui_status_value {Commit failed.} unlock_index return @@ -886,7 +878,6 @@ proc commit_stage3 {fd_wt curHEAD msg} { # -- Update status without invoking any git commands. # - set commit_active 0 set commit_type normal set HEAD $cmt_id set PARENT $cmt_id -- cgit v1.2.3 From c8ebafd84537473bb8a53880a6a6740d723b83bc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 18:08:10 -0500 Subject: git-gui: Added post-commit invocation after the commit is done. Since git-commit.sh invokes hooks/post-commit after running git rerere we should do the same if its available and executable. Signed-off-by: Shawn O. Pearce --- git-gui | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 3ccf0a41de..7eeff9061c 100755 --- a/git-gui +++ b/git-gui @@ -793,7 +793,7 @@ proc commit_stage2 {curHEAD msg} { } proc commit_stage3 {fd_wt curHEAD msg} { - global single_commit gitdir HEAD PARENT commit_type + global single_commit gitdir HEAD PARENT commit_type tcl_platform global ui_status_value ui_comm global file_states @@ -870,6 +870,21 @@ proc commit_stage3 {fd_wt curHEAD msg} { catch {exec git rerere} } + # -- Run the post-commit hook. + # + set pchook [file join $gitdir hooks post-commit] + if {$tcl_platform(platform) == {windows} && [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 != {}} { + catch {exec $pchook &} + } + $ui_comm delete 0.0 end $ui_comm edit modified false $ui_comm edit reset -- cgit v1.2.3 From 043f701116c30067c2a7096135b6e419dd1f7b47 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 18:16:45 -0500 Subject: git-gui: Always use eq/ne for string comparsions. This is one of those stupid Tcl mistakes that an experienced Tcl programmer just wouldn't make. We should always use eq and ne to compare string values (and never == or !=) as when we use ==/!= Tcl will attempt to convert either side to numeric if one of the two sides looks like a numeric. This could cause some trouble if a file named "1" exists and a different file named "1.0" also exists; their paths are equal according to == but not according to eq. Signed-off-by: Shawn O. Pearce --- git-gui | 140 ++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/git-gui b/git-gui index 7eeff9061c..05ef8e2e33 100755 --- a/git-gui +++ b/git-gui @@ -90,15 +90,15 @@ proc save_config {} { foreach name [array names default_config] { set value $global_config_new($name) - if {$value != $global_config($name)} { - if {$value == $default_config($name)} { + if {$value ne $global_config($name)} { + if {$value eq $default_config($name)} { catch {exec git repo-config --global --unset $name} } else { regsub -all "\[{}\]" $value {"} value exec git repo-config --global $name $value } set global_config($name) $value - if {$value == $repo_config($name)} { + if {$value eq $repo_config($name)} { catch {exec git repo-config --unset $name} set repo_config($name) $value } @@ -107,8 +107,8 @@ proc save_config {} { foreach name [array names default_config] { set value $repo_config_new($name) - if {$value != $repo_config($name)} { - if {$value == $global_config($name)} { + if {$value ne $repo_config($name)} { + if {$value eq $global_config($name)} { catch {exec git repo-config --unset $name} } else { regsub -all "\[{}\]" $value {"} value @@ -123,7 +123,7 @@ proc error_popup {msg} { global gitdir appname set title $appname - if {$gitdir != {}} { + if {$gitdir ne {}} { append title { (} append title [lindex \ [file split [file normalize [file dirname $gitdir]]] \ @@ -142,7 +142,7 @@ proc info_popup {msg} { global gitdir appname set title $appname - if {$gitdir != {}} { + if {$gitdir ne {}} { append title { (} append title [lindex \ [file split [file normalize [file dirname $gitdir]]] \ @@ -167,13 +167,13 @@ if { [catch {set cdup [exec git rev-parse --show-cdup]} err] error_popup "Cannot find the git directory:\n\n$err" exit 1 } -if {$cdup != ""} { +if {$cdup ne ""} { cd $cdup } unset cdup set single_commit 0 -if {$appname == {git-citool}} { +if {$appname eq {git-citool}} { set single_commit 1 } @@ -194,13 +194,13 @@ set commit_type {} proc lock_index {type} { global index_lock_type disable_on_lock - if {$index_lock_type == {none}} { + if {$index_lock_type eq {none}} { set index_lock_type $type foreach w $disable_on_lock { uplevel #0 $w disabled } return 1 - } elseif {$index_lock_type == {begin-update} && $type == {update}} { + } elseif {$index_lock_type eq {begin-update} && $type eq {update}} { set index_lock_type $type return 1 } @@ -242,9 +242,9 @@ proc update_status {{final Ready.}} { if {$status_active || ![lock_index read]} return repository_state new_HEAD new_type - if {$commit_type == {amend} - && $new_type == {normal} - && $new_HEAD == $HEAD} { + if {$commit_type eq {amend} + && $new_type eq {normal} + && $new_HEAD eq $HEAD} { } else { set HEAD $new_HEAD set PARENT $new_HEAD @@ -254,7 +254,7 @@ proc update_status {{final Ready.}} { array unset file_states if {![$ui_comm edit modified] - || [string trim [$ui_comm get 0.0 end]] == {}} { + || [string trim [$ui_comm get 0.0 end]] eq {}} { if {[load_message GITGUI_MSG]} { } elseif {[load_message MERGE_MSG]} { } elseif {[load_message SQUASH_MSG]} { @@ -263,7 +263,7 @@ proc update_status {{final Ready.}} { $ui_comm edit reset } - if {$repo_config(gui.trustmtime) == {true}} { + if {$repo_config(gui.trustmtime) eq {true}} { update_status_stage2 {} $final } else { set status_active 1 @@ -286,7 +286,7 @@ proc update_status_stage2 {fd final} { global status_active global buf_rdi buf_rdf buf_rlo - if {$fd != {}} { + if {$fd ne {}} { read $fd if {![eof $fd]} return close $fd @@ -442,7 +442,7 @@ proc clear_diff {} { proc reshow_diff {} { global ui_fname_value ui_status_value file_states - if {$ui_fname_value == {} + if {$ui_fname_value eq {} || [catch {set s $file_states($ui_fname_value)}]} { clear_diff } else { @@ -455,7 +455,7 @@ proc handle_empty_diff {} { set path $ui_fname_value set s $file_states($path) - if {[lindex $s 0] != {_M}} return + if {[lindex $s 0] ne {_M}} return info_popup "No differences detected. @@ -494,7 +494,7 @@ proc show_diff {path {w {}} {lno {}}} { if {$diff_active || ![lock_index read]} return clear_diff - if {$w == {} || $lno == {}} { + if {$w eq {} || $lno == {}} { foreach w [array names file_lists] { set lno [lsearch -sorted $file_lists($w) $path] if {$lno >= 0} { @@ -503,7 +503,7 @@ proc show_diff {path {w {}} {lno {}}} { } } } - if {$w != {} && $lno >= 1} { + if {$w ne {} && $lno >= 1} { $w tag add in_diff $lno.0 [expr $lno + 1].0 } @@ -604,8 +604,8 @@ proc read_diff {fd} { unlock_index set ui_status_value {Ready.} - if {$repo_config(gui.trustmtime) == {true} - && [$ui_diff index end] == {2.0}} { + if {$repo_config(gui.trustmtime) eq {true} + && [$ui_diff index end] eq {2.0}} { handle_empty_diff } } @@ -618,8 +618,8 @@ proc read_diff {fd} { proc load_last_commit {} { global HEAD PARENT commit_type ui_comm - if {$commit_type == {amend}} return - if {$commit_type != {normal}} { + if {$commit_type eq {amend}} return + if {$commit_type ne {normal}} { error_popup "Can't amend a $commit_type commit." return } @@ -671,10 +671,10 @@ proc commit_tree {} { # -- Our in memory state should match the repository. # repository_state curHEAD cur_type - if {$commit_type == {amend} - && $cur_type == {normal} - && $curHEAD == $HEAD} { - } elseif {$commit_type != $cur_type || $HEAD != $curHEAD} { + if {$commit_type eq {amend} + && $cur_type eq {normal} + && $curHEAD eq $HEAD} { + } elseif {$commit_type ne $cur_type || $HEAD ne $curHEAD} { error_popup {Last scanned state does not match repository state. Its highly likely that another Git program modified the @@ -725,7 +725,7 @@ You must include at least 1 file before you can commit. # -- A message is required. # set msg [string trim [$ui_comm get 1.0 end]] - if {$msg == {}} { + if {$msg eq {}} { error_popup {Please supply a commit message. A good commit message has the following format: @@ -741,7 +741,7 @@ A good commit message has the following format: # -- Ask the pre-commit hook for the go-ahead. # set pchook [file join $gitdir hooks pre-commit] - if {$tcl_platform(platform) == {windows} && [file isfile $pchook]} { + if {$tcl_platform(platform) eq {windows} && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ "then exec \"$pchook\" 2>&1;" \ @@ -751,7 +751,7 @@ A good commit message has the following format: } else { set pchook {} } - if {$pchook != {}} { + if {$pchook ne {}} { set ui_status_value {Calling pre-commit hook...} set pch_error {} set fd_ph [open "| $pchook" r] @@ -798,7 +798,7 @@ proc commit_stage3 {fd_wt curHEAD msg} { global file_states gets $fd_wt tree_id - if {$tree_id == {} || [catch {close $fd_wt} err]} { + if {$tree_id eq {} || [catch {close $fd_wt} err]} { error_popup "write-tree failed:\n\n$err" set ui_status_value {Commit failed.} unlock_index @@ -808,10 +808,10 @@ proc commit_stage3 {fd_wt curHEAD msg} { # -- Create the commit. # set cmd [list git commit-tree $tree_id] - if {$PARENT != {}} { + if {$PARENT ne {}} { lappend cmd -p $PARENT } - if {$commit_type == {merge}} { + if {$commit_type eq {merge}} { if {[catch { set fd_mh [open [file join $gitdir MERGE_HEAD] r] while {[gets $fd_mh merge_head] >= 0} { @@ -825,7 +825,7 @@ proc commit_stage3 {fd_wt curHEAD msg} { return } } - if {$PARENT == {}} { + if {$PARENT eq {}} { # git commit-tree writes to stderr during initial commit. lappend cmd 2>/dev/null } @@ -840,7 +840,7 @@ proc commit_stage3 {fd_wt curHEAD msg} { # -- Update the HEAD ref. # set reflogm commit - if {$commit_type != {normal}} { + if {$commit_type ne {normal}} { append reflogm " ($commit_type)" } set i [string first "\n" $msg] @@ -873,7 +873,7 @@ proc commit_stage3 {fd_wt curHEAD msg} { # -- Run the post-commit hook. # set pchook [file join $gitdir hooks post-commit] - if {$tcl_platform(platform) == {windows} && [file isfile $pchook]} { + if {$tcl_platform(platform) eq {windows} && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ "then exec \"$pchook\";" \ @@ -881,7 +881,7 @@ proc commit_stage3 {fd_wt curHEAD msg} { } elseif {![file executable $pchook]} { set pchook {} } - if {$pchook != {}} { + if {$pchook ne {}} { catch {exec $pchook &} } @@ -906,7 +906,7 @@ proc commit_stage3 {fd_wt curHEAD msg} { D? {set m _[string index $m 1]} } - if {$m == {__}} { + if {$m eq {__}} { unset file_states($path) } else { lset file_states($path) 0 $m @@ -940,7 +940,7 @@ proc pull_remote {remote branch} { # -- Our in memory state should match the repository. # repository_state curHEAD cur_type - if {$commit_type != $cur_type || $HEAD != $curHEAD} { + if {$commit_type ne $cur_type || $HEAD ne $curHEAD} { error_popup {Last scanned state does not match repository state. Its highly likely that another Git program modified the @@ -969,7 +969,7 @@ Commit or throw away all changes before starting a pull operation. set w [new_console "pull $remote $branch" \ "Pulling new changes from branch $branch in $remote"] set cmd [list git pull] - if {$repo_config(gui.pullsummary) == {false}} { + if {$repo_config(gui.pullsummary) eq {false}} { lappend cmd --no-summary } lappend cmd $remote @@ -1059,15 +1059,15 @@ proc merge_state {path new_state} { set icon [lindex $info 1] } - if {$s0 == {_}} { + if {$s0 eq {_}} { set s0 [string index $state 0] - } elseif {$s0 == {*}} { + } elseif {$s0 eq {*}} { set s0 _ } - if {$s1 == {_}} { + if {$s1 eq {_}} { set s1 [string index $state 1] - } elseif {$s1 == {*}} { + } elseif {$s1 eq {*}} { set s1 _ } @@ -1087,7 +1087,7 @@ proc display_file {path state} { set old_w [mapcol $old_m $path] set new_icon [mapicon $new_m $path] - if {$new_w != $old_w} { + if {$new_w ne $old_w} { set lno [lsearch -sorted $file_lists($old_w) $path] if {$lno >= 0} { incr lno @@ -1107,7 +1107,7 @@ proc display_file {path state} { -image $new_icon $new_w insert $lno.1 "[escape_path $path]\n" $new_w conf -state disabled - } elseif {$new_icon != [mapicon $old_m $path]} { + } elseif {$new_icon ne [mapicon $old_m $path]} { $new_w conf -state normal $new_w image conf [lindex $s 1] -image $new_icon $new_w conf -state disabled @@ -1199,7 +1199,7 @@ proc write_update_index {fd pathList totalCnt batch} { puts -nonewline $fd $path puts -nonewline $fd "\0" display_file $path $new - if {$ui_fname_value == $path} { + if {$ui_fname_value eq $path} { show_diff $path } } @@ -1252,8 +1252,8 @@ proc populate_pull_menu {m} { foreach remote $all_remotes { set rb {} - if {[array get repo_config remote.$remote.url] != {}} { - if {[array get repo_config remote.$remote.fetch] != {}} { + if {[array get repo_config remote.$remote.url] ne {}} { + if {[array get repo_config remote.$remote.fetch] ne {}} { regexp {^([^:]+):} \ [lindex $repo_config(remote.$remote.fetch) 0] \ line rb @@ -1272,7 +1272,7 @@ proc populate_pull_menu {m} { set rb_short $rb regsub ^refs/heads/ $rb {} rb_short - if {$rb_short != {}} { + if {$rb_short ne {}} { $m add command \ -label "Branch $rb_short from $remote..." \ -command [list pull_remote $remote $rb] \ @@ -1384,7 +1384,7 @@ foreach i { if {$max_status_desc < [string length [lindex $i 3]]} { set max_status_desc [string length [lindex $i 3]] } - if {[lindex $i 1] == {i}} { + if {[lindex $i 1] eq {i}} { set all_cols([lindex $i 0]) $ui_index } else { set all_cols([lindex $i 0]) $ui_other @@ -1400,8 +1400,8 @@ unset filemask i proc is_MacOSX {} { global tcl_platform tk_library - if {$tcl_platform(platform) == {unix} - && $tcl_platform(os) == {Darwin} + if {$tcl_platform(platform) eq {unix} + && $tcl_platform(os) eq {Darwin} && [string match /Library/Frameworks/* $tk_library]} { return 1 } @@ -1543,7 +1543,7 @@ proc console_exec {w cmd {after {}}} { # -- Windows tosses the enviroment when we exec our child. # But most users need that so we have to relogin. :-( # - if {$tcl_platform(platform) == {windows}} { + if {$tcl_platform(platform) eq {windows}} { set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"] } @@ -1561,7 +1561,7 @@ proc console_read {w fd after} { global console_cr console_data set buf [read $fd] - if {$buf != {}} { + if {$buf ne {}} { if {![winfo exists $w]} {console_init $w} $w.m.t conf -state normal set c 0 @@ -1605,7 +1605,7 @@ proc console_read {w fd after} { } array unset console_cr $w array unset console_data $w - if {$after != {}} { + if {$after ne {}} { uplevel #0 $after $ok } return @@ -1624,12 +1624,12 @@ proc do_gitk {} { set ui_status_value $starting_gitk_msg after 10000 { - if {$ui_status_value == $starting_gitk_msg} { + if {$ui_status_value eq $starting_gitk_msg} { set ui_status_value {Ready.} } } - if {$tcl_platform(platform) == {windows}} { + if {$tcl_platform(platform) eq {windows}} { exec sh -c gitk & } else { exec gitk & @@ -1656,13 +1656,13 @@ proc do_quit {} { # set save [file join $gitdir GITGUI_MSG] set msg [string trim [$ui_comm get 0.0 end]] - if {[$ui_comm edit modified] && $msg != {}} { + if {[$ui_comm edit modified] && $msg ne {}} { catch { set fd [open $save w] puts $fd [string trim [$ui_comm get 0.0 end]] close $fd } - } elseif {$msg == {} && [file exists $save]} { + } elseif {$msg eq {} && [file exists $save]} { file delete $save } @@ -1675,7 +1675,7 @@ proc do_quit {} { if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { set rc_geometry {} } - if {$cfg_geometry != $rc_geometry} { + if {$cfg_geometry ne $rc_geometry} { catch {exec git repo-config gui.geometry $cfg_geometry} } @@ -1702,7 +1702,7 @@ proc do_include_all {} { _D {lappend pathList $path} } } - if {$pathList == {}} { + if {$pathList eq {}} { unlock_index } else { update_index $pathList @@ -1714,7 +1714,7 @@ set GIT_COMMITTER_IDENT {} proc do_signoff {} { global ui_comm GIT_COMMITTER_IDENT - if {$GIT_COMMITTER_IDENT == {}} { + if {$GIT_COMMITTER_IDENT eq {}} { if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} { error_popup "Unable to obtain your identity:\n\n$err" return @@ -1728,9 +1728,9 @@ proc do_signoff {} { set sob "Signed-off-by: $GIT_COMMITTER_IDENT" set last [$ui_comm get {end -1c linestart} {end -1c}] - if {$last != $sob} { + if {$last ne $sob} { $ui_comm edit separator - if {$last != {} + if {$last ne {} && ![regexp {^[A-Z][A-Za-z]*-[A-Za-z-]+: *} $last]} { $ui_comm insert end "\n" } @@ -1888,7 +1888,7 @@ proc click {w x y shift wx wy} { set lno [lindex $pos 0] set col [lindex $pos 1] set path [lindex $file_lists($w) [expr $lno - 1]] - if {$path == {}} return + if {$path eq {}} return if {$col > 0 && $shift == 1} { show_diff $path $w $lno @@ -1902,7 +1902,7 @@ proc unclick {w x y} { set lno [lindex $pos 0] set col [lindex $pos 1] set path [lindex $file_lists($w) [expr $lno - 1]] - if {$path == {}} return + if {$path eq {}} return if {$col == 0} { update_index [list $path] @@ -1927,7 +1927,7 @@ font create font_diffbold set M1B M1 set M1T M1 -if {$tcl_platform(platform) == {windows}} { +if {$tcl_platform(platform) eq {windows}} { set M1B Control set M1T Ctrl } elseif {[is_MacOSX]} { -- cgit v1.2.3 From 2cbe5577a0e83ae7368eb2202ffe62c1d607b0bb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 18:22:59 -0500 Subject: git-gui: Reshow diff if we sent the file to update-index. We can't ask the diff viewer to recompute the diff until after our update-index child process terminates, as the diff programs need to be able to read the updated index in order to generate the correct diff. This is actually why we prevent diffs from being generated while there is an update lock on the index, which is why we ignored our own show_diff invocation in the middle of the write_update_index event handler. So now we mark a flag if we identify that the file currently in the diff viewer was also sent to update-index; then later when the update-index process has terminated we update the diff viewer if the flag is true. Signed-off-by: Shawn O. Pearce --- git-gui | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index 05ef8e2e33..fdb82812bd 100755 --- a/git-gui +++ b/git-gui @@ -1143,11 +1143,12 @@ proc display_all_files {} { } proc update_index {pathList} { - global update_index_cp ui_status_value + global update_index_cp update_index_rsd ui_status_value if {![lock_index update]} return set update_index_cp 0 + set update_index_rsd 0 set totalCnt [llength $pathList] set batch [expr {int($totalCnt * .01) + 1}] if {$batch > 25} {set batch 25} @@ -1170,13 +1171,17 @@ proc update_index {pathList} { } proc write_update_index {fd pathList totalCnt batch} { - global update_index_cp ui_status_value + global update_index_cp update_index_rsd ui_status_value global file_states ui_fname_value if {$update_index_cp >= $totalCnt} { close $fd unlock_index - set ui_status_value {Ready.} + if {$update_index_rsd} { + show_diff $ui_fname_value + } else { + set ui_status_value {Ready.} + } return } @@ -1200,7 +1205,7 @@ proc write_update_index {fd pathList totalCnt batch} { puts -nonewline $fd "\0" display_file $path $new if {$ui_fname_value eq $path} { - show_diff $path + set update_index_rsd 1 } } -- cgit v1.2.3 From fd2656fdfe575b55424b672f8bc274bf86526b9e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 18:51:38 -0500 Subject: git-gui: Cleanup diff construction code to prepare for more options. I'd like to allow the user to have more control over how we format the diff in the diff viewer; to that end we need to add additional options to the diff-index command line as we construct the command for execution. So cleanup the command handling code now to use lappend so we can come back and add in our additional options. Signed-off-by: Shawn O. Pearce --- git-gui | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index fdb82812bd..ea71526e60 100755 --- a/git-gui +++ b/git-gui @@ -488,7 +488,7 @@ prevent possible confusion. proc show_diff {path {w {}} {lno {}}} { global file_states file_lists - global PARENT diff_3way diff_active + global PARENT diff_3way diff_active repo_config global ui_diff ui_fname_value ui_fstatus_value ui_status_value if {$diff_active || ![lock_index read]} return @@ -515,10 +515,13 @@ proc show_diff {path {w {}} {lno {}}} { set ui_fstatus_value [mapdesc $m $path] set ui_status_value "Loading diff of [escape_path $path]..." - set cmd [list | git diff-index -p $PARENT -- $path] + set cmd [list | git diff-index] + lappend cmd --no-color + lappend cmd -p + switch $m { MM { - set cmd [list | git diff-index -p -c $PARENT $path] + lappend cmd -c } _O { if {[catch { @@ -542,6 +545,10 @@ proc show_diff {path {w {}} {lno {}}} { } } + lappend cmd $PARENT + lappend cmd -- + lappend cmd $path + if {[catch {set fd [open $cmd r]} err]} { set diff_active 0 unlock_index @@ -1178,7 +1185,7 @@ proc write_update_index {fd pathList totalCnt batch} { close $fd unlock_index if {$update_index_rsd} { - show_diff $ui_fname_value + reshow_diff } else { set ui_status_value {Ready.} } -- cgit v1.2.3 From 358d8de8f3b9d09c9c4d7d43c03d33a4f60ba1da Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 19:20:02 -0500 Subject: git-gui: Allow the user to control the number of context lines in a diff. When displaying a diff the Git default of 3 line of context may not be enough for a user to see what has actually changed. Consequently we set our own program default to 5 lines of context and then allow the user to adjust this on a per-repository and global level through our options dialog. Signed-off-by: Shawn O. Pearce --- git-gui | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/git-gui b/git-gui index ea71526e60..3f74fbb076 100755 --- a/git-gui +++ b/git-gui @@ -517,6 +517,9 @@ proc show_diff {path {w {}} {lno {}}} { set cmd [list | git diff-index] lappend cmd --no-color + if {$repo_config(gui.diffcontext) > 0} { + lappend cmd "-U$repo_config(gui.diffcontext)" + } lappend cmd -p switch $m { @@ -1765,12 +1768,18 @@ proc do_options {} { global repo_config global_config global repo_config_new global_config_new - load_config 1 array unset repo_config_new array unset global_config_new foreach name [array names repo_config] { set repo_config_new($name) $repo_config($name) } + load_config 1 + foreach name [array names repo_config] { + switch -- $name { + gui.diffcontext {continue} + } + set repo_config_new($name) $repo_config($name) + } foreach name [array names global_config] { set global_config_new($name) $global_config($name) } @@ -1811,18 +1820,36 @@ proc do_options {} { pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 foreach option { - {pullsummary {Show Pull Summary}} - {trustmtime {Trust File Modification Timestamps}} + {b pullsummary {Show Pull Summary}} + {b trustmtime {Trust File Modification Timestamps}} + {i diffcontext {Number of Diff Context Lines}} } { - set name [lindex $option 0] - set text [lindex $option 1] + set type [lindex $option 0] + set name [lindex $option 1] + set text [lindex $option 2] foreach f {repo global} { - checkbutton $w.$f.$name -text $text \ - -variable ${f}_config_new(gui.$name) \ - -onvalue true \ - -offvalue false \ - -font font_ui - pack $w.$f.$name -side top -anchor w + switch $type { + b { + checkbutton $w.$f.$name -text $text \ + -variable ${f}_config_new(gui.$name) \ + -onvalue true \ + -offvalue false \ + -font font_ui + pack $w.$f.$name -side top -anchor w + } + i { + frame $w.$f.$name + label $w.$f.$name.l -text "$text:" -font font_ui + pack $w.$f.$name.l -side left -anchor w -fill x + spinbox $w.$f.$name.v \ + -textvariable ${f}_config_new(gui.$name) \ + -from 1 -to 99 -increment 1 \ + -width 3 \ + -font font_ui + pack $w.$f.$name.v -side right -anchor e + pack $w.$f.$name -side top -anchor w -fill x + } + } } } @@ -1888,6 +1915,7 @@ proc do_save_config {w} { if {[catch {save_config} err]} { error_popup "Failed to completely save options:\n\n$err" } + reshow_diff destroy $w } @@ -1969,6 +1997,7 @@ proc apply_config {} { set default_config(gui.trustmtime) false set default_config(gui.pullsummary) true +set default_config(gui.diffcontext) 5 set default_config(gui.fontui) [font configure font_ui] set default_config(gui.fontdiff) [font configure font_diff] set font_descs { @@ -2318,6 +2347,21 @@ $ui_diff.ctxm add command -label "Decrease Font Size" \ $ui_diff.ctxm add command -label "Increase Font Size" \ -font font_ui \ -command {incr_font_size font_diff 1} +$ui_diff.ctxm add separator +$ui_diff.ctxm add command -label "Show Less Context" \ + -font font_ui \ + -command {if {$ui_fname_value ne {} + && $repo_config(gui.diffcontext) >= 2} { + incr repo_config(gui.diffcontext) -1 + reshow_diff + }} +$ui_diff.ctxm add command -label "Show More Context" \ + -font font_ui \ + -command {if {$ui_fname_value ne {}} { + incr repo_config(gui.diffcontext) + reshow_diff + }} +$ui_diff.ctxm add separator $ui_diff.ctxm add command -label {Options...} \ -font font_ui \ -command do_options -- cgit v1.2.3 From aaf1085a03a53eacff1b26459d0281a133f573d5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 19:29:04 -0500 Subject: git-gui: Sort the list of paths being updated in the index. Its a little surprising to see the UI update the icons for files in random order, due to the fact that the files are updating in the order they appear within the array (which is based on a hash function and not order). So sort the list of files before we send any to update-index so the order of operation is means something to the user. Signed-off-by: Shawn O. Pearce --- git-gui | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui b/git-gui index 3f74fbb076..b1a90e6661 100755 --- a/git-gui +++ b/git-gui @@ -1159,6 +1159,7 @@ proc update_index {pathList} { set update_index_cp 0 set update_index_rsd 0 + set pathList [lsort $pathList] set totalCnt [llength $pathList] set batch [expr {int($totalCnt * .01) + 1}] if {$batch > 25} {set batch 25} -- cgit v1.2.3 From 7f09cfafa8acf507f3a1358e05002e566f41783f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 19:33:33 -0500 Subject: git-gui: Use a smaller pipe buffer for update-index. When we shove a large number of files at update-index and they have very short path names we are likely going to fit a large number of them into the pipe buffer very early; thereby seeing a huge progress update followed by lots of waiting between progress updates due to the latency of update-index. Using a smaller buffer should help smooth out the progress updates as we are better able to keep tabs on the update-index process' progress through our list of paths. Signed-off-by: Shawn O. Pearce --- git-gui | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index b1a90e6661..7e28328cf0 100755 --- a/git-gui +++ b/git-gui @@ -1171,7 +1171,11 @@ proc update_index {pathList} { $totalCnt \ 0.0] set fd [open "| git update-index --add --remove -z --stdin" w] - fconfigure $fd -blocking 0 -translation binary + fconfigure $fd \ + -blocking 0 \ + -buffering full \ + -buffersize 512 \ + -translation binary fileevent $fd writable [list \ write_update_index \ $fd \ -- cgit v1.2.3 From c11b5f20d3bc2e8bc4fcd965b16e1575b87c0d5f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 21:11:12 -0500 Subject: git-gui: Allow the user to copy name of the file in the diff viewer. There's a lot of reasons why the user might need to obtain the complete (or just part of) path of a file which they are currently viewing in the diff viewer pane. So now we allow selection on this widget by using a text widget instead of a label. We also offer a context menu which has actions for copying the selection or the entire value onto the clipboard. Signed-off-by: Shawn O. Pearce --- git-gui | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index 7e28328cf0..1e5b0d049d 100755 --- a/git-gui +++ b/git-gui @@ -511,7 +511,7 @@ proc show_diff {path {w {}} {lno {}}} { set m [lindex $s 0] set diff_3way 0 set diff_active 1 - set ui_fname_value [escape_path $path] + set ui_fname_value $path set ui_fstatus_value [mapdesc $m $path] set ui_status_value "Loading diff of [escape_path $path]..." @@ -2284,11 +2284,33 @@ frame .vpane.lower.diff.header -background orange label .vpane.lower.diff.header.l1 -text {File:} \ -background orange \ -font font_ui -label .vpane.lower.diff.header.l2 -textvariable ui_fname_value \ +set ui_fname .vpane.lower.diff.header.l2 +text $ui_fname \ -background orange \ - -anchor w \ - -justify left \ + -height 1 \ + -relief flat \ + -state disabled \ -font font_ui +menu $ui_fname.ctxm -tearoff 0 +$ui_fname.ctxm add command -label "Copy Only Selection" \ + -font font_ui \ + -command "tk_textCopy $ui_fname" +$ui_fname.ctxm add command -label "Copy Complete Name" \ + -font font_ui \ + -command " + $ui_fname tag add sel 0.0 {end -1c} + tk_textCopy $ui_fname + $ui_fname tag remove sel 0.0 end + " +bind_button3 $ui_fname "tk_popup $ui_fname.ctxm %X %Y" +trace add variable ui_fname_value write $ui_fname.update +proc $ui_fname.update {varname args} { + global ui_fname ui_fname_value + $ui_fname configure -state normal + $ui_fname delete 0.0 end + $ui_fname insert end [escape_path $ui_fname_value] + $ui_fname configure -state disabled +} label .vpane.lower.diff.header.l3 -text {Status:} \ -background orange \ -font font_ui -- cgit v1.2.3 From 135f76ed996b6a0478831c561e1cddd249b7e19d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 21:49:49 -0500 Subject: git-gui: Correct language for M_/A_ status codes. When I changed from 'check in' to 'include' I missed the human friendly status displayed in the right side of the diff viewer heading. It was still reporting 'Checked in' for a fully included file, which is not what we wanted it to say. Signed-off-by: Shawn O. Pearce --- git-gui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index 1e5b0d049d..907db0e9dc 100755 --- a/git-gui +++ b/git-gui @@ -1385,17 +1385,17 @@ set max_status_desc 0 foreach i { {__ i plain "Unmodified"} {_M i mod "Modified"} - {M_ i fulltick "Checked in"} + {M_ i fulltick "Included in commit"} {MM i parttick "Partially included"} {_O o plain "Untracked"} - {A_ o fulltick "Added"} + {A_ o fulltick "Added by commit"} {AM o parttick "Partially added"} {AD o question "Added (but now gone)"} {_D i question "Missing"} - {D_ i removed "Removed"} - {DD i removed "Removed"} + {D_ i removed "Removed by commit"} + {DD i removed "Removed by commit"} {DO i removed "Removed (still exists)"} {UM i merge "Merge conflicts"} -- cgit v1.2.3 From 3e7b0e1d0ae509a54ca61c2b4c4990c8e6f0b2c0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 22:06:37 -0500 Subject: git-gui: Display status on left in diff header. Because the Tk pack layout manager gives all space to the right/bottom most widget during expand/contract of the frame we were adding and removing all space from the status area of the bar and not from the file name, which is what we actually wanted. A simple enough fix is to just put the status of the given file on the left side of the diff viewer header rather than on the right. Signed-off-by: Shawn O. Pearce --- git-gui | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/git-gui b/git-gui index 907db0e9dc..00b6cdf281 100755 --- a/git-gui +++ b/git-gui @@ -2281,6 +2281,13 @@ bind_button3 $ui_comm "tk_popup $ui_comm.ctxm %X %Y" set ui_fname_value {} set ui_fstatus_value {} frame .vpane.lower.diff.header -background orange +label .vpane.lower.diff.header.l4 \ + -textvariable ui_fstatus_value \ + -background orange \ + -width $max_status_desc \ + -anchor w \ + -justify left \ + -font font_ui label .vpane.lower.diff.header.l1 -text {File:} \ -background orange \ -font font_ui @@ -2288,6 +2295,7 @@ set ui_fname .vpane.lower.diff.header.l2 text $ui_fname \ -background orange \ -height 1 \ + -wrap none \ -relief flat \ -state disabled \ -font font_ui @@ -2311,19 +2319,9 @@ proc $ui_fname.update {varname args} { $ui_fname insert end [escape_path $ui_fname_value] $ui_fname configure -state disabled } -label .vpane.lower.diff.header.l3 -text {Status:} \ - -background orange \ - -font font_ui -label .vpane.lower.diff.header.l4 -textvariable ui_fstatus_value \ - -background orange \ - -width $max_status_desc \ - -anchor w \ - -justify left \ - -font font_ui +pack .vpane.lower.diff.header.l4 -side left pack .vpane.lower.diff.header.l1 -side left -pack .vpane.lower.diff.header.l2 -side left -fill x -pack .vpane.lower.diff.header.l4 -side right -pack .vpane.lower.diff.header.l3 -side right +pack $ui_fname -fill x # -- Diff Body frame .vpane.lower.diff.body -- cgit v1.2.3 From 1e5c18fb431c2d2493996e24ea68408e59ef6c16 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 12 Nov 2006 22:41:34 -0500 Subject: git-gui: Minor UI layout improvements for console windows. Moved the Close button over to the lower right corner where our Cancel/Save buttons are in the options dialog. This should fit better with our own look and feel as well as that of most apps on Mac OS X and Windows. Also set the lower status bar in a console window to indicate the process is working and that the user should wait for it to finish. Signed-off-by: Shawn O. Pearce --- git-gui | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/git-gui b/git-gui index 00b6cdf281..fbbc0caaac 100755 --- a/git-gui +++ b/git-gui @@ -1478,7 +1478,7 @@ proc hook_failed_popup {hook msg} { -width 15 \ -font font_ui \ -command "destroy $w" - pack $w.ok -side bottom + pack $w.ok -side bottom -anchor e -pady 10 -padx 10 bind $w "grab $w; focus $w" bind $w "destroy $w" @@ -1515,7 +1515,8 @@ proc console_init {w} { -font font_diff \ -state disabled \ -yscrollcommand [list $w.m.sby set] - label $w.m.s -anchor w \ + label $w.m.s -text {Working... please wait...} \ + -anchor w \ -justify left \ -font font_uibold scrollbar $w.m.sby -command [list $w.m.t yview] @@ -1540,12 +1541,11 @@ proc console_init {w} { $w.m.t tag remove sel 0.0 end " - button $w.ok -text {Running...} \ - -width 15 \ + button $w.ok -text {Close} \ -font font_ui \ -state disabled \ -command "destroy $w" - pack $w.ok -side bottom + pack $w.ok -side bottom -anchor e -pady 10 -padx 10 bind_button3 $w.m.t "tk_popup $w.ctxm %X %Y" bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" @@ -1614,12 +1614,10 @@ proc console_read {w fd after} { if {[catch {close $fd}]} { if {![winfo exists $w]} {console_init $w} $w.m.s conf -background red -text {Error: Command Failed} - $w.ok conf -text Close $w.ok conf -state normal set ok 0 } elseif {[winfo exists $w]} { $w.m.s conf -background green -text {Success} - $w.ok conf -text Close $w.ok conf -state normal set ok 1 } -- cgit v1.2.3 From fce89e466ae75961018ab88fec7000568f981d46 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 13 Nov 2006 00:48:44 -0500 Subject: git-gui: Reverted file name text field to a label. So although a text field with a flat relief looks like a label on Windows it doesn't on Mac OS X. The Aqua version of Tk is still drawing a border around the text field and that makes the diff pane header look pretty ugly. Earlier I had made the file name area into a text widget so the user could highlight parts of it and copy them onto the clipboard; but with the context menu being present this isn't quite as necessary as the user can copy the file name to the clipboard using that instead. So although this is a small loss in functionality for non-Mac OS X systems I think it is still reasonable. Signed-off-by: Shawn O. Pearce --- git-gui | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/git-gui b/git-gui index fbbc0caaac..ca7f8dbc41 100755 --- a/git-gui +++ b/git-gui @@ -2290,33 +2290,23 @@ label .vpane.lower.diff.header.l1 -text {File:} \ -background orange \ -font font_ui set ui_fname .vpane.lower.diff.header.l2 -text $ui_fname \ +label $ui_fname \ + -textvariable ui_fname_value \ -background orange \ - -height 1 \ - -wrap none \ - -relief flat \ - -state disabled \ + -anchor w \ + -justify left \ -font font_ui menu $ui_fname.ctxm -tearoff 0 -$ui_fname.ctxm add command -label "Copy Only Selection" \ +$ui_fname.ctxm add command -label "Copy" \ -font font_ui \ - -command "tk_textCopy $ui_fname" -$ui_fname.ctxm add command -label "Copy Complete Name" \ - -font font_ui \ - -command " - $ui_fname tag add sel 0.0 {end -1c} - tk_textCopy $ui_fname - $ui_fname tag remove sel 0.0 end - " + -command { + clipboard clear + clipboard append \ + -format STRING \ + -type STRING \ + -- $ui_fname_value + } bind_button3 $ui_fname "tk_popup $ui_fname.ctxm %X %Y" -trace add variable ui_fname_value write $ui_fname.update -proc $ui_fname.update {varname args} { - global ui_fname ui_fname_value - $ui_fname configure -state normal - $ui_fname delete 0.0 end - $ui_fname insert end [escape_path $ui_fname_value] - $ui_fname configure -state disabled -} pack .vpane.lower.diff.header.l4 -side left pack .vpane.lower.diff.header.l1 -side left pack $ui_fname -fill x -- cgit v1.2.3 From f7f8d32226595c22cb4f28f8e9e139cf42e1e640 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 13 Nov 2006 04:22:42 -0500 Subject: git-gui: By default don't allow partially included files. The concept of the Git index is confusing for many users, especially those who are newer to Git. Since git-gui is (at least partially) intended to be used by newer users who don't need the complexity of the index to be put in front of them early on, we should hide it by making any partially included file fully included as soon as we identify it. To do this we just run a quick update_index pass on any file which differs both in the index and the working directory, as these files have already been at least partially included by the user. A new option has been added in the options dialog (gui.partialinclude) which lets the user enable accessing the index from git-gui. This just disables the automatic update_index pass on partially included files. Signed-off-by: Shawn O. Pearce --- git-gui | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/git-gui b/git-gui index ca7f8dbc41..3e3a535326 100755 --- a/git-gui +++ b/git-gui @@ -406,19 +406,33 @@ proc read_ls_others {fd final} { proc status_eof {fd buf final} { global status_active ui_status_value + global file_states repo_config upvar $buf to_clear - if {[eof $fd]} { - set to_clear {} - close $fd + if {![eof $fd]} return + set to_clear {} + close $fd + if {[incr status_active -1] > 0} return - if {[incr status_active -1] == 0} { - display_all_files - unlock_index - reshow_diff - set ui_status_value $final + unlock_index + display_all_files + + if {$repo_config(gui.partialinclude) ne {true}} { + set pathList [list] + foreach path [array names file_states] { + switch -- [lindex $file_states($path) 0] { + AM - + MM {lappend pathList $path} + } + } + if {$pathList ne {}} { + update_index $pathList + return } } + + reshow_diff + set ui_status_value $final } ###################################################################### @@ -1164,7 +1178,6 @@ proc update_index {pathList} { set batch [expr {int($totalCnt * .01) + 1}] if {$batch > 25} {set batch 25} - set ui_status_value "Including files ... 0/$totalCnt 0%" set ui_status_value [format \ "Including files ... %i/%i files (%.2f%%)" \ $update_index_cp \ @@ -1192,10 +1205,9 @@ proc write_update_index {fd pathList totalCnt batch} { if {$update_index_cp >= $totalCnt} { close $fd unlock_index + set ui_status_value {Ready.} if {$update_index_rsd} { reshow_diff - } else { - set ui_status_value {Ready.} } return } @@ -1823,6 +1835,7 @@ proc do_options {} { pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 foreach option { + {b partialinclude {Allow Partially Included Files}} {b pullsummary {Show Pull Summary}} {b trustmtime {Trust File Modification Timestamps}} {i diffcontext {Number of Diff Context Lines}} @@ -2000,6 +2013,7 @@ proc apply_config {} { set default_config(gui.trustmtime) false set default_config(gui.pullsummary) true +set default_config(gui.partialinclude) false set default_config(gui.diffcontext) 5 set default_config(gui.fontui) [font configure font_ui] set default_config(gui.fontdiff) [font configure font_diff] -- cgit v1.2.3 From 7d0d289e457eb643b8638b6385a0ce056f1f5a97 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 13 Nov 2006 14:25:53 -0500 Subject: git-gui: Refactor mouse clicking on file names/icons. I'm not a huge fan of putting the left and right mouse actions into the same procedure. Originally this is how Paul had implemented the logic in gitool and I had carried some of that over into git-gui, but now that I'm getting ready to implement right mouse click features to act on files I really should split this apart. Signed-off-by: Shawn O. Pearce --- git-gui | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/git-gui b/git-gui index 3e3a535326..b8ebe31777 100755 --- a/git-gui +++ b/git-gui @@ -1935,10 +1935,8 @@ proc do_save_config {w} { destroy $w } -# shift == 1: left click -# 3: right click -proc click {w x y shift wx wy} { - global ui_index ui_other file_lists +proc file_left_click {w x y} { + global file_lists set pos [split [$w index @$x,$y] .] set lno [lindex $pos 0] @@ -1946,12 +1944,12 @@ proc click {w x y shift wx wy} { set path [lindex $file_lists($w) [expr $lno - 1]] if {$path eq {}} return - if {$col > 0 && $shift == 1} { + if {$col > 0} { show_diff $path $w $lno } } -proc unclick {w x y} { +proc file_left_unclick {w x y} { global file_lists set pos [split [$w index @$x,$y] .] @@ -2457,9 +2455,8 @@ bind all <$M1B-Key-Q> do_quit bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} foreach i [list $ui_index $ui_other] { - bind $i {click %W %x %y 1 %X %Y; break} - bind $i {unclick %W %x %y; break} - bind_button3 $i {click %W %x %y 3 %X %Y; break} + bind $i {file_left_click %W %x %y; break} + bind $i {file_left_unclick %W %x %y; break} } unset i -- cgit v1.2.3 From a37eee4406ff965fda4d234759aa58526657cbe3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 13 Nov 2006 14:37:41 -0500 Subject: git-gui: Narrow the no differences information message. On Mac OS X the no differences informational message was linewrapped at the wrong points due to the limited width of the system dialog, yet the LFs embedded in the message (where I linewrapped it manually) were also being honored. This resulted in a very difficult to read paragraph of text. So this narrows the text down by another 10 columns or so, making it more readable. Signed-off-by: Shawn O. Pearce --- git-gui | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/git-gui b/git-gui index b8ebe31777..6b8d25e9aa 100755 --- a/git-gui +++ b/git-gui @@ -475,13 +475,15 @@ proc handle_empty_diff {} { [short_path $path] has no changes. -The modification date of this file was updated by another -application and you currently have the Trust File Modification -Timestamps option enabled, so Git did not automatically detect -that there are no content differences in this file. - -This file will now be removed from the modified files list, to -prevent possible confusion. +The modification date of this file was updated +by another application and you currently have +the Trust File Modification Timestamps option +enabled, so Git did not automatically detect +that there are no content differences in this +file. + +This file will now be removed from the modified +files list, to prevent possible confusion. " if {[catch {exec git update-index -- $path} err]} { error_popup "Failed to refresh index:\n\n$err" -- cgit v1.2.3 From 24263b77165edfd438045ed3c25ec6669b3e76d4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 13 Nov 2006 16:06:38 -0500 Subject: git-gui: Implemented multiple selection in file lists. Because I want to let users apply actions to more than one file at a time we really needed a concept of "the current selection" from the two file lists. Since I'm abusing a Tk text widget for the file displays I can't really use the Tk selection to track which files are picked and which aren't. So instead we keep this in an array to tell us which paths are currently selected and we use an inverse fg/bg for the selected file display. This is common most operating systems as a selection indicator. The selection works like most users would expect; single click will clear the selection and pick only that file, M1-click (aka Ctrl-click or Cmd-click) will toggle the one file in/out of the selection, and Shift-click will select the range between the last clicked file and the currently clicked file. Signed-off-by: Shawn O. Pearce --- git-gui | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 111 insertions(+), 25 deletions(-) diff --git a/git-gui b/git-gui index 6b8d25e9aa..a60bf1c8a3 100755 --- a/git-gui +++ b/git-gui @@ -183,6 +183,7 @@ if {$appname eq {git-citool}} { set status_active 0 set diff_active 0 +set last_clicked {} set disable_on_lock [list] set index_lock_type none @@ -351,7 +352,7 @@ proc read_diff_index {fd final} { incr z2 -1 display_file \ [string range $buf_rdi $z1 $z2] \ - [string index $buf_rdi [expr $z1 - 2]]_ + [string index $buf_rdi [expr {$z1 - 2}]]_ incr c } if {$c < $n} { @@ -380,7 +381,7 @@ proc read_diff_files {fd final} { incr z2 -1 display_file \ [string range $buf_rdf $z1 $z2] \ - _[string index $buf_rdf [expr $z1 - 2]] + _[string index $buf_rdf [expr {$z1 - 2}]] incr c } if {$c < $n} { @@ -414,6 +415,7 @@ proc status_eof {fd buf final} { close $fd if {[incr status_active -1] > 0} return + prune_selection unlock_index display_all_files @@ -435,6 +437,16 @@ proc status_eof {fd buf final} { set ui_status_value $final } +proc prune_selection {} { + global file_states selected_paths + + foreach path [array names selected_paths] { + if {[catch {set still_here $file_states($path)}]} { + unset selected_paths($path) + } + } +} + ###################################################################### ## ## diff @@ -497,7 +509,7 @@ files list, to prevent possible confusion. [lreplace $file_lists($old_w) $lno $lno] incr lno $old_w conf -state normal - $old_w delete $lno.0 [expr $lno + 1].0 + $old_w delete $lno.0 [expr {$lno + 1}].0 $old_w conf -state disabled } } @@ -520,7 +532,7 @@ proc show_diff {path {w {}} {lno {}}} { } } if {$w ne {} && $lno >= 1} { - $w tag add in_diff $lno.0 [expr $lno + 1].0 + $w tag add in_diff $lno.0 [expr {$lno + 1}].0 } set s $file_states($path) @@ -821,7 +833,7 @@ proc commit_stage2 {curHEAD msg} { proc commit_stage3 {fd_wt curHEAD msg} { global single_commit gitdir HEAD PARENT commit_type tcl_platform global ui_status_value ui_comm - global file_states + global file_states selected_paths gets $fd_wt tree_id if {$tree_id eq {} || [catch {close $fd_wt} err]} { @@ -871,7 +883,7 @@ proc commit_stage3 {fd_wt curHEAD msg} { } set i [string first "\n" $msg] if {$i >= 0} { - append reflogm {: } [string range $msg 0 [expr $i - 1]] + append reflogm {: } [string range $msg 0 [expr {$i - 1}]] } else { append reflogm {: } $msg } @@ -934,6 +946,7 @@ proc commit_stage3 {fd_wt curHEAD msg} { if {$m eq {__}} { unset file_states($path) + catch {unset selected_paths($path)} } else { lset file_states($path) 0 $m } @@ -1102,7 +1115,7 @@ proc merge_state {path new_state} { } proc display_file {path state} { - global file_states file_lists status_active + global file_states file_lists selected_paths status_active set old_m [merge_state $path $state] if {$status_active} return @@ -1118,7 +1131,7 @@ proc display_file {path state} { if {$lno >= 0} { incr lno $old_w conf -state normal - $old_w delete $lno.0 [expr $lno + 1].0 + $old_w delete $lno.0 [expr {$lno + 1}].0 $old_w conf -state disabled } @@ -1132,6 +1145,12 @@ proc display_file {path state} { -name [lindex $s 1] \ -image $new_icon $new_w insert $lno.1 "[escape_path $path]\n" + if {[catch {set in_sel $selected_paths($path)}]} { + set in_sel 0 + } + if {$in_sel} { + $new_w tag add in_sel $lno.0 [expr {$lno + 1}].0 + } $new_w conf -state disabled } elseif {$new_icon ne [mapicon $old_m $path]} { $new_w conf -state normal @@ -1141,13 +1160,16 @@ proc display_file {path state} { } proc display_all_files {} { - global ui_index ui_other file_states file_lists + global ui_index ui_other + global file_states file_lists + global last_clicked selected_paths $ui_index conf -state normal $ui_other conf -state normal $ui_index delete 0.0 end $ui_other delete 0.0 end + set last_clicked {} set file_lists($ui_index) [list] set file_lists($ui_other) [list] @@ -1157,11 +1179,18 @@ proc display_all_files {} { set m [lindex $s 0] set w [mapcol $m $path] lappend file_lists($w) $path + set lno [expr {[lindex [split [$w index end] .] 0] - 1}] $w image create end \ -align center -padx 5 -pady 1 \ -name [lindex $s 1] \ -image [mapicon $m $path] $w insert end "[escape_path $path]\n" + if {[catch {set in_sel $selected_paths($path)}]} { + set in_sel 0 + } + if {$in_sel} { + $w tag add in_sel $lno.0 [expr {$lno + 1}].0 + } } $ui_index conf -state disabled @@ -1603,8 +1632,8 @@ proc console_read {w fd after} { while {$c < $n} { set cr [string first "\r" $buf $c] set lf [string first "\n" $buf $c] - if {$cr < 0} {set cr [expr $n + 1]} - if {$lf < 0} {set lf [expr $n + 1]} + if {$cr < 0} {set cr [expr {$n + 1}]} + if {$lf < 0} {set lf [expr {$n + 1}]} if {$lf < $cr} { $w.m.t insert end [string range $buf $c $lf] @@ -1937,32 +1966,83 @@ proc do_save_config {w} { destroy $w } -proc file_left_click {w x y} { - global file_lists +proc toggle_or_diff {w x y} { + global file_lists ui_index ui_other + global last_clicked selected_paths set pos [split [$w index @$x,$y] .] set lno [lindex $pos 0] set col [lindex $pos 1] - set path [lindex $file_lists($w) [expr $lno - 1]] - if {$path eq {}} return + set path [lindex $file_lists($w) [expr {$lno - 1}]] + if {$path eq {}} { + set last_clicked {} + return + } + + set last_clicked [list $w $lno] + array unset selected_paths + $ui_index tag remove in_sel 0.0 end + $ui_other tag remove in_sel 0.0 end - if {$col > 0} { + if {$col == 0} { + update_index [list $path] + } else { show_diff $path $w $lno } } -proc file_left_unclick {w x y} { +proc add_one_to_selection {w x y} { global file_lists + global last_clicked selected_paths set pos [split [$w index @$x,$y] .] set lno [lindex $pos 0] set col [lindex $pos 1] - set path [lindex $file_lists($w) [expr $lno - 1]] - if {$path eq {}} return + set path [lindex $file_lists($w) [expr {$lno - 1}]] + if {$path eq {}} { + set last_clicked {} + return + } - if {$col == 0} { - update_index [list $path] + set last_clicked [list $w $lno] + if {[catch {set in_sel $selected_paths($path)}]} { + set in_sel 0 + } + if {$in_sel} { + unset selected_paths($path) + $w tag remove in_sel $lno.0 [expr {$lno + 1}].0 + } else { + set selected_paths($path) 1 + $w tag add in_sel $lno.0 [expr {$lno + 1}].0 + } +} + +proc add_range_to_selection {w x y} { + global file_lists + global last_clicked selected_paths + + if {[lindex $last_clicked 0] ne $w} { + toggle_or_diff $w $x $y + return } + + set pos [split [$w index @$x,$y] .] + set lno [lindex $pos 0] + set lc [lindex $last_clicked 1] + if {$lc < $lno} { + set begin $lc + set end $lno + } else { + set begin $lno + set end $lc + } + + foreach path [lrange $file_lists($w) \ + [expr {$begin - 1}] \ + [expr {$end - 1}]] { + set selected_paths($path) 1 + } + $w tag add in_sel $begin.0 [expr {$end + 1}].0 } ###################################################################### @@ -2174,8 +2254,13 @@ pack .vpane.files.other.sb -side right -fill y pack $ui_other -side left -fill both -expand 1 .vpane.files add .vpane.files.other -sticky nsew -$ui_index tag conf in_diff -font font_uibold -$ui_other tag conf in_diff -font font_uibold +foreach i [list $ui_index $ui_other] { + $i tag conf in_diff -font font_uibold + $i tag conf in_sel \ + -background [$i cget -foreground] \ + -foreground [$i cget -background] +} +unset i # -- Diff and Commit Area frame .vpane.lower -height 300 -width 400 @@ -2457,8 +2542,9 @@ bind all <$M1B-Key-Q> do_quit bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} foreach i [list $ui_index $ui_other] { - bind $i {file_left_click %W %x %y; break} - bind $i {file_left_unclick %W %x %y; break} + bind $i "toggle_or_diff $i %x %y; break" + bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break" + bind $i "add_range_to_selection $i %x %y; break" } unset i -- cgit v1.2.3 From 99058720df7981aaaaf64e9a0d2c658d90b82340 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 14 Nov 2006 01:19:03 -0500 Subject: git-gui: Refactor update_status -> rescan. Since we refer to the act of updating our memory structures with index and working directory differences as a rescan in the UI its probably a good idea to make the related procedures have the same name. Signed-off-by: Shawn O. Pearce --- git-gui | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/git-gui b/git-gui index a60bf1c8a3..a1266fe7a7 100755 --- a/git-gui +++ b/git-gui @@ -234,7 +234,7 @@ proc repository_state {hdvar ctvar} { } } -proc update_status {{final Ready.}} { +proc rescan {{final Ready.}} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm global status_active file_states @@ -265,7 +265,7 @@ proc update_status {{final Ready.}} { } if {$repo_config(gui.trustmtime) eq {true}} { - update_status_stage2 {} $final + rescan_stage2 {} $final } else { set status_active 1 set ui_status_value {Refreshing file status...} @@ -277,11 +277,11 @@ proc update_status {{final Ready.}} { set fd_rf [open "| $cmd" r] fconfigure $fd_rf -blocking 0 -translation binary fileevent $fd_rf readable \ - [list update_status_stage2 $fd_rf $final] + [list rescan_stage2 $fd_rf $final] } } -proc update_status_stage2 {fd final} { +proc rescan_stage2 {fd final} { global gitdir PARENT commit_type global ui_index ui_other ui_status_value ui_comm global status_active @@ -684,7 +684,7 @@ proc load_last_commit {} { set commit_type amend set HEAD {} set PARENT {} - update_status + rescan } elseif {$parent_count == 1} { set commit_type amend set PARENT $parent @@ -692,7 +692,7 @@ proc load_last_commit {} { $ui_comm insert end $msg $ui_comm edit modified false $ui_comm edit reset - update_status + rescan } else { error_popup {You can't amend a merge commit.} return @@ -720,7 +720,7 @@ repository since our last scan. A rescan is required before committing. } unlock_index - update_status + rescan return } @@ -987,7 +987,7 @@ repository since our last scan. A rescan is required before a pull can be started. } unlock_index - update_status + rescan return } @@ -1026,7 +1026,7 @@ proc post_pull_remote {remote branch success} { set PARENT $HEAD set $ui_status_value {Ready.} } else { - update_status \ + rescan \ "Conflicts detected while pulling $branch from $remote." } } @@ -1744,7 +1744,7 @@ proc do_quit {} { } proc do_rescan {} { - update_status + rescan } proc do_include_all {} { @@ -2559,4 +2559,4 @@ if {!$single_commit} { populate_remote_menu .mbar.push To push_to populate_pull_menu .mbar.pull } -after 1 update_status +after 1 rescan -- cgit v1.2.3 From 8f52548a9ed078d581379ad526a4259920f80a88 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 14 Nov 2006 01:29:32 -0500 Subject: git-gui: Provide an after-rescan script to rescan. There are some situations where we need to run rescan and have it do more than just updating the status in the UI when its complete. To help with that this changes the rescan procedure to take a script which it will run at the global level as soon as the rescan is done and the UI has finished updating with the results. This is useful for example if we performed a rescan as part of a commit operation; we can go back to the commit where we left off when the rescan got initiated. Signed-off-by: Shawn O. Pearce --- git-gui | 68 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/git-gui b/git-gui index a1266fe7a7..2c8501eebf 100755 --- a/git-gui +++ b/git-gui @@ -181,7 +181,7 @@ if {$appname eq {git-citool}} { ## ## task management -set status_active 0 +set rescan_active 0 set diff_active 0 set last_clicked {} @@ -234,13 +234,13 @@ proc repository_state {hdvar ctvar} { } } -proc rescan {{final Ready.}} { +proc rescan {after} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm - global status_active file_states + global rescan_active file_states global repo_config - if {$status_active || ![lock_index read]} return + if {$rescan_active > 0 || ![lock_index read]} return repository_state new_HEAD new_type if {$commit_type eq {amend} @@ -265,9 +265,9 @@ proc rescan {{final Ready.}} { } if {$repo_config(gui.trustmtime) eq {true}} { - rescan_stage2 {} $final + rescan_stage2 {} $after } else { - set status_active 1 + set rescan_active 1 set ui_status_value {Refreshing file status...} set cmd [list git update-index] lappend cmd -q @@ -277,14 +277,14 @@ proc rescan {{final Ready.}} { set fd_rf [open "| $cmd" r] fconfigure $fd_rf -blocking 0 -translation binary fileevent $fd_rf readable \ - [list rescan_stage2 $fd_rf $final] + [list rescan_stage2 $fd_rf $after] } } -proc rescan_stage2 {fd final} { +proc rescan_stage2 {fd after} { global gitdir PARENT commit_type global ui_index ui_other ui_status_value ui_comm - global status_active + global rescan_active global buf_rdi buf_rdf buf_rlo if {$fd ne {}} { @@ -304,7 +304,7 @@ proc rescan_stage2 {fd final} { set buf_rdf {} set buf_rlo {} - set status_active 3 + set rescan_active 3 set ui_status_value {Scanning for modified files ...} set fd_di [open "| git diff-index --cached -z $PARENT" r] set fd_df [open "| git diff-files -z" r] @@ -313,9 +313,9 @@ proc rescan_stage2 {fd final} { fconfigure $fd_di -blocking 0 -translation binary fconfigure $fd_df -blocking 0 -translation binary fconfigure $fd_lo -blocking 0 -translation binary - fileevent $fd_di readable [list read_diff_index $fd_di $final] - fileevent $fd_df readable [list read_diff_files $fd_df $final] - fileevent $fd_lo readable [list read_ls_others $fd_lo $final] + fileevent $fd_di readable [list read_diff_index $fd_di $after] + fileevent $fd_df readable [list read_diff_files $fd_df $after] + fileevent $fd_lo readable [list read_ls_others $fd_lo $after] } proc load_message {file} { @@ -335,7 +335,7 @@ proc load_message {file} { return 0 } -proc read_diff_index {fd final} { +proc read_diff_index {fd after} { global buf_rdi append buf_rdi [read $fd] @@ -361,10 +361,10 @@ proc read_diff_index {fd final} { set buf_rdi {} } - status_eof $fd buf_rdi $final + rescan_done $fd buf_rdi $after } -proc read_diff_files {fd final} { +proc read_diff_files {fd after} { global buf_rdf append buf_rdf [read $fd] @@ -390,10 +390,10 @@ proc read_diff_files {fd final} { set buf_rdf {} } - status_eof $fd buf_rdf $final + rescan_done $fd buf_rdf $after } -proc read_ls_others {fd final} { +proc read_ls_others {fd after} { global buf_rlo append buf_rlo [read $fd] @@ -402,18 +402,18 @@ proc read_ls_others {fd final} { foreach p [lrange $pck 0 end-1] { display_file $p _O } - status_eof $fd buf_rlo $final + rescan_done $fd buf_rlo $after } -proc status_eof {fd buf final} { - global status_active ui_status_value +proc rescan_done {fd buf after} { + global rescan_active global file_states repo_config upvar $buf to_clear if {![eof $fd]} return set to_clear {} close $fd - if {[incr status_active -1] > 0} return + if {[incr rescan_active -1] > 0} return prune_selection unlock_index @@ -434,7 +434,7 @@ proc status_eof {fd buf final} { } reshow_diff - set ui_status_value $final + uplevel #0 $after } proc prune_selection {} { @@ -684,7 +684,7 @@ proc load_last_commit {} { set commit_type amend set HEAD {} set PARENT {} - rescan + rescan {set ui_status_value {Ready.}} } elseif {$parent_count == 1} { set commit_type amend set PARENT $parent @@ -692,7 +692,7 @@ proc load_last_commit {} { $ui_comm insert end $msg $ui_comm edit modified false $ui_comm edit reset - rescan + rescan {set ui_status_value {Ready.}} } else { error_popup {You can't amend a merge commit.} return @@ -720,7 +720,7 @@ repository since our last scan. A rescan is required before committing. } unlock_index - rescan + rescan {set ui_status_value {Ready.}} return } @@ -987,7 +987,7 @@ repository since our last scan. A rescan is required before a pull can be started. } unlock_index - rescan + rescan {set ui_status_value {Ready.}} return } @@ -1024,10 +1024,10 @@ proc post_pull_remote {remote branch success} { if {$success} { repository_state HEAD commit_type set PARENT $HEAD - set $ui_status_value {Ready.} + set $ui_status_value "Pulling $branch from $remote complete." } else { - rescan \ - "Conflicts detected while pulling $branch from $remote." + set m "Conflicts detected while pulling $branch from $remote." + rescan "set ui_status_value {$m}" } } @@ -1115,10 +1115,10 @@ proc merge_state {path new_state} { } proc display_file {path state} { - global file_states file_lists selected_paths status_active + global file_states file_lists selected_paths rescan_active set old_m [merge_state $path $state] - if {$status_active} return + if {$rescan_active > 0} return set s $file_states($path) set new_m [lindex $s 0] @@ -1744,7 +1744,7 @@ proc do_quit {} { } proc do_rescan {} { - rescan + rescan {set ui_status_value {Ready.}} } proc do_include_all {} { @@ -2559,4 +2559,4 @@ if {!$single_commit} { populate_remote_menu .mbar.push To push_to populate_pull_menu .mbar.pull } -after 1 rescan +after 1 do_rescan -- cgit v1.2.3 From 04b393824ff57b5fcb881a00466e513cd4ad2a7f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 14 Nov 2006 01:42:32 -0500 Subject: git-gui: Allow update_index to also run a script when it completes. Like rescan we also have cases where we need to perform a script after we have finished updating a number of files in the index. By changing the parameter structure of update_index we can easily pass through any script we need to run afterwards, such as picking up in the middle of a commit, or finishing what is left of a rescan. Signed-off-by: Shawn O. Pearce --- git-gui | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/git-gui b/git-gui index 2c8501eebf..be42b91067 100755 --- a/git-gui +++ b/git-gui @@ -428,7 +428,10 @@ proc rescan_done {fd buf after} { } } if {$pathList ne {}} { - update_index $pathList + update_index \ + "Updating included files" \ + $pathList \ + [concat {reshow_diff;} $after] return } } @@ -1197,7 +1200,7 @@ proc display_all_files {} { $ui_other conf -state disabled } -proc update_index {pathList} { +proc update_index {msg pathList after} { global update_index_cp update_index_rsd ui_status_value if {![lock_index update]} return @@ -1210,7 +1213,7 @@ proc update_index {pathList} { if {$batch > 25} {set batch 25} set ui_status_value [format \ - "Including files ... %i/%i files (%.2f%%)" \ + "$msg... %i/%i files (%.2f%%)" \ $update_index_cp \ $totalCnt \ 0.0] @@ -1226,20 +1229,20 @@ proc update_index {pathList} { $pathList \ $totalCnt \ $batch \ + $msg \ + $after \ ] } -proc write_update_index {fd pathList totalCnt batch} { +proc write_update_index {fd pathList totalCnt batch msg after} { global update_index_cp update_index_rsd ui_status_value global file_states ui_fname_value if {$update_index_cp >= $totalCnt} { close $fd unlock_index - set ui_status_value {Ready.} - if {$update_index_rsd} { - reshow_diff - } + if {$update_index_rsd} reshow_diff + uplevel #0 $after return } @@ -1268,7 +1271,7 @@ proc write_update_index {fd pathList totalCnt batch} { } set ui_status_value [format \ - "Including files ... %i/%i files (%.2f%%)" \ + "$msg... %i/%i files (%.2f%%)" \ $update_index_cp \ $totalCnt \ [expr {100.0 * $update_index_cp / $totalCnt}]] @@ -1766,7 +1769,10 @@ proc do_include_all {} { if {$pathList eq {}} { unlock_index } else { - update_index $pathList + update_index \ + "Including all modified files" \ + $pathList \ + {set ui_status_value {Ready to commit.}} } } @@ -1985,7 +1991,10 @@ proc toggle_or_diff {w x y} { $ui_other tag remove in_sel 0.0 end if {$col == 0} { - update_index [list $path] + update_index \ + "Including [short_path $path]" \ + [list $path] \ + {set ui_status_value {Ready.}} } else { show_diff $path $w $lno } -- cgit v1.2.3 From bbe3b3b9b93763ff4eff4fbb638b1c6a4bed8c95 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 15 Nov 2006 18:06:29 -0500 Subject: git-gui: Automatically update-index all included files before commit. If the user has "Allow Partially Included Files" disabled (and most probably will as its the default setting) we should run update-index on every included file before commit to make sure that any changes made by the user since the last rescan will still be part of this commit. If we don't update-index every modified file the user will likely become confused when part of their changes were committed and other parts weren't; and those other parts won't show up until a later rescan occurs. Since we don't rescan immediately after a commit this may be a while. Signed-off-by: Shawn O. Pearce --- git-gui | 101 ++++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/git-gui b/git-gui index be42b91067..9a6953e970 100755 --- a/git-gui +++ b/git-gui @@ -420,7 +420,7 @@ proc rescan_done {fd buf after} { display_all_files if {$repo_config(gui.partialinclude) ne {true}} { - set pathList [list] + set pathList [list] foreach path [array names file_states] { switch -- [lindex $file_states($path) 0] { AM - @@ -703,9 +703,7 @@ proc load_last_commit {} { } proc commit_tree {} { - global tcl_platform HEAD gitdir commit_type file_states - global pch_error - global ui_status_value ui_comm + global HEAD commit_type file_states ui_comm repo_config if {![lock_index update]} return @@ -719,8 +717,10 @@ proc commit_tree {} { error_popup {Last scanned state does not match repository state. Its highly likely that another Git program modified the -repository since our last scan. A rescan is required +repository since the last scan. A rescan is required before committing. + +A rescan will be automatically started now. } unlock_index rescan {set ui_status_value {Ready.}} @@ -731,8 +731,7 @@ before committing. # set files_ready 0 foreach path [array names file_states] { - set s $file_states($path) - switch -glob -- [lindex $s 0] { + switch -glob -- [lindex $file_states($path) 0] { _? {continue} A? - D? - @@ -779,10 +778,39 @@ A good commit message has the following format: return } - # -- Ask the pre-commit hook for the go-ahead. + # -- Update included files if partialincludes are off. # + if {$repo_config(gui.partialinclude) ne {true}} { + set pathList [list] + foreach path [array names file_states] { + switch -glob -- [lindex $file_states($path) 0] { + A? - + M? {lappend pathList $path} + } + } + if {$pathList ne {}} { + unlock_index + update_index \ + "Updating included files" \ + $pathList \ + [concat {lock_index update;} \ + [list commit_prehook $curHEAD $msg]] + return + } + } + + commit_prehook $curHEAD $msg +} + +proc commit_prehook {curHEAD msg} { + global tcl_platform gitdir ui_status_value pch_error + + # On Cygwin [file executable] might lie so we need to ask + # the shell if the hook is executable. Yes that's annoying. + set pchook [file join $gitdir hooks pre-commit] - if {$tcl_platform(platform) eq {windows} && [file isfile $pchook]} { + if {$tcl_platform(platform) eq {windows} + && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ "then exec \"$pchook\" 2>&1;" \ @@ -790,21 +818,19 @@ A good commit message has the following format: } elseif {[file executable $pchook]} { set pchook [list $pchook |& cat] } else { - set pchook {} - } - if {$pchook ne {}} { - set ui_status_value {Calling pre-commit hook...} - set pch_error {} - set fd_ph [open "| $pchook" r] - fconfigure $fd_ph -blocking 0 -translation binary - fileevent $fd_ph readable \ - [list commit_stage1 $fd_ph $curHEAD $msg] - } else { - commit_stage2 $curHEAD $msg + commit_writetree $curHEAD $msg + return } + + set ui_status_value {Calling pre-commit hook...} + set pch_error {} + set fd_ph [open "| $pchook" r] + fconfigure $fd_ph -blocking 0 -translation binary + fileevent $fd_ph readable \ + [list commit_prehook_wait $fd_ph $curHEAD $msg] } -proc commit_stage1 {fd_ph curHEAD msg} { +proc commit_prehook_wait {fd_ph curHEAD msg} { global pch_error ui_status_value append pch_error [read $fd_ph] @@ -815,25 +841,24 @@ proc commit_stage1 {fd_ph curHEAD msg} { hook_failed_popup pre-commit $pch_error unlock_index } else { - commit_stage2 $curHEAD $msg + commit_writetree $curHEAD $msg } set pch_error {} - } else { - fconfigure $fd_ph -blocking 0 + return } + fconfigure $fd_ph -blocking 0 } -proc commit_stage2 {curHEAD msg} { +proc commit_writetree {curHEAD msg} { global ui_status_value - # -- Write the tree in the background. - # set ui_status_value {Committing changes...} set fd_wt [open "| git write-tree" r] - fileevent $fd_wt readable [list commit_stage3 $fd_wt $curHEAD $msg] + fileevent $fd_wt readable \ + [list commit_committree $fd_wt $curHEAD $msg] } -proc commit_stage3 {fd_wt curHEAD msg} { +proc commit_committree {fd_wt curHEAD msg} { global single_commit gitdir HEAD PARENT commit_type tcl_platform global ui_status_value ui_comm global file_states selected_paths @@ -1252,14 +1277,20 @@ proc write_update_index {fd pathList totalCnt batch msg after} { set path [lindex $pathList $update_index_cp] incr update_index_cp - switch -- [lindex $file_states($path) 0] { - AM - - _O {set new A*} - _M - - MM {set new M*} + switch -glob -- [lindex $file_states($path) 0] { AD - + MD - _D {set new D*} - default {continue} + + _M - + MM - + M_ {set new M*} + + _O - + AM - + A_ {set new A*} + + ?? {continue} } puts -nonewline $fd $path -- cgit v1.2.3 From e8ab64461968392f126b0a0e2fef4ce8bdcca623 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 15 Nov 2006 18:55:05 -0500 Subject: git-gui: Disable diff actions when no diff is active. There is no reason why the user should be able to operate on the diff buffer if there is no currently selected diff; likewise the "File:" label text appears rather silly looking all by itself when no diff is being shown in the diff buffer. So now we only enable widgets (like menu items) if there is a diff currently showing, and we disable them when a diff isn't showing. Signed-off-by: Shawn O. Pearce --- git-gui | 186 ++++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 118 insertions(+), 68 deletions(-) diff --git a/git-gui b/git-gui index 9a6953e970..8449664538 100755 --- a/git-gui +++ b/git-gui @@ -455,34 +455,33 @@ proc prune_selection {} { ## diff proc clear_diff {} { - global ui_diff ui_fname_value ui_fstatus_value ui_index ui_other + global ui_diff current_diff ui_index ui_other $ui_diff conf -state normal $ui_diff delete 0.0 end $ui_diff conf -state disabled - set ui_fname_value {} - set ui_fstatus_value {} + set current_diff {} $ui_index tag remove in_diff 0.0 end $ui_other tag remove in_diff 0.0 end } proc reshow_diff {} { - global ui_fname_value ui_status_value file_states + global current_diff ui_status_value file_states - if {$ui_fname_value eq {} - || [catch {set s $file_states($ui_fname_value)}]} { + if {$current_diff eq {} + || [catch {set s $file_states($current_diff)}]} { clear_diff } else { - show_diff $ui_fname_value + show_diff $current_diff } } proc handle_empty_diff {} { - global ui_fname_value file_states file_lists + global current_diff file_states file_lists - set path $ui_fname_value + set path $current_diff set s $file_states($path) if {[lindex $s 0] ne {_M}} return @@ -520,7 +519,7 @@ files list, to prevent possible confusion. proc show_diff {path {w {}} {lno {}}} { global file_states file_lists global PARENT diff_3way diff_active repo_config - global ui_diff ui_fname_value ui_fstatus_value ui_status_value + global ui_diff current_diff ui_status_value if {$diff_active || ![lock_index read]} return @@ -542,8 +541,7 @@ proc show_diff {path {w {}} {lno {}}} { set m [lindex $s 0] set diff_3way 0 set diff_active 1 - set ui_fname_value $path - set ui_fstatus_value [mapdesc $m $path] + set current_diff $path set ui_status_value "Loading diff of [escape_path $path]..." set cmd [list | git diff-index] @@ -1261,7 +1259,7 @@ proc update_index {msg pathList after} { proc write_update_index {fd pathList totalCnt batch msg after} { global update_index_cp update_index_rsd ui_status_value - global file_states ui_fname_value + global file_states current_diff if {$update_index_cp >= $totalCnt} { close $fd @@ -1296,7 +1294,7 @@ proc write_update_index {fd pathList totalCnt batch msg after} { puts -nonewline $fd $path puts -nonewline $fd "\0" display_file $path $new - if {$ui_fname_value eq $path} { + if {$current_diff eq $path} { set update_index_rsd 1 } } @@ -2384,71 +2382,105 @@ pack .vpane.lower.commarea.buffer -side left -fill y # -- Commit Message Buffer Context Menu # -menu $ui_comm.ctxm -tearoff 0 -$ui_comm.ctxm add command -label "Cut" \ +set ctxm .vpane.lower.commarea.buffer.ctxm +menu $ctxm -tearoff 0 +$ctxm add command \ + -label {Cut} \ -font font_ui \ - -command "tk_textCut $ui_comm" -$ui_comm.ctxm add command -label "Copy" \ + -command {tk_textCut $ui_comm} +$ctxm add command \ + -label {Copy} \ -font font_ui \ - -command "tk_textCopy $ui_comm" -$ui_comm.ctxm add command -label "Paste" \ + -command {tk_textCopy $ui_comm} +$ctxm add command \ + -label {Paste} \ -font font_ui \ - -command "tk_textPaste $ui_comm" -$ui_comm.ctxm add command -label "Delete" \ + -command {tk_textPaste $ui_comm} +$ctxm add command \ + -label {Delete} \ -font font_ui \ - -command "$ui_comm delete sel.first sel.last" -$ui_comm.ctxm add separator -$ui_comm.ctxm add command -label "Select All" \ + -command {$ui_comm delete sel.first sel.last} +$ctxm add separator +$ctxm add command \ + -label {Select All} \ -font font_ui \ - -command "$ui_comm tag add sel 0.0 end" -$ui_comm.ctxm add command -label "Copy All" \ + -command {$ui_comm tag add sel 0.0 end} +$ctxm add command \ + -label {Copy All} \ -font font_ui \ - -command " + -command { $ui_comm tag add sel 0.0 end tk_textCopy $ui_comm $ui_comm tag remove sel 0.0 end - " -$ui_comm.ctxm add separator -$ui_comm.ctxm add command -label "Sign Off" \ + } +$ctxm add separator +$ctxm add command \ + -label {Sign Off} \ -font font_ui \ -command do_signoff -bind_button3 $ui_comm "tk_popup $ui_comm.ctxm %X %Y" +bind_button3 $ui_comm "tk_popup $ctxm %X %Y" # -- Diff Header -set ui_fname_value {} -set ui_fstatus_value {} +set current_diff {} +set diff_actions [list] +proc current_diff_trace {varname args} { + global current_diff diff_actions file_states + if {$current_diff eq {}} { + set s {} + set f {} + set p {} + set o disabled + } else { + set p $current_diff + set s [mapdesc [lindex $file_states($p) 0] $p] + set f {File:} + set p [escape_path $p] + set o normal + } + + .vpane.lower.diff.header.status configure -text $s + .vpane.lower.diff.header.file configure -text $f + .vpane.lower.diff.header.path configure -text $p + foreach w $diff_actions { + uplevel #0 $w $o + } +} +trace add variable current_diff write current_diff_trace + frame .vpane.lower.diff.header -background orange -label .vpane.lower.diff.header.l4 \ - -textvariable ui_fstatus_value \ +label .vpane.lower.diff.header.status \ -background orange \ -width $max_status_desc \ -anchor w \ -justify left \ -font font_ui -label .vpane.lower.diff.header.l1 -text {File:} \ +label .vpane.lower.diff.header.file \ -background orange \ + -anchor w \ + -justify left \ -font font_ui -set ui_fname .vpane.lower.diff.header.l2 -label $ui_fname \ - -textvariable ui_fname_value \ +label .vpane.lower.diff.header.path \ -background orange \ -anchor w \ -justify left \ -font font_ui -menu $ui_fname.ctxm -tearoff 0 -$ui_fname.ctxm add command -label "Copy" \ +pack .vpane.lower.diff.header.status -side left +pack .vpane.lower.diff.header.file -side left +pack .vpane.lower.diff.header.path -fill x +set ctxm .vpane.lower.diff.header.ctxm +menu $ctxm -tearoff 0 +$ctxm add command \ + -label {Copy} \ -font font_ui \ -command { clipboard clear clipboard append \ -format STRING \ -type STRING \ - -- $ui_fname_value + -- $current_diff } -bind_button3 $ui_fname "tk_popup $ui_fname.ctxm %X %Y" -pack .vpane.lower.diff.header.l4 -side left -pack .vpane.lower.diff.header.l1 -side left -pack $ui_fname -fill x +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y" # -- Diff Body frame .vpane.lower.diff.body @@ -2478,48 +2510,63 @@ $ui_diff tag conf bold -font font_diffbold # -- Diff Body Context Menu # -menu $ui_diff.ctxm -tearoff 0 -$ui_diff.ctxm add command -label "Copy" \ +set ctxm .vpane.lower.diff.body.ctxm +menu $ctxm -tearoff 0 +$ctxm add command \ + -label {Copy} \ -font font_ui \ - -command "tk_textCopy $ui_diff" -$ui_diff.ctxm add command -label "Select All" \ + -command {tk_textCopy $ui_diff} +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add command \ + -label {Select All} \ -font font_ui \ - -command "$ui_diff tag add sel 0.0 end" -$ui_diff.ctxm add command -label "Copy All" \ + -command {$ui_diff tag add sel 0.0 end} +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add command \ + -label {Copy All} \ -font font_ui \ - -command " + -command { $ui_diff tag add sel 0.0 end tk_textCopy $ui_diff $ui_diff tag remove sel 0.0 end - " -$ui_diff.ctxm add separator -$ui_diff.ctxm add command -label "Decrease Font Size" \ + } +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add separator +$ctxm add command \ + -label {Decrease Font Size} \ -font font_ui \ -command {incr_font_size font_diff -1} -$ui_diff.ctxm add command -label "Increase Font Size" \ +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add command \ + -label {Increase Font Size} \ -font font_ui \ -command {incr_font_size font_diff 1} -$ui_diff.ctxm add separator -$ui_diff.ctxm add command -label "Show Less Context" \ +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add separator +$ctxm add command \ + -label {Show Less Context} \ -font font_ui \ - -command {if {$ui_fname_value ne {} - && $repo_config(gui.diffcontext) >= 2} { + -command {if {$repo_config(gui.diffcontext) >= 2} { incr repo_config(gui.diffcontext) -1 reshow_diff }} -$ui_diff.ctxm add command -label "Show More Context" \ +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add command \ + -label {Show More Context} \ -font font_ui \ - -command {if {$ui_fname_value ne {}} { + -command { incr repo_config(gui.diffcontext) reshow_diff - }} -$ui_diff.ctxm add separator -$ui_diff.ctxm add command -label {Options...} \ + } +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add separator +$ctxm add command -label {Options...} \ -font font_ui \ -command do_options -bind_button3 $ui_diff "tk_popup $ui_diff.ctxm %X %Y" +bind_button3 $ui_diff "tk_popup $ctxm %X %Y" # -- Status Bar +# set ui_status_value {Initializing...} label .status -textvariable ui_status_value \ -anchor w \ @@ -2530,6 +2577,7 @@ label .status -textvariable ui_status_value \ pack .status -anchor w -side bottom -fill x # -- Load geometry +# catch { set gm $repo_config(gui.geometry) wm geometry . [lindex $gm 0] @@ -2543,6 +2591,7 @@ unset gm } # -- Key Bindings +# bind $ui_comm <$M1B-Key-Return> {do_commit;break} bind $ui_comm <$M1B-Key-i> {do_include_all;break} bind $ui_comm <$M1B-Key-I> {do_include_all;break} @@ -2590,6 +2639,7 @@ unset i set file_lists($ui_index) [list] set file_lists($ui_other) [list] +set current_diff {} wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm -- cgit v1.2.3 From b3678bacbcd2dc0e1c61fd91f4146d0c037e626f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 15 Nov 2006 20:04:17 -0500 Subject: git-gui: Created makefile to install the program. Since we want to be installed in gitexecdir so that "git gui" works we can guess where that directory is by asking the git wrapper executable and locating ourselves at the same location using the same install rules as core git. Signed-off-by: Shawn O. Pearce --- Makefile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..e3e871f7c4 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +all: git-gui + +gitexecdir := $(shell git --exec-path) +INSTALL = install + +DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) +gitexecdir_SQ = $(subst ','\'',$(gitexecdir)) + +GITGUI_BUILTIN = git-citool + +install: all + $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(INSTALL) git-gui '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(foreach p,$(GITGUI_BUILTIN), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) -- cgit v1.2.3 From fbee8500a5b64a1c0c6103232879bcecc30f64b4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 15 Nov 2006 22:13:45 -0500 Subject: git-gui: Correctly handle GIT_DIR environment variable. Some users may want to start us by running "git --git-dir=... gui" rather than trying to cd into the directory first. This is especially true if they want to just make a shortcut to our executable on Windows and always have that associated with a certain repository. Since Tcl on Windows throws away our environment and doesn't pass it down to the child process correctly we cannot call git-rev-parse to get the GIT_DIR environment variable. So instead we ask for it specifically ourselves; if its not defined then we ask rev-parse. This should actually reduce startup by 1 fork/exec if we were started as "git gui" as GIT_DIR will be set for us. Signed-off-by: Shawn O. Pearce --- git-gui | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/git-gui b/git-gui index 8449664538..7c2f803fec 100755 --- a/git-gui +++ b/git-gui @@ -161,16 +161,17 @@ proc info_popup {msg} { ## ## repository setup -if { [catch {set cdup [exec git rev-parse --show-cdup]} err] - || [catch {set gitdir [exec git rev-parse --git-dir]} err]} { +if { [catch {set gitdir $env(GIT_DIR)}] + && [catch {set gitdir [exec git rev-parse --git-dir]} err]} { catch {wm withdraw .} error_popup "Cannot find the git directory:\n\n$err" exit 1 } -if {$cdup ne ""} { - cd $cdup +if {[catch {cd [file dirname $gitdir]} err]} { + catch {wm withdraw .} + error_popup "No working directory [file dirname $gitdir]:\n\n$err" + exit 1 } -unset cdup set single_commit 0 if {$appname eq {git-citool}} { -- cgit v1.2.3 From 4aca740b3915b35d7fa1707be79b268b0cc94123 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 15 Nov 2006 22:35:26 -0500 Subject: git-gui: Create Windows shortcut icons for git-gui. If we are running on Windows we now offer a 'Create Desktop Icon' menu item under the Project menu. This pops up a save dialog box letting the user create a .bat file on their desktop (or somewhere else). The .bat script will startup Cygwin with a login shell then launch git-gui in the current working directory. This is very useful for Windows users who have little to no desire to start a command window just to run a git-gui session. Signed-off-by: Shawn O. Pearce --- git-gui | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/git-gui b/git-gui index 7c2f803fec..def6a5e070 100755 --- a/git-gui +++ b/git-gui @@ -2002,6 +2002,54 @@ proc do_save_config {w} { destroy $w } +proc do_windows_shortcut {} { + global gitdir appname argv0 + + set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] + + if {[catch { + set desktop [exec cygpath \ + --windows \ + --absolute \ + --long-name \ + --desktop] + }]} { + set desktop . + } + set fn [tk_getSaveFile \ + -parent . \ + -title "$appname ($reponame): Create Desktop Icon" \ + -initialdir $desktop \ + -initialfile "Git $reponame.bat"] + if {$fn != {}} { + if {[catch { + set fd [open $fn w] + set sh [exec cygpath \ + --windows \ + --absolute \ + --long-name \ + /bin/sh] + set me [exec cygpath \ + --unix \ + --absolute \ + $argv0] + set gd [exec cygpath \ + --unix \ + --absolute \ + $gitdir] + puts -nonewline $fd "\"$sh\" --login -c \"" + puts -nonewline $fd "GIT_DIR=\\\"$gd\\\"" + puts -nonewline $fd " \\\"$me\\\"" + puts $fd "&\"" + close $fd + } err]} { + error_popup "Cannot write script:\n\n$err" + } + } +} + proc toggle_or_diff {w x y} { global file_lists ui_index ui_other global last_clicked selected_paths @@ -2168,6 +2216,13 @@ if {!$single_commit} { .mbar.project add command -label {Repack Database} \ -command do_repack \ -font font_ui + + if {$tcl_platform(platform) eq {windows}} { + .mbar.project add command \ + -label {Create Desktop Icon} \ + -command do_windows_shortcut \ + -font font_ui + } } .mbar.project add command -label Quit \ -command do_quit \ -- cgit v1.2.3 From dbccbbda4f4c049552495a87b1747b1b2a1e2823 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 15 Nov 2006 22:45:33 -0500 Subject: git-gui: Protect ourselves from funny GIT_DIR/working directory setups. Since we have some serious problems with the GIT_DIR environment variable on Windows we cannot let the user use a non-standard GIT_DIR with their working directory. So require that the GIT_DIR name is actually ".git", that it exists, and that its parent directory is our working directory. Signed-off-by: Shawn O. Pearce --- git-gui | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index def6a5e070..c35c73c8c3 100755 --- a/git-gui +++ b/git-gui @@ -167,6 +167,16 @@ if { [catch {set gitdir $env(GIT_DIR)}] error_popup "Cannot find the git directory:\n\n$err" exit 1 } +if {![file isdirectory $gitdir]} { + catch {wm withdraw .} + error_popup "Git directory not found:\n\n$gitdir" + exit 1 +} +if {[lindex [file split $gitdir] end] ne {.git}} { + catch {wm withdraw .} + error_popup "Cannot use funny .git directory:\n\n$gitdir" + exit 1 +} if {[catch {cd [file dirname $gitdir]} err]} { catch {wm withdraw .} error_popup "No working directory [file dirname $gitdir]:\n\n$err" @@ -2040,8 +2050,8 @@ proc do_windows_shortcut {} { --absolute \ $gitdir] puts -nonewline $fd "\"$sh\" --login -c \"" - puts -nonewline $fd "GIT_DIR=\\\"$gd\\\"" - puts -nonewline $fd " \\\"$me\\\"" + puts -nonewline $fd "GIT_DIR='$gd'" + puts -nonewline $fd " '$me'" puts $fd "&\"" close $fd } err]} { -- cgit v1.2.3 From 306500fc09e7d8be042e8b9abbd9011b80b3300d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 15 Nov 2006 22:53:53 -0500 Subject: git-gui: Handle ' within paths when creating Windows shortcuts. Signed-off-by: Shawn O. Pearce --- git-gui | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git-gui b/git-gui index c35c73c8c3..013f21b2e8 100755 --- a/git-gui +++ b/git-gui @@ -2049,6 +2049,8 @@ proc do_windows_shortcut {} { --unix \ --absolute \ $gitdir] + regsub -all ' $me "'\\''" me + regsub -all ' $gd "'\\''" gd puts -nonewline $fd "\"$sh\" --login -c \"" puts -nonewline $fd "GIT_DIR='$gd'" puts -nonewline $fd " '$me'" -- cgit v1.2.3 From c1237ae288aae7e45a18f3d5097b49451293acfe Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 15 Nov 2006 23:52:20 -0500 Subject: git-gui: Only populate a fetch or push if we have an action. Don't offer to fetch from a remote unless we have at least one Pull: line in its .git/remotes/ file or at least one configuration value for remote..fetch. Ditto for push. Users shouldn't be fetching or pushing branch groups unless they have them configured; anything else is just crazy. Signed-off-by: Shawn O. Pearce --- git-gui | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/git-gui b/git-gui index 013f21b2e8..ea60e327ea 100755 --- a/git-gui +++ b/git-gui @@ -1343,13 +1343,65 @@ proc load_all_remotes {} { set all_remotes [lsort -unique $all_remotes] } -proc populate_remote_menu {m pfx op} { - global all_remotes +proc populate_fetch_menu {m} { + global gitdir all_remotes repo_config - foreach remote $all_remotes { - $m add command -label "$pfx $remote..." \ - -command [list $op $remote] \ - -font font_ui + foreach r $all_remotes { + set enable 0 + if {![catch {set a $repo_config(remote.$r.url)}]} { + if {![catch {set a $repo_config(remote.$r.fetch)}]} { + set enable 1 + } + } else { + catch { + set fd [open [file join $gitdir remotes $r] r] + while {[gets $fd n] >= 0} { + if {[regexp {^Pull:[ \t]*([^:]+):} $n]} { + set enable 1 + break + } + } + close $fd + } + } + + if {$enable} { + $m add command \ + -label "Fetch from $r..." \ + -command [list fetch_from $r] \ + -font font_ui + } + } +} + +proc populate_push_menu {m} { + global gitdir all_remotes repo_config + + foreach r $all_remotes { + set enable 0 + if {![catch {set a $repo_config(remote.$r.url)}]} { + if {![catch {set a $repo_config(remote.$r.push)}]} { + set enable 1 + } + } else { + catch { + set fd [open [file join $gitdir remotes $r] r] + while {[gets $fd n] >= 0} { + if {[regexp {^Push:[ \t]*([^:]+):} $n]} { + set enable 1 + break + } + } + close $fd + } + } + + if {$enable} { + $m add command \ + -label "Push to $r..." \ + -command [list push_to $r] \ + -font font_ui + } } } @@ -2713,8 +2765,8 @@ wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm if {!$single_commit} { load_all_remotes - populate_remote_menu .mbar.fetch From fetch_from - populate_remote_menu .mbar.push To push_to + populate_fetch_menu .mbar.fetch populate_pull_menu .mbar.pull + populate_push_menu .mbar.push } after 1 do_rescan -- cgit v1.2.3 From 06c311157a045c2189acc5496fdc71a806c28f8c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 00:31:00 -0500 Subject: git-gui: Create a .app file on MacOS X if requested. If a user works with a repository frequently they may want to just create an icon they can use to launch git-gui against that repository. Since we already support this concept on Windows we can do the same on Mac OS X by creating a .app file with a tiny shell script in it that sets up the necessary environment then invokes our script. Signed-off-by: Shawn O. Pearce --- git-gui | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/git-gui b/git-gui index ea60e327ea..472bcb7e32 100755 --- a/git-gui +++ b/git-gui @@ -2114,6 +2114,79 @@ proc do_windows_shortcut {} { } } +proc do_macosx_app {} { + global gitdir appname argv0 env + + set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] + + set fn [tk_getSaveFile \ + -parent . \ + -title "$appname ($reponame): Create Desktop Icon" \ + -initialdir [file join $env(HOME) Desktop] \ + -initialfile "Git $reponame.app"] + if {$fn != {}} { + if {[catch { + set Contents [file join $fn Contents] + set MacOS [file join $Contents MacOS] + set exe [file join $MacOS git-gui] + + file mkdir $MacOS + + set fd [open [file join $Contents PkgInfo] w] + puts -nonewline $fd {APPL????} + close $fd + + set fd [open [file join $Contents Info.plist] w] + puts $fd { + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + git-gui + CFBundleIdentifier + org.spearce.git-gui + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSPrincipalClass + NSApplication + +} + close $fd + + set fd [open $exe w] + set gd [file normalize $gitdir] + set ep [file normalize [exec git --exec-path]] + regsub -all ' $gd "'\\''" gd + regsub -all ' $ep "'\\''" ep + puts $fd "#!/bin/sh" + foreach name [array names env] { + if {[string match GIT_* $name]} { + regsub -all ' $env($name) "'\\''" v + puts $fd "export $name='$v'" + } + } + puts $fd "export PATH='$ep':\$PATH" + puts $fd "export GIT_DIR='$gd'" + puts $fd "exec [file normalize $argv0]" + close $fd + + file attributes $exe -permissions u+x,g+x,o+x + } err]} { + error_popup "Cannot write icon:\n\n$err" + } + } +} + proc toggle_or_diff {w x y} { global file_lists ui_index ui_other global last_clicked selected_paths @@ -2286,6 +2359,11 @@ if {!$single_commit} { -label {Create Desktop Icon} \ -command do_windows_shortcut \ -font font_ui + } elseif {[is_MacOSX]} { + .mbar.project add command \ + -label {Create Desktop Icon} \ + -command do_macosx_app \ + -font font_ui } } .mbar.project add command -label Quit \ -- cgit v1.2.3 From cbbaa28bc0a2b607b0f5e2e23f910d1619b52b0f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 01:20:37 -0500 Subject: git-gui: Display error dialog on Mac OS X when no .git found. If we can't locate a .git directory for the given directory we need to show a message to the user to let them know the directory wasn't found. But since this is before we have shown our main application window we cannot use that as the parent for the error popup; on Mac OS X this causes an error and prevents the dialog from showing. Instead only add -parent . to the popup call if we have mapped (shown) the main window. Signed-off-by: Shawn O. Pearce --- git-gui | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index 472bcb7e32..12a46e976c 100755 --- a/git-gui +++ b/git-gui @@ -130,12 +130,15 @@ proc error_popup {msg} { end] append title {)} } - tk_messageBox \ - -parent . \ + set cmd [list tk_messageBox \ -icon error \ -type ok \ -title "$title: error" \ - -message $msg + -message $msg] + if {[winfo ismapped .]} { + lappend cmd -parent . + } + eval $cmd } proc info_popup {msg} { -- cgit v1.2.3 From 4539eacd6dde87b4e73c859e946bb0a2c89d71f4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 02:50:58 -0500 Subject: git-gui: Make initial commits work properly. Apparently I never really tested the logic for making or amending an initial commit, so although most of the code was here in git-gui it didn't quite work as it was intended to. So this is all just bug fixes to make initial commits correctly generate the list of files going into the initial commit, or to show a newly added file's diff, and to amend an initial commit. Because we really want to diff the index against a tree-ish and there is no such tree-ish on an initial commit we create an empty tree through git-mktree and diff against that. This unfortunately creates a dangling tree, which may confuse a new user who uses git-gui to make a new commit and then immediately afterwards runs git fsck-objects to see if their object database is corrupt or not. Signed-off-by: Shawn O. Pearce --- git-gui | 74 ++++++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/git-gui b/git-gui index 12a46e976c..e2e0beae95 100755 --- a/git-gui +++ b/git-gui @@ -205,6 +205,7 @@ set index_lock_type none set HEAD {} set PARENT {} set commit_type {} +set empty_tree {} proc lock_index {type} { global index_lock_type disable_on_lock @@ -240,6 +241,7 @@ proc repository_state {hdvar ctvar} { upvar $hdvar hd $ctvar ct if {[catch {set hd [exec git rev-parse --verify HEAD]}]} { + set hd {} set ct initial } elseif {[file exists [file join $gitdir MERGE_HEAD]]} { set ct merge @@ -248,6 +250,18 @@ proc repository_state {hdvar ctvar} { } } +proc PARENT {} { + global PARENT empty_tree + + if {$PARENT ne {}} { + return $PARENT + } + if {$empty_tree eq {}} { + set empty_tree [exec git mktree << {}] + } + return $empty_tree +} + proc rescan {after} { global HEAD PARENT commit_type global ui_index ui_other ui_status_value ui_comm @@ -257,7 +271,7 @@ proc rescan {after} { if {$rescan_active > 0 || ![lock_index read]} return repository_state new_HEAD new_type - if {$commit_type eq {amend} + if {[string match amend* $commit_type] && $new_type eq {normal} && $new_HEAD eq $HEAD} { } else { @@ -296,10 +310,8 @@ proc rescan {after} { } proc rescan_stage2 {fd after} { - global gitdir PARENT commit_type - global ui_index ui_other ui_status_value ui_comm - global rescan_active - global buf_rdi buf_rdf buf_rlo + global gitdir ui_status_value + global rescan_active buf_rdi buf_rdf buf_rlo if {$fd ne {}} { read $fd @@ -320,7 +332,7 @@ proc rescan_stage2 {fd after} { set rescan_active 3 set ui_status_value {Scanning for modified files ...} - set fd_di [open "| git diff-index --cached -z $PARENT" r] + set fd_di [open "| git diff-index --cached -z [PARENT]" r] set fd_df [open "| git diff-files -z" r] set fd_lo [open $ls_others r] @@ -532,7 +544,7 @@ files list, to prevent possible confusion. proc show_diff {path {w {}} {lno {}}} { global file_states file_lists - global PARENT diff_3way diff_active repo_config + global diff_3way diff_active repo_config global ui_diff current_diff ui_status_value if {$diff_active || ![lock_index read]} return @@ -591,7 +603,7 @@ proc show_diff {path {w {}} {lno {}}} { } } - lappend cmd $PARENT + lappend cmd [PARENT] lappend cmd -- lappend cmd $path @@ -671,7 +683,7 @@ proc read_diff {fd} { proc load_last_commit {} { global HEAD PARENT commit_type ui_comm - if {$commit_type eq {amend}} return + if {[string match amend* $commit_type]} return if {$commit_type ne {normal}} { error_popup "Can't amend a $commit_type commit." return @@ -695,23 +707,24 @@ proc load_last_commit {} { return } + if {$parent_count > 1} { + error_popup {Can't amend a merge commit.} + return + } + if {$parent_count == 0} { - set commit_type amend - set HEAD {} + set commit_type amend-initial set PARENT {} - rescan {set ui_status_value {Ready.}} } elseif {$parent_count == 1} { set commit_type amend set PARENT $parent - $ui_comm delete 0.0 end - $ui_comm insert end $msg - $ui_comm edit modified false - $ui_comm edit reset - rescan {set ui_status_value {Ready.}} - } else { - error_popup {You can't amend a merge commit.} - return } + + $ui_comm delete 0.0 end + $ui_comm insert end $msg + $ui_comm edit modified false + $ui_comm edit reset + rescan {set ui_status_value {Ready.}} } proc commit_tree {} { @@ -722,7 +735,7 @@ proc commit_tree {} { # -- Our in memory state should match the repository. # repository_state curHEAD cur_type - if {$commit_type eq {amend} + if {[string match amend* $commit_type] && $cur_type eq {normal} && $curHEAD eq $HEAD} { } elseif {$commit_type ne $cur_type || $HEAD ne $curHEAD} { @@ -2559,13 +2572,18 @@ label $ui_coml -text {Commit Message:} \ -anchor w \ -justify left \ -font font_ui -trace add variable commit_type write {uplevel #0 { - switch -glob $commit_type \ - initial {$ui_coml conf -text {Initial Commit Message:}} \ - amend {$ui_coml conf -text {Amended Commit Message:}} \ - merge {$ui_coml conf -text {Merge Commit Message:}} \ - * {$ui_coml conf -text {Commit Message:}} -}} +proc trace_commit_type {varname args} { + global ui_coml commit_type + switch -glob -- $commit_type { + initial {set txt {Initial Commit Message:}} + amend {set txt {Amended Commit Message:}} + amend-initial {set txt {Amended Initial Commit Message:}} + merge {set txt {Merge Commit Message:}} + * {set txt {Commit Message:}} + } + $ui_coml conf -text $txt +} +trace add variable commit_type write trace_commit_type text $ui_comm -background white -borderwidth 1 \ -undo true \ -maxundo 20 \ -- cgit v1.2.3 From 32e0bcab59334ed8f1955c8afa830de22362c1ec Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 03:03:16 -0500 Subject: git-gui: Only reshow diff when really necessary. I noticed that we were reshowing the current diff during a commit; this occurs because we feed every added and modified file through update-index just before commit. During the update-index process we reshow the current diff if the current file in the diff pane was one of those added or modified files we reprocessed. This just slows down the UI more than is necessary. So refactoring update_index so that we don't call reshow_diff from within that code; instead we do it at a higher level. Signed-off-by: Shawn O. Pearce --- git-gui | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/git-gui b/git-gui index e2e0beae95..62545b7094 100755 --- a/git-gui +++ b/git-gui @@ -1251,12 +1251,11 @@ proc display_all_files {} { } proc update_index {msg pathList after} { - global update_index_cp update_index_rsd ui_status_value + global update_index_cp ui_status_value if {![lock_index update]} return set update_index_cp 0 - set update_index_rsd 0 set pathList [lsort $pathList] set totalCnt [llength $pathList] set batch [expr {int($totalCnt * .01) + 1}] @@ -1285,13 +1284,12 @@ proc update_index {msg pathList after} { } proc write_update_index {fd pathList totalCnt batch msg after} { - global update_index_cp update_index_rsd ui_status_value + global update_index_cp ui_status_value global file_states current_diff if {$update_index_cp >= $totalCnt} { close $fd unlock_index - if {$update_index_rsd} reshow_diff uplevel #0 $after return } @@ -1321,9 +1319,6 @@ proc write_update_index {fd pathList totalCnt batch msg after} { puts -nonewline $fd $path puts -nonewline $fd "\0" display_file $path $new - if {$current_diff eq $path} { - set update_index_rsd 1 - } } set ui_status_value [format \ @@ -1859,19 +1854,23 @@ proc do_rescan {} { } proc do_include_all {} { - global file_states + global file_states current_diff if {![lock_index begin-update]} return set pathList [list] + set after {} foreach path [array names file_states] { - set s $file_states($path) - set m [lindex $s 0] - switch -- $m { + switch -- [lindex $file_states($path) 0] { AM - MM - _M - - _D {lappend pathList $path} + _D { + lappend pathList $path + if {$path eq $current_diff} { + set after {reshow_diff;} + } + } } } if {$pathList eq {}} { @@ -1880,7 +1879,7 @@ proc do_include_all {} { update_index \ "Including all modified files" \ $pathList \ - {set ui_status_value {Ready to commit.}} + [concat $after {set ui_status_value {Ready to commit.}}] } } @@ -2204,7 +2203,7 @@ proc do_macosx_app {} { } proc toggle_or_diff {w x y} { - global file_lists ui_index ui_other + global file_lists current_diff ui_index ui_other global last_clicked selected_paths set pos [split [$w index @$x,$y] .] @@ -2222,10 +2221,15 @@ proc toggle_or_diff {w x y} { $ui_other tag remove in_sel 0.0 end if {$col == 0} { + if {$current_diff eq $path} { + set after {reshow_diff;} + } else { + set after {} + } update_index \ "Including [short_path $path]" \ [list $path] \ - {set ui_status_value {Ready.}} + [concat $after {set ui_status_value {Ready.}}] } else { show_diff $path $w $lno } @@ -2642,7 +2646,7 @@ bind_button3 $ui_comm "tk_popup $ctxm %X %Y" # -- Diff Header set current_diff {} set diff_actions [list] -proc current_diff_trace {varname args} { +proc trace_current_diff {varname args} { global current_diff diff_actions file_states if {$current_diff eq {}} { set s {} @@ -2664,7 +2668,7 @@ proc current_diff_trace {varname args} { uplevel #0 $w $o } } -trace add variable current_diff write current_diff_trace +trace add variable current_diff write trace_current_diff frame .vpane.lower.diff.header -background orange label .vpane.lower.diff.header.status \ -- cgit v1.2.3 From b67651129897738496a9dcd3c01129a42acd11db Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 03:08:51 -0500 Subject: git-gui: Refactor file state representations. It just felt wrong to me that I was using _ as part of the mode argument to display_file to mean "don't care/use existing" and * as part of the mode argument to mean "force to _". So instead use ? to mean "don't care/use existing" and _ to mean "force to _". The code is a lot clearer this way and hopefully it won't drive another developer insane, as it did me. Signed-off-by: Shawn O. Pearce --- git-gui | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/git-gui b/git-gui index 62545b7094..901d93236e 100755 --- a/git-gui +++ b/git-gui @@ -378,7 +378,7 @@ proc read_diff_index {fd after} { incr z2 -1 display_file \ [string range $buf_rdi $z1 $z2] \ - [string index $buf_rdi [expr {$z1 - 2}]]_ + [string index $buf_rdi [expr {$z1 - 2}]]? incr c } if {$c < $n} { @@ -407,7 +407,7 @@ proc read_diff_files {fd after} { incr z2 -1 display_file \ [string range $buf_rdf $z1 $z2] \ - _[string index $buf_rdf [expr {$z1 - 2}]] + ?[string index $buf_rdf [expr {$z1 - 2}]] incr c } if {$c < $n} { @@ -426,7 +426,7 @@ proc read_ls_others {fd after} { set pck [split $buf_rlo "\0"] set buf_rlo [lindex $pck end] foreach p [lrange $pck 0 end-1] { - display_file $p _O + display_file $p ?O } rescan_done $fd buf_rlo $after } @@ -1151,15 +1151,15 @@ proc merge_state {path new_state} { set icon [lindex $info 1] } - if {$s0 eq {_}} { + if {$s0 eq {?}} { set s0 [string index $state 0] - } elseif {$s0 eq {*}} { + } elseif {$s0 eq {_}} { set s0 _ } - if {$s1 eq {_}} { + if {$s1 eq {?}} { set s1 [string index $state 1] - } elseif {$s1 eq {*}} { + } elseif {$s1 eq {_}} { set s1 _ } @@ -1303,15 +1303,15 @@ proc write_update_index {fd pathList totalCnt batch msg after} { switch -glob -- [lindex $file_states($path) 0] { AD - MD - - _D {set new D*} + _D {set new D_} _M - MM - - M_ {set new M*} + M_ {set new M_} _O - AM - - A_ {set new A*} + A_ {set new A_} ?? {continue} } -- cgit v1.2.3 From c4ed879fc2b64b6c6f8a42bd156b2e4bd18e9420 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 03:24:20 -0500 Subject: git-gui: Add menu option to include only selected files. When the user selects a number of files they would typically expect to be able to act on that selection, such as by including those files into the next commit. So we now have a menu option under the Commit menu that lets the user include only the selection, rather than everything. If there is no selection but there is a file in the diff viewer than we consider that to be the selection (a selection of 1). Unfortunately we don't disable this option yet when there's nothing selected to include, but this is probably not a big deal as there are very few situations where there are no selected files. Signed-off-by: Shawn O. Pearce --- git-gui | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index 901d93236e..a020895020 100755 --- a/git-gui +++ b/git-gui @@ -1853,14 +1853,14 @@ proc do_rescan {} { rescan {set ui_status_value {Ready.}} } -proc do_include_all {} { +proc include_helper {txt paths} { global file_states current_diff if {![lock_index begin-update]} return set pathList [list] set after {} - foreach path [array names file_states] { + foreach path $paths { switch -- [lindex $file_states($path) 0] { AM - MM - @@ -1877,12 +1877,33 @@ proc do_include_all {} { unlock_index } else { update_index \ - "Including all modified files" \ + $txt \ $pathList \ [concat $after {set ui_status_value {Ready to commit.}}] } } +proc do_include_selection {} { + global current_diff selected_paths + + if {[array size selected_paths] > 0} { + include_helper \ + {Including selected files} \ + [array names selected_paths] + } elseif {$current_diff ne {}} { + include_helper \ + "Including [short_path $current_diff]" \ + [list $current_diff] + } +} + +proc do_include_all {} { + global file_states + include_helper \ + {Including all modified files} \ + [array names file_states] +} + set GIT_COMMITTER_IDENT {} proc do_signoff {} { @@ -2442,6 +2463,11 @@ lappend disable_on_lock \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] +.mbar.commit add command -label {Include Selected Files} \ + -command do_include_selection \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Include All Files} \ -command do_include_all \ -accelerator $M1T-I \ -- cgit v1.2.3 From a49c67d1ff9861d80bae57642d891016ea5debd5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 03:27:23 -0500 Subject: git-gui: Misc. comment formatting cleanups. Signed-off-by: Shawn O. Pearce --- git-gui | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/git-gui b/git-gui index a020895020..c2b4dd7067 100755 --- a/git-gui +++ b/git-gui @@ -202,11 +202,6 @@ set last_clicked {} set disable_on_lock [list] set index_lock_type none -set HEAD {} -set PARENT {} -set commit_type {} -set empty_tree {} - proc lock_index {type} { global index_lock_type disable_on_lock @@ -2374,6 +2369,7 @@ apply_config ## ui construction # -- Menu Bar +# menu .mbar -tearoff 0 .mbar add cascade -label Project -menu .mbar.project .mbar add cascade -label Edit -menu .mbar.edit @@ -2386,6 +2382,7 @@ if {!$single_commit} { . configure -menu .mbar # -- Project Menu +# menu .mbar.project .mbar.project add command -label Visualize \ -command do_gitk \ @@ -2451,6 +2448,7 @@ menu .mbar.edit -font font_ui # -- Commit Menu +# menu .mbar.commit .mbar.commit add command -label Rescan \ -command do_rescan \ @@ -2485,24 +2483,23 @@ lappend disable_on_lock \ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] +# -- Transport menus +# if {!$single_commit} { - # -- Fetch Menu menu .mbar.fetch - - # -- Pull Menu menu .mbar.pull - - # -- Push Menu menu .mbar.push } # -- Main Window Layout +# panedwindow .vpane -orient vertical panedwindow .vpane.files -orient horizontal .vpane add .vpane.files -sticky nsew -height 100 -width 400 pack .vpane -anchor n -side top -fill both -expand 1 # -- Index File List +# frame .vpane.files.index -height 100 -width 400 label .vpane.files.index.title -text {Modified Files} \ -background green \ @@ -2520,6 +2517,7 @@ pack $ui_index -side left -fill both -expand 1 .vpane.files add .vpane.files.index -sticky nsew # -- Other (Add) File List +# frame .vpane.files.other -height 100 -width 100 label .vpane.files.other.title -text {Untracked Files} \ -background red \ @@ -2545,6 +2543,7 @@ foreach i [list $ui_index $ui_other] { unset i # -- Diff and Commit Area +# frame .vpane.lower -height 300 -width 400 frame .vpane.lower.commarea frame .vpane.lower.diff -relief sunken -borderwidth 1 @@ -2553,6 +2552,7 @@ pack .vpane.lower.diff -side bottom -fill both -expand 1 .vpane add .vpane.lower -stick nsew # -- Commit Area Buttons +# frame .vpane.lower.commarea.buttons label .vpane.lower.commarea.buttons.l -text {} \ -anchor w \ @@ -2595,10 +2595,11 @@ lappend disable_on_lock \ {.vpane.lower.commarea.buttons.commit conf -state} # -- Commit Message Buffer +# frame .vpane.lower.commarea.buffer set ui_comm .vpane.lower.commarea.buffer.t set ui_coml .vpane.lower.commarea.buffer.l -label $ui_coml -text {Commit Message:} \ +label $ui_coml \ -anchor w \ -justify left \ -font font_ui @@ -2670,6 +2671,7 @@ $ctxm add command \ bind_button3 $ui_comm "tk_popup $ctxm %X %Y" # -- Diff Header +# set current_diff {} set diff_actions [list] proc trace_current_diff {varname args} { @@ -2732,6 +2734,7 @@ lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y" # -- Diff Body +# frame .vpane.lower.diff.body set ui_diff .vpane.lower.diff.body.t text $ui_diff -background white -borderwidth 0 \ @@ -2888,6 +2891,11 @@ unset i set file_lists($ui_index) [list] set file_lists($ui_other) [list] + +set HEAD {} +set PARENT {} +set commit_type {} +set empty_tree {} set current_diff {} wm title . "$appname ([file normalize [file dirname $gitdir]])" -- cgit v1.2.3 From 53716a7bc94497cd6d2dfd53b1e4f7669d677b01 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 03:31:25 -0500 Subject: git-gui: Start UI with the index locked. Because we immediately start a rescan operation, but do so slightly delayed (by 1 ms, to let the UI show before we start forking off git processes), we can't let the user try to activate any of the restricted GUI commands before the 1 ms timer expires and we kick off the rescan. So now we lock the index before we enter the Tk event loop, ensuring that it is impossible for the user to inject a conflicting UI event before our rescan can begin. Signed-off-by: Shawn O. Pearce --- git-gui | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index c2b4dd7067..4850e78ab0 100755 --- a/git-gui +++ b/git-gui @@ -211,7 +211,7 @@ proc lock_index {type} { uplevel #0 $w disabled } return 1 - } elseif {$index_lock_type eq {begin-update} && $type eq {update}} { + } elseif {$index_lock_type eq "begin-$type"} { set index_lock_type $type return 1 } @@ -2906,4 +2906,5 @@ if {!$single_commit} { populate_pull_menu .mbar.pull populate_push_menu .mbar.push } +lock_index begin-read after 1 do_rescan -- cgit v1.2.3 From ef5c971506bef5046f1a50e1bfb2630cf458afff Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 03:41:54 -0500 Subject: git-gui: Remove completed items from TODO list. Signed-off-by: Shawn O. Pearce --- TODO | 4 ---- 1 file changed, 4 deletions(-) diff --git a/TODO b/TODO index bb40a13bee..e9525cda3a 100644 --- a/TODO +++ b/TODO @@ -6,12 +6,8 @@ Items outstanding: * Update index with $PARENT version but leave working directory alone. - * Add all new files in one shot (e.g. "git add ."). - * Add file to .gitignore or info/excludes. - * Make initial commits work (currently it crashes the UI). - * Populate the pull menu with local branches. * Make use of the new default merge data stored in repo-config. -- cgit v1.2.3 From 24ac9b752d6df7ab309826d487fbeaae53806025 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 20:59:49 -0500 Subject: git-gui: Toggle between new commit and amend commit modes. I was starting to find it annoying that once you entered the 'Amend Last' mode there was no way to go back to the 'New Commit' mode without quitting and restarting git-gui. Its just confusing for the end-user. Now we can flip back and forth between a new commit and an amend commit through a pair of radio buttons on the header of the commit buffer area and through a pair of radio menu buttons in the Commit menu. Signed-off-by: Shawn O. Pearce --- git-gui | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 18 deletions(-) diff --git a/git-gui b/git-gui index 4850e78ab0..5c6af8940e 100755 --- a/git-gui +++ b/git-gui @@ -722,6 +722,16 @@ proc load_last_commit {} { rescan {set ui_status_value {Ready.}} } +proc create_new_commit {} { + global commit_type ui_comm + + set commit_type normal + $ui_comm delete 0.0 end + $ui_comm edit modified false + $ui_comm edit reset + rescan {set ui_status_value {Ready.}} +} + proc commit_tree {} { global HEAD commit_type file_states ui_comm repo_config @@ -880,7 +890,7 @@ proc commit_writetree {curHEAD msg} { proc commit_committree {fd_wt curHEAD msg} { global single_commit gitdir HEAD PARENT commit_type tcl_platform - global ui_status_value ui_comm + global ui_status_value ui_comm selected_commit_type global file_states selected_paths gets $fd_wt tree_id @@ -980,6 +990,7 @@ proc commit_committree {fd_wt curHEAD msg} { # -- Update status without invoking any git commands. # set commit_type normal + set selected_commit_type new set HEAD $cmt_id set PARENT $cmt_id @@ -1065,13 +1076,14 @@ Commit or throw away all changes before starting a pull operation. } proc post_pull_remote {remote branch success} { - global HEAD PARENT commit_type + global HEAD PARENT commit_type selected_commit_type global ui_status_value unlock_index if {$success} { repository_state HEAD commit_type set PARENT $HEAD + set selected_commit_type new set $ui_status_value "Pulling $branch from $remote complete." } else { set m "Conflicts detected while pulling $branch from $remote." @@ -1930,8 +1942,22 @@ proc do_signoff {} { } } -proc do_amend_last {} { - load_last_commit +proc do_select_commit_type {} { + global commit_type selected_commit_type + + if {$selected_commit_type eq {new} + && [string match amend* $commit_type]} { + create_new_commit + } elseif {$selected_commit_type eq {amend} + && ![string match amend* $commit_type]} { + load_last_commit + + # The amend request was rejected... + # + if {![string match amend* $commit_type]} { + set commit_type new + } + } } proc do_commit {} { @@ -2450,32 +2476,52 @@ menu .mbar.edit # -- Commit Menu # menu .mbar.commit -.mbar.commit add command -label Rescan \ - -command do_rescan \ - -accelerator F5 \ + +.mbar.commit add radiobutton \ + -label {New Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value new \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add radiobutton \ + -label {Amend Last Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value amend \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Amend Last Commit} \ - -command do_amend_last \ + +.mbar.commit add separator + +.mbar.commit add command -label Rescan \ + -command do_rescan \ + -accelerator F5 \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label {Include Selected Files} \ -command do_include_selection \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label {Include All Files} \ -command do_include_all \ -accelerator $M1T-I \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label {Sign Off} \ -command do_signoff \ -accelerator $M1T-S \ -font font_ui + .mbar.commit add command -label Commit \ -command do_commit \ -accelerator $M1T-Return \ @@ -2568,13 +2614,6 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.rescan conf -state} -button .vpane.lower.commarea.buttons.amend -text {Amend Last} \ - -command do_amend_last \ - -font font_ui -pack .vpane.lower.commarea.buttons.amend -side top -fill x -lappend disable_on_lock \ - {.vpane.lower.commarea.buttons.amend conf -state} - button .vpane.lower.commarea.buttons.incall -text {Include All} \ -command do_include_all \ -font font_ui @@ -2597,8 +2636,25 @@ lappend disable_on_lock \ # -- Commit Message Buffer # frame .vpane.lower.commarea.buffer +frame .vpane.lower.commarea.buffer.header set ui_comm .vpane.lower.commarea.buffer.t -set ui_coml .vpane.lower.commarea.buffer.l +set ui_coml .vpane.lower.commarea.buffer.header.l +radiobutton .vpane.lower.commarea.buffer.header.new \ + -text {New Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value new \ + -font font_ui +lappend disable_on_lock \ + [list .vpane.lower.commarea.buffer.header.new conf -state] +radiobutton .vpane.lower.commarea.buffer.header.amend \ + -text {Amend Last Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value amend \ + -font font_ui +lappend disable_on_lock \ + [list .vpane.lower.commarea.buffer.header.amend conf -state] label $ui_coml \ -anchor w \ -justify left \ @@ -2615,6 +2671,10 @@ proc trace_commit_type {varname args} { $ui_coml conf -text $txt } trace add variable commit_type write trace_commit_type +pack $ui_coml -side left -fill x +pack .vpane.lower.commarea.buffer.header.amend -side right +pack .vpane.lower.commarea.buffer.header.new -side right + text $ui_comm -background white -borderwidth 1 \ -undo true \ -maxundo 20 \ @@ -2625,7 +2685,7 @@ text $ui_comm -background white -borderwidth 1 \ -yscrollcommand {.vpane.lower.commarea.buffer.sby set} scrollbar .vpane.lower.commarea.buffer.sby \ -command [list $ui_comm yview] -pack $ui_coml -side top -fill x +pack .vpane.lower.commarea.buffer.header -side top -fill x pack .vpane.lower.commarea.buffer.sby -side right -fill y pack $ui_comm -side left -fill y pack .vpane.lower.commarea.buffer -side left -fill y @@ -2897,6 +2957,7 @@ set PARENT {} set commit_type {} set empty_tree {} set current_diff {} +set selected_commit_type new wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm -- cgit v1.2.3 From d63efae2816a277e40a379eb5c5f23f2a656fddc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 21:07:05 -0500 Subject: git-gui: Verify the user has GIT_COMMITTER_IDENT before comitting. Since git-commit also checks that the user has a GIT_COMMITTER_IDENT value before it lets the user make a commit we should do the same check here in git-gui. We cache the result and assume that the user won't do something which would change the status of GIT_COMMITTER_IDENT while we are running. Signed-off-by: Shawn O. Pearce --- git-gui | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/git-gui b/git-gui index 5c6af8940e..7630a2d2e7 100755 --- a/git-gui +++ b/git-gui @@ -732,10 +732,31 @@ proc create_new_commit {} { rescan {set ui_status_value {Ready.}} } +set GIT_COMMITTER_IDENT {} + +proc committer_ident {} { + global GIT_COMMITTER_IDENT + + if {$GIT_COMMITTER_IDENT eq {}} { + if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} { + error_popup "Unable to obtain your identity:\n\n$err" + return {} + } + if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \ + $me me GIT_COMMITTER_IDENT]} { + error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me" + return {} + } + } + + return $GIT_COMMITTER_IDENT +} + proc commit_tree {} { global HEAD commit_type file_states ui_comm repo_config if {![lock_index update]} return + if {[committer_ident] eq {}} return # -- Our in memory state should match the repository. # @@ -1911,24 +1932,13 @@ proc do_include_all {} { [array names file_states] } -set GIT_COMMITTER_IDENT {} - proc do_signoff {} { - global ui_comm GIT_COMMITTER_IDENT + global ui_comm - if {$GIT_COMMITTER_IDENT eq {}} { - if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} { - error_popup "Unable to obtain your identity:\n\n$err" - return - } - if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \ - $me me GIT_COMMITTER_IDENT]} { - error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me" - return - } - } + set me [committer_ident] + if {$me eq {}} return - set sob "Signed-off-by: $GIT_COMMITTER_IDENT" + set sob "Signed-off-by: $me" set last [$ui_comm get {end -1c linestart} {end -1c}] if {$last ne $sob} { $ui_comm edit separator -- cgit v1.2.3 From bca680b054ffa736e876075fad358bee1caa898a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 21:13:16 -0500 Subject: git-gui: Rephrase rescan before commit informational message. Its not an error that a rescan is required before commit; its just something we do as a safety feature to try and ensure the user knows what is going into this commit. So the dialog should use the info icon (if one is used by the host OS) rather than the error icon. Its also not "highly likely" that another Git program modified the repository, its completely the case. There is no reason why the repository would not match our last scanned state unless another Git program modified the repository (or someone else did so by hand). So don't be vague about it, own up to the issue and go on with our business. Signed-off-by: Shawn O. Pearce --- git-gui | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/git-gui b/git-gui index 7630a2d2e7..23efe4742b 100755 --- a/git-gui +++ b/git-gui @@ -765,13 +765,13 @@ proc commit_tree {} { && $cur_type eq {normal} && $curHEAD eq $HEAD} { } elseif {$commit_type ne $cur_type || $HEAD ne $curHEAD} { - error_popup {Last scanned state does not match repository state. + info_popup {Last scanned state does not match repository state. -Its highly likely that another Git program modified the -repository since the last scan. A rescan is required -before committing. +Another Git program has modified this repository +since the last scan. A rescan must be performed +before another commit can be created. -A rescan will be automatically started now. +The rescan will be automatically started now. } unlock_index rescan {set ui_status_value {Ready.}} -- cgit v1.2.3 From 54896cf7c150038bd286b909897673f85c691abc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 21:33:04 -0500 Subject: git-gui: Allow adding untracked files in selection. The previous implementation of do_include_selection did not actually add files in state _O (untracked, not added) into the repository when they were in the selection and Commit->Include Selected Files was used. This was due to the file state filtering logic being the same as that of Commit->Include All Files, which only considers existing files. Also fixed a minor issue with rejected attempts to amend an initial commit. Signed-off-by: Shawn O. Pearce --- git-gui | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index 23efe4742b..d1054f6632 100755 --- a/git-gui +++ b/git-gui @@ -1891,9 +1891,13 @@ proc include_helper {txt paths} { foreach path $paths { switch -- [lindex $file_states($path) 0] { AM - + AD - MM - + UM - + U_ - _M - - _D { + _D - + _O { lappend pathList $path if {$path eq $current_diff} { set after {reshow_diff;} @@ -1927,9 +1931,20 @@ proc do_include_selection {} { proc do_include_all {} { global file_states + + set paths [list] + foreach path [array names file_states] { + switch -- [lindex $file_states($path) 0] { + AM - + AD - + MM - + _M - + _D {lappend paths $path} + } + } include_helper \ {Including all modified files} \ - [array names file_states] + $paths } proc do_signoff {} { @@ -1965,7 +1980,7 @@ proc do_select_commit_type {} { # The amend request was rejected... # if {![string match amend* $commit_type]} { - set commit_type new + set selected_commit_type new } } } -- cgit v1.2.3 From d7c0d7c86191925dcd0dfff353823f9f7e473de5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 18 Nov 2006 23:17:41 -0500 Subject: git-gui: Don't create PkgInfo on Mac OS X "desktop icons". Turns out that we really don't need the Contents/PkgInfo file on Mac OS 10.4. The Finder will still launch the application properly without one. Signed-off-by: Shawn O. Pearce --- git-gui | 4 ---- 1 file changed, 4 deletions(-) diff --git a/git-gui b/git-gui index d1054f6632..063d83c124 100755 --- a/git-gui +++ b/git-gui @@ -2216,10 +2216,6 @@ proc do_macosx_app {} { file mkdir $MacOS - set fd [open [file join $Contents PkgInfo] w] - puts -nonewline $fd {APPL????} - close $fd - set fd [open [file join $Contents Info.plist] w] puts $fd { -- cgit v1.2.3 From 1461c5f3d0634f6fad0013eba1ef1087a38cc748 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 00:29:55 -0500 Subject: git-gui: Teach the gui how to uninclude a file. Sometimes the user may want to keep their working directory file to be the same content but they don't want it to be part of the current commit anymore. In this case we need to undo any changes made to the index for that file (by reloading the info from HEAD or removing the file from the index) but leave the working directory alone. Signed-off-by: Shawn O. Pearce --- git-gui | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 157 insertions(+), 28 deletions(-) diff --git a/git-gui b/git-gui index 063d83c124..6b886c64ce 100755 --- a/git-gui +++ b/git-gui @@ -1,4 +1,3 @@ -#!/bin/sh # Tcl ignores the next line -*- tcl -*- \ exec wish "$0" -- "$@" @@ -369,12 +368,14 @@ proc read_diff_index {fd after} { set z2 [string first "\0" $buf_rdi $z1] if {$z2 == -1} break - set c $z2 - incr z2 -1 - display_file \ - [string range $buf_rdi $z1 $z2] \ - [string index $buf_rdi [expr {$z1 - 2}]]? incr c + set n [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }] + merge_state \ + [string range $buf_rdi $z1 [expr {$z2 - 1}]] \ + [lindex $n 4]? \ + [list [lindex $n 0] [lindex $n 2]] \ + [list] + set c $z2 } if {$c < $n} { set buf_rdi [string range $buf_rdi $c end] @@ -398,12 +399,14 @@ proc read_diff_files {fd after} { set z2 [string first "\0" $buf_rdf $z1] if {$z2 == -1} break - set c $z2 - incr z2 -1 - display_file \ - [string range $buf_rdf $z1 $z2] \ - ?[string index $buf_rdf [expr {$z1 - 2}]] incr c + set n [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }] + merge_state \ + [string range $buf_rdf $z1 [expr {$z2 - 1}]] \ + ?[lindex $n 4] \ + [list] \ + [list [lindex $n 0] [lindex $n 2]] + set c $z2 } if {$c < $n} { set buf_rdf [string range $buf_rdf $c end] @@ -421,7 +424,7 @@ proc read_ls_others {fd after} { set pck [split $buf_rlo "\0"] set buf_rlo [lindex $pck end] foreach p [lrange $pck 0 end-1] { - display_file $p ?O + merge_state $p ?O } rescan_done $fd buf_rlo $after } @@ -1165,7 +1168,7 @@ proc short_path {path} { set next_icon_id 0 -proc merge_state {path new_state} { +proc merge_state {path new_state {head_info {}} {index_info {}}} { global file_states next_icon_id set s0 [string index $new_state 0] @@ -1177,30 +1180,31 @@ proc merge_state {path new_state} { } else { set state [lindex $info 0] set icon [lindex $info 1] + if {$head_info eq {}} {set head_info [lindex $info 2]} + if {$index_info eq {}} {set index_info [lindex $info 3]} } - if {$s0 eq {?}} { - set s0 [string index $state 0] - } elseif {$s0 eq {_}} { - set s0 _ - } + if {$s0 eq {?}} {set s0 [string index $state 0]} \ + elseif {$s0 eq {_}} {set s0 _} + + if {$s1 eq {?}} {set s1 [string index $state 1]} \ + elseif {$s1 eq {_}} {set s1 _} - if {$s1 eq {?}} { - set s1 [string index $state 1] - } elseif {$s1 eq {_}} { - set s1 _ + if {$s0 ne {_} && [string index $state 0] eq {_} + && $head_info eq {}} { + set head_info $index_info } - set file_states($path) [list $s0$s1 $icon] + set file_states($path) [list $s0$s1 $icon \ + $head_info $index_info \ + ] return $state } proc display_file {path state} { - global file_states file_lists selected_paths rescan_active + global file_states file_lists selected_paths set old_m [merge_state $path $state] - if {$rescan_active > 0} return - set s $file_states($path) set new_m [lindex $s 0] set new_w [mapcol $new_m $path] @@ -1278,6 +1282,80 @@ proc display_all_files {} { $ui_other conf -state disabled } +proc update_indexinfo {msg pathList after} { + global update_index_cp ui_status_value + + if {![lock_index update]} return + + set update_index_cp 0 + set pathList [lsort $pathList] + set totalCnt [llength $pathList] + set batch [expr {int($totalCnt * .01) + 1}] + if {$batch > 25} {set batch 25} + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + 0.0] + set fd [open "| git update-index -z --index-info" w] + fconfigure $fd \ + -blocking 0 \ + -buffering full \ + -buffersize 512 \ + -translation binary + fileevent $fd writable [list \ + write_update_indexinfo \ + $fd \ + $pathList \ + $totalCnt \ + $batch \ + $msg \ + $after \ + ] +} + +proc write_update_indexinfo {fd pathList totalCnt batch msg after} { + global update_index_cp ui_status_value + global file_states current_diff + + if {$update_index_cp >= $totalCnt} { + close $fd + unlock_index + uplevel #0 $after + return + } + + for {set i $batch} \ + {$update_index_cp < $totalCnt && $i > 0} \ + {incr i -1} { + set path [lindex $pathList $update_index_cp] + incr update_index_cp + + set s $file_states($path) + switch -glob -- [lindex $s 0] { + A? {set new _O} + M? {set new _M} + D? {set new _?} + ?? {continue} + } + set info [lindex $s 2] + if {$info eq {}} continue + + puts -nonewline $fd $info + puts -nonewline $fd "\t" + puts -nonewline $fd $path + puts -nonewline $fd "\0" + display_file $path $new + } + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + [expr {100.0 * $update_index_cp / $totalCnt}]] +} + proc update_index {msg pathList after} { global update_index_cp ui_status_value @@ -1881,6 +1959,49 @@ proc do_rescan {} { rescan {set ui_status_value {Ready.}} } +proc remove_helper {txt paths} { + global file_states current_diff + + if {![lock_index begin-update]} return + + set pathList [list] + set after {} + foreach path $paths { + switch -glob -- [lindex $file_states($path) 0] { + A? - + M? - + D? { + lappend pathList $path + if {$path eq $current_diff} { + set after {reshow_diff;} + } + } + } + } + if {$pathList eq {}} { + unlock_index + } else { + update_indexinfo \ + $txt \ + $pathList \ + [concat $after {set ui_status_value {Ready.}}] + } +} + +proc do_remove_selection {} { + global current_diff selected_paths + + if {[array size selected_paths] > 0} { + remove_helper \ + {Removing selected files from commit} \ + [array names selected_paths] + } elseif {$current_diff ne {}} { + remove_helper \ + "Removing [short_path $current_diff] from commit" \ + [list $current_diff] + } +} + proc include_helper {txt paths} { global file_states current_diff @@ -2525,19 +2646,27 @@ lappend disable_on_lock \ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Include Selected Files} \ +.mbar.commit add command -label {Remove From Commit} \ + -command do_remove_selection \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add command -label {Include In Commit} \ -command do_include_selection \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Include All Files} \ +.mbar.commit add command -label {Include All} \ -command do_include_all \ -accelerator $M1T-I \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] +.mbar.commit add separator + .mbar.commit add command -label {Sign Off} \ -command do_signoff \ -accelerator $M1T-S \ -- cgit v1.2.3 From 74d18d2edfc84d35b20e1b4520e496aadc14c365 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 00:37:49 -0500 Subject: git-gui: Make consecutive icon clicks toggle included status of a file. If the user clicks on the icon associated with a file we now flip to the inverse status. Partially included files first fully include, then fully uninclude, as we don't keep track of intermediate partial inclusions. Signed-off-by: Shawn O. Pearce --- git-gui | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/git-gui b/git-gui index 6b886c64ce..7126f8d897 100755 --- a/git-gui +++ b/git-gui @@ -2387,7 +2387,7 @@ proc do_macosx_app {} { } proc toggle_or_diff {w x y} { - global file_lists current_diff ui_index ui_other + global file_states file_lists current_diff ui_index ui_other global last_clicked selected_paths set pos [split [$w index @$x,$y] .] @@ -2410,10 +2410,23 @@ proc toggle_or_diff {w x y} { } else { set after {} } - update_index \ - "Including [short_path $path]" \ - [list $path] \ - [concat $after {set ui_status_value {Ready.}}] + switch -glob -- [lindex $file_states($path) 0] { + A_ - + AO - + M_ - + D_ { + update_indexinfo \ + "Removing [short_path $path] from commit" \ + [list $path] \ + [concat $after {set ui_status_value {Ready.}}] + } + ?? { + update_index \ + "Including [short_path $path]" \ + [list $path] \ + [concat $after {set ui_status_value {Ready.}}] + } + } } else { show_diff $path $w $lno } -- cgit v1.2.3 From dde5974ef109ed3aadfbac4d233899fb04d1c9ff Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 00:46:08 -0500 Subject: git-gui: Correct toggling of deleted file status. There was a bug with the way we handled deleted file status. A file really shouldn't be in D_ state when it has been deleted, instead it is really DD. Therefore we should have toggled _D to DD, not D_, thereby letting us toggle back to _D. Signed-off-by: Shawn O. Pearce --- git-gui | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 7126f8d897..7ea31e5089 100755 --- a/git-gui +++ b/git-gui @@ -1022,6 +1022,8 @@ proc commit_committree {fd_wt curHEAD msg} { set s $file_states($path) set m [lindex $s 0] switch -glob -- $m { + DD - + AO {set m __} A? - M? - D? {set m _[string index $m 1]} @@ -1409,7 +1411,7 @@ proc write_update_index {fd pathList totalCnt batch msg after} { switch -glob -- [lindex $file_states($path) 0] { AD - MD - - _D {set new D_} + _D {set new DD} _M - MM - @@ -2414,6 +2416,7 @@ proc toggle_or_diff {w x y} { A_ - AO - M_ - + DD - D_ { update_indexinfo \ "Removing [short_path $path] from commit" \ -- cgit v1.2.3 From 86291555c94300b057a156d87239a6cab09511b3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 01:00:48 -0500 Subject: git-gui: Fix list loading corruption introduced by 1461c5f3. Tcl let me assign two different types of values to the variable $n. Prior to 1461c5f3 $n was the total number of bytes in the string; but in that commit it also became the current info list for the current file. This caused $c < $n to fail as $n was now treated as 0 and we only loaded the first file in each buffer. So use a different variable, like $i, instead. Signed-off-by: Shawn O. Pearce --- git-gui | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/git-gui b/git-gui index 7ea31e5089..bd22125e51 100755 --- a/git-gui +++ b/git-gui @@ -369,13 +369,14 @@ proc read_diff_index {fd after} { if {$z2 == -1} break incr c - set n [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }] + set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }] merge_state \ [string range $buf_rdi $z1 [expr {$z2 - 1}]] \ - [lindex $n 4]? \ - [list [lindex $n 0] [lindex $n 2]] \ + [lindex $i 4]? \ + [list [lindex $i 0] [lindex $i 2]] \ [list] set c $z2 + incr c } if {$c < $n} { set buf_rdi [string range $buf_rdi $c end] @@ -400,13 +401,14 @@ proc read_diff_files {fd after} { if {$z2 == -1} break incr c - set n [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }] + set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }] merge_state \ [string range $buf_rdf $z1 [expr {$z2 - 1}]] \ - ?[lindex $n 4] \ + ?[lindex $i 4] \ [list] \ - [list [lindex $n 0] [lindex $n 2]] + [list [lindex $i 0] [lindex $i 2]] set c $z2 + incr c } if {$c < $n} { set buf_rdf [string range $buf_rdf $c end] -- cgit v1.2.3 From 0d5709cf88e9f242e0e31ccbda42a1c827c90a22 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 01:06:42 -0500 Subject: git-gui: Describe deleted symlinks in a more friendly way. Currently core-git's diff utilities report a deleted symlink as a deleted file with a mode of 120000. This is not nearly as user friendly as one might like, as the user must remember that 120000 is the UNIX mode bits for a symlink. So instead we transform the not-so-friendly message from core-git into a slightly more user friendly "deleted symlink" message. Signed-off-by: Shawn O. Pearce --- git-gui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-gui b/git-gui index bd22125e51..3f7e408356 100755 --- a/git-gui +++ b/git-gui @@ -628,6 +628,9 @@ proc read_diff {fd} { if {[string match {diff --combined *} $line]} continue if {[string match {--- *} $line]} continue if {[string match {+++ *} $line]} continue + if {$line eq {deleted file mode 120000}} { + set line "deleted symlink" + } if {[string match index* $line]} { if {[string first , $line] >= 0} { set diff_3way 1 -- cgit v1.2.3 From 51cc47fedaaea46a556aac7d4e32683abca1b57b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 01:20:42 -0500 Subject: git-gui: Correct toggling of added/untracked status for new files. New files also lack index data from diff-files therefore we cannot use their diff-files index data when we update-index. Instead we can use the fact that Git has them hardcoded as "0 0{40}" and do the same thing ourselves. This way you can toggle an untracked file into added status and back out to untracked. Signed-off-by: Shawn O. Pearce --- git-gui | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index 3f7e408356..75d1640c0f 100755 --- a/git-gui +++ b/git-gui @@ -1174,9 +1174,10 @@ proc short_path {path} { } set next_icon_id 0 +set null_sha1 [string repeat 0 40] proc merge_state {path new_state {head_info {}} {index_info {}}} { - global file_states next_icon_id + global file_states next_icon_id null_sha1 set s0 [string index $new_state 0] set s1 [string index $new_state 1] @@ -1197,7 +1198,9 @@ proc merge_state {path new_state {head_info {}} {index_info {}}} { if {$s1 eq {?}} {set s1 [string index $state 1]} \ elseif {$s1 eq {_}} {set s1 _} - if {$s0 ne {_} && [string index $state 0] eq {_} + if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} { + set head_info [list 0 $null_sha1] + } elseif {$s0 ne {_} && [string index $state 0] eq {_} && $head_info eq {}} { set head_info $index_info } -- cgit v1.2.3 From 0c70864b8506ea17cc0615ae4ba55b60fed6af58 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 01:23:06 -0500 Subject: git-gui: Updated TODO list now that a task is complete. Signed-off-by: Shawn O. Pearce --- TODO | 3 --- 1 file changed, 3 deletions(-) diff --git a/TODO b/TODO index e9525cda3a..d27227c376 100644 --- a/TODO +++ b/TODO @@ -3,9 +3,6 @@ Items outstanding: * Checkout $PARENT version to working directory, overwriting current version. ($PARENT is HEAD, except when amending). - * Update index with $PARENT version but leave working directory - alone. - * Add file to .gitignore or info/excludes. * Populate the pull menu with local branches. -- cgit v1.2.3 From 38dbe273ffb86d8dc5c90403c27c561e683ade4a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 02:46:52 -0500 Subject: git-gui: Refactored diff line display formatting logic. The tags used for diff formatting (which I inherited from gitool) just didn't make a whole lot of sense, especially if you wanted to try to match them to the diff output you were seeing on screen. It did not help that the diff-index -c output's first two columns are also munged to make the diff output more user friendly. So this is a large refactoring of the tags used for diff display. Now our tag names match what we put in the left column of each line, which makes it easier to correlate presentation and implementation. I removed bold font usage from everything except the hunk headers as I really did not like the way bold font caused column alignments to become out of whack within the diff viewer. It also drew attention to the parts of the file which were identically changed in both the index and in the working directory, yet these are usually the parts I find myself caring the least about. So its very counter-intuitive. Lines which are changed differently by both the index and the working directory are now shown with background colors which span the entire line, making these lines easier to pick out of the diff. In general these are the lines that appear to be more interesting to me when looking at the 3-way diff as they are the ones which contain recent and quite different changes. Signed-off-by: Shawn O. Pearce --- git-gui | 80 +++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/git-gui b/git-gui index 75d1640c0f..a2a76c11dd 100755 --- a/git-gui +++ b/git-gui @@ -544,7 +544,7 @@ files list, to prevent possible confusion. proc show_diff {path {w {}} {lno {}}} { global file_states file_lists - global diff_3way diff_active repo_config + global is_3way_diff diff_active repo_config global ui_diff current_diff ui_status_value if {$diff_active || ![lock_index read]} return @@ -565,7 +565,7 @@ proc show_diff {path {w {}} {lno {}}} { set s $file_states($path) set m [lindex $s 0] - set diff_3way 0 + set is_3way_diff 0 set diff_active 1 set current_diff $path set ui_status_value "Loading diff of [escape_path $path]..." @@ -620,51 +620,52 @@ proc show_diff {path {w {}} {lno {}}} { } proc read_diff {fd} { - global ui_diff ui_status_value diff_3way diff_active + global ui_diff ui_status_value is_3way_diff diff_active global repo_config + $ui_diff conf -state normal while {[gets $fd line] >= 0} { - if {[string match {diff --git *} $line]} continue + # -- Cleanup uninteresting diff header lines. + # + if {[string match {diff --git *} $line]} continue if {[string match {diff --combined *} $line]} continue - if {[string match {--- *} $line]} continue - if {[string match {+++ *} $line]} continue + if {[string match {--- *} $line]} continue + if {[string match {+++ *} $line]} continue if {$line eq {deleted file mode 120000}} { set line "deleted symlink" } - if {[string match index* $line]} { - if {[string first , $line] >= 0} { - set diff_3way 1 - } - } - $ui_diff conf -state normal - if {!$diff_3way} { - set x [string index $line 0] - switch -- $x { - "@" {set tags da} - "+" {set tags dp} - "-" {set tags dm} + # -- Automatically detect if this is a 3 way diff. + # + if {[string match {@@@ *} $line]} {set is_3way_diff 1} + + # -- Reformat a 3 way diff, 'cause its too weird. + # + if {$is_3way_diff} { + set op [string range $line 0 1] + switch -- $op { + {@@} {set tags d_@} + {++} {set tags d_+ ; set op { +}} + {--} {set tags d_- ; set op { -}} + { +} {set tags d_++; set op {++}} + { -} {set tags d_--; set op {--}} + {+ } {set tags d_-+; set op {-+}} + {- } {set tags d_+-; set op {+-}} default {set tags {}} } + set line [string replace $line 0 1 $op] } else { - set x [string range $line 0 1] - switch -- $x { - default {set tags {}} - "@@" {set tags da} - "++" {set tags dp; set x " +"} - " +" {set tags {di bold}; set x "++"} - "+ " {set tags dni; set x "-+"} - "--" {set tags dm; set x " -"} - " -" {set tags {dm bold}; set x "--"} - "- " {set tags di; set x "+-"} + switch -- [string index $line 0] { + @ {set tags d_@} + + {set tags d_+} + - {set tags d_-} default {set tags {}} } - set line [string replace $line 0 1 $x] } $ui_diff insert end $line $tags - $ui_diff insert end "\n" - $ui_diff conf -state disabled + $ui_diff insert end "\n" $tags } + $ui_diff conf -state disabled if {[eof $fd]} { close $fd @@ -2987,12 +2988,17 @@ pack $ui_diff -side left -fill both -expand 1 pack .vpane.lower.diff.header -side top -fill x pack .vpane.lower.diff.body -side bottom -fill both -expand 1 -$ui_diff tag conf dm -foreground red -$ui_diff tag conf dp -foreground blue -$ui_diff tag conf di -foreground {#00a000} -$ui_diff tag conf dni -foreground {#a000a0} -$ui_diff tag conf da -font font_diffbold -$ui_diff tag conf bold -font font_diffbold +$ui_diff tag conf d_@ -font font_diffbold +$ui_diff tag conf d_+ -foreground blue +$ui_diff tag conf d_- -foreground red +$ui_diff tag conf d_++ -foreground {#00a000} +$ui_diff tag conf d_-- -foreground {#a000a0} +$ui_diff tag conf d_+- \ + -foreground red \ + -background {light goldenrod yellow} +$ui_diff tag conf d_-+ \ + -foreground blue \ + -background azure2 # -- Diff Body Context Menu # -- cgit v1.2.3 From bd11b82db88049fc4822d52764c009cc55b55afd Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 02:57:58 -0500 Subject: git-gui: Restore the all important shebang line. Accidentally removed by an unnoticed fat finger accident in vi during commit 1461c5f3. Signed-off-by: Shawn O. Pearce --- git-gui | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui b/git-gui index a2a76c11dd..3f0424a33a 100755 --- a/git-gui +++ b/git-gui @@ -1,3 +1,4 @@ +#!/bin/sh # Tcl ignores the next line -*- tcl -*- \ exec wish "$0" -- "$@" -- cgit v1.2.3 From a29481e2123c2fd14fbcb2531be9de16adf21779 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 03:38:48 -0500 Subject: git-gui: Update in memory states after commit. In order to allow the user to toggle include/exclude from next commit for files which were partially included in the last commit we need the current index mode+sha1 data stored in our file_states array. For any partially included file we have this information from diff-files, so we just have to copy it over to the diff-index portion of our state array. Signed-off-by: Shawn O. Pearce --- git-gui | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/git-gui b/git-gui index 3f0424a33a..dcb25c8d69 100755 --- a/git-gui +++ b/git-gui @@ -922,7 +922,7 @@ proc commit_writetree {curHEAD msg} { proc commit_committree {fd_wt curHEAD msg} { global single_commit gitdir HEAD PARENT commit_type tcl_platform global ui_status_value ui_comm selected_commit_type - global file_states selected_paths + global file_states selected_paths rescan_active gets $fd_wt tree_id if {$tree_id eq {} || [catch {close $fd_wt} err]} { @@ -1018,7 +1018,7 @@ proc commit_committree {fd_wt curHEAD msg} { if {$single_commit} do_quit - # -- Update status without invoking any git commands. + # -- Update in memory status # set commit_type normal set selected_commit_type new @@ -1029,18 +1029,29 @@ proc commit_committree {fd_wt curHEAD msg} { set s $file_states($path) set m [lindex $s 0] switch -glob -- $m { - DD - - AO {set m __} - A? - - M? - - D? {set m _[string index $m 1]} - } - - if {$m eq {__}} { + _O - + _M - + _D {continue} + __ - + A_ - + M_ - + DD { unset file_states($path) catch {unset selected_paths($path)} - } else { - lset file_states($path) 0 $m + } + DO { + set file_states($path) [list _O [lindex $s 1] {} {}] + } + AM - + AD - + MM - + DM { + set file_states($path) [list \ + _[string index $m 1] \ + [lindex $s 1] \ + [lindex $s 3] \ + {}] + } } } @@ -1661,9 +1672,9 @@ foreach i { {AD o question "Added (but now gone)"} {_D i question "Missing"} - {D_ i removed "Removed by commit"} {DD i removed "Removed by commit"} {DO i removed "Removed (still exists)"} + {DM i removed "Removed (but modified)"} {UM i merge "Merge conflicts"} {U_ i merge "Merge conflicts"} @@ -2424,7 +2435,6 @@ proc toggle_or_diff {w x y} { } switch -glob -- [lindex $file_states($path) 0] { A_ - - AO - M_ - DD - D_ { -- cgit v1.2.3 From 375f38828e9c50ad79b6582e768db410216c2c41 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 19 Nov 2006 03:46:29 -0500 Subject: git-gui: Correct some state matchings for include/remove. Signed-off-by: Shawn O. Pearce --- git-gui | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index dcb25c8d69..dcbb100feb 100755 --- a/git-gui +++ b/git-gui @@ -1676,6 +1676,7 @@ foreach i { {DO i removed "Removed (still exists)"} {DM i removed "Removed (but modified)"} + {UD i merge "Merge conflicts"} {UM i merge "Merge conflicts"} {U_ i merge "Merge conflicts"} } { @@ -2033,12 +2034,11 @@ proc include_helper {txt paths} { set pathList [list] set after {} foreach path $paths { - switch -- [lindex $file_states($path) 0] { + switch -glob -- [lindex $file_states($path) 0] { AM - AD - MM - - UM - - U_ - + U? - _M - _D - _O { @@ -2437,7 +2437,8 @@ proc toggle_or_diff {w x y} { A_ - M_ - DD - - D_ { + DO - + DM { update_indexinfo \ "Removing [short_path $path] from commit" \ [list $path] \ -- cgit v1.2.3 From f18e40a1a60d506065b6cc0e45704ce29ea0a035 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 20 Nov 2006 21:27:22 -0500 Subject: git-gui: Improve handling of merge commits. Its useful to be able to amend the last commit even if it was a merge commit, so we really should support that in the gui. We now do so by making PARENT a list. We always diff against the first parent but we create a commit consisting of the parent(s) listed in this list, in order. We also should recheck the repository state during an amend. Earlier I was bitten by this exact bug when I switched branches through a command prompt and then did not do a rescan in git-gui. When I hit "Amend Last Commit" I was surprised to see information from the prior branch appear. This was due to git-gui caching the data from the last rescan and using that data form the amend data load request, rather than the data of the current branch. Improved error text in the dialogs used to tell the user why an amend is being refused by git-gui. In general this is only during an initial commit (nothing prior to amend) and during a merge commit (it is simply too confusing to amend the last commit while also trying to complete a merge). Fixed a couple of minor bugs in the pull logic. Since this code isn't really useful nobody has recently tested it and noticed the breakage. It really needs to be rewritten anyway. Signed-off-by: Shawn O. Pearce --- git-gui | 141 +++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 78 insertions(+), 63 deletions(-) diff --git a/git-gui b/git-gui index dcbb100feb..d5738baf10 100755 --- a/git-gui +++ b/git-gui @@ -231,25 +231,38 @@ proc unlock_index {} { ## ## status -proc repository_state {hdvar ctvar} { +proc repository_state {ctvar hdvar mhvar} { global gitdir - upvar $hdvar hd $ctvar ct + upvar $ctvar ct $hdvar hd $mhvar mh + + set mh [list] if {[catch {set hd [exec git rev-parse --verify HEAD]}]} { set hd {} set ct initial - } elseif {[file exists [file join $gitdir MERGE_HEAD]]} { + return + } + + set merge_head [file join $gitdir MERGE_HEAD] + if {[file exists $merge_head]} { set ct merge - } else { - set ct normal + set fd_mh [open $merge_head r] + while {[gets $fd_mh line] >= 0} { + lappend mh $line + } + close $fd_mh + return } + + set ct normal } proc PARENT {} { global PARENT empty_tree - if {$PARENT ne {}} { - return $PARENT + set p [lindex $PARENT 0] + if {$p ne {}} { + return $p } if {$empty_tree eq {}} { set empty_tree [exec git mktree << {}] @@ -258,21 +271,22 @@ proc PARENT {} { } proc rescan {after} { - global HEAD PARENT commit_type + global HEAD PARENT MERGE_HEAD commit_type global ui_index ui_other ui_status_value ui_comm global rescan_active file_states global repo_config if {$rescan_active > 0 || ![lock_index read]} return - repository_state new_HEAD new_type + repository_state newType newHEAD newMERGE_HEAD if {[string match amend* $commit_type] - && $new_type eq {normal} - && $new_HEAD eq $HEAD} { + && $newType eq {normal} + && $newHEAD eq $HEAD} { } else { - set HEAD $new_HEAD - set PARENT $new_HEAD - set commit_type $new_type + set HEAD $newHEAD + set PARENT $newHEAD + set MERGE_HEAD $newMERGE_HEAD + set commit_type $newType } array unset file_states @@ -686,23 +700,36 @@ proc read_diff {fd} { ## commit proc load_last_commit {} { - global HEAD PARENT commit_type ui_comm + global HEAD PARENT MERGE_HEAD commit_type ui_comm - if {[string match amend* $commit_type]} return - if {$commit_type ne {normal}} { - error_popup "Can't amend a $commit_type commit." + if {[llength $PARENT] == 0} { + error_popup {There is nothing 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}} { + error_popup {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. +} return } set msg {} - set parent {} - set parent_count 0 + set parents [list] if {[catch { - set fd [open "| git cat-file commit $HEAD" r] + set fd [open "| git cat-file commit $curHEAD" r] while {[gets $fd line] > 0} { if {[string match {parent *} $line]} { - set parent [string range $line 7 end] - incr parent_count + lappend parents [string range $line 7 end] } } set msg [string trim [read $fd]] @@ -712,17 +739,13 @@ proc load_last_commit {} { return } - if {$parent_count > 1} { - error_popup {Can't amend a merge commit.} - return - } - - if {$parent_count == 0} { - set commit_type amend-initial - set PARENT {} - } elseif {$parent_count == 1} { - set commit_type amend - set PARENT $parent + set HEAD $curHEAD + set PARENT $parents + set MERGE_HEAD [list] + switch -- [llength $parents] { + 0 {set commit_type amend-initial} + 1 {set commit_type amend} + default {set commit_type amend-merge} } $ui_comm delete 0.0 end @@ -770,11 +793,11 @@ proc commit_tree {} { # -- Our in memory state should match the repository. # - repository_state curHEAD cur_type + repository_state curType curHEAD curMERGE_HEAD if {[string match amend* $commit_type] - && $cur_type eq {normal} + && $curType eq {normal} && $curHEAD eq $HEAD} { - } elseif {$commit_type ne $cur_type || $HEAD ne $curHEAD} { + } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} { info_popup {Last scanned state does not match repository state. Another Git program has modified this repository @@ -920,7 +943,8 @@ proc commit_writetree {curHEAD msg} { } proc commit_committree {fd_wt curHEAD msg} { - global single_commit gitdir HEAD PARENT commit_type tcl_platform + global HEAD PARENT MERGE_HEAD commit_type + global single_commit gitdir tcl_platform global ui_status_value ui_comm selected_commit_type global file_states selected_paths rescan_active @@ -935,24 +959,12 @@ proc commit_committree {fd_wt curHEAD msg} { # -- Create the commit. # set cmd [list git commit-tree $tree_id] - if {$PARENT ne {}} { - lappend cmd -p $PARENT - } - if {$commit_type eq {merge}} { - if {[catch { - set fd_mh [open [file join $gitdir MERGE_HEAD] r] - while {[gets $fd_mh merge_head] >= 0} { - lappend cmd -p $merge_head - } - close $fd_mh - } err]} { - error_popup "Loading MERGE_HEAD failed:\n\n$err" - set ui_status_value {Commit failed.} - unlock_index - return + set parents [concat $PARENT $MERGE_HEAD] + if {[llength $parents] > 0} { + foreach p $parents { + lappend cmd -p $p } - } - if {$PARENT eq {}} { + } else { # git commit-tree writes to stderr during initial commit. lappend cmd 2>/dev/null } @@ -1020,10 +1032,11 @@ proc commit_committree {fd_wt curHEAD msg} { # -- Update in memory status # - set commit_type normal set selected_commit_type new + set commit_type normal set HEAD $cmt_id set PARENT $cmt_id + set MERGE_HEAD [list] foreach path [array names file_states] { set s $file_states($path) @@ -1081,8 +1094,8 @@ proc pull_remote {remote branch} { # -- Our in memory state should match the repository. # - repository_state curHEAD cur_type - if {$commit_type ne $cur_type || $HEAD ne $curHEAD} { + repository_state curType curHEAD curMERGE_HEAD + if {$commit_type ne $curType || $HEAD ne $curHEAD} { error_popup {Last scanned state does not match repository state. Its highly likely that another Git program modified the @@ -1120,18 +1133,18 @@ Commit or throw away all changes before starting a pull operation. } proc post_pull_remote {remote branch success} { - global HEAD PARENT commit_type selected_commit_type + global HEAD PARENT MERGE_HEAD commit_type selected_commit_type global ui_status_value unlock_index if {$success} { - repository_state HEAD commit_type + repository_state commit_type HEAD MERGE_HEAD set PARENT $HEAD set selected_commit_type new - set $ui_status_value "Pulling $branch from $remote complete." + set ui_status_value "Pulling $branch from $remote complete." } else { - set m "Conflicts detected while pulling $branch from $remote." - rescan "set ui_status_value {$m}" + rescan [list set ui_status_value \ + "Conflicts detected while pulling $branch from $remote."] } } @@ -2852,6 +2865,7 @@ proc trace_commit_type {varname args} { initial {set txt {Initial Commit Message:}} amend {set txt {Amended Commit Message:}} amend-initial {set txt {Amended Initial Commit Message:}} + amend-merge {set txt {Amended Merge Commit Message:}} merge {set txt {Merge Commit Message:}} * {set txt {Commit Message:}} } @@ -3146,6 +3160,7 @@ set file_lists($ui_other) [list] set HEAD {} set PARENT {} +set MERGE_HEAD [list] set commit_type {} set empty_tree {} set current_diff {} -- cgit v1.2.3 From 444f92d097425e8d1043a14571ebfd82c1c3b0a5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 20 Nov 2006 21:43:41 -0500 Subject: git-gui: Allow users to run fsck-objects from the gui. I recently found a need to run fsck-objects in a number of repositories that I also use git-gui against. Tossing in a menu option to invoke fsck-objects and have its output show up in a console window is simple enough to do. We probably need to enhance the console window used by fsck-objects, like to open up the Git fsck-objects manual page and let the user see what each message means (such as "dangling commit") and to also let the user invoke prune, to cleanup any such dangling objects. But right now I'm going to ignore that problem in favor of getting other more important features implemented. Signed-off-by: Shawn O. Pearce --- git-gui | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/git-gui b/git-gui index d5738baf10..2aa82781e9 100755 --- a/git-gui +++ b/git-gui @@ -1954,6 +1954,15 @@ proc do_repack {} { console_exec $w $cmd } +proc do_fsck_objects {} { + set w [new_console "verify" "Verifying the object database"] + set cmd [list git fsck-objects] + lappend cmd --full + lappend cmd --cache + lappend cmd --strict + console_exec $w $cmd +} + set is_quitting 0 proc do_quit {} { @@ -2610,6 +2619,10 @@ if {!$single_commit} { -command do_repack \ -font font_ui + .mbar.project add command -label {Verify Database} \ + -command do_fsck_objects \ + -font font_ui + if {$tcl_platform(platform) eq {windows}} { .mbar.project add command \ -label {Create Desktop Icon} \ -- cgit v1.2.3 From 21d7744fbc98b526bc00138fce287565df2c0075 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 20 Nov 2006 21:59:19 -0500 Subject: git-gui: Don't save amended commit message buffer. Because we don't automatically restart in amend mode when we quit while in amend mode the commit message buffer shouldn't be saved to GITGUI_MSG as it would be misleading when the user restarts the application. Signed-off-by: Shawn O. Pearce --- git-gui | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/git-gui b/git-gui index 2aa82781e9..d0176aca77 100755 --- a/git-gui +++ b/git-gui @@ -297,8 +297,8 @@ proc rescan {after} { } elseif {[load_message MERGE_MSG]} { } elseif {[load_message SQUASH_MSG]} { } - $ui_comm edit modified false $ui_comm edit reset + $ui_comm edit modified false } if {$repo_config(gui.trustmtime) eq {true}} { @@ -750,8 +750,8 @@ current merge activity. $ui_comm delete 0.0 end $ui_comm insert end $msg - $ui_comm edit modified false $ui_comm edit reset + $ui_comm edit modified false rescan {set ui_status_value {Ready.}} } @@ -760,8 +760,8 @@ proc create_new_commit {} { set commit_type normal $ui_comm delete 0.0 end - $ui_comm edit modified false $ui_comm edit reset + $ui_comm edit modified false rescan {set ui_status_value {Ready.}} } @@ -1025,8 +1025,8 @@ proc commit_committree {fd_wt curHEAD msg} { } $ui_comm delete 0.0 end - $ui_comm edit modified false $ui_comm edit reset + $ui_comm edit modified false if {$single_commit} do_quit @@ -1966,7 +1966,7 @@ proc do_fsck_objects {} { set is_quitting 0 proc do_quit {} { - global gitdir ui_comm is_quitting repo_config + global gitdir ui_comm is_quitting repo_config commit_type if {$is_quitting} return set is_quitting 1 @@ -1975,14 +1975,16 @@ proc do_quit {} { # set save [file join $gitdir GITGUI_MSG] set msg [string trim [$ui_comm get 0.0 end]] - if {[$ui_comm edit modified] && $msg ne {}} { + if {![string match amend* $commit_type] + && [$ui_comm edit modified] + && $msg ne {}} { catch { set fd [open $save w] puts $fd [string trim [$ui_comm get 0.0 end]] close $fd } - } elseif {$msg eq {} && [file exists $save]} { - file delete $save + } else { + catch {file delete $save} } # -- Stash our current window geometry into this repository. -- cgit v1.2.3 From 93a79912055dfc0cbdc974e98c705bc75950862a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 20 Nov 2006 22:17:15 -0500 Subject: git-gui: Reworded verify console title. It would be something of a disservice to our users if we refer to fsck-objects as "verify". So instead we call it fsck-objects in the console title, and indicate that's how we are verifying the object database. We probably should call our menu option "fsck-objects" or similar but I really do think that "Verify Database" more accurately describes the action then "fsck-objects" does, especially to users who aren't file system developers. Signed-off-by: Shawn O. Pearce --- git-gui | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index d0176aca77..7f75ffeaa0 100755 --- a/git-gui +++ b/git-gui @@ -1947,7 +1947,8 @@ proc do_gitk {} { } proc do_repack {} { - set w [new_console "repack" "Repacking the object database"] + set w [new_console {repack} \ + {Repacking the object database}] set cmd [list git repack] lappend cmd -a lappend cmd -d @@ -1955,7 +1956,8 @@ proc do_repack {} { } proc do_fsck_objects {} { - set w [new_console "verify" "Verifying the object database"] + set w [new_console {fsck-objects} \ + {Verifying the object database with fsck-objects}] set cmd [list git fsck-objects] lappend cmd --full lappend cmd --cache -- cgit v1.2.3 From 75e355d6bec2796ea0e69f4f4421670141ac77a2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 20 Nov 2006 22:22:10 -0500 Subject: git-gui: Seperate out the database operations in project menu. The project menu is just too cluttered without using separator entries to split out the database operations (such as repack and verify) from the other options in the same menu. Signed-off-by: Shawn O. Pearce --- git-gui | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/git-gui b/git-gui index 7f75ffeaa0..83ff5100ac 100755 --- a/git-gui +++ b/git-gui @@ -2619,6 +2619,8 @@ menu .mbar.project -command do_gitk \ -font font_ui if {!$single_commit} { + .mbar.project add separator + .mbar.project add command -label {Repack Database} \ -command do_repack \ -font font_ui @@ -2627,6 +2629,8 @@ if {!$single_commit} { -command do_fsck_objects \ -font font_ui + .mbar.project add separator + if {$tcl_platform(platform) eq {windows}} { .mbar.project add command \ -label {Create Desktop Icon} \ -- cgit v1.2.3 From a4abfa62d69ff92e3159ca0fd41184f5b72e16a4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 20 Nov 2006 23:01:47 -0500 Subject: git-gui: Rename Project menu to Repository. Since all of the actions in our Project menu actually apply to the Git concept of a repository, it is a disservice to our users to call it "project". This is especially true if Git ever gets any sort of subproject support, as the term would then most definately conflict. Signed-off-by: Shawn O. Pearce --- git-gui | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/git-gui b/git-gui index 83ff5100ac..e770717b4e 100755 --- a/git-gui +++ b/git-gui @@ -2602,7 +2602,7 @@ apply_config # -- Menu Bar # menu .mbar -tearoff 0 -.mbar add cascade -label Project -menu .mbar.project +.mbar add cascade -label Repository -menu .mbar.repository .mbar add cascade -label Edit -menu .mbar.edit .mbar add cascade -label Commit -menu .mbar.commit if {!$single_commit} { @@ -2612,38 +2612,38 @@ if {!$single_commit} { } . configure -menu .mbar -# -- Project Menu +# -- Repository Menu # -menu .mbar.project -.mbar.project add command -label Visualize \ +menu .mbar.repository +.mbar.repository add command -label Visualize \ -command do_gitk \ -font font_ui if {!$single_commit} { - .mbar.project add separator + .mbar.repository add separator - .mbar.project add command -label {Repack Database} \ + .mbar.repository add command -label {Repack Database} \ -command do_repack \ -font font_ui - .mbar.project add command -label {Verify Database} \ + .mbar.repository add command -label {Verify Database} \ -command do_fsck_objects \ -font font_ui - .mbar.project add separator + .mbar.repository add separator if {$tcl_platform(platform) eq {windows}} { - .mbar.project add command \ + .mbar.repository add command \ -label {Create Desktop Icon} \ -command do_windows_shortcut \ -font font_ui } elseif {[is_MacOSX]} { - .mbar.project add command \ + .mbar.repository add command \ -label {Create Desktop Icon} \ -command do_macosx_app \ -font font_ui } } -.mbar.project add command -label Quit \ +.mbar.repository add command -label Quit \ -command do_quit \ -accelerator $M1T-Q \ -font font_ui @@ -2729,7 +2729,7 @@ lappend disable_on_lock \ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Include All} \ +.mbar.commit add command -label {Include All In Commit} \ -command do_include_all \ -accelerator $M1T-I \ -font font_ui -- cgit v1.2.3 From 82aa23545f84f01e83fb2164b5751b41fd449b62 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 20 Nov 2006 23:55:51 -0500 Subject: git-gui: Added about dialog box. Created a help menu with an about dialog box. This about dialog shows the copyright notice for the application, the fact that it is covered by the GPL v2.0 or later, the authors, and the current version of Git it is invoking when users perform actions within it. Signed-off-by: Shawn O. Pearce --- git-gui | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/git-gui b/git-gui index e770717b4e..57120bd2b4 100755 --- a/git-gui +++ b/git-gui @@ -2159,6 +2159,55 @@ proc do_commit {} { commit_tree } +proc do_about {} { + global appname + + set w .about_dialog + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + label $w.header -text "About $appname" \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.close -text {Close} \ + -font font_ui \ + -command [list destroy $w] + pack $w.buttons.close -side right + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + label $w.desc \ + -text "$appname - a commit creation tool for Git. + +Copyright © 2006 Shawn Pearce, Paul Mackerras + +Use and redistribute under the terms of the +GNU General Public License, v. 2.0 or later." \ + -padx 5 -pady 5 \ + -justify left \ + -anchor w \ + -borderwidth 1 \ + -relief solid \ + -font font_ui + pack $w.desc -side top -fill x -padx 5 -pady 5 + + label $w.vers \ + -text [exec git --version] \ + -padx 5 -pady 5 \ + -justify left \ + -anchor w \ + -borderwidth 1 \ + -relief solid \ + -font font_ui + pack $w.vers -side top -fill x -padx 5 -pady 5 + + bind $w "grab $w; focus $w" + bind $w "destroy $w" + wm title $w "About $appname" + tkwait window $w +} + proc do_options {} { global appname gitdir font_descs global repo_config global_config @@ -2610,6 +2659,7 @@ if {!$single_commit} { .mbar add cascade -label Pull -menu .mbar.pull .mbar add cascade -label Push -menu .mbar.push } +.mbar add cascade -label Help -menu .mbar.help . configure -menu .mbar # -- Repository Menu @@ -2758,6 +2808,14 @@ if {!$single_commit} { menu .mbar.push } +# -- Help Menm +# +menu .mbar.help + +.mbar.help add command -label "About $appname" \ + -command do_about \ + -font font_ui + # -- Main Window Layout # panedwindow .vpane -orient vertical -- cgit v1.2.3 From 0c8d7839c9dafcfd33dae2bd35b9bf4a98ffa07a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 02:33:56 -0500 Subject: git-gui: Be more Macintosh like. It is tradition for applications to store their about and preferences menu options within the application menu. This is the first menu in the menu bar, just after the apple menu. Apparently the way to access this menu from Tk on Mac OS X systems is to create a special menu whose name ends in ".apple" and place it into the menu bar. So now if we are on Mac OS X we move our about menu and our options menu into the application menu, like other Mac OS X applications. Signed-off-by: Shawn O. Pearce --- git-gui | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/git-gui b/git-gui index 57120bd2b4..4b07612f58 100755 --- a/git-gui +++ b/git-gui @@ -2659,7 +2659,6 @@ if {!$single_commit} { .mbar add cascade -label Pull -menu .mbar.pull .mbar add cascade -label Push -menu .mbar.push } -.mbar add cascade -label Help -menu .mbar.help . configure -menu .mbar # -- Repository Menu @@ -2731,10 +2730,6 @@ menu .mbar.edit -command {catch {[focus] tag add sel 0.0 end}} \ -accelerator $M1T-A \ -font font_ui -.mbar.edit add separator -.mbar.edit add command -label {Options...} \ - -command do_options \ - -font font_ui # -- Commit Menu # @@ -2808,13 +2803,36 @@ if {!$single_commit} { menu .mbar.push } -# -- Help Menm -# -menu .mbar.help +if {[is_MacOSX]} { + # -- Apple Menu (Mac OS X only) + # + .mbar add cascade -label Apple -menu .mbar.apple + menu .mbar.apple + + .mbar.apple add command -label "About $appname" \ + -command do_about \ + -font font_ui + .mbar.apple add command -label "$appname Options..." \ + -command do_options \ + -font font_ui +} else { + # -- Edit Menu + # + .mbar.edit add separator + .mbar.edit add command -label {Options...} \ + -command do_options \ + -font font_ui + + # -- Help Menu + # + .mbar add cascade -label Help -menu .mbar.help + menu .mbar.help + + .mbar.help add command -label "About $appname" \ + -command do_about \ + -font font_ui +} -.mbar.help add command -label "About $appname" \ - -command do_about \ - -font font_ui # -- Main Window Layout # -- cgit v1.2.3 From bdc9ea202407114737d1d58f7bef00b9579df9b7 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 02:36:55 -0500 Subject: git-gui: Make the copyright notice serve double duty. The copyright notice we display in the about dialog should be the same as the one at the top of our source code. By putting the copyright notice that appears at the top of our source code into a global variable rather than a comment we can trivially make them the same at all times. Signed-off-by: Shawn O. Pearce --- git-gui | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/git-gui b/git-gui index 4b07612f58..b28786657d 100755 --- a/git-gui +++ b/git-gui @@ -2,10 +2,15 @@ # Tcl ignores the next line -*- tcl -*- \ exec wish "$0" -- "$@" -# Copyright (C) 2006 Shawn Pearce, Paul Mackerras. All rights reserved. -# This program is free software; it may be used, copied, modified -# and distributed under the terms of the GNU General Public Licence, -# either version 2, or (at your option) any later version. +set copyright { +Copyright © 2006 Shawn Pearce, Paul Mackerras. + +All rights reserved. + +This program is free software; it may be used, copied, modified +and distributed under the terms of the GNU General Public Licence, +either version 2, or (at your option) any later version. +} set appname [lindex [file split $argv0] end] set gitdir {} @@ -2160,7 +2165,7 @@ proc do_commit {} { } proc do_about {} { - global appname + global appname copyright set w .about_dialog toplevel $w @@ -2179,11 +2184,7 @@ proc do_about {} { label $w.desc \ -text "$appname - a commit creation tool for Git. - -Copyright © 2006 Shawn Pearce, Paul Mackerras - -Use and redistribute under the terms of the -GNU General Public License, v. 2.0 or later." \ +$copyright" \ -padx 5 -pady 5 \ -justify left \ -anchor w \ -- cgit v1.2.3 From 53f7a33bdc89a6ec870fdce5221c05a2759974d3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 02:46:51 -0500 Subject: git-gui: Include the Tcl/Tk version in the about dialog. Users may need to know what version of Tcl they are running git-gui under, in case there is an interesting interface quirk or other compatability problem we don't know about right now that we may need to explore (and maybe fix). Since its simple enough to show a line with this version data we should do so. We also try to reduce the amount of text shown as often the Tcl and Tk version numbers will be identical; when this happens we should only show the one version number. Signed-off-by: Shawn O. Pearce --- git-gui | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index b28786657d..dfdce94cf6 100755 --- a/git-gui +++ b/git-gui @@ -9,8 +9,7 @@ All rights reserved. This program is free software; it may be used, copied, modified and distributed under the terms of the GNU General Public Licence, -either version 2, or (at your option) any later version. -} +either version 2, or (at your option) any later version.} set appname [lindex [file split $argv0] end] set gitdir {} @@ -2166,6 +2165,7 @@ proc do_commit {} { proc do_about {} { global appname copyright + global tcl_patchLevel tk_patchLevel set w .about_dialog toplevel $w @@ -2193,8 +2193,17 @@ $copyright" \ -font font_ui pack $w.desc -side top -fill x -padx 5 -pady 5 + set v [exec git --version] + append v "\n\n" + if {$tcl_patchLevel eq $tk_patchLevel} { + append v "Tcl/Tk version $tcl_patchLevel" + } else { + append v "Tcl version $tcl_patchLevel" + append v ", Tk version $tk_patchLevel" + } + label $w.vers \ - -text [exec git --version] \ + -text $v \ -padx 5 -pady 5 \ -justify left \ -anchor w \ -- cgit v1.2.3 From 7b85a17b86062c87ceebbf70acfbd62e00cd4bac Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 11:57:41 -0500 Subject: git-gui: Abstract out windows platform test to is_Windows proc. Like the is_MacOSX proc we shouldn't keep repeating the platform test for Windows. Instead abstract the code out into a procedure and use the procedure whenever we need to do something special. Signed-off-by: Shawn O. Pearce --- git-gui | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/git-gui b/git-gui index dfdce94cf6..3e53fbd623 100755 --- a/git-gui +++ b/git-gui @@ -891,14 +891,14 @@ A good commit message has the following format: } proc commit_prehook {curHEAD msg} { - global tcl_platform gitdir ui_status_value pch_error + global gitdir ui_status_value pch_error + + set pchook [file join $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. - - set pchook [file join $gitdir hooks pre-commit] - if {$tcl_platform(platform) eq {windows} - && [file isfile $pchook]} { + # + if {[is_Windows] && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ "then exec \"$pchook\" 2>&1;" \ @@ -948,7 +948,7 @@ proc commit_writetree {curHEAD msg} { proc commit_committree {fd_wt curHEAD msg} { global HEAD PARENT MERGE_HEAD commit_type - global single_commit gitdir tcl_platform + global single_commit gitdir global ui_status_value ui_comm selected_commit_type global file_states selected_paths rescan_active @@ -1016,7 +1016,7 @@ proc commit_committree {fd_wt curHEAD msg} { # -- Run the post-commit hook. # set pchook [file join $gitdir hooks post-commit] - if {$tcl_platform(platform) eq {windows} && [file isfile $pchook]} { + if {[is_Windows] && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ "then exec \"$pchook\";" \ @@ -1724,6 +1724,14 @@ proc is_MacOSX {} { return 0 } +proc is_Windows {} { + global tcl_platform + if {$tcl_platform(platform) eq {windows}} { + return 1 + } + return 0 +} + proc bind_button3 {w cmd} { bind $w $cmd if {[is_MacOSX]} { @@ -1854,12 +1862,10 @@ proc console_init {w} { } proc console_exec {w cmd {after {}}} { - global tcl_platform - # -- Windows tosses the enviroment when we exec our child. # But most users need that so we have to relogin. :-( # - if {$tcl_platform(platform) eq {windows}} { + if {[is_Windows]} { set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"] } @@ -1934,7 +1940,7 @@ proc console_read {w fd after} { set starting_gitk_msg {Please wait... Starting gitk...} proc do_gitk {} { - global tcl_platform ui_status_value starting_gitk_msg + global ui_status_value starting_gitk_msg set ui_status_value $starting_gitk_msg after 10000 { @@ -1943,7 +1949,7 @@ proc do_gitk {} { } } - if {$tcl_platform(platform) eq {windows}} { + if {[is_Windows]} { exec sh -c gitk & } else { exec gitk & @@ -2613,7 +2619,7 @@ font create font_diffbold set M1B M1 set M1T M1 -if {$tcl_platform(platform) eq {windows}} { +if {[is_Windows]} { set M1B Control set M1T Ctrl } elseif {[is_MacOSX]} { @@ -2690,7 +2696,7 @@ if {!$single_commit} { .mbar.repository add separator - if {$tcl_platform(platform) eq {windows}} { + if {[is_Windows]} { .mbar.repository add command \ -label {Create Desktop Icon} \ -command do_windows_shortcut \ -- cgit v1.2.3 From 3add5d3517972f6407da450fcd7dff08187cca34 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 12:00:50 -0500 Subject: git-gui: Correct is_MacOSX platform test. Darwn based UNIX systems are not necessarily Mac OS X. However the only windowing system used by Tk that is Mac OS X is 'aqua', and only 'aqua' exists on Mac OS X. Therefore this is a more reliable test for the Macintosh platform. Signed-off-by: Shawn O. Pearce --- git-gui | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/git-gui b/git-gui index 3e53fbd623..afd9ac026e 100755 --- a/git-gui +++ b/git-gui @@ -1716,9 +1716,7 @@ unset filemask i proc is_MacOSX {} { global tcl_platform tk_library - if {$tcl_platform(platform) eq {unix} - && $tcl_platform(os) eq {Darwin} - && [string match /Library/Frameworks/* $tk_library]} { + if {[tk windowingsystem] eq {aqua}} { return 1 } return 0 -- cgit v1.2.3 From 1d8b3cbf2841612791178853efbfe9ba60b48f2b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 15:28:14 -0500 Subject: git-gui: Warn Cygwin users about possible environment issues. Because the Tcl binary distributed with Cygwin tends to not pass along its own environment (the env array) to its children, its unlikely that any Git commands spawned by git-gui will receive the same environment variables that git-gui itself received from the shell which started it. If the user is counting on environment variables to pass down, like say GIT_INDEX_FILE, they may not, so we warn them during git-gui startup that things may not work out as the user intended. Perhaps one day when git-gui and git are running on native Windows (rather than through the Cygwin emulation layers) things will work better. Signed-off-by: Shawn O. Pearce --- git-gui | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index afd9ac026e..5253141875 100755 --- a/git-gui +++ b/git-gui @@ -145,6 +145,28 @@ proc error_popup {msg} { eval $cmd } +proc warn_popup {msg} { + global gitdir appname + + set title $appname + if {$gitdir ne {}} { + append title { (} + append title [lindex \ + [file split [file normalize [file dirname $gitdir]]] \ + end] + append title {)} + } + set cmd [list tk_messageBox \ + -icon warning \ + -type ok \ + -title "$title: warning" \ + -message $msg] + if {[winfo ismapped .]} { + lappend cmd -parent . + } + eval $cmd +} + proc info_popup {msg} { global gitdir appname @@ -158,7 +180,7 @@ proc info_popup {msg} { } tk_messageBox \ -parent . \ - -icon error \ + -icon info \ -type ok \ -title $title \ -message $msg @@ -3279,6 +3301,64 @@ set selected_commit_type new wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm + +# -- Warn the user about environmental problems. +# Cygwin's Tcl does *not* pass its env array +# onto any processes it spawns. This means +# that the git processes get none of our +# environment. That may not work... +# +if {[is_Windows]} { + set ignored_env 0 + set suggest_user {} + set msg "Possible environment issues exist. + +The following environment variables are probably +going to be ignored by any Git subprocess run +by $appname: + +" + foreach name [array names env] { + switch -regexp -- $name { + {^GIT_INDEX_FILE$} - + {^GIT_OBJECT_DIRECTORY$} - + {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} - + {^GIT_DIFF_OPTS$} - + {^GIT_EXTERNAL_DIFF$} - + {^GIT_PAGER$} - + {^GIT_TRACE$} - + {^GIT_CONFIG$} - + {^GIT_CONFIG_LOCAL$} - + {^GIT_(AUTHOR|COMMITTER)_DATE$} { + append msg " - $name\n" + incr ignored_env + } + {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} { + append msg " - $name\n" + incr ignored_env + set suggest_user $name + } + } + } + if {$ignored_env > 0} { + append msg " +This is due to a known issue with the +Tcl binary distributed by Cygwin." + + if {$suggest_user ne {}} { + append msg " + +A good replacement for $suggest_user +is placing values for the user.name and +user.email settings into your personal +~/.gitconfig file. +" + } + warn_popup $msg + } + unset ignored_env msg suggest_user name +} + if {!$single_commit} { load_all_remotes populate_fetch_menu .mbar.fetch -- cgit v1.2.3 From 1d72cb6c1833ec0b5065e33cfc7b5a55ee6b3d8e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 15:38:49 -0500 Subject: git-gui: Added configuration editor TODO list. Signed-off-by: Shawn O. Pearce --- TODO | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index d27227c376..b8fa807938 100644 --- a/TODO +++ b/TODO @@ -29,4 +29,11 @@ Items outstanding: * Show a shortlog of the last couple of commits in the main window, to give the user warm fuzzy feelings that we have their data - saved. + saved. Actually this may be the set of commits not yet in + the upstream (aka default merge branch remote repository). + + * GUI configuration editor for options listed in + git.git/Documentation/config.txt. Ideally this would + parse that file and generate the options dialog from + the documentation itself, and include the help text + from the documentation as part of the UI somehow. -- cgit v1.2.3 From b673bbc59cf6950808d12b0d4020ae606019a8cd Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 20:21:11 -0500 Subject: git-gui: Refactor M1 binding selection. Signed-off-by: Shawn O. Pearce --- git-gui | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index 5253141875..13b40823ee 100755 --- a/git-gui +++ b/git-gui @@ -2637,14 +2637,15 @@ catch { font create font_uibold font create font_diffbold -set M1B M1 -set M1T M1 if {[is_Windows]} { set M1B Control set M1T Ctrl } elseif {[is_MacOSX]} { set M1B M1 set M1T Cmd +} else { + set M1B M1 + set M1T M1 } proc apply_config {} { -- cgit v1.2.3 From d075242923691e5f3c671f240945b0bdf26a7cd0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 20:33:09 -0500 Subject: git-gui: Added menu command to visualize all branches. Sometimes its useful to start gitk with the --all option, to view all of the known branches and tags within this repository. Rather than making the user startup gitk and then edit the view we can pass the option along for them. This also makes it slightly more explicit, that when gitk starts up by default its showing the current branch and not everything. Yes gitk isn't showing that to the user, but the fact that the user had to make a decision between seeing this current branch or all branches will hopefully make them study gitk's display before jumping to a conclusion. Signed-off-by: Shawn O. Pearce --- git-gui | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/git-gui b/git-gui index 13b40823ee..ef8c7cf065 100755 --- a/git-gui +++ b/git-gui @@ -1959,20 +1959,28 @@ proc console_read {w fd after} { set starting_gitk_msg {Please wait... Starting gitk...} -proc do_gitk {} { +proc do_gitk {revs} { global ui_status_value starting_gitk_msg - set ui_status_value $starting_gitk_msg - after 10000 { - if {$ui_status_value eq $starting_gitk_msg} { - set ui_status_value {Ready.} - } + set cmd gitk + if {$revs ne {}} { + append cmd { } + append cmd $revs } - if {[is_Windows]} { - exec sh -c gitk & + set cmd "sh -c \"exec $cmd\"" + } + append cmd { &} + + if {[catch {eval exec $cmd} err]} { + error_popup "Failed to start gitk:\n\n$err" } else { - exec gitk & + set ui_status_value $starting_gitk_msg + after 10000 { + if {$ui_status_value eq $starting_gitk_msg} { + set ui_status_value {Ready.} + } + } } } @@ -2701,12 +2709,17 @@ if {!$single_commit} { # -- Repository Menu # menu .mbar.repository -.mbar.repository add command -label Visualize \ - -command do_gitk \ +.mbar.repository add command \ + -label {Visualize Current Branch} \ + -command {do_gitk {}} \ -font font_ui -if {!$single_commit} { - .mbar.repository add separator +.mbar.repository add command \ + -label {Visualize All Branches} \ + -command {do_gitk {--all}} \ + -font font_ui +.mbar.repository add separator +if {!$single_commit} { .mbar.repository add command -label {Repack Database} \ -command do_repack \ -font font_ui -- cgit v1.2.3 From 5040f926f9b06946b6b6144eb358db1850dce505 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Nov 2006 22:58:28 -0500 Subject: git-gui: Don't start 'gitk --all' on Mac OS X. Since gitk is currently broken on Mac OS X and is unable to start itself when given command line parameters just don't offer the "Visual All Branches" menu option on Mac OS X. Once this feature of gitk is fixed we should change this section of code to make sure a working version of gitk will be executed before we offer the option up to the user. Signed-off-by: Shawn O. Pearce --- git-gui | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/git-gui b/git-gui index ef8c7cf065..d1cc0ec248 100755 --- a/git-gui +++ b/git-gui @@ -2713,10 +2713,12 @@ menu .mbar.repository -label {Visualize Current Branch} \ -command {do_gitk {}} \ -font font_ui -.mbar.repository add command \ - -label {Visualize All Branches} \ - -command {do_gitk {--all}} \ - -font font_ui +if {![is_MacOSX]} { + .mbar.repository add command \ + -label {Visualize All Branches} \ + -command {do_gitk {--all}} \ + -font font_ui +} .mbar.repository add separator if {!$single_commit} { -- cgit v1.2.3 From 4c2035d55e0a5c013677a8e83193e37d51000793 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 22 Nov 2006 19:24:41 -0500 Subject: git-gui: Improve pull error dialogs. Just like prior to a commit its only an informational message that we refuse to perform a pull on a dirty working directory. Therefore we should not use an error icon. Signed-off-by: Shawn O. Pearce --- git-gui | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/git-gui b/git-gui index d1cc0ec248..8246037fac 100755 --- a/git-gui +++ b/git-gui @@ -1122,11 +1122,13 @@ proc pull_remote {remote branch} { # repository_state curType curHEAD curMERGE_HEAD if {$commit_type ne $curType || $HEAD ne $curHEAD} { - error_popup {Last scanned state does not match repository state. + info_popup {Last scanned state does not match repository state. -Its highly likely that another Git program modified the -repository since our last scan. A rescan is required -before a pull can be started. +Another Git program has modified this repository +since the last scan. A rescan must be performed +before a pull operation can be started. + +The rescan will be automatically started now. } unlock_index rescan {set ui_status_value {Ready.}} @@ -1138,10 +1140,12 @@ before a pull can be started. if {[array size file_states] != 0} { error_popup {Uncommitted but modified files are present. -You should not perform a pull with unmodified files in your working -directory as Git would be unable to recover from an incorrect merge. +You should not perform a pull with unmodified +files in your working directory as Git will be +unable to recover from an incorrect merge. -Commit or throw away all changes before starting a pull operation. +You should commit or revert all changes before +starting a pull operation. } unlock_index return -- cgit v1.2.3 From e734817db0af028f2aea356b44c74b7ac51023cd Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 23 Nov 2006 21:40:45 -0500 Subject: git-gui: Added revert changes command. Users sometimes need to be able to throw away locally modified files in order to go back to the last committed version of that file. To perform a revert the user must first uninclude each file from the new commit as the working file must at least partially match the index, and we use git-checkout-index to update the working directory. Signed-off-by: Shawn O. Pearce --- git-gui | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 177 insertions(+), 7 deletions(-) diff --git a/git-gui b/git-gui index 8246037fac..34a1daa44d 100755 --- a/git-gui +++ b/git-gui @@ -1277,9 +1277,26 @@ proc display_file {path state} { set old_w [mapcol $old_m $path] set new_icon [mapicon $new_m $path] + if {$new_m eq {__}} { + set lno [lsearch -sorted $file_lists($old_w) $path] + if {$lno >= 0} { + set file_lists($old_w) \ + [lreplace $file_lists($old_w) $lno $lno] + incr lno + $old_w conf -state normal + $old_w delete $lno.0 [expr {$lno + 1}].0 + $old_w conf -state disabled + } + unset file_states($path) + catch {unset selected_paths($path)} + return + } + if {$new_w ne $old_w} { set lno [lsearch -sorted $file_lists($old_w) $path] if {$lno >= 0} { + set file_lists($old_w) \ + [lreplace $file_lists($old_w) $lno $lno] incr lno $old_w conf -state normal $old_w delete $lno.0 [expr {$lno + 1}].0 @@ -1500,6 +1517,84 @@ proc write_update_index {fd pathList totalCnt batch msg after} { [expr {100.0 * $update_index_cp / $totalCnt}]] } +proc checkout_index {msg pathList after} { + global update_index_cp ui_status_value + + if {![lock_index update]} return + + set update_index_cp 0 + set pathList [lsort $pathList] + set totalCnt [llength $pathList] + set batch [expr {int($totalCnt * .01) + 1}] + if {$batch > 25} {set batch 25} + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + 0.0] + set cmd [list git checkout-index] + lappend cmd --index + lappend cmd --quiet + lappend cmd --force + lappend cmd -z + lappend cmd --stdin + set fd [open "| $cmd " w] + fconfigure $fd \ + -blocking 0 \ + -buffering full \ + -buffersize 512 \ + -translation binary + fileevent $fd writable [list \ + write_checkout_index \ + $fd \ + $pathList \ + $totalCnt \ + $batch \ + $msg \ + $after \ + ] +} + +proc write_checkout_index {fd pathList totalCnt batch msg after} { + global update_index_cp ui_status_value + global file_states current_diff + + if {$update_index_cp >= $totalCnt} { + close $fd + unlock_index + uplevel #0 $after + return + } + + for {set i $batch} \ + {$update_index_cp < $totalCnt && $i > 0} \ + {incr i -1} { + set path [lindex $pathList $update_index_cp] + incr update_index_cp + + switch -glob -- [lindex $file_states($path) 0] { + AM - + AD {set new A_} + MM - + MD {set new M_} + _M - + _D {set new __} + ?? {continue} + } + + puts -nonewline $fd $path + puts -nonewline $fd "\0" + display_file $path $new + } + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + [expr {100.0 * $update_index_cp / $totalCnt}]] +} + ###################################################################### ## ## remote management @@ -1708,11 +1803,12 @@ foreach i { {_M i mod "Modified"} {M_ i fulltick "Included in commit"} {MM i parttick "Partially included"} + {MD i question "Included (but gone)"} {_O o plain "Untracked"} {A_ o fulltick "Added by commit"} {AM o parttick "Partially added"} - {AD o question "Added (but now gone)"} + {AD o question "Added (but gone)"} {_D i question "Missing"} {DD i removed "Removed by commit"} @@ -2159,6 +2255,74 @@ proc do_include_all {} { $paths } +proc revert_helper {txt paths} { + global file_states current_diff + + if {![lock_index begin-update]} return + + set pathList [list] + set after {} + foreach path $paths { + switch -glob -- [lindex $file_states($path) 0] { + AM - + AD - + MM - + MD - + _M - + _D { + lappend pathList $path + if {$path eq $current_diff} { + set after {reshow_diff;} + } + } + } + } + + set n [llength $pathList] + if {$n == 0} { + unlock_index + return + } elseif {$n == 1} { + set s "[short_path [lindex $pathList]]" + } else { + set s "these $n files" + } + + set reply [tk_dialog \ + .confirm_revert \ + "title" \ + "Revert unincluded changes in $s? + +Any unincluded changes will be permanently lost by the revert." \ + questhead \ + 1 \ + {Do Nothing} \ + {Revert Changes} \ + ] + if {$reply == 1} { + checkout_index \ + $txt \ + $pathList \ + [concat $after {set ui_status_value {Ready.}}] + } else { + unlock_index + } +} + +proc do_revert_selection {} { + global current_diff selected_paths + + if {[array size selected_paths] > 0} { + revert_helper \ + {Reverting selected files} \ + [array names selected_paths] + } elseif {$current_diff ne {}} { + revert_helper \ + "Reverting [short_path $current_diff]" \ + [list $current_diff] + } +} + proc do_signoff {} { global ui_comm @@ -2818,12 +2982,6 @@ lappend disable_on_lock \ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Remove From Commit} \ - -command do_remove_selection \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - .mbar.commit add command -label {Include In Commit} \ -command do_include_selection \ -font font_ui @@ -2837,6 +2995,18 @@ lappend disable_on_lock \ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] +.mbar.commit add command -label {Remove From Commit} \ + -command do_remove_selection \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add command -label {Revert Changes} \ + -command do_revert_selection \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add separator .mbar.commit add command -label {Sign Off} \ -- cgit v1.2.3 From 8553b772d7aeebe8c83710233877483dd409b846 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 24 Nov 2006 15:38:18 -0500 Subject: git-gui: Display the current branch. Users want to know what branch they are sitting on before making a commit, as they may need to switch to a different branch first. Signed-off-by: Shawn O. Pearce --- git-gui | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 34a1daa44d..dd2d750ab1 100755 --- a/git-gui +++ b/git-gui @@ -258,11 +258,20 @@ proc unlock_index {} { ## status proc repository_state {ctvar hdvar mhvar} { - global gitdir + global gitdir current_branch upvar $ctvar ct $hdvar hd $mhvar mh set mh [list] + if {[catch {set current_branch [exec git symbolic-ref HEAD]}]} { + set current_branch {} + } else { + regsub ^refs/(heads|tags)/ \ + $current_branch \ + {} \ + current_branch + } + if {[catch {set hd [exec git rev-parse --verify HEAD]}]} { set hd {} set ct initial @@ -3060,6 +3069,25 @@ if {[is_MacOSX]} { } +# -- Branch Control +# +frame .branch \ + -borderwidth 1 \ + -relief sunken +label .branch.l1 \ + -text {Current Branch:} \ + -anchor w \ + -justify left \ + -font font_ui +label .branch.cb \ + -textvariable current_branch \ + -anchor w \ + -justify left \ + -font font_ui +pack .branch.l1 -side left +pack .branch.cb -side left -fill x +pack .branch -side top -fill x + # -- Main Window Layout # panedwindow .vpane -orient vertical @@ -3486,6 +3514,7 @@ set PARENT {} set MERGE_HEAD [list] set commit_type {} set empty_tree {} +set current_branch {} set current_diff {} set selected_commit_type new -- cgit v1.2.3 From 9342e26d3a832dd24878725b42444b8efe4fa1c4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 24 Nov 2006 15:59:34 -0500 Subject: git-gui: Support file state MD (modified/deleted). Apparently I missed the file state MD, which is a file modified and updated in the index but then removed from the working directory. This should be treated just like AD, an added file which has been deleted from the working directory. Signed-off-by: Shawn O. Pearce --- git-gui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-gui b/git-gui index dd2d750ab1..e9d4600a25 100755 --- a/git-gui +++ b/git-gui @@ -1093,6 +1093,7 @@ proc commit_committree {fd_wt curHEAD msg} { AM - AD - MM - + MD - DM { set file_states($path) [list \ _[string index $m 1] \ @@ -2211,6 +2212,7 @@ proc include_helper {txt paths} { AM - AD - MM - + MD - U? - _M - _D - @@ -2255,6 +2257,7 @@ proc do_include_all {} { AM - AD - MM - + MD - _M - _D {lappend paths $path} } -- cgit v1.2.3 From 700a65ce380f29a5083bcc230aa1ef5c28e66f2c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 24 Nov 2006 17:30:12 -0500 Subject: git-gui: Created Branch menu. This is an early start at branch management from within git-gui. The branch menu has create/delete command entries to create and delete branches as well as a list of radiobutton entries for each branch found in the repository through for-each-ref. Signed-off-by: Shawn O. Pearce --- git-gui | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/git-gui b/git-gui index e9d4600a25..69ebd90958 100755 --- a/git-gui +++ b/git-gui @@ -1605,6 +1605,44 @@ proc write_checkout_index {fd pathList totalCnt batch msg after} { [expr {100.0 * $update_index_cp / $totalCnt}]] } +###################################################################### +## +## branch management + +proc load_all_branches {} { + global all_branches + + set all_branches [list] + set cmd [list git for-each-ref] + lappend cmd --format=%(refname) + lappend cmd refs/heads + set fd [open "| $cmd" r] + while {[gets $fd line] > 0} { + if {[regsub ^refs/heads/ $line {} line]} { + lappend all_branches $line + } + } + close $fd + + set all_branches [lsort $all_branches] +} + +proc populate_branch_menu {m} { + global all_branches disable_on_lock + + $m add separator + foreach b $all_branches { + $m add radiobutton \ + -label $b \ + -command [list do_switch_branch $b] \ + -variable current_branch \ + -value $b \ + -font font_ui + lappend disable_on_lock \ + [list $m entryconf [$m index last] -state] + } +} + ###################################################################### ## ## remote management @@ -2878,6 +2916,9 @@ apply_config menu .mbar -tearoff 0 .mbar add cascade -label Repository -menu .mbar.repository .mbar add cascade -label Edit -menu .mbar.edit +if {!$single_commit} { + .mbar add cascade -label Branch -menu .mbar.branch +} .mbar add cascade -label Commit -menu .mbar.commit if {!$single_commit} { .mbar add cascade -label Fetch -menu .mbar.fetch @@ -2963,6 +3004,24 @@ menu .mbar.edit -accelerator $M1T-A \ -font font_ui +if {!$single_commit} { + # -- Branch Menu + # + menu .mbar.branch + + .mbar.branch add command -label {Create...} \ + -command do_create_branch \ + -font font_ui + lappend disable_on_lock [list .mbar.branch entryconf \ + [.mbar.branch index last] -state] + + .mbar.branch add command -label {Delete...} \ + -command do_delete_branch \ + -font font_ui + lappend disable_on_lock [list .mbar.branch entryconf \ + [.mbar.branch index last] -state] +} + # -- Commit Menu # menu .mbar.commit @@ -3583,6 +3642,8 @@ user.email settings into your personal if {!$single_commit} { load_all_remotes + load_all_branches + populate_branch_menu .mbar.branch populate_fetch_menu .mbar.fetch populate_pull_menu .mbar.pull populate_push_menu .mbar.push -- cgit v1.2.3 From d90d83a3a95e5fb4672906589ac0a19c19f1187b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 25 Nov 2006 02:45:19 -0500 Subject: git-gui: Parse off refs/remotes when showing current branch. Even though the user shouldn't have a remote branch checked out, if they do we should still show as short of the branch name as possible. Signed-off-by: Shawn O. Pearce --- git-gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 69ebd90958..48e781dd30 100755 --- a/git-gui +++ b/git-gui @@ -266,7 +266,7 @@ proc repository_state {ctvar hdvar mhvar} { if {[catch {set current_branch [exec git symbolic-ref HEAD]}]} { set current_branch {} } else { - regsub ^refs/(heads|tags)/ \ + regsub ^refs/((heads|tags|remotes)/)? \ $current_branch \ {} \ current_branch -- cgit v1.2.3 From 2171bf4b44884fd75bc5c1c412a39c2d4e645453 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 25 Nov 2006 02:47:18 -0500 Subject: git-gui: Abort on not implemented branch switching. I'm not currently ready to implement branch switching, so I'm just going to punt on it for now. :-) Signed-off-by: Shawn O. Pearce --- git-gui | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 48e781dd30..9bfed1f6a9 100755 --- a/git-gui +++ b/git-gui @@ -1634,7 +1634,7 @@ proc populate_branch_menu {m} { foreach b $all_branches { $m add radiobutton \ -label $b \ - -command [list do_switch_branch $b] \ + -command [list switch_branch $b] \ -variable current_branch \ -value $b \ -font font_ui @@ -1643,6 +1643,10 @@ proc populate_branch_menu {m} { } } +proc switch_branch {b} { + error "NOT IMPLEMENTED" +} + ###################################################################### ## ## remote management -- cgit v1.2.3 From 359ca42a4b9288421d2f0409652f76e9a365b801 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 25 Nov 2006 03:33:03 -0500 Subject: git-gui: Automatically skip tracking branches in branch menu. Since the user should not work on a tracking branch we automatically hide any branch which is used as a tracking branch by either a remote..fetch config entry or by a Pull: line in a remotes file. Signed-off-by: Shawn O. Pearce --- git-gui | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/git-gui b/git-gui index 9bfed1f6a9..7406238c90 100755 --- a/git-gui +++ b/git-gui @@ -1610,7 +1610,7 @@ proc write_checkout_index {fd pathList totalCnt batch msg after} { ## branch management proc load_all_branches {} { - global all_branches + global all_branches tracking_branches set all_branches [list] set cmd [list git for-each-ref] @@ -1618,9 +1618,9 @@ proc load_all_branches {} { lappend cmd refs/heads set fd [open "| $cmd" r] while {[gets $fd line] > 0} { - if {[regsub ^refs/heads/ $line {} line]} { - lappend all_branches $line - } + if {![catch {set info $tracking_branches($line)}]} continue + if {![regsub ^refs/heads/ $line {} name]} continue + lappend all_branches $name } close $fd @@ -1652,21 +1652,49 @@ proc switch_branch {b} { ## remote management proc load_all_remotes {} { - global gitdir all_remotes repo_config + global gitdir repo_config + global all_remotes tracking_branches set all_remotes [list] + array unset tracking_branches + set rm_dir [file join $gitdir remotes] if {[file isdirectory $rm_dir]} { - set all_remotes [concat $all_remotes [glob \ + set all_remotes [glob \ -types f \ -tails \ -nocomplain \ - -directory $rm_dir *]] + -directory $rm_dir *] + + foreach name $all_remotes { + catch { + set fd [open [file join $rm_dir $name] r] + while {[gets $fd line] >= 0} { + if {![regexp {^Pull:[ ]*([^:]+):(.+)$} \ + $line line src dst]} continue + if {![regexp ^refs/ $dst]} { + set dst "refs/heads/$dst" + } + set tracking_branches($dst) [list $name $src] + } + close $fd + } + } } foreach line [array names repo_config remote.*.url] { - if {[regexp ^remote\.(.*)\.url\$ $line line name]} { - lappend all_remotes $name + if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue + lappend all_remotes $name + + if {[catch {set fl $repo_config(remote.$name.fetch)}]} { + set fl {} + } + foreach line $fl { + if {![regexp {^([^:]+):(.+)$} $line line src dst]} continue + if {![regexp ^refs/ $dst]} { + set dst "refs/heads/$dst" + } + set tracking_branches($dst) [list $name $src] } } -- cgit v1.2.3 From bb1ad51a5365cbe48d5dcee7e00a8d9f90d89171 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 25 Nov 2006 03:35:33 -0500 Subject: git-gui: Rename all_branches -> all_heads. Since this list is really the set of refs which match "refs/heads/*" it really is the set of heads and not necessarily the set of all branches, as the remote tracking branches are not listed in this set, even if it appears in the "refs/heads/*" namespace (e.g. an old style repository). Signed-off-by: Shawn O. Pearce --- git-gui | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/git-gui b/git-gui index 7406238c90..6035105f8e 100755 --- a/git-gui +++ b/git-gui @@ -1609,10 +1609,10 @@ proc write_checkout_index {fd pathList totalCnt batch msg after} { ## ## branch management -proc load_all_branches {} { - global all_branches tracking_branches +proc load_all_heads {} { + global all_heads tracking_branches - set all_branches [list] + set all_heads [list] set cmd [list git for-each-ref] lappend cmd --format=%(refname) lappend cmd refs/heads @@ -1620,18 +1620,18 @@ proc load_all_branches {} { while {[gets $fd line] > 0} { if {![catch {set info $tracking_branches($line)}]} continue if {![regsub ^refs/heads/ $line {} name]} continue - lappend all_branches $name + lappend all_heads $name } close $fd - set all_branches [lsort $all_branches] + set all_heads [lsort $all_heads] } proc populate_branch_menu {m} { - global all_branches disable_on_lock + global all_heads disable_on_lock $m add separator - foreach b $all_branches { + foreach b $all_heads { $m add radiobutton \ -label $b \ -command [list switch_branch $b] \ @@ -3674,7 +3674,7 @@ user.email settings into your personal if {!$single_commit} { load_all_remotes - load_all_branches + load_all_heads populate_branch_menu .mbar.branch populate_fetch_menu .mbar.fetch populate_pull_menu .mbar.pull -- cgit v1.2.3 From 85ab313ed3dc9a951ec3859fcc3a32a6c5c3ee19 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 25 Nov 2006 03:38:39 -0500 Subject: git-gui: Misc. comment and formatting cleanups. Signed-off-by: Shawn O. Pearce --- git-gui | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/git-gui b/git-gui index 6035105f8e..f52dd522c1 100755 --- a/git-gui +++ b/git-gui @@ -2997,6 +2997,7 @@ if {!$single_commit} { -font font_ui } } + .mbar.repository add command -label Quit \ -command do_quit \ -accelerator $M1T-Q \ @@ -3036,9 +3037,9 @@ menu .mbar.edit -accelerator $M1T-A \ -font font_ui +# -- Branch Menu +# if {!$single_commit} { - # -- Branch Menu - # menu .mbar.branch .mbar.branch add command -label {Create...} \ @@ -3615,11 +3616,9 @@ set selected_commit_type new wm title . "$appname ([file normalize [file dirname $gitdir]])" focus -force $ui_comm -# -- Warn the user about environmental problems. -# Cygwin's Tcl does *not* pass its env array -# onto any processes it spawns. This means -# that the git processes get none of our -# environment. That may not work... +# -- Warn the user about environmental problems. Cygwin's Tcl +# does *not* pass its env array onto any processes it spawns. +# This means that git processes get none of our environment. # if {[is_Windows]} { set ignored_env 0 @@ -3672,13 +3671,17 @@ user.email settings into your personal unset ignored_env msg suggest_user name } +# -- Only initialize complex UI if we are going to stay running. +# if {!$single_commit} { load_all_remotes load_all_heads + populate_branch_menu .mbar.branch populate_fetch_menu .mbar.fetch populate_pull_menu .mbar.pull populate_push_menu .mbar.push } + lock_index begin-read after 1 do_rescan -- cgit v1.2.3 From 84e0bf1de4fcdada4698e2bd53bafaeaea6b5cbd Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 25 Nov 2006 04:04:24 -0500 Subject: git-gui: Started implementation of switch_branch. This implementation of switch_branch is not yet finished, and thus it throws a "NOT FINISHED" error rather than completing the switch. But its a rough sketch of the procedure required. Signed-off-by: Shawn O. Pearce --- git-gui | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/git-gui b/git-gui index f52dd522c1..3845cf7168 100755 --- a/git-gui +++ b/git-gui @@ -1643,10 +1643,59 @@ proc populate_branch_menu {m} { } } -proc switch_branch {b} { +proc do_create_branch {} { error "NOT IMPLEMENTED" } +proc do_delete_branch {} { + error "NOT IMPLEMENTED" +} + +proc switch_branch {b} { + global HEAD commit_type file_states current_branch + global selected_commit_type ui_comm + + if {![lock_index switch]} return + + # -- Backup the selected branch (repository_state resets it) + # + set new_branch $current_branch + + # -- Our in memory state should match the repository. + # + repository_state curType curHEAD curMERGE_HEAD + if {[string match amend* $commit_type] + && $curType eq {normal} + && $curHEAD eq $HEAD} { + } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} { + info_popup {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 the current branch can be changed. + +The rescan will be automatically started now. +} + unlock_index + rescan {set ui_status_value {Ready.}} + return + } + + # -- Toss the message buffer if we are in amend mode. + # + if {[string match amend* $curType]} { + $ui_comm delete 0.0 end + $ui_comm edit reset + $ui_comm edit modified false + } + + set selected_commit_type new + set current_branch $new_branch + + unlock_index + error "NOT FINISHED" +} + ###################################################################### ## ## remote management -- cgit v1.2.3 From 9208487b34706887fcc10ce6423099134f301f5e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 25 Nov 2006 12:40:29 -0500 Subject: git-gui: Set a proper title on our revert confirm dialog box. Signed-off-by: Shawn O. Pearce --- git-gui | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index 3845cf7168..09fc40aaf8 100755 --- a/git-gui +++ b/git-gui @@ -2387,6 +2387,7 @@ proc do_include_all {} { } proc revert_helper {txt paths} { + global gitdir appname global file_states current_diff if {![lock_index begin-update]} return @@ -2419,13 +2420,17 @@ proc revert_helper {txt paths} { set s "these $n files" } + set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] + set reply [tk_dialog \ .confirm_revert \ - "title" \ + "$appname ($reponame)" \ "Revert unincluded changes in $s? Any unincluded changes will be permanently lost by the revert." \ - questhead \ + question \ 1 \ {Do Nothing} \ {Revert Changes} \ -- cgit v1.2.3 From 5e926cbf7eeb2c89e0957bed7941c2c6490767c2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 25 Nov 2006 23:16:16 -0500 Subject: git-gui: Updated todo list. Signed-off-by: Shawn O. Pearce --- TODO | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/TODO b/TODO index b8fa807938..ef4f50b304 100644 --- a/TODO +++ b/TODO @@ -1,16 +1,11 @@ Items outstanding: - * Checkout $PARENT version to working directory, overwriting current - version. ($PARENT is HEAD, except when amending). - * Add file to .gitignore or info/excludes. * Populate the pull menu with local branches. * Make use of the new default merge data stored in repo-config. - * Indicate what the current branch is. - * Checkout or create a different local branch. * Delete a local branch. @@ -37,3 +32,18 @@ Items outstanding: parse that file and generate the options dialog from the documentation itself, and include the help text from the documentation as part of the UI somehow. + +Known bugs: + + * git-gui sometimes just closes on Windows with no error message. + I'm not sure what the problem is here. I suspect the wish + process is just terminating due to a segfault or something, + as the do_quit proc in git-gui doesn't run. It often seems to + occur while writing a commit message in the buffer. Odd. + + * At one point after using git-gui for a while to make many commits + to a repository I reverted one file through git-gui and another + manually in my editor; during commit git-gui crashed with an + error about the icon name it was trying to update no longer + existed in the widget. I suspect something didn't update right + in file_states... -- cgit v1.2.3 From f70c3a2caccf4f0f5abfd3d0db4120e8659dd0d7 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 26 Nov 2006 19:45:39 -0500 Subject: git-gui: Enable resolution of merge conflicts. If a file has a merge conflict (index state = U) the user will need to run update-index on that file to resolve all stages down to stage 0, by including the file in the working directory. Like core Git we'll just trust the user that their resolution is correct, and that they didn't just include the file into the commit while merge conflicts still exist within the file. Signed-off-by: Shawn O. Pearce --- git-gui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-gui b/git-gui index 09fc40aaf8..0b0f1e3e70 100755 --- a/git-gui +++ b/git-gui @@ -1502,10 +1502,13 @@ proc write_update_index {fd pathList totalCnt batch msg after} { switch -glob -- [lindex $file_states($path) 0] { AD - MD - + UD - _D {set new DD} _M - MM - + UM - + U_ - M_ {set new M_} _O - -- cgit v1.2.3 From c15ad650c77ef3213d723efec4e1dca89efba6cd Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 26 Nov 2006 19:46:45 -0500 Subject: git-gui: Auto-update any A? or M? files during rescan. If the user has partial includes disabled then it doesn't matter what state the working directory is in; if the file has been included in the next commit its index state is A or M and we should immediately run update-index on the working directory file to bring the index in sync with the working directory. Signed-off-by: Shawn O. Pearce --- git-gui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index 0b0f1e3e70..899fa35a60 100755 --- a/git-gui +++ b/git-gui @@ -499,8 +499,8 @@ proc rescan_done {fd buf after} { set pathList [list] foreach path [array names file_states] { switch -- [lindex $file_states($path) 0] { - AM - - MM {lappend pathList $path} + A? - + M? {lappend pathList $path} } } if {$pathList ne {}} { -- cgit v1.2.3 From eae2ce619277903e73550663e6826f0299191bf3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 7 Dec 2006 19:59:46 -0500 Subject: git-gui: Reworded 'Include' to 'Add' to match core Git. Now that git-add is a first class citizen in core Git (Nico's 366bfcb6) users may start to expect the term 'add' to refer to the act of including a file's changes into a commit. So I'm replacing all uses of the term 'Include' in the UI with 'Add'. Signed-off-by: Shawn O. Pearce --- git-gui | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-gui b/git-gui index 899fa35a60..1891215a64 100755 --- a/git-gui +++ b/git-gui @@ -3143,13 +3143,13 @@ lappend disable_on_lock \ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Include In Commit} \ +.mbar.commit add command -label {Add To Commit} \ -command do_include_selection \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Include All In Commit} \ +.mbar.commit add command -label {Add All To Commit} \ -command do_include_all \ -accelerator $M1T-I \ -font font_ui @@ -3317,7 +3317,7 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.rescan conf -state} -button .vpane.lower.commarea.buttons.incall -text {Include All} \ +button .vpane.lower.commarea.buttons.incall -text {Add All} \ -command do_include_all \ -font font_ui pack .vpane.lower.commarea.buttons.incall -side top -fill x -- cgit v1.2.3 From 557afe820baccb21206c974fbd4afa65bd7f1e03 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 7 Dec 2006 22:07:38 -0500 Subject: git-gui: Created very crude Tools menu, to support miga. In one particular case I have a tool called 'miga' which users may need to invoke on their repository. This is a homegrown tool which is not (and should be) part of git-gui, but I still want to be able to run it from within the gui. Right now I'm taking a shortcut and adding it to the Tools menu if we are not on Mac OS X and the support script used to launch the tool exists in the local filesystem. This is nothing but a complete and utter hack. Signed-off-by: Shawn O. Pearce --- git-gui | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/git-gui b/git-gui index 1891215a64..36979afd77 100755 --- a/git-gui +++ b/git-gui @@ -3210,6 +3210,35 @@ if {[is_MacOSX]} { -command do_options \ -font font_ui + # -- Tools Menu + # + if {[file exists /usr/local/miga/lib/gui-miga]} { + proc do_miga {} { + global gitdir ui_status_value + if {![lock_index update]} return + set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""] + set miga_fd [open "|$cmd" r] + fconfigure $miga_fd -blocking 0 + fileevent $miga_fd readable [list miga_done $miga_fd] + set ui_status_value {Running miga...} + } + proc miga_done {fd} { + read $fd 512 + if {[eof $fd]} { + close $fd + unlock_index + rescan [list set ui_status_value {Ready.}] + } + } + .mbar add cascade -label Tools -menu .mbar.tools + menu .mbar.tools + .mbar.tools add command -label "Migrate" \ + -command do_miga \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.tools entryconf [.mbar.tools index last] -state] + } + # -- Help Menu # .mbar add cascade -label Help -menu .mbar.help -- cgit v1.2.3 From 51e7e568c0a7854b2f93b86d6085695ce80053cc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 12 Dec 2006 22:44:38 -0500 Subject: git-gui: Show all fetched branches for remote pulls. Loop through every remote..fetch entry and add it as a valid option in the Pull menu. This way users can pull any remote branch that they track, without needing to leave the gui. Its a rather crude work around for not having a full merge interface. Signed-off-by: Shawn O. Pearce --- git-gui | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/git-gui b/git-gui index 36979afd77..0c88e4c7c3 100755 --- a/git-gui +++ b/git-gui @@ -1819,28 +1819,29 @@ proc populate_pull_menu {m} { global gitdir repo_config all_remotes disable_on_lock foreach remote $all_remotes { - set rb {} + set rb_list [list] if {[array get repo_config remote.$remote.url] ne {}} { if {[array get repo_config remote.$remote.fetch] ne {}} { - regexp {^([^:]+):} \ - [lindex $repo_config(remote.$remote.fetch) 0] \ - line rb + foreach line $repo_config(remote.$remote.fetch) { + if {[regexp {^([^:]+):} $line line rb]} { + lappend rb_list $rb + } + } } } else { catch { set fd [open [file join $gitdir remotes $remote] r] while {[gets $fd line] >= 0} { if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} { - break + lappend rb_list $rb } } close $fd } } - set rb_short $rb - regsub ^refs/heads/ $rb {} rb_short - if {$rb_short ne {}} { + foreach rb $rb_list { + regsub ^refs/heads/ $rb {} rb_short $m add command \ -label "Branch $rb_short from $remote..." \ -command [list pull_remote $remote $rb] \ -- cgit v1.2.3 From 81c0f29a5633f6a9ab01e0e9ded5e1c6d715b70b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 18:38:12 -0500 Subject: git-gui: Run git-gc rather than git-repack. Now that git 1.5.0-rc1 and later has a 'git gc' command which performs all important repository management activites (including reflog pruning, repacking local objects, unnecessary loose object pruning and rerere cache expiration) we should run 'gc' when the user wants us to cleanup their object database for them. I think the name 'gc' is horrible for a GUI application like git-gui, so I'm labeling the menu action 'Compress Database' instead. Hopefully this will provide some clue to the user about what the action does. Signed-off-by: Shawn O. Pearce --- git-gui | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/git-gui b/git-gui index 0c88e4c7c3..95c922b21e 100755 --- a/git-gui +++ b/git-gui @@ -2217,13 +2217,9 @@ proc do_gitk {revs} { } } -proc do_repack {} { - set w [new_console {repack} \ - {Repacking the object database}] - set cmd [list git repack] - lappend cmd -a - lappend cmd -d - console_exec $w $cmd +proc do_gc {} { + set w [new_console {gc} {Compressing the object database}] + console_exec $w {git gc} } proc do_fsck_objects {} { @@ -3033,8 +3029,8 @@ if {![is_MacOSX]} { .mbar.repository add separator if {!$single_commit} { - .mbar.repository add command -label {Repack Database} \ - -command do_repack \ + .mbar.repository add command -label {Compress Database} \ + -command do_gc \ -font font_ui .mbar.repository add command -label {Verify Database} \ -- cgit v1.2.3 From 6b0f3f46293e2f718054e9947e209c0344721a69 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 18:50:14 -0500 Subject: git-gui: Corrected behavior of deleted (but existing in HEAD) files. Apparently I did not account for the D_ file state. This can occur when a file has been marked for deletion by deleting it from the index, and the file also does not exist in the working directory. Typically this happens when the user deletes the file, hits Rescan, then includes the missing file in the commit, then hits Rescan again. We don't find the file in the working directory but its been removed in the index, so the state becomes D_. This state should be identical with DD. I'm not entirely sure why DD occurs sometimes and D_ others, it would seem like D_ is the state that should be happening instead of DD, leading me to believe there is a quirk in git-gui's state manipulation code. Signed-off-by: Shawn O. Pearce --- git-gui | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git-gui b/git-gui index 95c922b21e..04fdb0c1e7 100755 --- a/git-gui +++ b/git-gui @@ -1429,6 +1429,7 @@ proc write_update_indexinfo {fd pathList totalCnt batch msg after} { switch -glob -- [lindex $s 0] { A? {set new _O} M? {set new _M} + D_ {set new _D} D? {set new _?} ?? {continue} } @@ -1945,6 +1946,7 @@ foreach i { {_D i question "Missing"} {DD i removed "Removed by commit"} + {D_ i removed "Removed by commit"} {DO i removed "Removed (still exists)"} {DM i removed "Removed (but modified)"} -- cgit v1.2.3 From 68cbfb13919132cb2ddc591a765f4f20f9294657 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 18:54:56 -0500 Subject: git-gui: Correct wording of the revert confirmation dialog. We no longer describe updating the index as including changes, as we now use the add notation used by core Git's command line tools. So its confusing to be talking about unincluded changes within the revert dialog. Instead we should used language like 'unadded changes'. Signed-off-by: Shawn O. Pearce --- git-gui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-gui b/git-gui index 04fdb0c1e7..ade64dcd12 100755 --- a/git-gui +++ b/git-gui @@ -2429,9 +2429,9 @@ proc revert_helper {txt paths} { set reply [tk_dialog \ .confirm_revert \ "$appname ($reponame)" \ - "Revert unincluded changes in $s? + "Revert changes in $s? -Any unincluded changes will be permanently lost by the revert." \ +Any unadded changes will be permanently lost by the revert." \ question \ 1 \ {Do Nothing} \ -- cgit v1.2.3 From bdadecbae5b9f7317994bf2f521bb15068823a1d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 19:03:26 -0500 Subject: git-gui: Work around odd cygpath bug on Windows. There appears to be a bug on one of my test systems where cygpath with the --long-name option is generating a corrupt string that does not actually refer to sh.exe. This breaks any desktop icon created by git-gui as the executable we are trying to invoke does not exist. Since Cygwin is typically installed as C:\cygwin long path names is probably not actually necessary to link to the shell. I also added a small echo to the start of the icon script, as it can take one of my test systems several seconds to startup git-gui. This way the user knows we're starting git-gui, and was politely asked to wait for the action to complete. Signed-off-by: Shawn O. Pearce --- git-gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui b/git-gui index ade64dcd12..c5120cc1ac 100755 --- a/git-gui +++ b/git-gui @@ -2742,7 +2742,6 @@ proc do_windows_shortcut {} { set sh [exec cygpath \ --windows \ --absolute \ - --long-name \ /bin/sh] set me [exec cygpath \ --unix \ @@ -2754,6 +2753,7 @@ proc do_windows_shortcut {} { $gitdir] regsub -all ' $me "'\\''" me regsub -all ' $gd "'\\''" gd + puts $fd "@ECHO Starting git-gui... Please wait..." puts -nonewline $fd "\"$sh\" --login -c \"" puts -nonewline $fd "GIT_DIR='$gd'" puts -nonewline $fd " '$me'" -- cgit v1.2.3 From 4d583c86ec52f8b2937a0b9dc02667b54c4a28a2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 19:07:46 -0500 Subject: git-gui: Change more 'include' language to 'add'. I just found a whole slew of places where we still were using the term 'include' rather than 'add' to refer to the act of updating the index with modifications from the working directory. To be consistent with all Git documentation and command line tools, these should be 'add'. Signed-off-by: Shawn O. Pearce --- git-gui | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/git-gui b/git-gui index c5120cc1ac..11ba41f74b 100755 --- a/git-gui +++ b/git-gui @@ -1935,9 +1935,9 @@ set max_status_desc 0 foreach i { {__ i plain "Unmodified"} {_M i mod "Modified"} - {M_ i fulltick "Included in commit"} + {M_ i fulltick "Added to commit"} {MM i parttick "Partially included"} - {MD i question "Included (but gone)"} + {MD i question "Added (but gone)"} {_O o plain "Untracked"} {A_ o fulltick "Added by commit"} @@ -2360,11 +2360,11 @@ proc do_include_selection {} { if {[array size selected_paths] > 0} { include_helper \ - {Including selected files} \ + {Adding selected files} \ [array names selected_paths] } elseif {$current_diff ne {}} { include_helper \ - "Including [short_path $current_diff]" \ + "Adding [short_path $current_diff]" \ [list $current_diff] } } @@ -2384,7 +2384,7 @@ proc do_include_all {} { } } include_helper \ - {Including all modified files} \ + {Adding all modified files} \ $paths } @@ -2615,7 +2615,7 @@ proc do_options {} { pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 foreach option { - {b partialinclude {Allow Partially Included Files}} + {b partialinclude {Allow Partially Added Files}} {b pullsummary {Show Pull Summary}} {b trustmtime {Trust File Modification Timestamps}} {i diffcontext {Number of Diff Context Lines}} @@ -2871,7 +2871,7 @@ proc toggle_or_diff {w x y} { } ?? { update_index \ - "Including [short_path $path]" \ + "Adding [short_path $path]" \ [list $path] \ [concat $after {set ui_status_value {Ready.}}] } -- cgit v1.2.3 From c25623321d52642fe8fb80c64904a53363f91b12 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 19:45:26 -0500 Subject: git-gui: Hide the ugly bash command line from the windows desktop icon. The user really doesn't need to see the technical details of how we launch git-gui from within their "desktop icon". Instead we should hide the command line from being displayed when the icon launches by putting @ at the start of the line. If they really need to see the command we are running they can edit the batch file. Signed-off-by: Shawn O. Pearce --- git-gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui b/git-gui index 11ba41f74b..b79eb451d2 100755 --- a/git-gui +++ b/git-gui @@ -2754,7 +2754,7 @@ proc do_windows_shortcut {} { regsub -all ' $me "'\\''" me regsub -all ' $gd "'\\''" gd puts $fd "@ECHO Starting git-gui... Please wait..." - puts -nonewline $fd "\"$sh\" --login -c \"" + puts -nonewline $fd "@\"$sh\" --login -c \"" puts -nonewline $fd "GIT_DIR='$gd'" puts -nonewline $fd " '$me'" puts $fd "&\"" -- cgit v1.2.3 From 41bdcda37376a5faa63028f01260890723c3fcfa Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 20:00:07 -0500 Subject: git-gui: Modified makefile to embed version into git-gui script. We want to embed the version of git-gui directly into the script file, so that we can display it properly in the about dialog. Consequently I've refactored the Makefile process to act like the one in core git.git with regards to shell scripts, allowing git-gui to be constructed by a sed replacement performed on git-gui.sh. Signed-off-by: Shawn O. Pearce --- .gitignore | 2 + Makefile | 31 +- git-citool | 1 - git-gui | 3772 ----------------------------------------------------------- git-gui.sh | 3773 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 3803 insertions(+), 3776 deletions(-) create mode 100644 .gitignore delete mode 120000 git-citool delete mode 100755 git-gui create mode 100755 git-gui.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..5bda901aeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +git-citool +git-gui diff --git a/Makefile b/Makefile index e3e871f7c4..606bec640e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,13 @@ -all: git-gui +all:: + +SCRIPT_SH = git-gui.sh +GITGUI_BUILT_INS = git-citool +ALL_PROGRAMS = $(GITGUI_BUILT_INS) $(patsubst %.sh,%,$(SCRIPT_SH)) +GITGUI_VERSION := $(shell git describe) + +ifndef SHELL_PATH + SHELL_PATH = /bin/sh +endif gitexecdir := $(shell git --exec-path) INSTALL = install @@ -6,9 +15,25 @@ INSTALL = install DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) gitexecdir_SQ = $(subst ','\'',$(gitexecdir)) -GITGUI_BUILTIN = git-citool +SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) + +$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh + rm -f $@ $@+ + sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ + -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \ + $@.sh >$@+ + chmod +x $@+ + mv $@+ $@ + +$(GITGUI_BUILT_INS): git-gui + rm -f $@ && ln git-gui $@ + +all:: $(ALL_PROGRAMS) install: all $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL) git-gui '$(DESTDIR_SQ)$(gitexecdir_SQ)' - $(foreach p,$(GITGUI_BUILTIN), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) + $(foreach p,$(GITGUI_BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) + +clean:: + rm -f $(ALL_PROGRAMS) diff --git a/git-citool b/git-citool deleted file mode 120000 index b5f620fd09..0000000000 --- a/git-citool +++ /dev/null @@ -1 +0,0 @@ -git-gui \ No newline at end of file diff --git a/git-gui b/git-gui deleted file mode 100755 index b79eb451d2..0000000000 --- a/git-gui +++ /dev/null @@ -1,3772 +0,0 @@ -#!/bin/sh -# Tcl ignores the next line -*- tcl -*- \ -exec wish "$0" -- "$@" - -set copyright { -Copyright © 2006 Shawn Pearce, Paul Mackerras. - -All rights reserved. - -This program is free software; it may be used, copied, modified -and distributed under the terms of the GNU General Public Licence, -either version 2, or (at your option) any later version.} - -set appname [lindex [file split $argv0] end] -set gitdir {} - -###################################################################### -## -## config - -proc is_many_config {name} { - switch -glob -- $name { - remote.*.fetch - - remote.*.push - {return 1} - * - {return 0} - } -} - -proc load_config {include_global} { - global repo_config global_config default_config - - array unset global_config - if {$include_global} { - catch { - set fd_rc [open "| git repo-config --global --list" r] - while {[gets $fd_rc line] >= 0} { - if {[regexp {^([^=]+)=(.*)$} $line line name value]} { - if {[is_many_config $name]} { - lappend global_config($name) $value - } else { - set global_config($name) $value - } - } - } - close $fd_rc - } - } - - array unset repo_config - catch { - set fd_rc [open "| git repo-config --list" r] - while {[gets $fd_rc line] >= 0} { - if {[regexp {^([^=]+)=(.*)$} $line line name value]} { - if {[is_many_config $name]} { - lappend repo_config($name) $value - } else { - set repo_config($name) $value - } - } - } - close $fd_rc - } - - foreach name [array names default_config] { - if {[catch {set v $global_config($name)}]} { - set global_config($name) $default_config($name) - } - if {[catch {set v $repo_config($name)}]} { - set repo_config($name) $default_config($name) - } - } -} - -proc save_config {} { - global default_config font_descs - global repo_config global_config - global repo_config_new global_config_new - - foreach option $font_descs { - set name [lindex $option 0] - set font [lindex $option 1] - font configure $font \ - -family $global_config_new(gui.$font^^family) \ - -size $global_config_new(gui.$font^^size) - font configure ${font}bold \ - -family $global_config_new(gui.$font^^family) \ - -size $global_config_new(gui.$font^^size) - set global_config_new(gui.$name) [font configure $font] - unset global_config_new(gui.$font^^family) - unset global_config_new(gui.$font^^size) - } - - foreach name [array names default_config] { - set value $global_config_new($name) - if {$value ne $global_config($name)} { - if {$value eq $default_config($name)} { - catch {exec git repo-config --global --unset $name} - } else { - regsub -all "\[{}\]" $value {"} value - exec git repo-config --global $name $value - } - set global_config($name) $value - if {$value eq $repo_config($name)} { - catch {exec git repo-config --unset $name} - set repo_config($name) $value - } - } - } - - foreach name [array names default_config] { - set value $repo_config_new($name) - if {$value ne $repo_config($name)} { - if {$value eq $global_config($name)} { - catch {exec git repo-config --unset $name} - } else { - regsub -all "\[{}\]" $value {"} value - exec git repo-config $name $value - } - set repo_config($name) $value - } - } -} - -proc error_popup {msg} { - global gitdir appname - - set title $appname - if {$gitdir ne {}} { - append title { (} - append title [lindex \ - [file split [file normalize [file dirname $gitdir]]] \ - end] - append title {)} - } - set cmd [list tk_messageBox \ - -icon error \ - -type ok \ - -title "$title: error" \ - -message $msg] - if {[winfo ismapped .]} { - lappend cmd -parent . - } - eval $cmd -} - -proc warn_popup {msg} { - global gitdir appname - - set title $appname - if {$gitdir ne {}} { - append title { (} - append title [lindex \ - [file split [file normalize [file dirname $gitdir]]] \ - end] - append title {)} - } - set cmd [list tk_messageBox \ - -icon warning \ - -type ok \ - -title "$title: warning" \ - -message $msg] - if {[winfo ismapped .]} { - lappend cmd -parent . - } - eval $cmd -} - -proc info_popup {msg} { - global gitdir appname - - set title $appname - if {$gitdir ne {}} { - append title { (} - append title [lindex \ - [file split [file normalize [file dirname $gitdir]]] \ - end] - append title {)} - } - tk_messageBox \ - -parent . \ - -icon info \ - -type ok \ - -title $title \ - -message $msg -} - -###################################################################### -## -## repository setup - -if { [catch {set gitdir $env(GIT_DIR)}] - && [catch {set gitdir [exec git rev-parse --git-dir]} err]} { - catch {wm withdraw .} - error_popup "Cannot find the git directory:\n\n$err" - exit 1 -} -if {![file isdirectory $gitdir]} { - catch {wm withdraw .} - error_popup "Git directory not found:\n\n$gitdir" - exit 1 -} -if {[lindex [file split $gitdir] end] ne {.git}} { - catch {wm withdraw .} - error_popup "Cannot use funny .git directory:\n\n$gitdir" - exit 1 -} -if {[catch {cd [file dirname $gitdir]} err]} { - catch {wm withdraw .} - error_popup "No working directory [file dirname $gitdir]:\n\n$err" - exit 1 -} - -set single_commit 0 -if {$appname eq {git-citool}} { - set single_commit 1 -} - -###################################################################### -## -## task management - -set rescan_active 0 -set diff_active 0 -set last_clicked {} - -set disable_on_lock [list] -set index_lock_type none - -proc lock_index {type} { - global index_lock_type disable_on_lock - - if {$index_lock_type eq {none}} { - set index_lock_type $type - foreach w $disable_on_lock { - uplevel #0 $w disabled - } - return 1 - } elseif {$index_lock_type eq "begin-$type"} { - set index_lock_type $type - return 1 - } - return 0 -} - -proc unlock_index {} { - global index_lock_type disable_on_lock - - set index_lock_type none - foreach w $disable_on_lock { - uplevel #0 $w normal - } -} - -###################################################################### -## -## status - -proc repository_state {ctvar hdvar mhvar} { - global gitdir current_branch - upvar $ctvar ct $hdvar hd $mhvar mh - - set mh [list] - - if {[catch {set current_branch [exec git symbolic-ref HEAD]}]} { - set current_branch {} - } else { - regsub ^refs/((heads|tags|remotes)/)? \ - $current_branch \ - {} \ - current_branch - } - - if {[catch {set hd [exec git rev-parse --verify HEAD]}]} { - set hd {} - set ct initial - return - } - - set merge_head [file join $gitdir MERGE_HEAD] - if {[file exists $merge_head]} { - set ct merge - set fd_mh [open $merge_head r] - while {[gets $fd_mh line] >= 0} { - lappend mh $line - } - close $fd_mh - return - } - - set ct normal -} - -proc PARENT {} { - global PARENT empty_tree - - set p [lindex $PARENT 0] - if {$p ne {}} { - return $p - } - if {$empty_tree eq {}} { - set empty_tree [exec git mktree << {}] - } - return $empty_tree -} - -proc rescan {after} { - global HEAD PARENT MERGE_HEAD commit_type - global ui_index ui_other ui_status_value ui_comm - global rescan_active file_states - global repo_config - - if {$rescan_active > 0 || ![lock_index read]} return - - repository_state newType newHEAD newMERGE_HEAD - if {[string match amend* $commit_type] - && $newType eq {normal} - && $newHEAD eq $HEAD} { - } else { - set HEAD $newHEAD - set PARENT $newHEAD - set MERGE_HEAD $newMERGE_HEAD - set commit_type $newType - } - - array unset file_states - - if {![$ui_comm edit modified] - || [string trim [$ui_comm get 0.0 end]] eq {}} { - if {[load_message GITGUI_MSG]} { - } elseif {[load_message MERGE_MSG]} { - } elseif {[load_message SQUASH_MSG]} { - } - $ui_comm edit reset - $ui_comm edit modified false - } - - if {$repo_config(gui.trustmtime) eq {true}} { - rescan_stage2 {} $after - } else { - set rescan_active 1 - set ui_status_value {Refreshing file status...} - set cmd [list git update-index] - lappend cmd -q - lappend cmd --unmerged - lappend cmd --ignore-missing - lappend cmd --refresh - set fd_rf [open "| $cmd" r] - fconfigure $fd_rf -blocking 0 -translation binary - fileevent $fd_rf readable \ - [list rescan_stage2 $fd_rf $after] - } -} - -proc rescan_stage2 {fd after} { - global gitdir ui_status_value - global rescan_active buf_rdi buf_rdf buf_rlo - - if {$fd ne {}} { - read $fd - if {![eof $fd]} return - close $fd - } - - set ls_others [list | git ls-files --others -z \ - --exclude-per-directory=.gitignore] - set info_exclude [file join $gitdir info exclude] - if {[file readable $info_exclude]} { - lappend ls_others "--exclude-from=$info_exclude" - } - - set buf_rdi {} - set buf_rdf {} - set buf_rlo {} - - set rescan_active 3 - set ui_status_value {Scanning for modified files ...} - set fd_di [open "| git diff-index --cached -z [PARENT]" r] - set fd_df [open "| git diff-files -z" r] - set fd_lo [open $ls_others r] - - fconfigure $fd_di -blocking 0 -translation binary - fconfigure $fd_df -blocking 0 -translation binary - fconfigure $fd_lo -blocking 0 -translation binary - fileevent $fd_di readable [list read_diff_index $fd_di $after] - fileevent $fd_df readable [list read_diff_files $fd_df $after] - fileevent $fd_lo readable [list read_ls_others $fd_lo $after] -} - -proc load_message {file} { - global gitdir ui_comm - - set f [file join $gitdir $file] - if {[file isfile $f]} { - if {[catch {set fd [open $f r]}]} { - return 0 - } - set content [string trim [read $fd]] - close $fd - $ui_comm delete 0.0 end - $ui_comm insert end $content - return 1 - } - return 0 -} - -proc read_diff_index {fd after} { - global buf_rdi - - append buf_rdi [read $fd] - set c 0 - set n [string length $buf_rdi] - while {$c < $n} { - set z1 [string first "\0" $buf_rdi $c] - if {$z1 == -1} break - incr z1 - set z2 [string first "\0" $buf_rdi $z1] - if {$z2 == -1} break - - incr c - set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }] - merge_state \ - [string range $buf_rdi $z1 [expr {$z2 - 1}]] \ - [lindex $i 4]? \ - [list [lindex $i 0] [lindex $i 2]] \ - [list] - set c $z2 - incr c - } - if {$c < $n} { - set buf_rdi [string range $buf_rdi $c end] - } else { - set buf_rdi {} - } - - rescan_done $fd buf_rdi $after -} - -proc read_diff_files {fd after} { - global buf_rdf - - append buf_rdf [read $fd] - set c 0 - set n [string length $buf_rdf] - while {$c < $n} { - set z1 [string first "\0" $buf_rdf $c] - if {$z1 == -1} break - incr z1 - set z2 [string first "\0" $buf_rdf $z1] - if {$z2 == -1} break - - incr c - set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }] - merge_state \ - [string range $buf_rdf $z1 [expr {$z2 - 1}]] \ - ?[lindex $i 4] \ - [list] \ - [list [lindex $i 0] [lindex $i 2]] - set c $z2 - incr c - } - if {$c < $n} { - set buf_rdf [string range $buf_rdf $c end] - } else { - set buf_rdf {} - } - - rescan_done $fd buf_rdf $after -} - -proc read_ls_others {fd after} { - global buf_rlo - - append buf_rlo [read $fd] - set pck [split $buf_rlo "\0"] - set buf_rlo [lindex $pck end] - foreach p [lrange $pck 0 end-1] { - merge_state $p ?O - } - rescan_done $fd buf_rlo $after -} - -proc rescan_done {fd buf after} { - global rescan_active - global file_states repo_config - upvar $buf to_clear - - if {![eof $fd]} return - set to_clear {} - close $fd - if {[incr rescan_active -1] > 0} return - - prune_selection - unlock_index - display_all_files - - if {$repo_config(gui.partialinclude) ne {true}} { - set pathList [list] - foreach path [array names file_states] { - switch -- [lindex $file_states($path) 0] { - A? - - M? {lappend pathList $path} - } - } - if {$pathList ne {}} { - update_index \ - "Updating included files" \ - $pathList \ - [concat {reshow_diff;} $after] - return - } - } - - reshow_diff - uplevel #0 $after -} - -proc prune_selection {} { - global file_states selected_paths - - foreach path [array names selected_paths] { - if {[catch {set still_here $file_states($path)}]} { - unset selected_paths($path) - } - } -} - -###################################################################### -## -## diff - -proc clear_diff {} { - global ui_diff current_diff ui_index ui_other - - $ui_diff conf -state normal - $ui_diff delete 0.0 end - $ui_diff conf -state disabled - - set current_diff {} - - $ui_index tag remove in_diff 0.0 end - $ui_other tag remove in_diff 0.0 end -} - -proc reshow_diff {} { - global current_diff ui_status_value file_states - - if {$current_diff eq {} - || [catch {set s $file_states($current_diff)}]} { - clear_diff - } else { - show_diff $current_diff - } -} - -proc handle_empty_diff {} { - global current_diff file_states file_lists - - set path $current_diff - set s $file_states($path) - if {[lindex $s 0] ne {_M}} return - - info_popup "No differences detected. - -[short_path $path] has no changes. - -The modification date of this file was updated -by another application and you currently have -the Trust File Modification Timestamps option -enabled, so Git did not automatically detect -that there are no content differences in this -file. - -This file will now be removed from the modified -files list, to prevent possible confusion. -" - if {[catch {exec git update-index -- $path} err]} { - error_popup "Failed to refresh index:\n\n$err" - } - - clear_diff - set old_w [mapcol [lindex $file_states($path) 0] $path] - set lno [lsearch -sorted $file_lists($old_w) $path] - if {$lno >= 0} { - set file_lists($old_w) \ - [lreplace $file_lists($old_w) $lno $lno] - incr lno - $old_w conf -state normal - $old_w delete $lno.0 [expr {$lno + 1}].0 - $old_w conf -state disabled - } -} - -proc show_diff {path {w {}} {lno {}}} { - global file_states file_lists - global is_3way_diff diff_active repo_config - global ui_diff current_diff ui_status_value - - if {$diff_active || ![lock_index read]} return - - clear_diff - if {$w eq {} || $lno == {}} { - foreach w [array names file_lists] { - set lno [lsearch -sorted $file_lists($w) $path] - if {$lno >= 0} { - incr lno - break - } - } - } - if {$w ne {} && $lno >= 1} { - $w tag add in_diff $lno.0 [expr {$lno + 1}].0 - } - - set s $file_states($path) - set m [lindex $s 0] - set is_3way_diff 0 - set diff_active 1 - set current_diff $path - set ui_status_value "Loading diff of [escape_path $path]..." - - set cmd [list | git diff-index] - lappend cmd --no-color - if {$repo_config(gui.diffcontext) > 0} { - lappend cmd "-U$repo_config(gui.diffcontext)" - } - lappend cmd -p - - switch $m { - MM { - lappend cmd -c - } - _O { - if {[catch { - set fd [open $path r] - set content [read $fd] - close $fd - } err ]} { - set diff_active 0 - unlock_index - set ui_status_value "Unable to display [escape_path $path]" - error_popup "Error loading file:\n\n$err" - return - } - $ui_diff conf -state normal - $ui_diff insert end $content - $ui_diff conf -state disabled - set diff_active 0 - unlock_index - set ui_status_value {Ready.} - return - } - } - - lappend cmd [PARENT] - lappend cmd -- - lappend cmd $path - - if {[catch {set fd [open $cmd r]} err]} { - set diff_active 0 - unlock_index - set ui_status_value "Unable to display [escape_path $path]" - error_popup "Error loading diff:\n\n$err" - return - } - - fconfigure $fd -blocking 0 -translation auto - fileevent $fd readable [list read_diff $fd] -} - -proc read_diff {fd} { - global ui_diff ui_status_value is_3way_diff diff_active - global repo_config - - $ui_diff conf -state normal - while {[gets $fd line] >= 0} { - # -- Cleanup uninteresting diff header lines. - # - if {[string match {diff --git *} $line]} continue - if {[string match {diff --combined *} $line]} continue - if {[string match {--- *} $line]} continue - if {[string match {+++ *} $line]} continue - if {$line eq {deleted file mode 120000}} { - set line "deleted symlink" - } - - # -- Automatically detect if this is a 3 way diff. - # - if {[string match {@@@ *} $line]} {set is_3way_diff 1} - - # -- Reformat a 3 way diff, 'cause its too weird. - # - if {$is_3way_diff} { - set op [string range $line 0 1] - switch -- $op { - {@@} {set tags d_@} - {++} {set tags d_+ ; set op { +}} - {--} {set tags d_- ; set op { -}} - { +} {set tags d_++; set op {++}} - { -} {set tags d_--; set op {--}} - {+ } {set tags d_-+; set op {-+}} - {- } {set tags d_+-; set op {+-}} - default {set tags {}} - } - set line [string replace $line 0 1 $op] - } else { - switch -- [string index $line 0] { - @ {set tags d_@} - + {set tags d_+} - - {set tags d_-} - default {set tags {}} - } - } - $ui_diff insert end $line $tags - $ui_diff insert end "\n" $tags - } - $ui_diff conf -state disabled - - if {[eof $fd]} { - close $fd - set diff_active 0 - unlock_index - set ui_status_value {Ready.} - - if {$repo_config(gui.trustmtime) eq {true} - && [$ui_diff index end] eq {2.0}} { - handle_empty_diff - } - } -} - -###################################################################### -## -## commit - -proc load_last_commit {} { - global HEAD PARENT MERGE_HEAD commit_type ui_comm - - if {[llength $PARENT] == 0} { - error_popup {There is nothing 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}} { - error_popup {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. -} - return - } - - set msg {} - set parents [list] - if {[catch { - set fd [open "| git cat-file commit $curHEAD" r] - while {[gets $fd line] > 0} { - if {[string match {parent *} $line]} { - lappend parents [string range $line 7 end] - } - } - set msg [string trim [read $fd]] - close $fd - } err]} { - error_popup "Error loading commit data for amend:\n\n$err" - return - } - - set HEAD $curHEAD - set PARENT $parents - set MERGE_HEAD [list] - switch -- [llength $parents] { - 0 {set commit_type amend-initial} - 1 {set commit_type amend} - default {set commit_type amend-merge} - } - - $ui_comm delete 0.0 end - $ui_comm insert end $msg - $ui_comm edit reset - $ui_comm edit modified false - rescan {set ui_status_value {Ready.}} -} - -proc create_new_commit {} { - global commit_type ui_comm - - set commit_type normal - $ui_comm delete 0.0 end - $ui_comm edit reset - $ui_comm edit modified false - rescan {set ui_status_value {Ready.}} -} - -set GIT_COMMITTER_IDENT {} - -proc committer_ident {} { - global GIT_COMMITTER_IDENT - - if {$GIT_COMMITTER_IDENT eq {}} { - if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} { - error_popup "Unable to obtain your identity:\n\n$err" - return {} - } - if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \ - $me me GIT_COMMITTER_IDENT]} { - error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me" - return {} - } - } - - return $GIT_COMMITTER_IDENT -} - -proc commit_tree {} { - global HEAD commit_type file_states ui_comm repo_config - - if {![lock_index update]} return - if {[committer_ident] eq {}} return - - # -- Our in memory state should match the repository. - # - repository_state curType curHEAD curMERGE_HEAD - if {[string match amend* $commit_type] - && $curType eq {normal} - && $curHEAD eq $HEAD} { - } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} { - info_popup {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. -} - unlock_index - rescan {set ui_status_value {Ready.}} - return - } - - # -- At least one file should differ in the index. - # - set files_ready 0 - foreach path [array names file_states] { - switch -glob -- [lindex $file_states($path) 0] { - _? {continue} - A? - - D? - - M? {set files_ready 1; break} - U? { - error_popup "Unmerged files cannot be committed. - -File [short_path $path] has merge conflicts. -You must resolve them and include the file before committing. -" - unlock_index - return - } - default { - error_popup "Unknown file state [lindex $s 0] detected. - -File [short_path $path] cannot be committed by this program. -" - } - } - } - if {!$files_ready} { - error_popup {No included files to commit. - -You must include at least 1 file before you can commit. -} - unlock_index - return - } - - # -- A message is required. - # - set msg [string trim [$ui_comm get 1.0 end]] - if {$msg eq {}} { - error_popup {Please supply a commit message. - -A good commit message has the following format: - -- First line: Describe in one sentance what you did. -- Second line: Blank -- Remaining lines: Describe why this change is good. -} - unlock_index - return - } - - # -- Update included files if partialincludes are off. - # - if {$repo_config(gui.partialinclude) ne {true}} { - set pathList [list] - foreach path [array names file_states] { - switch -glob -- [lindex $file_states($path) 0] { - A? - - M? {lappend pathList $path} - } - } - if {$pathList ne {}} { - unlock_index - update_index \ - "Updating included files" \ - $pathList \ - [concat {lock_index update;} \ - [list commit_prehook $curHEAD $msg]] - return - } - } - - commit_prehook $curHEAD $msg -} - -proc commit_prehook {curHEAD msg} { - global gitdir ui_status_value pch_error - - set pchook [file join $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_Windows] && [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 - return - } - - set ui_status_value {Calling pre-commit hook...} - set pch_error {} - set fd_ph [open "| $pchook" r] - fconfigure $fd_ph -blocking 0 -translation binary - fileevent $fd_ph readable \ - [list commit_prehook_wait $fd_ph $curHEAD $msg] -} - -proc commit_prehook_wait {fd_ph curHEAD msg} { - global pch_error ui_status_value - - 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.} - hook_failed_popup pre-commit $pch_error - unlock_index - } else { - commit_writetree $curHEAD $msg - } - set pch_error {} - return - } - fconfigure $fd_ph -blocking 0 -} - -proc commit_writetree {curHEAD msg} { - global ui_status_value - - set ui_status_value {Committing changes...} - set fd_wt [open "| git write-tree" r] - fileevent $fd_wt readable \ - [list commit_committree $fd_wt $curHEAD $msg] -} - -proc commit_committree {fd_wt curHEAD msg} { - global HEAD PARENT MERGE_HEAD commit_type - global single_commit gitdir - global ui_status_value ui_comm selected_commit_type - global file_states selected_paths rescan_active - - 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.} - unlock_index - return - } - - # -- Create the commit. - # - set cmd [list git commit-tree $tree_id] - set parents [concat $PARENT $MERGE_HEAD] - if {[llength $parents] > 0} { - foreach p $parents { - lappend cmd -p $p - } - } else { - # git commit-tree writes to stderr during initial commit. - lappend cmd 2>/dev/null - } - lappend cmd << $msg - if {[catch {set cmt_id [eval exec $cmd]} err]} { - error_popup "commit-tree failed:\n\n$err" - set ui_status_value {Commit failed.} - unlock_index - return - } - - # -- Update the HEAD ref. - # - set reflogm commit - if {$commit_type ne {normal}} { - append reflogm " ($commit_type)" - } - set i [string first "\n" $msg] - if {$i >= 0} { - append reflogm {: } [string range $msg 0 [expr {$i - 1}]] - } else { - append reflogm {: } $msg - } - set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD] - if {[catch {eval exec $cmd} err]} { - error_popup "update-ref failed:\n\n$err" - set ui_status_value {Commit failed.} - unlock_index - return - } - - # -- Cleanup after ourselves. - # - catch {file delete [file join $gitdir MERGE_HEAD]} - catch {file delete [file join $gitdir MERGE_MSG]} - catch {file delete [file join $gitdir SQUASH_MSG]} - catch {file delete [file join $gitdir GITGUI_MSG]} - - # -- Let rerere do its thing. - # - if {[file isdirectory [file join $gitdir rr-cache]]} { - catch {exec git rerere} - } - - # -- Run the post-commit hook. - # - set pchook [file join $gitdir hooks post-commit] - if {[is_Windows] && [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 &} - } - - $ui_comm delete 0.0 end - $ui_comm edit reset - $ui_comm edit modified false - - if {$single_commit} do_quit - - # -- Update in memory status - # - set selected_commit_type new - set commit_type normal - set HEAD $cmt_id - set PARENT $cmt_id - set MERGE_HEAD [list] - - foreach path [array names file_states] { - set s $file_states($path) - set m [lindex $s 0] - switch -glob -- $m { - _O - - _M - - _D {continue} - __ - - A_ - - M_ - - DD { - unset file_states($path) - catch {unset selected_paths($path)} - } - DO { - set file_states($path) [list _O [lindex $s 1] {} {}] - } - AM - - AD - - MM - - MD - - DM { - set file_states($path) [list \ - _[string index $m 1] \ - [lindex $s 1] \ - [lindex $s 3] \ - {}] - } - } - } - - display_all_files - unlock_index - reshow_diff - set ui_status_value \ - "Changes committed as [string range $cmt_id 0 7]." -} - -###################################################################### -## -## fetch pull push - -proc fetch_from {remote} { - set w [new_console "fetch $remote" \ - "Fetching new changes from $remote"] - set cmd [list git fetch] - lappend cmd $remote - console_exec $w $cmd -} - -proc pull_remote {remote branch} { - global HEAD commit_type file_states repo_config - - if {![lock_index update]} return - - # -- Our in memory state should match the repository. - # - repository_state curType curHEAD curMERGE_HEAD - if {$commit_type ne $curType || $HEAD ne $curHEAD} { - info_popup {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 a pull operation can be started. - -The rescan will be automatically started now. -} - unlock_index - rescan {set ui_status_value {Ready.}} - return - } - - # -- No differences should exist before a pull. - # - if {[array size file_states] != 0} { - error_popup {Uncommitted but modified files are present. - -You should not perform a pull with unmodified -files in your working directory as Git will be -unable to recover from an incorrect merge. - -You should commit or revert all changes before -starting a pull operation. -} - unlock_index - return - } - - set w [new_console "pull $remote $branch" \ - "Pulling new changes from branch $branch in $remote"] - set cmd [list git pull] - if {$repo_config(gui.pullsummary) eq {false}} { - lappend cmd --no-summary - } - lappend cmd $remote - lappend cmd $branch - console_exec $w $cmd [list post_pull_remote $remote $branch] -} - -proc post_pull_remote {remote branch success} { - global HEAD PARENT MERGE_HEAD commit_type selected_commit_type - global ui_status_value - - unlock_index - if {$success} { - repository_state commit_type HEAD MERGE_HEAD - set PARENT $HEAD - set selected_commit_type new - set ui_status_value "Pulling $branch from $remote complete." - } else { - rescan [list set ui_status_value \ - "Conflicts detected while pulling $branch from $remote."] - } -} - -proc push_to {remote} { - set w [new_console "push $remote" \ - "Pushing changes to $remote"] - set cmd [list git push] - lappend cmd $remote - console_exec $w $cmd -} - -###################################################################### -## -## ui helpers - -proc mapcol {state path} { - global all_cols ui_other - - if {[catch {set r $all_cols($state)}]} { - puts "error: no column for state={$state} $path" - return $ui_other - } - return $r -} - -proc mapicon {state path} { - global all_icons - - if {[catch {set r $all_icons($state)}]} { - puts "error: no icon for state={$state} $path" - return file_plain - } - return $r -} - -proc mapdesc {state path} { - global all_descs - - if {[catch {set r $all_descs($state)}]} { - puts "error: no desc for state={$state} $path" - return $state - } - return $r -} - -proc escape_path {path} { - regsub -all "\n" $path "\\n" path - return $path -} - -proc short_path {path} { - return [escape_path [lindex [file split $path] end]] -} - -set next_icon_id 0 -set null_sha1 [string repeat 0 40] - -proc merge_state {path new_state {head_info {}} {index_info {}}} { - global file_states next_icon_id null_sha1 - - set s0 [string index $new_state 0] - set s1 [string index $new_state 1] - - if {[catch {set info $file_states($path)}]} { - set state __ - set icon n[incr next_icon_id] - } else { - set state [lindex $info 0] - set icon [lindex $info 1] - if {$head_info eq {}} {set head_info [lindex $info 2]} - if {$index_info eq {}} {set index_info [lindex $info 3]} - } - - if {$s0 eq {?}} {set s0 [string index $state 0]} \ - elseif {$s0 eq {_}} {set s0 _} - - if {$s1 eq {?}} {set s1 [string index $state 1]} \ - elseif {$s1 eq {_}} {set s1 _} - - if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} { - set head_info [list 0 $null_sha1] - } elseif {$s0 ne {_} && [string index $state 0] eq {_} - && $head_info eq {}} { - set head_info $index_info - } - - set file_states($path) [list $s0$s1 $icon \ - $head_info $index_info \ - ] - return $state -} - -proc display_file {path state} { - global file_states file_lists selected_paths - - set old_m [merge_state $path $state] - set s $file_states($path) - set new_m [lindex $s 0] - set new_w [mapcol $new_m $path] - set old_w [mapcol $old_m $path] - set new_icon [mapicon $new_m $path] - - if {$new_m eq {__}} { - set lno [lsearch -sorted $file_lists($old_w) $path] - if {$lno >= 0} { - set file_lists($old_w) \ - [lreplace $file_lists($old_w) $lno $lno] - incr lno - $old_w conf -state normal - $old_w delete $lno.0 [expr {$lno + 1}].0 - $old_w conf -state disabled - } - unset file_states($path) - catch {unset selected_paths($path)} - return - } - - if {$new_w ne $old_w} { - set lno [lsearch -sorted $file_lists($old_w) $path] - if {$lno >= 0} { - set file_lists($old_w) \ - [lreplace $file_lists($old_w) $lno $lno] - incr lno - $old_w conf -state normal - $old_w delete $lno.0 [expr {$lno + 1}].0 - $old_w conf -state disabled - } - - lappend file_lists($new_w) $path - set file_lists($new_w) [lsort $file_lists($new_w)] - set lno [lsearch -sorted $file_lists($new_w) $path] - incr lno - $new_w conf -state normal - $new_w image create $lno.0 \ - -align center -padx 5 -pady 1 \ - -name [lindex $s 1] \ - -image $new_icon - $new_w insert $lno.1 "[escape_path $path]\n" - if {[catch {set in_sel $selected_paths($path)}]} { - set in_sel 0 - } - if {$in_sel} { - $new_w tag add in_sel $lno.0 [expr {$lno + 1}].0 - } - $new_w conf -state disabled - } elseif {$new_icon ne [mapicon $old_m $path]} { - $new_w conf -state normal - $new_w image conf [lindex $s 1] -image $new_icon - $new_w conf -state disabled - } -} - -proc display_all_files {} { - global ui_index ui_other - global file_states file_lists - global last_clicked selected_paths - - $ui_index conf -state normal - $ui_other conf -state normal - - $ui_index delete 0.0 end - $ui_other delete 0.0 end - set last_clicked {} - - set file_lists($ui_index) [list] - set file_lists($ui_other) [list] - - foreach path [lsort [array names file_states]] { - set s $file_states($path) - set m [lindex $s 0] - set w [mapcol $m $path] - lappend file_lists($w) $path - set lno [expr {[lindex [split [$w index end] .] 0] - 1}] - $w image create end \ - -align center -padx 5 -pady 1 \ - -name [lindex $s 1] \ - -image [mapicon $m $path] - $w insert end "[escape_path $path]\n" - if {[catch {set in_sel $selected_paths($path)}]} { - set in_sel 0 - } - if {$in_sel} { - $w tag add in_sel $lno.0 [expr {$lno + 1}].0 - } - } - - $ui_index conf -state disabled - $ui_other conf -state disabled -} - -proc update_indexinfo {msg pathList after} { - global update_index_cp ui_status_value - - if {![lock_index update]} return - - set update_index_cp 0 - set pathList [lsort $pathList] - set totalCnt [llength $pathList] - set batch [expr {int($totalCnt * .01) + 1}] - if {$batch > 25} {set batch 25} - - set ui_status_value [format \ - "$msg... %i/%i files (%.2f%%)" \ - $update_index_cp \ - $totalCnt \ - 0.0] - set fd [open "| git update-index -z --index-info" w] - fconfigure $fd \ - -blocking 0 \ - -buffering full \ - -buffersize 512 \ - -translation binary - fileevent $fd writable [list \ - write_update_indexinfo \ - $fd \ - $pathList \ - $totalCnt \ - $batch \ - $msg \ - $after \ - ] -} - -proc write_update_indexinfo {fd pathList totalCnt batch msg after} { - global update_index_cp ui_status_value - global file_states current_diff - - if {$update_index_cp >= $totalCnt} { - close $fd - unlock_index - uplevel #0 $after - return - } - - for {set i $batch} \ - {$update_index_cp < $totalCnt && $i > 0} \ - {incr i -1} { - set path [lindex $pathList $update_index_cp] - incr update_index_cp - - set s $file_states($path) - switch -glob -- [lindex $s 0] { - A? {set new _O} - M? {set new _M} - D_ {set new _D} - D? {set new _?} - ?? {continue} - } - set info [lindex $s 2] - if {$info eq {}} continue - - puts -nonewline $fd $info - puts -nonewline $fd "\t" - puts -nonewline $fd $path - puts -nonewline $fd "\0" - display_file $path $new - } - - set ui_status_value [format \ - "$msg... %i/%i files (%.2f%%)" \ - $update_index_cp \ - $totalCnt \ - [expr {100.0 * $update_index_cp / $totalCnt}]] -} - -proc update_index {msg pathList after} { - global update_index_cp ui_status_value - - if {![lock_index update]} return - - set update_index_cp 0 - set pathList [lsort $pathList] - set totalCnt [llength $pathList] - set batch [expr {int($totalCnt * .01) + 1}] - if {$batch > 25} {set batch 25} - - set ui_status_value [format \ - "$msg... %i/%i files (%.2f%%)" \ - $update_index_cp \ - $totalCnt \ - 0.0] - set fd [open "| git update-index --add --remove -z --stdin" w] - fconfigure $fd \ - -blocking 0 \ - -buffering full \ - -buffersize 512 \ - -translation binary - fileevent $fd writable [list \ - write_update_index \ - $fd \ - $pathList \ - $totalCnt \ - $batch \ - $msg \ - $after \ - ] -} - -proc write_update_index {fd pathList totalCnt batch msg after} { - global update_index_cp ui_status_value - global file_states current_diff - - if {$update_index_cp >= $totalCnt} { - close $fd - unlock_index - uplevel #0 $after - return - } - - for {set i $batch} \ - {$update_index_cp < $totalCnt && $i > 0} \ - {incr i -1} { - set path [lindex $pathList $update_index_cp] - incr update_index_cp - - switch -glob -- [lindex $file_states($path) 0] { - AD - - MD - - UD - - _D {set new DD} - - _M - - MM - - UM - - U_ - - M_ {set new M_} - - _O - - AM - - A_ {set new A_} - - ?? {continue} - } - - puts -nonewline $fd $path - puts -nonewline $fd "\0" - display_file $path $new - } - - set ui_status_value [format \ - "$msg... %i/%i files (%.2f%%)" \ - $update_index_cp \ - $totalCnt \ - [expr {100.0 * $update_index_cp / $totalCnt}]] -} - -proc checkout_index {msg pathList after} { - global update_index_cp ui_status_value - - if {![lock_index update]} return - - set update_index_cp 0 - set pathList [lsort $pathList] - set totalCnt [llength $pathList] - set batch [expr {int($totalCnt * .01) + 1}] - if {$batch > 25} {set batch 25} - - set ui_status_value [format \ - "$msg... %i/%i files (%.2f%%)" \ - $update_index_cp \ - $totalCnt \ - 0.0] - set cmd [list git checkout-index] - lappend cmd --index - lappend cmd --quiet - lappend cmd --force - lappend cmd -z - lappend cmd --stdin - set fd [open "| $cmd " w] - fconfigure $fd \ - -blocking 0 \ - -buffering full \ - -buffersize 512 \ - -translation binary - fileevent $fd writable [list \ - write_checkout_index \ - $fd \ - $pathList \ - $totalCnt \ - $batch \ - $msg \ - $after \ - ] -} - -proc write_checkout_index {fd pathList totalCnt batch msg after} { - global update_index_cp ui_status_value - global file_states current_diff - - if {$update_index_cp >= $totalCnt} { - close $fd - unlock_index - uplevel #0 $after - return - } - - for {set i $batch} \ - {$update_index_cp < $totalCnt && $i > 0} \ - {incr i -1} { - set path [lindex $pathList $update_index_cp] - incr update_index_cp - - switch -glob -- [lindex $file_states($path) 0] { - AM - - AD {set new A_} - MM - - MD {set new M_} - _M - - _D {set new __} - ?? {continue} - } - - puts -nonewline $fd $path - puts -nonewline $fd "\0" - display_file $path $new - } - - set ui_status_value [format \ - "$msg... %i/%i files (%.2f%%)" \ - $update_index_cp \ - $totalCnt \ - [expr {100.0 * $update_index_cp / $totalCnt}]] -} - -###################################################################### -## -## branch management - -proc load_all_heads {} { - global all_heads tracking_branches - - set all_heads [list] - set cmd [list git for-each-ref] - lappend cmd --format=%(refname) - lappend cmd refs/heads - set fd [open "| $cmd" r] - while {[gets $fd line] > 0} { - if {![catch {set info $tracking_branches($line)}]} continue - if {![regsub ^refs/heads/ $line {} name]} continue - lappend all_heads $name - } - close $fd - - set all_heads [lsort $all_heads] -} - -proc populate_branch_menu {m} { - global all_heads disable_on_lock - - $m add separator - foreach b $all_heads { - $m add radiobutton \ - -label $b \ - -command [list switch_branch $b] \ - -variable current_branch \ - -value $b \ - -font font_ui - lappend disable_on_lock \ - [list $m entryconf [$m index last] -state] - } -} - -proc do_create_branch {} { - error "NOT IMPLEMENTED" -} - -proc do_delete_branch {} { - error "NOT IMPLEMENTED" -} - -proc switch_branch {b} { - global HEAD commit_type file_states current_branch - global selected_commit_type ui_comm - - if {![lock_index switch]} return - - # -- Backup the selected branch (repository_state resets it) - # - set new_branch $current_branch - - # -- Our in memory state should match the repository. - # - repository_state curType curHEAD curMERGE_HEAD - if {[string match amend* $commit_type] - && $curType eq {normal} - && $curHEAD eq $HEAD} { - } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} { - info_popup {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 the current branch can be changed. - -The rescan will be automatically started now. -} - unlock_index - rescan {set ui_status_value {Ready.}} - return - } - - # -- Toss the message buffer if we are in amend mode. - # - if {[string match amend* $curType]} { - $ui_comm delete 0.0 end - $ui_comm edit reset - $ui_comm edit modified false - } - - set selected_commit_type new - set current_branch $new_branch - - unlock_index - error "NOT FINISHED" -} - -###################################################################### -## -## remote management - -proc load_all_remotes {} { - global gitdir repo_config - global all_remotes tracking_branches - - set all_remotes [list] - array unset tracking_branches - - set rm_dir [file join $gitdir remotes] - if {[file isdirectory $rm_dir]} { - set all_remotes [glob \ - -types f \ - -tails \ - -nocomplain \ - -directory $rm_dir *] - - foreach name $all_remotes { - catch { - set fd [open [file join $rm_dir $name] r] - while {[gets $fd line] >= 0} { - if {![regexp {^Pull:[ ]*([^:]+):(.+)$} \ - $line line src dst]} continue - if {![regexp ^refs/ $dst]} { - set dst "refs/heads/$dst" - } - set tracking_branches($dst) [list $name $src] - } - close $fd - } - } - } - - foreach line [array names repo_config remote.*.url] { - if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue - lappend all_remotes $name - - if {[catch {set fl $repo_config(remote.$name.fetch)}]} { - set fl {} - } - foreach line $fl { - if {![regexp {^([^:]+):(.+)$} $line line src dst]} continue - if {![regexp ^refs/ $dst]} { - set dst "refs/heads/$dst" - } - set tracking_branches($dst) [list $name $src] - } - } - - set all_remotes [lsort -unique $all_remotes] -} - -proc populate_fetch_menu {m} { - global gitdir all_remotes repo_config - - foreach r $all_remotes { - set enable 0 - if {![catch {set a $repo_config(remote.$r.url)}]} { - if {![catch {set a $repo_config(remote.$r.fetch)}]} { - set enable 1 - } - } else { - catch { - set fd [open [file join $gitdir remotes $r] r] - while {[gets $fd n] >= 0} { - if {[regexp {^Pull:[ \t]*([^:]+):} $n]} { - set enable 1 - break - } - } - close $fd - } - } - - if {$enable} { - $m add command \ - -label "Fetch from $r..." \ - -command [list fetch_from $r] \ - -font font_ui - } - } -} - -proc populate_push_menu {m} { - global gitdir all_remotes repo_config - - foreach r $all_remotes { - set enable 0 - if {![catch {set a $repo_config(remote.$r.url)}]} { - if {![catch {set a $repo_config(remote.$r.push)}]} { - set enable 1 - } - } else { - catch { - set fd [open [file join $gitdir remotes $r] r] - while {[gets $fd n] >= 0} { - if {[regexp {^Push:[ \t]*([^:]+):} $n]} { - set enable 1 - break - } - } - close $fd - } - } - - if {$enable} { - $m add command \ - -label "Push to $r..." \ - -command [list push_to $r] \ - -font font_ui - } - } -} - -proc populate_pull_menu {m} { - global gitdir repo_config all_remotes disable_on_lock - - foreach remote $all_remotes { - set rb_list [list] - if {[array get repo_config remote.$remote.url] ne {}} { - if {[array get repo_config remote.$remote.fetch] ne {}} { - foreach line $repo_config(remote.$remote.fetch) { - if {[regexp {^([^:]+):} $line line rb]} { - lappend rb_list $rb - } - } - } - } else { - catch { - set fd [open [file join $gitdir remotes $remote] r] - while {[gets $fd line] >= 0} { - if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} { - lappend rb_list $rb - } - } - close $fd - } - } - - foreach rb $rb_list { - regsub ^refs/heads/ $rb {} rb_short - $m add command \ - -label "Branch $rb_short from $remote..." \ - -command [list pull_remote $remote $rb] \ - -font font_ui - lappend disable_on_lock \ - [list $m entryconf [$m index last] -state] - } - } -} - -###################################################################### -## -## icons - -set filemask { -#define mask_width 14 -#define mask_height 15 -static unsigned char mask_bits[] = { - 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, - 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, - 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f}; -} - -image create bitmap file_plain -background white -foreground black -data { -#define plain_width 14 -#define plain_height 15 -static unsigned char plain_bits[] = { - 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10, - 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, - 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask - -image create bitmap file_mod -background white -foreground blue -data { -#define mod_width 14 -#define mod_height 15 -static unsigned char mod_bits[] = { - 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, - 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, - 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask - -image create bitmap file_fulltick -background white -foreground "#007000" -data { -#define file_fulltick_width 14 -#define file_fulltick_height 15 -static unsigned char file_fulltick_bits[] = { - 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16, - 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10, - 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask - -image create bitmap file_parttick -background white -foreground "#005050" -data { -#define parttick_width 14 -#define parttick_height 15 -static unsigned char parttick_bits[] = { - 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, - 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10, - 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask - -image create bitmap file_question -background white -foreground black -data { -#define file_question_width 14 -#define file_question_height 15 -static unsigned char file_question_bits[] = { - 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13, - 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10, - 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask - -image create bitmap file_removed -background white -foreground red -data { -#define file_removed_width 14 -#define file_removed_height 15 -static unsigned char file_removed_bits[] = { - 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10, - 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13, - 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask - -image create bitmap file_merge -background white -foreground blue -data { -#define file_merge_width 14 -#define file_merge_height 15 -static unsigned char file_merge_bits[] = { - 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10, - 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, - 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask - -set ui_index .vpane.files.index.list -set ui_other .vpane.files.other.list -set max_status_desc 0 -foreach i { - {__ i plain "Unmodified"} - {_M i mod "Modified"} - {M_ i fulltick "Added to commit"} - {MM i parttick "Partially included"} - {MD i question "Added (but gone)"} - - {_O o plain "Untracked"} - {A_ o fulltick "Added by commit"} - {AM o parttick "Partially added"} - {AD o question "Added (but gone)"} - - {_D i question "Missing"} - {DD i removed "Removed by commit"} - {D_ i removed "Removed by commit"} - {DO i removed "Removed (still exists)"} - {DM i removed "Removed (but modified)"} - - {UD i merge "Merge conflicts"} - {UM i merge "Merge conflicts"} - {U_ i merge "Merge conflicts"} - } { - if {$max_status_desc < [string length [lindex $i 3]]} { - set max_status_desc [string length [lindex $i 3]] - } - if {[lindex $i 1] eq {i}} { - set all_cols([lindex $i 0]) $ui_index - } else { - set all_cols([lindex $i 0]) $ui_other - } - set all_icons([lindex $i 0]) file_[lindex $i 2] - set all_descs([lindex $i 0]) [lindex $i 3] -} -unset filemask i - -###################################################################### -## -## util - -proc is_MacOSX {} { - global tcl_platform tk_library - if {[tk windowingsystem] eq {aqua}} { - return 1 - } - return 0 -} - -proc is_Windows {} { - global tcl_platform - if {$tcl_platform(platform) eq {windows}} { - return 1 - } - return 0 -} - -proc bind_button3 {w cmd} { - bind $w $cmd - if {[is_MacOSX]} { - bind $w $cmd - } -} - -proc incr_font_size {font {amt 1}} { - set sz [font configure $font -size] - incr sz $amt - font configure $font -size $sz - font configure ${font}bold -size $sz -} - -proc hook_failed_popup {hook msg} { - global gitdir appname - - set w .hookfail - toplevel $w - - frame $w.m - label $w.m.l1 -text "$hook hook failed:" \ - -anchor w \ - -justify left \ - -font font_uibold - text $w.m.t \ - -background white -borderwidth 1 \ - -relief sunken \ - -width 80 -height 10 \ - -font font_diff \ - -yscrollcommand [list $w.m.sby set] - label $w.m.l2 \ - -text {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 - 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 - - $w.m.t insert 1.0 $msg - $w.m.t conf -state disabled - - button $w.ok -text OK \ - -width 15 \ - -font font_ui \ - -command "destroy $w" - pack $w.ok -side bottom -anchor e -pady 10 -padx 10 - - bind $w "grab $w; focus $w" - bind $w "destroy $w" - wm title $w "$appname ([lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end]): error" - tkwait window $w -} - -set next_console_id 0 - -proc new_console {short_title long_title} { - global next_console_id console_data - set w .console[incr next_console_id] - set console_data($w) [list $short_title $long_title] - return [console_init $w] -} - -proc console_init {w} { - global console_cr console_data - global gitdir appname M1B - - set console_cr($w) 1.0 - toplevel $w - frame $w.m - label $w.m.l1 -text "[lindex $console_data($w) 1]:" \ - -anchor w \ - -justify left \ - -font font_uibold - text $w.m.t \ - -background white -borderwidth 1 \ - -relief sunken \ - -width 80 -height 10 \ - -font font_diff \ - -state disabled \ - -yscrollcommand [list $w.m.sby set] - label $w.m.s -text {Working... please wait...} \ - -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.s -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 - - menu $w.ctxm -tearoff 0 - $w.ctxm add command -label "Copy" \ - -font font_ui \ - -command "tk_textCopy $w.m.t" - $w.ctxm add command -label "Select All" \ - -font font_ui \ - -command "$w.m.t tag add sel 0.0 end" - $w.ctxm add command -label "Copy All" \ - -font font_ui \ - -command " - $w.m.t tag add sel 0.0 end - tk_textCopy $w.m.t - $w.m.t tag remove sel 0.0 end - " - - button $w.ok -text {Close} \ - -font font_ui \ - -state disabled \ - -command "destroy $w" - pack $w.ok -side bottom -anchor e -pady 10 -padx 10 - - bind_button3 $w.m.t "tk_popup $w.ctxm %X %Y" - bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" - bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break" - bind $w "focus $w" - wm title $w "$appname ([lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end]): [lindex $console_data($w) 0]" - return $w -} - -proc console_exec {w cmd {after {}}} { - # -- Windows tosses the enviroment when we exec our child. - # But most users need that so we have to relogin. :-( - # - if {[is_Windows]} { - set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"] - } - - # -- Tcl won't let us redirect both stdout and stderr to - # the same pipe. So pass it through cat... - # - set cmd [concat | $cmd |& cat] - - set fd_f [open $cmd r] - fconfigure $fd_f -blocking 0 -translation binary - fileevent $fd_f readable [list console_read $w $fd_f $after] -} - -proc console_read {w fd after} { - global console_cr console_data - - set buf [read $fd] - if {$buf ne {}} { - if {![winfo exists $w]} {console_init $w} - $w.m.t conf -state normal - set c 0 - set n [string length $buf] - while {$c < $n} { - set cr [string first "\r" $buf $c] - set lf [string first "\n" $buf $c] - if {$cr < 0} {set cr [expr {$n + 1}]} - if {$lf < 0} {set lf [expr {$n + 1}]} - - if {$lf < $cr} { - $w.m.t insert end [string range $buf $c $lf] - set console_cr($w) [$w.m.t index {end -1c}] - set c $lf - incr c - } else { - $w.m.t delete $console_cr($w) end - $w.m.t insert end "\n" - $w.m.t insert end [string range $buf $c $cr] - set c $cr - incr c - } - } - $w.m.t conf -state disabled - $w.m.t see end - } - - fconfigure $fd -blocking 1 - if {[eof $fd]} { - if {[catch {close $fd}]} { - if {![winfo exists $w]} {console_init $w} - $w.m.s conf -background red -text {Error: Command Failed} - $w.ok conf -state normal - set ok 0 - } elseif {[winfo exists $w]} { - $w.m.s conf -background green -text {Success} - $w.ok conf -state normal - set ok 1 - } - array unset console_cr $w - array unset console_data $w - if {$after ne {}} { - uplevel #0 $after $ok - } - return - } - fconfigure $fd -blocking 0 -} - -###################################################################### -## -## ui commands - -set starting_gitk_msg {Please wait... Starting gitk...} - -proc do_gitk {revs} { - global ui_status_value starting_gitk_msg - - set cmd gitk - if {$revs ne {}} { - append cmd { } - append cmd $revs - } - if {[is_Windows]} { - set cmd "sh -c \"exec $cmd\"" - } - append cmd { &} - - if {[catch {eval exec $cmd} err]} { - error_popup "Failed to start gitk:\n\n$err" - } else { - set ui_status_value $starting_gitk_msg - after 10000 { - if {$ui_status_value eq $starting_gitk_msg} { - set ui_status_value {Ready.} - } - } - } -} - -proc do_gc {} { - set w [new_console {gc} {Compressing the object database}] - console_exec $w {git gc} -} - -proc do_fsck_objects {} { - set w [new_console {fsck-objects} \ - {Verifying the object database with fsck-objects}] - set cmd [list git fsck-objects] - lappend cmd --full - lappend cmd --cache - lappend cmd --strict - console_exec $w $cmd -} - -set is_quitting 0 - -proc do_quit {} { - global gitdir ui_comm is_quitting repo_config commit_type - - if {$is_quitting} return - set is_quitting 1 - - # -- Stash our current commit buffer. - # - set save [file join $gitdir GITGUI_MSG] - set msg [string trim [$ui_comm get 0.0 end]] - if {![string match amend* $commit_type] - && [$ui_comm edit modified] - && $msg ne {}} { - catch { - set fd [open $save w] - puts $fd [string trim [$ui_comm get 0.0 end]] - close $fd - } - } else { - catch {file delete $save} - } - - # -- Stash our current window geometry into this repository. - # - set cfg_geometry [list] - lappend cfg_geometry [wm geometry .] - lappend cfg_geometry [lindex [.vpane sash coord 0] 1] - lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0] - if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { - set rc_geometry {} - } - if {$cfg_geometry ne $rc_geometry} { - catch {exec git repo-config gui.geometry $cfg_geometry} - } - - destroy . -} - -proc do_rescan {} { - rescan {set ui_status_value {Ready.}} -} - -proc remove_helper {txt paths} { - global file_states current_diff - - if {![lock_index begin-update]} return - - set pathList [list] - set after {} - foreach path $paths { - switch -glob -- [lindex $file_states($path) 0] { - A? - - M? - - D? { - lappend pathList $path - if {$path eq $current_diff} { - set after {reshow_diff;} - } - } - } - } - if {$pathList eq {}} { - unlock_index - } else { - update_indexinfo \ - $txt \ - $pathList \ - [concat $after {set ui_status_value {Ready.}}] - } -} - -proc do_remove_selection {} { - global current_diff selected_paths - - if {[array size selected_paths] > 0} { - remove_helper \ - {Removing selected files from commit} \ - [array names selected_paths] - } elseif {$current_diff ne {}} { - remove_helper \ - "Removing [short_path $current_diff] from commit" \ - [list $current_diff] - } -} - -proc include_helper {txt paths} { - global file_states current_diff - - if {![lock_index begin-update]} return - - set pathList [list] - set after {} - foreach path $paths { - switch -glob -- [lindex $file_states($path) 0] { - AM - - AD - - MM - - MD - - U? - - _M - - _D - - _O { - lappend pathList $path - if {$path eq $current_diff} { - set after {reshow_diff;} - } - } - } - } - if {$pathList eq {}} { - unlock_index - } else { - update_index \ - $txt \ - $pathList \ - [concat $after {set ui_status_value {Ready to commit.}}] - } -} - -proc do_include_selection {} { - global current_diff selected_paths - - if {[array size selected_paths] > 0} { - include_helper \ - {Adding selected files} \ - [array names selected_paths] - } elseif {$current_diff ne {}} { - include_helper \ - "Adding [short_path $current_diff]" \ - [list $current_diff] - } -} - -proc do_include_all {} { - global file_states - - set paths [list] - foreach path [array names file_states] { - switch -- [lindex $file_states($path) 0] { - AM - - AD - - MM - - MD - - _M - - _D {lappend paths $path} - } - } - include_helper \ - {Adding all modified files} \ - $paths -} - -proc revert_helper {txt paths} { - global gitdir appname - global file_states current_diff - - if {![lock_index begin-update]} return - - set pathList [list] - set after {} - foreach path $paths { - switch -glob -- [lindex $file_states($path) 0] { - AM - - AD - - MM - - MD - - _M - - _D { - lappend pathList $path - if {$path eq $current_diff} { - set after {reshow_diff;} - } - } - } - } - - set n [llength $pathList] - if {$n == 0} { - unlock_index - return - } elseif {$n == 1} { - set s "[short_path [lindex $pathList]]" - } else { - set s "these $n files" - } - - set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end] - - set reply [tk_dialog \ - .confirm_revert \ - "$appname ($reponame)" \ - "Revert changes in $s? - -Any unadded changes will be permanently lost by the revert." \ - question \ - 1 \ - {Do Nothing} \ - {Revert Changes} \ - ] - if {$reply == 1} { - checkout_index \ - $txt \ - $pathList \ - [concat $after {set ui_status_value {Ready.}}] - } else { - unlock_index - } -} - -proc do_revert_selection {} { - global current_diff selected_paths - - if {[array size selected_paths] > 0} { - revert_helper \ - {Reverting selected files} \ - [array names selected_paths] - } elseif {$current_diff ne {}} { - revert_helper \ - "Reverting [short_path $current_diff]" \ - [list $current_diff] - } -} - -proc do_signoff {} { - global ui_comm - - set me [committer_ident] - if {$me eq {}} return - - set sob "Signed-off-by: $me" - set last [$ui_comm get {end -1c linestart} {end -1c}] - if {$last ne $sob} { - $ui_comm edit separator - if {$last ne {} - && ![regexp {^[A-Z][A-Za-z]*-[A-Za-z-]+: *} $last]} { - $ui_comm insert end "\n" - } - $ui_comm insert end "\n$sob" - $ui_comm edit separator - $ui_comm see end - } -} - -proc do_select_commit_type {} { - global commit_type selected_commit_type - - if {$selected_commit_type eq {new} - && [string match amend* $commit_type]} { - create_new_commit - } elseif {$selected_commit_type eq {amend} - && ![string match amend* $commit_type]} { - load_last_commit - - # The amend request was rejected... - # - if {![string match amend* $commit_type]} { - set selected_commit_type new - } - } -} - -proc do_commit {} { - commit_tree -} - -proc do_about {} { - global appname copyright - global tcl_patchLevel tk_patchLevel - - set w .about_dialog - toplevel $w - wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - - label $w.header -text "About $appname" \ - -font font_uibold - pack $w.header -side top -fill x - - frame $w.buttons - button $w.buttons.close -text {Close} \ - -font font_ui \ - -command [list destroy $w] - pack $w.buttons.close -side right - pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - - label $w.desc \ - -text "$appname - a commit creation tool for Git. -$copyright" \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid \ - -font font_ui - pack $w.desc -side top -fill x -padx 5 -pady 5 - - set v [exec git --version] - append v "\n\n" - if {$tcl_patchLevel eq $tk_patchLevel} { - append v "Tcl/Tk version $tcl_patchLevel" - } else { - append v "Tcl version $tcl_patchLevel" - append v ", Tk version $tk_patchLevel" - } - - label $w.vers \ - -text $v \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid \ - -font font_ui - pack $w.vers -side top -fill x -padx 5 -pady 5 - - bind $w "grab $w; focus $w" - bind $w "destroy $w" - wm title $w "About $appname" - tkwait window $w -} - -proc do_options {} { - global appname gitdir font_descs - global repo_config global_config - global repo_config_new global_config_new - - array unset repo_config_new - array unset global_config_new - foreach name [array names repo_config] { - set repo_config_new($name) $repo_config($name) - } - load_config 1 - foreach name [array names repo_config] { - switch -- $name { - gui.diffcontext {continue} - } - set repo_config_new($name) $repo_config($name) - } - foreach name [array names global_config] { - set global_config_new($name) $global_config($name) - } - set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end] - - set w .options_editor - toplevel $w - wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - - label $w.header -text "$appname Options" \ - -font font_uibold - pack $w.header -side top -fill x - - frame $w.buttons - button $w.buttons.restore -text {Restore Defaults} \ - -font font_ui \ - -command do_restore_defaults - pack $w.buttons.restore -side left - button $w.buttons.save -text Save \ - -font font_ui \ - -command [list do_save_config $w] - pack $w.buttons.save -side right - button $w.buttons.cancel -text {Cancel} \ - -font font_ui \ - -command [list destroy $w] - pack $w.buttons.cancel -side right - pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - - labelframe $w.repo -text "$reponame Repository" \ - -font font_ui \ - -relief raised -borderwidth 2 - labelframe $w.global -text {Global (All Repositories)} \ - -font font_ui \ - -relief raised -borderwidth 2 - pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 - pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 - - foreach option { - {b partialinclude {Allow Partially Added Files}} - {b pullsummary {Show Pull Summary}} - {b trustmtime {Trust File Modification Timestamps}} - {i diffcontext {Number of Diff Context Lines}} - } { - set type [lindex $option 0] - set name [lindex $option 1] - set text [lindex $option 2] - foreach f {repo global} { - switch $type { - b { - checkbutton $w.$f.$name -text $text \ - -variable ${f}_config_new(gui.$name) \ - -onvalue true \ - -offvalue false \ - -font font_ui - pack $w.$f.$name -side top -anchor w - } - i { - frame $w.$f.$name - label $w.$f.$name.l -text "$text:" -font font_ui - pack $w.$f.$name.l -side left -anchor w -fill x - spinbox $w.$f.$name.v \ - -textvariable ${f}_config_new(gui.$name) \ - -from 1 -to 99 -increment 1 \ - -width 3 \ - -font font_ui - pack $w.$f.$name.v -side right -anchor e - pack $w.$f.$name -side top -anchor w -fill x - } - } - } - } - - set all_fonts [lsort [font families]] - foreach option $font_descs { - set name [lindex $option 0] - set font [lindex $option 1] - set text [lindex $option 2] - - set global_config_new(gui.$font^^family) \ - [font configure $font -family] - set global_config_new(gui.$font^^size) \ - [font configure $font -size] - - frame $w.global.$name - label $w.global.$name.l -text "$text:" -font font_ui - pack $w.global.$name.l -side left -anchor w -fill x - eval tk_optionMenu $w.global.$name.family \ - global_config_new(gui.$font^^family) \ - $all_fonts - spinbox $w.global.$name.size \ - -textvariable global_config_new(gui.$font^^size) \ - -from 2 -to 80 -increment 1 \ - -width 3 \ - -font font_ui - pack $w.global.$name.size -side right -anchor e - pack $w.global.$name.family -side right -anchor e - pack $w.global.$name -side top -anchor w -fill x - } - - bind $w "grab $w; focus $w" - bind $w "destroy $w" - wm title $w "$appname ($reponame): Options" - tkwait window $w -} - -proc do_restore_defaults {} { - global font_descs default_config repo_config - global repo_config_new global_config_new - - foreach name [array names default_config] { - set repo_config_new($name) $default_config($name) - set global_config_new($name) $default_config($name) - } - - foreach option $font_descs { - set name [lindex $option 0] - set repo_config(gui.$name) $default_config(gui.$name) - } - apply_config - - foreach option $font_descs { - set name [lindex $option 0] - set font [lindex $option 1] - set global_config_new(gui.$font^^family) \ - [font configure $font -family] - set global_config_new(gui.$font^^size) \ - [font configure $font -size] - } -} - -proc do_save_config {w} { - if {[catch {save_config} err]} { - error_popup "Failed to completely save options:\n\n$err" - } - reshow_diff - destroy $w -} - -proc do_windows_shortcut {} { - global gitdir appname argv0 - - set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end] - - if {[catch { - set desktop [exec cygpath \ - --windows \ - --absolute \ - --long-name \ - --desktop] - }]} { - set desktop . - } - set fn [tk_getSaveFile \ - -parent . \ - -title "$appname ($reponame): Create Desktop Icon" \ - -initialdir $desktop \ - -initialfile "Git $reponame.bat"] - if {$fn != {}} { - if {[catch { - set fd [open $fn w] - set sh [exec cygpath \ - --windows \ - --absolute \ - /bin/sh] - set me [exec cygpath \ - --unix \ - --absolute \ - $argv0] - set gd [exec cygpath \ - --unix \ - --absolute \ - $gitdir] - regsub -all ' $me "'\\''" me - regsub -all ' $gd "'\\''" gd - puts $fd "@ECHO Starting git-gui... Please wait..." - puts -nonewline $fd "@\"$sh\" --login -c \"" - puts -nonewline $fd "GIT_DIR='$gd'" - puts -nonewline $fd " '$me'" - puts $fd "&\"" - close $fd - } err]} { - error_popup "Cannot write script:\n\n$err" - } - } -} - -proc do_macosx_app {} { - global gitdir appname argv0 env - - set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end] - - set fn [tk_getSaveFile \ - -parent . \ - -title "$appname ($reponame): Create Desktop Icon" \ - -initialdir [file join $env(HOME) Desktop] \ - -initialfile "Git $reponame.app"] - if {$fn != {}} { - if {[catch { - set Contents [file join $fn Contents] - set MacOS [file join $Contents MacOS] - set exe [file join $MacOS git-gui] - - file mkdir $MacOS - - set fd [open [file join $Contents Info.plist] w] - puts $fd { - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - git-gui - CFBundleIdentifier - org.spearce.git-gui - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleVersion - 1.0 - NSPrincipalClass - NSApplication - -} - close $fd - - set fd [open $exe w] - set gd [file normalize $gitdir] - set ep [file normalize [exec git --exec-path]] - regsub -all ' $gd "'\\''" gd - regsub -all ' $ep "'\\''" ep - puts $fd "#!/bin/sh" - foreach name [array names env] { - if {[string match GIT_* $name]} { - regsub -all ' $env($name) "'\\''" v - puts $fd "export $name='$v'" - } - } - puts $fd "export PATH='$ep':\$PATH" - puts $fd "export GIT_DIR='$gd'" - puts $fd "exec [file normalize $argv0]" - close $fd - - file attributes $exe -permissions u+x,g+x,o+x - } err]} { - error_popup "Cannot write icon:\n\n$err" - } - } -} - -proc toggle_or_diff {w x y} { - global file_states file_lists current_diff ui_index ui_other - global last_clicked selected_paths - - set pos [split [$w index @$x,$y] .] - set lno [lindex $pos 0] - set col [lindex $pos 1] - set path [lindex $file_lists($w) [expr {$lno - 1}]] - if {$path eq {}} { - set last_clicked {} - return - } - - set last_clicked [list $w $lno] - array unset selected_paths - $ui_index tag remove in_sel 0.0 end - $ui_other tag remove in_sel 0.0 end - - if {$col == 0} { - if {$current_diff eq $path} { - set after {reshow_diff;} - } else { - set after {} - } - switch -glob -- [lindex $file_states($path) 0] { - A_ - - M_ - - DD - - DO - - DM { - update_indexinfo \ - "Removing [short_path $path] from commit" \ - [list $path] \ - [concat $after {set ui_status_value {Ready.}}] - } - ?? { - update_index \ - "Adding [short_path $path]" \ - [list $path] \ - [concat $after {set ui_status_value {Ready.}}] - } - } - } else { - show_diff $path $w $lno - } -} - -proc add_one_to_selection {w x y} { - global file_lists - global last_clicked selected_paths - - set pos [split [$w index @$x,$y] .] - set lno [lindex $pos 0] - set col [lindex $pos 1] - set path [lindex $file_lists($w) [expr {$lno - 1}]] - if {$path eq {}} { - set last_clicked {} - return - } - - set last_clicked [list $w $lno] - if {[catch {set in_sel $selected_paths($path)}]} { - set in_sel 0 - } - if {$in_sel} { - unset selected_paths($path) - $w tag remove in_sel $lno.0 [expr {$lno + 1}].0 - } else { - set selected_paths($path) 1 - $w tag add in_sel $lno.0 [expr {$lno + 1}].0 - } -} - -proc add_range_to_selection {w x y} { - global file_lists - global last_clicked selected_paths - - if {[lindex $last_clicked 0] ne $w} { - toggle_or_diff $w $x $y - return - } - - set pos [split [$w index @$x,$y] .] - set lno [lindex $pos 0] - set lc [lindex $last_clicked 1] - if {$lc < $lno} { - set begin $lc - set end $lno - } else { - set begin $lno - set end $lc - } - - foreach path [lrange $file_lists($w) \ - [expr {$begin - 1}] \ - [expr {$end - 1}]] { - set selected_paths($path) 1 - } - $w tag add in_sel $begin.0 [expr {$end + 1}].0 -} - -###################################################################### -## -## config defaults - -set cursor_ptr arrow -font create font_diff -family Courier -size 10 -font create font_ui -catch { - label .dummy - eval font configure font_ui [font actual [.dummy cget -font]] - destroy .dummy -} - -font create font_uibold -font create font_diffbold - -if {[is_Windows]} { - set M1B Control - set M1T Ctrl -} elseif {[is_MacOSX]} { - set M1B M1 - set M1T Cmd -} else { - set M1B M1 - set M1T M1 -} - -proc apply_config {} { - global repo_config font_descs - - foreach option $font_descs { - set name [lindex $option 0] - set font [lindex $option 1] - if {[catch { - foreach {cn cv} $repo_config(gui.$name) { - font configure $font $cn $cv - } - } err]} { - error_popup "Invalid font specified in gui.$name:\n\n$err" - } - foreach {cn cv} [font configure $font] { - font configure ${font}bold $cn $cv - } - font configure ${font}bold -weight bold - } -} - -set default_config(gui.trustmtime) false -set default_config(gui.pullsummary) true -set default_config(gui.partialinclude) false -set default_config(gui.diffcontext) 5 -set default_config(gui.fontui) [font configure font_ui] -set default_config(gui.fontdiff) [font configure font_diff] -set font_descs { - {fontui font_ui {Main Font}} - {fontdiff font_diff {Diff/Console Font}} -} -load_config 0 -apply_config - -###################################################################### -## -## ui construction - -# -- Menu Bar -# -menu .mbar -tearoff 0 -.mbar add cascade -label Repository -menu .mbar.repository -.mbar add cascade -label Edit -menu .mbar.edit -if {!$single_commit} { - .mbar add cascade -label Branch -menu .mbar.branch -} -.mbar add cascade -label Commit -menu .mbar.commit -if {!$single_commit} { - .mbar add cascade -label Fetch -menu .mbar.fetch - .mbar add cascade -label Pull -menu .mbar.pull - .mbar add cascade -label Push -menu .mbar.push -} -. configure -menu .mbar - -# -- Repository Menu -# -menu .mbar.repository -.mbar.repository add command \ - -label {Visualize Current Branch} \ - -command {do_gitk {}} \ - -font font_ui -if {![is_MacOSX]} { - .mbar.repository add command \ - -label {Visualize All Branches} \ - -command {do_gitk {--all}} \ - -font font_ui -} -.mbar.repository add separator - -if {!$single_commit} { - .mbar.repository add command -label {Compress Database} \ - -command do_gc \ - -font font_ui - - .mbar.repository add command -label {Verify Database} \ - -command do_fsck_objects \ - -font font_ui - - .mbar.repository add separator - - if {[is_Windows]} { - .mbar.repository add command \ - -label {Create Desktop Icon} \ - -command do_windows_shortcut \ - -font font_ui - } elseif {[is_MacOSX]} { - .mbar.repository add command \ - -label {Create Desktop Icon} \ - -command do_macosx_app \ - -font font_ui - } -} - -.mbar.repository add command -label Quit \ - -command do_quit \ - -accelerator $M1T-Q \ - -font font_ui - -# -- Edit Menu -# -menu .mbar.edit -.mbar.edit add command -label Undo \ - -command {catch {[focus] edit undo}} \ - -accelerator $M1T-Z \ - -font font_ui -.mbar.edit add command -label Redo \ - -command {catch {[focus] edit redo}} \ - -accelerator $M1T-Y \ - -font font_ui -.mbar.edit add separator -.mbar.edit add command -label Cut \ - -command {catch {tk_textCut [focus]}} \ - -accelerator $M1T-X \ - -font font_ui -.mbar.edit add command -label Copy \ - -command {catch {tk_textCopy [focus]}} \ - -accelerator $M1T-C \ - -font font_ui -.mbar.edit add command -label Paste \ - -command {catch {tk_textPaste [focus]; [focus] see insert}} \ - -accelerator $M1T-V \ - -font font_ui -.mbar.edit add command -label Delete \ - -command {catch {[focus] delete sel.first sel.last}} \ - -accelerator Del \ - -font font_ui -.mbar.edit add separator -.mbar.edit add command -label {Select All} \ - -command {catch {[focus] tag add sel 0.0 end}} \ - -accelerator $M1T-A \ - -font font_ui - -# -- Branch Menu -# -if {!$single_commit} { - menu .mbar.branch - - .mbar.branch add command -label {Create...} \ - -command do_create_branch \ - -font font_ui - lappend disable_on_lock [list .mbar.branch entryconf \ - [.mbar.branch index last] -state] - - .mbar.branch add command -label {Delete...} \ - -command do_delete_branch \ - -font font_ui - lappend disable_on_lock [list .mbar.branch entryconf \ - [.mbar.branch index last] -state] -} - -# -- Commit Menu -# -menu .mbar.commit - -.mbar.commit add radiobutton \ - -label {New Commit} \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value new \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - -.mbar.commit add radiobutton \ - -label {Amend Last Commit} \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value amend \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - -.mbar.commit add separator - -.mbar.commit add command -label Rescan \ - -command do_rescan \ - -accelerator F5 \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - -.mbar.commit add command -label {Add To Commit} \ - -command do_include_selection \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - -.mbar.commit add command -label {Add All To Commit} \ - -command do_include_all \ - -accelerator $M1T-I \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - -.mbar.commit add command -label {Remove From Commit} \ - -command do_remove_selection \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - -.mbar.commit add command -label {Revert Changes} \ - -command do_revert_selection \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - -.mbar.commit add separator - -.mbar.commit add command -label {Sign Off} \ - -command do_signoff \ - -accelerator $M1T-S \ - -font font_ui - -.mbar.commit add command -label Commit \ - -command do_commit \ - -accelerator $M1T-Return \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - -# -- Transport menus -# -if {!$single_commit} { - menu .mbar.fetch - menu .mbar.pull - menu .mbar.push -} - -if {[is_MacOSX]} { - # -- Apple Menu (Mac OS X only) - # - .mbar add cascade -label Apple -menu .mbar.apple - menu .mbar.apple - - .mbar.apple add command -label "About $appname" \ - -command do_about \ - -font font_ui - .mbar.apple add command -label "$appname Options..." \ - -command do_options \ - -font font_ui -} else { - # -- Edit Menu - # - .mbar.edit add separator - .mbar.edit add command -label {Options...} \ - -command do_options \ - -font font_ui - - # -- Tools Menu - # - if {[file exists /usr/local/miga/lib/gui-miga]} { - proc do_miga {} { - global gitdir ui_status_value - if {![lock_index update]} return - set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""] - set miga_fd [open "|$cmd" r] - fconfigure $miga_fd -blocking 0 - fileevent $miga_fd readable [list miga_done $miga_fd] - set ui_status_value {Running miga...} - } - proc miga_done {fd} { - read $fd 512 - if {[eof $fd]} { - close $fd - unlock_index - rescan [list set ui_status_value {Ready.}] - } - } - .mbar add cascade -label Tools -menu .mbar.tools - menu .mbar.tools - .mbar.tools add command -label "Migrate" \ - -command do_miga \ - -font font_ui - lappend disable_on_lock \ - [list .mbar.tools entryconf [.mbar.tools index last] -state] - } - - # -- Help Menu - # - .mbar add cascade -label Help -menu .mbar.help - menu .mbar.help - - .mbar.help add command -label "About $appname" \ - -command do_about \ - -font font_ui -} - - -# -- Branch Control -# -frame .branch \ - -borderwidth 1 \ - -relief sunken -label .branch.l1 \ - -text {Current Branch:} \ - -anchor w \ - -justify left \ - -font font_ui -label .branch.cb \ - -textvariable current_branch \ - -anchor w \ - -justify left \ - -font font_ui -pack .branch.l1 -side left -pack .branch.cb -side left -fill x -pack .branch -side top -fill x - -# -- Main Window Layout -# -panedwindow .vpane -orient vertical -panedwindow .vpane.files -orient horizontal -.vpane add .vpane.files -sticky nsew -height 100 -width 400 -pack .vpane -anchor n -side top -fill both -expand 1 - -# -- Index File List -# -frame .vpane.files.index -height 100 -width 400 -label .vpane.files.index.title -text {Modified Files} \ - -background green \ - -font font_ui -text $ui_index -background white -borderwidth 0 \ - -width 40 -height 10 \ - -font font_ui \ - -cursor $cursor_ptr \ - -yscrollcommand {.vpane.files.index.sb set} \ - -state disabled -scrollbar .vpane.files.index.sb -command [list $ui_index yview] -pack .vpane.files.index.title -side top -fill x -pack .vpane.files.index.sb -side right -fill y -pack $ui_index -side left -fill both -expand 1 -.vpane.files add .vpane.files.index -sticky nsew - -# -- Other (Add) File List -# -frame .vpane.files.other -height 100 -width 100 -label .vpane.files.other.title -text {Untracked Files} \ - -background red \ - -font font_ui -text $ui_other -background white -borderwidth 0 \ - -width 40 -height 10 \ - -font font_ui \ - -cursor $cursor_ptr \ - -yscrollcommand {.vpane.files.other.sb set} \ - -state disabled -scrollbar .vpane.files.other.sb -command [list $ui_other yview] -pack .vpane.files.other.title -side top -fill x -pack .vpane.files.other.sb -side right -fill y -pack $ui_other -side left -fill both -expand 1 -.vpane.files add .vpane.files.other -sticky nsew - -foreach i [list $ui_index $ui_other] { - $i tag conf in_diff -font font_uibold - $i tag conf in_sel \ - -background [$i cget -foreground] \ - -foreground [$i cget -background] -} -unset i - -# -- Diff and Commit Area -# -frame .vpane.lower -height 300 -width 400 -frame .vpane.lower.commarea -frame .vpane.lower.diff -relief sunken -borderwidth 1 -pack .vpane.lower.commarea -side top -fill x -pack .vpane.lower.diff -side bottom -fill both -expand 1 -.vpane add .vpane.lower -stick nsew - -# -- Commit Area Buttons -# -frame .vpane.lower.commarea.buttons -label .vpane.lower.commarea.buttons.l -text {} \ - -anchor w \ - -justify left \ - -font font_ui -pack .vpane.lower.commarea.buttons.l -side top -fill x -pack .vpane.lower.commarea.buttons -side left -fill y - -button .vpane.lower.commarea.buttons.rescan -text {Rescan} \ - -command do_rescan \ - -font font_ui -pack .vpane.lower.commarea.buttons.rescan -side top -fill x -lappend disable_on_lock \ - {.vpane.lower.commarea.buttons.rescan conf -state} - -button .vpane.lower.commarea.buttons.incall -text {Add All} \ - -command do_include_all \ - -font font_ui -pack .vpane.lower.commarea.buttons.incall -side top -fill x -lappend disable_on_lock \ - {.vpane.lower.commarea.buttons.incall conf -state} - -button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \ - -command do_signoff \ - -font font_ui -pack .vpane.lower.commarea.buttons.signoff -side top -fill x - -button .vpane.lower.commarea.buttons.commit -text {Commit} \ - -command do_commit \ - -font font_ui -pack .vpane.lower.commarea.buttons.commit -side top -fill x -lappend disable_on_lock \ - {.vpane.lower.commarea.buttons.commit conf -state} - -# -- Commit Message Buffer -# -frame .vpane.lower.commarea.buffer -frame .vpane.lower.commarea.buffer.header -set ui_comm .vpane.lower.commarea.buffer.t -set ui_coml .vpane.lower.commarea.buffer.header.l -radiobutton .vpane.lower.commarea.buffer.header.new \ - -text {New Commit} \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value new \ - -font font_ui -lappend disable_on_lock \ - [list .vpane.lower.commarea.buffer.header.new conf -state] -radiobutton .vpane.lower.commarea.buffer.header.amend \ - -text {Amend Last Commit} \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value amend \ - -font font_ui -lappend disable_on_lock \ - [list .vpane.lower.commarea.buffer.header.amend conf -state] -label $ui_coml \ - -anchor w \ - -justify left \ - -font font_ui -proc trace_commit_type {varname args} { - global ui_coml commit_type - switch -glob -- $commit_type { - initial {set txt {Initial Commit Message:}} - amend {set txt {Amended Commit Message:}} - amend-initial {set txt {Amended Initial Commit Message:}} - amend-merge {set txt {Amended Merge Commit Message:}} - merge {set txt {Merge Commit Message:}} - * {set txt {Commit Message:}} - } - $ui_coml conf -text $txt -} -trace add variable commit_type write trace_commit_type -pack $ui_coml -side left -fill x -pack .vpane.lower.commarea.buffer.header.amend -side right -pack .vpane.lower.commarea.buffer.header.new -side right - -text $ui_comm -background white -borderwidth 1 \ - -undo true \ - -maxundo 20 \ - -autoseparators true \ - -relief sunken \ - -width 75 -height 9 -wrap none \ - -font font_diff \ - -yscrollcommand {.vpane.lower.commarea.buffer.sby set} -scrollbar .vpane.lower.commarea.buffer.sby \ - -command [list $ui_comm yview] -pack .vpane.lower.commarea.buffer.header -side top -fill x -pack .vpane.lower.commarea.buffer.sby -side right -fill y -pack $ui_comm -side left -fill y -pack .vpane.lower.commarea.buffer -side left -fill y - -# -- Commit Message Buffer Context Menu -# -set ctxm .vpane.lower.commarea.buffer.ctxm -menu $ctxm -tearoff 0 -$ctxm add command \ - -label {Cut} \ - -font font_ui \ - -command {tk_textCut $ui_comm} -$ctxm add command \ - -label {Copy} \ - -font font_ui \ - -command {tk_textCopy $ui_comm} -$ctxm add command \ - -label {Paste} \ - -font font_ui \ - -command {tk_textPaste $ui_comm} -$ctxm add command \ - -label {Delete} \ - -font font_ui \ - -command {$ui_comm delete sel.first sel.last} -$ctxm add separator -$ctxm add command \ - -label {Select All} \ - -font font_ui \ - -command {$ui_comm tag add sel 0.0 end} -$ctxm add command \ - -label {Copy All} \ - -font font_ui \ - -command { - $ui_comm tag add sel 0.0 end - tk_textCopy $ui_comm - $ui_comm tag remove sel 0.0 end - } -$ctxm add separator -$ctxm add command \ - -label {Sign Off} \ - -font font_ui \ - -command do_signoff -bind_button3 $ui_comm "tk_popup $ctxm %X %Y" - -# -- Diff Header -# -set current_diff {} -set diff_actions [list] -proc trace_current_diff {varname args} { - global current_diff diff_actions file_states - if {$current_diff eq {}} { - set s {} - set f {} - set p {} - set o disabled - } else { - set p $current_diff - set s [mapdesc [lindex $file_states($p) 0] $p] - set f {File:} - set p [escape_path $p] - set o normal - } - - .vpane.lower.diff.header.status configure -text $s - .vpane.lower.diff.header.file configure -text $f - .vpane.lower.diff.header.path configure -text $p - foreach w $diff_actions { - uplevel #0 $w $o - } -} -trace add variable current_diff write trace_current_diff - -frame .vpane.lower.diff.header -background orange -label .vpane.lower.diff.header.status \ - -background orange \ - -width $max_status_desc \ - -anchor w \ - -justify left \ - -font font_ui -label .vpane.lower.diff.header.file \ - -background orange \ - -anchor w \ - -justify left \ - -font font_ui -label .vpane.lower.diff.header.path \ - -background orange \ - -anchor w \ - -justify left \ - -font font_ui -pack .vpane.lower.diff.header.status -side left -pack .vpane.lower.diff.header.file -side left -pack .vpane.lower.diff.header.path -fill x -set ctxm .vpane.lower.diff.header.ctxm -menu $ctxm -tearoff 0 -$ctxm add command \ - -label {Copy} \ - -font font_ui \ - -command { - clipboard clear - clipboard append \ - -format STRING \ - -type STRING \ - -- $current_diff - } -lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] -bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y" - -# -- Diff Body -# -frame .vpane.lower.diff.body -set ui_diff .vpane.lower.diff.body.t -text $ui_diff -background white -borderwidth 0 \ - -width 80 -height 15 -wrap none \ - -font font_diff \ - -xscrollcommand {.vpane.lower.diff.body.sbx set} \ - -yscrollcommand {.vpane.lower.diff.body.sby set} \ - -state disabled -scrollbar .vpane.lower.diff.body.sbx -orient horizontal \ - -command [list $ui_diff xview] -scrollbar .vpane.lower.diff.body.sby -orient vertical \ - -command [list $ui_diff yview] -pack .vpane.lower.diff.body.sbx -side bottom -fill x -pack .vpane.lower.diff.body.sby -side right -fill y -pack $ui_diff -side left -fill both -expand 1 -pack .vpane.lower.diff.header -side top -fill x -pack .vpane.lower.diff.body -side bottom -fill both -expand 1 - -$ui_diff tag conf d_@ -font font_diffbold -$ui_diff tag conf d_+ -foreground blue -$ui_diff tag conf d_- -foreground red -$ui_diff tag conf d_++ -foreground {#00a000} -$ui_diff tag conf d_-- -foreground {#a000a0} -$ui_diff tag conf d_+- \ - -foreground red \ - -background {light goldenrod yellow} -$ui_diff tag conf d_-+ \ - -foreground blue \ - -background azure2 - -# -- Diff Body Context Menu -# -set ctxm .vpane.lower.diff.body.ctxm -menu $ctxm -tearoff 0 -$ctxm add command \ - -label {Copy} \ - -font font_ui \ - -command {tk_textCopy $ui_diff} -lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] -$ctxm add command \ - -label {Select All} \ - -font font_ui \ - -command {$ui_diff tag add sel 0.0 end} -lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] -$ctxm add command \ - -label {Copy All} \ - -font font_ui \ - -command { - $ui_diff tag add sel 0.0 end - tk_textCopy $ui_diff - $ui_diff tag remove sel 0.0 end - } -lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] -$ctxm add separator -$ctxm add command \ - -label {Decrease Font Size} \ - -font font_ui \ - -command {incr_font_size font_diff -1} -lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] -$ctxm add command \ - -label {Increase Font Size} \ - -font font_ui \ - -command {incr_font_size font_diff 1} -lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] -$ctxm add separator -$ctxm add command \ - -label {Show Less Context} \ - -font font_ui \ - -command {if {$repo_config(gui.diffcontext) >= 2} { - incr repo_config(gui.diffcontext) -1 - reshow_diff - }} -lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] -$ctxm add command \ - -label {Show More Context} \ - -font font_ui \ - -command { - incr repo_config(gui.diffcontext) - reshow_diff - } -lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] -$ctxm add separator -$ctxm add command -label {Options...} \ - -font font_ui \ - -command do_options -bind_button3 $ui_diff "tk_popup $ctxm %X %Y" - -# -- Status Bar -# -set ui_status_value {Initializing...} -label .status -textvariable ui_status_value \ - -anchor w \ - -justify left \ - -borderwidth 1 \ - -relief sunken \ - -font font_ui -pack .status -anchor w -side bottom -fill x - -# -- Load geometry -# -catch { -set gm $repo_config(gui.geometry) -wm geometry . [lindex $gm 0] -.vpane sash place 0 \ - [lindex [.vpane sash coord 0] 0] \ - [lindex $gm 1] -.vpane.files sash place 0 \ - [lindex $gm 2] \ - [lindex [.vpane.files sash coord 0] 1] -unset gm -} - -# -- Key Bindings -# -bind $ui_comm <$M1B-Key-Return> {do_commit;break} -bind $ui_comm <$M1B-Key-i> {do_include_all;break} -bind $ui_comm <$M1B-Key-I> {do_include_all;break} -bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break} -bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break} -bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break} -bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break} -bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break} -bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break} -bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break} -bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break} - -bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break} -bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break} -bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break} -bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break} -bind $ui_diff <$M1B-Key-v> {break} -bind $ui_diff <$M1B-Key-V> {break} -bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break} -bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break} -bind $ui_diff {catch {%W yview scroll -1 units};break} -bind $ui_diff {catch {%W yview scroll 1 units};break} -bind $ui_diff {catch {%W xview scroll -1 units};break} -bind $ui_diff {catch {%W xview scroll 1 units};break} - -bind . do_quit -bind all do_rescan -bind all <$M1B-Key-r> do_rescan -bind all <$M1B-Key-R> do_rescan -bind . <$M1B-Key-s> do_signoff -bind . <$M1B-Key-S> do_signoff -bind . <$M1B-Key-i> do_include_all -bind . <$M1B-Key-I> do_include_all -bind . <$M1B-Key-Return> do_commit -bind all <$M1B-Key-q> do_quit -bind all <$M1B-Key-Q> do_quit -bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} -bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} -foreach i [list $ui_index $ui_other] { - bind $i "toggle_or_diff $i %x %y; break" - bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break" - bind $i "add_range_to_selection $i %x %y; break" -} -unset i - -set file_lists($ui_index) [list] -set file_lists($ui_other) [list] - -set HEAD {} -set PARENT {} -set MERGE_HEAD [list] -set commit_type {} -set empty_tree {} -set current_branch {} -set current_diff {} -set selected_commit_type new - -wm title . "$appname ([file normalize [file dirname $gitdir]])" -focus -force $ui_comm - -# -- Warn the user about environmental problems. Cygwin's Tcl -# does *not* pass its env array onto any processes it spawns. -# This means that git processes get none of our environment. -# -if {[is_Windows]} { - set ignored_env 0 - set suggest_user {} - set msg "Possible environment issues exist. - -The following environment variables are probably -going to be ignored by any Git subprocess run -by $appname: - -" - foreach name [array names env] { - switch -regexp -- $name { - {^GIT_INDEX_FILE$} - - {^GIT_OBJECT_DIRECTORY$} - - {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} - - {^GIT_DIFF_OPTS$} - - {^GIT_EXTERNAL_DIFF$} - - {^GIT_PAGER$} - - {^GIT_TRACE$} - - {^GIT_CONFIG$} - - {^GIT_CONFIG_LOCAL$} - - {^GIT_(AUTHOR|COMMITTER)_DATE$} { - append msg " - $name\n" - incr ignored_env - } - {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} { - append msg " - $name\n" - incr ignored_env - set suggest_user $name - } - } - } - if {$ignored_env > 0} { - append msg " -This is due to a known issue with the -Tcl binary distributed by Cygwin." - - if {$suggest_user ne {}} { - append msg " - -A good replacement for $suggest_user -is placing values for the user.name and -user.email settings into your personal -~/.gitconfig file. -" - } - warn_popup $msg - } - unset ignored_env msg suggest_user name -} - -# -- Only initialize complex UI if we are going to stay running. -# -if {!$single_commit} { - load_all_remotes - load_all_heads - - populate_branch_menu .mbar.branch - populate_fetch_menu .mbar.fetch - populate_pull_menu .mbar.pull - populate_push_menu .mbar.push -} - -lock_index begin-read -after 1 do_rescan diff --git a/git-gui.sh b/git-gui.sh new file mode 100755 index 0000000000..0770ad03f9 --- /dev/null +++ b/git-gui.sh @@ -0,0 +1,3773 @@ +#!/bin/sh +# Tcl ignores the next line -*- tcl -*- \ +exec wish "$0" -- "$@" + +set copyright { +Copyright © 2006, 2007 Shawn Pearce, Paul Mackerras. + +All rights reserved. + +This program is free software; it may be used, copied, modified +and distributed under the terms of the GNU General Public Licence, +either version 2, or (at your option) any later version.} + +set appvers {@@GITGUI_VERSION@@} +set appname [lindex [file split $argv0] end] +set gitdir {} + +###################################################################### +## +## config + +proc is_many_config {name} { + switch -glob -- $name { + remote.*.fetch - + remote.*.push + {return 1} + * + {return 0} + } +} + +proc load_config {include_global} { + global repo_config global_config default_config + + array unset global_config + if {$include_global} { + catch { + set fd_rc [open "| git repo-config --global --list" r] + while {[gets $fd_rc line] >= 0} { + if {[regexp {^([^=]+)=(.*)$} $line line name value]} { + if {[is_many_config $name]} { + lappend global_config($name) $value + } else { + set global_config($name) $value + } + } + } + close $fd_rc + } + } + + array unset repo_config + catch { + set fd_rc [open "| git repo-config --list" r] + while {[gets $fd_rc line] >= 0} { + if {[regexp {^([^=]+)=(.*)$} $line line name value]} { + if {[is_many_config $name]} { + lappend repo_config($name) $value + } else { + set repo_config($name) $value + } + } + } + close $fd_rc + } + + foreach name [array names default_config] { + if {[catch {set v $global_config($name)}]} { + set global_config($name) $default_config($name) + } + if {[catch {set v $repo_config($name)}]} { + set repo_config($name) $default_config($name) + } + } +} + +proc save_config {} { + global default_config font_descs + global repo_config global_config + global repo_config_new global_config_new + + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + font configure $font \ + -family $global_config_new(gui.$font^^family) \ + -size $global_config_new(gui.$font^^size) + font configure ${font}bold \ + -family $global_config_new(gui.$font^^family) \ + -size $global_config_new(gui.$font^^size) + set global_config_new(gui.$name) [font configure $font] + unset global_config_new(gui.$font^^family) + unset global_config_new(gui.$font^^size) + } + + foreach name [array names default_config] { + set value $global_config_new($name) + if {$value ne $global_config($name)} { + if {$value eq $default_config($name)} { + catch {exec git repo-config --global --unset $name} + } else { + regsub -all "\[{}\]" $value {"} value + exec git repo-config --global $name $value + } + set global_config($name) $value + if {$value eq $repo_config($name)} { + catch {exec git repo-config --unset $name} + set repo_config($name) $value + } + } + } + + foreach name [array names default_config] { + set value $repo_config_new($name) + if {$value ne $repo_config($name)} { + if {$value eq $global_config($name)} { + catch {exec git repo-config --unset $name} + } else { + regsub -all "\[{}\]" $value {"} value + exec git repo-config $name $value + } + set repo_config($name) $value + } + } +} + +proc error_popup {msg} { + global gitdir appname + + set title $appname + if {$gitdir ne {}} { + append title { (} + append title [lindex \ + [file split [file normalize [file dirname $gitdir]]] \ + end] + append title {)} + } + set cmd [list tk_messageBox \ + -icon error \ + -type ok \ + -title "$title: error" \ + -message $msg] + if {[winfo ismapped .]} { + lappend cmd -parent . + } + eval $cmd +} + +proc warn_popup {msg} { + global gitdir appname + + set title $appname + if {$gitdir ne {}} { + append title { (} + append title [lindex \ + [file split [file normalize [file dirname $gitdir]]] \ + end] + append title {)} + } + set cmd [list tk_messageBox \ + -icon warning \ + -type ok \ + -title "$title: warning" \ + -message $msg] + if {[winfo ismapped .]} { + lappend cmd -parent . + } + eval $cmd +} + +proc info_popup {msg} { + global gitdir appname + + set title $appname + if {$gitdir ne {}} { + append title { (} + append title [lindex \ + [file split [file normalize [file dirname $gitdir]]] \ + end] + append title {)} + } + tk_messageBox \ + -parent . \ + -icon info \ + -type ok \ + -title $title \ + -message $msg +} + +###################################################################### +## +## repository setup + +if { [catch {set gitdir $env(GIT_DIR)}] + && [catch {set gitdir [exec git rev-parse --git-dir]} err]} { + catch {wm withdraw .} + error_popup "Cannot find the git directory:\n\n$err" + exit 1 +} +if {![file isdirectory $gitdir]} { + catch {wm withdraw .} + error_popup "Git directory not found:\n\n$gitdir" + exit 1 +} +if {[lindex [file split $gitdir] end] ne {.git}} { + catch {wm withdraw .} + error_popup "Cannot use funny .git directory:\n\n$gitdir" + exit 1 +} +if {[catch {cd [file dirname $gitdir]} err]} { + catch {wm withdraw .} + error_popup "No working directory [file dirname $gitdir]:\n\n$err" + exit 1 +} + +set single_commit 0 +if {$appname eq {git-citool}} { + set single_commit 1 +} + +###################################################################### +## +## task management + +set rescan_active 0 +set diff_active 0 +set last_clicked {} + +set disable_on_lock [list] +set index_lock_type none + +proc lock_index {type} { + global index_lock_type disable_on_lock + + if {$index_lock_type eq {none}} { + set index_lock_type $type + foreach w $disable_on_lock { + uplevel #0 $w disabled + } + return 1 + } elseif {$index_lock_type eq "begin-$type"} { + set index_lock_type $type + return 1 + } + return 0 +} + +proc unlock_index {} { + global index_lock_type disable_on_lock + + set index_lock_type none + foreach w $disable_on_lock { + uplevel #0 $w normal + } +} + +###################################################################### +## +## status + +proc repository_state {ctvar hdvar mhvar} { + global gitdir current_branch + upvar $ctvar ct $hdvar hd $mhvar mh + + set mh [list] + + if {[catch {set current_branch [exec git symbolic-ref HEAD]}]} { + set current_branch {} + } else { + regsub ^refs/((heads|tags|remotes)/)? \ + $current_branch \ + {} \ + current_branch + } + + if {[catch {set hd [exec git rev-parse --verify HEAD]}]} { + set hd {} + set ct initial + return + } + + set merge_head [file join $gitdir MERGE_HEAD] + if {[file exists $merge_head]} { + set ct merge + set fd_mh [open $merge_head r] + while {[gets $fd_mh line] >= 0} { + lappend mh $line + } + close $fd_mh + return + } + + set ct normal +} + +proc PARENT {} { + global PARENT empty_tree + + set p [lindex $PARENT 0] + if {$p ne {}} { + return $p + } + if {$empty_tree eq {}} { + set empty_tree [exec git mktree << {}] + } + return $empty_tree +} + +proc rescan {after} { + global HEAD PARENT MERGE_HEAD commit_type + global ui_index ui_other ui_status_value ui_comm + global rescan_active file_states + global repo_config + + if {$rescan_active > 0 || ![lock_index read]} return + + repository_state newType newHEAD newMERGE_HEAD + if {[string match amend* $commit_type] + && $newType eq {normal} + && $newHEAD eq $HEAD} { + } else { + set HEAD $newHEAD + set PARENT $newHEAD + set MERGE_HEAD $newMERGE_HEAD + set commit_type $newType + } + + array unset file_states + + if {![$ui_comm edit modified] + || [string trim [$ui_comm get 0.0 end]] eq {}} { + if {[load_message GITGUI_MSG]} { + } elseif {[load_message MERGE_MSG]} { + } elseif {[load_message SQUASH_MSG]} { + } + $ui_comm edit reset + $ui_comm edit modified false + } + + if {$repo_config(gui.trustmtime) eq {true}} { + rescan_stage2 {} $after + } else { + set rescan_active 1 + set ui_status_value {Refreshing file status...} + set cmd [list git update-index] + lappend cmd -q + lappend cmd --unmerged + lappend cmd --ignore-missing + lappend cmd --refresh + set fd_rf [open "| $cmd" r] + fconfigure $fd_rf -blocking 0 -translation binary + fileevent $fd_rf readable \ + [list rescan_stage2 $fd_rf $after] + } +} + +proc rescan_stage2 {fd after} { + global gitdir ui_status_value + global rescan_active buf_rdi buf_rdf buf_rlo + + if {$fd ne {}} { + read $fd + if {![eof $fd]} return + close $fd + } + + set ls_others [list | git ls-files --others -z \ + --exclude-per-directory=.gitignore] + set info_exclude [file join $gitdir info exclude] + if {[file readable $info_exclude]} { + lappend ls_others "--exclude-from=$info_exclude" + } + + set buf_rdi {} + set buf_rdf {} + set buf_rlo {} + + set rescan_active 3 + set ui_status_value {Scanning for modified files ...} + set fd_di [open "| git diff-index --cached -z [PARENT]" r] + set fd_df [open "| git diff-files -z" r] + set fd_lo [open $ls_others r] + + fconfigure $fd_di -blocking 0 -translation binary + fconfigure $fd_df -blocking 0 -translation binary + fconfigure $fd_lo -blocking 0 -translation binary + fileevent $fd_di readable [list read_diff_index $fd_di $after] + fileevent $fd_df readable [list read_diff_files $fd_df $after] + fileevent $fd_lo readable [list read_ls_others $fd_lo $after] +} + +proc load_message {file} { + global gitdir ui_comm + + set f [file join $gitdir $file] + if {[file isfile $f]} { + if {[catch {set fd [open $f r]}]} { + return 0 + } + set content [string trim [read $fd]] + close $fd + $ui_comm delete 0.0 end + $ui_comm insert end $content + return 1 + } + return 0 +} + +proc read_diff_index {fd after} { + global buf_rdi + + append buf_rdi [read $fd] + set c 0 + set n [string length $buf_rdi] + while {$c < $n} { + set z1 [string first "\0" $buf_rdi $c] + if {$z1 == -1} break + incr z1 + set z2 [string first "\0" $buf_rdi $z1] + if {$z2 == -1} break + + incr c + set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }] + merge_state \ + [string range $buf_rdi $z1 [expr {$z2 - 1}]] \ + [lindex $i 4]? \ + [list [lindex $i 0] [lindex $i 2]] \ + [list] + set c $z2 + incr c + } + if {$c < $n} { + set buf_rdi [string range $buf_rdi $c end] + } else { + set buf_rdi {} + } + + rescan_done $fd buf_rdi $after +} + +proc read_diff_files {fd after} { + global buf_rdf + + append buf_rdf [read $fd] + set c 0 + set n [string length $buf_rdf] + while {$c < $n} { + set z1 [string first "\0" $buf_rdf $c] + if {$z1 == -1} break + incr z1 + set z2 [string first "\0" $buf_rdf $z1] + if {$z2 == -1} break + + incr c + set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }] + merge_state \ + [string range $buf_rdf $z1 [expr {$z2 - 1}]] \ + ?[lindex $i 4] \ + [list] \ + [list [lindex $i 0] [lindex $i 2]] + set c $z2 + incr c + } + if {$c < $n} { + set buf_rdf [string range $buf_rdf $c end] + } else { + set buf_rdf {} + } + + rescan_done $fd buf_rdf $after +} + +proc read_ls_others {fd after} { + global buf_rlo + + append buf_rlo [read $fd] + set pck [split $buf_rlo "\0"] + set buf_rlo [lindex $pck end] + foreach p [lrange $pck 0 end-1] { + merge_state $p ?O + } + rescan_done $fd buf_rlo $after +} + +proc rescan_done {fd buf after} { + global rescan_active + global file_states repo_config + upvar $buf to_clear + + if {![eof $fd]} return + set to_clear {} + close $fd + if {[incr rescan_active -1] > 0} return + + prune_selection + unlock_index + display_all_files + + if {$repo_config(gui.partialinclude) ne {true}} { + set pathList [list] + foreach path [array names file_states] { + switch -- [lindex $file_states($path) 0] { + A? - + M? {lappend pathList $path} + } + } + if {$pathList ne {}} { + update_index \ + "Updating included files" \ + $pathList \ + [concat {reshow_diff;} $after] + return + } + } + + reshow_diff + uplevel #0 $after +} + +proc prune_selection {} { + global file_states selected_paths + + foreach path [array names selected_paths] { + if {[catch {set still_here $file_states($path)}]} { + unset selected_paths($path) + } + } +} + +###################################################################### +## +## diff + +proc clear_diff {} { + global ui_diff current_diff ui_index ui_other + + $ui_diff conf -state normal + $ui_diff delete 0.0 end + $ui_diff conf -state disabled + + set current_diff {} + + $ui_index tag remove in_diff 0.0 end + $ui_other tag remove in_diff 0.0 end +} + +proc reshow_diff {} { + global current_diff ui_status_value file_states + + if {$current_diff eq {} + || [catch {set s $file_states($current_diff)}]} { + clear_diff + } else { + show_diff $current_diff + } +} + +proc handle_empty_diff {} { + global current_diff file_states file_lists + + set path $current_diff + set s $file_states($path) + if {[lindex $s 0] ne {_M}} return + + info_popup "No differences detected. + +[short_path $path] has no changes. + +The modification date of this file was updated +by another application and you currently have +the Trust File Modification Timestamps option +enabled, so Git did not automatically detect +that there are no content differences in this +file. + +This file will now be removed from the modified +files list, to prevent possible confusion. +" + if {[catch {exec git update-index -- $path} err]} { + error_popup "Failed to refresh index:\n\n$err" + } + + clear_diff + set old_w [mapcol [lindex $file_states($path) 0] $path] + set lno [lsearch -sorted $file_lists($old_w) $path] + if {$lno >= 0} { + set file_lists($old_w) \ + [lreplace $file_lists($old_w) $lno $lno] + incr lno + $old_w conf -state normal + $old_w delete $lno.0 [expr {$lno + 1}].0 + $old_w conf -state disabled + } +} + +proc show_diff {path {w {}} {lno {}}} { + global file_states file_lists + global is_3way_diff diff_active repo_config + global ui_diff current_diff ui_status_value + + if {$diff_active || ![lock_index read]} return + + clear_diff + if {$w eq {} || $lno == {}} { + foreach w [array names file_lists] { + set lno [lsearch -sorted $file_lists($w) $path] + if {$lno >= 0} { + incr lno + break + } + } + } + if {$w ne {} && $lno >= 1} { + $w tag add in_diff $lno.0 [expr {$lno + 1}].0 + } + + set s $file_states($path) + set m [lindex $s 0] + set is_3way_diff 0 + set diff_active 1 + set current_diff $path + set ui_status_value "Loading diff of [escape_path $path]..." + + set cmd [list | git diff-index] + lappend cmd --no-color + if {$repo_config(gui.diffcontext) > 0} { + lappend cmd "-U$repo_config(gui.diffcontext)" + } + lappend cmd -p + + switch $m { + MM { + lappend cmd -c + } + _O { + if {[catch { + set fd [open $path r] + set content [read $fd] + close $fd + } err ]} { + set diff_active 0 + unlock_index + set ui_status_value "Unable to display [escape_path $path]" + error_popup "Error loading file:\n\n$err" + return + } + $ui_diff conf -state normal + $ui_diff insert end $content + $ui_diff conf -state disabled + set diff_active 0 + unlock_index + set ui_status_value {Ready.} + return + } + } + + lappend cmd [PARENT] + lappend cmd -- + lappend cmd $path + + if {[catch {set fd [open $cmd r]} err]} { + set diff_active 0 + unlock_index + set ui_status_value "Unable to display [escape_path $path]" + error_popup "Error loading diff:\n\n$err" + return + } + + fconfigure $fd -blocking 0 -translation auto + fileevent $fd readable [list read_diff $fd] +} + +proc read_diff {fd} { + global ui_diff ui_status_value is_3way_diff diff_active + global repo_config + + $ui_diff conf -state normal + while {[gets $fd line] >= 0} { + # -- Cleanup uninteresting diff header lines. + # + if {[string match {diff --git *} $line]} continue + if {[string match {diff --combined *} $line]} continue + if {[string match {--- *} $line]} continue + if {[string match {+++ *} $line]} continue + if {$line eq {deleted file mode 120000}} { + set line "deleted symlink" + } + + # -- Automatically detect if this is a 3 way diff. + # + if {[string match {@@@ *} $line]} {set is_3way_diff 1} + + # -- Reformat a 3 way diff, 'cause its too weird. + # + if {$is_3way_diff} { + set op [string range $line 0 1] + switch -- $op { + {@@} {set tags d_@} + {++} {set tags d_+ ; set op { +}} + {--} {set tags d_- ; set op { -}} + { +} {set tags d_++; set op {++}} + { -} {set tags d_--; set op {--}} + {+ } {set tags d_-+; set op {-+}} + {- } {set tags d_+-; set op {+-}} + default {set tags {}} + } + set line [string replace $line 0 1 $op] + } else { + switch -- [string index $line 0] { + @ {set tags d_@} + + {set tags d_+} + - {set tags d_-} + default {set tags {}} + } + } + $ui_diff insert end $line $tags + $ui_diff insert end "\n" $tags + } + $ui_diff conf -state disabled + + if {[eof $fd]} { + close $fd + set diff_active 0 + unlock_index + set ui_status_value {Ready.} + + if {$repo_config(gui.trustmtime) eq {true} + && [$ui_diff index end] eq {2.0}} { + handle_empty_diff + } + } +} + +###################################################################### +## +## commit + +proc load_last_commit {} { + global HEAD PARENT MERGE_HEAD commit_type ui_comm + + if {[llength $PARENT] == 0} { + error_popup {There is nothing 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}} { + error_popup {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. +} + return + } + + set msg {} + set parents [list] + if {[catch { + set fd [open "| git cat-file commit $curHEAD" r] + while {[gets $fd line] > 0} { + if {[string match {parent *} $line]} { + lappend parents [string range $line 7 end] + } + } + set msg [string trim [read $fd]] + close $fd + } err]} { + error_popup "Error loading commit data for amend:\n\n$err" + return + } + + set HEAD $curHEAD + set PARENT $parents + set MERGE_HEAD [list] + switch -- [llength $parents] { + 0 {set commit_type amend-initial} + 1 {set commit_type amend} + default {set commit_type amend-merge} + } + + $ui_comm delete 0.0 end + $ui_comm insert end $msg + $ui_comm edit reset + $ui_comm edit modified false + rescan {set ui_status_value {Ready.}} +} + +proc create_new_commit {} { + global commit_type ui_comm + + set commit_type normal + $ui_comm delete 0.0 end + $ui_comm edit reset + $ui_comm edit modified false + rescan {set ui_status_value {Ready.}} +} + +set GIT_COMMITTER_IDENT {} + +proc committer_ident {} { + global GIT_COMMITTER_IDENT + + if {$GIT_COMMITTER_IDENT eq {}} { + if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} { + error_popup "Unable to obtain your identity:\n\n$err" + return {} + } + if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \ + $me me GIT_COMMITTER_IDENT]} { + error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me" + return {} + } + } + + return $GIT_COMMITTER_IDENT +} + +proc commit_tree {} { + global HEAD commit_type file_states ui_comm repo_config + + if {![lock_index update]} return + if {[committer_ident] eq {}} return + + # -- Our in memory state should match the repository. + # + repository_state curType curHEAD curMERGE_HEAD + if {[string match amend* $commit_type] + && $curType eq {normal} + && $curHEAD eq $HEAD} { + } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} { + info_popup {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. +} + unlock_index + rescan {set ui_status_value {Ready.}} + return + } + + # -- At least one file should differ in the index. + # + set files_ready 0 + foreach path [array names file_states] { + switch -glob -- [lindex $file_states($path) 0] { + _? {continue} + A? - + D? - + M? {set files_ready 1; break} + U? { + error_popup "Unmerged files cannot be committed. + +File [short_path $path] has merge conflicts. +You must resolve them and include the file before committing. +" + unlock_index + return + } + default { + error_popup "Unknown file state [lindex $s 0] detected. + +File [short_path $path] cannot be committed by this program. +" + } + } + } + if {!$files_ready} { + error_popup {No included files to commit. + +You must include at least 1 file before you can commit. +} + unlock_index + return + } + + # -- A message is required. + # + set msg [string trim [$ui_comm get 1.0 end]] + if {$msg eq {}} { + error_popup {Please supply a commit message. + +A good commit message has the following format: + +- First line: Describe in one sentance what you did. +- Second line: Blank +- Remaining lines: Describe why this change is good. +} + unlock_index + return + } + + # -- Update included files if partialincludes are off. + # + if {$repo_config(gui.partialinclude) ne {true}} { + set pathList [list] + foreach path [array names file_states] { + switch -glob -- [lindex $file_states($path) 0] { + A? - + M? {lappend pathList $path} + } + } + if {$pathList ne {}} { + unlock_index + update_index \ + "Updating included files" \ + $pathList \ + [concat {lock_index update;} \ + [list commit_prehook $curHEAD $msg]] + return + } + } + + commit_prehook $curHEAD $msg +} + +proc commit_prehook {curHEAD msg} { + global gitdir ui_status_value pch_error + + set pchook [file join $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_Windows] && [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 + return + } + + set ui_status_value {Calling pre-commit hook...} + set pch_error {} + set fd_ph [open "| $pchook" r] + fconfigure $fd_ph -blocking 0 -translation binary + fileevent $fd_ph readable \ + [list commit_prehook_wait $fd_ph $curHEAD $msg] +} + +proc commit_prehook_wait {fd_ph curHEAD msg} { + global pch_error ui_status_value + + 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.} + hook_failed_popup pre-commit $pch_error + unlock_index + } else { + commit_writetree $curHEAD $msg + } + set pch_error {} + return + } + fconfigure $fd_ph -blocking 0 +} + +proc commit_writetree {curHEAD msg} { + global ui_status_value + + set ui_status_value {Committing changes...} + set fd_wt [open "| git write-tree" r] + fileevent $fd_wt readable \ + [list commit_committree $fd_wt $curHEAD $msg] +} + +proc commit_committree {fd_wt curHEAD msg} { + global HEAD PARENT MERGE_HEAD commit_type + global single_commit gitdir + global ui_status_value ui_comm selected_commit_type + global file_states selected_paths rescan_active + + 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.} + unlock_index + return + } + + # -- Create the commit. + # + set cmd [list git commit-tree $tree_id] + set parents [concat $PARENT $MERGE_HEAD] + if {[llength $parents] > 0} { + foreach p $parents { + lappend cmd -p $p + } + } else { + # git commit-tree writes to stderr during initial commit. + lappend cmd 2>/dev/null + } + lappend cmd << $msg + if {[catch {set cmt_id [eval exec $cmd]} err]} { + error_popup "commit-tree failed:\n\n$err" + set ui_status_value {Commit failed.} + unlock_index + return + } + + # -- Update the HEAD ref. + # + set reflogm commit + if {$commit_type ne {normal}} { + append reflogm " ($commit_type)" + } + set i [string first "\n" $msg] + if {$i >= 0} { + append reflogm {: } [string range $msg 0 [expr {$i - 1}]] + } else { + append reflogm {: } $msg + } + set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD] + if {[catch {eval exec $cmd} err]} { + error_popup "update-ref failed:\n\n$err" + set ui_status_value {Commit failed.} + unlock_index + return + } + + # -- Cleanup after ourselves. + # + catch {file delete [file join $gitdir MERGE_HEAD]} + catch {file delete [file join $gitdir MERGE_MSG]} + catch {file delete [file join $gitdir SQUASH_MSG]} + catch {file delete [file join $gitdir GITGUI_MSG]} + + # -- Let rerere do its thing. + # + if {[file isdirectory [file join $gitdir rr-cache]]} { + catch {exec git rerere} + } + + # -- Run the post-commit hook. + # + set pchook [file join $gitdir hooks post-commit] + if {[is_Windows] && [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 &} + } + + $ui_comm delete 0.0 end + $ui_comm edit reset + $ui_comm edit modified false + + if {$single_commit} do_quit + + # -- Update in memory status + # + set selected_commit_type new + set commit_type normal + set HEAD $cmt_id + set PARENT $cmt_id + set MERGE_HEAD [list] + + foreach path [array names file_states] { + set s $file_states($path) + set m [lindex $s 0] + switch -glob -- $m { + _O - + _M - + _D {continue} + __ - + A_ - + M_ - + DD { + unset file_states($path) + catch {unset selected_paths($path)} + } + DO { + set file_states($path) [list _O [lindex $s 1] {} {}] + } + AM - + AD - + MM - + MD - + DM { + set file_states($path) [list \ + _[string index $m 1] \ + [lindex $s 1] \ + [lindex $s 3] \ + {}] + } + } + } + + display_all_files + unlock_index + reshow_diff + set ui_status_value \ + "Changes committed as [string range $cmt_id 0 7]." +} + +###################################################################### +## +## fetch pull push + +proc fetch_from {remote} { + set w [new_console "fetch $remote" \ + "Fetching new changes from $remote"] + set cmd [list git fetch] + lappend cmd $remote + console_exec $w $cmd +} + +proc pull_remote {remote branch} { + global HEAD commit_type file_states repo_config + + if {![lock_index update]} return + + # -- Our in memory state should match the repository. + # + repository_state curType curHEAD curMERGE_HEAD + if {$commit_type ne $curType || $HEAD ne $curHEAD} { + info_popup {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 a pull operation can be started. + +The rescan will be automatically started now. +} + unlock_index + rescan {set ui_status_value {Ready.}} + return + } + + # -- No differences should exist before a pull. + # + if {[array size file_states] != 0} { + error_popup {Uncommitted but modified files are present. + +You should not perform a pull with unmodified +files in your working directory as Git will be +unable to recover from an incorrect merge. + +You should commit or revert all changes before +starting a pull operation. +} + unlock_index + return + } + + set w [new_console "pull $remote $branch" \ + "Pulling new changes from branch $branch in $remote"] + set cmd [list git pull] + if {$repo_config(gui.pullsummary) eq {false}} { + lappend cmd --no-summary + } + lappend cmd $remote + lappend cmd $branch + console_exec $w $cmd [list post_pull_remote $remote $branch] +} + +proc post_pull_remote {remote branch success} { + global HEAD PARENT MERGE_HEAD commit_type selected_commit_type + global ui_status_value + + unlock_index + if {$success} { + repository_state commit_type HEAD MERGE_HEAD + set PARENT $HEAD + set selected_commit_type new + set ui_status_value "Pulling $branch from $remote complete." + } else { + rescan [list set ui_status_value \ + "Conflicts detected while pulling $branch from $remote."] + } +} + +proc push_to {remote} { + set w [new_console "push $remote" \ + "Pushing changes to $remote"] + set cmd [list git push] + lappend cmd $remote + console_exec $w $cmd +} + +###################################################################### +## +## ui helpers + +proc mapcol {state path} { + global all_cols ui_other + + if {[catch {set r $all_cols($state)}]} { + puts "error: no column for state={$state} $path" + return $ui_other + } + return $r +} + +proc mapicon {state path} { + global all_icons + + if {[catch {set r $all_icons($state)}]} { + puts "error: no icon for state={$state} $path" + return file_plain + } + return $r +} + +proc mapdesc {state path} { + global all_descs + + if {[catch {set r $all_descs($state)}]} { + puts "error: no desc for state={$state} $path" + return $state + } + return $r +} + +proc escape_path {path} { + regsub -all "\n" $path "\\n" path + return $path +} + +proc short_path {path} { + return [escape_path [lindex [file split $path] end]] +} + +set next_icon_id 0 +set null_sha1 [string repeat 0 40] + +proc merge_state {path new_state {head_info {}} {index_info {}}} { + global file_states next_icon_id null_sha1 + + set s0 [string index $new_state 0] + set s1 [string index $new_state 1] + + if {[catch {set info $file_states($path)}]} { + set state __ + set icon n[incr next_icon_id] + } else { + set state [lindex $info 0] + set icon [lindex $info 1] + if {$head_info eq {}} {set head_info [lindex $info 2]} + if {$index_info eq {}} {set index_info [lindex $info 3]} + } + + if {$s0 eq {?}} {set s0 [string index $state 0]} \ + elseif {$s0 eq {_}} {set s0 _} + + if {$s1 eq {?}} {set s1 [string index $state 1]} \ + elseif {$s1 eq {_}} {set s1 _} + + if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} { + set head_info [list 0 $null_sha1] + } elseif {$s0 ne {_} && [string index $state 0] eq {_} + && $head_info eq {}} { + set head_info $index_info + } + + set file_states($path) [list $s0$s1 $icon \ + $head_info $index_info \ + ] + return $state +} + +proc display_file {path state} { + global file_states file_lists selected_paths + + set old_m [merge_state $path $state] + set s $file_states($path) + set new_m [lindex $s 0] + set new_w [mapcol $new_m $path] + set old_w [mapcol $old_m $path] + set new_icon [mapicon $new_m $path] + + if {$new_m eq {__}} { + set lno [lsearch -sorted $file_lists($old_w) $path] + if {$lno >= 0} { + set file_lists($old_w) \ + [lreplace $file_lists($old_w) $lno $lno] + incr lno + $old_w conf -state normal + $old_w delete $lno.0 [expr {$lno + 1}].0 + $old_w conf -state disabled + } + unset file_states($path) + catch {unset selected_paths($path)} + return + } + + if {$new_w ne $old_w} { + set lno [lsearch -sorted $file_lists($old_w) $path] + if {$lno >= 0} { + set file_lists($old_w) \ + [lreplace $file_lists($old_w) $lno $lno] + incr lno + $old_w conf -state normal + $old_w delete $lno.0 [expr {$lno + 1}].0 + $old_w conf -state disabled + } + + lappend file_lists($new_w) $path + set file_lists($new_w) [lsort $file_lists($new_w)] + set lno [lsearch -sorted $file_lists($new_w) $path] + incr lno + $new_w conf -state normal + $new_w image create $lno.0 \ + -align center -padx 5 -pady 1 \ + -name [lindex $s 1] \ + -image $new_icon + $new_w insert $lno.1 "[escape_path $path]\n" + if {[catch {set in_sel $selected_paths($path)}]} { + set in_sel 0 + } + if {$in_sel} { + $new_w tag add in_sel $lno.0 [expr {$lno + 1}].0 + } + $new_w conf -state disabled + } elseif {$new_icon ne [mapicon $old_m $path]} { + $new_w conf -state normal + $new_w image conf [lindex $s 1] -image $new_icon + $new_w conf -state disabled + } +} + +proc display_all_files {} { + global ui_index ui_other + global file_states file_lists + global last_clicked selected_paths + + $ui_index conf -state normal + $ui_other conf -state normal + + $ui_index delete 0.0 end + $ui_other delete 0.0 end + set last_clicked {} + + set file_lists($ui_index) [list] + set file_lists($ui_other) [list] + + foreach path [lsort [array names file_states]] { + set s $file_states($path) + set m [lindex $s 0] + set w [mapcol $m $path] + lappend file_lists($w) $path + set lno [expr {[lindex [split [$w index end] .] 0] - 1}] + $w image create end \ + -align center -padx 5 -pady 1 \ + -name [lindex $s 1] \ + -image [mapicon $m $path] + $w insert end "[escape_path $path]\n" + if {[catch {set in_sel $selected_paths($path)}]} { + set in_sel 0 + } + if {$in_sel} { + $w tag add in_sel $lno.0 [expr {$lno + 1}].0 + } + } + + $ui_index conf -state disabled + $ui_other conf -state disabled +} + +proc update_indexinfo {msg pathList after} { + global update_index_cp ui_status_value + + if {![lock_index update]} return + + set update_index_cp 0 + set pathList [lsort $pathList] + set totalCnt [llength $pathList] + set batch [expr {int($totalCnt * .01) + 1}] + if {$batch > 25} {set batch 25} + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + 0.0] + set fd [open "| git update-index -z --index-info" w] + fconfigure $fd \ + -blocking 0 \ + -buffering full \ + -buffersize 512 \ + -translation binary + fileevent $fd writable [list \ + write_update_indexinfo \ + $fd \ + $pathList \ + $totalCnt \ + $batch \ + $msg \ + $after \ + ] +} + +proc write_update_indexinfo {fd pathList totalCnt batch msg after} { + global update_index_cp ui_status_value + global file_states current_diff + + if {$update_index_cp >= $totalCnt} { + close $fd + unlock_index + uplevel #0 $after + return + } + + for {set i $batch} \ + {$update_index_cp < $totalCnt && $i > 0} \ + {incr i -1} { + set path [lindex $pathList $update_index_cp] + incr update_index_cp + + set s $file_states($path) + switch -glob -- [lindex $s 0] { + A? {set new _O} + M? {set new _M} + D_ {set new _D} + D? {set new _?} + ?? {continue} + } + set info [lindex $s 2] + if {$info eq {}} continue + + puts -nonewline $fd $info + puts -nonewline $fd "\t" + puts -nonewline $fd $path + puts -nonewline $fd "\0" + display_file $path $new + } + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + [expr {100.0 * $update_index_cp / $totalCnt}]] +} + +proc update_index {msg pathList after} { + global update_index_cp ui_status_value + + if {![lock_index update]} return + + set update_index_cp 0 + set pathList [lsort $pathList] + set totalCnt [llength $pathList] + set batch [expr {int($totalCnt * .01) + 1}] + if {$batch > 25} {set batch 25} + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + 0.0] + set fd [open "| git update-index --add --remove -z --stdin" w] + fconfigure $fd \ + -blocking 0 \ + -buffering full \ + -buffersize 512 \ + -translation binary + fileevent $fd writable [list \ + write_update_index \ + $fd \ + $pathList \ + $totalCnt \ + $batch \ + $msg \ + $after \ + ] +} + +proc write_update_index {fd pathList totalCnt batch msg after} { + global update_index_cp ui_status_value + global file_states current_diff + + if {$update_index_cp >= $totalCnt} { + close $fd + unlock_index + uplevel #0 $after + return + } + + for {set i $batch} \ + {$update_index_cp < $totalCnt && $i > 0} \ + {incr i -1} { + set path [lindex $pathList $update_index_cp] + incr update_index_cp + + switch -glob -- [lindex $file_states($path) 0] { + AD - + MD - + UD - + _D {set new DD} + + _M - + MM - + UM - + U_ - + M_ {set new M_} + + _O - + AM - + A_ {set new A_} + + ?? {continue} + } + + puts -nonewline $fd $path + puts -nonewline $fd "\0" + display_file $path $new + } + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + [expr {100.0 * $update_index_cp / $totalCnt}]] +} + +proc checkout_index {msg pathList after} { + global update_index_cp ui_status_value + + if {![lock_index update]} return + + set update_index_cp 0 + set pathList [lsort $pathList] + set totalCnt [llength $pathList] + set batch [expr {int($totalCnt * .01) + 1}] + if {$batch > 25} {set batch 25} + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + 0.0] + set cmd [list git checkout-index] + lappend cmd --index + lappend cmd --quiet + lappend cmd --force + lappend cmd -z + lappend cmd --stdin + set fd [open "| $cmd " w] + fconfigure $fd \ + -blocking 0 \ + -buffering full \ + -buffersize 512 \ + -translation binary + fileevent $fd writable [list \ + write_checkout_index \ + $fd \ + $pathList \ + $totalCnt \ + $batch \ + $msg \ + $after \ + ] +} + +proc write_checkout_index {fd pathList totalCnt batch msg after} { + global update_index_cp ui_status_value + global file_states current_diff + + if {$update_index_cp >= $totalCnt} { + close $fd + unlock_index + uplevel #0 $after + return + } + + for {set i $batch} \ + {$update_index_cp < $totalCnt && $i > 0} \ + {incr i -1} { + set path [lindex $pathList $update_index_cp] + incr update_index_cp + + switch -glob -- [lindex $file_states($path) 0] { + AM - + AD {set new A_} + MM - + MD {set new M_} + _M - + _D {set new __} + ?? {continue} + } + + puts -nonewline $fd $path + puts -nonewline $fd "\0" + display_file $path $new + } + + set ui_status_value [format \ + "$msg... %i/%i files (%.2f%%)" \ + $update_index_cp \ + $totalCnt \ + [expr {100.0 * $update_index_cp / $totalCnt}]] +} + +###################################################################### +## +## branch management + +proc load_all_heads {} { + global all_heads tracking_branches + + set all_heads [list] + set cmd [list git for-each-ref] + lappend cmd --format=%(refname) + lappend cmd refs/heads + set fd [open "| $cmd" r] + while {[gets $fd line] > 0} { + if {![catch {set info $tracking_branches($line)}]} continue + if {![regsub ^refs/heads/ $line {} name]} continue + lappend all_heads $name + } + close $fd + + set all_heads [lsort $all_heads] +} + +proc populate_branch_menu {m} { + global all_heads disable_on_lock + + $m add separator + foreach b $all_heads { + $m add radiobutton \ + -label $b \ + -command [list switch_branch $b] \ + -variable current_branch \ + -value $b \ + -font font_ui + lappend disable_on_lock \ + [list $m entryconf [$m index last] -state] + } +} + +proc do_create_branch {} { + error "NOT IMPLEMENTED" +} + +proc do_delete_branch {} { + error "NOT IMPLEMENTED" +} + +proc switch_branch {b} { + global HEAD commit_type file_states current_branch + global selected_commit_type ui_comm + + if {![lock_index switch]} return + + # -- Backup the selected branch (repository_state resets it) + # + set new_branch $current_branch + + # -- Our in memory state should match the repository. + # + repository_state curType curHEAD curMERGE_HEAD + if {[string match amend* $commit_type] + && $curType eq {normal} + && $curHEAD eq $HEAD} { + } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} { + info_popup {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 the current branch can be changed. + +The rescan will be automatically started now. +} + unlock_index + rescan {set ui_status_value {Ready.}} + return + } + + # -- Toss the message buffer if we are in amend mode. + # + if {[string match amend* $curType]} { + $ui_comm delete 0.0 end + $ui_comm edit reset + $ui_comm edit modified false + } + + set selected_commit_type new + set current_branch $new_branch + + unlock_index + error "NOT FINISHED" +} + +###################################################################### +## +## remote management + +proc load_all_remotes {} { + global gitdir repo_config + global all_remotes tracking_branches + + set all_remotes [list] + array unset tracking_branches + + set rm_dir [file join $gitdir remotes] + if {[file isdirectory $rm_dir]} { + set all_remotes [glob \ + -types f \ + -tails \ + -nocomplain \ + -directory $rm_dir *] + + foreach name $all_remotes { + catch { + set fd [open [file join $rm_dir $name] r] + while {[gets $fd line] >= 0} { + if {![regexp {^Pull:[ ]*([^:]+):(.+)$} \ + $line line src dst]} continue + if {![regexp ^refs/ $dst]} { + set dst "refs/heads/$dst" + } + set tracking_branches($dst) [list $name $src] + } + close $fd + } + } + } + + foreach line [array names repo_config remote.*.url] { + if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue + lappend all_remotes $name + + if {[catch {set fl $repo_config(remote.$name.fetch)}]} { + set fl {} + } + foreach line $fl { + if {![regexp {^([^:]+):(.+)$} $line line src dst]} continue + if {![regexp ^refs/ $dst]} { + set dst "refs/heads/$dst" + } + set tracking_branches($dst) [list $name $src] + } + } + + set all_remotes [lsort -unique $all_remotes] +} + +proc populate_fetch_menu {m} { + global gitdir all_remotes repo_config + + foreach r $all_remotes { + set enable 0 + if {![catch {set a $repo_config(remote.$r.url)}]} { + if {![catch {set a $repo_config(remote.$r.fetch)}]} { + set enable 1 + } + } else { + catch { + set fd [open [file join $gitdir remotes $r] r] + while {[gets $fd n] >= 0} { + if {[regexp {^Pull:[ \t]*([^:]+):} $n]} { + set enable 1 + break + } + } + close $fd + } + } + + if {$enable} { + $m add command \ + -label "Fetch from $r..." \ + -command [list fetch_from $r] \ + -font font_ui + } + } +} + +proc populate_push_menu {m} { + global gitdir all_remotes repo_config + + foreach r $all_remotes { + set enable 0 + if {![catch {set a $repo_config(remote.$r.url)}]} { + if {![catch {set a $repo_config(remote.$r.push)}]} { + set enable 1 + } + } else { + catch { + set fd [open [file join $gitdir remotes $r] r] + while {[gets $fd n] >= 0} { + if {[regexp {^Push:[ \t]*([^:]+):} $n]} { + set enable 1 + break + } + } + close $fd + } + } + + if {$enable} { + $m add command \ + -label "Push to $r..." \ + -command [list push_to $r] \ + -font font_ui + } + } +} + +proc populate_pull_menu {m} { + global gitdir repo_config all_remotes disable_on_lock + + foreach remote $all_remotes { + set rb_list [list] + if {[array get repo_config remote.$remote.url] ne {}} { + if {[array get repo_config remote.$remote.fetch] ne {}} { + foreach line $repo_config(remote.$remote.fetch) { + if {[regexp {^([^:]+):} $line line rb]} { + lappend rb_list $rb + } + } + } + } else { + catch { + set fd [open [file join $gitdir remotes $remote] r] + while {[gets $fd line] >= 0} { + if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} { + lappend rb_list $rb + } + } + close $fd + } + } + + foreach rb $rb_list { + regsub ^refs/heads/ $rb {} rb_short + $m add command \ + -label "Branch $rb_short from $remote..." \ + -command [list pull_remote $remote $rb] \ + -font font_ui + lappend disable_on_lock \ + [list $m entryconf [$m index last] -state] + } + } +} + +###################################################################### +## +## icons + +set filemask { +#define mask_width 14 +#define mask_height 15 +static unsigned char mask_bits[] = { + 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, + 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, + 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f}; +} + +image create bitmap file_plain -background white -foreground black -data { +#define plain_width 14 +#define plain_height 15 +static unsigned char plain_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10, + 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, + 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_mod -background white -foreground blue -data { +#define mod_width 14 +#define mod_height 15 +static unsigned char mod_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_fulltick -background white -foreground "#007000" -data { +#define file_fulltick_width 14 +#define file_fulltick_height 15 +static unsigned char file_fulltick_bits[] = { + 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16, + 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10, + 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_parttick -background white -foreground "#005050" -data { +#define parttick_width 14 +#define parttick_height 15 +static unsigned char parttick_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, + 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10, + 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_question -background white -foreground black -data { +#define file_question_width 14 +#define file_question_height 15 +static unsigned char file_question_bits[] = { + 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13, + 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10, + 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_removed -background white -foreground red -data { +#define file_removed_width 14 +#define file_removed_height 15 +static unsigned char file_removed_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10, + 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13, + 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_merge -background white -foreground blue -data { +#define file_merge_width 14 +#define file_merge_height 15 +static unsigned char file_merge_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10, + 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +set ui_index .vpane.files.index.list +set ui_other .vpane.files.other.list +set max_status_desc 0 +foreach i { + {__ i plain "Unmodified"} + {_M i mod "Modified"} + {M_ i fulltick "Added to commit"} + {MM i parttick "Partially included"} + {MD i question "Added (but gone)"} + + {_O o plain "Untracked"} + {A_ o fulltick "Added by commit"} + {AM o parttick "Partially added"} + {AD o question "Added (but gone)"} + + {_D i question "Missing"} + {DD i removed "Removed by commit"} + {D_ i removed "Removed by commit"} + {DO i removed "Removed (still exists)"} + {DM i removed "Removed (but modified)"} + + {UD i merge "Merge conflicts"} + {UM i merge "Merge conflicts"} + {U_ i merge "Merge conflicts"} + } { + if {$max_status_desc < [string length [lindex $i 3]]} { + set max_status_desc [string length [lindex $i 3]] + } + if {[lindex $i 1] eq {i}} { + set all_cols([lindex $i 0]) $ui_index + } else { + set all_cols([lindex $i 0]) $ui_other + } + set all_icons([lindex $i 0]) file_[lindex $i 2] + set all_descs([lindex $i 0]) [lindex $i 3] +} +unset filemask i + +###################################################################### +## +## util + +proc is_MacOSX {} { + global tcl_platform tk_library + if {[tk windowingsystem] eq {aqua}} { + return 1 + } + return 0 +} + +proc is_Windows {} { + global tcl_platform + if {$tcl_platform(platform) eq {windows}} { + return 1 + } + return 0 +} + +proc bind_button3 {w cmd} { + bind $w $cmd + if {[is_MacOSX]} { + bind $w $cmd + } +} + +proc incr_font_size {font {amt 1}} { + set sz [font configure $font -size] + incr sz $amt + font configure $font -size $sz + font configure ${font}bold -size $sz +} + +proc hook_failed_popup {hook msg} { + global gitdir appname + + set w .hookfail + toplevel $w + + frame $w.m + label $w.m.l1 -text "$hook hook failed:" \ + -anchor w \ + -justify left \ + -font font_uibold + text $w.m.t \ + -background white -borderwidth 1 \ + -relief sunken \ + -width 80 -height 10 \ + -font font_diff \ + -yscrollcommand [list $w.m.sby set] + label $w.m.l2 \ + -text {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 + 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 + + $w.m.t insert 1.0 $msg + $w.m.t conf -state disabled + + button $w.ok -text OK \ + -width 15 \ + -font font_ui \ + -command "destroy $w" + pack $w.ok -side bottom -anchor e -pady 10 -padx 10 + + bind $w "grab $w; focus $w" + bind $w "destroy $w" + wm title $w "$appname ([lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end]): error" + tkwait window $w +} + +set next_console_id 0 + +proc new_console {short_title long_title} { + global next_console_id console_data + set w .console[incr next_console_id] + set console_data($w) [list $short_title $long_title] + return [console_init $w] +} + +proc console_init {w} { + global console_cr console_data + global gitdir appname M1B + + set console_cr($w) 1.0 + toplevel $w + frame $w.m + label $w.m.l1 -text "[lindex $console_data($w) 1]:" \ + -anchor w \ + -justify left \ + -font font_uibold + text $w.m.t \ + -background white -borderwidth 1 \ + -relief sunken \ + -width 80 -height 10 \ + -font font_diff \ + -state disabled \ + -yscrollcommand [list $w.m.sby set] + label $w.m.s -text {Working... please wait...} \ + -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.s -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 + + menu $w.ctxm -tearoff 0 + $w.ctxm add command -label "Copy" \ + -font font_ui \ + -command "tk_textCopy $w.m.t" + $w.ctxm add command -label "Select All" \ + -font font_ui \ + -command "$w.m.t tag add sel 0.0 end" + $w.ctxm add command -label "Copy All" \ + -font font_ui \ + -command " + $w.m.t tag add sel 0.0 end + tk_textCopy $w.m.t + $w.m.t tag remove sel 0.0 end + " + + button $w.ok -text {Close} \ + -font font_ui \ + -state disabled \ + -command "destroy $w" + pack $w.ok -side bottom -anchor e -pady 10 -padx 10 + + bind_button3 $w.m.t "tk_popup $w.ctxm %X %Y" + bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" + bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break" + bind $w "focus $w" + wm title $w "$appname ([lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end]): [lindex $console_data($w) 0]" + return $w +} + +proc console_exec {w cmd {after {}}} { + # -- Windows tosses the enviroment when we exec our child. + # But most users need that so we have to relogin. :-( + # + if {[is_Windows]} { + set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"] + } + + # -- Tcl won't let us redirect both stdout and stderr to + # the same pipe. So pass it through cat... + # + set cmd [concat | $cmd |& cat] + + set fd_f [open $cmd r] + fconfigure $fd_f -blocking 0 -translation binary + fileevent $fd_f readable [list console_read $w $fd_f $after] +} + +proc console_read {w fd after} { + global console_cr console_data + + set buf [read $fd] + if {$buf ne {}} { + if {![winfo exists $w]} {console_init $w} + $w.m.t conf -state normal + set c 0 + set n [string length $buf] + while {$c < $n} { + set cr [string first "\r" $buf $c] + set lf [string first "\n" $buf $c] + if {$cr < 0} {set cr [expr {$n + 1}]} + if {$lf < 0} {set lf [expr {$n + 1}]} + + if {$lf < $cr} { + $w.m.t insert end [string range $buf $c $lf] + set console_cr($w) [$w.m.t index {end -1c}] + set c $lf + incr c + } else { + $w.m.t delete $console_cr($w) end + $w.m.t insert end "\n" + $w.m.t insert end [string range $buf $c $cr] + set c $cr + incr c + } + } + $w.m.t conf -state disabled + $w.m.t see end + } + + fconfigure $fd -blocking 1 + if {[eof $fd]} { + if {[catch {close $fd}]} { + if {![winfo exists $w]} {console_init $w} + $w.m.s conf -background red -text {Error: Command Failed} + $w.ok conf -state normal + set ok 0 + } elseif {[winfo exists $w]} { + $w.m.s conf -background green -text {Success} + $w.ok conf -state normal + set ok 1 + } + array unset console_cr $w + array unset console_data $w + if {$after ne {}} { + uplevel #0 $after $ok + } + return + } + fconfigure $fd -blocking 0 +} + +###################################################################### +## +## ui commands + +set starting_gitk_msg {Please wait... Starting gitk...} + +proc do_gitk {revs} { + global ui_status_value starting_gitk_msg + + set cmd gitk + if {$revs ne {}} { + append cmd { } + append cmd $revs + } + if {[is_Windows]} { + set cmd "sh -c \"exec $cmd\"" + } + append cmd { &} + + if {[catch {eval exec $cmd} err]} { + error_popup "Failed to start gitk:\n\n$err" + } else { + set ui_status_value $starting_gitk_msg + after 10000 { + if {$ui_status_value eq $starting_gitk_msg} { + set ui_status_value {Ready.} + } + } + } +} + +proc do_gc {} { + set w [new_console {gc} {Compressing the object database}] + console_exec $w {git gc} +} + +proc do_fsck_objects {} { + set w [new_console {fsck-objects} \ + {Verifying the object database with fsck-objects}] + set cmd [list git fsck-objects] + lappend cmd --full + lappend cmd --cache + lappend cmd --strict + console_exec $w $cmd +} + +set is_quitting 0 + +proc do_quit {} { + global gitdir ui_comm is_quitting repo_config commit_type + + if {$is_quitting} return + set is_quitting 1 + + # -- Stash our current commit buffer. + # + set save [file join $gitdir GITGUI_MSG] + set msg [string trim [$ui_comm get 0.0 end]] + if {![string match amend* $commit_type] + && [$ui_comm edit modified] + && $msg ne {}} { + catch { + set fd [open $save w] + puts $fd [string trim [$ui_comm get 0.0 end]] + close $fd + } + } else { + catch {file delete $save} + } + + # -- Stash our current window geometry into this repository. + # + set cfg_geometry [list] + lappend cfg_geometry [wm geometry .] + lappend cfg_geometry [lindex [.vpane sash coord 0] 1] + lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0] + if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { + set rc_geometry {} + } + if {$cfg_geometry ne $rc_geometry} { + catch {exec git repo-config gui.geometry $cfg_geometry} + } + + destroy . +} + +proc do_rescan {} { + rescan {set ui_status_value {Ready.}} +} + +proc remove_helper {txt paths} { + global file_states current_diff + + if {![lock_index begin-update]} return + + set pathList [list] + set after {} + foreach path $paths { + switch -glob -- [lindex $file_states($path) 0] { + A? - + M? - + D? { + lappend pathList $path + if {$path eq $current_diff} { + set after {reshow_diff;} + } + } + } + } + if {$pathList eq {}} { + unlock_index + } else { + update_indexinfo \ + $txt \ + $pathList \ + [concat $after {set ui_status_value {Ready.}}] + } +} + +proc do_remove_selection {} { + global current_diff selected_paths + + if {[array size selected_paths] > 0} { + remove_helper \ + {Removing selected files from commit} \ + [array names selected_paths] + } elseif {$current_diff ne {}} { + remove_helper \ + "Removing [short_path $current_diff] from commit" \ + [list $current_diff] + } +} + +proc include_helper {txt paths} { + global file_states current_diff + + if {![lock_index begin-update]} return + + set pathList [list] + set after {} + foreach path $paths { + switch -glob -- [lindex $file_states($path) 0] { + AM - + AD - + MM - + MD - + U? - + _M - + _D - + _O { + lappend pathList $path + if {$path eq $current_diff} { + set after {reshow_diff;} + } + } + } + } + if {$pathList eq {}} { + unlock_index + } else { + update_index \ + $txt \ + $pathList \ + [concat $after {set ui_status_value {Ready to commit.}}] + } +} + +proc do_include_selection {} { + global current_diff selected_paths + + if {[array size selected_paths] > 0} { + include_helper \ + {Adding selected files} \ + [array names selected_paths] + } elseif {$current_diff ne {}} { + include_helper \ + "Adding [short_path $current_diff]" \ + [list $current_diff] + } +} + +proc do_include_all {} { + global file_states + + set paths [list] + foreach path [array names file_states] { + switch -- [lindex $file_states($path) 0] { + AM - + AD - + MM - + MD - + _M - + _D {lappend paths $path} + } + } + include_helper \ + {Adding all modified files} \ + $paths +} + +proc revert_helper {txt paths} { + global gitdir appname + global file_states current_diff + + if {![lock_index begin-update]} return + + set pathList [list] + set after {} + foreach path $paths { + switch -glob -- [lindex $file_states($path) 0] { + AM - + AD - + MM - + MD - + _M - + _D { + lappend pathList $path + if {$path eq $current_diff} { + set after {reshow_diff;} + } + } + } + } + + set n [llength $pathList] + if {$n == 0} { + unlock_index + return + } elseif {$n == 1} { + set s "[short_path [lindex $pathList]]" + } else { + set s "these $n files" + } + + set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] + + set reply [tk_dialog \ + .confirm_revert \ + "$appname ($reponame)" \ + "Revert changes in $s? + +Any unadded changes will be permanently lost by the revert." \ + question \ + 1 \ + {Do Nothing} \ + {Revert Changes} \ + ] + if {$reply == 1} { + checkout_index \ + $txt \ + $pathList \ + [concat $after {set ui_status_value {Ready.}}] + } else { + unlock_index + } +} + +proc do_revert_selection {} { + global current_diff selected_paths + + if {[array size selected_paths] > 0} { + revert_helper \ + {Reverting selected files} \ + [array names selected_paths] + } elseif {$current_diff ne {}} { + revert_helper \ + "Reverting [short_path $current_diff]" \ + [list $current_diff] + } +} + +proc do_signoff {} { + global ui_comm + + set me [committer_ident] + if {$me eq {}} return + + set sob "Signed-off-by: $me" + set last [$ui_comm get {end -1c linestart} {end -1c}] + if {$last ne $sob} { + $ui_comm edit separator + if {$last ne {} + && ![regexp {^[A-Z][A-Za-z]*-[A-Za-z-]+: *} $last]} { + $ui_comm insert end "\n" + } + $ui_comm insert end "\n$sob" + $ui_comm edit separator + $ui_comm see end + } +} + +proc do_select_commit_type {} { + global commit_type selected_commit_type + + if {$selected_commit_type eq {new} + && [string match amend* $commit_type]} { + create_new_commit + } elseif {$selected_commit_type eq {amend} + && ![string match amend* $commit_type]} { + load_last_commit + + # The amend request was rejected... + # + if {![string match amend* $commit_type]} { + set selected_commit_type new + } + } +} + +proc do_commit {} { + commit_tree +} + +proc do_about {} { + global appname copyright + global tcl_patchLevel tk_patchLevel + + set w .about_dialog + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + label $w.header -text "About $appname" \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.close -text {Close} \ + -font font_ui \ + -command [list destroy $w] + pack $w.buttons.close -side right + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + label $w.desc \ + -text "$appname - a commit creation tool for Git. +$copyright" \ + -padx 5 -pady 5 \ + -justify left \ + -anchor w \ + -borderwidth 1 \ + -relief solid \ + -font font_ui + pack $w.desc -side top -fill x -padx 5 -pady 5 + + set v [exec git --version] + append v "\n\n" + if {$tcl_patchLevel eq $tk_patchLevel} { + append v "Tcl/Tk version $tcl_patchLevel" + } else { + append v "Tcl version $tcl_patchLevel" + append v ", Tk version $tk_patchLevel" + } + + label $w.vers \ + -text $v \ + -padx 5 -pady 5 \ + -justify left \ + -anchor w \ + -borderwidth 1 \ + -relief solid \ + -font font_ui + pack $w.vers -side top -fill x -padx 5 -pady 5 + + bind $w "grab $w; focus $w" + bind $w "destroy $w" + wm title $w "About $appname" + tkwait window $w +} + +proc do_options {} { + global appname gitdir font_descs + global repo_config global_config + global repo_config_new global_config_new + + array unset repo_config_new + array unset global_config_new + foreach name [array names repo_config] { + set repo_config_new($name) $repo_config($name) + } + load_config 1 + foreach name [array names repo_config] { + switch -- $name { + gui.diffcontext {continue} + } + set repo_config_new($name) $repo_config($name) + } + foreach name [array names global_config] { + set global_config_new($name) $global_config($name) + } + set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] + + set w .options_editor + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + label $w.header -text "$appname Options" \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.restore -text {Restore Defaults} \ + -font font_ui \ + -command do_restore_defaults + pack $w.buttons.restore -side left + button $w.buttons.save -text Save \ + -font font_ui \ + -command [list do_save_config $w] + pack $w.buttons.save -side right + button $w.buttons.cancel -text {Cancel} \ + -font font_ui \ + -command [list destroy $w] + pack $w.buttons.cancel -side right + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + labelframe $w.repo -text "$reponame Repository" \ + -font font_ui \ + -relief raised -borderwidth 2 + labelframe $w.global -text {Global (All Repositories)} \ + -font font_ui \ + -relief raised -borderwidth 2 + pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 + pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 + + foreach option { + {b partialinclude {Allow Partially Added Files}} + {b pullsummary {Show Pull Summary}} + {b trustmtime {Trust File Modification Timestamps}} + {i diffcontext {Number of Diff Context Lines}} + } { + set type [lindex $option 0] + set name [lindex $option 1] + set text [lindex $option 2] + foreach f {repo global} { + switch $type { + b { + checkbutton $w.$f.$name -text $text \ + -variable ${f}_config_new(gui.$name) \ + -onvalue true \ + -offvalue false \ + -font font_ui + pack $w.$f.$name -side top -anchor w + } + i { + frame $w.$f.$name + label $w.$f.$name.l -text "$text:" -font font_ui + pack $w.$f.$name.l -side left -anchor w -fill x + spinbox $w.$f.$name.v \ + -textvariable ${f}_config_new(gui.$name) \ + -from 1 -to 99 -increment 1 \ + -width 3 \ + -font font_ui + pack $w.$f.$name.v -side right -anchor e + pack $w.$f.$name -side top -anchor w -fill x + } + } + } + } + + set all_fonts [lsort [font families]] + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + set text [lindex $option 2] + + set global_config_new(gui.$font^^family) \ + [font configure $font -family] + set global_config_new(gui.$font^^size) \ + [font configure $font -size] + + frame $w.global.$name + label $w.global.$name.l -text "$text:" -font font_ui + pack $w.global.$name.l -side left -anchor w -fill x + eval tk_optionMenu $w.global.$name.family \ + global_config_new(gui.$font^^family) \ + $all_fonts + spinbox $w.global.$name.size \ + -textvariable global_config_new(gui.$font^^size) \ + -from 2 -to 80 -increment 1 \ + -width 3 \ + -font font_ui + pack $w.global.$name.size -side right -anchor e + pack $w.global.$name.family -side right -anchor e + pack $w.global.$name -side top -anchor w -fill x + } + + bind $w "grab $w; focus $w" + bind $w "destroy $w" + wm title $w "$appname ($reponame): Options" + tkwait window $w +} + +proc do_restore_defaults {} { + global font_descs default_config repo_config + global repo_config_new global_config_new + + foreach name [array names default_config] { + set repo_config_new($name) $default_config($name) + set global_config_new($name) $default_config($name) + } + + foreach option $font_descs { + set name [lindex $option 0] + set repo_config(gui.$name) $default_config(gui.$name) + } + apply_config + + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + set global_config_new(gui.$font^^family) \ + [font configure $font -family] + set global_config_new(gui.$font^^size) \ + [font configure $font -size] + } +} + +proc do_save_config {w} { + if {[catch {save_config} err]} { + error_popup "Failed to completely save options:\n\n$err" + } + reshow_diff + destroy $w +} + +proc do_windows_shortcut {} { + global gitdir appname argv0 + + set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] + + if {[catch { + set desktop [exec cygpath \ + --windows \ + --absolute \ + --long-name \ + --desktop] + }]} { + set desktop . + } + set fn [tk_getSaveFile \ + -parent . \ + -title "$appname ($reponame): Create Desktop Icon" \ + -initialdir $desktop \ + -initialfile "Git $reponame.bat"] + if {$fn != {}} { + if {[catch { + set fd [open $fn w] + set sh [exec cygpath \ + --windows \ + --absolute \ + /bin/sh] + set me [exec cygpath \ + --unix \ + --absolute \ + $argv0] + set gd [exec cygpath \ + --unix \ + --absolute \ + $gitdir] + regsub -all ' $me "'\\''" me + regsub -all ' $gd "'\\''" gd + puts $fd "@ECHO Starting git-gui... Please wait..." + puts -nonewline $fd "@\"$sh\" --login -c \"" + puts -nonewline $fd "GIT_DIR='$gd'" + puts -nonewline $fd " '$me'" + puts $fd "&\"" + close $fd + } err]} { + error_popup "Cannot write script:\n\n$err" + } + } +} + +proc do_macosx_app {} { + global gitdir appname argv0 env + + set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] + + set fn [tk_getSaveFile \ + -parent . \ + -title "$appname ($reponame): Create Desktop Icon" \ + -initialdir [file join $env(HOME) Desktop] \ + -initialfile "Git $reponame.app"] + if {$fn != {}} { + if {[catch { + set Contents [file join $fn Contents] + set MacOS [file join $Contents MacOS] + set exe [file join $MacOS git-gui] + + file mkdir $MacOS + + set fd [open [file join $Contents Info.plist] w] + puts $fd { + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + git-gui + CFBundleIdentifier + org.spearce.git-gui + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSPrincipalClass + NSApplication + +} + close $fd + + set fd [open $exe w] + set gd [file normalize $gitdir] + set ep [file normalize [exec git --exec-path]] + regsub -all ' $gd "'\\''" gd + regsub -all ' $ep "'\\''" ep + puts $fd "#!/bin/sh" + foreach name [array names env] { + if {[string match GIT_* $name]} { + regsub -all ' $env($name) "'\\''" v + puts $fd "export $name='$v'" + } + } + puts $fd "export PATH='$ep':\$PATH" + puts $fd "export GIT_DIR='$gd'" + puts $fd "exec [file normalize $argv0]" + close $fd + + file attributes $exe -permissions u+x,g+x,o+x + } err]} { + error_popup "Cannot write icon:\n\n$err" + } + } +} + +proc toggle_or_diff {w x y} { + global file_states file_lists current_diff ui_index ui_other + global last_clicked selected_paths + + set pos [split [$w index @$x,$y] .] + set lno [lindex $pos 0] + set col [lindex $pos 1] + set path [lindex $file_lists($w) [expr {$lno - 1}]] + if {$path eq {}} { + set last_clicked {} + return + } + + set last_clicked [list $w $lno] + array unset selected_paths + $ui_index tag remove in_sel 0.0 end + $ui_other tag remove in_sel 0.0 end + + if {$col == 0} { + if {$current_diff eq $path} { + set after {reshow_diff;} + } else { + set after {} + } + switch -glob -- [lindex $file_states($path) 0] { + A_ - + M_ - + DD - + DO - + DM { + update_indexinfo \ + "Removing [short_path $path] from commit" \ + [list $path] \ + [concat $after {set ui_status_value {Ready.}}] + } + ?? { + update_index \ + "Adding [short_path $path]" \ + [list $path] \ + [concat $after {set ui_status_value {Ready.}}] + } + } + } else { + show_diff $path $w $lno + } +} + +proc add_one_to_selection {w x y} { + global file_lists + global last_clicked selected_paths + + set pos [split [$w index @$x,$y] .] + set lno [lindex $pos 0] + set col [lindex $pos 1] + set path [lindex $file_lists($w) [expr {$lno - 1}]] + if {$path eq {}} { + set last_clicked {} + return + } + + set last_clicked [list $w $lno] + if {[catch {set in_sel $selected_paths($path)}]} { + set in_sel 0 + } + if {$in_sel} { + unset selected_paths($path) + $w tag remove in_sel $lno.0 [expr {$lno + 1}].0 + } else { + set selected_paths($path) 1 + $w tag add in_sel $lno.0 [expr {$lno + 1}].0 + } +} + +proc add_range_to_selection {w x y} { + global file_lists + global last_clicked selected_paths + + if {[lindex $last_clicked 0] ne $w} { + toggle_or_diff $w $x $y + return + } + + set pos [split [$w index @$x,$y] .] + set lno [lindex $pos 0] + set lc [lindex $last_clicked 1] + if {$lc < $lno} { + set begin $lc + set end $lno + } else { + set begin $lno + set end $lc + } + + foreach path [lrange $file_lists($w) \ + [expr {$begin - 1}] \ + [expr {$end - 1}]] { + set selected_paths($path) 1 + } + $w tag add in_sel $begin.0 [expr {$end + 1}].0 +} + +###################################################################### +## +## config defaults + +set cursor_ptr arrow +font create font_diff -family Courier -size 10 +font create font_ui +catch { + label .dummy + eval font configure font_ui [font actual [.dummy cget -font]] + destroy .dummy +} + +font create font_uibold +font create font_diffbold + +if {[is_Windows]} { + set M1B Control + set M1T Ctrl +} elseif {[is_MacOSX]} { + set M1B M1 + set M1T Cmd +} else { + set M1B M1 + set M1T M1 +} + +proc apply_config {} { + global repo_config font_descs + + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + if {[catch { + foreach {cn cv} $repo_config(gui.$name) { + font configure $font $cn $cv + } + } err]} { + error_popup "Invalid font specified in gui.$name:\n\n$err" + } + foreach {cn cv} [font configure $font] { + font configure ${font}bold $cn $cv + } + font configure ${font}bold -weight bold + } +} + +set default_config(gui.trustmtime) false +set default_config(gui.pullsummary) true +set default_config(gui.partialinclude) false +set default_config(gui.diffcontext) 5 +set default_config(gui.fontui) [font configure font_ui] +set default_config(gui.fontdiff) [font configure font_diff] +set font_descs { + {fontui font_ui {Main Font}} + {fontdiff font_diff {Diff/Console Font}} +} +load_config 0 +apply_config + +###################################################################### +## +## ui construction + +# -- Menu Bar +# +menu .mbar -tearoff 0 +.mbar add cascade -label Repository -menu .mbar.repository +.mbar add cascade -label Edit -menu .mbar.edit +if {!$single_commit} { + .mbar add cascade -label Branch -menu .mbar.branch +} +.mbar add cascade -label Commit -menu .mbar.commit +if {!$single_commit} { + .mbar add cascade -label Fetch -menu .mbar.fetch + .mbar add cascade -label Pull -menu .mbar.pull + .mbar add cascade -label Push -menu .mbar.push +} +. configure -menu .mbar + +# -- Repository Menu +# +menu .mbar.repository +.mbar.repository add command \ + -label {Visualize Current Branch} \ + -command {do_gitk {}} \ + -font font_ui +if {![is_MacOSX]} { + .mbar.repository add command \ + -label {Visualize All Branches} \ + -command {do_gitk {--all}} \ + -font font_ui +} +.mbar.repository add separator + +if {!$single_commit} { + .mbar.repository add command -label {Compress Database} \ + -command do_gc \ + -font font_ui + + .mbar.repository add command -label {Verify Database} \ + -command do_fsck_objects \ + -font font_ui + + .mbar.repository add separator + + if {[is_Windows]} { + .mbar.repository add command \ + -label {Create Desktop Icon} \ + -command do_windows_shortcut \ + -font font_ui + } elseif {[is_MacOSX]} { + .mbar.repository add command \ + -label {Create Desktop Icon} \ + -command do_macosx_app \ + -font font_ui + } +} + +.mbar.repository add command -label Quit \ + -command do_quit \ + -accelerator $M1T-Q \ + -font font_ui + +# -- Edit Menu +# +menu .mbar.edit +.mbar.edit add command -label Undo \ + -command {catch {[focus] edit undo}} \ + -accelerator $M1T-Z \ + -font font_ui +.mbar.edit add command -label Redo \ + -command {catch {[focus] edit redo}} \ + -accelerator $M1T-Y \ + -font font_ui +.mbar.edit add separator +.mbar.edit add command -label Cut \ + -command {catch {tk_textCut [focus]}} \ + -accelerator $M1T-X \ + -font font_ui +.mbar.edit add command -label Copy \ + -command {catch {tk_textCopy [focus]}} \ + -accelerator $M1T-C \ + -font font_ui +.mbar.edit add command -label Paste \ + -command {catch {tk_textPaste [focus]; [focus] see insert}} \ + -accelerator $M1T-V \ + -font font_ui +.mbar.edit add command -label Delete \ + -command {catch {[focus] delete sel.first sel.last}} \ + -accelerator Del \ + -font font_ui +.mbar.edit add separator +.mbar.edit add command -label {Select All} \ + -command {catch {[focus] tag add sel 0.0 end}} \ + -accelerator $M1T-A \ + -font font_ui + +# -- Branch Menu +# +if {!$single_commit} { + menu .mbar.branch + + .mbar.branch add command -label {Create...} \ + -command do_create_branch \ + -font font_ui + lappend disable_on_lock [list .mbar.branch entryconf \ + [.mbar.branch index last] -state] + + .mbar.branch add command -label {Delete...} \ + -command do_delete_branch \ + -font font_ui + lappend disable_on_lock [list .mbar.branch entryconf \ + [.mbar.branch index last] -state] +} + +# -- Commit Menu +# +menu .mbar.commit + +.mbar.commit add radiobutton \ + -label {New Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value new \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add radiobutton \ + -label {Amend Last Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value amend \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add separator + +.mbar.commit add command -label Rescan \ + -command do_rescan \ + -accelerator F5 \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add command -label {Add To Commit} \ + -command do_include_selection \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add command -label {Add All To Commit} \ + -command do_include_all \ + -accelerator $M1T-I \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add command -label {Remove From Commit} \ + -command do_remove_selection \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add command -label {Revert Changes} \ + -command do_revert_selection \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +.mbar.commit add separator + +.mbar.commit add command -label {Sign Off} \ + -command do_signoff \ + -accelerator $M1T-S \ + -font font_ui + +.mbar.commit add command -label Commit \ + -command do_commit \ + -accelerator $M1T-Return \ + -font font_ui +lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] + +# -- Transport menus +# +if {!$single_commit} { + menu .mbar.fetch + menu .mbar.pull + menu .mbar.push +} + +if {[is_MacOSX]} { + # -- Apple Menu (Mac OS X only) + # + .mbar add cascade -label Apple -menu .mbar.apple + menu .mbar.apple + + .mbar.apple add command -label "About $appname" \ + -command do_about \ + -font font_ui + .mbar.apple add command -label "$appname Options..." \ + -command do_options \ + -font font_ui +} else { + # -- Edit Menu + # + .mbar.edit add separator + .mbar.edit add command -label {Options...} \ + -command do_options \ + -font font_ui + + # -- Tools Menu + # + if {[file exists /usr/local/miga/lib/gui-miga]} { + proc do_miga {} { + global gitdir ui_status_value + if {![lock_index update]} return + set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""] + set miga_fd [open "|$cmd" r] + fconfigure $miga_fd -blocking 0 + fileevent $miga_fd readable [list miga_done $miga_fd] + set ui_status_value {Running miga...} + } + proc miga_done {fd} { + read $fd 512 + if {[eof $fd]} { + close $fd + unlock_index + rescan [list set ui_status_value {Ready.}] + } + } + .mbar add cascade -label Tools -menu .mbar.tools + menu .mbar.tools + .mbar.tools add command -label "Migrate" \ + -command do_miga \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.tools entryconf [.mbar.tools index last] -state] + } + + # -- Help Menu + # + .mbar add cascade -label Help -menu .mbar.help + menu .mbar.help + + .mbar.help add command -label "About $appname" \ + -command do_about \ + -font font_ui +} + + +# -- Branch Control +# +frame .branch \ + -borderwidth 1 \ + -relief sunken +label .branch.l1 \ + -text {Current Branch:} \ + -anchor w \ + -justify left \ + -font font_ui +label .branch.cb \ + -textvariable current_branch \ + -anchor w \ + -justify left \ + -font font_ui +pack .branch.l1 -side left +pack .branch.cb -side left -fill x +pack .branch -side top -fill x + +# -- Main Window Layout +# +panedwindow .vpane -orient vertical +panedwindow .vpane.files -orient horizontal +.vpane add .vpane.files -sticky nsew -height 100 -width 400 +pack .vpane -anchor n -side top -fill both -expand 1 + +# -- Index File List +# +frame .vpane.files.index -height 100 -width 400 +label .vpane.files.index.title -text {Modified Files} \ + -background green \ + -font font_ui +text $ui_index -background white -borderwidth 0 \ + -width 40 -height 10 \ + -font font_ui \ + -cursor $cursor_ptr \ + -yscrollcommand {.vpane.files.index.sb set} \ + -state disabled +scrollbar .vpane.files.index.sb -command [list $ui_index yview] +pack .vpane.files.index.title -side top -fill x +pack .vpane.files.index.sb -side right -fill y +pack $ui_index -side left -fill both -expand 1 +.vpane.files add .vpane.files.index -sticky nsew + +# -- Other (Add) File List +# +frame .vpane.files.other -height 100 -width 100 +label .vpane.files.other.title -text {Untracked Files} \ + -background red \ + -font font_ui +text $ui_other -background white -borderwidth 0 \ + -width 40 -height 10 \ + -font font_ui \ + -cursor $cursor_ptr \ + -yscrollcommand {.vpane.files.other.sb set} \ + -state disabled +scrollbar .vpane.files.other.sb -command [list $ui_other yview] +pack .vpane.files.other.title -side top -fill x +pack .vpane.files.other.sb -side right -fill y +pack $ui_other -side left -fill both -expand 1 +.vpane.files add .vpane.files.other -sticky nsew + +foreach i [list $ui_index $ui_other] { + $i tag conf in_diff -font font_uibold + $i tag conf in_sel \ + -background [$i cget -foreground] \ + -foreground [$i cget -background] +} +unset i + +# -- Diff and Commit Area +# +frame .vpane.lower -height 300 -width 400 +frame .vpane.lower.commarea +frame .vpane.lower.diff -relief sunken -borderwidth 1 +pack .vpane.lower.commarea -side top -fill x +pack .vpane.lower.diff -side bottom -fill both -expand 1 +.vpane add .vpane.lower -stick nsew + +# -- Commit Area Buttons +# +frame .vpane.lower.commarea.buttons +label .vpane.lower.commarea.buttons.l -text {} \ + -anchor w \ + -justify left \ + -font font_ui +pack .vpane.lower.commarea.buttons.l -side top -fill x +pack .vpane.lower.commarea.buttons -side left -fill y + +button .vpane.lower.commarea.buttons.rescan -text {Rescan} \ + -command do_rescan \ + -font font_ui +pack .vpane.lower.commarea.buttons.rescan -side top -fill x +lappend disable_on_lock \ + {.vpane.lower.commarea.buttons.rescan conf -state} + +button .vpane.lower.commarea.buttons.incall -text {Add All} \ + -command do_include_all \ + -font font_ui +pack .vpane.lower.commarea.buttons.incall -side top -fill x +lappend disable_on_lock \ + {.vpane.lower.commarea.buttons.incall conf -state} + +button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \ + -command do_signoff \ + -font font_ui +pack .vpane.lower.commarea.buttons.signoff -side top -fill x + +button .vpane.lower.commarea.buttons.commit -text {Commit} \ + -command do_commit \ + -font font_ui +pack .vpane.lower.commarea.buttons.commit -side top -fill x +lappend disable_on_lock \ + {.vpane.lower.commarea.buttons.commit conf -state} + +# -- Commit Message Buffer +# +frame .vpane.lower.commarea.buffer +frame .vpane.lower.commarea.buffer.header +set ui_comm .vpane.lower.commarea.buffer.t +set ui_coml .vpane.lower.commarea.buffer.header.l +radiobutton .vpane.lower.commarea.buffer.header.new \ + -text {New Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value new \ + -font font_ui +lappend disable_on_lock \ + [list .vpane.lower.commarea.buffer.header.new conf -state] +radiobutton .vpane.lower.commarea.buffer.header.amend \ + -text {Amend Last Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value amend \ + -font font_ui +lappend disable_on_lock \ + [list .vpane.lower.commarea.buffer.header.amend conf -state] +label $ui_coml \ + -anchor w \ + -justify left \ + -font font_ui +proc trace_commit_type {varname args} { + global ui_coml commit_type + switch -glob -- $commit_type { + initial {set txt {Initial Commit Message:}} + amend {set txt {Amended Commit Message:}} + amend-initial {set txt {Amended Initial Commit Message:}} + amend-merge {set txt {Amended Merge Commit Message:}} + merge {set txt {Merge Commit Message:}} + * {set txt {Commit Message:}} + } + $ui_coml conf -text $txt +} +trace add variable commit_type write trace_commit_type +pack $ui_coml -side left -fill x +pack .vpane.lower.commarea.buffer.header.amend -side right +pack .vpane.lower.commarea.buffer.header.new -side right + +text $ui_comm -background white -borderwidth 1 \ + -undo true \ + -maxundo 20 \ + -autoseparators true \ + -relief sunken \ + -width 75 -height 9 -wrap none \ + -font font_diff \ + -yscrollcommand {.vpane.lower.commarea.buffer.sby set} +scrollbar .vpane.lower.commarea.buffer.sby \ + -command [list $ui_comm yview] +pack .vpane.lower.commarea.buffer.header -side top -fill x +pack .vpane.lower.commarea.buffer.sby -side right -fill y +pack $ui_comm -side left -fill y +pack .vpane.lower.commarea.buffer -side left -fill y + +# -- Commit Message Buffer Context Menu +# +set ctxm .vpane.lower.commarea.buffer.ctxm +menu $ctxm -tearoff 0 +$ctxm add command \ + -label {Cut} \ + -font font_ui \ + -command {tk_textCut $ui_comm} +$ctxm add command \ + -label {Copy} \ + -font font_ui \ + -command {tk_textCopy $ui_comm} +$ctxm add command \ + -label {Paste} \ + -font font_ui \ + -command {tk_textPaste $ui_comm} +$ctxm add command \ + -label {Delete} \ + -font font_ui \ + -command {$ui_comm delete sel.first sel.last} +$ctxm add separator +$ctxm add command \ + -label {Select All} \ + -font font_ui \ + -command {$ui_comm tag add sel 0.0 end} +$ctxm add command \ + -label {Copy All} \ + -font font_ui \ + -command { + $ui_comm tag add sel 0.0 end + tk_textCopy $ui_comm + $ui_comm tag remove sel 0.0 end + } +$ctxm add separator +$ctxm add command \ + -label {Sign Off} \ + -font font_ui \ + -command do_signoff +bind_button3 $ui_comm "tk_popup $ctxm %X %Y" + +# -- Diff Header +# +set current_diff {} +set diff_actions [list] +proc trace_current_diff {varname args} { + global current_diff diff_actions file_states + if {$current_diff eq {}} { + set s {} + set f {} + set p {} + set o disabled + } else { + set p $current_diff + set s [mapdesc [lindex $file_states($p) 0] $p] + set f {File:} + set p [escape_path $p] + set o normal + } + + .vpane.lower.diff.header.status configure -text $s + .vpane.lower.diff.header.file configure -text $f + .vpane.lower.diff.header.path configure -text $p + foreach w $diff_actions { + uplevel #0 $w $o + } +} +trace add variable current_diff write trace_current_diff + +frame .vpane.lower.diff.header -background orange +label .vpane.lower.diff.header.status \ + -background orange \ + -width $max_status_desc \ + -anchor w \ + -justify left \ + -font font_ui +label .vpane.lower.diff.header.file \ + -background orange \ + -anchor w \ + -justify left \ + -font font_ui +label .vpane.lower.diff.header.path \ + -background orange \ + -anchor w \ + -justify left \ + -font font_ui +pack .vpane.lower.diff.header.status -side left +pack .vpane.lower.diff.header.file -side left +pack .vpane.lower.diff.header.path -fill x +set ctxm .vpane.lower.diff.header.ctxm +menu $ctxm -tearoff 0 +$ctxm add command \ + -label {Copy} \ + -font font_ui \ + -command { + clipboard clear + clipboard append \ + -format STRING \ + -type STRING \ + -- $current_diff + } +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y" + +# -- Diff Body +# +frame .vpane.lower.diff.body +set ui_diff .vpane.lower.diff.body.t +text $ui_diff -background white -borderwidth 0 \ + -width 80 -height 15 -wrap none \ + -font font_diff \ + -xscrollcommand {.vpane.lower.diff.body.sbx set} \ + -yscrollcommand {.vpane.lower.diff.body.sby set} \ + -state disabled +scrollbar .vpane.lower.diff.body.sbx -orient horizontal \ + -command [list $ui_diff xview] +scrollbar .vpane.lower.diff.body.sby -orient vertical \ + -command [list $ui_diff yview] +pack .vpane.lower.diff.body.sbx -side bottom -fill x +pack .vpane.lower.diff.body.sby -side right -fill y +pack $ui_diff -side left -fill both -expand 1 +pack .vpane.lower.diff.header -side top -fill x +pack .vpane.lower.diff.body -side bottom -fill both -expand 1 + +$ui_diff tag conf d_@ -font font_diffbold +$ui_diff tag conf d_+ -foreground blue +$ui_diff tag conf d_- -foreground red +$ui_diff tag conf d_++ -foreground {#00a000} +$ui_diff tag conf d_-- -foreground {#a000a0} +$ui_diff tag conf d_+- \ + -foreground red \ + -background {light goldenrod yellow} +$ui_diff tag conf d_-+ \ + -foreground blue \ + -background azure2 + +# -- Diff Body Context Menu +# +set ctxm .vpane.lower.diff.body.ctxm +menu $ctxm -tearoff 0 +$ctxm add command \ + -label {Copy} \ + -font font_ui \ + -command {tk_textCopy $ui_diff} +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add command \ + -label {Select All} \ + -font font_ui \ + -command {$ui_diff tag add sel 0.0 end} +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add command \ + -label {Copy All} \ + -font font_ui \ + -command { + $ui_diff tag add sel 0.0 end + tk_textCopy $ui_diff + $ui_diff tag remove sel 0.0 end + } +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add separator +$ctxm add command \ + -label {Decrease Font Size} \ + -font font_ui \ + -command {incr_font_size font_diff -1} +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add command \ + -label {Increase Font Size} \ + -font font_ui \ + -command {incr_font_size font_diff 1} +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add separator +$ctxm add command \ + -label {Show Less Context} \ + -font font_ui \ + -command {if {$repo_config(gui.diffcontext) >= 2} { + incr repo_config(gui.diffcontext) -1 + reshow_diff + }} +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add command \ + -label {Show More Context} \ + -font font_ui \ + -command { + incr repo_config(gui.diffcontext) + reshow_diff + } +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] +$ctxm add separator +$ctxm add command -label {Options...} \ + -font font_ui \ + -command do_options +bind_button3 $ui_diff "tk_popup $ctxm %X %Y" + +# -- Status Bar +# +set ui_status_value {Initializing...} +label .status -textvariable ui_status_value \ + -anchor w \ + -justify left \ + -borderwidth 1 \ + -relief sunken \ + -font font_ui +pack .status -anchor w -side bottom -fill x + +# -- Load geometry +# +catch { +set gm $repo_config(gui.geometry) +wm geometry . [lindex $gm 0] +.vpane sash place 0 \ + [lindex [.vpane sash coord 0] 0] \ + [lindex $gm 1] +.vpane.files sash place 0 \ + [lindex $gm 2] \ + [lindex [.vpane.files sash coord 0] 1] +unset gm +} + +# -- Key Bindings +# +bind $ui_comm <$M1B-Key-Return> {do_commit;break} +bind $ui_comm <$M1B-Key-i> {do_include_all;break} +bind $ui_comm <$M1B-Key-I> {do_include_all;break} +bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break} +bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break} +bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break} +bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break} +bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break} +bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break} +bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break} +bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break} + +bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break} +bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break} +bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break} +bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break} +bind $ui_diff <$M1B-Key-v> {break} +bind $ui_diff <$M1B-Key-V> {break} +bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break} +bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break} +bind $ui_diff {catch {%W yview scroll -1 units};break} +bind $ui_diff {catch {%W yview scroll 1 units};break} +bind $ui_diff {catch {%W xview scroll -1 units};break} +bind $ui_diff {catch {%W xview scroll 1 units};break} + +bind . do_quit +bind all do_rescan +bind all <$M1B-Key-r> do_rescan +bind all <$M1B-Key-R> do_rescan +bind . <$M1B-Key-s> do_signoff +bind . <$M1B-Key-S> do_signoff +bind . <$M1B-Key-i> do_include_all +bind . <$M1B-Key-I> do_include_all +bind . <$M1B-Key-Return> do_commit +bind all <$M1B-Key-q> do_quit +bind all <$M1B-Key-Q> do_quit +bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} +bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} +foreach i [list $ui_index $ui_other] { + bind $i "toggle_or_diff $i %x %y; break" + bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break" + bind $i "add_range_to_selection $i %x %y; break" +} +unset i + +set file_lists($ui_index) [list] +set file_lists($ui_other) [list] + +set HEAD {} +set PARENT {} +set MERGE_HEAD [list] +set commit_type {} +set empty_tree {} +set current_branch {} +set current_diff {} +set selected_commit_type new + +wm title . "$appname ([file normalize [file dirname $gitdir]])" +focus -force $ui_comm + +# -- Warn the user about environmental problems. Cygwin's Tcl +# does *not* pass its env array onto any processes it spawns. +# This means that git processes get none of our environment. +# +if {[is_Windows]} { + set ignored_env 0 + set suggest_user {} + set msg "Possible environment issues exist. + +The following environment variables are probably +going to be ignored by any Git subprocess run +by $appname: + +" + foreach name [array names env] { + switch -regexp -- $name { + {^GIT_INDEX_FILE$} - + {^GIT_OBJECT_DIRECTORY$} - + {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} - + {^GIT_DIFF_OPTS$} - + {^GIT_EXTERNAL_DIFF$} - + {^GIT_PAGER$} - + {^GIT_TRACE$} - + {^GIT_CONFIG$} - + {^GIT_CONFIG_LOCAL$} - + {^GIT_(AUTHOR|COMMITTER)_DATE$} { + append msg " - $name\n" + incr ignored_env + } + {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} { + append msg " - $name\n" + incr ignored_env + set suggest_user $name + } + } + } + if {$ignored_env > 0} { + append msg " +This is due to a known issue with the +Tcl binary distributed by Cygwin." + + if {$suggest_user ne {}} { + append msg " + +A good replacement for $suggest_user +is placing values for the user.name and +user.email settings into your personal +~/.gitconfig file. +" + } + warn_popup $msg + } + unset ignored_env msg suggest_user name +} + +# -- Only initialize complex UI if we are going to stay running. +# +if {!$single_commit} { + load_all_remotes + load_all_heads + + populate_branch_menu .mbar.branch + populate_fetch_menu .mbar.fetch + populate_pull_menu .mbar.pull + populate_push_menu .mbar.push +} + +lock_index begin-read +after 1 do_rescan -- cgit v1.2.3 From 2f4479fb177fbbeab7c7311f3b2f1f31c6de428a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 20:04:02 -0500 Subject: git-gui: Display the git-gui version in the Help->About dialog. Now that we know what version git-gui is, the about dialog should display it to the end-user. This way users can find out what version they have before they report a problem or request a feature. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 0770ad03f9..212a093118 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2505,7 +2505,7 @@ proc do_commit {} { } proc do_about {} { - global appname copyright + global appname appvers copyright global tcl_patchLevel tk_patchLevel set w .about_dialog @@ -2534,8 +2534,9 @@ $copyright" \ -font font_ui pack $w.desc -side top -fill x -padx 5 -pady 5 - set v [exec git --version] - append v "\n\n" + set v {} + append v "$appname version $appvers\n\n" + append v "[exec git --version]\n\n" if {$tcl_patchLevel eq $tk_patchLevel} { append v "Tcl/Tk version $tcl_patchLevel" } else { -- cgit v1.2.3 From 0499b24ad664d7f6a33d4cfe4a11912ae455b039 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 20:08:20 -0500 Subject: git-gui: Display the full GPL copyright notice in about dialog. We're a true GPL program, and we're interactive. We should show the entire GPL notice and disclaimer of warranty in our about dialog upon request by the user, as well as include it in the header of our source. Perhaps overkill, but is recommended by our license. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 212a093118..cb2b459ffd 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -5,11 +5,19 @@ exec wish "$0" -- "$@" set copyright { Copyright © 2006, 2007 Shawn Pearce, Paul Mackerras. -All rights reserved. - -This program is free software; it may be used, copied, modified -and distributed under the terms of the GNU General Public Licence, -either version 2, or (at your option) any later version.} +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA} set appvers {@@GITGUI_VERSION@@} set appname [lindex [file split $argv0] end] -- cgit v1.2.3 From f1cee4e6d19919f6e333f9e671c720003a9a7cec Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 20:31:09 -0500 Subject: git-gui: Ensure version number is always current. I'm stealing the exact logic used by core Git within its own Makefile to setup the version number within scripts and executables. This way we can be sure that the version number is always updated after a commit, and that the version number also reflects when it is coming from a dirty working directory (and is thus pretty worthless). I've cleaned up some of the version display code in the about dialog too. There were simply too many blank lines in the bottom section where we showed the version data. Signed-off-by: Shawn O. Pearce --- .gitignore | 1 + GIT-VERSION-GEN | 46 ++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 15 ++++++++++++--- git-gui.sh | 7 ++++--- 4 files changed, 63 insertions(+), 6 deletions(-) create mode 100755 GIT-VERSION-GEN diff --git a/.gitignore b/.gitignore index 5bda901aeb..c714d382e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +GIT-VERSION-FILE git-citool git-gui diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN new file mode 100755 index 0000000000..79f1c527ff --- /dev/null +++ b/GIT-VERSION-GEN @@ -0,0 +1,46 @@ +#!/bin/sh + +GVF=GIT-VERSION-FILE +DEF_VER=v0.5.GIT + +LF=' +' + +# First try git-describe, then see if there is a version file +# (included in release tarballs), then default +if VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && + case "$VN" in + *$LF*) (exit 1) ;; + v[0-9]*) : happy ;; + esac +then + VN=$(echo "$VN" | sed -e 's/-/./g'); +elif test -f version +then + VN=$(cat version) || VN="$DEF_VER" +else + VN="$DEF_VER" +fi + +VN=$(expr "$VN" : v*'\(.*\)') + +dirty=$(sh -c 'git diff-index --name-only HEAD' 2>/dev/null) || dirty= +case "$dirty" in +'') + ;; +*) + VN="$VN-dirty" ;; +esac + +if test -r $GVF +then + VC=$(sed -e 's/^GIT_VERSION = //' <$GVF) +else + VC=unset +fi +test "$VN" = "$VC" || { + echo >&2 "GIT_VERSION = $VN" + echo "GIT_VERSION = $VN" >$GVF +} + + diff --git a/Makefile b/Makefile index 606bec640e..8fade69127 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,12 @@ all:: +GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE + @$(SHELL_PATH) ./GIT-VERSION-GEN +-include GIT-VERSION-FILE + SCRIPT_SH = git-gui.sh GITGUI_BUILT_INS = git-citool ALL_PROGRAMS = $(GITGUI_BUILT_INS) $(patsubst %.sh,%,$(SCRIPT_SH)) -GITGUI_VERSION := $(shell git describe) ifndef SHELL_PATH SHELL_PATH = /bin/sh @@ -20,7 +23,7 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh rm -f $@ $@+ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ - -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \ + -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ $@.sh >$@+ chmod +x $@+ mv $@+ $@ @@ -28,6 +31,9 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh $(GITGUI_BUILT_INS): git-gui rm -f $@ && ln git-gui $@ +# These can record GIT_VERSION +$(patsubst %.sh,%,$(SCRIPT_SH)): GIT-VERSION-FILE + all:: $(ALL_PROGRAMS) install: all @@ -36,4 +42,7 @@ install: all $(foreach p,$(GITGUI_BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) clean:: - rm -f $(ALL_PROGRAMS) + rm -f $(ALL_PROGRAMS) GIT-VERSION-FILE + +.PHONY: all install clean +.PHONY: .FORCE-GIT-VERSION-FILE diff --git a/git-gui.sh b/git-gui.sh index cb2b459ffd..e136e329c3 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA} -set appvers {@@GITGUI_VERSION@@} +set appvers {@@GIT_VERSION@@} set appname [lindex [file split $argv0] end] set gitdir {} @@ -2543,8 +2543,9 @@ $copyright" \ pack $w.desc -side top -fill x -padx 5 -pady 5 set v {} - append v "$appname version $appvers\n\n" - append v "[exec git --version]\n\n" + append v "$appname version $appvers\n" + append v "[exec git version]\n" + append v "\n" if {$tcl_patchLevel eq $tk_patchLevel} { append v "Tcl/Tk version $tcl_patchLevel" } else { -- cgit v1.2.3 From 98063115920338a1a6472a22c6792b13e37012b5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 20:47:31 -0500 Subject: git-gui: Allow the user to copy the version data to the clipboard. If a user wants to report an issue they will likely want to include the version number with their issue report. This may be difficult to enter if the version number includes an abbreviated commit SHA1 on the end of it. So we now give the user a context menu option on the version box which allows them to copy all of the relevant version data to the clipboard, ready for pasting into a report. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index e136e329c3..cfec89b45d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2563,8 +2563,18 @@ $copyright" \ -font font_ui pack $w.vers -side top -fill x -padx 5 -pady 5 + menu $w.ctxm -tearoff 0 + $w.ctxm add command \ + -label {Copy} \ + -font font_ui \ + -command " + clipboard clear + clipboard append -format STRING -type STRING -- \[$w.vers cget -text\] + " + bind $w "grab $w; focus $w" bind $w "destroy $w" + bind_button3 $w.vers "tk_popup $w.ctxm %X %Y; grab $w; focus $w" wm title $w "About $appname" tkwait window $w } -- cgit v1.2.3 From f7b9f6e4406458d522f9b376efdfecadf6b4ab35 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 20:53:37 -0500 Subject: git-gui: Don't offer my miga hack if its configuration file isn't present. I really hate that I have this specialized hack within git-gui, but its here. The hack shouldn't be offered unless miga's required .pvcsrc file is in the top level of the repository's working directory. If this file is missing miga will fail to startup properly, and the user cannot wouldn't be able to use it within this directory. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index cfec89b45d..e2dc931e48 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3232,7 +3232,8 @@ if {[is_MacOSX]} { # -- Tools Menu # - if {[file exists /usr/local/miga/lib/gui-miga]} { + if {[file exists /usr/local/miga/lib/gui-miga] + && [file exists .pvcsrc]} { proc do_miga {} { global gitdir ui_status_value if {![lock_index update]} return -- cgit v1.2.3 From 8ff487c737fe7a611da03c20f08888fb7f3a3550 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 21:23:21 -0500 Subject: git-gui: Suggest when running 'git gc' may be worthwhile. Users often forget to repack their object database, then start to complain about how slow it is to perform common operations after they have collected thousands of loose objects in their objects directory. A simple repack usually restores performance. During startup git-gui now asks git-count-objects how many loose objects exist, and if this number exceeds a hardcoded threshold we suggest that the user compress the database (aka run 'git gc') at this time. I've hardcoded this to 2000 objects on non-Windows systems as there the filesystems tend to handle the ~8 objects per directory just fine. On Windows NTFS and FAT are just so slow that we really start to lag when more than 200 loose objects exist, so the hardcoded threshold is much lower there. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index e2dc931e48..fb2d92d17c 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -195,6 +195,25 @@ proc info_popup {msg} { -message $msg } +proc ask_popup {msg} { + global gitdir appname + + set title $appname + if {$gitdir ne {}} { + append title { (} + append title [lindex \ + [file split [file normalize [file dirname $gitdir]]] \ + end] + append title {)} + } + return [tk_messageBox \ + -parent . \ + -icon question \ + -type yesno \ + -title $title \ + -message $msg] +} + ###################################################################### ## ## repository setup @@ -3790,5 +3809,26 @@ if {!$single_commit} { populate_push_menu .mbar.push } +# -- Only suggest a gc run if we are going to stay running. +# +if {!$single_commit} { + set object_limit 2000 + if {[is_Windows]} {set object_limit 200} + regexp {^([0-9]+) objects,} [exec git count-objects] _junk objects_current + if {$objects_current >= $object_limit} { + if {[ask_popup \ + "This repository currently has $objects_current loose objects. + +To maintain optimal performance it is strongly +recommended that you compress the database +when more than $object_limit loose objects exist. + +Compress the database now?"] eq yes} { + do_gc + } + } + unset object_limit _junk objects_current +} + lock_index begin-read after 1 do_rescan -- cgit v1.2.3 From 16d18b853b91e66219a26130c0485914c2969389 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 21:36:21 -0500 Subject: git-gui: Refactor reponame computation. We use reponame in a number of locations, and every time its always the same value. Instead of computing this multiple times with code that was copied and pasted around we can compute it once immediately after the global gitdir has been computed and set. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index fb2d92d17c..54cc5faad2 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -239,6 +239,9 @@ if {[catch {cd [file dirname $gitdir]} err]} { error_popup "No working directory [file dirname $gitdir]:\n\n$err" exit 1 } +set reponame [lindex [file split \ + [file normalize [file dirname $gitdir]]] \ + end] set single_commit 0 if {$appname eq {git-citool}} { @@ -2417,7 +2420,7 @@ proc do_include_all {} { } proc revert_helper {txt paths} { - global gitdir appname + global gitdir appname reponame global file_states current_diff if {![lock_index begin-update]} return @@ -2450,10 +2453,6 @@ proc revert_helper {txt paths} { set s "these $n files" } - set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end] - set reply [tk_dialog \ .confirm_revert \ "$appname ($reponame)" \ @@ -2599,7 +2598,7 @@ $copyright" \ } proc do_options {} { - global appname gitdir font_descs + global appname gitdir reponame font_descs global repo_config global_config global repo_config_new global_config_new @@ -2618,9 +2617,6 @@ proc do_options {} { foreach name [array names global_config] { set global_config_new($name) $global_config($name) } - set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end] set w .options_editor toplevel $w @@ -2756,11 +2752,7 @@ proc do_save_config {w} { } proc do_windows_shortcut {} { - global gitdir appname argv0 - - set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end] + global gitdir appname reponame argv0 if {[catch { set desktop [exec cygpath \ @@ -2806,11 +2798,7 @@ proc do_windows_shortcut {} { } proc do_macosx_app {} { - global gitdir appname argv0 env - - set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end] + global gitdir appname reponame argv0 env set fn [tk_getSaveFile \ -parent . \ -- cgit v1.2.3 From c950c66ed91ba8e66dd26c3b19167e6eecb47fe9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 21:48:56 -0500 Subject: git-gui: Cleanup usage of gitdir global variable. The gitdir global variable is essentially read-only, and is used rather frequently. So are appname and reponame. Needing to constantly declare 'global appname' just so we can access the value as $appname is downright annoying and redundant. So instead I'm declaring these as procedures and changing all uses to invoke the procedure rather than access the global directly. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 207 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 97 insertions(+), 110 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 54cc5faad2..b937cf2163 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2,6 +2,7 @@ # Tcl ignores the next line -*- tcl -*- \ exec wish "$0" -- "$@" +set appvers {@@GIT_VERSION@@} set copyright { Copyright © 2006, 2007 Shawn Pearce, Paul Mackerras. @@ -19,9 +20,28 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA} -set appvers {@@GIT_VERSION@@} -set appname [lindex [file split $argv0] end] -set gitdir {} +###################################################################### +## +## read only globals + +set _appname [lindex [file split $argv0] end] +set _gitdir {} +set _reponame {} + +proc appname {} { + global _appname + return $_appname +} + +proc gitdir {} { + global _gitdir + return $_gitdir +} + +proc reponame {} { + global _reponame + return $_reponame +} ###################################################################### ## @@ -133,15 +153,9 @@ proc save_config {} { } proc error_popup {msg} { - global gitdir appname - - set title $appname - if {$gitdir ne {}} { - append title { (} - append title [lindex \ - [file split [file normalize [file dirname $gitdir]]] \ - end] - append title {)} + set title [appname] + if {[reponame] ne {}} { + append title " ([reponame])" } set cmd [list tk_messageBox \ -icon error \ @@ -155,15 +169,9 @@ proc error_popup {msg} { } proc warn_popup {msg} { - global gitdir appname - - set title $appname - if {$gitdir ne {}} { - append title { (} - append title [lindex \ - [file split [file normalize [file dirname $gitdir]]] \ - end] - append title {)} + set title [appname] + if {[reponame] ne {}} { + append title " ([reponame])" } set cmd [list tk_messageBox \ -icon warning \ @@ -177,15 +185,9 @@ proc warn_popup {msg} { } proc info_popup {msg} { - global gitdir appname - - set title $appname - if {$gitdir ne {}} { - append title { (} - append title [lindex \ - [file split [file normalize [file dirname $gitdir]]] \ - end] - append title {)} + set title [appname] + if {[reponame] ne {}} { + append title " ([reponame])" } tk_messageBox \ -parent . \ @@ -196,15 +198,9 @@ proc info_popup {msg} { } proc ask_popup {msg} { - global gitdir appname - - set title $appname - if {$gitdir ne {}} { - append title { (} - append title [lindex \ - [file split [file normalize [file dirname $gitdir]]] \ - end] - append title {)} + set title [appname] + if {[reponame] ne {}} { + append title " ([reponame])" } return [tk_messageBox \ -parent . \ @@ -218,33 +214,33 @@ proc ask_popup {msg} { ## ## repository setup -if { [catch {set gitdir $env(GIT_DIR)}] - && [catch {set gitdir [exec git rev-parse --git-dir]} err]} { +if { [catch {set _gitdir $env(GIT_DIR)}] + && [catch {set _gitdir [exec git rev-parse --git-dir]} err]} { catch {wm withdraw .} error_popup "Cannot find the git directory:\n\n$err" exit 1 } -if {![file isdirectory $gitdir]} { +if {![file isdirectory $_gitdir]} { catch {wm withdraw .} - error_popup "Git directory not found:\n\n$gitdir" + error_popup "Git directory not found:\n\n$_gitdir" exit 1 } -if {[lindex [file split $gitdir] end] ne {.git}} { +if {[lindex [file split $_gitdir] end] ne {.git}} { catch {wm withdraw .} error_popup "Cannot use funny .git directory:\n\n$gitdir" exit 1 } -if {[catch {cd [file dirname $gitdir]} err]} { +if {[catch {cd [file dirname $_gitdir]} err]} { catch {wm withdraw .} - error_popup "No working directory [file dirname $gitdir]:\n\n$err" + error_popup "No working directory [file dirname $_gitdir]:\n\n$err" exit 1 } -set reponame [lindex [file split \ - [file normalize [file dirname $gitdir]]] \ +set _reponame [lindex [file split \ + [file normalize [file dirname $_gitdir]]] \ end] set single_commit 0 -if {$appname eq {git-citool}} { +if {[appname] eq {git-citool}} { set single_commit 1 } @@ -289,7 +285,7 @@ proc unlock_index {} { ## status proc repository_state {ctvar hdvar mhvar} { - global gitdir current_branch + global current_branch upvar $ctvar ct $hdvar hd $mhvar mh set mh [list] @@ -309,7 +305,7 @@ proc repository_state {ctvar hdvar mhvar} { return } - set merge_head [file join $gitdir MERGE_HEAD] + set merge_head [file join [gitdir] MERGE_HEAD] if {[file exists $merge_head]} { set ct merge set fd_mh [open $merge_head r] @@ -385,7 +381,7 @@ proc rescan {after} { } proc rescan_stage2 {fd after} { - global gitdir ui_status_value + global ui_status_value global rescan_active buf_rdi buf_rdf buf_rlo if {$fd ne {}} { @@ -396,7 +392,7 @@ proc rescan_stage2 {fd after} { set ls_others [list | git ls-files --others -z \ --exclude-per-directory=.gitignore] - set info_exclude [file join $gitdir info exclude] + set info_exclude [file join [gitdir] info exclude] if {[file readable $info_exclude]} { lappend ls_others "--exclude-from=$info_exclude" } @@ -420,9 +416,9 @@ proc rescan_stage2 {fd after} { } proc load_message {file} { - global gitdir ui_comm + global ui_comm - set f [file join $gitdir $file] + set f [file join [gitdir] $file] if {[file isfile $f]} { if {[catch {set fd [open $f r]}]} { return 0 @@ -953,9 +949,9 @@ A good commit message has the following format: } proc commit_prehook {curHEAD msg} { - global gitdir ui_status_value pch_error + global ui_status_value pch_error - set pchook [file join $gitdir hooks pre-commit] + set pchook [file join [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. @@ -1010,7 +1006,7 @@ proc commit_writetree {curHEAD msg} { proc commit_committree {fd_wt curHEAD msg} { global HEAD PARENT MERGE_HEAD commit_type - global single_commit gitdir + global single_commit global ui_status_value ui_comm selected_commit_type global file_states selected_paths rescan_active @@ -1064,20 +1060,20 @@ proc commit_committree {fd_wt curHEAD msg} { # -- Cleanup after ourselves. # - catch {file delete [file join $gitdir MERGE_HEAD]} - catch {file delete [file join $gitdir MERGE_MSG]} - catch {file delete [file join $gitdir SQUASH_MSG]} - catch {file delete [file join $gitdir GITGUI_MSG]} + catch {file delete [file join [gitdir] MERGE_HEAD]} + catch {file delete [file join [gitdir] MERGE_MSG]} + catch {file delete [file join [gitdir] SQUASH_MSG]} + catch {file delete [file join [gitdir] GITGUI_MSG]} # -- Let rerere do its thing. # - if {[file isdirectory [file join $gitdir rr-cache]]} { + if {[file isdirectory [file join [gitdir] rr-cache]]} { catch {exec git rerere} } # -- Run the post-commit hook. # - set pchook [file join $gitdir hooks post-commit] + set pchook [file join [gitdir] hooks post-commit] if {[is_Windows] && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ @@ -1736,13 +1732,13 @@ The rescan will be automatically started now. ## remote management proc load_all_remotes {} { - global gitdir repo_config + global repo_config global all_remotes tracking_branches set all_remotes [list] array unset tracking_branches - set rm_dir [file join $gitdir remotes] + set rm_dir [file join [gitdir] remotes] if {[file isdirectory $rm_dir]} { set all_remotes [glob \ -types f \ @@ -1786,7 +1782,7 @@ proc load_all_remotes {} { } proc populate_fetch_menu {m} { - global gitdir all_remotes repo_config + global all_remotes repo_config foreach r $all_remotes { set enable 0 @@ -1796,7 +1792,7 @@ proc populate_fetch_menu {m} { } } else { catch { - set fd [open [file join $gitdir remotes $r] r] + set fd [open [file join [gitdir] remotes $r] r] while {[gets $fd n] >= 0} { if {[regexp {^Pull:[ \t]*([^:]+):} $n]} { set enable 1 @@ -1817,7 +1813,7 @@ proc populate_fetch_menu {m} { } proc populate_push_menu {m} { - global gitdir all_remotes repo_config + global all_remotes repo_config foreach r $all_remotes { set enable 0 @@ -1827,7 +1823,7 @@ proc populate_push_menu {m} { } } else { catch { - set fd [open [file join $gitdir remotes $r] r] + set fd [open [file join [gitdir] remotes $r] r] while {[gets $fd n] >= 0} { if {[regexp {^Push:[ \t]*([^:]+):} $n]} { set enable 1 @@ -1848,7 +1844,7 @@ proc populate_push_menu {m} { } proc populate_pull_menu {m} { - global gitdir repo_config all_remotes disable_on_lock + global repo_config all_remotes disable_on_lock foreach remote $all_remotes { set rb_list [list] @@ -1862,7 +1858,7 @@ proc populate_pull_menu {m} { } } else { catch { - set fd [open [file join $gitdir remotes $remote] r] + set fd [open [file join [gitdir] remotes $remote] r] while {[gets $fd line] >= 0} { if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} { lappend rb_list $rb @@ -2033,8 +2029,6 @@ proc incr_font_size {font {amt 1}} { } proc hook_failed_popup {hook msg} { - global gitdir appname - set w .hookfail toplevel $w @@ -2072,9 +2066,7 @@ proc hook_failed_popup {hook msg} { bind $w "grab $w; focus $w" bind $w "destroy $w" - wm title $w "$appname ([lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end]): error" + wm title $w "[appname] ([reponame]): error" tkwait window $w } @@ -2088,8 +2080,7 @@ proc new_console {short_title long_title} { } proc console_init {w} { - global console_cr console_data - global gitdir appname M1B + global console_cr console_data M1B set console_cr($w) 1.0 toplevel $w @@ -2141,9 +2132,7 @@ proc console_init {w} { bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break" bind $w "focus $w" - wm title $w "$appname ([lindex [file split \ - [file normalize [file dirname $gitdir]]] \ - end]): [lindex $console_data($w) 0]" + wm title $w "[appname] ([reponame]): [lindex $console_data($w) 0]" return $w } @@ -2268,14 +2257,14 @@ proc do_fsck_objects {} { set is_quitting 0 proc do_quit {} { - global gitdir ui_comm is_quitting repo_config commit_type + global ui_comm is_quitting repo_config commit_type if {$is_quitting} return set is_quitting 1 # -- Stash our current commit buffer. # - set save [file join $gitdir GITGUI_MSG] + set save [file join [gitdir] GITGUI_MSG] set msg [string trim [$ui_comm get 0.0 end]] if {![string match amend* $commit_type] && [$ui_comm edit modified] @@ -2420,7 +2409,6 @@ proc do_include_all {} { } proc revert_helper {txt paths} { - global gitdir appname reponame global file_states current_diff if {![lock_index begin-update]} return @@ -2455,7 +2443,7 @@ proc revert_helper {txt paths} { set reply [tk_dialog \ .confirm_revert \ - "$appname ($reponame)" \ + "[appname] ([reponame])" \ "Revert changes in $s? Any unadded changes will be permanently lost by the revert." \ @@ -2531,14 +2519,14 @@ proc do_commit {} { } proc do_about {} { - global appname appvers copyright + global appvers copyright global tcl_patchLevel tk_patchLevel set w .about_dialog toplevel $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - label $w.header -text "About $appname" \ + label $w.header -text "About [appname]" \ -font font_uibold pack $w.header -side top -fill x @@ -2550,7 +2538,7 @@ proc do_about {} { pack $w.buttons -side bottom -fill x -pady 10 -padx 10 label $w.desc \ - -text "$appname - a commit creation tool for Git. + -text "[appname] - a commit creation tool for Git. $copyright" \ -padx 5 -pady 5 \ -justify left \ @@ -2561,7 +2549,7 @@ $copyright" \ pack $w.desc -side top -fill x -padx 5 -pady 5 set v {} - append v "$appname version $appvers\n" + append v "[appname] version $appvers\n" append v "[exec git version]\n" append v "\n" if {$tcl_patchLevel eq $tk_patchLevel} { @@ -2593,13 +2581,12 @@ $copyright" \ bind $w "grab $w; focus $w" bind $w "destroy $w" bind_button3 $w.vers "tk_popup $w.ctxm %X %Y; grab $w; focus $w" - wm title $w "About $appname" + wm title $w "About [appname]" tkwait window $w } proc do_options {} { - global appname gitdir reponame font_descs - global repo_config global_config + global repo_config global_config font_descs global repo_config_new global_config_new array unset repo_config_new @@ -2622,7 +2609,7 @@ proc do_options {} { toplevel $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - label $w.header -text "$appname Options" \ + label $w.header -text "[appname] Options" \ -font font_uibold pack $w.header -side top -fill x @@ -2641,7 +2628,7 @@ proc do_options {} { pack $w.buttons.cancel -side right pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - labelframe $w.repo -text "$reponame Repository" \ + labelframe $w.repo -text "[reponame] Repository" \ -font font_ui \ -relief raised -borderwidth 2 labelframe $w.global -text {Global (All Repositories)} \ @@ -2714,7 +2701,7 @@ proc do_options {} { bind $w "grab $w; focus $w" bind $w "destroy $w" - wm title $w "$appname ($reponame): Options" + wm title $w "[appname] ([reponame]): Options" tkwait window $w } @@ -2752,7 +2739,7 @@ proc do_save_config {w} { } proc do_windows_shortcut {} { - global gitdir appname reponame argv0 + global argv0 if {[catch { set desktop [exec cygpath \ @@ -2765,9 +2752,9 @@ proc do_windows_shortcut {} { } set fn [tk_getSaveFile \ -parent . \ - -title "$appname ($reponame): Create Desktop Icon" \ + -title "[appname] ([reponame]): Create Desktop Icon" \ -initialdir $desktop \ - -initialfile "Git $reponame.bat"] + -initialfile "Git [reponame].bat"] if {$fn != {}} { if {[catch { set fd [open $fn w] @@ -2782,7 +2769,7 @@ proc do_windows_shortcut {} { set gd [exec cygpath \ --unix \ --absolute \ - $gitdir] + [gitdir]] regsub -all ' $me "'\\''" me regsub -all ' $gd "'\\''" gd puts $fd "@ECHO Starting git-gui... Please wait..." @@ -2798,13 +2785,13 @@ proc do_windows_shortcut {} { } proc do_macosx_app {} { - global gitdir appname reponame argv0 env + global argv0 env set fn [tk_getSaveFile \ -parent . \ - -title "$appname ($reponame): Create Desktop Icon" \ + -title "[appname] ([reponame]): Create Desktop Icon" \ -initialdir [file join $env(HOME) Desktop] \ - -initialfile "Git $reponame.app"] + -initialfile "Git [reponame].app"] if {$fn != {}} { if {[catch { set Contents [file join $fn Contents] @@ -2839,7 +2826,7 @@ proc do_macosx_app {} { close $fd set fd [open $exe w] - set gd [file normalize $gitdir] + set gd [file normalize [gitdir]] set ep [file normalize [exec git --exec-path]] regsub -all ' $gd "'\\''" gd regsub -all ' $ep "'\\''" ep @@ -3223,10 +3210,10 @@ if {[is_MacOSX]} { .mbar add cascade -label Apple -menu .mbar.apple menu .mbar.apple - .mbar.apple add command -label "About $appname" \ + .mbar.apple add command -label "About [appname]" \ -command do_about \ -font font_ui - .mbar.apple add command -label "$appname Options..." \ + .mbar.apple add command -label "[appname] Options..." \ -command do_options \ -font font_ui } else { @@ -3242,7 +3229,7 @@ if {[is_MacOSX]} { if {[file exists /usr/local/miga/lib/gui-miga] && [file exists .pvcsrc]} { proc do_miga {} { - global gitdir ui_status_value + global ui_status_value if {![lock_index update]} return set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""] set miga_fd [open "|$cmd" r] @@ -3272,7 +3259,7 @@ if {[is_MacOSX]} { .mbar add cascade -label Help -menu .mbar.help menu .mbar.help - .mbar.help add command -label "About $appname" \ + .mbar.help add command -label "About [appname]" \ -command do_about \ -font font_ui } @@ -3727,7 +3714,7 @@ set current_branch {} set current_diff {} set selected_commit_type new -wm title . "$appname ([file normalize [file dirname $gitdir]])" +wm title . "[appname] ([file normalize [file dirname [gitdir]]])" focus -force $ui_comm # -- Warn the user about environmental problems. Cygwin's Tcl @@ -3741,7 +3728,7 @@ if {[is_Windows]} { The following environment variables are probably going to be ignored by any Git subprocess run -by $appname: +by [appname]: " foreach name [array names env] { -- cgit v1.2.3 From c2758a17cb268fa2bb7ff46e27b571038a9eeb5d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 21:55:05 -0500 Subject: git-gui: Allow [gitdir ...] to act as [file join [gitdir] ...]. Because it is such a common idiom to use [gitdir] along with [file join] to locate the path of an item within the .git directory of the current repository we might as well allow gitdir to act as a wrapper for the file join operation. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index b937cf2163..84205b08bf 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -33,9 +33,12 @@ proc appname {} { return $_appname } -proc gitdir {} { +proc gitdir {args} { global _gitdir - return $_gitdir + if {$args eq {}} { + return $_gitdir + } + return [eval [concat [list file join $_gitdir] $args]] } proc reponame {} { @@ -305,7 +308,7 @@ proc repository_state {ctvar hdvar mhvar} { return } - set merge_head [file join [gitdir] MERGE_HEAD] + set merge_head [gitdir MERGE_HEAD] if {[file exists $merge_head]} { set ct merge set fd_mh [open $merge_head r] @@ -392,7 +395,7 @@ proc rescan_stage2 {fd after} { set ls_others [list | git ls-files --others -z \ --exclude-per-directory=.gitignore] - set info_exclude [file join [gitdir] info exclude] + set info_exclude [gitdir info exclude] if {[file readable $info_exclude]} { lappend ls_others "--exclude-from=$info_exclude" } @@ -418,7 +421,7 @@ proc rescan_stage2 {fd after} { proc load_message {file} { global ui_comm - set f [file join [gitdir] $file] + set f [gitdir $file] if {[file isfile $f]} { if {[catch {set fd [open $f r]}]} { return 0 @@ -951,7 +954,7 @@ A good commit message has the following format: proc commit_prehook {curHEAD msg} { global ui_status_value pch_error - set pchook [file join [gitdir] hooks pre-commit] + 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. @@ -1060,20 +1063,20 @@ proc commit_committree {fd_wt curHEAD msg} { # -- Cleanup after ourselves. # - catch {file delete [file join [gitdir] MERGE_HEAD]} - catch {file delete [file join [gitdir] MERGE_MSG]} - catch {file delete [file join [gitdir] SQUASH_MSG]} - catch {file delete [file join [gitdir] GITGUI_MSG]} + catch {file delete [gitdir MERGE_HEAD]} + catch {file delete [gitdir MERGE_MSG]} + catch {file delete [gitdir SQUASH_MSG]} + catch {file delete [gitdir GITGUI_MSG]} # -- Let rerere do its thing. # - if {[file isdirectory [file join [gitdir] rr-cache]]} { + if {[file isdirectory [gitdir rr-cache]]} { catch {exec git rerere} } # -- Run the post-commit hook. # - set pchook [file join [gitdir] hooks post-commit] + set pchook [gitdir hooks post-commit] if {[is_Windows] && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ @@ -1738,7 +1741,7 @@ proc load_all_remotes {} { set all_remotes [list] array unset tracking_branches - set rm_dir [file join [gitdir] remotes] + set rm_dir [gitdir remotes] if {[file isdirectory $rm_dir]} { set all_remotes [glob \ -types f \ @@ -1792,7 +1795,7 @@ proc populate_fetch_menu {m} { } } else { catch { - set fd [open [file join [gitdir] remotes $r] r] + set fd [open [gitdir remotes $r] r] while {[gets $fd n] >= 0} { if {[regexp {^Pull:[ \t]*([^:]+):} $n]} { set enable 1 @@ -1823,7 +1826,7 @@ proc populate_push_menu {m} { } } else { catch { - set fd [open [file join [gitdir] remotes $r] r] + set fd [open [gitdir remotes $r] r] while {[gets $fd n] >= 0} { if {[regexp {^Push:[ \t]*([^:]+):} $n]} { set enable 1 @@ -1858,7 +1861,7 @@ proc populate_pull_menu {m} { } } else { catch { - set fd [open [file join [gitdir] remotes $remote] r] + set fd [open [gitdir remotes $remote] r] while {[gets $fd line] >= 0} { if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} { lappend rb_list $rb @@ -2264,7 +2267,7 @@ proc do_quit {} { # -- Stash our current commit buffer. # - set save [file join [gitdir] GITGUI_MSG] + set save [gitdir GITGUI_MSG] set msg [string trim [$ui_comm get 0.0 end]] if {![string match amend* $commit_type] && [$ui_comm edit modified] -- cgit v1.2.3 From dccfa6727e78190002364981158cb46878fdcfe2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 21:56:25 -0500 Subject: git-gui: Make the gitk starting message match our usual format. Because we usually say "Operation... please wait..." we should do the same thing when starting gitk. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 84205b08bf..0851eaeebc 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2215,7 +2215,7 @@ proc console_read {w fd after} { ## ## ui commands -set starting_gitk_msg {Please wait... Starting gitk...} +set starting_gitk_msg {Starting gitk... please wait...} proc do_gitk {revs} { global ui_status_value starting_gitk_msg -- cgit v1.2.3 From c2faa43677ec2471dadc3cc789cab27f6bc2abbb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 22:00:28 -0500 Subject: git-gui: Display the directory we are entering during startup. If the user has many git-gui icons it may be confusing when they start one which git-gui is still coming up. So on the windows systems we now include an echo statement which displays the full pathname of the working directory we are trying to enter into. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 0851eaeebc..e719314e7b 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2773,9 +2773,14 @@ proc do_windows_shortcut {} { --unix \ --absolute \ [gitdir]] + set gw [exec cygpath \ + --windows \ + --absolute \ + [file dirname [gitdir]]] regsub -all ' $me "'\\''" me regsub -all ' $gd "'\\''" gd - puts $fd "@ECHO Starting git-gui... Please wait..." + puts $fd "@ECHO Entering $gw" + puts $fd "@ECHO Starting git-gui... please wait..." puts -nonewline $fd "@\"$sh\" --login -c \"" puts -nonewline $fd "GIT_DIR='$gd'" puts -nonewline $fd " '$me'" -- cgit v1.2.3 From 0812665e578cf8ac1fe9a69b5557e3781be2d8ff Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 22:06:51 -0500 Subject: git-gui: Start file status display refactoring. I'm going to refactor the way file status information gets displayed so it more closely aligns with the way 'git-runstatus' displays the differences between HEAD<->index and index<->working directory. To that end the other file list is going to be changed to be the working directory difference. So this change renames it. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index e719314e7b..df21638cfb 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -337,7 +337,7 @@ proc PARENT {} { proc rescan {after} { global HEAD PARENT MERGE_HEAD commit_type - global ui_index ui_other ui_status_value ui_comm + global ui_index ui_workdir ui_status_value ui_comm global rescan_active file_states global repo_config @@ -561,7 +561,7 @@ proc prune_selection {} { ## diff proc clear_diff {} { - global ui_diff current_diff ui_index ui_other + global ui_diff current_diff ui_index ui_workdir $ui_diff conf -state normal $ui_diff delete 0.0 end @@ -570,7 +570,7 @@ proc clear_diff {} { set current_diff {} $ui_index tag remove in_diff 0.0 end - $ui_other tag remove in_diff 0.0 end + $ui_workdir tag remove in_diff 0.0 end } proc reshow_diff {} { @@ -1231,11 +1231,11 @@ proc push_to {remote} { ## ui helpers proc mapcol {state path} { - global all_cols ui_other + global all_cols ui_workdir if {[catch {set r $all_cols($state)}]} { puts "error: no column for state={$state} $path" - return $ui_other + return $ui_workdir } return $r } @@ -1368,19 +1368,19 @@ proc display_file {path state} { } proc display_all_files {} { - global ui_index ui_other + global ui_index ui_workdir global file_states file_lists global last_clicked selected_paths $ui_index conf -state normal - $ui_other conf -state normal + $ui_workdir conf -state normal $ui_index delete 0.0 end - $ui_other delete 0.0 end + $ui_workdir delete 0.0 end set last_clicked {} set file_lists($ui_index) [list] - set file_lists($ui_other) [list] + set file_lists($ui_workdir) [list] foreach path [lsort [array names file_states]] { set s $file_states($path) @@ -1402,7 +1402,7 @@ proc display_all_files {} { } $ui_index conf -state disabled - $ui_other conf -state disabled + $ui_workdir conf -state disabled } proc update_indexinfo {msg pathList after} { @@ -1960,7 +1960,7 @@ static unsigned char file_merge_bits[] = { } -maskdata $filemask set ui_index .vpane.files.index.list -set ui_other .vpane.files.other.list +set ui_workdir .vpane.files.workdir.list set max_status_desc 0 foreach i { {__ i plain "Unmodified"} @@ -1990,7 +1990,7 @@ foreach i { if {[lindex $i 1] eq {i}} { set all_cols([lindex $i 0]) $ui_index } else { - set all_cols([lindex $i 0]) $ui_other + set all_cols([lindex $i 0]) $ui_workdir } set all_icons([lindex $i 0]) file_[lindex $i 2] set all_descs([lindex $i 0]) [lindex $i 3] @@ -2858,7 +2858,7 @@ proc do_macosx_app {} { } proc toggle_or_diff {w x y} { - global file_states file_lists current_diff ui_index ui_other + global file_states file_lists current_diff ui_index ui_workdir global last_clicked selected_paths set pos [split [$w index @$x,$y] .] @@ -2873,7 +2873,7 @@ proc toggle_or_diff {w x y} { set last_clicked [list $w $lno] array unset selected_paths $ui_index tag remove in_sel 0.0 end - $ui_other tag remove in_sel 0.0 end + $ui_workdir tag remove in_sel 0.0 end if {$col == 0} { if {$current_diff eq $path} { @@ -3317,25 +3317,25 @@ pack .vpane.files.index.sb -side right -fill y pack $ui_index -side left -fill both -expand 1 .vpane.files add .vpane.files.index -sticky nsew -# -- Other (Add) File List +# -- Working Directory File List # -frame .vpane.files.other -height 100 -width 100 -label .vpane.files.other.title -text {Untracked Files} \ +frame .vpane.files.workdir -height 100 -width 100 +label .vpane.files.workdir.title -text {Untracked Files} \ -background red \ -font font_ui -text $ui_other -background white -borderwidth 0 \ +text $ui_workdir -background white -borderwidth 0 \ -width 40 -height 10 \ -font font_ui \ -cursor $cursor_ptr \ - -yscrollcommand {.vpane.files.other.sb set} \ + -yscrollcommand {.vpane.files.workdir.sb set} \ -state disabled -scrollbar .vpane.files.other.sb -command [list $ui_other yview] -pack .vpane.files.other.title -side top -fill x -pack .vpane.files.other.sb -side right -fill y -pack $ui_other -side left -fill both -expand 1 -.vpane.files add .vpane.files.other -sticky nsew +scrollbar .vpane.files.workdir.sb -command [list $ui_workdir yview] +pack .vpane.files.workdir.title -side top -fill x +pack .vpane.files.workdir.sb -side right -fill y +pack $ui_workdir -side left -fill both -expand 1 +.vpane.files add .vpane.files.workdir -sticky nsew -foreach i [list $ui_index $ui_other] { +foreach i [list $ui_index $ui_workdir] { $i tag conf in_diff -font font_uibold $i tag conf in_sel \ -background [$i cget -foreground] \ @@ -3703,7 +3703,7 @@ bind all <$M1B-Key-q> do_quit bind all <$M1B-Key-Q> do_quit bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} -foreach i [list $ui_index $ui_other] { +foreach i [list $ui_index $ui_workdir] { bind $i "toggle_or_diff $i %x %y; break" bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break" bind $i "add_range_to_selection $i %x %y; break" @@ -3711,7 +3711,7 @@ foreach i [list $ui_index $ui_other] { unset i set file_lists($ui_index) [list] -set file_lists($ui_other) [list] +set file_lists($ui_workdir) [list] set HEAD {} set PARENT {} -- cgit v1.2.3 From 21e409ad7f85b0557b0baa6d32f9b2064c7aa633 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 22:45:19 -0500 Subject: git-gui: Convert UI to use 'staged for commit' interface. This is a rather drastic change to the git-gui user interface, but it doesn't really look any different yet. I've taken the two lists and converted them to being "changes to be committed" and "changed but not updated". These lists correspond to the same lists output by git-runstatus based on how files differ in the HEAD<->index and the index<->working directory comparsions it performs. This change is meant to correlate with the change in Git 1.5.0 where we have brought the index more into the foreground and are trying to teach users to make use of it as part of their daily operations. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 214 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 101 insertions(+), 113 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index df21638cfb..9fa467ab9f 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -610,16 +610,7 @@ files list, to prevent possible confusion. } clear_diff - set old_w [mapcol [lindex $file_states($path) 0] $path] - set lno [lsearch -sorted $file_lists($old_w) $path] - if {$lno >= 0} { - set file_lists($old_w) \ - [lreplace $file_lists($old_w) $lno $lno] - incr lno - $old_w conf -state normal - $old_w delete $lno.0 [expr {$lno + 1}].0 - $old_w conf -state disabled - } + display_file $path __ } proc show_diff {path {w {}} {lno {}}} { @@ -1230,21 +1221,11 @@ proc push_to {remote} { ## ## ui helpers -proc mapcol {state path} { - global all_cols ui_workdir - - if {[catch {set r $all_cols($state)}]} { - puts "error: no column for state={$state} $path" - return $ui_workdir - } - return $r -} - -proc mapicon {state path} { +proc mapicon {w state path} { global all_icons - if {[catch {set r $all_icons($state)}]} { - puts "error: no icon for state={$state} $path" + if {[catch {set r $all_icons($state$w)}]} { + puts "error: no icon for $w state={$state} $path" return file_plain } return $r @@ -1307,64 +1288,69 @@ proc merge_state {path new_state {head_info {}} {index_info {}}} { return $state } +proc display_file_helper {w path icon_name old_m new_m} { + global file_lists + + if {$new_m eq {_}} { + set lno [lsearch -sorted $file_lists($w) $path] + if {$lno >= 0} { + set file_lists($w) [lreplace $file_lists($w) $lno $lno] + incr lno + $w conf -state normal + $w delete $lno.0 [expr {$lno + 1}].0 + $w conf -state disabled + } + } elseif {$old_m eq {_} && $new_m ne {_}} { + lappend file_lists($w) $path + set file_lists($w) [lsort -unique $file_lists($w)] + set lno [lsearch -sorted $file_lists($w) $path] + incr lno + $w conf -state normal + $w image create $lno.0 \ + -align center -padx 5 -pady 1 \ + -name $icon_name \ + -image [mapicon $w $new_m $path] + $w insert $lno.1 "[escape_path $path]\n" + $w conf -state disabled + } elseif {$old_m ne $new_m} { + $w conf -state normal + $w image conf $icon_name -image [mapicon $w $new_m $path] + $w conf -state disabled + } +} + proc display_file {path state} { - global file_states file_lists selected_paths + global file_states selected_paths + global ui_index ui_workdir set old_m [merge_state $path $state] set s $file_states($path) set new_m [lindex $s 0] - set new_w [mapcol $new_m $path] - set old_w [mapcol $old_m $path] - set new_icon [mapicon $new_m $path] + set icon_name [lindex $s 1] + + display_file_helper $ui_index $path $icon_name \ + [string index $old_m 0] \ + [string index $new_m 0] + display_file_helper $ui_workdir $path $icon_name \ + [string index $old_m 1] \ + [string index $new_m 1] if {$new_m eq {__}} { - set lno [lsearch -sorted $file_lists($old_w) $path] - if {$lno >= 0} { - set file_lists($old_w) \ - [lreplace $file_lists($old_w) $lno $lno] - incr lno - $old_w conf -state normal - $old_w delete $lno.0 [expr {$lno + 1}].0 - $old_w conf -state disabled - } unset file_states($path) catch {unset selected_paths($path)} - return } +} - if {$new_w ne $old_w} { - set lno [lsearch -sorted $file_lists($old_w) $path] - if {$lno >= 0} { - set file_lists($old_w) \ - [lreplace $file_lists($old_w) $lno $lno] - incr lno - $old_w conf -state normal - $old_w delete $lno.0 [expr {$lno + 1}].0 - $old_w conf -state disabled - } +proc display_all_files_helper {w path icon_name m} { + global file_lists - lappend file_lists($new_w) $path - set file_lists($new_w) [lsort $file_lists($new_w)] - set lno [lsearch -sorted $file_lists($new_w) $path] - incr lno - $new_w conf -state normal - $new_w image create $lno.0 \ - -align center -padx 5 -pady 1 \ - -name [lindex $s 1] \ - -image $new_icon - $new_w insert $lno.1 "[escape_path $path]\n" - if {[catch {set in_sel $selected_paths($path)}]} { - set in_sel 0 - } - if {$in_sel} { - $new_w tag add in_sel $lno.0 [expr {$lno + 1}].0 - } - $new_w conf -state disabled - } elseif {$new_icon ne [mapicon $old_m $path]} { - $new_w conf -state normal - $new_w image conf [lindex $s 1] -image $new_icon - $new_w conf -state disabled - } + lappend file_lists($w) $path + set lno [expr {[lindex [split [$w index end] .] 0] - 1}] + $w image create end \ + -align center -padx 5 -pady 1 \ + -name $icon_name \ + -image [mapicon $w $m $path] + $w insert end "[escape_path $path]\n" } proc display_all_files {} { @@ -1385,19 +1371,15 @@ proc display_all_files {} { foreach path [lsort [array names file_states]] { set s $file_states($path) set m [lindex $s 0] - set w [mapcol $m $path] - lappend file_lists($w) $path - set lno [expr {[lindex [split [$w index end] .] 0] - 1}] - $w image create end \ - -align center -padx 5 -pady 1 \ - -name [lindex $s 1] \ - -image [mapicon $m $path] - $w insert end "[escape_path $path]\n" - if {[catch {set in_sel $selected_paths($path)}]} { - set in_sel 0 + set icon_name [lindex $s 1] + + if {[string index $m 0] ne {_}} { + display_all_files_helper $ui_index $path \ + $icon_name [string index $m 0] } - if {$in_sel} { - $w tag add in_sel $lno.0 [expr {$lno + 1}].0 + if {[string index $m 1] ne {_}} { + display_all_files_helper $ui_workdir $path \ + $icon_name [string index $m 1] } } @@ -1961,41 +1943,47 @@ static unsigned char file_merge_bits[] = { set ui_index .vpane.files.index.list set ui_workdir .vpane.files.workdir.list + +set all_icons(_$ui_index) file_plain +set all_icons(A$ui_index) file_fulltick +set all_icons(M$ui_index) file_fulltick +set all_icons(D$ui_index) file_removed +set all_icons(U$ui_index) file_merge + +set all_icons(_$ui_workdir) file_plain +set all_icons(M$ui_workdir) file_mod +set all_icons(D$ui_workdir) file_question +set all_icons(O$ui_workdir) file_plain + set max_status_desc 0 foreach i { - {__ i plain "Unmodified"} - {_M i mod "Modified"} - {M_ i fulltick "Added to commit"} - {MM i parttick "Partially included"} - {MD i question "Added (but gone)"} - - {_O o plain "Untracked"} - {A_ o fulltick "Added by commit"} - {AM o parttick "Partially added"} - {AD o question "Added (but gone)"} - - {_D i question "Missing"} - {DD i removed "Removed by commit"} - {D_ i removed "Removed by commit"} - {DO i removed "Removed (still exists)"} - {DM i removed "Removed (but modified)"} - - {UD i merge "Merge conflicts"} - {UM i merge "Merge conflicts"} - {U_ i merge "Merge conflicts"} + {__ "Unmodified"} + {_M "Modified"} + {M_ "Added to commit"} + {MM "Partially added"} + {MD "Added (but gone)"} + + {_O "Untracked"} + {A_ "Added by commit"} + {AM "Partially added"} + {AD "Added (but gone)"} + + {_D "Missing"} + {DD "Removed by commit"} + {D_ "Removed by commit"} + {DO "Removed (still exists)"} + {DM "Removed (but modified)"} + + {UD "Merge conflicts"} + {UM "Merge conflicts"} + {U_ "Merge conflicts"} } { - if {$max_status_desc < [string length [lindex $i 3]]} { - set max_status_desc [string length [lindex $i 3]] + if {$max_status_desc < [string length [lindex $i 1]]} { + set max_status_desc [string length [lindex $i 1]] } - if {[lindex $i 1] eq {i}} { - set all_cols([lindex $i 0]) $ui_index - } else { - set all_cols([lindex $i 0]) $ui_workdir - } - set all_icons([lindex $i 0]) file_[lindex $i 2] - set all_descs([lindex $i 0]) [lindex $i 3] + set all_descs([lindex $i 0]) [lindex $i 1] } -unset filemask i +unset i ###################################################################### ## @@ -3302,7 +3290,7 @@ pack .vpane -anchor n -side top -fill both -expand 1 # -- Index File List # frame .vpane.files.index -height 100 -width 400 -label .vpane.files.index.title -text {Modified Files} \ +label .vpane.files.index.title -text {Changes To Be Committed} \ -background green \ -font font_ui text $ui_index -background white -borderwidth 0 \ @@ -3320,7 +3308,7 @@ pack $ui_index -side left -fill both -expand 1 # -- Working Directory File List # frame .vpane.files.workdir -height 100 -width 100 -label .vpane.files.workdir.title -text {Untracked Files} \ +label .vpane.files.workdir.title -text {Changed But Not Updated} \ -background red \ -font font_ui text $ui_workdir -background white -borderwidth 0 \ -- cgit v1.2.3 From b4b2b8454bca8f0636260358e3a63b9097351fc1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 22:57:19 -0500 Subject: git-gui: Correct DD file state to be only D_. Apparently my earlier suspicion that the file state DD was a bug was correct. A file which has been deleted from the working directory and from the index will always get the state of D_ during a rescan. Thus the only valid state for this to have is D_. We should always use only D_ internally during our state changes. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 9fa467ab9f..9a2b70b475 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1104,7 +1104,7 @@ proc commit_committree {fd_wt curHEAD msg} { __ - A_ - M_ - - DD { + D_ { unset file_states($path) catch {unset selected_paths($path)} } @@ -1516,7 +1516,7 @@ proc write_update_index {fd pathList totalCnt batch msg after} { AD - MD - UD - - _D {set new DD} + _D {set new D_} _M - MM - @@ -1969,7 +1969,6 @@ foreach i { {AD "Added (but gone)"} {_D "Missing"} - {DD "Removed by commit"} {D_ "Removed by commit"} {DO "Removed (still exists)"} {DM "Removed (but modified)"} @@ -2872,7 +2871,7 @@ proc toggle_or_diff {w x y} { switch -glob -- [lindex $file_states($path) 0] { A_ - M_ - - DD - + D_ - DO - DM { update_indexinfo \ -- cgit v1.2.3 From 5989a57734dfd4d6cb7eb0045a008dfdc90c2fbb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 22:58:52 -0500 Subject: git-gui: Remove invalid DM state. The DM state cannot really happen. Its implying that the file has been deleted in the index, but the file in the working directory has been modified relative to the file in the index. This is complete nonsense, the file doesn't exist in the index for it to be different against! Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 9a2b70b475..be286b0e78 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1114,8 +1114,7 @@ proc commit_committree {fd_wt curHEAD msg} { AM - AD - MM - - MD - - DM { + MD { set file_states($path) [list \ _[string index $m 1] \ [lindex $s 1] \ @@ -2872,8 +2871,7 @@ proc toggle_or_diff {w x y} { A_ - M_ - D_ - - DO - - DM { + DO { update_indexinfo \ "Removing [short_path $path] from commit" \ [list $path] \ -- cgit v1.2.3 From ac39160cd27cd4a57dbea80db6fbaec21ddb6ff4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 23:00:28 -0500 Subject: git-gui: Cleanup state descriptions. Updated the state descriptions for individual file states to try and make them more closely align with what git-runstatus might display. This way a user who is reading Git documentation will be less confused by our descriptions. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index be286b0e78..1d342392ab 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1957,24 +1957,24 @@ set all_icons(O$ui_workdir) file_plain set max_status_desc 0 foreach i { {__ "Unmodified"} - {_M "Modified"} - {M_ "Added to commit"} - {MM "Partially added"} - {MD "Added (but gone)"} - {_O "Untracked"} - {A_ "Added by commit"} - {AM "Partially added"} - {AD "Added (but gone)"} + {_M "Modified, not staged"} + {M_ "Staged for commit"} + {MM "Portions staged for commit"} + {MD "Staged for commit, missing"} + + {_O "Untracked, not staged"} + {A_ "Staged for commit"} + {AM "Portions staged for commit"} + {AD "Staged for commit, missing"} {_D "Missing"} - {D_ "Removed by commit"} - {DO "Removed (still exists)"} - {DM "Removed (but modified)"} + {D_ "Staged for removal"} + {DO "Staged for removal, still present"} - {UD "Merge conflicts"} - {UM "Merge conflicts"} - {U_ "Merge conflicts"} + {U_ "Requires merge resolution"} + {UM "Requires merge resolution"} + {UD "Requires merge resolution"} } { if {$max_status_desc < [string length [lindex $i 1]]} { set max_status_desc [string length [lindex $i 1]] -- cgit v1.2.3 From 93e912c5e62220bd3cc2d00bfe357ed8fd44d413 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 23:07:04 -0500 Subject: git-gui: Refactor add/remove proc names to align with reality. Now that core Git refers to resetting paths in the index as "unstaging" the paths we should do the same in git-gui, both internally in our code and also within the menu action name. The same follows for our staging logic, as core Git refers to this as 'add'. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 1d342392ab..84ec57366c 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2287,7 +2287,7 @@ proc do_rescan {} { rescan {set ui_status_value {Ready.}} } -proc remove_helper {txt paths} { +proc unstage_helper {txt paths} { global file_states current_diff if {![lock_index begin-update]} return @@ -2316,21 +2316,21 @@ proc remove_helper {txt paths} { } } -proc do_remove_selection {} { +proc do_unstage_selection {} { global current_diff selected_paths if {[array size selected_paths] > 0} { - remove_helper \ - {Removing selected files from commit} \ + unstage_helper \ + {Unstaging selected files from commit} \ [array names selected_paths] } elseif {$current_diff ne {}} { - remove_helper \ - "Removing [short_path $current_diff] from commit" \ + unstage_helper \ + "Unstaging [short_path $current_diff] from commit" \ [list $current_diff] } } -proc include_helper {txt paths} { +proc add_helper {txt paths} { global file_states current_diff if {![lock_index begin-update]} return @@ -2364,21 +2364,21 @@ proc include_helper {txt paths} { } } -proc do_include_selection {} { +proc do_add_selection {} { global current_diff selected_paths if {[array size selected_paths] > 0} { - include_helper \ + add_helper \ {Adding selected files} \ [array names selected_paths] } elseif {$current_diff ne {}} { - include_helper \ + add_helper \ "Adding [short_path $current_diff]" \ [list $current_diff] } } -proc do_include_all {} { +proc do_add_all {} { global file_states set paths [list] @@ -2392,7 +2392,7 @@ proc do_include_all {} { _D {lappend paths $path} } } - include_helper \ + add_helper \ {Adding all modified files} \ $paths } @@ -2873,7 +2873,7 @@ proc toggle_or_diff {w x y} { D_ - DO { update_indexinfo \ - "Removing [short_path $path] from commit" \ + "Unstaging [short_path $path] from commit" \ [list $path] \ [concat $after {set ui_status_value {Ready.}}] } @@ -3151,20 +3151,20 @@ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Add To Commit} \ - -command do_include_selection \ + -command do_add_selection \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add command -label {Add All To Commit} \ - -command do_include_all \ + -command do_add_all \ -accelerator $M1T-I \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Remove From Commit} \ - -command do_remove_selection \ +.mbar.commit add command -label {Unstage From Commit} \ + -command do_unstage_selection \ -font font_ui lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] @@ -3355,7 +3355,7 @@ lappend disable_on_lock \ {.vpane.lower.commarea.buttons.rescan conf -state} button .vpane.lower.commarea.buttons.incall -text {Add All} \ - -command do_include_all \ + -command do_add_all \ -font font_ui pack .vpane.lower.commarea.buttons.incall -side top -fill x lappend disable_on_lock \ @@ -3651,8 +3651,8 @@ unset gm # -- Key Bindings # bind $ui_comm <$M1B-Key-Return> {do_commit;break} -bind $ui_comm <$M1B-Key-i> {do_include_all;break} -bind $ui_comm <$M1B-Key-I> {do_include_all;break} +bind $ui_comm <$M1B-Key-i> {do_add_all;break} +bind $ui_comm <$M1B-Key-I> {do_add_all;break} bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break} bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break} bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break} @@ -3681,8 +3681,8 @@ bind all <$M1B-Key-r> do_rescan bind all <$M1B-Key-R> do_rescan bind . <$M1B-Key-s> do_signoff bind . <$M1B-Key-S> do_signoff -bind . <$M1B-Key-i> do_include_all -bind . <$M1B-Key-I> do_include_all +bind . <$M1B-Key-i> do_add_all +bind . <$M1B-Key-I> do_add_all bind . <$M1B-Key-Return> do_commit bind all <$M1B-Key-q> do_quit bind all <$M1B-Key-Q> do_quit -- cgit v1.2.3 From de5f6d5d178a1c8113aaca34c8f6b3842732a741 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 23:10:30 -0500 Subject: git-gui: Add or unstage based on the specific icon used. Rather than relying on the file state and just inverting it, we should look at which file icon the user clicked on. If they clicked on the one in the "Changes To Be Committed" list then they want to unstage the file. If they clicked on the icon in the "Changed But Not Updated" list then they want to add the file to the commit. This should be much more reliable about capturing the user's intent then looking at the file state. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 84ec57366c..c1ee48bf07 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2867,23 +2867,17 @@ proc toggle_or_diff {w x y} { } else { set after {} } - switch -glob -- [lindex $file_states($path) 0] { - A_ - - M_ - - D_ - - DO { + if {$w eq $ui_index} { update_indexinfo \ "Unstaging [short_path $path] from commit" \ [list $path] \ [concat $after {set ui_status_value {Ready.}}] - } - ?? { + } elseif {$w eq $ui_workdir} { update_index \ "Adding [short_path $path]" \ [list $path] \ [concat $after {set ui_status_value {Ready.}}] } - } } else { show_diff $path $w $lno } -- cgit v1.2.3 From 7d40edfa06adf7f31f787bb6379c1eb454242c19 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 23:20:17 -0500 Subject: git-gui: Refactor the revert (aka checkout-index) implementation. We can revert any file which has a valid stage 0 (is not unmerged) and which is has a working directory status of M or D. This vastly simplifies our pattern matching on file status when building up the list of files to perform a checkout-index against. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index c1ee48bf07..7bd2b87fe3 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1597,20 +1597,14 @@ proc write_checkout_index {fd pathList totalCnt batch msg after} { {incr i -1} { set path [lindex $pathList $update_index_cp] incr update_index_cp - switch -glob -- [lindex $file_states($path) 0] { - AM - - AD {set new A_} - MM - - MD {set new M_} - _M - - _D {set new __} - ?? {continue} + U? {continue} + ?M - + ?D { + puts -nonewline $fd "$path\0" + display_file $path ?_ + } } - - puts -nonewline $fd $path - puts -nonewline $fd "\0" - display_file $path $new } set ui_status_value [format \ @@ -2406,12 +2400,9 @@ proc revert_helper {txt paths} { set after {} foreach path $paths { switch -glob -- [lindex $file_states($path) 0] { - AM - - AD - - MM - - MD - - _M - - _D { + U? {continue} + ?M - + ?D { lappend pathList $path if {$path eq $current_diff} { set after {reshow_diff;} -- cgit v1.2.3 From b4b491e388627b75ed3aee5dc60e4199eae7b362 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 23:33:34 -0500 Subject: git-gui: Refactor the add to commit state filters. The list of states which are valid for update-index were a little too verbose and fed a few too many cases to the program. We can do better with less lines of code by using more pattern matching, and since we already were globbing here there's little change in runtime cost. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 50 +++++++++++++++----------------------------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 7bd2b87fe3..954b1983e8 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1512,26 +1512,15 @@ proc write_update_index {fd pathList totalCnt batch msg after} { incr update_index_cp switch -glob -- [lindex $file_states($path) 0] { - AD - - MD - - UD - - _D {set new D_} - - _M - - MM - - UM - - U_ - - M_ {set new M_} - + AD {set new __} + ?D {set new D_} _O - - AM - - A_ {set new A_} - + AM {set new A_} + U_ - + ?M {set new M_} ?? {continue} } - - puts -nonewline $fd $path - puts -nonewline $fd "\0" + puts -nonewline $fd "$path\0" display_file $path $new } @@ -2333,14 +2322,10 @@ proc add_helper {txt paths} { set after {} foreach path $paths { switch -glob -- [lindex $file_states($path) 0] { - AM - - AD - - MM - - MD - - U? - - _M - - _D - - _O { + _O - + ?M - + ?D - + U? { lappend pathList $path if {$path eq $current_diff} { set after {reshow_diff;} @@ -2377,18 +2362,13 @@ proc do_add_all {} { set paths [list] foreach path [array names file_states] { - switch -- [lindex $file_states($path) 0] { - AM - - AD - - MM - - MD - - _M - - _D {lappend paths $path} + switch -glob -- [lindex $file_states($path) 0] { + U? {continue} + ?M - + ?D {lappend paths $path} } } - add_helper \ - {Adding all modified files} \ - $paths + add_helper {Adding all changed files} $paths } proc revert_helper {txt paths} { -- cgit v1.2.3 From 31a8d1968ed12c0b98c1c34d789a66f54ecbbc13 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 23:35:39 -0500 Subject: git-gui: Simplify printing of index info to update-index. During unstaging we can simplify the way we perform the output by combining our four puts into a single call. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 954b1983e8..984535687c 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1447,10 +1447,7 @@ proc write_update_indexinfo {fd pathList totalCnt batch msg after} { set info [lindex $s 2] if {$info eq {}} continue - puts -nonewline $fd $info - puts -nonewline $fd "\t" - puts -nonewline $fd $path - puts -nonewline $fd "\0" + puts -nonewline $fd "$info\t$path\0" display_file $path $new } -- cgit v1.2.3 From 833eda736ad1ec99c30b2c4d7a565ccc406c97a0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 23:46:53 -0500 Subject: git-gui: Only permit selection in one list at a time. Now that our lists represent more defined states it no longer makes any sense to permit a user to make selections from both lists at once, as the each available operation acts only on files whose status corresponds to only one of the lists. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 984535687c..a1002ecab8 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1355,7 +1355,7 @@ proc display_all_files_helper {w path icon_name m} { proc display_all_files {} { global ui_index ui_workdir global file_states file_lists - global last_clicked selected_paths + global last_clicked $ui_index conf -state normal $ui_workdir conf -state normal @@ -2852,18 +2852,21 @@ proc toggle_or_diff {w x y} { } proc add_one_to_selection {w x y} { - global file_lists - global last_clicked selected_paths + global file_lists last_clicked selected_paths - set pos [split [$w index @$x,$y] .] - set lno [lindex $pos 0] - set col [lindex $pos 1] + set lno [lindex [split [$w index @$x,$y] .] 0] set path [lindex $file_lists($w) [expr {$lno - 1}]] if {$path eq {}} { set last_clicked {} return } + if {$last_clicked ne {} + && [lindex $last_clicked 0] ne $w} { + array unset selected_paths + [lindex $last_clicked 0] tag remove in_sel 0.0 end + } + set last_clicked [list $w $lno] if {[catch {set in_sel $selected_paths($path)}]} { set in_sel 0 @@ -2878,16 +2881,14 @@ proc add_one_to_selection {w x y} { } proc add_range_to_selection {w x y} { - global file_lists - global last_clicked selected_paths + global file_lists last_clicked selected_paths if {[lindex $last_clicked 0] ne $w} { toggle_or_diff $w $x $y return } - set pos [split [$w index @$x,$y] .] - set lno [lindex $pos 0] + set lno [lindex [split [$w index @$x,$y] .] 0] set lc [lindex $last_clicked 1] if {$lc < $lno} { set begin $lc -- cgit v1.2.3 From ab26abd483d2e63f782a4335331ba6b2cfeed1bc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 20 Jan 2007 23:52:19 -0500 Subject: git-gui: Pad the cancel/save buttons in the options window. It looks horrible to have the cancel and save buttons wedged up against each other in our options dialog. Therefore toss a 5 pixel pad between them. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index a1002ecab8..13cd1b9b4a 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2582,7 +2582,7 @@ proc do_options {} { button $w.buttons.cancel -text {Cancel} \ -font font_ui \ -command [list destroy $w] - pack $w.buttons.cancel -side right + pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 labelframe $w.repo -text "[reponame] Repository" \ -- cgit v1.2.3 From c5ab47cbe4cf9f2f3068c9bf23a666404b615176 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 01:31:14 -0500 Subject: git-gui: Implemented create branch GUI. Users may now create new branches by activating the Branch->Create menu item. This opens a dialog which lets the user enter the new branch name and select the starting revision for the new branch. For the starting revision we allow the user to either select from a list of known heads (aka local branches) or to enter an arbitrary SHA1 expression. For either creation technique we run the starting revision through rev-parse to verify it is valid before trying to create the ref with update-ref. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 13cd1b9b4a..605fc17dc5 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1622,9 +1622,25 @@ proc load_all_heads {} { set all_heads [lsort $all_heads] } -proc populate_branch_menu {m} { +proc populate_branch_menu {} { global all_heads disable_on_lock + set m .mbar.branch + set last [$m index last] + for {set i 0} {$i <= $last} {incr i} { + if {[$m type $i] eq {separator}} { + $m delete $i last + set new_dol [list] + foreach a $disable_on_lock { + if {[lindex $a 0] ne $m || [lindex $a 2] < $i} { + lappend new_dol $a + } + } + set disable_on_lock $new_dol + break + } + } + $m add separator foreach b $all_heads { $m add radiobutton \ @@ -1638,8 +1654,169 @@ proc populate_branch_menu {m} { } } +proc do_create_branch_action {w} { + global all_heads null_sha1 + global create_branch_checkout create_branch_revtype create_branch_head + + set newbranch [string trim [$w.name.t get 0.0 end]] + if {![catch {exec git show-ref --verify -- "refs/heads/$newbranch"}]} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message "Branch '$newbranch' already exists." + focus $w.name.t + return + } + if {[catch {exec git check-ref-format "heads/$newbranch"}]} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message "We do not like '$newbranch' as a branch name." + focus $w.name.t + return + } + + set rev {} + switch -- $create_branch_revtype { + head {set rev $create_branch_head} + expression {set rev [string trim [$w.from.exp.t get 0.0 end]]} + } + if {[catch {set cmt [exec git rev-parse --verify "${rev}^0"]}]} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message "Invalid starting revision: $rev" + return + } + set cmd [list git update-ref] + lappend cmd -m + lappend cmd "branch: Created from $rev" + lappend cmd "refs/heads/$newbranch" + lappend cmd $cmt + lappend cmd $null_sha1 + if {[catch {eval exec $cmd} err]} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message "Failed to create '$newbranch'.\n\n$err" + return + } + + lappend all_heads $newbranch + set all_heads [lsort $all_heads] + populate_branch_menu + + destroy $w +} + proc do_create_branch {} { - error "NOT IMPLEMENTED" + global all_heads current_branch + global create_branch_checkout create_branch_revtype create_branch_head + + set create_branch_checkout true + set create_branch_revtype head + set create_branch_head $current_branch + + set w .branch_editor + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + label $w.header -text {Create New Branch} \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.create -text Create \ + -font font_ui \ + -default active \ + -command [list do_create_branch_action $w] + pack $w.buttons.create -side right + button $w.buttons.cancel -text {Cancel} \ + -font font_ui \ + -command [list destroy $w] + pack $w.buttons.cancel -side right -padx 5 + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + labelframe $w.name \ + -text {Branch Description} \ + -font font_ui + label $w.name.l -text {Name:} -font font_ui + text $w.name.t \ + -height 1 \ + -width 40 \ + -font font_ui + bind $w.name.t "focus $w.postActions.checkout;break" + bind $w.name.t "focus $w.from.exp.t;break" + bind $w.name.t "do_create_branch_action $w;break" + bind $w.name.t { + if {{%K} ne {BackSpace} + && {%K} ne {Tab} + && {%K} ne {Escape} + && {%K} ne {Return}} { + if {%k <= 32} break + if {[string first %A {~^:?*[}] >= 0} break + } + } + pack $w.name.l -side left -padx 5 + pack $w.name.t -side left -fill x -expand 1 + pack $w.name -anchor nw -fill x -pady 5 -padx 5 + + labelframe $w.from \ + -text {Starting Revision} \ + -font font_ui + frame $w.from.head + radiobutton $w.from.head.r \ + -text {Local Branch:} \ + -value head \ + -variable create_branch_revtype \ + -font font_ui + eval tk_optionMenu $w.from.head.m create_branch_head $all_heads + pack $w.from.head.r -side left + pack $w.from.head.m -side left + frame $w.from.exp + radiobutton $w.from.exp.r \ + -text {Revision Expression:} \ + -value expression \ + -variable create_branch_revtype \ + -font font_ui + text $w.from.exp.t \ + -height 1 \ + -width 50 \ + -font font_ui + bind $w.from.exp.t "focus $w.name.t;break" + bind $w.from.exp.t "focus $w.postActions.checkout;break" + bind $w.from.exp.t "do_create_branch_action $w;break" + pack $w.from.exp.r -side left + pack $w.from.exp.t -side left -fill x -expand 1 + pack $w.from.head -padx 5 -fill x -expand 1 + pack $w.from.exp -padx 5 -fill x -expand 1 + pack $w.from -anchor nw -fill x -pady 5 -padx 5 + + labelframe $w.postActions \ + -text {Post Creation Actions} \ + -font font_ui + checkbutton $w.postActions.checkout \ + -text {Checkout after creation} \ + -offvalue false \ + -onvalue true \ + -variable create_branch_checkout \ + -font font_ui + pack $w.postActions.checkout -anchor nw + pack $w.postActions -anchor nw -fill x -pady 5 -padx 5 + + bind $w "grab $w; focus $w.name.t" + bind $w "destroy $w" + bind $w "do_create_branch_action $w;break" + wm title $w "[appname] ([reponame]): Create Branch" + tkwait window $w } proc do_delete_branch {} { @@ -3734,7 +3911,7 @@ if {!$single_commit} { load_all_remotes load_all_heads - populate_branch_menu .mbar.branch + populate_branch_menu populate_fetch_menu .mbar.fetch populate_pull_menu .mbar.pull populate_push_menu .mbar.push -- cgit v1.2.3 From bd29ebc3927780c2bc6c91abb12054a283201c15 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 01:34:55 -0500 Subject: git-gui: Bind M1-N to create branch. Creating branches is a common enough activity within a Git project that we probably should give it a keyboard accelerator. N is not currently used and seems reasonable to stand for "New Branch". To bad our menu calls it create. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 605fc17dc5..db6d014e66 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3248,6 +3248,7 @@ if {!$single_commit} { .mbar.branch add command -label {Create...} \ -command do_create_branch \ + -accelerator $M1T-N \ -font font_ui lappend disable_on_lock [list .mbar.branch entryconf \ [.mbar.branch index last] -state] @@ -3815,6 +3816,11 @@ bind $ui_diff {catch {%W yview scroll 1 units};break} bind $ui_diff {catch {%W xview scroll -1 units};break} bind $ui_diff {catch {%W xview scroll 1 units};break} +if {!$single_commit} { + bind . <$M1B-Key-n> do_create_branch + bind . <$M1B-Key-N> do_create_branch +} + bind . do_quit bind all do_rescan bind all <$M1B-Key-r> do_rescan -- cgit v1.2.3 From 887412d4e84c8249987a686aded6a7eab4345cb8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 02:14:00 -0500 Subject: git-gui: Implemented local branch deletion. Users can now delete a local branch by selecting from a list of available branches. The list automatically does not include the current branch, as deleting the current branch could be quite dangerous and should not be supported. The user may also chose to have us verify the branches are fully merged into another branch before deleting them. By default we select the current branch, matching 'git branch -d' behavior, but the user could also select any other local branch. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 2 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index db6d014e66..48b11111b2 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1713,7 +1713,6 @@ proc do_create_branch_action {w} { lappend all_heads $newbranch set all_heads [lsort $all_heads] populate_branch_menu - destroy $w } @@ -1819,8 +1818,138 @@ proc do_create_branch {} { tkwait window $w } +proc do_delete_branch_action {w} { + global all_heads + global delete_branch_checkhead delete_branch_head + + set to_delete [list] + set msg {Are you sure you want to delete the following branches? + +} + foreach i [$w.list.l curselection] { + set b [$w.list.l get $i] + if {[catch {set o [exec git rev-parse --verify $b]}]} continue + if {$delete_branch_checkhead} { + if {[catch {set m [exec git merge-base $o $delete_branch_head]}]} continue + if {$o ne $m} continue + } + lappend to_delete [list $b $o] + append msg " - $b\n" + } + if {$to_delete eq {}} { + tk_messageBox \ + -icon info \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message {No branches are able to be deleted. + +This is likely because you did not select any branches, +or all selected branches are not completely merged. +} + return + } + append msg { +It can be difficult to recover deleted branches. + +Delete the above branches?} + if {[tk_messageBox \ + -icon warning \ + -type yesno \ + -title [wm title $w] \ + -parent $w \ + -message $msg] ne yes} { + return + } + + set failed {} + foreach i $to_delete { + set b [lindex $i 0] + set o [lindex $i 1] + if {[catch {exec git update-ref -d "refs/heads/$b" $o} err]} { + append failed " - $b: $err\n" + } else { + set x [lsearch -sorted $all_heads $b] + if {$x >= 0} { + set all_heads [lreplace $all_heads $x $x] + } + } + } + + if {$failed ne {}} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message "Failed to delete branches:\n$failed" + } + + set all_heads [lsort $all_heads] + populate_branch_menu + destroy $w +} + proc do_delete_branch {} { - error "NOT IMPLEMENTED" + global all_heads current_branch + global delete_branch_checkhead delete_branch_head + + set delete_branch_checkhead 1 + set delete_branch_head $current_branch + + set w .branch_editor + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + label $w.header -text {Delete Local Branch} \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.create -text Delete \ + -font font_ui \ + -command [list do_delete_branch_action $w] + pack $w.buttons.create -side right + button $w.buttons.cancel -text {Cancel} \ + -font font_ui \ + -command [list destroy $w] + pack $w.buttons.cancel -side right -padx 5 + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + labelframe $w.list \ + -text {Local Branches} \ + -font font_ui + listbox $w.list.l \ + -height 10 \ + -width 50 \ + -selectmode extended \ + -font font_ui + foreach h $all_heads { + if {$h ne $current_branch} { + $w.list.l insert end $h + } + } + pack $w.list.l -fill both -pady 5 -padx 5 + pack $w.list -fill both -pady 5 -padx 5 + + labelframe $w.validate \ + -text {Only Delete If} \ + -font font_ui + frame $w.validate.head + checkbutton $w.validate.head.r \ + -text {Already Merged Into Local Branch:} \ + -variable delete_branch_checkhead \ + -font font_ui + eval tk_optionMenu $w.validate.head.m delete_branch_head $all_heads + pack $w.validate.head.r -side left + pack $w.validate.head.m -side left + pack $w.validate.head -padx 5 -fill x -expand 1 + pack $w.validate -anchor nw -fill x -pady 5 -padx 5 + + bind $w "grab $w; focus $w" + bind $w "destroy $w" + wm title $w "[appname] ([reponame]): Delete Branch" + tkwait window $w } proc switch_branch {b} { -- cgit v1.2.3 From 0a25f93cdaee5ef4da67c4b1db2cb06bfc88e84a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 02:21:45 -0500 Subject: git-gui: Allow users to delete branches merged upstream. Most of the time when you are deleting branches you want to delete those which have been merged into your upstream source. Typically that means it has been merged into the tip commit of some tracking branch, and the current branch (or any other head) doesn't matter. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 48b11111b2..85be9833a0 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1891,7 +1891,7 @@ Delete the above branches?} } proc do_delete_branch {} { - global all_heads current_branch + global all_heads tracking_branches current_branch global delete_branch_checkhead delete_branch_head set delete_branch_checkhead 1 @@ -1932,15 +1932,23 @@ proc do_delete_branch {} { pack $w.list.l -fill both -pady 5 -padx 5 pack $w.list -fill both -pady 5 -padx 5 + set all_trackings [list] + foreach b [array names tracking_branches] { + regsub ^refs/(heads|remotes)/ $b {} b + lappend all_trackings $b + } + labelframe $w.validate \ -text {Only Delete If} \ -font font_ui frame $w.validate.head checkbutton $w.validate.head.r \ - -text {Already Merged Into Local Branch:} \ + -text {Already Merged Into:} \ -variable delete_branch_checkhead \ -font font_ui - eval tk_optionMenu $w.validate.head.m delete_branch_head $all_heads + eval tk_optionMenu $w.validate.head.m delete_branch_head \ + $all_heads \ + [lsort -unique $all_trackings] pack $w.validate.head.r -side left pack $w.validate.head.m -side left pack $w.validate.head -padx 5 -fill x -expand 1 -- cgit v1.2.3 From 859d8057bd0194bf21fac74d196cd9c07e2fec19 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 02:27:26 -0500 Subject: git-gui: Allow creating branches from tracking heads. Sometimes you want to create a branch from a remote tracking branch. Needing to enter it in the revision expression field is very annoying, so instead let the user select it from a list of known tracking branches. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 85be9833a0..c187e9bbc2 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1656,7 +1656,8 @@ proc populate_branch_menu {} { proc do_create_branch_action {w} { global all_heads null_sha1 - global create_branch_checkout create_branch_revtype create_branch_head + global create_branch_checkout create_branch_revtype + global create_branch_head create_branch_trackinghead set newbranch [string trim [$w.name.t get 0.0 end]] if {![catch {exec git show-ref --verify -- "refs/heads/$newbranch"}]} { @@ -1683,6 +1684,7 @@ proc do_create_branch_action {w} { set rev {} switch -- $create_branch_revtype { head {set rev $create_branch_head} + tracking {set rev $create_branch_trackinghead} expression {set rev [string trim [$w.from.exp.t get 0.0 end]]} } if {[catch {set cmt [exec git rev-parse --verify "${rev}^0"]}]} { @@ -1717,12 +1719,14 @@ proc do_create_branch_action {w} { } proc do_create_branch {} { - global all_heads current_branch - global create_branch_checkout create_branch_revtype create_branch_head + global all_heads current_branch tracking_branches + global create_branch_checkout create_branch_revtype + global create_branch_head create_branch_trackinghead set create_branch_checkout true set create_branch_revtype head set create_branch_head $current_branch + set create_branch_trackinghead {} set w .branch_editor toplevel $w @@ -1768,6 +1772,16 @@ proc do_create_branch {} { pack $w.name.t -side left -fill x -expand 1 pack $w.name -anchor nw -fill x -pady 5 -padx 5 + set all_trackings [list] + foreach b [array names tracking_branches] { + regsub ^refs/(heads|remotes)/ $b {} b + lappend all_trackings $b + } + set all_trackings [lsort -unique $all_trackings] + if {$all_trackings ne {}} { + set create_branch_trackinghead [lindex $all_trackings 0] + } + labelframe $w.from \ -text {Starting Revision} \ -font font_ui @@ -1780,6 +1794,17 @@ proc do_create_branch {} { eval tk_optionMenu $w.from.head.m create_branch_head $all_heads pack $w.from.head.r -side left pack $w.from.head.m -side left + frame $w.from.tracking + radiobutton $w.from.tracking.r \ + -text {Tracking Branch:} \ + -value tracking \ + -variable create_branch_revtype \ + -font font_ui + eval tk_optionMenu $w.from.tracking.m \ + create_branch_trackinghead \ + $all_trackings + pack $w.from.tracking.r -side left + pack $w.from.tracking.m -side left frame $w.from.exp radiobutton $w.from.exp.r \ -text {Revision Expression:} \ @@ -1796,6 +1821,7 @@ proc do_create_branch {} { pack $w.from.exp.r -side left pack $w.from.exp.t -side left -fill x -expand 1 pack $w.from.head -padx 5 -fill x -expand 1 + pack $w.from.tracking -padx 5 -fill x -expand 1 pack $w.from.exp -padx 5 -fill x -expand 1 pack $w.from -anchor nw -fill x -pady 5 -padx 5 -- cgit v1.2.3 From 62efea111fe6935c5916f3537fb135bdab324264 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 03:13:13 -0500 Subject: git-gui: Use borders on text fields in branch dialog. On Mac OS X wish does not draw borders around text fields, making the field look like its not even there until the user focuses into it. I don't know the Mac OS X UI standards very well, but that just seems wrong. Other applications (e.g. Terminal.app) show their input boxes with a sunken relief, so we should do the same. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index c187e9bbc2..26b1f346fc 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1753,6 +1753,8 @@ proc do_create_branch {} { -font font_ui label $w.name.l -text {Name:} -font font_ui text $w.name.t \ + -borderwidth 1 \ + -relief sunken \ -height 1 \ -width 40 \ -font font_ui @@ -1812,6 +1814,8 @@ proc do_create_branch {} { -variable create_branch_revtype \ -font font_ui text $w.from.exp.t \ + -borderwidth 1 \ + -relief sunken \ -height 1 \ -width 50 \ -font font_ui -- cgit v1.2.3 From 6f48f3a6885e9dcc7d05a3a392723084dbf960d2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 04:19:33 -0500 Subject: git-gui: Remove 'Allow Partially Added Files' option. Now that we take the approach of core Git where we allow the user to stage their changes directly into the index all of the time there is absolutely no reason to have the Allow Partially Added Files option, nor is there a reason or desire to default that option to false. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 26b1f346fc..e82eb6bbe5 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -524,24 +524,6 @@ proc rescan_done {fd buf after} { prune_selection unlock_index display_all_files - - if {$repo_config(gui.partialinclude) ne {true}} { - set pathList [list] - foreach path [array names file_states] { - switch -- [lindex $file_states($path) 0] { - A? - - M? {lappend pathList $path} - } - } - if {$pathList ne {}} { - update_index \ - "Updating included files" \ - $pathList \ - [concat {reshow_diff;} $after] - return - } - } - reshow_diff uplevel #0 $after } @@ -918,27 +900,6 @@ A good commit message has the following format: return } - # -- Update included files if partialincludes are off. - # - if {$repo_config(gui.partialinclude) ne {true}} { - set pathList [list] - foreach path [array names file_states] { - switch -glob -- [lindex $file_states($path) 0] { - A? - - M? {lappend pathList $path} - } - } - if {$pathList ne {}} { - unlock_index - update_index \ - "Updating included files" \ - $pathList \ - [concat {lock_index update;} \ - [list commit_prehook $curHEAD $msg]] - return - } - } - commit_prehook $curHEAD $msg } @@ -2939,7 +2900,6 @@ proc do_options {} { pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 foreach option { - {b partialinclude {Allow Partially Added Files}} {b pullsummary {Show Pull Summary}} {b trustmtime {Trust File Modification Timestamps}} {i diffcontext {Number of Diff Context Lines}} @@ -3299,7 +3259,6 @@ proc apply_config {} { set default_config(gui.trustmtime) false set default_config(gui.pullsummary) true -set default_config(gui.partialinclude) false set default_config(gui.diffcontext) 5 set default_config(gui.fontui) [font configure font_ui] set default_config(gui.fontdiff) [font configure font_diff] -- cgit v1.2.3 From 6858efbda324ab30f8857a06000933bcc55b11cc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 04:28:22 -0500 Subject: git-gui: Move commit_prehook into commit_tree. The only reason the commit_prehook logic was broken out into its own proc was so it could be invoked after the current set of files that were already added to the commit could be refreshed if 'Allow Partially Added Files' was set to false. Now that we no longer even offer that option to the user there is no reason to keep this code broken out into its own procedure. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index e82eb6bbe5..e4676bf795 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -825,6 +825,7 @@ proc committer_ident {} { proc commit_tree {} { global HEAD commit_type file_states ui_comm repo_config + global ui_status_value pch_error if {![lock_index update]} return if {[committer_ident] eq {}} return @@ -900,12 +901,8 @@ A good commit message has the following format: return } - commit_prehook $curHEAD $msg -} - -proc commit_prehook {curHEAD msg} { - global ui_status_value pch_error - + # -- Run the pre-commit hook. + # set pchook [gitdir hooks pre-commit] # On Cygwin [file executable] might lie so we need to ask -- cgit v1.2.3 From 4f9d8519fb3297063392d0268c0430fe5a6a02f1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 04:51:45 -0500 Subject: git-gui: Improve the branch delete confirmation dialogs. If the user is deleting a branch which is fully merged into the selected test branch we should not confirm the delete with them, the fact that the branch is fully merged means we can recover the branch and no work will be lost. If a branch is not fully merged, we should warn the user about which branch(es) that is and continue deleting those which are fully merged. We should only delete a branch if the user disables the merge check, and in that case we should confirm with the user that a delete should occur as this may cause them to lose changes. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index e4676bf795..c9143973d7 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1811,43 +1811,43 @@ proc do_delete_branch_action {w} { global delete_branch_checkhead delete_branch_head set to_delete [list] - set msg {Are you sure you want to delete the following branches? - -} + set not_merged [list] foreach i [$w.list.l curselection] { set b [$w.list.l get $i] if {[catch {set o [exec git rev-parse --verify $b]}]} continue if {$delete_branch_checkhead} { if {[catch {set m [exec git merge-base $o $delete_branch_head]}]} continue - if {$o ne $m} continue + if {$o ne $m} { + lappend not_merged $b + continue + } } lappend to_delete [list $b $o] - append msg " - $b\n" } - if {$to_delete eq {}} { + if {$not_merged ne {}} { + set msg "The following branches are not completely merged into $delete_branch_head: + + - [join $not_merged "\n - "]" tk_messageBox \ -icon info \ -type ok \ -title [wm title $w] \ -parent $w \ - -message {No branches are able to be deleted. - -This is likely because you did not select any branches, -or all selected branches are not completely merged. -} - return + -message $msg } - append msg { -It can be difficult to recover deleted branches. + if {$to_delete eq {}} return + if {!$delete_branch_checkhead} { + set msg {Recovering deleted branches is difficult. -Delete the above branches?} - if {[tk_messageBox \ - -icon warning \ - -type yesno \ - -title [wm title $w] \ - -parent $w \ - -message $msg] ne yes} { - return +Delete the selected branches?} + if {[tk_messageBox \ + -icon warning \ + -type yesno \ + -title [wm title $w] \ + -parent $w \ + -message $msg] ne yes} { + return + } } set failed {} -- cgit v1.2.3 From 3dcdfdf015746c7ec10d915705c1a76cc9d9de31 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 04:54:01 -0500 Subject: git-gui: Don't delete the test target branch. Its possible for the user to select a branch for the merge test (while deleting branches) and also select that branch for deletion. Doing so would have bypassed our merge check for that branch, as a branch is always a strict subset of itself. So we will simply skip over a branch and not delete it if that is the branch which the user selected for the merge check. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index c9143973d7..e79a0ae073 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1816,6 +1816,7 @@ proc do_delete_branch_action {w} { set b [$w.list.l get $i] if {[catch {set o [exec git rev-parse --verify $b]}]} continue if {$delete_branch_checkhead} { + if {$b eq $delete_branch_head} continue if {[catch {set m [exec git merge-base $o $delete_branch_head]}]} continue if {$o ne $m} { lappend not_merged $b -- cgit v1.2.3 From e21594a998d71eff39d80af878162abe94ef6e17 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 04:57:11 -0500 Subject: git-gui: Attempt to checkout the new branch after creation. If the user asked us to checkout the branch after creating it then we should try to do so. This may fail, especially right now since branch switching from within git-gui is not supported. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index e79a0ae073..ae1f0248f5 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1674,6 +1674,9 @@ proc do_create_branch_action {w} { set all_heads [lsort $all_heads] populate_branch_menu destroy $w + if {$create_branch_checkout} { + switch_branch $newbranch + } } proc do_create_branch {} { @@ -1681,7 +1684,7 @@ proc do_create_branch {} { global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead - set create_branch_checkout true + set create_branch_checkout 1 set create_branch_revtype head set create_branch_head $current_branch set create_branch_trackinghead {} @@ -1792,8 +1795,6 @@ proc do_create_branch {} { -font font_ui checkbutton $w.postActions.checkout \ -text {Checkout after creation} \ - -offvalue false \ - -onvalue true \ -variable create_branch_checkout \ -font font_ui pack $w.postActions.checkout -anchor nw -- cgit v1.2.3 From 20a53c029efe15cacc26c0e6a0c980ff4bda6635 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 11:37:58 -0500 Subject: git-gui: Refactor current_diff -> current_diff_path. We now need to keep track of which side the current diff is for, HEAD<->index or index<->working directory. Consequently we need an additional "current diff" variable to tell us which side the diff is for. Since this is really only necessary in reshow_diff I'm going to declare a new global, rather than try to shove both the path and the side into current_diff. To keep things clear later on, I'm renaming current_diff to current_diff_path. There is no functionality change in this commit. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 82 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ae1f0248f5..0bfd56051d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -543,33 +543,33 @@ proc prune_selection {} { ## diff proc clear_diff {} { - global ui_diff current_diff ui_index ui_workdir + global ui_diff current_diff_path ui_index ui_workdir $ui_diff conf -state normal $ui_diff delete 0.0 end $ui_diff conf -state disabled - set current_diff {} + set current_diff_path {} $ui_index tag remove in_diff 0.0 end $ui_workdir tag remove in_diff 0.0 end } proc reshow_diff {} { - global current_diff ui_status_value file_states + global current_diff_path ui_status_value file_states - if {$current_diff eq {} - || [catch {set s $file_states($current_diff)}]} { + if {$current_diff_path eq {} + || [catch {set s $file_states($current_diff_path)}]} { clear_diff } else { - show_diff $current_diff + show_diff $current_diff_path } } proc handle_empty_diff {} { - global current_diff file_states file_lists + global current_diff_path file_states file_lists - set path $current_diff + set path $current_diff_path set s $file_states($path) if {[lindex $s 0] ne {_M}} return @@ -598,7 +598,7 @@ files list, to prevent possible confusion. proc show_diff {path {w {}} {lno {}}} { global file_states file_lists global is_3way_diff diff_active repo_config - global ui_diff current_diff ui_status_value + global ui_diff current_diff_path ui_status_value if {$diff_active || ![lock_index read]} return @@ -620,7 +620,7 @@ proc show_diff {path {w {}} {lno {}}} { set m [lindex $s 0] set is_3way_diff 0 set diff_active 1 - set current_diff $path + set current_diff_path $path set ui_status_value "Loading diff of [escape_path $path]..." set cmd [list | git diff-index] @@ -1379,7 +1379,7 @@ proc update_indexinfo {msg pathList after} { proc write_update_indexinfo {fd pathList totalCnt batch msg after} { global update_index_cp ui_status_value - global file_states current_diff + global file_states current_diff_path if {$update_index_cp >= $totalCnt} { close $fd @@ -1451,7 +1451,7 @@ proc update_index {msg pathList after} { proc write_update_index {fd pathList totalCnt batch msg after} { global update_index_cp ui_status_value - global file_states current_diff + global file_states current_diff_path if {$update_index_cp >= $totalCnt} { close $fd @@ -1527,7 +1527,7 @@ proc checkout_index {msg pathList after} { proc write_checkout_index {fd pathList totalCnt batch msg after} { global update_index_cp ui_status_value - global file_states current_diff + global file_states current_diff_path if {$update_index_cp >= $totalCnt} { close $fd @@ -2572,7 +2572,7 @@ proc do_rescan {} { } proc unstage_helper {txt paths} { - global file_states current_diff + global file_states current_diff_path if {![lock_index begin-update]} return @@ -2584,7 +2584,7 @@ proc unstage_helper {txt paths} { M? - D? { lappend pathList $path - if {$path eq $current_diff} { + if {$path eq $current_diff_path} { set after {reshow_diff;} } } @@ -2601,21 +2601,21 @@ proc unstage_helper {txt paths} { } proc do_unstage_selection {} { - global current_diff selected_paths + global current_diff_path selected_paths if {[array size selected_paths] > 0} { unstage_helper \ {Unstaging selected files from commit} \ [array names selected_paths] - } elseif {$current_diff ne {}} { + } elseif {$current_diff_path ne {}} { unstage_helper \ - "Unstaging [short_path $current_diff] from commit" \ - [list $current_diff] + "Unstaging [short_path $current_diff_path] from commit" \ + [list $current_diff_path] } } proc add_helper {txt paths} { - global file_states current_diff + global file_states current_diff_path if {![lock_index begin-update]} return @@ -2628,7 +2628,7 @@ proc add_helper {txt paths} { ?D - U? { lappend pathList $path - if {$path eq $current_diff} { + if {$path eq $current_diff_path} { set after {reshow_diff;} } } @@ -2645,16 +2645,16 @@ proc add_helper {txt paths} { } proc do_add_selection {} { - global current_diff selected_paths + global current_diff_path selected_paths if {[array size selected_paths] > 0} { add_helper \ {Adding selected files} \ [array names selected_paths] - } elseif {$current_diff ne {}} { + } elseif {$current_diff_path ne {}} { add_helper \ - "Adding [short_path $current_diff]" \ - [list $current_diff] + "Adding [short_path $current_diff_path]" \ + [list $current_diff_path] } } @@ -2673,7 +2673,7 @@ proc do_add_all {} { } proc revert_helper {txt paths} { - global file_states current_diff + global file_states current_diff_path if {![lock_index begin-update]} return @@ -2685,7 +2685,7 @@ proc revert_helper {txt paths} { ?M - ?D { lappend pathList $path - if {$path eq $current_diff} { + if {$path eq $current_diff_path} { set after {reshow_diff;} } } @@ -2724,16 +2724,16 @@ Any unadded changes will be permanently lost by the revert." \ } proc do_revert_selection {} { - global current_diff selected_paths + global current_diff_path selected_paths if {[array size selected_paths] > 0} { revert_helper \ {Reverting selected files} \ [array names selected_paths] - } elseif {$current_diff ne {}} { + } elseif {$current_diff_path ne {}} { revert_helper \ - "Reverting [short_path $current_diff]" \ - [list $current_diff] + "Reverting [short_path $current_diff_path]" \ + [list $current_diff_path] } } @@ -3115,7 +3115,7 @@ proc do_macosx_app {} { } proc toggle_or_diff {w x y} { - global file_states file_lists current_diff ui_index ui_workdir + global file_states file_lists current_diff_path ui_index ui_workdir global last_clicked selected_paths set pos [split [$w index @$x,$y] .] @@ -3133,7 +3133,7 @@ proc toggle_or_diff {w x y} { $ui_workdir tag remove in_sel 0.0 end if {$col == 0} { - if {$current_diff eq $path} { + if {$current_diff_path eq $path} { set after {reshow_diff;} } else { set after {} @@ -3739,17 +3739,17 @@ bind_button3 $ui_comm "tk_popup $ctxm %X %Y" # -- Diff Header # -set current_diff {} +set current_diff_path {} set diff_actions [list] -proc trace_current_diff {varname args} { - global current_diff diff_actions file_states - if {$current_diff eq {}} { +proc trace_current_diff_path {varname args} { + global current_diff_path diff_actions file_states + if {$current_diff_path eq {}} { set s {} set f {} set p {} set o disabled } else { - set p $current_diff + set p $current_diff_path set s [mapdesc [lindex $file_states($p) 0] $p] set f {File:} set p [escape_path $p] @@ -3763,7 +3763,7 @@ proc trace_current_diff {varname args} { uplevel #0 $w $o } } -trace add variable current_diff write trace_current_diff +trace add variable current_diff_path write trace_current_diff_path frame .vpane.lower.diff.header -background orange label .vpane.lower.diff.header.status \ @@ -3795,7 +3795,7 @@ $ctxm add command \ clipboard append \ -format STRING \ -type STRING \ - -- $current_diff + -- $current_diff_path } lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y" @@ -3975,7 +3975,7 @@ set MERGE_HEAD [list] set commit_type {} set empty_tree {} set current_branch {} -set current_diff {} +set current_diff_path {} set selected_commit_type new wm title . "[appname] ([file normalize [file dirname [gitdir]]])" -- cgit v1.2.3 From 82cb8706bb9f4f43c72e0228ad33d91c29448f3f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 11:54:16 -0500 Subject: git-gui: Remove combined diff showing behavior. The combined diff format can be very confusing, especially to new users who may not even be familiar with a standard two way diff format. So for files which are already staged for commit and which are modifed in the working directory we should show two different diffs, depending on which side the user clicked on. If the user clicks on the "Changes To Be Committed" side then we should show them the PARENT<->index difference. This is the set of changes they will actually commit. If the user clicks on the "Changed But Not Updated" side we should show them the index<->working directory difference. This is the set of changes which will not be committed, as they have not been staged into the index. This is especially useful when merging, as the "Changed But Not Updated" files are the ones that need merge conflict resolution, and the diff here is the conflict hunks and/or any evil merge created by the user. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 0bfd56051d..64c2ae30e7 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -556,13 +556,14 @@ proc clear_diff {} { } proc reshow_diff {} { - global current_diff_path ui_status_value file_states + global ui_status_value file_states + global current_diff_path current_diff_side if {$current_diff_path eq {} || [catch {set s $file_states($current_diff_path)}]} { clear_diff } else { - show_diff $current_diff_path + show_diff $current_diff_path $current_diff_side } } @@ -595,10 +596,11 @@ files list, to prevent possible confusion. display_file $path __ } -proc show_diff {path {w {}} {lno {}}} { +proc show_diff {path w {lno {}}} { global file_states file_lists global is_3way_diff diff_active repo_config - global ui_diff current_diff_path ui_status_value + global ui_diff ui_status_value ui_index ui_workdir + global current_diff_path current_diff_side if {$diff_active || ![lock_index read]} return @@ -621,20 +623,12 @@ proc show_diff {path {w {}} {lno {}}} { set is_3way_diff 0 set diff_active 1 set current_diff_path $path + set current_diff_side $w set ui_status_value "Loading diff of [escape_path $path]..." - set cmd [list | git diff-index] - lappend cmd --no-color - if {$repo_config(gui.diffcontext) > 0} { - lappend cmd "-U$repo_config(gui.diffcontext)" - } - lappend cmd -p - - switch $m { - MM { - lappend cmd -c - } - _O { + # - Git won't give us the diff, there's nothing to compare to! + # + if {$m eq {_O}} { if {[catch { set fd [open $path r] set content [read $fd] @@ -654,9 +648,23 @@ proc show_diff {path {w {}} {lno {}}} { set ui_status_value {Ready.} return } + + set cmd [list | git] + if {$w eq $ui_index} { + lappend cmd diff-index + lappend cmd --cached + } elseif {$w eq $ui_workdir} { + lappend cmd diff-files } - lappend cmd [PARENT] + lappend cmd -p + lappend cmd --no-color + if {$repo_config(gui.diffcontext) > 0} { + lappend cmd "-U$repo_config(gui.diffcontext)" + } + if {$w eq $ui_index} { + lappend cmd [PARENT] + } lappend cmd -- lappend cmd $path -- cgit v1.2.3 From 3b4db3c1a3b9be66b46d8bd64560ee3f14f1084d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 12:30:51 -0500 Subject: git-gui: Improve the display of merge conflicts. If a file has a merge conflict we want it to show up in the 'Changed But Not Updated' file list rather than the 'Changes To Be Committed' file list. This way the user can mostly ignore the left side (the HEAD<->index comparsion) while resolving a merge and instead focus on the merge conflicts, which are just shown on the right hand side. This requires detecting the U state in the index side and drawing it as though it were _, then forcing the working directory side to have a U state. We have to delay this until presentation time as we don't want to change our internal state data to be different from what Git is telling us (I tried, the patch for that was ugly and didn't work). When showing a working directory diff and its a merge conflict we don't want to use diff-files as this would wind up showing any automatically merged hunks obtained from MERGE_HEAD in the diff. These are not usually very interesting as they were completed by the system. Instead we just want to see the conflicts. Fortunately the diff porcelain-ish frontend (aka 'git diff') detects the case of an unmerged file and generates a --cc diff against HEAD and MERGE_HEAD. So we now force any working directory diff with an index state of 'U' to go through that difference path. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 64c2ae30e7..2d130faba4 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -654,7 +654,11 @@ proc show_diff {path w {lno {}}} { lappend cmd diff-index lappend cmd --cached } elseif {$w eq $ui_workdir} { - lappend cmd diff-files + if {[string index $m 0] eq {U}} { + lappend cmd diff + } else { + lappend cmd diff-files + } } lappend cmd -p @@ -1293,12 +1297,20 @@ proc display_file {path state} { set new_m [lindex $s 0] set icon_name [lindex $s 1] + set s [string index $new_m 0] + if {$s eq {U}} { + set s _ + } display_file_helper $ui_index $path $icon_name \ - [string index $old_m 0] \ - [string index $new_m 0] + [string index $old_m 0] $s + + if {[string index $new_m 0] eq {U}} { + set s U + } else { + set s [string index $new_m 1] + } display_file_helper $ui_workdir $path $icon_name \ - [string index $old_m 1] \ - [string index $new_m 1] + [string index $old_m 1] $s if {$new_m eq {__}} { unset file_states($path) @@ -1338,13 +1350,20 @@ proc display_all_files {} { set m [lindex $s 0] set icon_name [lindex $s 1] - if {[string index $m 0] ne {_}} { + set s [string index $m 0] + if {$s ne {U} && $s ne {_}} { display_all_files_helper $ui_index $path \ - $icon_name [string index $m 0] + $icon_name $s } - if {[string index $m 1] ne {_}} { + + if {[string index $m 0] eq {U}} { + set s U + } else { + set s [string index $m 1] + } + if {$s ne {_}} { display_all_files_helper $ui_workdir $path \ - $icon_name [string index $m 1] + $icon_name $s } } @@ -1479,7 +1498,13 @@ proc write_update_index {fd pathList totalCnt batch msg after} { ?D {set new D_} _O - AM {set new A_} - U_ - + U? { + if {[file exists $path]} { + set new M_ + } else { + set new D_ + } + } ?M {set new M_} ?? {continue} } @@ -2244,6 +2269,7 @@ set all_icons(U$ui_index) file_merge set all_icons(_$ui_workdir) file_plain set all_icons(M$ui_workdir) file_mod set all_icons(D$ui_workdir) file_question +set all_icons(U$ui_workdir) file_merge set all_icons(O$ui_workdir) file_plain set max_status_desc 0 @@ -2265,6 +2291,7 @@ foreach i { {DO "Staged for removal, still present"} {U_ "Requires merge resolution"} + {UU "Requires merge resolution"} {UM "Requires merge resolution"} {UD "Requires merge resolution"} } { -- cgit v1.2.3 From fec4a78590229aab648e37195a071e3aae02bfe0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 13:12:02 -0500 Subject: git-gui: Improve diff --cc viewing for unmerged files. Now that we are using 'git diff' to display unmerged working directory files we are getting 'diff --cc' output rather than 'diff --combined' output. Further the markers in the first two columns actually make sense here, we shouldn't attempt to rewrite them to something else. I've added 'diff --cc *' to the skip list in our diff viewer, as that particular line is not very interesting to display. I've completely refactored how we perform detection of the state of a line during diff parsing; we now report an error message if we don't understand the particular state of any given line. This way we know if we aren't tagging something we maybe should have tagged in the UI. I've also added special display of the standard conflict hunk markers (<<<<<<<, =======, >>>>>>>). These are formatted without a patch op as the patch op is always '+' or '++' (meaning the line has been added relative to the committed state) and are displayed in orange bold text, sort of like the @@ or @@@ marker line is at the start of each hunk. In a 3 way merge diff hunks which came from our HEAD are shown with a azure2 background, and hunks which came from the incoming MERGE_HEAD are displayed with a 'light goldenrod yellow' background. This makes the two different hunks clearly visible within the file. Hunks which are ++ or -- (added or deleted relative to both parents) are shown without any background at all. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 88 +++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 24 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 2d130faba4..5463bb98ae 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -693,6 +693,7 @@ proc read_diff {fd} { # -- Cleanup uninteresting diff header lines. # if {[string match {diff --git *} $line]} continue + if {[string match {diff --cc *} $line]} continue if {[string match {diff --combined *} $line]} continue if {[string match {--- *} $line]} continue if {[string match {+++ *} $line]} continue @@ -704,27 +705,49 @@ proc read_diff {fd} { # if {[string match {@@@ *} $line]} {set is_3way_diff 1} - # -- Reformat a 3 way diff, 'cause its too weird. - # - if {$is_3way_diff} { + if {[string match {index *} $line]} { + set tags {} + } elseif {$is_3way_diff} { set op [string range $line 0 1] switch -- $op { + { } {set tags {}} {@@} {set tags d_@} - {++} {set tags d_+ ; set op { +}} - {--} {set tags d_- ; set op { -}} - { +} {set tags d_++; set op {++}} - { -} {set tags d_--; set op {--}} - {+ } {set tags d_-+; set op {-+}} - {- } {set tags d_+-; set op {+-}} - default {set tags {}} + { +} {set tags d_s+} + { -} {set tags d_s-} + {+ } {set tags d_+s} + {- } {set tags d_-s} + {--} {set tags d_--} + {++} { + if {[regexp {^\+\+([<>]{7} |={7})} $line _g op]} { + set line [string replace $line 0 1 { }] + set tags d$op + } else { + set tags d_++ + } + } + default { + puts "error: Unhandled 3 way diff marker: {$op}" + set tags {} + } } - set line [string replace $line 0 1 $op] } else { - switch -- [string index $line 0] { - @ {set tags d_@} - + {set tags d_+} - - {set tags d_-} - default {set tags {}} + set op [string index $line 0] + switch -- $op { + { } {set tags {}} + {@} {set tags d_@} + {-} {set tags d_-} + {+} { + if {[regexp {^\+([<>]{7} |={7})} $line _g op]} { + set line [string replace $line 0 0 { }] + set tags d$op + } else { + set tags d_+ + } + } + default { + puts "error: Unhandled 2 way diff marker: {$op}" + set tags {} + } } } $ui_diff insert end $line $tags @@ -3856,16 +3879,33 @@ pack .vpane.lower.diff.header -side top -fill x pack .vpane.lower.diff.body -side bottom -fill both -expand 1 $ui_diff tag conf d_@ -font font_diffbold -$ui_diff tag conf d_+ -foreground blue -$ui_diff tag conf d_- -foreground red -$ui_diff tag conf d_++ -foreground {#00a000} -$ui_diff tag conf d_-- -foreground {#a000a0} -$ui_diff tag conf d_+- \ - -foreground red \ - -background {light goldenrod yellow} -$ui_diff tag conf d_-+ \ +$ui_diff tag conf d_+ -foreground blue +$ui_diff tag conf d_- -foreground red + +$ui_diff tag conf d_++ -foreground blue +$ui_diff tag conf d_-- -foreground red +$ui_diff tag conf d_+s \ -foreground blue \ -background azure2 +$ui_diff tag conf d_-s \ + -foreground red \ + -background azure2 +$ui_diff tag conf d_s+ \ + -foreground blue \ + -background {light goldenrod yellow} +$ui_diff tag conf d_s- \ + -foreground red \ + -background {light goldenrod yellow} + +$ui_diff tag conf d<<<<<<< \ + -foreground orange \ + -font font_diffbold +$ui_diff tag conf d======= \ + -foreground orange \ + -font font_diffbold +$ui_diff tag conf d>>>>>>> \ + -foreground orange \ + -font font_diffbold # -- Diff Body Context Menu # -- cgit v1.2.3 From 079d0d5057dd66916c8d23802d48b19235fedf09 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 13:18:11 -0500 Subject: git-gui: Fix bug in unmerged file display. We were not correctly setting the old state of an index display to _ if the index was previously unmerged. This caused us to try and update a U->M when resolving a merge conflict but we were unable to do so as the icon did not exist in the index viewer. Tk did not like being asked to modify an icon which was undefined. Now we always transform both the old and the new states for both sides (index and working directory) prior to updating the UI. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 5463bb98ae..28c71c0b2d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1320,20 +1320,27 @@ proc display_file {path state} { set new_m [lindex $s 0] set icon_name [lindex $s 1] - set s [string index $new_m 0] - if {$s eq {U}} { - set s _ + set o [string index $old_m 0] + set n [string index $new_m 0] + if {$o eq {U}} { + set o _ } - display_file_helper $ui_index $path $icon_name \ - [string index $old_m 0] $s + if {$n eq {U}} { + set n _ + } + display_file_helper $ui_index $path $icon_name $o $n + if {[string index $old_m 0] eq {U}} { + set o U + } else { + set o [string index $old_m 0] + } if {[string index $new_m 0] eq {U}} { - set s U + set n U } else { - set s [string index $new_m 1] + set n [string index $new_m 1] } - display_file_helper $ui_workdir $path $icon_name \ - [string index $old_m 1] $s + display_file_helper $ui_workdir $path $icon_name $o $n if {$new_m eq {__}} { unset file_states($path) -- cgit v1.2.3 From 6bdc929984f84318783ef320115f46151fa4f0d6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 13:22:26 -0500 Subject: git-gui: Clear diff from viewer if the side changed. If the user switches the currently shown file from one side of the UI to the other then how its diff is presented would be different. And leaving the old diff up is downright confusing. Since the diff is probably not interesting to the user after the switch we should just clear the diff viewer. This saves the user time, as they won't need to wait for us to reload the diff. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 28c71c0b2d..c4d77fafe7 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -556,14 +556,17 @@ proc clear_diff {} { } proc reshow_diff {} { - global ui_status_value file_states + global ui_status_value file_states file_lists global current_diff_path current_diff_side - if {$current_diff_path eq {} - || [catch {set s $file_states($current_diff_path)}]} { + set p $current_diff_path + if {$p eq {} + || $current_diff_side eq {} + || [catch {set s $file_states($p)}] + || [lsearch -sorted $file_lists($current_diff_side) $p] == -1} { clear_diff } else { - show_diff $current_diff_path $current_diff_side + show_diff $p $current_diff_side } } -- cgit v1.2.3 From a4b1786b954917294483feb176e6ca473e01b615 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 13:25:06 -0500 Subject: git-gui: Correct disappearing unstaged files. A prior commit tried to use the old index state for the old working directory state during a UI refresh of a file. This caused files which were being unstaged (and thus becoming unmodified) to drop out of the working directory side of the display, at least until the user performed a rescan to force the UI to redisplay everything. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index c4d77fafe7..aa8f0ba067 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1336,7 +1336,7 @@ proc display_file {path state} { if {[string index $old_m 0] eq {U}} { set o U } else { - set o [string index $old_m 0] + set o [string index $old_m 1] } if {[string index $new_m 0] eq {U}} { set n U -- cgit v1.2.3 From 68c30b4af1b1d6f95ae6724364641aa787247f0f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 13:27:43 -0500 Subject: git-gui: Add Refresh to diff viewer context menu. Sometimes you want to just force the diff to redisplay itself without rescanning every file in the filesystem (as that can be very costly on large projects and slow operating systems). Now you can force a diff-only refresh from the context menu. Previously you could also do this by reclicking on the file name in the UI, but it may not be obvious to all users, having a context menu option makes it more clear. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index aa8f0ba067..f618a60d7b 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3921,6 +3921,10 @@ $ui_diff tag conf d>>>>>>> \ # set ctxm .vpane.lower.diff.body.ctxm menu $ctxm -tearoff 0 +$ctxm add command \ + -label {Refresh} \ + -font font_ui \ + -command reshow_diff $ctxm add command \ -label {Copy} \ -font font_ui \ -- cgit v1.2.3 From 14efcc748597f8b00d362df26adf5c4b4b7777f7 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 13:34:00 -0500 Subject: git-gui: Correct unmerged file detection at commit time. Its impossible to commit an index which has unmerged stages. Unfortunately a bug in git-gui allowed the user to try to do exactly that, as we broke out of our file scanning loop as soon as we found a valid AMD index state. That's wrong, as the files are coming back from our array in pseudo-random order; an unmerged file may get returned only after all merged files. I also noticed the grammer around here in our dialog boxes still used the term 'include', so this has been updated to reflect current usage. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index f618a60d7b..32c33672d6 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -896,12 +896,12 @@ The rescan will be automatically started now. _? {continue} A? - D? - - M? {set files_ready 1; break} + M? {set files_ready 1} U? { error_popup "Unmerged files cannot be committed. File [short_path $path] has merge conflicts. -You must resolve them and include the file before committing. +You must resolve them and add the file before committing. " unlock_index return @@ -915,9 +915,9 @@ File [short_path $path] cannot be committed by this program. } } if {!$files_ready} { - error_popup {No included files to commit. + error_popup {No changes to commit. -You must include at least 1 file before you can commit. +You must add at least 1 file before you can commit. } unlock_index return -- cgit v1.2.3 From f8a1518d06378c34fa8a0d8530e5041527004af5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 13:37:53 -0500 Subject: git-gui: Pad new branch name input box. The new branch name input box was showing up too close to the labelframe border, it was basically right on top of it on Windows. This didn't look right when compared to the Starting Revision's expression input field, as that had a 5 pixel padding. So I've put the new name input box into its own frame and padded that frame by 5 pixels, making the UI more consistent. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 32c33672d6..f12be315b1 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1683,7 +1683,7 @@ proc do_create_branch_action {w} { global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead - set newbranch [string trim [$w.name.t get 0.0 end]] + set newbranch [string trim [$w.desc.name.t get 0.0 end]] if {![catch {exec git show-ref --verify -- "refs/heads/$newbranch"}]} { tk_messageBox \ -icon error \ @@ -1691,7 +1691,7 @@ proc do_create_branch_action {w} { -title [wm title $w] \ -parent $w \ -message "Branch '$newbranch' already exists." - focus $w.name.t + focus $w.desc.name.t return } if {[catch {exec git check-ref-format "heads/$newbranch"}]} { @@ -1701,7 +1701,7 @@ proc do_create_branch_action {w} { -title [wm title $w] \ -parent $w \ -message "We do not like '$newbranch' as a branch name." - focus $w.name.t + focus $w.desc.name.t return } @@ -1775,20 +1775,21 @@ proc do_create_branch {} { pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - labelframe $w.name \ + labelframe $w.desc \ -text {Branch Description} \ -font font_ui - label $w.name.l -text {Name:} -font font_ui - text $w.name.t \ + frame $w.desc.name + label $w.desc.name.l -text {Name:} -font font_ui + text $w.desc.name.t \ -borderwidth 1 \ -relief sunken \ -height 1 \ -width 40 \ -font font_ui - bind $w.name.t "focus $w.postActions.checkout;break" - bind $w.name.t "focus $w.from.exp.t;break" - bind $w.name.t "do_create_branch_action $w;break" - bind $w.name.t { + bind $w.desc.name.t "focus $w.postActions.checkout;break" + bind $w.desc.name.t "focus $w.from.exp.t;break" + bind $w.desc.name.t "do_create_branch_action $w;break" + bind $w.desc.name.t { if {{%K} ne {BackSpace} && {%K} ne {Tab} && {%K} ne {Escape} @@ -1797,9 +1798,10 @@ proc do_create_branch {} { if {[string first %A {~^:?*[}] >= 0} break } } - pack $w.name.l -side left -padx 5 - pack $w.name.t -side left -fill x -expand 1 - pack $w.name -anchor nw -fill x -pady 5 -padx 5 + pack $w.desc.name.l -side left -padx 5 + pack $w.desc.name.t -side left -fill x -expand 1 + pack $w.desc.name -padx 5 -fill x -expand 1 + pack $w.desc -anchor nw -fill x -pady 5 -padx 5 set all_trackings [list] foreach b [array names tracking_branches] { @@ -1846,7 +1848,7 @@ proc do_create_branch {} { -height 1 \ -width 50 \ -font font_ui - bind $w.from.exp.t "focus $w.name.t;break" + bind $w.from.exp.t "focus $w.desc.name.t;break" bind $w.from.exp.t "focus $w.postActions.checkout;break" bind $w.from.exp.t "do_create_branch_action $w;break" pack $w.from.exp.r -side left @@ -1866,7 +1868,7 @@ proc do_create_branch {} { pack $w.postActions.checkout -anchor nw pack $w.postActions -anchor nw -fill x -pady 5 -padx 5 - bind $w "grab $w; focus $w.name.t" + bind $w "grab $w; focus $w.desc.name.t" bind $w "destroy $w" bind $w "do_create_branch_action $w;break" wm title $w "[appname] ([reponame]): Create Branch" -- cgit v1.2.3 From 66cc17d1d3bead2ebf6fbe41ea31e7ec111d891b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 13:56:38 -0500 Subject: git-gui: Use a grid layout for branch dialog. Using a stack of frames in the Starting Revision section of the new branch dialog turned out to be a mess. The varying lengths of each label caused the optionMenu widgets to be spread around the screen at unaligned locations, making the interface very kludgy looking. Now we layout the major sections of the branch dialog using grid rather than pack, allowing these widgets to line up vertically in a nice neat column. All extra space is given to column 1, which is where we have located the text fields. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 62 ++++++++++++++++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index f12be315b1..2e2d775464 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1683,7 +1683,7 @@ proc do_create_branch_action {w} { global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead - set newbranch [string trim [$w.desc.name.t get 0.0 end]] + set newbranch [string trim [$w.desc.name_t get 0.0 end]] if {![catch {exec git show-ref --verify -- "refs/heads/$newbranch"}]} { tk_messageBox \ -icon error \ @@ -1691,7 +1691,7 @@ proc do_create_branch_action {w} { -title [wm title $w] \ -parent $w \ -message "Branch '$newbranch' already exists." - focus $w.desc.name.t + focus $w.desc.name_t return } if {[catch {exec git check-ref-format "heads/$newbranch"}]} { @@ -1701,7 +1701,7 @@ proc do_create_branch_action {w} { -title [wm title $w] \ -parent $w \ -message "We do not like '$newbranch' as a branch name." - focus $w.desc.name.t + focus $w.desc.name_t return } @@ -1709,7 +1709,7 @@ proc do_create_branch_action {w} { switch -- $create_branch_revtype { head {set rev $create_branch_head} tracking {set rev $create_branch_trackinghead} - expression {set rev [string trim [$w.from.exp.t get 0.0 end]]} + expression {set rev [string trim [$w.from.exp_t get 0.0 end]]} } if {[catch {set cmt [exec git rev-parse --verify "${rev}^0"]}]} { tk_messageBox \ @@ -1778,18 +1778,18 @@ proc do_create_branch {} { labelframe $w.desc \ -text {Branch Description} \ -font font_ui - frame $w.desc.name - label $w.desc.name.l -text {Name:} -font font_ui - text $w.desc.name.t \ + label $w.desc.name_l -text {Name:} -font font_ui + text $w.desc.name_t \ -borderwidth 1 \ -relief sunken \ -height 1 \ -width 40 \ -font font_ui - bind $w.desc.name.t "focus $w.postActions.checkout;break" - bind $w.desc.name.t "focus $w.from.exp.t;break" - bind $w.desc.name.t "do_create_branch_action $w;break" - bind $w.desc.name.t { + grid $w.desc.name_l $w.desc.name_t -stick we -padx {0 5} + bind $w.desc.name_t "focus $w.postActions.checkout;break" + bind $w.desc.name_t "focus $w.from.exp_t;break" + bind $w.desc.name_t "do_create_branch_action $w;break" + bind $w.desc.name_t { if {{%K} ne {BackSpace} && {%K} ne {Tab} && {%K} ne {Escape} @@ -1798,9 +1798,7 @@ proc do_create_branch {} { if {[string first %A {~^:?*[}] >= 0} break } } - pack $w.desc.name.l -side left -padx 5 - pack $w.desc.name.t -side left -fill x -expand 1 - pack $w.desc.name -padx 5 -fill x -expand 1 + grid columnconfigure $w.desc 1 -weight 1 pack $w.desc -anchor nw -fill x -pady 5 -padx 5 set all_trackings [list] @@ -1816,46 +1814,38 @@ proc do_create_branch {} { labelframe $w.from \ -text {Starting Revision} \ -font font_ui - frame $w.from.head - radiobutton $w.from.head.r \ + radiobutton $w.from.head_r \ -text {Local Branch:} \ -value head \ -variable create_branch_revtype \ -font font_ui - eval tk_optionMenu $w.from.head.m create_branch_head $all_heads - pack $w.from.head.r -side left - pack $w.from.head.m -side left - frame $w.from.tracking - radiobutton $w.from.tracking.r \ + eval tk_optionMenu $w.from.head_m create_branch_head $all_heads + grid $w.from.head_r $w.from.head_m -sticky w + radiobutton $w.from.tracking_r \ -text {Tracking Branch:} \ -value tracking \ -variable create_branch_revtype \ -font font_ui - eval tk_optionMenu $w.from.tracking.m \ + eval tk_optionMenu $w.from.tracking_m \ create_branch_trackinghead \ $all_trackings - pack $w.from.tracking.r -side left - pack $w.from.tracking.m -side left - frame $w.from.exp - radiobutton $w.from.exp.r \ + grid $w.from.tracking_r $w.from.tracking_m -sticky w + radiobutton $w.from.exp_r \ -text {Revision Expression:} \ -value expression \ -variable create_branch_revtype \ -font font_ui - text $w.from.exp.t \ + text $w.from.exp_t \ -borderwidth 1 \ -relief sunken \ -height 1 \ -width 50 \ -font font_ui - bind $w.from.exp.t "focus $w.desc.name.t;break" - bind $w.from.exp.t "focus $w.postActions.checkout;break" - bind $w.from.exp.t "do_create_branch_action $w;break" - pack $w.from.exp.r -side left - pack $w.from.exp.t -side left -fill x -expand 1 - pack $w.from.head -padx 5 -fill x -expand 1 - pack $w.from.tracking -padx 5 -fill x -expand 1 - pack $w.from.exp -padx 5 -fill x -expand 1 + grid $w.from.exp_r $w.from.exp_t -stick we -padx {0 5} + bind $w.from.exp_t "focus $w.desc.name_t;break" + bind $w.from.exp_t "focus $w.postActions.checkout;break" + bind $w.from.exp_t "do_create_branch_action $w;break" + grid columnconfigure $w.from 1 -weight 1 pack $w.from -anchor nw -fill x -pady 5 -padx 5 labelframe $w.postActions \ @@ -1868,7 +1858,7 @@ proc do_create_branch {} { pack $w.postActions.checkout -anchor nw pack $w.postActions -anchor nw -fill x -pady 5 -padx 5 - bind $w "grab $w; focus $w.desc.name.t" + bind $w "grab $w; focus $w.desc.name_t" bind $w "destroy $w" bind $w "do_create_branch_action $w;break" wm title $w "[appname] ([reponame]): Create Branch" -- cgit v1.2.3 From 15e1374927b1dc42a575235c63852d444cf9c1ab Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 14:14:54 -0500 Subject: git-gui: Improve the merge check interface for branch deletion. Just like how we split out the local and remote branches into two different pick lists for branch creation, we should do the same thing for branch deletion. This means that there are really 3 modes of operation here: * delete only if merged into designated local branch; * delete only if merged into designated tracking (remote) branch; * delete no matter what So we now use radio buttons to select between these operations. We still default to checking for merge into the current branch, as that is probably the most commonly used behavior. It also is what core Git's command line tools do. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 73 ++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 2e2d775464..f80c3b9639 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1867,16 +1867,34 @@ proc do_create_branch {} { proc do_delete_branch_action {w} { global all_heads - global delete_branch_checkhead delete_branch_head + global delete_branch_checktype delete_branch_head delete_branch_trackinghead + + set check_rev {} + switch -- $delete_branch_checktype { + head {set check_rev $delete_branch_head} + tracking {set check_rev $delete_branch_trackinghead} + always {set check_rev {:none}} + } + if {$check_rev eq {:none}} { + set check_cmt {} + } elseif {[catch {set check_cmt [exec git rev-parse --verify "${check_rev}^0"]}]} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message "Invalid check revision: $check_rev" + return + } set to_delete [list] set not_merged [list] foreach i [$w.list.l curselection] { set b [$w.list.l get $i] if {[catch {set o [exec git rev-parse --verify $b]}]} continue - if {$delete_branch_checkhead} { - if {$b eq $delete_branch_head} continue - if {[catch {set m [exec git merge-base $o $delete_branch_head]}]} continue + if {$check_cmt ne {}} { + if {$b eq $check_rev} continue + if {[catch {set m [exec git merge-base $o $check_cmt]}]} continue if {$o ne $m} { lappend not_merged $b continue @@ -1885,7 +1903,7 @@ proc do_delete_branch_action {w} { lappend to_delete [list $b $o] } if {$not_merged ne {}} { - set msg "The following branches are not completely merged into $delete_branch_head: + set msg "The following branches are not completely merged into $check_rev: - [join $not_merged "\n - "]" tk_messageBox \ @@ -1896,7 +1914,7 @@ proc do_delete_branch_action {w} { -message $msg } if {$to_delete eq {}} return - if {!$delete_branch_checkhead} { + if {$delete_branch_checktype eq {always}} { set msg {Recovering deleted branches is difficult. Delete the selected branches?} @@ -1940,10 +1958,11 @@ Delete the selected branches?} proc do_delete_branch {} { global all_heads tracking_branches current_branch - global delete_branch_checkhead delete_branch_head + global delete_branch_checktype delete_branch_head delete_branch_trackinghead - set delete_branch_checkhead 1 + set delete_branch_checktype head set delete_branch_head $current_branch + set delete_branch_trackinghead {} set w .branch_editor toplevel $w @@ -1985,21 +2004,37 @@ proc do_delete_branch {} { regsub ^refs/(heads|remotes)/ $b {} b lappend all_trackings $b } + set all_trackings [lsort -unique $all_trackings] + if {$all_trackings ne {} && $delete_branch_trackinghead eq {}} { + set delete_branch_trackinghead [lindex $all_trackings 0] + } labelframe $w.validate \ - -text {Only Delete If} \ + -text {Delete Only If} \ + -font font_ui + radiobutton $w.validate.head_r \ + -text {Merged Into Local Branch:} \ + -value head \ + -variable delete_branch_checktype \ + -font font_ui + eval tk_optionMenu $w.validate.head_m delete_branch_head $all_heads + grid $w.validate.head_r $w.validate.head_m -sticky w + radiobutton $w.validate.tracking_r \ + -text {Merged Into Tracking Branch:} \ + -value tracking \ + -variable delete_branch_checktype \ -font font_ui - frame $w.validate.head - checkbutton $w.validate.head.r \ - -text {Already Merged Into:} \ - -variable delete_branch_checkhead \ + eval tk_optionMenu $w.validate.tracking_m \ + delete_branch_trackinghead \ + $all_trackings + grid $w.validate.tracking_r $w.validate.tracking_m -sticky w + radiobutton $w.validate.always_r \ + -text {Always (Do not perform merge checks)} \ + -value always \ + -variable delete_branch_checktype \ -font font_ui - eval tk_optionMenu $w.validate.head.m delete_branch_head \ - $all_heads \ - [lsort -unique $all_trackings] - pack $w.validate.head.r -side left - pack $w.validate.head.m -side left - pack $w.validate.head -padx 5 -fill x -expand 1 + grid $w.validate.always_r -columnspan 2 -sticky w + grid columnconfigure $w.validate 1 -weight 1 pack $w.validate -anchor nw -fill x -pady 5 -padx 5 bind $w "grab $w; focus $w" -- cgit v1.2.3 From 884fd059f8c86dc815ea7fd769f7190e3f248536 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 14:16:40 -0500 Subject: git-gui: Change rude error popup to info popup. If the user has not added any files yet they cannot commit. But telling them this isn't an error, its really just an informational note meant to push the user in the correct direction. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index f80c3b9639..d26868beca 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -915,7 +915,7 @@ File [short_path $path] cannot be committed by this program. } } if {!$files_ready} { - error_popup {No changes to commit. + info_popup {No changes to commit. You must add at least 1 file before you can commit. } -- cgit v1.2.3 From 37d2a1c9fa2f94093cb6bd5e6b2aa6a38c95593e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 14:23:51 -0500 Subject: git-gui: Correctly ignore '* Unmerged path' during diff. If a path is really unmerged, such as because it has been deleted and also modifed, we cannot obtain a diff for it. Instead Git is sending back '* Unmerged path ' for file . We should display this line as-is as our tag selecting switches don't recognize it. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index d26868beca..8e664f54e8 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -708,7 +708,8 @@ proc read_diff {fd} { # if {[string match {@@@ *} $line]} {set is_3way_diff 1} - if {[string match {index *} $line]} { + if {[string match {index *} $line] + || [regexp {^\* Unmerged path } $line]} { set tags {} } elseif {$is_3way_diff} { set op [string range $line 0 1] -- cgit v1.2.3 From ca52156618ce374711e37c8d633f0ee30cdd58c3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 14:49:45 -0500 Subject: git-gui: Make diff viewer colors match gitk's defaults. Because users who use git-gui are likely to also be using gitk, we should at least match gitk's default colors and formatting within the diff viewer. Unfortunately this meant that I needed to change the background colors of the hunks in a 'diff --cc' output, as the green used for 'added line' was completely unreadable on the old color. We now use ivory1 to show hunks which came from HEAD/parent^1, which are the portions that the current branch has contributed, and are probably the user's own changes. We use a very light blue for the portions which came from FETCH_HEAD, as this makes the changes made by the other branch stand out more in the diff. I've also modified the hunk header lines to be blue, as that is how gitk is showing them. Apparently I forgot to raise the sel tag above everything else in the diff viewer, which meant that selections in the diff viewer were not visible if they were made on a 'diff --cc' hunk which had a background. Its now the higest priority tag, ensuring the selection is always visible and readable. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 8e664f54e8..39daa745de 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3916,24 +3916,24 @@ pack $ui_diff -side left -fill both -expand 1 pack .vpane.lower.diff.header -side top -fill x pack .vpane.lower.diff.body -side bottom -fill both -expand 1 -$ui_diff tag conf d_@ -font font_diffbold -$ui_diff tag conf d_+ -foreground blue +$ui_diff tag conf d_@ -foreground blue -font font_diffbold +$ui_diff tag conf d_+ -foreground {#00a000} $ui_diff tag conf d_- -foreground red -$ui_diff tag conf d_++ -foreground blue +$ui_diff tag conf d_++ -foreground {#00a000} $ui_diff tag conf d_-- -foreground red $ui_diff tag conf d_+s \ - -foreground blue \ - -background azure2 + -foreground {#00a000} \ + -background {#e2effa} $ui_diff tag conf d_-s \ -foreground red \ - -background azure2 + -background {#e2effa} $ui_diff tag conf d_s+ \ - -foreground blue \ - -background {light goldenrod yellow} + -foreground {#00a000} \ + -background ivory1 $ui_diff tag conf d_s- \ -foreground red \ - -background {light goldenrod yellow} + -background ivory1 $ui_diff tag conf d<<<<<<< \ -foreground orange \ @@ -3945,6 +3945,8 @@ $ui_diff tag conf d>>>>>>> \ -foreground orange \ -font font_diffbold +$ui_diff tag raise sel + # -- Diff Body Context Menu # set ctxm .vpane.lower.diff.body.ctxm -- cgit v1.2.3 From 3c2369773929e299002d5e4e7737ea769ddc3bf8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 14:58:01 -0500 Subject: git-gui: Never line wrap in file lists. Some of my file paths in some of my repositories are very long, this is rather typical in Java projects where the path name contains a deep package structure and then the file name itself is rather long and (hopefully) descriptive. Seeing these paths line wrap in the file lists looks absolutely horrible. The entire rendering is almost unreadable. Now we draw both horizontal and vertical scrollbars for both file lists, and we never line wrap within the list text itself. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 39daa745de..a0c87e5ab4 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3654,13 +3654,17 @@ label .vpane.files.index.title -text {Changes To Be Committed} \ -font font_ui text $ui_index -background white -borderwidth 0 \ -width 40 -height 10 \ + -wrap none \ -font font_ui \ -cursor $cursor_ptr \ - -yscrollcommand {.vpane.files.index.sb set} \ + -xscrollcommand {.vpane.files.index.sx set} \ + -yscrollcommand {.vpane.files.index.sy set} \ -state disabled -scrollbar .vpane.files.index.sb -command [list $ui_index yview] +scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview] +scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview] pack .vpane.files.index.title -side top -fill x -pack .vpane.files.index.sb -side right -fill y +pack .vpane.files.index.sx -side bottom -fill x +pack .vpane.files.index.sy -side right -fill y pack $ui_index -side left -fill both -expand 1 .vpane.files add .vpane.files.index -sticky nsew @@ -3672,13 +3676,17 @@ label .vpane.files.workdir.title -text {Changed But Not Updated} \ -font font_ui text $ui_workdir -background white -borderwidth 0 \ -width 40 -height 10 \ + -wrap none \ -font font_ui \ -cursor $cursor_ptr \ - -yscrollcommand {.vpane.files.workdir.sb set} \ + -xscrollcommand {.vpane.files.workdir.sx set} \ + -yscrollcommand {.vpane.files.workdir.sy set} \ -state disabled -scrollbar .vpane.files.workdir.sb -command [list $ui_workdir yview] +scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview] +scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview] pack .vpane.files.workdir.title -side top -fill x -pack .vpane.files.workdir.sb -side right -fill y +pack .vpane.files.workdir.sx -side bottom -fill x +pack .vpane.files.workdir.sy -side right -fill y pack $ui_workdir -side left -fill both -expand 1 .vpane.files add .vpane.files.workdir -sticky nsew -- cgit v1.2.3 From 19e283f5c25b64a55fca099342f9bebddef4e17e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 15:38:09 -0500 Subject: git-gui: Don't offer tracking branches if none exist. I refactored the common code related to tracking branch listing into a new procedure all_tracking_branches. This saves a few lines and should make the create and delete dialogs easier to maintain. We now don't offer a radio button to create from a tracking branch or merge-check a tracking branch if there are no tracking branches known to git-gui. This prevents us from creating an empty option list and letting the user try to shoot themselves in the foot by asking us to work against an empty initial revision. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 77 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index a0c87e5ab4..c4ab824b9d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1679,6 +1679,17 @@ proc populate_branch_menu {} { } } +proc all_tracking_branches {} { + global tracking_branches + + set all_trackings [list] + foreach b [array names tracking_branches] { + regsub ^refs/(heads|remotes)/ $b {} b + lappend all_trackings $b + } + return [lsort -unique $all_trackings] +} + proc do_create_branch_action {w} { global all_heads null_sha1 global create_branch_checkout create_branch_revtype @@ -1747,7 +1758,7 @@ proc do_create_branch_action {w} { } proc do_create_branch {} { - global all_heads current_branch tracking_branches + global all_heads current_branch global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead @@ -1802,16 +1813,6 @@ proc do_create_branch {} { grid columnconfigure $w.desc 1 -weight 1 pack $w.desc -anchor nw -fill x -pady 5 -padx 5 - set all_trackings [list] - foreach b [array names tracking_branches] { - regsub ^refs/(heads|remotes)/ $b {} b - lappend all_trackings $b - } - set all_trackings [lsort -unique $all_trackings] - if {$all_trackings ne {}} { - set create_branch_trackinghead [lindex $all_trackings 0] - } - labelframe $w.from \ -text {Starting Revision} \ -font font_ui @@ -1822,15 +1823,19 @@ proc do_create_branch {} { -font font_ui eval tk_optionMenu $w.from.head_m create_branch_head $all_heads grid $w.from.head_r $w.from.head_m -sticky w - radiobutton $w.from.tracking_r \ - -text {Tracking Branch:} \ - -value tracking \ - -variable create_branch_revtype \ - -font font_ui - eval tk_optionMenu $w.from.tracking_m \ - create_branch_trackinghead \ - $all_trackings - grid $w.from.tracking_r $w.from.tracking_m -sticky w + set all_trackings [all_tracking_branches] + if {$all_trackings ne {}} { + set create_branch_trackinghead [lindex $all_trackings 0] + radiobutton $w.from.tracking_r \ + -text {Tracking Branch:} \ + -value tracking \ + -variable create_branch_revtype \ + -font font_ui + eval tk_optionMenu $w.from.tracking_m \ + create_branch_trackinghead \ + $all_trackings + grid $w.from.tracking_r $w.from.tracking_m -sticky w + } radiobutton $w.from.exp_r \ -text {Revision Expression:} \ -value expression \ @@ -2000,16 +2005,6 @@ proc do_delete_branch {} { pack $w.list.l -fill both -pady 5 -padx 5 pack $w.list -fill both -pady 5 -padx 5 - set all_trackings [list] - foreach b [array names tracking_branches] { - regsub ^refs/(heads|remotes)/ $b {} b - lappend all_trackings $b - } - set all_trackings [lsort -unique $all_trackings] - if {$all_trackings ne {} && $delete_branch_trackinghead eq {}} { - set delete_branch_trackinghead [lindex $all_trackings 0] - } - labelframe $w.validate \ -text {Delete Only If} \ -font font_ui @@ -2020,15 +2015,19 @@ proc do_delete_branch {} { -font font_ui eval tk_optionMenu $w.validate.head_m delete_branch_head $all_heads grid $w.validate.head_r $w.validate.head_m -sticky w - radiobutton $w.validate.tracking_r \ - -text {Merged Into Tracking Branch:} \ - -value tracking \ - -variable delete_branch_checktype \ - -font font_ui - eval tk_optionMenu $w.validate.tracking_m \ - delete_branch_trackinghead \ - $all_trackings - grid $w.validate.tracking_r $w.validate.tracking_m -sticky w + set all_trackings [all_tracking_branches] + if {$all_trackings ne {}} { + set delete_branch_trackinghead [lindex $all_trackings 0] + radiobutton $w.validate.tracking_r \ + -text {Merged Into Tracking Branch:} \ + -value tracking \ + -variable delete_branch_checktype \ + -font font_ui + eval tk_optionMenu $w.validate.tracking_m \ + delete_branch_trackinghead \ + $all_trackings + grid $w.validate.tracking_r $w.validate.tracking_m -sticky w + } radiobutton $w.validate.always_r \ -text {Always (Do not perform merge checks)} \ -value always \ -- cgit v1.2.3 From e754d6efe7810f5f2c6cbd48dca21f1bc84a6a5e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 15:40:55 -0500 Subject: git-gui: Give a better error message on an empty branch name. New branches must have a name. An empty one is not a valid ref, but the generic message "We do not like '' as a branch name." is just too vague or difficult to read. So detect the missing name early and tell the user it must be entered. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index c4ab824b9d..0f98d2ccea 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1696,6 +1696,16 @@ proc do_create_branch_action {w} { global create_branch_head create_branch_trackinghead set newbranch [string trim [$w.desc.name_t get 0.0 end]] + if {$newbranch eq {}} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message "Please supply a branch name." + focus $w.desc.name_t + return + } if {![catch {exec git show-ref --verify -- "refs/heads/$newbranch"}]} { tk_messageBox \ -icon error \ -- cgit v1.2.3 From c845692d7551cbcbfd8c6dda2aa2e3b135838e39 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 16:28:59 -0500 Subject: git-gui: Allow user to specify a branch name pattern. Typically I'm creating all new branches with the same prefix, e.g. 'sp/'. So its handy to be able to setup a repository (or global) level config option for git gui which contains this initial prefix. Once set then git-gui will load it into the new branch name field whenever a new branch is being created. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 0f98d2ccea..1a7c4d6b15 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1691,12 +1691,13 @@ proc all_tracking_branches {} { } proc do_create_branch_action {w} { - global all_heads null_sha1 + global all_heads null_sha1 repo_config global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead set newbranch [string trim [$w.desc.name_t get 0.0 end]] - if {$newbranch eq {}} { + if {$newbranch eq {} + || $newbranch eq $repo_config(gui.newbranchtemplate)} { tk_messageBox \ -icon error \ -type ok \ @@ -1768,7 +1769,7 @@ proc do_create_branch_action {w} { } proc do_create_branch {} { - global all_heads current_branch + global all_heads current_branch repo_config global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead @@ -1807,6 +1808,7 @@ proc do_create_branch {} { -height 1 \ -width 40 \ -font font_ui + $w.desc.name_t insert 0.0 $repo_config(gui.newbranchtemplate) grid $w.desc.name_l $w.desc.name_t -stick we -padx {0 5} bind $w.desc.name_t "focus $w.postActions.checkout;break" bind $w.desc.name_t "focus $w.from.exp_t;break" @@ -2986,7 +2988,10 @@ proc do_options {} { pack $w.buttons.restore -side left button $w.buttons.save -text Save \ -font font_ui \ - -command [list do_save_config $w] + -command " + catch {eval \[bind \[focus -displayof $w\] \]} + do_save_config $w + " pack $w.buttons.save -side right button $w.buttons.cancel -text {Cancel} \ -font font_ui \ @@ -3007,6 +3012,7 @@ proc do_options {} { {b pullsummary {Show Pull Summary}} {b trustmtime {Trust File Modification Timestamps}} {i diffcontext {Number of Diff Context Lines}} + {t newbranchtemplate {New Branch Name Template}} } { set type [lindex $option 0] set name [lindex $option 1] @@ -3030,7 +3036,29 @@ proc do_options {} { -from 1 -to 99 -increment 1 \ -width 3 \ -font font_ui - pack $w.$f.$name.v -side right -anchor e + pack $w.$f.$name.v -side right -anchor e -padx 5 + pack $w.$f.$name -side top -anchor w -fill x + } + t { + frame $w.$f.$name + label $w.$f.$name.l -text "$text:" -font font_ui + text $w.$f.$name.v \ + -borderwidth 1 \ + -relief sunken \ + -height 1 \ + -width 20 \ + -font font_ui + $w.$f.$name.v insert 0.0 [set ${f}_config_new(gui.$name)] + bind $w.$f.$name.v break + bind $w.$f.$name.v break + bind $w.$f.$name.v " + set ${f}_config_new(gui.$name) \ + \[string trim \[$w.$f.$name.v get 0.0 end\]\] + " + pack $w.$f.$name.l -side left -anchor w + pack $w.$f.$name.v -side left -anchor w \ + -fill x -expand 1 \ + -padx 5 pack $w.$f.$name -side top -anchor w -fill x } } @@ -3364,6 +3392,7 @@ proc apply_config {} { set default_config(gui.trustmtime) false set default_config(gui.pullsummary) true set default_config(gui.diffcontext) 5 +set default_config(gui.newbranchtemplate) {} set default_config(gui.fontui) [font configure font_ui] set default_config(gui.fontdiff) [font configure font_diff] set font_descs { -- cgit v1.2.3 From f250091b77949766724ffb67beb7a6adadc6b5b4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 16:37:05 -0500 Subject: git-gui: Improve keyboard traversal in dialogs. When we are in a dialog such as the new branch dialog or our options dialog we should permit the user to traverse around through the available widgets with their Tab/Shift-Tab key combinations. So in any single line text field where we don't want tab characters to actually be inserted into the value rebind Tab and Shift-Tab to honor what the tk_focusPrev and tk_focusNext scripts recommend. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 1a7c4d6b15..d59e720408 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1810,8 +1810,8 @@ proc do_create_branch {} { -font font_ui $w.desc.name_t insert 0.0 $repo_config(gui.newbranchtemplate) grid $w.desc.name_l $w.desc.name_t -stick we -padx {0 5} - bind $w.desc.name_t "focus $w.postActions.checkout;break" - bind $w.desc.name_t "focus $w.from.exp_t;break" + bind $w.desc.name_t {focus [tk_focusPrev %W];break} + bind $w.desc.name_t {focus [tk_focusNext %W];break} bind $w.desc.name_t "do_create_branch_action $w;break" bind $w.desc.name_t { if {{%K} ne {BackSpace} @@ -1860,8 +1860,8 @@ proc do_create_branch {} { -width 50 \ -font font_ui grid $w.from.exp_r $w.from.exp_t -stick we -padx {0 5} - bind $w.from.exp_t "focus $w.desc.name_t;break" - bind $w.from.exp_t "focus $w.postActions.checkout;break" + bind $w.from.exp_t {focus [tk_focusPrev %W];break} + bind $w.from.exp_t {focus [tk_focusNext %W];break} bind $w.from.exp_t "do_create_branch_action $w;break" grid columnconfigure $w.from 1 -weight 1 pack $w.from -anchor nw -fill x -pady 5 -padx 5 @@ -3049,7 +3049,8 @@ proc do_options {} { -width 20 \ -font font_ui $w.$f.$name.v insert 0.0 [set ${f}_config_new(gui.$name)] - bind $w.$f.$name.v break + bind $w.$f.$name.v {focus [tk_focusPrev %W];break} + bind $w.$f.$name.v {focus [tk_focusNext %W];break} bind $w.$f.$name.v break bind $w.$f.$name.v " set ${f}_config_new(gui.$name) \ -- cgit v1.2.3 From b36ffe800d830556e8df7eda8743c15dfac70e27 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 16:43:14 -0500 Subject: git-gui: Fully select a field when entering into it. If the user is tabbing through fields in the options dialog they are likely to want to just enter a new value for the field, rather than edit the value in-place. This is easier if we select the entire value upon focusing into the field. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index d59e720408..87fdce593d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3036,6 +3036,7 @@ proc do_options {} { -from 1 -to 99 -increment 1 \ -width 3 \ -font font_ui + bind $w.$f.$name.v {%W selection range 0 end} pack $w.$f.$name.v -side right -anchor e -padx 5 pack $w.$f.$name -side top -anchor w -fill x } @@ -3052,6 +3053,7 @@ proc do_options {} { bind $w.$f.$name.v {focus [tk_focusPrev %W];break} bind $w.$f.$name.v {focus [tk_focusNext %W];break} bind $w.$f.$name.v break + bind $w.$f.$name.v "$w.$f.$name.v tag add sel 0.0 end" bind $w.$f.$name.v " set ${f}_config_new(gui.$name) \ \[string trim \[$w.$f.$name.v get 0.0 end\]\] @@ -3088,6 +3090,7 @@ proc do_options {} { -from 2 -to 80 -increment 1 \ -width 3 \ -font font_ui + bind $w.global.$name.size {%W selection range 0 end} pack $w.global.$name.size -side right -anchor e pack $w.global.$name.family -side right -anchor e pack $w.global.$name -side top -anchor w -fill x -- cgit v1.2.3 From 4343434307dca441cd2dade1ece7c0ef5d4a7de2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 17:02:25 -0500 Subject: git-gui: Automatically toggle the relevant radio buttons. When the user selects a starting revision from one of our offered popup lists (local branches or tracking branches) or enters in an expression in the expression input field we should automatically activate the corresponding radio button for them. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 87fdce593d..593e4fd5a2 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1768,16 +1768,26 @@ proc do_create_branch_action {w} { } } +proc radio_selector {varname value args} { + upvar #0 $varname var + set var $value +} + +trace add variable create_branch_head write \ + [list radio_selector create_branch_revtype head] +trace add variable create_branch_trackinghead write \ + [list radio_selector create_branch_revtype tracking] + +trace add variable delete_branch_head write \ + [list radio_selector delete_branch_checktype head] +trace add variable delete_branch_trackinghead write \ + [list radio_selector delete_branch_checktype tracking] + proc do_create_branch {} { global all_heads current_branch repo_config global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead - set create_branch_checkout 1 - set create_branch_revtype head - set create_branch_head $current_branch - set create_branch_trackinghead {} - set w .branch_editor toplevel $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" @@ -1863,6 +1873,8 @@ proc do_create_branch {} { bind $w.from.exp_t {focus [tk_focusPrev %W];break} bind $w.from.exp_t {focus [tk_focusNext %W];break} bind $w.from.exp_t "do_create_branch_action $w;break" + bind $w.from.exp_t break + bind $w.from.exp_t {set create_branch_revtype expression} grid columnconfigure $w.from 1 -weight 1 pack $w.from -anchor nw -fill x -pady 5 -padx 5 @@ -1876,6 +1888,10 @@ proc do_create_branch {} { pack $w.postActions.checkout -anchor nw pack $w.postActions -anchor nw -fill x -pady 5 -padx 5 + set create_branch_checkout 1 + set create_branch_head $current_branch + set create_branch_revtype head + bind $w "grab $w; focus $w.desc.name_t" bind $w "destroy $w" bind $w "do_create_branch_action $w;break" @@ -1978,10 +1994,6 @@ proc do_delete_branch {} { global all_heads tracking_branches current_branch global delete_branch_checktype delete_branch_head delete_branch_trackinghead - set delete_branch_checktype head - set delete_branch_head $current_branch - set delete_branch_trackinghead {} - set w .branch_editor toplevel $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" @@ -2049,6 +2061,9 @@ proc do_delete_branch {} { grid columnconfigure $w.validate 1 -weight 1 pack $w.validate -anchor nw -fill x -pady 5 -padx 5 + set delete_branch_head $current_branch + set delete_branch_checktype head + bind $w "grab $w; focus $w" bind $w "destroy $w" wm title $w "[appname] ([reponame]): Delete Branch" -- cgit v1.2.3 From 9c10deab6ceb15b6b576d9bc77955c24d5baa709 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 17:22:40 -0500 Subject: git-gui: Correctly categorize tracking branches and heads. Up until now git-gui did not support the new wildcard syntax used to fetch any remote branch into a tracking branch during 'git fetch'. Now if we identify a tracking branch as ending with the string '/*' then we use for-each-ref to print out the reference names which may have been fetched by that pattern. We also now correctly filter any tracking branches out of refs/heads, if they user has placed any there. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 593e4fd5a2..c969db5fa5 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1629,16 +1629,27 @@ proc write_checkout_index {fd pathList totalCnt batch msg after} { ## ## branch management +proc is_tracking_branch {name} { + global tracking_branches + + if {![catch {set info $tracking_branches($name)}]} { + return 1 + } + foreach t [array names tracking_branches] { + if {[string match {*/\*} $t] && [string match $t $name]} { + return 1 + } + } + return 0 +} + proc load_all_heads {} { - global all_heads tracking_branches + global all_heads set all_heads [list] - set cmd [list git for-each-ref] - lappend cmd --format=%(refname) - lappend cmd refs/heads - set fd [open "| $cmd" r] + set fd [open "| git for-each-ref --format=%(refname) refs/heads" r] while {[gets $fd line] > 0} { - if {![catch {set info $tracking_branches($line)}]} continue + if {[is_tracking_branch $line]} continue if {![regsub ^refs/heads/ $line {} name]} continue lappend all_heads $name } @@ -1682,11 +1693,26 @@ proc populate_branch_menu {} { proc all_tracking_branches {} { global tracking_branches - set all_trackings [list] - foreach b [array names tracking_branches] { - regsub ^refs/(heads|remotes)/ $b {} b - lappend all_trackings $b + set all_trackings {} + set cmd {} + foreach name [array names tracking_branches] { + if {[regsub {/\*$} $name {} name]} { + lappend cmd $name + } else { + regsub ^refs/(heads|remotes)/ $name {} name + lappend all_trackings $name + } + } + + if {$cmd ne {}} { + set fd [open "| git for-each-ref --format=%(refname) $cmd" r] + while {[gets $fd name] > 0} { + regsub ^refs/(heads|remotes)/ $name {} name + lappend all_trackings $name + } + close $fd } + return [lsort -unique $all_trackings] } -- cgit v1.2.3 From d4dd034ab5bb13162c4a4e6c588fe530c7739a95 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 17:26:22 -0500 Subject: git-gui: Update todo list with finished and new items. Signed-off-by: Shawn O. Pearce --- TODO | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/TODO b/TODO index ef4f50b304..b95a137322 100644 --- a/TODO +++ b/TODO @@ -6,9 +6,11 @@ Items outstanding: * Make use of the new default merge data stored in repo-config. - * Checkout or create a different local branch. + * Checkout a different local branch. - * Delete a local branch. + * Push any local branch to a remote branch. + + * Merge any local branches through a real merge UI. * Allow user to define keyboard shortcuts for frequently used fetch or merge operations. Or maybe just define a keyboard shortcut @@ -40,10 +42,3 @@ Known bugs: process is just terminating due to a segfault or something, as the do_quit proc in git-gui doesn't run. It often seems to occur while writing a commit message in the buffer. Odd. - - * At one point after using git-gui for a while to make many commits - to a repository I reverted one file through git-gui and another - manually in my editor; during commit git-gui crashed with an - error about the icon name it was trying to update no longer - existed in the widget. I suspect something didn't update right - in file_states... -- cgit v1.2.3 From c5a1eb889ca3df51ac443916a037b59c46983239 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 17:50:42 -0500 Subject: git-gui: Slightly tweak new window geometry. I didn't really like the way a new git-gui launched in a new repository as the window geometry wasn't quite the best layou. So this is a minor tweak to try and get space distributed around the window better. By decreasing the widths we're also able to shrink the gui smaller without Tk clipping content at the edge of the window. A nice feature. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index c969db5fa5..7115cb5d05 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3726,17 +3726,17 @@ pack .branch -side top -fill x # panedwindow .vpane -orient vertical panedwindow .vpane.files -orient horizontal -.vpane add .vpane.files -sticky nsew -height 100 -width 400 +.vpane add .vpane.files -sticky nsew -height 100 -width 200 pack .vpane -anchor n -side top -fill both -expand 1 # -- Index File List # -frame .vpane.files.index -height 100 -width 400 +frame .vpane.files.index -height 100 -width 200 label .vpane.files.index.title -text {Changes To Be Committed} \ -background green \ -font font_ui text $ui_index -background white -borderwidth 0 \ - -width 40 -height 10 \ + -width 20 -height 10 \ -wrap none \ -font font_ui \ -cursor $cursor_ptr \ @@ -3753,12 +3753,12 @@ pack $ui_index -side left -fill both -expand 1 # -- Working Directory File List # -frame .vpane.files.workdir -height 100 -width 100 +frame .vpane.files.workdir -height 100 -width 200 label .vpane.files.workdir.title -text {Changed But Not Updated} \ -background red \ -font font_ui text $ui_workdir -background white -borderwidth 0 \ - -width 40 -height 10 \ + -width 20 -height 10 \ -wrap none \ -font font_ui \ -cursor $cursor_ptr \ -- cgit v1.2.3 From f5925d934fdd1ffc3ded7272ea278437608f5f50 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 18:00:03 -0500 Subject: git-gui: Create missing branch head on initial commit. If we are making an initial commit our branch head did not exist when we scanned for all heads during startup. Consequently we won't have it in our branch menu. So force it to be put there after the ref was created. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 7115cb5d05..d71fcd8220 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -997,7 +997,7 @@ proc commit_writetree {curHEAD msg} { proc commit_committree {fd_wt curHEAD msg} { global HEAD PARENT MERGE_HEAD commit_type - global single_commit + global single_commit all_heads current_branch global ui_status_value ui_comm selected_commit_type global file_states selected_paths rescan_active @@ -1049,6 +1049,14 @@ proc commit_committree {fd_wt curHEAD msg} { return } + # -- 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 + } + # -- Cleanup after ourselves. # catch {file delete [gitdir MERGE_HEAD]} -- cgit v1.2.3 From 8ce03164842d250f1cf1dfbf9a245fd030195e24 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Jan 2007 23:11:47 -0500 Subject: git-gui: Don't format the mode line of a diff. We sometimes see a mode line show up in a diff if the file mode was changed. But its not something we format specially. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index d71fcd8220..396a44e654 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -709,6 +709,7 @@ proc read_diff {fd} { if {[string match {@@@ *} $line]} {set is_3way_diff 1} if {[string match {index *} $line] + || [string match {mode *} $line] || [regexp {^\* Unmerged path } $line]} { set tags {} } elseif {$is_3way_diff} { -- cgit v1.2.3 From 46aaf90b4901cbae9da9d59ba8cc914dcfc38aa3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 22 Jan 2007 17:10:38 -0500 Subject: git-gui: Force an update-index --refresh on unchanged files. Its possible for external programs to update file modification dates of many files within a repository. I've seen this on Windows with a popular virus scanner, sadly enough. If the user has Trust File Modification Timestamp enabled and the virus scanner touches a large number of files it can be annoying trying to clear them out of the 'Changed But Not Updated' file list by clicking on them one at a time to load the diff. So now we force a rescan as soon as one such file is found, and for just that rescan we disable the Trust File Modification Timestamp option thereby allowing Git to update the modification dates in the index. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 396a44e654..2350baa3a3 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -335,7 +335,7 @@ proc PARENT {} { return $empty_tree } -proc rescan {after} { +proc rescan {after {honor_trustmtime 1}} { global HEAD PARENT MERGE_HEAD commit_type global ui_index ui_workdir ui_status_value ui_comm global rescan_active file_states @@ -366,7 +366,7 @@ proc rescan {after} { $ui_comm edit modified false } - if {$repo_config(gui.trustmtime) eq {true}} { + if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} { rescan_stage2 {} $after } else { set rescan_active 1 @@ -586,17 +586,11 @@ by another application and you currently have the Trust File Modification Timestamps option enabled, so Git did not automatically detect that there are no content differences in this -file. - -This file will now be removed from the modified -files list, to prevent possible confusion. -" - if {[catch {exec git update-index -- $path} err]} { - error_popup "Failed to refresh index:\n\n$err" - } +file." clear_diff display_file $path __ + rescan {set ui_status_value {Ready.}} 0 } proc show_diff {path w {lno {}}} { -- cgit v1.2.3 From e0c781b34756ce47ab9a6ae89eba8067733d6e5c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 22 Jan 2007 18:24:45 -0500 Subject: git-gui: Don't attempt to tag new file/deleted file headers in diffs. We don't want to tag these new file/delete file lines, as they aren't actually that interesting. Its quite clear from the diff itself that the file is a new file or is a deleted file (as the entire thing will appear in the diff). Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 2350baa3a3..d697d1ebe2 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -704,6 +704,8 @@ proc read_diff {fd} { if {[string match {index *} $line] || [string match {mode *} $line] + || [string match {new file *} $line] + || [string match {deleted file *} $line] || [regexp {^\* Unmerged path } $line]} { set tags {} } elseif {$is_3way_diff} { -- cgit v1.2.3 From 75e78c8a1bb273461c14a5b37f9b06215f502e67 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 22 Jan 2007 18:31:12 -0500 Subject: git-gui: Fix 'Select All' action on Windows. Sometimes the Select All action from our context menus doesn't work unless the text field its supposed to act on has focus. I'm not really sure why adding the sel tag requires having focus. It technically should not be required to update the sel tag membership, but perhaps there is a bug in Tcl/Tk 8.4.1 on Windows which is causing this odd behavior. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index d697d1ebe2..bbf57b96a0 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2539,7 +2539,7 @@ proc console_init {w} { -command "tk_textCopy $w.m.t" $w.ctxm add command -label "Select All" \ -font font_ui \ - -command "$w.m.t tag add sel 0.0 end" + -command "focus $w.m.t;$w.m.t tag add sel 0.0 end" $w.ctxm add command -label "Copy All" \ -font font_ui \ -command " @@ -3913,7 +3913,7 @@ $ctxm add separator $ctxm add command \ -label {Select All} \ -font font_ui \ - -command {$ui_comm tag add sel 0.0 end} + -command {focus $ui_comm;$ui_comm tag add sel 0.0 end} $ctxm add command \ -label {Copy All} \ -font font_ui \ @@ -4059,7 +4059,7 @@ lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add command \ -label {Select All} \ -font font_ui \ - -command {$ui_diff tag add sel 0.0 end} + -command {focus $ui_diff;$ui_diff tag add sel 0.0 end} lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add command \ -label {Copy All} \ -- cgit v1.2.3 From e54a1bd122007af30fcf174a57f892157501673f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 22 Jan 2007 19:18:39 -0500 Subject: git-gui: Ignore 'No newline at end of file' marker line. If one or both versions of the file don't have a newline at the end of the file we get a line telling us so in the diff output. This shouldn't be tagged, nor should it generate a warning about not being tagged. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index bbf57b96a0..54204aee78 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -706,6 +706,7 @@ proc read_diff {fd} { || [string match {mode *} $line] || [string match {new file *} $line] || [string match {deleted file *} $line] + || $line eq {\ No newline at end of file} || [regexp {^\* Unmerged path } $line]} { set tags {} } elseif {$is_3way_diff} { -- cgit v1.2.3 From 124355d32c0612192e729e12f1d3d68849754f29 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 22 Jan 2007 22:41:13 -0500 Subject: git-gui: Always start a rescan on an empty diff. If we got an empty diff its probably because the modification time of the file was changed but the file content hasn't been changed. Typically this happens because an outside program modified the file and git-gui was told to not run 'update-index --refresh', as the user generally trusts file modification timestamps. But we can also get an empty diff when a program undos a file change and still updates the modification timestamp upon saving, but has undone the file back to the same as what is in the index or in PARENT. So even if gui.trustmtime is false we should still run a rescan on an empty diff. This change also lets us cleanup the dialog message that we show when this case occurs, as its no longer got anything to do with Trust File Modification Timestamps. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 54204aee78..6ccd4113d8 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -582,11 +582,11 @@ proc handle_empty_diff {} { [short_path $path] has no changes. The modification date of this file was updated -by another application and you currently have -the Trust File Modification Timestamps option -enabled, so Git did not automatically detect -that there are no content differences in this -file." +by another application, but the content within +the file was not changed. + +A rescan will be automatically started to find +other files which may have the same state." clear_diff display_file $path __ @@ -683,7 +683,6 @@ proc show_diff {path w {lno {}}} { proc read_diff {fd} { global ui_diff ui_status_value is_3way_diff diff_active - global repo_config $ui_diff conf -state normal while {[gets $fd line] >= 0} { @@ -763,8 +762,7 @@ proc read_diff {fd} { unlock_index set ui_status_value {Ready.} - if {$repo_config(gui.trustmtime) eq {true} - && [$ui_diff index end] eq {2.0}} { + if {[$ui_diff index end] eq {2.0}} { handle_empty_diff } } -- cgit v1.2.3 From 464c9ffee45520c8be3ec656f926224501bcf977 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 23 Jan 2007 02:08:09 -0500 Subject: git-gui: Don't show content of untracked binary files. A binary file can be very large, and showing the complete content of one is horribly ugly and confusing. So we now use the same rule that core Git uses; if there is a NUL byte (\0) within the first 8000 bytes of the file we assume it is binary and refuse to show the content. Given that we have loaded the entire content of the file into memory we probably could just afford to search the whole thing, but we also probably should not load multi-megabyte binary files either. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 6ccd4113d8..9136e7fe98 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -637,6 +637,9 @@ proc show_diff {path w {lno {}}} { error_popup "Error loading file:\n\n$err" return } + if {[string first "\0" [string range $content 0 8000]] != -1} { + set content {* Binary file (not showing content).} + } $ui_diff conf -state normal $ui_diff insert end $content $ui_diff conf -state disabled -- cgit v1.2.3 From 19b41e455932d56a578becdfd91095a9a041f21c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 23 Jan 2007 02:33:58 -0500 Subject: git-gui: Limit display of large untracked files. Our internal diff viewer displays untracked files to help users see if they should become tracked, or not. It is not meant as a full file viewer that handles any sort of input. Consequently it is rather unreasonable for users to expect us to show them very large files. Some users may click on a very big file (and not know its very big) then get surprised when Tk takes a long time to load the content and render it, especially if their memory is tight and their OS starts to swap processes out. Instead we now limit the amount of data we load to the first 128 KiB of any untracked file. If the file is larger than 128 KiB we display a warning message at the top of our diff viewer to notify the user that we are not going to load the entire thing. Users should be able to recognize a file just by its first 128 KiB and determine if it should be added to the repository or not. Since we are loading 128 KiB we may as well scan it to see if the file is binary. So I've removed the "first 8000 bytes" rule and just allowed git-gui to scan the entire data chunk that it read in. This is probably faster anyway if Tcl's [string range] command winds up making a copy of the data. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 9136e7fe98..f71dabe68a 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -626,10 +626,12 @@ proc show_diff {path w {lno {}}} { # - Git won't give us the diff, there's nothing to compare to! # if {$m eq {_O}} { + set max_sz [expr {128 * 1024}] if {[catch { set fd [open $path r] - set content [read $fd] + set content [read $fd $max_sz] close $fd + set sz [file size $path] } err ]} { set diff_active 0 unlock_index @@ -637,11 +639,27 @@ proc show_diff {path w {lno {}}} { error_popup "Error loading file:\n\n$err" return } - if {[string first "\0" [string range $content 0 8000]] != -1} { - set content {* Binary file (not showing content).} - } $ui_diff conf -state normal - $ui_diff insert end $content + if {[string first "\0" $content] != -1} { + $ui_diff insert end \ + "* Binary file (not showing content)." \ + d_@ + } else { + if {$sz > $max_sz} { + $ui_diff insert end \ +"* Untracked file is $sz bytes. +* Showing only first $max_sz bytes. + +" d_@ + } + $ui_diff insert end $content + if {$sz > $max_sz} { + $ui_diff insert end " +* Untracked file clipped here by [appname]. +* To see the entire file, use an external editor. +" d_@ + } + } $ui_diff conf -state disabled set diff_active 0 unlock_index -- cgit v1.2.3 From d3596fd9486b602252d5b2c3e14ac55a07a2bdb9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 23 Jan 2007 03:18:37 -0500 Subject: git-gui: When possible show the type of an untracked file. Users may want to know what a file is before they add it to the repository, especially if its a binary file. So when possible invoke 'file' on the path and try to get its output. Since this is strictly advice to the user we won't bother to report any failures from our attempt to run `file`. Since some file commands also output the path name they were given we look for that case and strip it off the front of the returned output before placing it into the diff viewer. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index f71dabe68a..37757cfb65 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -640,6 +640,14 @@ proc show_diff {path w {lno {}}} { return } $ui_diff conf -state normal + if {![catch {set type [exec file $path]}]} { + set n [string length $path] + if {[string equal -length $n $path $type]} { + set type [string range $type $n end] + regsub {^:?\s*} $type {} type + } + $ui_diff insert end "* $type\n" d_@ + } if {[string first "\0" $content] != -1} { $ui_diff insert end \ "* Binary file (not showing content)." \ -- cgit v1.2.3 From 4e62e2725e487b515a57e1c6ae5ef9b81546884d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 23 Jan 2007 03:25:17 -0500 Subject: git-gui: Don't try to tag the 'Binary files * and * differ' line. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index 37757cfb65..c6757045ab 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -734,6 +734,7 @@ proc read_diff {fd} { || [string match {mode *} $line] || [string match {new file *} $line] || [string match {deleted file *} $line] + || [string match {Binary files * and * differ} $line] || $line eq {\ No newline at end of file} || [regexp {^\* Unmerged path } $line]} { set tags {} -- cgit v1.2.3 From 0565246a7c886475878ea08cd501ea7ac4de6ada Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 23 Jan 2007 03:30:02 -0500 Subject: git-gui: Remove spurious newline in untracked file display. This newline is stupid; it doesn't get put here unless the file is very large, and then its just sort of out of place. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index c6757045ab..2ebc463b31 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -657,7 +657,6 @@ proc show_diff {path w {lno {}}} { $ui_diff insert end \ "* Untracked file is $sz bytes. * Showing only first $max_sz bytes. - " d_@ } $ui_diff insert end $content -- cgit v1.2.3 From 51a989ba5a4d1299d08ddad19c6a45485bdb7dd8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 23 Jan 2007 04:07:18 -0500 Subject: git-gui: Honor system encoding for filenames. Since git operates on filenames using the operating system encoding any data we are receiving from it by way of a pipe, or sending to it by way of a pipe must be formatted in that encoding. This should be the same as the Tcl system encoding, as its the encoding that applications should be using to converse with the operating system. Sadly this does not fix the gitweb/test file in git.git on Macs; that's due to something really broken happening in the filesystem. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 2ebc463b31..386ae989b8 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -410,9 +410,9 @@ proc rescan_stage2 {fd after} { set fd_df [open "| git diff-files -z" r] set fd_lo [open $ls_others r] - fconfigure $fd_di -blocking 0 -translation binary - fconfigure $fd_df -blocking 0 -translation binary - fconfigure $fd_lo -blocking 0 -translation binary + fconfigure $fd_di -blocking 0 -translation binary -encoding binary + fconfigure $fd_df -blocking 0 -translation binary -encoding binary + fconfigure $fd_lo -blocking 0 -translation binary -encoding binary fileevent $fd_di readable [list read_diff_index $fd_di $after] fileevent $fd_df readable [list read_diff_files $fd_df $after] fileevent $fd_lo readable [list read_ls_others $fd_lo $after] @@ -450,8 +450,9 @@ proc read_diff_index {fd after} { incr c set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }] + set p [string range $buf_rdi $z1 [expr {$z2 - 1}]] merge_state \ - [string range $buf_rdi $z1 [expr {$z2 - 1}]] \ + [encoding convertfrom $p] \ [lindex $i 4]? \ [list [lindex $i 0] [lindex $i 2]] \ [list] @@ -482,8 +483,9 @@ proc read_diff_files {fd after} { incr c set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }] + set p [string range $buf_rdf $z1 [expr {$z2 - 1}]] merge_state \ - [string range $buf_rdf $z1 [expr {$z2 - 1}]] \ + [encoding convertfrom $p] \ ?[lindex $i 4] \ [list] \ [list [lindex $i 0] [lindex $i 2]] @@ -506,7 +508,7 @@ proc read_ls_others {fd after} { set pck [split $buf_rlo "\0"] set buf_rlo [lindex $pck end] foreach p [lrange $pck 0 end-1] { - merge_state $p ?O + merge_state [encoding convertfrom $p] ?O } rescan_done $fd buf_rlo $after } @@ -1459,6 +1461,7 @@ proc update_indexinfo {msg pathList after} { -blocking 0 \ -buffering full \ -buffersize 512 \ + -encoding binary \ -translation binary fileevent $fd writable [list \ write_update_indexinfo \ @@ -1499,7 +1502,7 @@ proc write_update_indexinfo {fd pathList totalCnt batch msg after} { set info [lindex $s 2] if {$info eq {}} continue - puts -nonewline $fd "$info\t$path\0" + puts -nonewline $fd "$info\t[encoding convertto $path]\0" display_file $path $new } @@ -1531,6 +1534,7 @@ proc update_index {msg pathList after} { -blocking 0 \ -buffering full \ -buffersize 512 \ + -encoding binary \ -translation binary fileevent $fd writable [list \ write_update_index \ @@ -1575,7 +1579,7 @@ proc write_update_index {fd pathList totalCnt batch msg after} { ?M {set new M_} ?? {continue} } - puts -nonewline $fd "$path\0" + puts -nonewline $fd "[encoding convertto $path]\0" display_file $path $new } @@ -1613,6 +1617,7 @@ proc checkout_index {msg pathList after} { -blocking 0 \ -buffering full \ -buffersize 512 \ + -encoding binary \ -translation binary fileevent $fd writable [list \ write_checkout_index \ @@ -1645,7 +1650,7 @@ proc write_checkout_index {fd pathList totalCnt batch msg after} { U? {continue} ?M - ?D { - puts -nonewline $fd "$path\0" + puts -nonewline $fd "[encoding convertto $path]\0" display_file $path ?_ } } -- cgit v1.2.3 From 59885273c337df937430b8731633c2e9020ce8e3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 23 Jan 2007 04:40:21 -0500 Subject: git-gui: Handle commit encoding better. Git prefers that all log messages are encoding in UTF-8. So now when git-gui generates the commit message it converts the commit message text from the internal Tcl Unicode representation into a UTF-8 file. The file is then fed as stdin to git-commit-tree. I had to start using a file here rather than feeding the message in with << as << uses the system encoding, which we may not want. When we reload a commit message via git-cat-file we are getting the raw byte stream, with no encoding performed by Git itself. So unless the new 'encoding' header appears in the message we should probably assume it is utf-8 encoded; but if the header is present we need to use whatever it claims. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 386ae989b8..6a4086d475 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -805,6 +805,7 @@ proc read_diff {fd} { proc load_last_commit {} { global HEAD PARENT MERGE_HEAD commit_type ui_comm + global repo_config if {[llength $PARENT] == 0} { error_popup {There is nothing to amend. @@ -831,11 +832,18 @@ current merge activity. set parents [list] if {[catch { set fd [open "| git cat-file commit $curHEAD" r] + fconfigure $fd -encoding binary -translation lf + if {[catch {set enc $repo_config(i18n.commitencoding)}]} { + set enc utf-8 + } while {[gets $fd line] > 0} { if {[string match {parent *} $line]} { lappend parents [string range $line 7 end] + } elseif {[string match {encoding *} $line]} { + set enc [string tolower [string range $line 9 end]] } } + fconfigure $fd -encoding $enc set msg [string trim [read $fd]] close $fd } err]} { @@ -1027,6 +1035,7 @@ proc commit_committree {fd_wt curHEAD msg} { global single_commit all_heads current_branch global ui_status_value ui_comm selected_commit_type global file_states selected_paths rescan_active + global repo_config gets $fd_wt tree_id if {$tree_id eq {} || [catch {close $fd_wt} err]} { @@ -1036,6 +1045,17 @@ proc commit_committree {fd_wt curHEAD msg} { 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 $enc -translation binary + puts -nonewline $msg_wt $msg + close $msg_wt + # -- Create the commit. # set cmd [list git commit-tree $tree_id] @@ -1048,7 +1068,7 @@ proc commit_committree {fd_wt curHEAD msg} { # git commit-tree writes to stderr during initial commit. lappend cmd 2>/dev/null } - lappend cmd << $msg + lappend cmd <$msg_p if {[catch {set cmt_id [eval exec $cmd]} err]} { error_popup "commit-tree failed:\n\n$err" set ui_status_value {Commit failed.} @@ -1086,6 +1106,7 @@ proc commit_committree {fd_wt curHEAD msg} { # -- Cleanup after ourselves. # + catch {file delete $msg_p} catch {file delete [gitdir MERGE_HEAD]} catch {file delete [gitdir MERGE_MSG]} catch {file delete [gitdir SQUASH_MSG]} -- cgit v1.2.3 From 0fd49d0a7d579722bd1a0a1645afc16646b05794 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 24 Jan 2007 15:21:01 -0500 Subject: git-gui: Display database stats (count-objects -v) on demand. Its nice to know how many loose objects and roughly how much disk space they are taking up, so that you can guestimate about when might be a good time to run 'Compress Database'. The same is true of packfiles, especially once the automatic keep-pack code in git-fetch starts to be more widely used. We now offer the output of count-objects -v in a nice little dialog hung off the Repository menu. Our labels are slightly more verbose than those of `count-objects -v`, so users will hopefully be able to make better sense of what we are showing them here. We probably should also offer pack file size information, and data about *.idx files which exist which lack corresponding *.pack files (a situation caused by the HTTP fetch client). But in the latter case we should only offer the data once we have way to let the user clean up old and inactive index files. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 6a4086d475..5171db6f27 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1904,7 +1904,7 @@ proc do_create_branch {} { -width 40 \ -font font_ui $w.desc.name_t insert 0.0 $repo_config(gui.newbranchtemplate) - grid $w.desc.name_l $w.desc.name_t -stick we -padx {0 5} + grid $w.desc.name_l $w.desc.name_t -sticky we -padx {0 5} bind $w.desc.name_t {focus [tk_focusPrev %W];break} bind $w.desc.name_t {focus [tk_focusNext %W];break} bind $w.desc.name_t "do_create_branch_action $w;break" @@ -1954,7 +1954,7 @@ proc do_create_branch {} { -height 1 \ -width 50 \ -font font_ui - grid $w.from.exp_r $w.from.exp_t -stick we -padx {0 5} + grid $w.from.exp_r $w.from.exp_t -sticky we -padx {0 5} bind $w.from.exp_t {focus [tk_focusPrev %W];break} bind $w.from.exp_t {focus [tk_focusNext %W];break} bind $w.from.exp_t "do_create_branch_action $w;break" @@ -2719,6 +2719,63 @@ proc do_gitk {revs} { } } +proc do_stats {} { + set fd [open "| git count-objects -v" r] + while {[gets $fd line] > 0} { + if {[regexp {^([^:]+): (\d+)$} $line _ name value]} { + set stats($name) $value + } + } + close $fd + + set w .stats_view + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + label $w.header -text {Database Statistics} \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons -border 1 + button $w.buttons.close -text Close \ + -font font_ui \ + -command [list destroy $w] + button $w.buttons.gc -text {Compress Database} \ + -font font_ui \ + -command "destroy $w;do_gc" + pack $w.buttons.close -side right + pack $w.buttons.gc -side left + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + frame $w.stat -borderwidth 1 -relief solid + foreach s { + {count {Number of loose objects}} + {size {Disk space used by loose objects} { KiB}} + {in-pack {Number of packed objects}} + {packs {Number of packs}} + {prune-packable {Packed objects waiting for pruning}} + {garbage {Garbage files}} + } { + set name [lindex $s 0] + set label [lindex $s 1] + if {[catch {set value $stats($name)}]} continue + if {[llength $s] > 2} { + set value "$value[lindex $s 2]" + } + + label $w.stat.l_$name -text "$label:" -anchor w -font font_ui + label $w.stat.v_$name -text $value -anchor w -font font_ui + grid $w.stat.l_$name $w.stat.v_$name -sticky we -padx {0 5} + } + pack $w.stat + + bind $w "grab $w; focus $w" + bind $w [list destroy $w] + bind $w [list destroy $w] + wm title $w "[appname] ([reponame]): Database Statistics" + tkwait window $w +} + proc do_gc {} { set w [new_console {gc} {Compressing the object database}] console_exec $w {git gc} @@ -3542,6 +3599,10 @@ if {![is_MacOSX]} { .mbar.repository add separator if {!$single_commit} { + .mbar.repository add command -label {Database Statistics} \ + -command do_stats \ + -font font_ui + .mbar.repository add command -label {Compress Database} \ -command do_gc \ -font font_ui @@ -3847,7 +3908,7 @@ frame .vpane.lower.commarea frame .vpane.lower.diff -relief sunken -borderwidth 1 pack .vpane.lower.commarea -side top -fill x pack .vpane.lower.diff -side bottom -fill both -expand 1 -.vpane add .vpane.lower -stick nsew +.vpane add .vpane.lower -sticky nsew # -- Commit Area Buttons # -- cgit v1.2.3 From b5b6b434527e5f0df43f994d690a48dad1fb4555 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 24 Jan 2007 16:51:59 -0500 Subject: git-gui: Implement basic branch switching through read-tree. If the user selects a different branch from the Branch menu, or asks us to create a new branch and immediately checkout that branch we now perform the update of the working directory by way of a 2 way read-tree invocation. This emulates the behavior of `git checkout branch` or the behavior of `git checkout -b branch initrev`. We don't however support the -m style behavior, where a switch can occur with file level merging performed by merge-recursive. Support for this is planned for a future update. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 15 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 5171db6f27..b5c2c7406d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2155,16 +2155,11 @@ proc do_delete_branch {} { tkwait window $w } -proc switch_branch {b} { - global HEAD commit_type file_states current_branch - global selected_commit_type ui_comm +proc switch_branch {new_branch} { + global HEAD commit_type current_branch repo_config if {![lock_index switch]} return - # -- Backup the selected branch (repository_state resets it) - # - set new_branch $current_branch - # -- Our in memory state should match the repository. # repository_state curType curHEAD curMERGE_HEAD @@ -2185,19 +2180,110 @@ The rescan will be automatically started now. return } - # -- Toss the message buffer if we are in amend mode. + if {$repo_config(gui.trustmtime) eq {true}} { + switch_branch_stage2 {} $new_branch + } else { + set ui_status_value {Refreshing file status...} + set cmd [list git update-index] + lappend cmd -q + lappend cmd --unmerged + lappend cmd --ignore-missing + lappend cmd --refresh + set fd_rf [open "| $cmd" r] + fconfigure $fd_rf -blocking 0 -translation binary + fileevent $fd_rf readable \ + [list switch_branch_stage2 $fd_rf $new_branch] + } +} + +proc switch_branch_stage2 {fd_rf new_branch} { + global ui_status_value HEAD + + if {$fd_rf ne {}} { + read $fd_rf + if {![eof $fd_rf]} return + close $fd_rf + } + + set ui_status_value "Updating working directory to '$new_branch'..." + set cmd [list git read-tree] + lappend cmd -m + lappend cmd -u + lappend cmd --exclude-per-directory=.gitignore + lappend cmd $HEAD + lappend cmd $new_branch + set fd_rt [open "| $cmd" r] + fconfigure $fd_rt -blocking 0 -translation binary + fileevent $fd_rt readable \ + [list switch_branch_readtree_wait $fd_rt $new_branch] +} + +proc switch_branch_readtree_wait {fd_rt new_branch} { + global selected_commit_type commit_type HEAD MERGE_HEAD PARENT + global current_branch + global ui_comm ui_status_value + + # -- We never get interesting output on stdout; only stderr. # - if {[string match amend* $curType]} { - $ui_comm delete 0.0 end - $ui_comm edit reset - $ui_comm edit modified false + read $fd_rt + fconfigure $fd_rt -blocking 1 + if {![eof $fd_rt]} { + fconfigure $fd_rt -blocking 0 + return } - set selected_commit_type new - set current_branch $new_branch + # -- The working directory wasn't in sync with the index and + # we'd have to overwrite something to make the switch. A + # merge is required. + # + if {[catch {close $fd_rt} err]} { + regsub {^fatal: } $err {} err + warn_popup "File level merge required. + +$err + +Staying on branch '$current_branch'." + set ui_status_value "Aborted checkout of '$new_branch' (file level merging is required)." + unlock_index + return + } + + # -- Update the symbolic ref. Core git doesn't even check for failure + # here, it Just Works(tm). If it doesn't we are in some really ugly + # state that is difficult to recover from within git-gui. + # + if {[catch {exec git symbolic-ref HEAD "refs/heads/$new_branch"} err]} { + error_popup "Failed to set current branch. + +This working directory is only partially switched. +We successfully updated your files, but failed to +update an internal Git file. + +This should not have occurred. [appname] will now +close and give up. +$err" + do_quit + return + } + + # -- Update our repository state. If we were previously in amend mode + # we need to toss the current buffer and do a full rescan to update + # our file lists. If we weren't in amend mode our file lists are + # accurate and we can avoid the rescan. + # unlock_index - error "NOT FINISHED" + set selected_commit_type new + if {[string match amend* $commit_type]} { + $ui_comm delete 0.0 end + $ui_comm edit reset + $ui_comm edit modified false + rescan {set ui_status_value "Checked out branch '$current_branch'."} + } else { + repository_state commit_type HEAD MERGE_HEAD + set PARENT $HEAD + set ui_status_value "Checked out branch '$current_branch'." + } } ###################################################################### -- cgit v1.2.3 From f747133720d26e52b6ed83dfd57cdd1a74668b3d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 24 Jan 2007 17:01:49 -0500 Subject: git-gui: Use system default labelframe bordering. In the new branch dialog and delete branch dialog we are using the system default labelframe border settings (whatever those are) and they look reasonable on both Windows and Mac OS X. But for some unknown reason to me I used a raised border for the options dialog. It doesn't look consistent anymore, so I'm switching it to the defaults. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index b5c2c7406d..842fccc9f8 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3243,11 +3243,9 @@ proc do_options {} { pack $w.buttons -side bottom -fill x -pady 10 -padx 10 labelframe $w.repo -text "[reponame] Repository" \ - -font font_ui \ - -relief raised -borderwidth 2 + -font font_ui labelframe $w.global -text {Global (All Repositories)} \ - -font font_ui \ - -relief raised -borderwidth 2 + -font font_ui pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 -- cgit v1.2.3 From bb816c5a25e3a87d4d9588ddcccf83b8a10d8d31 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 24 Jan 2007 19:08:49 -0500 Subject: git-gui: Display the size of the pack directory. Just as we show the amount of disk space taken by the loose objects, its interesting to know how much space is taken by the packs directory. So show that in our Database Statistics dialog. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 842fccc9f8..79f980c470 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2814,6 +2814,16 @@ proc do_stats {} { } close $fd + set packed_sz 0 + foreach p [glob -directory [gitdir objects pack] \ + -type f \ + -nocomplain -- *] { + incr packed_sz [file size $p] + } + if {$packed_sz > 0} { + set stats(size-pack) [expr {$packed_sz / 1024}] + } + set w .stats_view toplevel $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" @@ -2839,6 +2849,7 @@ proc do_stats {} { {size {Disk space used by loose objects} { KiB}} {in-pack {Number of packed objects}} {packs {Number of packs}} + {size-pack {Disk space used by packed objects} { KiB}} {prune-packable {Packed objects waiting for pruning}} {garbage {Garbage files}} } { -- cgit v1.2.3 From 86773d9bfc6124dec7b33094103f4819684bc4ae Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 24 Jan 2007 20:39:30 -0500 Subject: git-gui: Only allow Refresh in diff context menu when we have a diff. There is no reason to attempt refreshing an empty diff viewer, so the Refresh option of our diff context menu should be disabled when there is no diff currently shown. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index 79f980c470..b9e3d563e2 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -4261,6 +4261,7 @@ $ctxm add command \ -label {Refresh} \ -font font_ui \ -command reshow_diff +lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add command \ -label {Copy} \ -font font_ui \ -- cgit v1.2.3 From a25c51893317bcbd8e8a85b6da3a573fcd096d86 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 24 Jan 2007 21:20:57 -0500 Subject: git-gui: Allow staging/unstaging individual diff hunks. Just like `git-add --interactive` we can now stage and unstage individual hunks within a file, rather than the entire file at once. This works on the basic idea of scanning backwards from the mouse position to find the hunk header, then going forwards to find the end of the hunk. Everything in that is sent to `git apply --cached`, prefixed by the diff header lines. We ignore whitespace errors while applying a hunk, as we expect the user's pre-commit hook to catch any possible problems. This matches our existing behavior with regards to adding an entire file with no whitespace error checking. Applying hunks means that we now have to capture and save the diff header lines, rather than chucking them. Not really a big deal, we just needed a new global to hang onto that current header information. We probably could have recreated it on demand during apply_hunk but that would mean we need to implement all of the funny rules about how to encode weird path names (e.g. ones containing LF) into a diff header so that the `git apply` process would understand what we are asking it to do. Much simpler to just store this small amount of data in a global and replay it when needed. I'm making absolutely no attempt to correct the line numbers on the remaining hunk headers after one hunk has been applied. This may cause some hunks to fail, as the position information would not be correct. Users can always refresh the current diff before applying a failing hunk to work around the issue. Perhaps if we ever implement hunk splitting we could also fix the remaining hunk headers. Applying hunks directly means that we need to process the diff data in binary, rather than using the system encoding and an automatic linefeed translation. This ensures that CRLF formatted files will be able to be fed directly to `git apply` without failures. Unfortunately it also means we will see CRs show up in the GUI as ugly little boxes at the end of each line in a CRLF file. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 12 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index b9e3d563e2..c8098ac9f6 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -545,13 +545,15 @@ proc prune_selection {} { ## diff proc clear_diff {} { - global ui_diff current_diff_path ui_index ui_workdir + global ui_diff current_diff_path current_diff_header + global ui_index ui_workdir $ui_diff conf -state normal $ui_diff delete 0.0 end $ui_diff conf -state disabled set current_diff_path {} + set current_diff_header {} $ui_index tag remove in_diff 0.0 end $ui_workdir tag remove in_diff 0.0 end @@ -599,7 +601,7 @@ proc show_diff {path w {lno {}}} { global file_states file_lists global is_3way_diff diff_active repo_config global ui_diff ui_status_value ui_index ui_workdir - global current_diff_path current_diff_side + global current_diff_path current_diff_side current_diff_header if {$diff_active || ![lock_index read]} return @@ -623,6 +625,7 @@ proc show_diff {path w {lno {}}} { set diff_active 1 set current_diff_path $path set current_diff_side $w + set current_diff_header {} set ui_status_value "Loading diff of [escape_path $path]..." # - Git won't give us the diff, there's nothing to compare to! @@ -707,22 +710,30 @@ proc show_diff {path w {lno {}}} { return } - fconfigure $fd -blocking 0 -translation auto + fconfigure $fd \ + -blocking 0 \ + -encoding binary \ + -translation binary fileevent $fd readable [list read_diff $fd] } proc read_diff {fd} { - global ui_diff ui_status_value is_3way_diff diff_active + global ui_diff ui_status_value diff_active + global is_3way_diff current_diff_header $ui_diff conf -state normal while {[gets $fd line] >= 0} { # -- Cleanup uninteresting diff header lines. # - if {[string match {diff --git *} $line]} continue - if {[string match {diff --cc *} $line]} continue - if {[string match {diff --combined *} $line]} continue - if {[string match {--- *} $line]} continue - if {[string match {+++ *} $line]} continue + if { [string match {diff --git *} $line] + || [string match {diff --cc *} $line] + || [string match {diff --combined *} $line] + || [string match {--- *} $line] + || [string match {+++ *} $line]} { + append current_diff_header $line "\n" + continue + } + if {[string match {index *} $line]} continue if {$line eq {deleted file mode 120000}} { set line "deleted symlink" } @@ -731,8 +742,7 @@ proc read_diff {fd} { # if {[string match {@@@ *} $line]} {set is_3way_diff 1} - if {[string match {index *} $line] - || [string match {mode *} $line] + if {[string match {mode *} $line] || [string match {new file *} $line] || [string match {deleted file *} $line] || [string match {Binary files * and * differ} $line] @@ -799,6 +809,77 @@ proc read_diff {fd} { } } +proc apply_hunk {x y} { + global current_diff_path current_diff_header current_diff_side + global ui_diff ui_index file_states + + if {$current_diff_path eq {} || $current_diff_header eq {}} return + if {![lock_index apply_hunk]} return + + set apply_cmd {git apply --cached --whitespace=nowarn} + set mi [lindex $file_states($current_diff_path) 0] + if {$current_diff_side eq $ui_index} { + set mode unstage + lappend apply_cmd --reverse + if {[string index $mi 0] ne {M}} { + unlock_index + return + } + } else { + set mode stage + if {[string index $mi 1] ne {M}} { + unlock_index + return + } + } + + set s_lno [lindex [split [$ui_diff index @$x,$y] .] 0] + set s_lno [$ui_diff search -backwards -regexp ^@@ $s_lno.0 0.0] + if {$s_lno eq {}} { + unlock_index + return + } + + set e_lno [$ui_diff search -forwards -regexp ^@@ "$s_lno + 1 lines" end] + if {$e_lno eq {}} { + set e_lno end + } + + if {[catch { + set p [open "| $apply_cmd" w] + fconfigure $p -translation binary -encoding binary + puts -nonewline $p $current_diff_header + puts -nonewline $p [$ui_diff get $s_lno $e_lno] + close $p} err]} { + error_popup "Failed to $mode selected hunk.\n\n$err" + unlock_index + return + } + + $ui_diff conf -state normal + $ui_diff delete $s_lno $e_lno + $ui_diff conf -state disabled + + if {[$ui_diff get 1.0 end] eq "\n"} { + set o _ + } else { + set o ? + } + + if {$current_diff_side eq $ui_index} { + set mi ${o}M + } elseif {[string index $mi 0] eq {_}} { + set mi M$o + } else { + set mi ?$o + } + unlock_index + display_file $current_diff_path $mi + if {$o eq {_}} { + clear_diff + } +} + ###################################################################### ## ## commit @@ -4142,6 +4223,7 @@ bind_button3 $ui_comm "tk_popup $ctxm %X %Y" # -- Diff Header # set current_diff_path {} +set current_diff_side {} set diff_actions [list] proc trace_current_diff_path {varname args} { global current_diff_path diff_actions file_states @@ -4282,6 +4364,13 @@ $ctxm add command \ } lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add separator +$ctxm add command \ + -label {Apply/Reverse Hunk} \ + -font font_ui \ + -command {apply_hunk $cursorX $cursorY} +set ui_diff_applyhunk [$ctxm index last] +lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state] +$ctxm add separator $ctxm add command \ -label {Decrease Font Size} \ -font font_ui \ @@ -4313,7 +4402,16 @@ $ctxm add separator $ctxm add command -label {Options...} \ -font font_ui \ -command do_options -bind_button3 $ui_diff "tk_popup $ctxm %X %Y" +bind_button3 $ui_diff " + set cursorX %x + set cursorY %y + if {\$ui_index eq \$current_diff_side} { + $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit} + } else { + $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit} + } + tk_popup $ctxm %X %Y +" # -- Status Bar # -- cgit v1.2.3 From 30b14ed390f543e52665c8fc4f626aab78e3dcab Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 24 Jan 2007 21:30:23 -0500 Subject: git-gui: Elide CRs appearing in diff output from display. If we are displaying a diff for a DOS-style (CRLF) formatted file then the Tk text widget would normally show the CR at the end of every line; in most fonts this will come out as a square box. Rather than showing this character we'll tag it with a tag which forces the character to be elided away, so its not displayed. However since the character is still within the text buffer we can still obtain it and supply it over to `git apply` when staging or unstaging an individual hunk, ensuring that the file contents is always fully preserved as-is. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index c8098ac9f6..ee7fdaabfd 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -793,6 +793,9 @@ proc read_diff {fd} { } } $ui_diff insert end $line $tags + if {[string index $line end] eq "\r"} { + $ui_diff tag add d_cr {end - 2c} + } $ui_diff insert end "\n" $tags } $ui_diff conf -state disabled @@ -4304,6 +4307,7 @@ pack $ui_diff -side left -fill both -expand 1 pack .vpane.lower.diff.header -side top -fill x pack .vpane.lower.diff.body -side bottom -fill both -expand 1 +$ui_diff tag conf d_cr -elide true $ui_diff tag conf d_@ -foreground blue -font font_diffbold $ui_diff tag conf d_+ -foreground {#00a000} $ui_diff tag conf d_- -foreground red -- cgit v1.2.3 From 4e55d19a1377de55c950a90c10b31ae37b79363d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 12:54:59 -0500 Subject: git-gui: Cleanup end-of-line whitespace in commit messages. When committing changes its useless to have trailing whitespace on the end of a line within the commit message itself; this serves no purpose beyond wasting space in the repository. But it happens a lot on my Mac OS X system if I copy text out of a Terminal.app window and paste it into git-gui. We now clip any trailing whitespace from the commit buffer when loading it from a file, when saving it out to our backup file, or when making the actual commit object. I also fixed a bug where we lost the commit message buffer if you quit without editing the text region. This can happen if you quit and restart git-gui frequently in the middle of an editing session. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ee7fdaabfd..5d418b99c8 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -428,6 +428,7 @@ proc load_message {file} { } set content [string trim [read $fd]] close $fd + regsub -all -line {[ \r\t]+$} $content {} content $ui_comm delete 0.0 end $ui_comm insert end $content return 1 @@ -1046,6 +1047,7 @@ You must add at least 1 file before you can commit. # -- A message is required. # 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. @@ -2984,12 +2986,13 @@ proc do_quit {} { # set save [gitdir GITGUI_MSG] set msg [string trim [$ui_comm get 0.0 end]] - if {![string match amend* $commit_type] - && [$ui_comm edit modified] + regsub -all -line {[ \r\t]+$} $msg {} msg + if {(![string match amend* $commit_type] + || [$ui_comm edit modified]) && $msg ne {}} { catch { set fd [open $save w] - puts $fd [string trim [$ui_comm get 0.0 end]] + puts -nonewline $fd $msg close $fd } } else { -- cgit v1.2.3 From b9a75e3a979cc39f5c8d0e6d79e19ae31aef17e3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 12:55:20 -0500 Subject: git-gui: Unset unnecessary UI setup variable. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index 5d418b99c8..f587f31a2c 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -4419,6 +4419,7 @@ bind_button3 $ui_diff " } tk_popup $ctxm %X %Y " +unset ui_diff_applyhunk # -- Status Bar # -- cgit v1.2.3 From 23effa79f7697cd27f04cb6edc9680e11e15f02a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 12:57:57 -0500 Subject: git-gui: Force focus to the diff viewer on mouse click. Apparently a "feature" of Tcl/Tk on Mac OS X is that a disabled text widget cannot receive focus or receive a selection within it. This makes the diff viewer almost useless on that platform as you cannot select individual parts of the buffer. Now we force focus into the diff viewer when its clicked on with button 1. This works around the feature and allows selection to work within the viewer just like it does on other less sane systems, like Microsoft Windows. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index f587f31a2c..ea4136c98b 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -4472,6 +4472,7 @@ bind $ui_diff {catch {%W yview scroll -1 units};break} bind $ui_diff {catch {%W yview scroll 1 units};break} bind $ui_diff {catch {%W xview scroll -1 units};break} bind $ui_diff {catch {%W xview scroll 1 units};break} +bind $ui_diff {focus %W} if {!$single_commit} { bind . <$M1B-Key-n> do_create_branch -- cgit v1.2.3 From 5753ef1a4eca7b4ce04d1e4ea2250442bba4dbb9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 13:01:16 -0500 Subject: git-gui: Support 'Visualize All Branches' on Mac OS X. Now that recent versions of gitk (shipping with at least git 1.5.0-rc1 and later) actually accept command line revision specifiers without crashing on internal Tk errors we can offer the 'Visualize All Branches' menu item in the Repository menu on Mac OS X. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ea4136c98b..639f380784 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3772,12 +3772,10 @@ menu .mbar.repository -label {Visualize Current Branch} \ -command {do_gitk {}} \ -font font_ui -if {![is_MacOSX]} { - .mbar.repository add command \ - -label {Visualize All Branches} \ - -command {do_gitk {--all}} \ - -font font_ui -} +.mbar.repository add command \ + -label {Visualize All Branches} \ + -command {do_gitk {--all}} \ + -font font_ui .mbar.repository add separator if {!$single_commit} { -- cgit v1.2.3 From 68567679a2d7cec04be1a62e09874189e46de4b6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 13:07:53 -0500 Subject: git-gui: Pad the database statistics dialog window. The stat frame was right on the edge of the window on Mac OS X, making the frame's border blend in with the window border. Not exactly the effect I had in mind. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 639f380784..9dbe1156c5 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2950,7 +2950,7 @@ proc do_stats {} { label $w.stat.v_$name -text $value -anchor w -font font_ui grid $w.stat.l_$name $w.stat.v_$name -sticky we -padx {0 5} } - pack $w.stat + pack $w.stat -pady 10 -padx 10 bind $w "grab $w; focus $w" bind $w [list destroy $w] -- cgit v1.2.3 From fb08baca3340099d149a88f6ede19a6870870c08 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 16:50:15 -0500 Subject: git-gui: Prefer Tk's entry widget over a 1 line text field. I'm a fool and previously used a text widget configured with a height of 1 and special bindings to handle focus traversal rather than the already built (and properly behaved) entry widget. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 75 +++++++++++++++++++++++++++----------------------------------- 1 file changed, 33 insertions(+), 42 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 9dbe1156c5..811e20217d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1865,8 +1865,9 @@ proc do_create_branch_action {w} { global all_heads null_sha1 repo_config global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead + global create_branch_name create_branch_revexp - set newbranch [string trim [$w.desc.name_t get 0.0 end]] + set newbranch $create_branch_name if {$newbranch eq {} || $newbranch eq $repo_config(gui.newbranchtemplate)} { tk_messageBox \ @@ -1903,7 +1904,7 @@ proc do_create_branch_action {w} { switch -- $create_branch_revtype { head {set rev $create_branch_head} tracking {set rev $create_branch_trackinghead} - expression {set rev [string trim [$w.from.exp_t get 0.0 end]]} + expression {set rev $create_branch_revexp} } if {[catch {set cmt [exec git rev-parse --verify "${rev}^0"]}]} { tk_messageBox \ @@ -1958,6 +1959,7 @@ proc do_create_branch {} { global all_heads current_branch repo_config global create_branch_checkout create_branch_revtype global create_branch_head create_branch_trackinghead + global create_branch_name create_branch_revexp set w .branch_editor toplevel $w @@ -1983,26 +1985,18 @@ proc do_create_branch {} { -text {Branch Description} \ -font font_ui label $w.desc.name_l -text {Name:} -font font_ui - text $w.desc.name_t \ + entry $w.desc.name_t \ -borderwidth 1 \ -relief sunken \ - -height 1 \ -width 40 \ - -font font_ui - $w.desc.name_t insert 0.0 $repo_config(gui.newbranchtemplate) - grid $w.desc.name_l $w.desc.name_t -sticky we -padx {0 5} - bind $w.desc.name_t {focus [tk_focusPrev %W];break} - bind $w.desc.name_t {focus [tk_focusNext %W];break} - bind $w.desc.name_t "do_create_branch_action $w;break" - bind $w.desc.name_t { - if {{%K} ne {BackSpace} - && {%K} ne {Tab} - && {%K} ne {Escape} - && {%K} ne {Return}} { - if {%k <= 32} break - if {[string first %A {~^:?*[}] >= 0} break + -textvariable create_branch_name \ + -font font_ui \ + -validate key \ + -validatecommand { + if {%d == 1 && [regexp {[~^:?*\[\0- ]} %S]} {return 0} + return 1 } - } + grid $w.desc.name_l $w.desc.name_t -sticky we -padx {0 5} grid columnconfigure $w.desc 1 -weight 1 pack $w.desc -anchor nw -fill x -pady 5 -padx 5 @@ -2034,18 +2028,21 @@ proc do_create_branch {} { -value expression \ -variable create_branch_revtype \ -font font_ui - text $w.from.exp_t \ + entry $w.from.exp_t \ -borderwidth 1 \ -relief sunken \ - -height 1 \ -width 50 \ - -font font_ui + -textvariable create_branch_revexp \ + -font font_ui \ + -validate key \ + -validatecommand { + if {%d == 1 && [regexp {\s} %S]} {return 0} + if {%d == 1 && [string length %S] > 0} { + set create_branch_revtype expression + } + return 1 + } grid $w.from.exp_r $w.from.exp_t -sticky we -padx {0 5} - bind $w.from.exp_t {focus [tk_focusPrev %W];break} - bind $w.from.exp_t {focus [tk_focusNext %W];break} - bind $w.from.exp_t "do_create_branch_action $w;break" - bind $w.from.exp_t break - bind $w.from.exp_t {set create_branch_revtype expression} grid columnconfigure $w.from 1 -weight 1 pack $w.from -anchor nw -fill x -pady 5 -padx 5 @@ -2062,8 +2059,14 @@ proc do_create_branch {} { set create_branch_checkout 1 set create_branch_head $current_branch set create_branch_revtype head + set create_branch_name $repo_config(gui.newbranchtemplate) + set create_branch_revexp {} - bind $w "grab $w; focus $w.desc.name_t" + bind $w " + grab $w + $w.desc.name_t icursor end + focus $w.desc.name_t + " bind $w "destroy $w" bind $w "do_create_branch_action $w;break" wm title $w "[appname] ([reponame]): Create Branch" @@ -3329,10 +3332,7 @@ proc do_options {} { pack $w.buttons.restore -side left button $w.buttons.save -text Save \ -font font_ui \ - -command " - catch {eval \[bind \[focus -displayof $w\] \]} - do_save_config $w - " + -command [list do_save_config $w] pack $w.buttons.save -side right button $w.buttons.cancel -text {Cancel} \ -font font_ui \ @@ -3382,21 +3382,12 @@ proc do_options {} { t { frame $w.$f.$name label $w.$f.$name.l -text "$text:" -font font_ui - text $w.$f.$name.v \ + entry $w.$f.$name.v \ -borderwidth 1 \ -relief sunken \ - -height 1 \ -width 20 \ + -textvariable ${f}_config_new(gui.$name) \ -font font_ui - $w.$f.$name.v insert 0.0 [set ${f}_config_new(gui.$name)] - bind $w.$f.$name.v {focus [tk_focusPrev %W];break} - bind $w.$f.$name.v {focus [tk_focusNext %W];break} - bind $w.$f.$name.v break - bind $w.$f.$name.v "$w.$f.$name.v tag add sel 0.0 end" - bind $w.$f.$name.v " - set ${f}_config_new(gui.$name) \ - \[string trim \[$w.$f.$name.v get 0.0 end\]\] - " pack $w.$f.$name.l -side left -anchor w pack $w.$f.$name.v -side left -anchor w \ -fill x -expand 1 \ -- cgit v1.2.3 From 3f7fd924a92c07c0fd4e8bd80b941171097db7f0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 17:16:22 -0500 Subject: git-gui: Remove Pull menu and cleanup Branch/Fetch/Push menus. The Pull menu as it stands right now is a really horrible idea. Most users will have too many branches show up in this menu, and what with the new globbing syntax for fetch entries we were offering up possible merging that just isn't really valid. So this menu is dead and will be rewritten to support better merge capabilities. The Branch menu shouldn't include a separator entry if there are no branches, it just looks too damn weird. This can happen in an initial repository before any branches have been created and before the first commit. The Fetch and Push menus should just be organized around their own menus rather than being given the menu to populate. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 54 +++++++++--------------------------------------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 811e20217d..7792eb4d7f 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1822,7 +1822,9 @@ proc populate_branch_menu {} { } } - $m add separator + if {$all_heads ne {}} { + $m add separator + } foreach b $all_heads { $m add radiobutton \ -label $b \ @@ -2429,9 +2431,10 @@ proc load_all_remotes {} { set all_remotes [lsort -unique $all_remotes] } -proc populate_fetch_menu {m} { +proc populate_fetch_menu {} { global all_remotes repo_config + set m .mbar.fetch foreach r $all_remotes { set enable 0 if {![catch {set a $repo_config(remote.$r.url)}]} { @@ -2460,9 +2463,10 @@ proc populate_fetch_menu {m} { } } -proc populate_push_menu {m} { +proc populate_push_menu {} { global all_remotes repo_config + set m .mbar.push foreach r $all_remotes { set enable 0 if {![catch {set a $repo_config(remote.$r.url)}]} { @@ -2491,43 +2495,6 @@ proc populate_push_menu {m} { } } -proc populate_pull_menu {m} { - global repo_config all_remotes disable_on_lock - - foreach remote $all_remotes { - set rb_list [list] - if {[array get repo_config remote.$remote.url] ne {}} { - if {[array get repo_config remote.$remote.fetch] ne {}} { - foreach line $repo_config(remote.$remote.fetch) { - if {[regexp {^([^:]+):} $line line rb]} { - lappend rb_list $rb - } - } - } - } else { - catch { - set fd [open [gitdir remotes $remote] r] - while {[gets $fd line] >= 0} { - if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} { - lappend rb_list $rb - } - } - close $fd - } - } - - foreach rb $rb_list { - regsub ^refs/heads/ $rb {} rb_short - $m add command \ - -label "Branch $rb_short from $remote..." \ - -command [list pull_remote $remote $rb] \ - -font font_ui - lappend disable_on_lock \ - [list $m entryconf [$m index last] -state] - } - } -} - ###################################################################### ## ## icons @@ -3751,7 +3718,6 @@ if {!$single_commit} { .mbar add cascade -label Commit -menu .mbar.commit if {!$single_commit} { .mbar add cascade -label Fetch -menu .mbar.fetch - .mbar add cascade -label Pull -menu .mbar.pull .mbar add cascade -label Push -menu .mbar.push } . configure -menu .mbar @@ -3929,7 +3895,6 @@ lappend disable_on_lock \ # if {!$single_commit} { menu .mbar.fetch - menu .mbar.pull menu .mbar.push } @@ -4565,9 +4530,8 @@ if {!$single_commit} { load_all_heads populate_branch_menu - populate_fetch_menu .mbar.fetch - populate_pull_menu .mbar.pull - populate_push_menu .mbar.push + populate_fetch_menu + populate_push_menu } # -- Only suggest a gc run if we are going to stay running. -- cgit v1.2.3 From d070c4cb17e807c157aaeb24d9b80a1112334e57 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 17:07:03 -0500 Subject: git-gui: Don't switch branches if changing to the current branch. Its pointless to switch to the current branch, so don't do it. We are already on it and the current index and working directory should just be left alone. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 7792eb4d7f..faae6ce7c5 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2271,6 +2271,13 @@ The rescan will be automatically started now. return } + # -- Don't do a pointless switch. + # + if {$current_branch eq $new_branch} { + unlock_index + return + } + if {$repo_config(gui.trustmtime) eq {true}} { switch_branch_stage2 {} $new_branch } else { -- cgit v1.2.3 From 5f8b70b1dc3d149782564e04d5d55565a5157d93 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 21:33:06 -0500 Subject: git-gui: Maintain the same file list for diff during refresh. I just noticed that a file was always jumping to compare against HEAD and the index during a refresh, even if the diff viewer was comparing the index against the working directory prior to the refresh. The bug turned out to be caused by a foreach loop going through all file list names searching for the path. Since $ui_index was the first one searched and the file was contained in that file list the loop broke out, leaving $w set to $ui_index when it had been set by the caller to $ui_workdir. Silly bug caused by using a parameter as a loop index. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index faae6ce7c5..b0a195ce8a 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -607,16 +607,13 @@ proc show_diff {path w {lno {}}} { if {$diff_active || ![lock_index read]} return clear_diff - if {$w eq {} || $lno == {}} { - foreach w [array names file_lists] { - set lno [lsearch -sorted $file_lists($w) $path] - if {$lno >= 0} { - incr lno - break - } + if {$lno == {}} { + set lno [lsearch -sorted $file_lists($w) $path] + if {$lno >= 0} { + incr lno } } - if {$w ne {} && $lno >= 1} { + if {$lno >= 1} { $w tag add in_diff $lno.0 [expr {$lno + 1}].0 } -- cgit v1.2.3 From 156b29211ae4f8143f143fd6cce6e93b60c51f43 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 22:38:59 -0500 Subject: git-gui: Always use lsearch -exact, to prevent globbing. Anytime we are using lsearch we are doing [lsearch -sorted] and we are applying it to file paths (or file path like things). Its valid for these to contain special glob characters, but when that happens we do not want globbing to occur. Instead we really need exact match semantics. Always supplying -exact to lsearch will ensure that is the case. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index b0a195ce8a..b203c56a98 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -568,7 +568,7 @@ proc reshow_diff {} { if {$p eq {} || $current_diff_side eq {} || [catch {set s $file_states($p)}] - || [lsearch -sorted $file_lists($current_diff_side) $p] == -1} { + || [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} { clear_diff } else { show_diff $p $current_diff_side @@ -608,7 +608,7 @@ proc show_diff {path w {lno {}}} { clear_diff if {$lno == {}} { - set lno [lsearch -sorted $file_lists($w) $path] + set lno [lsearch -sorted -exact $file_lists($w) $path] if {$lno >= 0} { incr lno } @@ -1427,7 +1427,7 @@ proc display_file_helper {w path icon_name old_m new_m} { global file_lists if {$new_m eq {_}} { - set lno [lsearch -sorted $file_lists($w) $path] + set lno [lsearch -sorted -exact $file_lists($w) $path] if {$lno >= 0} { set file_lists($w) [lreplace $file_lists($w) $lno $lno] incr lno @@ -1438,7 +1438,7 @@ proc display_file_helper {w path icon_name old_m new_m} { } elseif {$old_m eq {_} && $new_m ne {_}} { lappend file_lists($w) $path set file_lists($w) [lsort -unique $file_lists($w)] - set lno [lsearch -sorted $file_lists($w) $path] + set lno [lsearch -sorted -exact $file_lists($w) $path] incr lno $w conf -state normal $w image create $lno.0 \ @@ -2142,7 +2142,7 @@ Delete the selected branches?} if {[catch {exec git update-ref -d "refs/heads/$b" $o} err]} { append failed " - $b: $err\n" } else { - set x [lsearch -sorted $all_heads $b] + set x [lsearch -sorted -exact $all_heads $b] if {$x >= 0} { set all_heads [lreplace $all_heads $x $x] } -- cgit v1.2.3 From 1d6a9787526372609d3b105659e9cb5f3718df4a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 25 Jan 2007 23:50:27 -0500 Subject: git-gui: Added arbitrary branch pushing support. Because its common for some users to push topic branches to a remote repository for review and merging by other parties, users need an easy way to push one or more branches to a remote repository without needing to edit their .git/config file anytime their set of active branches changes. We now provide a basic 'Push...' menu action in the Push menu which opens a dialog allowing the user to select from their set of local branches (refs/heads, minus tracking branches). The user can designate which repository to send the changes to by selecting from an already configured remote or by entering any valid Git URL. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index b203c56a98..de4cec20aa 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2471,6 +2471,7 @@ proc populate_push_menu {} { global all_remotes repo_config set m .mbar.push + set fast_count 0 foreach r $all_remotes { set enable 0 if {![catch {set a $repo_config(remote.$r.url)}]} { @@ -2491,12 +2492,165 @@ proc populate_push_menu {} { } if {$enable} { + if {!$fast_count} { + $m add separator + } $m add command \ -label "Push to $r..." \ -command [list push_to $r] \ -font font_ui + incr fast_count + } + } +} + +proc start_push_anywhere_action {w} { + global push_urltype push_remote push_url push_thin push_tags + + set r_url {} + switch -- $push_urltype { + remote {set r_url $push_remote} + url {set r_url $push_url} + } + if {$r_url eq {}} return + + set cmd [list git push] + lappend cmd -v + if {$push_thin} { + lappend cmd --thin + } + if {$push_tags} { + lappend cmd --tags + } + lappend cmd $r_url + set cnt 0 + foreach i [$w.source.l curselection] { + set b [$w.source.l get $i] + lappend cmd "refs/heads/$b:refs/heads/$b" + incr cnt + } + if {$cnt == 0} { + return + } elseif {$cnt == 1} { + set unit branch + } else { + set unit branches + } + + set cons [new_console "push $r_url" "Pushing $cnt $unit to $r_url"] + console_exec $cons $cmd + destroy $w +} + +trace add variable push_remote write \ + [list radio_selector push_urltype remote] + +proc do_push_anywhere {} { + global all_heads all_remotes current_branch + global push_urltype push_remote push_url push_thin push_tags + + set w .push_setup + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + label $w.header -text {Push Branches} -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.create -text Push \ + -font font_ui \ + -command [list start_push_anywhere_action $w] + pack $w.buttons.create -side right + button $w.buttons.cancel -text {Cancel} \ + -font font_ui \ + -command [list destroy $w] + pack $w.buttons.cancel -side right -padx 5 + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + labelframe $w.source \ + -text {Source Branches} \ + -font font_ui + listbox $w.source.l \ + -height 10 \ + -width 50 \ + -selectmode extended \ + -font font_ui + foreach h $all_heads { + $w.source.l insert end $h + if {$h eq $current_branch} { + $w.source.l select set end + } + } + pack $w.source.l -fill both -pady 5 -padx 5 + pack $w.source -fill both -pady 5 -padx 5 + + labelframe $w.dest \ + -text {Destination Repository} \ + -font font_ui + if {$all_remotes ne {}} { + radiobutton $w.dest.remote_r \ + -text {Remote:} \ + -value remote \ + -variable push_urltype \ + -font font_ui + eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes + grid $w.dest.remote_r $w.dest.remote_m -sticky w + if {[lsearch -sorted -exact $all_remotes origin] != -1} { + set push_remote origin + } else { + set push_remote [lindex $all_remotes 0] } + set push_urltype remote + } else { + set push_urltype url } + radiobutton $w.dest.url_r \ + -text {Arbitrary URL:} \ + -value url \ + -variable push_urltype \ + -font font_ui + entry $w.dest.url_t \ + -borderwidth 1 \ + -relief sunken \ + -width 50 \ + -textvariable push_url \ + -font font_ui \ + -validate key \ + -validatecommand { + if {%d == 1 && [regexp {\s} %S]} {return 0} + if {%d == 1 && [string length %S] > 0} { + set push_urltype url + } + return 1 + } + grid $w.dest.url_r $w.dest.url_t -sticky we -padx {0 5} + grid columnconfigure $w.dest 1 -weight 1 + pack $w.dest -anchor nw -fill x -pady 5 -padx 5 + + labelframe $w.options \ + -text {Transfer Options} \ + -font font_ui + checkbutton $w.options.thin \ + -text {Use thin pack (for slow network connections)} \ + -variable push_thin \ + -font font_ui + grid $w.options.thin -columnspan 2 -sticky w + checkbutton $w.options.tags \ + -text {Include tags} \ + -variable push_tags \ + -font font_ui + grid $w.options.tags -columnspan 2 -sticky w + grid columnconfigure $w.options 1 -weight 1 + pack $w.options -anchor nw -fill x -pady 5 -padx 5 + + set push_url {} + set push_thin 0 + set push_tags 0 + + bind $w "grab $w" + bind $w "destroy $w" + wm title $w "[appname] ([reponame]): Push" + tkwait window $w } ###################################################################### @@ -3900,6 +4054,10 @@ lappend disable_on_lock \ if {!$single_commit} { menu .mbar.fetch menu .mbar.push + + .mbar.push add command -label {Push...} \ + -command do_push_anywhere \ + -font font_ui } if {[is_MacOSX]} { -- cgit v1.2.3 From 86a2af608700896a004be50c939a76a539bdba4d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 00:47:44 -0500 Subject: git-gui: Remove no longer used pull from remote code. Because we aren't going to support single click pulling of changes from an existing remote anytime in the near future, I'm moving the code which used to perform that action. Hopefully we'll be able to do something like it in the near-future, but also support local branches just as easily. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 73 +++++--------------------------------------------------------- 1 file changed, 5 insertions(+), 68 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index de4cec20aa..c42673c8e3 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1269,83 +1269,20 @@ proc commit_committree {fd_wt curHEAD msg} { ###################################################################### ## -## fetch pull push +## fetch push proc fetch_from {remote} { - set w [new_console "fetch $remote" \ + set w [new_console \ + "fetch $remote" \ "Fetching new changes from $remote"] set cmd [list git fetch] lappend cmd $remote console_exec $w $cmd } -proc pull_remote {remote branch} { - global HEAD commit_type file_states repo_config - - if {![lock_index update]} return - - # -- Our in memory state should match the repository. - # - repository_state curType curHEAD curMERGE_HEAD - if {$commit_type ne $curType || $HEAD ne $curHEAD} { - info_popup {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 a pull operation can be started. - -The rescan will be automatically started now. -} - unlock_index - rescan {set ui_status_value {Ready.}} - return - } - - # -- No differences should exist before a pull. - # - if {[array size file_states] != 0} { - error_popup {Uncommitted but modified files are present. - -You should not perform a pull with unmodified -files in your working directory as Git will be -unable to recover from an incorrect merge. - -You should commit or revert all changes before -starting a pull operation. -} - unlock_index - return - } - - set w [new_console "pull $remote $branch" \ - "Pulling new changes from branch $branch in $remote"] - set cmd [list git pull] - if {$repo_config(gui.pullsummary) eq {false}} { - lappend cmd --no-summary - } - lappend cmd $remote - lappend cmd $branch - console_exec $w $cmd [list post_pull_remote $remote $branch] -} - -proc post_pull_remote {remote branch success} { - global HEAD PARENT MERGE_HEAD commit_type selected_commit_type - global ui_status_value - - unlock_index - if {$success} { - repository_state commit_type HEAD MERGE_HEAD - set PARENT $HEAD - set selected_commit_type new - set ui_status_value "Pulling $branch from $remote complete." - } else { - rescan [list set ui_status_value \ - "Conflicts detected while pulling $branch from $remote."] - } -} - proc push_to {remote} { - set w [new_console "push $remote" \ + set w [new_console \ + "push $remote" \ "Pushing changes to $remote"] set cmd [list git push] lappend cmd $remote -- cgit v1.2.3 From b972ea59e422588963bda7be8e04c59728accadf Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 00:49:17 -0500 Subject: git-gui: Always use -v option to push. Right now `git-push -v` is actually not that verbose; it merely adds the URL it is pushing to. This can be informative if you are pushing to a configured remote, as you may not actually remember what URL that remote is connected to. That detail can be important if the push fails and you attempt to communicate the errors to a 3rd party to help you resolve the issue. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index c42673c8e3..48e1f821de 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1285,6 +1285,7 @@ proc push_to {remote} { "push $remote" \ "Pushing changes to $remote"] set cmd [list git push] + lappend cmd -v lappend cmd $remote console_exec $w $cmd } -- cgit v1.2.3 From 6c3d1481bab96659a80771194d935cb6db02300e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 01:29:00 -0500 Subject: git-gui: Refactor console success/failure handling. Because I want to be able to run multiple output-producing commands in a single 'console' window within git-gui I'm refactoring the console handling routines to require the "after" argument of console_exec. This should specify a procedure to execute which will receive two args, the first is the console window handle and the second is the status of the last command (0 on failure, 1 on success). A new procedure console_done can be passed to the last console_exec command to forward perform all cleanup and enable the Close button. Its status argument is used to update the final status bar on the bottom of the console window. This isn't any real logic changing, and no new functionality is in this patch. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 48e1f821de..d264558076 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1277,7 +1277,7 @@ proc fetch_from {remote} { "Fetching new changes from $remote"] set cmd [list git fetch] lappend cmd $remote - console_exec $w $cmd + console_exec $w $cmd console_done } proc push_to {remote} { @@ -1287,7 +1287,7 @@ proc push_to {remote} { set cmd [list git push] lappend cmd -v lappend cmd $remote - console_exec $w $cmd + console_exec $w $cmd console_done } ###################################################################### @@ -2476,7 +2476,7 @@ proc start_push_anywhere_action {w} { } set cons [new_console "push $r_url" "Pushing $cnt $unit to $r_url"] - console_exec $cons $cmd + console_exec $cons $cmd console_done destroy $w } @@ -2854,7 +2854,7 @@ proc console_init {w} { return $w } -proc console_exec {w cmd {after {}}} { +proc console_exec {w cmd after} { # -- Windows tosses the enviroment when we exec our child. # But most users need that so we have to relogin. :-( # @@ -2873,7 +2873,7 @@ proc console_exec {w cmd {after {}}} { } proc console_read {w fd after} { - global console_cr console_data + global console_cr set buf [read $fd] if {$buf ne {}} { @@ -2907,25 +2907,36 @@ proc console_read {w fd after} { fconfigure $fd -blocking 1 if {[eof $fd]} { if {[catch {close $fd}]} { - if {![winfo exists $w]} {console_init $w} - $w.m.s conf -background red -text {Error: Command Failed} - $w.ok conf -state normal set ok 0 - } elseif {[winfo exists $w]} { - $w.m.s conf -background green -text {Success} - $w.ok conf -state normal + } else { set ok 1 } - array unset console_cr $w - array unset console_data $w - if {$after ne {}} { - uplevel #0 $after $ok - } + uplevel #0 $after $w $ok return } fconfigure $fd -blocking 0 } +proc console_done {w ok} { + global console_cr console_data + + if {$ok} { + if {[winfo exists $w]} { + $w.m.s conf -background green -text {Success} + $w.ok conf -state normal + } + } else { + if {![winfo exists $w]} { + console_init $w + } + $w.m.s conf -background red -text {Error: Command Failed} + $w.ok conf -state normal + } + + array unset console_cr $w + array unset console_data $w +} + ###################################################################### ## ## ui commands @@ -3027,7 +3038,7 @@ proc do_stats {} { proc do_gc {} { set w [new_console {gc} {Compressing the object database}] - console_exec $w {git gc} + console_exec $w {git gc} console_done } proc do_fsck_objects {} { @@ -3037,7 +3048,7 @@ proc do_fsck_objects {} { lappend cmd --full lappend cmd --cache lappend cmd --strict - console_exec $w $cmd + console_exec $w $cmd console_done } set is_quitting 0 -- cgit v1.2.3 From bc7452f5e7c0c472bc580e2e00d84f31d1af50ce Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 02:02:09 -0500 Subject: git-gui: Use builtin version of 'git gc'. Technically the new git-gc command is strictly Porcelain; its invoking multiple plumbing commands to do its work. Since git-gui tries to not rely on Porclain we shouldn't be invoking git-gc directly, instead we should perform its tasks on our own. To make this easy I've created console_chain, which takes a list of tasks to perform and runs them all in the same console window. If any individual task fails then the chain stops running and the window shows a failure bar. Only once all tasks have been completed will it invoke console_done with a successful status. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index d264558076..0982da5690 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2917,9 +2917,45 @@ proc console_read {w fd after} { fconfigure $fd -blocking 0 } -proc console_done {w ok} { +proc console_chain {cmdlist w {ok 1}} { + if {$ok} { + if {[llength $cmdlist] == 0} { + console_done $w $ok + return + } + + set cmd [lindex $cmdlist 0] + set cmdlist [lrange $cmdlist 1 end] + + if {[lindex $cmd 0] eq {console_exec}} { + console_exec $w \ + [lindex $cmd 1] \ + [list console_chain $cmdlist] + } else { + uplevel #0 $cmd $cmdlist $w $ok + } + } else { + console_done $w $ok + } +} + +proc console_done {args} { global console_cr console_data + switch -- [llength $args] { + 2 { + set w [lindex $args 0] + set ok [lindex $args 1] + } + 3 { + set w [lindex $args 1] + set ok [lindex $args 2] + } + default { + error "wrong number of args: console_done ?ignored? w ok" + } + } + if {$ok} { if {[winfo exists $w]} { $w.m.s conf -background green -text {Success} @@ -3038,7 +3074,12 @@ proc do_stats {} { proc do_gc {} { set w [new_console {gc} {Compressing the object database}] - console_exec $w {git gc} console_done + console_chain { + {console_exec {git pack-refs --prune}} + {console_exec {git reflog expire --all}} + {console_exec {git repack -a -d -l}} + {console_exec {git rerere gc}} + } $w } proc do_fsck_objects {} { -- cgit v1.2.3 From e4834837a8a02708efb6a7f8a248087a8cd49ab3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 03:33:56 -0500 Subject: git-gui: Implement local merge operations. To allow users to merge local heads and tracking branches we now offer a dialog which lets the user select 1-15 branches and merge them using the stock `git merge` Grand Unified Merge Driver. Originally I had wanted to implement this merge internally within git-gui as I consider GUMD to be mostly Porcelain-ish, but the truth is it does its job exceedingly well and its a relatively complex chunk of code. I'll probably circle back later and try to remove the invocation of GUMD from git-gui, but right now it lets me get the job done faster. Users cannot start a merge if they are currently in the middle of one, or if they are amending a commit. Trying to do either is just stupid and should be stopped as early as possible. I've also made it simple for users to startup a gitk session prior to a merge by offering a Visualize button which runs `gitk $revs --not HEAD`, where $revs is the list of branches currently selected in the merge dialog. This makes it quite simple to find out what the damage will be to the current branch if you were to carry out the currently proposed merge. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 225 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 213 insertions(+), 12 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 0982da5690..c25cc4f145 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -983,8 +983,8 @@ proc commit_tree {} { global HEAD commit_type file_states ui_comm repo_config global ui_status_value pch_error - if {![lock_index update]} return if {[committer_ident] eq {}} return + if {![lock_index update]} return # -- Our in memory state should match the repository. # @@ -2591,6 +2591,201 @@ proc do_push_anywhere {} { tkwait window $w } +###################################################################### +## +## merge + +proc can_merge {} { + global HEAD commit_type file_states + + if {[string match amend* $commit_type]} { + info_popup {Cannot merge while amending. + +You must finish amending this commit before +starting any type of merge. +} + return 0 + } + + if {[committer_ident] eq {}} {return 0} + if {![lock_index merge]} {return 0} + + # -- Our in memory state should match the repository. + # + repository_state curType curHEAD curMERGE_HEAD + if {$commit_type ne $curType || $HEAD ne $curHEAD} { + info_popup {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 a merge can be performed. + +The rescan will be automatically started now. +} + unlock_index + rescan {set ui_status_value {Ready.}} + return 0 + } + + foreach path [array names file_states] { + switch -glob -- [lindex $file_states($path) 0] { + U? { + error_popup "You are in the middle of a conflicted merge. + +File [short_path $path] has merge conflicts. + +You must resolve them, add the file, and commit to +complete the current merge. Only then can you +begin another merge. +" + unlock_index + return 0 + } + } + } + + return 1 +} + +proc visualize_local_merge {w} { + set revs {} + foreach i [$w.source.l curselection] { + lappend revs [$w.source.l get $i] + } + if {$revs eq {}} return + lappend revs --not HEAD + do_gitk $revs +} + +proc start_local_merge_action {w} { + global HEAD + + set cmd [list git merge] + set names {} + set revcnt 0 + foreach i [$w.source.l curselection] { + set b [$w.source.l get $i] + lappend cmd $b + lappend names $b + incr revcnt + } + + if {$revcnt == 0} { + return + } elseif {$revcnt == 1} { + set unit branch + } elseif {$revcnt <= 15} { + set unit branches + } else { + tk_messageBox \ + -icon error \ + -type ok \ + -title [wm title $w] \ + -parent $w \ + -message "Too many branches selected. + +You have requested to merge $revcnt branches +in an octopus merge. This exceeds Git's +internal limit of 15 branches per merge. + +Please select fewer branches. To merge more +than 15 branches, merge the branches in batches. +" + return + } + + set cons [new_console "Merge" "Merging [join $names {, }]"] + console_exec $cons $cmd finish_merge + bind $w {} + destroy $w +} + +proc finish_merge {w ok} { + console_done $w $ok + if {$ok} { + set msg {Merge completed successfully.} + } else { + set msg {Merge failed. Conflict resolution is required.} + } + unlock_index + rescan [list set ui_status_value $msg] +} + +proc do_local_merge {} { + global current_branch + + if {![can_merge]} return + + set w .merge_setup + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + label $w.header \ + -text "Merge Into $current_branch" \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.visualize -text Visualize \ + -font font_ui \ + -command [list visualize_local_merge $w] + pack $w.buttons.visualize -side left + button $w.buttons.create -text Merge \ + -font font_ui \ + -command [list start_local_merge_action $w] + pack $w.buttons.create -side right + button $w.buttons.cancel -text {Cancel} \ + -font font_ui \ + -command [list destroy $w] + pack $w.buttons.cancel -side right -padx 5 + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + labelframe $w.source \ + -text {Source Branches} \ + -font font_ui + listbox $w.source.l \ + -height 10 \ + -width 25 \ + -selectmode extended \ + -yscrollcommand [list $w.source.sby set] \ + -font font_ui + scrollbar $w.source.sby -command [list $w.source.l yview] + pack $w.source.sby -side right -fill y + pack $w.source.l -side left -fill both -expand 1 + pack $w.source -fill both -expand 1 -pady 5 -padx 5 + + set cmd [list git for-each-ref] + lappend cmd {--format=%(objectname) %(refname)} + lappend cmd refs/heads + lappend cmd refs/remotes + set fr_fd [open "| $cmd" r] + fconfigure $fr_fd -translation binary + while {[gets $fr_fd line] > 0} { + set line [split $line { }] + set sha1([lindex $line 0]) [lindex $line 1] + } + close $fr_fd + + set to_show {} + set fr_fd [open "| git rev-list --all --not HEAD"] + while {[gets $fr_fd line] > 0} { + if {[catch {set ref $sha1($line)}]} continue + regsub ^refs/(heads|remotes)/ $ref {} ref + lappend to_show $ref + } + close $fr_fd + + foreach ref [lsort -unique $to_show] { + $w.source.l insert end $ref + } + + bind $w "grab $w" + bind $w "unlock_index;destroy $w" + bind $w unlock_index + wm title $w "[appname] ([reponame]): Merge" + tkwait window $w +} + ###################################################################### ## ## icons @@ -3865,6 +4060,7 @@ if {!$single_commit} { } .mbar add cascade -label Commit -menu .mbar.commit if {!$single_commit} { + .mbar add cascade -label Merge -menu .mbar.merge .mbar add cascade -label Fetch -menu .mbar.fetch .mbar add cascade -label Push -menu .mbar.push } @@ -4039,17 +4235,6 @@ lappend disable_on_lock \ lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] -# -- Transport menus -# -if {!$single_commit} { - menu .mbar.fetch - menu .mbar.push - - .mbar.push add command -label {Push...} \ - -command do_push_anywhere \ - -font font_ui -} - if {[is_MacOSX]} { # -- Apple Menu (Mac OS X only) # @@ -4130,6 +4315,22 @@ pack .branch.l1 -side left pack .branch.cb -side left -fill x pack .branch -side top -fill x +if {!$single_commit} { + menu .mbar.merge + .mbar.merge add command -label {Local Merge...} \ + -command do_local_merge \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.merge entryconf [.mbar.merge index last] -state] + + menu .mbar.fetch + + menu .mbar.push + .mbar.push add command -label {Push...} \ + -command do_push_anywhere \ + -font font_ui +} + # -- Main Window Layout # panedwindow .vpane -orient vertical -- cgit v1.2.3 From ce9735dfbd77ab7cbcb97ba8749b2f6eaa7f2527 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 03:54:05 -0500 Subject: git-gui: Let users abort with `reset --hard` type logic. If you get into the middle of a merge that turns out to be horrible and just not something you want to do right now, odds are you need to run `git reset --hard` to recover your working directory to a pre-merge state. We now offer Merge->Abort Merge for exactly this purpose, however its also useful to thow away a non-merge, as its basically the same logic as `git reset --hard`. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index c25cc4f145..0c2dbbebe1 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2786,6 +2786,61 @@ proc do_local_merge {} { tkwait window $w } +proc do_reset_hard {} { + global HEAD commit_type file_states + + if {[string match amend* $commit_type]} { + info_popup {Cannot abort while amending. + +You must finish amending this commit. +} + return + } + + if {![lock_index abort]} return + + if {[string match *merge* $commit_type]} { + set op merge + } else { + set op commit + } + + if {[ask_popup "Abort $op? + +Aborting the current $op will cause +*ALL* uncommitted changes to be lost. + +Continue with aborting the current $op?"] eq {yes}} { + set fd [open "| git read-tree --reset -u HEAD" r] + fconfigure $fd -blocking 0 -translation binary + fileevent $fd readable [list reset_hard_wait $fd] + set ui_status_value {Aborting... please wait...} + } else { + unlock_index + } +} + +proc reset_hard_wait {fd} { + global ui_comm + + read $fd + if {[eof $fd]} { + close $fd + unlock_index + + $ui_comm delete 0.0 end + $ui_comm edit modified false + + catch {file delete [gitdir MERGE_HEAD]} + catch {file delete [gitdir rr-cache MERGE_RR]} + catch {file delete [gitdir SQUASH_MSG]} + catch {file delete [gitdir MERGE_MSG]} + catch {file delete [gitdir GITGUI_MSG]} + + rescan {set ui_status_value {Abort completed. Ready.}} + } +} + ###################################################################### ## ## icons @@ -4322,6 +4377,12 @@ if {!$single_commit} { -font font_ui lappend disable_on_lock \ [list .mbar.merge entryconf [.mbar.merge index last] -state] + .mbar.merge add command -label {Abort Merge...} \ + -command do_reset_hard \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.merge entryconf [.mbar.merge index last] -state] + menu .mbar.fetch -- cgit v1.2.3 From ee3cfb595415e2f076d5b90efba83088e5af961c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 03:58:56 -0500 Subject: git-gui: Update status bar during a merge. I got slightly confused when I did two merges in a row, as the status bar said "merge completed successfully" while the second merge was still running. Now we show what branches are actively being merged. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 0c2dbbebe1..6675d3018b 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2658,7 +2658,7 @@ proc visualize_local_merge {w} { } proc start_local_merge_action {w} { - global HEAD + global HEAD ui_status_value current_branch set cmd [list git merge] set names {} @@ -2694,7 +2694,9 @@ than 15 branches, merge the branches in batches. return } - set cons [new_console "Merge" "Merging [join $names {, }]"] + set msg "Merging $current_branch, [join $names {, }]" + set ui_status_value "$msg..." + set cons [new_console "Merge" $msg] console_exec $cons $cmd finish_merge bind $w {} destroy $w -- cgit v1.2.3 From dff7e88febf85b1b8b1b789cd7c99434b70fb19b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 04:07:34 -0500 Subject: git-gui: Don't allow users to commit a bad octopus merge. If an octopus merge goes horribly wrong git-merge will leave the working directory and index dirty, but will not leave behind a MERGE_HEAD file for a later commit. Consequently we won't know its a merge commit and instead would let the user resolve the conflicts and commit a single-parent commit, which is wrong. So now if an octopus merge fails we notify the user that the merge did not work, tell them we will reset the working directory, and suggest that they merge one branch at a time. This prevents the user from committing a bad octopus merge. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 6675d3018b..31ef0e60ac 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -187,13 +187,13 @@ proc warn_popup {msg} { eval $cmd } -proc info_popup {msg} { +proc info_popup {msg {parent .}} { set title [appname] if {[reponame] ne {}} { append title " ([reponame])" } tk_messageBox \ - -parent . \ + -parent $parent \ -icon info \ -type ok \ -title $title \ @@ -2697,16 +2697,36 @@ than 15 branches, merge the branches in batches. set msg "Merging $current_branch, [join $names {, }]" set ui_status_value "$msg..." set cons [new_console "Merge" $msg] - console_exec $cons $cmd finish_merge + console_exec $cons $cmd [list finish_merge $revcnt] bind $w {} destroy $w } -proc finish_merge {w ok} { +proc finish_merge {revcnt w ok} { console_done $w $ok if {$ok} { set msg {Merge completed successfully.} } else { + if {$revcnt != 1} { + info_popup "Octopus merge failed. + +Your merge of $revcnt branches has failed. + +There are file-level conflicts between the +branches which must be resolved manually. + +The working directory will now be reset. + +You can attempt this merge again +by merging only one branch at a time." $w + + set fd [open "| git read-tree --reset -u HEAD" r] + fconfigure $fd -blocking 0 -translation binary + fileevent $fd readable [list reset_hard_wait $fd] + set ui_status_value {Aborting... please wait...} + return + } + set msg {Merge failed. Conflict resolution is required.} } unlock_index -- cgit v1.2.3 From 5f39dbf64f5e57c6ab7b20ba8c397df1074bc30c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 04:11:10 -0500 Subject: git-gui: Don't allow merges in the middle of other things. If the user is in the middle of a commit they have files which are modified. These may conflict with any merge that they may want to perform, which would cause problems if the user wants to abort a bad merge as we wouldn't have a checkpoint to roll back onto. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 31ef0e60ac..ae883f90bd 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2629,6 +2629,9 @@ The rescan will be automatically started now. foreach path [array names file_states] { switch -glob -- [lindex $file_states($path) 0] { + _O { + continue; # and pray it works! + } U? { error_popup "You are in the middle of a conflicted merge. @@ -2637,6 +2640,18 @@ File [short_path $path] has merge conflicts. You must resolve them, add the file, and commit to complete the current merge. Only then can you begin another merge. +" + unlock_index + return 0 + } + ?? { + error_popup "You are in the middle of a change. + +File [short_path $path] is modified. + +You should complete the current commit before +starting a merge. Doing so will help you abort +a failed merge, should the need arise. " unlock_index return 0 -- cgit v1.2.3 From 729a6f60ddd0b3317c581a01687a32e585fa0c3e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 04:16:39 -0500 Subject: git-gui: Always offer scrollbars for branch lists. Anytime we use a listbox to show branch names its possible for the listbox to exceed 10 entries (actually its probably very common). So we should always offer a scrollbar for the Y axis on these listboxes. I just forgot to add it when I defined them. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ae883f90bd..68fd7ebf18 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2129,16 +2129,19 @@ proc do_delete_branch {} { -font font_ui listbox $w.list.l \ -height 10 \ - -width 50 \ + -width 70 \ -selectmode extended \ + -yscrollcommand [list $w.list.sby set] \ -font font_ui foreach h $all_heads { if {$h ne $current_branch} { $w.list.l insert end $h } } - pack $w.list.l -fill both -pady 5 -padx 5 - pack $w.list -fill both -pady 5 -padx 5 + scrollbar $w.list.sby -command [list $w.list.l yview] + pack $w.list.sby -side right -fill y + pack $w.list.l -side left -fill both -expand 1 + pack $w.list -fill both -expand 1 -pady 5 -padx 5 labelframe $w.validate \ -text {Delete Only If} \ @@ -2510,8 +2513,9 @@ proc do_push_anywhere {} { -font font_ui listbox $w.source.l \ -height 10 \ - -width 50 \ + -width 70 \ -selectmode extended \ + -yscrollcommand [list $w.source.sby set] \ -font font_ui foreach h $all_heads { $w.source.l insert end $h @@ -2519,8 +2523,10 @@ proc do_push_anywhere {} { $w.source.l select set end } } - pack $w.source.l -fill both -pady 5 -padx 5 - pack $w.source -fill both -pady 5 -padx 5 + scrollbar $w.source.sby -command [list $w.source.l yview] + pack $w.source.sby -side right -fill y + pack $w.source.l -side left -fill both -expand 1 + pack $w.source -fill both -expand 1 -pady 5 -padx 5 labelframe $w.dest \ -text {Destination Repository} \ @@ -2782,7 +2788,7 @@ proc do_local_merge {} { -font font_ui listbox $w.source.l \ -height 10 \ - -width 25 \ + -width 70 \ -selectmode extended \ -yscrollcommand [list $w.source.sby set] \ -font font_ui -- cgit v1.2.3 From c539449b2df0381de1d34c70b093b68370e89a4e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 26 Jan 2007 04:43:43 -0500 Subject: git-gui: Support merge.summary, merge.verbosity. Changed our private merge summary config option to be the same as the merge.summary option supported by core Git. This means setting the "Show Merge Summary" flag in git-gui will have the same effect on the command line. In the same vein I've also added merge.verbosity to the gui options, allowing the user to adjust the verbosity level of the recursive merge strategy. I happen to like level 1 and suggest that other users use that, but level 2 is the core Git default right now so we'll use the same default in git-gui. Unfortunately it appears as though core Git has broken support for the merge.summary option, even though its still in the documentation For the time being we should pass along --no-summary to git-merge if merge.summary is false. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 77 ++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 68fd7ebf18..1ba7f5a293 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -60,6 +60,17 @@ proc is_many_config {name} { } } +proc is_config_true {name} { + global repo_config + if {[catch {set v $repo_config($name)}]} { + return 0 + } elseif {$v eq {true} || $v eq {1} || $v eq {yes}} { + return 1 + } else { + return 0 + } +} + proc load_config {include_global} { global repo_config global_config default_config @@ -2682,6 +2693,10 @@ proc start_local_merge_action {w} { global HEAD ui_status_value current_branch set cmd [list git merge] + if {![is_config_true merge.summary]} { + lappend cmd --no-summary + } + set names {} set revcnt 0 foreach i [$w.source.l curselection] { @@ -3755,52 +3770,59 @@ proc do_options {} { pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 + set optid 0 foreach option { - {b pullsummary {Show Pull Summary}} - {b trustmtime {Trust File Modification Timestamps}} - {i diffcontext {Number of Diff Context Lines}} - {t newbranchtemplate {New Branch Name Template}} + {b merge.summary {Show Merge Summary}} + {i-1..5 merge.verbosity {Merge Verbosity}} + + {b gui.trustmtime {Trust File Modification Timestamps}} + {i-1..99 gui.diffcontext {Number of Diff Context Lines}} + {t gui.newbranchtemplate {New Branch Name Template}} } { set type [lindex $option 0] set name [lindex $option 1] set text [lindex $option 2] + incr optid foreach f {repo global} { - switch $type { + switch -glob -- $type { b { - checkbutton $w.$f.$name -text $text \ - -variable ${f}_config_new(gui.$name) \ + checkbutton $w.$f.$optid -text $text \ + -variable ${f}_config_new($name) \ -onvalue true \ -offvalue false \ -font font_ui - pack $w.$f.$name -side top -anchor w + pack $w.$f.$optid -side top -anchor w } - i { - frame $w.$f.$name - label $w.$f.$name.l -text "$text:" -font font_ui - pack $w.$f.$name.l -side left -anchor w -fill x - spinbox $w.$f.$name.v \ - -textvariable ${f}_config_new(gui.$name) \ - -from 1 -to 99 -increment 1 \ - -width 3 \ + i-* { + regexp -- {-(\d+)\.\.(\d+)$} $type _junk min max + frame $w.$f.$optid + label $w.$f.$optid.l -text "$text:" -font font_ui + pack $w.$f.$optid.l -side left -anchor w -fill x + spinbox $w.$f.$optid.v \ + -textvariable ${f}_config_new($name) \ + -from $min \ + -to $max \ + -increment 1 \ + -width [expr {1 + [string length $max]}] \ -font font_ui - bind $w.$f.$name.v {%W selection range 0 end} - pack $w.$f.$name.v -side right -anchor e -padx 5 - pack $w.$f.$name -side top -anchor w -fill x + bind $w.$f.$optid.v {%W selection range 0 end} + pack $w.$f.$optid.v -side right -anchor e -padx 5 + pack $w.$f.$optid -side top -anchor w -fill x } t { - frame $w.$f.$name - label $w.$f.$name.l -text "$text:" -font font_ui - entry $w.$f.$name.v \ + frame $w.$f.$optid + label $w.$f.$optid.l -text "$text:" -font font_ui + entry $w.$f.$optid.v \ -borderwidth 1 \ -relief sunken \ -width 20 \ - -textvariable ${f}_config_new(gui.$name) \ + -textvariable ${f}_config_new($name) \ -font font_ui - pack $w.$f.$name.l -side left -anchor w - pack $w.$f.$name.v -side left -anchor w \ + pack $w.$f.$optid.l -side left -anchor w + pack $w.$f.$optid.v -side left -anchor w \ -fill x -expand 1 \ -padx 5 - pack $w.$f.$name -side top -anchor w -fill x + pack $w.$f.$optid -side top -anchor w -fill x } } } @@ -4131,8 +4153,9 @@ proc apply_config {} { } } +set default_config(merge.summary) true +set default_config(merge.verbosity) 2 set default_config(gui.trustmtime) false -set default_config(gui.pullsummary) true set default_config(gui.diffcontext) 5 set default_config(gui.newbranchtemplate) {} set default_config(gui.fontui) [font configure font_ui] -- cgit v1.2.3 From 6b90d39186dbfabfff94692d33d0a6e94f02016c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 27 Jan 2007 02:31:01 -0500 Subject: git-gui: Reword meaning of merge.summary. OK, its official, I'm not reading documentation as well as I should be. Core Git's merge.summary configuration option is used to control the generation of the text appearing within the merge commit itself. It is not (and never has been) used to default the --no-summary command line option, which disables the diffstat at the end of the merge. I completely blame Git for naming two unrelated options almost the exact same thing. But its my own fault for allowing git-gui to confuse the two. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 1ba7f5a293..be92fa9626 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2693,10 +2693,6 @@ proc start_local_merge_action {w} { global HEAD ui_status_value current_branch set cmd [list git merge] - if {![is_config_true merge.summary]} { - lappend cmd --no-summary - } - set names {} set revcnt 0 foreach i [$w.source.l curselection] { @@ -3772,7 +3768,7 @@ proc do_options {} { set optid 0 foreach option { - {b merge.summary {Show Merge Summary}} + {b merge.summary {Summarize Merge Commits}} {i-1..5 merge.verbosity {Merge Verbosity}} {b gui.trustmtime {Trust File Modification Timestamps}} @@ -4153,7 +4149,7 @@ proc apply_config {} { } } -set default_config(merge.summary) true +set default_config(merge.summary) false set default_config(merge.verbosity) 2 set default_config(gui.trustmtime) false set default_config(gui.diffcontext) 5 -- cgit v1.2.3 From 273984fc4f26ab92aa8ebf5b033b9cdb56322b71 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 28 Jan 2007 20:00:36 -0500 Subject: git-gui: Offer quick access to the HTML formatted documentation. Users may want to be able to read Git documentation, even if they are not command line users. There are many important concepts and terms covered within the standard Git documentation which would be useful to even non command line using people. We now try to offer an 'Online Documentation' menu option within the Help menu. First we try to guess to see what browser the user has setup. We default to instaweb.browser, if set, as this is probably accurate for the user's configuration. If not then we try to guess based on the operating system and the available browsers for each. We prefer documentation which is installed parallel to Git's own executables, e.g. `git --exec-path`/../Documentation/index.html, as that is how I typically install the HTML docs. If those are not found then we open the documentation published on kernel.org. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index be92fa9626..c168826ecc 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -4401,17 +4401,57 @@ if {[is_MacOSX]} { lappend disable_on_lock \ [list .mbar.tools entryconf [.mbar.tools index last] -state] } +} - # -- Help Menu - # - .mbar add cascade -label Help -menu .mbar.help - menu .mbar.help +# -- Help Menu +# +.mbar add cascade -label Help -menu .mbar.help +menu .mbar.help +if {![is_MacOSX]} { .mbar.help add command -label "About [appname]" \ -command do_about \ -font font_ui } +set browser {} +catch {set browser $repo_config(instaweb.browser)} +set doc_path [file dirname [exec git --exec-path]] +set doc_path [file join $doc_path Documentation index.html] + +if {[is_Windows]} { + set doc_path [exec cygpath --windows $doc_path] +} + +if {$browser eq {}} { + if {[is_MacOSX]} { + set browser open + } elseif {[is_Windows]} { + set program_files [file dirname [exec cygpath --windir]] + set program_files [file join $program_files {Program Files}] + set firefox [file join $program_files {Mozilla Firefox} firefox.exe] + set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE] + if {[file exists $firefox]} { + set browser $firefox + } elseif {[file exists $ie]} { + set browser $ie + } + unset program_files firefox ie + } +} + +if {[file isfile $doc_path]} { + set doc_url "file:$doc_path" +} else { + set doc_url {http://www.kernel.org/pub/software/scm/git/docs/} +} + +if {$browser ne {}} { + .mbar.help add command -label {Online Documentation} \ + -command [list exec $browser $doc_url &] \ + -font font_ui +} +unset browser doc_path doc_url # -- Branch Control # -- cgit v1.2.3 From 20ddfcaa7e7f91816f1195a2dbb1b5268fd71e3d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 28 Jan 2007 20:58:47 -0500 Subject: git-gui: Test for Cygwin differently than from Windows. Running on Cygwin is different than if we were running through MinGW. In the Cygwin case we have cygpath available to us, we need to perform UNIX<->Windows path translation sometimes, and we need to perform odd things like spawning our own login shells to perform network operations. But in the MinGW case these don't occur. Git knows native Windows file paths, and login shells may not even exist. Now git-gui will avoid running cygpath unless it knows its on Cygwin. It also uses a different shortcut type when Cygwin is not present, and it avoids invoking /bin/sh to execute hooks if Cygwin is not present. This latter part probably needs more testing in the MinGW case. This change also improves how we start gitk. If the user is on any type of Windows system its known that gitk won't start right if ~/.gitk exists. So we delete it before starting if we are running on any type of Windows operating system. We always use the same wish executable which launched git-gui to start gitk; this way on Windows we don't have to jump back to /bin/sh just to go into the first wish found in the user's PATH. This should help on MinGW when we probably don't want to spawn a shell just to start gitk. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 138 ++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 104 insertions(+), 34 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index c168826ecc..46e019becd 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -26,7 +26,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA} set _appname [lindex [file split $argv0] end] set _gitdir {} +set _gitexec {} set _reponame {} +set _iscygwin {} proc appname {} { global _appname @@ -41,11 +43,56 @@ proc gitdir {args} { return [eval [concat [list file join $_gitdir] $args]] } +proc gitexec {args} { + global _gitexec + if {$_gitexec eq {}} { + if {[catch {set _gitexec [exec git --exec-path]} err]} { + error "Git not installed?\n\n$err" + } + } + if {$args eq {}} { + return $_gitexec + } + return [eval [concat [list file join $_gitexec] $args]] +} + proc reponame {} { global _reponame return $_reponame } +proc is_MacOSX {} { + global tcl_platform tk_library + if {[tk windowingsystem] eq {aqua}} { + return 1 + } + return 0 +} + +proc is_Windows {} { + global tcl_platform + if {$tcl_platform(platform) eq {windows}} { + return 1 + } + return 0 +} + +proc is_Cygwin {} { + global tcl_platform _iscygwin + if {$_iscygwin eq {}} { + if {$tcl_platform(platform) eq {windows}} { + if {[catch {set p [exec cygpath --windir]} err]} { + set _iscygwin 0 + } else { + set _iscygwin 1 + } + } else { + set _iscygwin 0 + } + } + return $_iscygwin +} + ###################################################################### ## ## config @@ -234,6 +281,9 @@ if { [catch {set _gitdir $env(GIT_DIR)}] error_popup "Cannot find the git directory:\n\n$err" exit 1 } +if {![file isdirectory $_gitdir] && [is_Cygwin]} { + catch {set _gitdir [exec cygpath --unix $_gitdir]} +} if {![file isdirectory $_gitdir]} { catch {wm withdraw .} error_popup "Git directory not found:\n\n$_gitdir" @@ -241,7 +291,7 @@ if {![file isdirectory $_gitdir]} { } if {[lindex [file split $_gitdir] end] ne {.git}} { catch {wm withdraw .} - error_popup "Cannot use funny .git directory:\n\n$gitdir" + error_popup "Cannot use funny .git directory:\n\n$_gitdir" exit 1 } if {[catch {cd [file dirname $_gitdir]} err]} { @@ -1076,7 +1126,7 @@ A good commit message has the following format: # On Cygwin [file executable] might lie so we need to ask # the shell if the hook is executable. Yes that's annoying. # - if {[is_Windows] && [file isfile $pchook]} { + if {[is_Cygwin] && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ "then exec \"$pchook\" 2>&1;" \ @@ -1215,7 +1265,7 @@ proc commit_committree {fd_wt curHEAD msg} { # -- Run the post-commit hook. # set pchook [gitdir hooks post-commit] - if {[is_Windows] && [file isfile $pchook]} { + if {[is_Cygwin] && [file isfile $pchook]} { set pchook [list sh -c [concat \ "if test -x \"$pchook\";" \ "then exec \"$pchook\";" \ @@ -3020,22 +3070,6 @@ unset i ## ## util -proc is_MacOSX {} { - global tcl_platform tk_library - if {[tk windowingsystem] eq {aqua}} { - return 1 - } - return 0 -} - -proc is_Windows {} { - global tcl_platform - if {$tcl_platform(platform) eq {windows}} { - return 1 - } - return 0 -} - proc bind_button3 {w cmd} { bind $w $cmd if {[is_MacOSX]} { @@ -3159,10 +3193,10 @@ proc console_init {w} { } proc console_exec {w cmd after} { - # -- Windows tosses the enviroment when we exec our child. + # -- Cygwin's Tcl tosses the enviroment when we exec our child. # But most users need that so we have to relogin. :-( # - if {[is_Windows]} { + if {[is_Cygwin]} { set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"] } @@ -3284,19 +3318,27 @@ proc console_done {args} { set starting_gitk_msg {Starting gitk... please wait...} proc do_gitk {revs} { - global ui_status_value starting_gitk_msg + global env ui_status_value starting_gitk_msg + + # -- On Windows gitk is severly broken, and right now it seems like + # nobody cares about fixing it. The only known workaround is to + # always delete ~/.gitk before starting the program. + # + if {[is_Windows]} { + catch {file delete [file join $env(HOME) .gitk]} + } - set cmd gitk + # -- Always start gitk through whatever we were loaded with. This + # lets us bypass using shell process on Windows systems. + # + set cmd [info nameofexecutable] + lappend cmd [gitexec gitk] if {$revs ne {}} { append cmd { } append cmd $revs } - if {[is_Windows]} { - set cmd "sh -c \"exec $cmd\"" - } - append cmd { &} - if {[catch {eval exec $cmd} err]} { + if {[catch {eval exec $cmd &} err]} { error_popup "Failed to start gitk:\n\n$err" } else { set ui_status_value $starting_gitk_msg @@ -3894,6 +3936,29 @@ proc do_save_config {w} { proc do_windows_shortcut {} { global argv0 + set fn [tk_getSaveFile \ + -parent . \ + -title "[appname] ([reponame]): Create Desktop Icon" \ + -initialfile "Git [reponame].bat"] + if {$fn != {}} { + if {[catch { + set fd [open $fn w] + puts $fd "@ECHO Entering [reponame]" + puts $fd "@ECHO Starting git-gui... please wait..." + puts $fd "@SET PATH=[file normalize [gitexec]];%PATH%" + puts $fd "@SET GIT_DIR=[file normalize [gitdir]]" + puts -nonewline $fd "@\"[info nameofexecutable]\"" + puts $fd " \"[file normalize $argv0]\"" + close $fd + } err]} { + error_popup "Cannot write script:\n\n$err" + } + } +} + +proc do_cygwin_shortcut {} { + global argv0 + if {[catch { set desktop [exec cygpath \ --windows \ @@ -3985,7 +4050,7 @@ proc do_macosx_app {} { set fd [open $exe w] set gd [file normalize [gitdir]] - set ep [file normalize [exec git --exec-path]] + set ep [file normalize [gitexec]] regsub -all ' $gd "'\\''" gd regsub -all ' $ep "'\\''" ep puts $fd "#!/bin/sh" @@ -4211,7 +4276,12 @@ if {!$single_commit} { .mbar.repository add separator - if {[is_Windows]} { + if {[is_Cygwin]} { + .mbar.repository add command \ + -label {Create Desktop Icon} \ + -command do_cygwin_shortcut \ + -font font_ui + } elseif {[is_Windows]} { .mbar.repository add command \ -label {Create Desktop Icon} \ -command do_windows_shortcut \ @@ -4416,17 +4486,17 @@ if {![is_MacOSX]} { set browser {} catch {set browser $repo_config(instaweb.browser)} -set doc_path [file dirname [exec git --exec-path]] +set doc_path [file dirname [gitexec]] set doc_path [file join $doc_path Documentation index.html] -if {[is_Windows]} { +if {[is_Cygwin]} { set doc_path [exec cygpath --windows $doc_path] } if {$browser eq {}} { if {[is_MacOSX]} { set browser open - } elseif {[is_Windows]} { + } elseif {[is_Cygwin]} { set program_files [file dirname [exec cygpath --windir]] set program_files [file join $program_files {Program Files}] set firefox [file join $program_files {Mozilla Firefox} firefox.exe] @@ -4988,7 +5058,7 @@ focus -force $ui_comm # does *not* pass its env array onto any processes it spawns. # This means that git processes get none of our environment. # -if {[is_Windows]} { +if {[is_Cygwin]} { set ignored_env 0 set suggest_user {} set msg "Possible environment issues exist. -- cgit v1.2.3 From 35874c163e2f5c1def3cc5c3465beceea0355b8f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 00:50:41 -0500 Subject: git-gui: Implemented file browser and incremental blame. This rather huge change provides a browser for the current branch. The browser simply shows the contents of tree HEAD, and lets the user drill down through the tree. The icons used really stink, as I just copied in icon which we already had. I really need to replace the file_dir and file_uplevel icons with something more useful. If the user double clicks on a file within the browser we open it in a blame viewer. This makes use of the new incremental blame feature that Linus just added yesterday to core Git. Fortunately the feature will be in 1.5.0 final so we can rely on having it available here. Since the blame engine is incremental the user will get blame data for groups which can be determined early. Git will slowly fill in the remaining lines as it goes. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 459 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 46e019becd..0192f80dd7 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2945,6 +2945,431 @@ proc reset_hard_wait {fd} { } } +###################################################################### +## +## browser + +set next_browser_id 0 + +proc new_browser {} { + global next_browser_id cursor_ptr + global browser_commit browser_status browser_stack browser_path browser_busy + + set w .browser[incr next_browser_id] + set w_list $w.list.l + set browser_commit($w_list) HEAD + set browser_status($w_list) {Starting...} + set browser_stack($w_list) {} + set browser_path($w_list) $browser_commit($w_list): + set browser_busy($w_list) 1 + + toplevel $w + label $w.path -textvariable browser_path($w_list) \ + -anchor w \ + -justify left \ + -borderwidth 1 \ + -relief sunken \ + -font font_uibold + pack $w.path -anchor w -side top -fill x + + frame $w.list + text $w_list -background white -borderwidth 0 \ + -cursor $cursor_ptr \ + -state disabled \ + -wrap none \ + -height 20 \ + -width 70 \ + -xscrollcommand [list $w.list.sbx set] \ + -yscrollcommand [list $w.list.sby set] \ + -font font_ui + $w_list tag conf in_sel \ + -background [$w_list cget -foreground] \ + -foreground [$w_list cget -background] + scrollbar $w.list.sbx -orient h -command [list $w_list xview] + scrollbar $w.list.sby -orient v -command [list $w_list yview] + pack $w.list.sbx -side bottom -fill x + pack $w.list.sby -side right -fill y + pack $w_list -side left -fill both -expand 1 + pack $w.list -side top -fill both -expand 1 + + label $w.status -textvariable browser_status($w_list) \ + -anchor w \ + -justify left \ + -borderwidth 1 \ + -relief sunken \ + -font font_ui + pack $w.status -anchor w -side bottom -fill x + + bind $w_list "browser_click 0 $w_list @%x,%y;break" + bind $w_list "browser_click 1 $w_list @%x,%y;break" + + bind $w "focus $w" + bind $w " + array unset browser_buffer $w_list + array unset browser_files $w_list + array unset browser_status $w_list + array unset browser_stack $w_list + array unset browser_path $w_list + array unset browser_commit $w_list + array unset browser_busy $w_list + " + wm title $w "[appname] ([reponame]): File Browser" + ls_tree $w_list $browser_commit($w_list) {} +} + +proc browser_click {was_double_click w pos} { + global browser_files browser_status browser_path + global browser_commit browser_stack browser_busy + + if {$browser_busy($w)} return + set lno [lindex [split [$w index $pos] .] 0] + set info [lindex $browser_files($w) [expr {$lno - 1}]] + + $w conf -state normal + $w tag remove sel 0.0 end + $w tag remove in_sel 0.0 end + if {$info ne {}} { + $w tag add in_sel $lno.0 [expr {$lno + 1}].0 + if {$was_double_click} { + switch -- [lindex $info 0] { + parent { + set parent [lindex $browser_stack($w) end-1] + set browser_stack($w) [lrange $browser_stack($w) 0 end-2] + if {$browser_stack($w) eq {}} { + regsub {:.*$} $browser_path($w) {:} browser_path($w) + } else { + regsub {/[^/]+$} $browser_path($w) {} browser_path($w) + } + set browser_status($w) "Loading $browser_path($w)..." + ls_tree $w [lindex $parent 0] [lindex $parent 1] + } + tree { + set name [lindex $info 2] + set escn [escape_path $name] + set browser_status($w) "Loading $escn..." + append browser_path($w) $escn + ls_tree $w [lindex $info 1] $name + } + blob { + set name [lindex $info 2] + set p {} + foreach n $browser_stack($w) { + append p [lindex $n 1] + } + append p $name + show_blame $browser_commit($w) $p + } + } + } + } + $w conf -state disabled +} + +proc ls_tree {w tree_id name} { + global browser_buffer browser_files browser_stack browser_busy + + set browser_buffer($w) {} + set browser_files($w) {} + set browser_busy($w) 1 + + $w conf -state normal + $w tag remove in_sel 0.0 end + $w tag remove sel 0.0 end + $w delete 0.0 end + if {$browser_stack($w) ne {}} { + $w image create end \ + -align center -padx 5 -pady 1 \ + -name icon0 \ + -image file_uplevel + $w insert end {[Up To Parent]} + lappend browser_files($w) parent + } + lappend browser_stack($w) [list $tree_id $name] + $w conf -state disabled + + set fd [open "| git ls-tree -z $tree_id" r] + fconfigure $fd -blocking 0 -translation binary -encoding binary + fileevent $fd readable [list read_ls_tree $fd $w] +} + +proc read_ls_tree {fd w} { + global browser_buffer browser_files browser_status browser_busy + + if {![winfo exists $w]} { + catch {close $fd} + return + } + + append browser_buffer($w) [read $fd] + set pck [split $browser_buffer($w) "\0"] + set browser_buffer($w) [lindex $pck end] + + set n [llength $browser_files($w)] + $w conf -state normal + foreach p [lrange $pck 0 end-1] { + set info [split $p "\t"] + set path [lindex $info 1] + set info [split [lindex $info 0] { }] + set type [lindex $info 1] + set object [lindex $info 2] + + switch -- $type { + blob { + set image file_plain + } + tree { + set image file_dir + append path / + } + default { + set image file_question + } + } + + if {$n > 0} {$w insert end "\n"} + $w image create end \ + -align center -padx 5 -pady 1 \ + -name icon[incr n] \ + -image $image + $w insert end [escape_path $path] + lappend browser_files($w) [list $type $object $path] + } + $w conf -state disabled + + if {[eof $fd]} { + close $fd + set browser_status($w) Ready. + set browser_busy($w) 0 + array unset browser_buffer $w + } +} + +proc show_blame {commit path} { + global next_browser_id blame_status blame_data + + set w .browser[incr next_browser_id] + set blame_status($w) {Loading current file content...} + set texts [list] + + toplevel $w + panedwindow $w.out -orient horizontal + + label $w.path -text "$commit:$path" \ + -anchor w \ + -justify left \ + -borderwidth 1 \ + -relief sunken \ + -font font_uibold + pack $w.path -anchor w -side top -fill x + + text $w.out.commit -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 40 \ + -width 8 \ + -font font_diff + $w.out add $w.out.commit + lappend texts $w.out.commit + + text $w.out.author -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 40 \ + -width 20 \ + -font font_diff + $w.out add $w.out.author + lappend texts $w.out.author + + text $w.out.date -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 40 \ + -width [string length "yyyy-mm-dd hh:mm:ss"] \ + -font font_diff + $w.out add $w.out.date + lappend texts $w.out.date + + text $w.out.linenumber -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 40 \ + -width 5 \ + -font font_diff + $w.out.linenumber tag conf linenumber -justify right + $w.out add $w.out.linenumber + lappend texts $w.out.linenumber + + text $w.out.file -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 40 \ + -width 80 \ + -font font_diff + $w.out add $w.out.file + lappend texts $w.out.file + + label $w.status -textvariable blame_status($w) \ + -anchor w \ + -justify left \ + -borderwidth 1 \ + -relief sunken \ + -font font_ui + pack $w.status -anchor w -side bottom -fill x + + scrollbar $w.sby -orient v -command [list scrollbar2many $texts yview] + pack $w.sby -side right -fill y + pack $w.out -side left -fill both -expand 1 + + menu $w.ctxm -tearoff 0 + $w.ctxm add command -label "Copy Commit" \ + -font font_ui \ + -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY" + + foreach i $texts { + $i tag conf in_sel \ + -background [$i cget -foreground] \ + -foreground [$i cget -background] + $i conf -yscrollcommand [list many2scrollbar $texts yview $w.sby] + bind $i "blame_highlight $i @%x,%y $texts;break" + bind_button3 $i " + set cursorX %x + set cursorY %y + set cursorW %W + tk_popup $w.ctxm %X %Y + " + } + + bind $w "focus $w" + bind $w " + array unset blame_status $w + array unset blame_data $w,* + " + wm title $w "[appname] ([reponame]): File Viewer" + + set blame_data($w,total_lines) 0 + set fd [open "| git cat-file blob $commit:$path" r] + fconfigure $fd -blocking 0 -translation lf -encoding binary + fileevent $fd readable [list read_blame_catfile $fd $w $commit $path \ + $texts $w.out.linenumber $w.out.file] +} + +proc read_blame_catfile {fd w commit path texts w_lno w_file} { + global blame_status blame_data + + if {![winfo exists $w_file]} { + catch {close $fd} + return + } + + set n $blame_data($w,total_lines) + foreach i $texts {$i conf -state normal} + while {[gets $fd line] >= 0} { + regsub "\r\$" $line {} line + incr n + $w_lno insert end $n linenumber + $w_file insert end $line + foreach i $texts {$i insert end "\n"} + } + foreach i $texts {$i conf -state disabled} + set blame_data($w,total_lines) $n + + if {[eof $fd]} { + close $fd + set blame_status($w) {Loading annotations...} + set fd [open "| git blame --incremental $commit -- $path" r] + fconfigure $fd -blocking 0 -translation lf -encoding binary + fileevent $fd readable "read_blame_incremental $fd $w $texts" + } +} + +proc read_blame_incremental {fd w w_commit w_author w_date w_lno w_file} { + global blame_status blame_data + + if {![winfo exists $w_commit]} { + catch {close $fd} + return + } + + $w_commit conf -state normal + $w_author conf -state normal + $w_date conf -state normal + + while {[gets $fd line] >= 0} { + if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \ + commit original_line final_line line_count]} { + set blame_data($w,commit) $commit + set blame_data($w,original_line) $original_line + set blame_data($w,final_line) $final_line + set blame_data($w,line_count) $line_count + } elseif {[string match {filename *} $line]} { + set n $blame_data($w,line_count) + set lno $blame_data($w,final_line) + set file [string range $line 9 end] + set commit $blame_data($w,commit) + set abbrev [string range $commit 0 8] + + if {[catch {set author $blame_data($w,$commit,author)} err]} { + puts $err + set author {} + } + + if {[catch {set atime $blame_data($w,$commit,author-time)}]} { + set atime {} + } else { + set atime [clock format $atime -format {%Y-%m-%d %T}] + } + + while {$n > 0} { + $w_commit delete $lno.0 "$lno.0 lineend" + $w_author delete $lno.0 "$lno.0 lineend" + $w_date delete $lno.0 "$lno.0 lineend" + + $w_commit insert $lno.0 $abbrev + $w_author insert $lno.0 $author + $w_date insert $lno.0 $atime + set blame_data($w,line$lno,commit) $commit + + incr n -1 + incr lno + } + } elseif {[regexp {^([a-z-]+) (.*)$} $line line header data]} { + set blame_data($w,$blame_data($w,commit),$header) $data + } + } + + $w_commit conf -state disabled + $w_author conf -state disabled + $w_date conf -state disabled + + if {[eof $fd]} { + close $fd + set blame_status($w) {Annotation complete.} + } +} + +proc blame_highlight {w pos args} { + set lno [lindex [split [$w index $pos] .] 0] + foreach i $args { + $i tag remove in_sel 0.0 end + } + if {$lno eq {}} return + foreach i $args { + $i tag add in_sel $lno.0 "$lno.0 + 1 line" + } +} + +proc blame_copycommit {w i pos} { + global blame_data + set lno [lindex [split [$i index $pos] .] 0] + if {![catch {set commit $blame_data($w,line$lno,commit)}]} { + clipboard clear + clipboard append \ + -format STRING \ + -type STRING \ + -- $commit + } +} + ###################################################################### ## ## icons @@ -3021,6 +3446,24 @@ static unsigned char file_merge_bits[] = { 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; } -maskdata $filemask +image create bitmap file_dir -background white -foreground blue -data { +#define mod_width 14 +#define mod_height 15 +static unsigned char mod_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + +image create bitmap file_uplevel -background white -foreground blue -data { +#define mod_width 14 +#define mod_height 15 +static unsigned char mod_bits[] = { + 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, + 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; +} -maskdata $filemask + set ui_index .vpane.files.index.list set ui_workdir .vpane.files.workdir.list @@ -3077,6 +3520,15 @@ proc bind_button3 {w cmd} { } } +proc scrollbar2many {list mode args} { + foreach w $list {eval $w $mode $args} +} + +proc many2scrollbar {list mode sb top bottom} { + $sb set $top $bottom + foreach w $list {$w $mode moveto $top} +} + proc incr_font_size {font {amt 1}} { set sz [font configure $font -size] incr sz $amt @@ -4251,6 +4703,13 @@ if {!$single_commit} { # -- Repository Menu # menu .mbar.repository + +.mbar.repository add command \ + -label {Browse Current Branch} \ + -command new_browser \ + -font font_ui +.mbar.repository add separator + .mbar.repository add command \ -label {Visualize Current Branch} \ -command {do_gitk {}} \ -- cgit v1.2.3 From 3eddda98435f67ffb0afc8baf4bfb51ed8160f2c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 02:50:10 -0500 Subject: git-gui: Improve the icons used in the browser display. Real icons which seem to indicate going up to the parent (an up arrow) and a subdirectory (an open folder). Files are now drawn with the file_mod icon, like a modified file is. This just looks better as it is more consistent with the rest of our UI. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 0192f80dd7..9892ec32ae 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3115,7 +3115,7 @@ proc read_ls_tree {fd w} { switch -- $type { blob { - set image file_plain + set image file_mod } tree { set image file_dir @@ -3446,23 +3446,31 @@ static unsigned char file_merge_bits[] = { 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; } -maskdata $filemask -image create bitmap file_dir -background white -foreground blue -data { -#define mod_width 14 -#define mod_height 15 -static unsigned char mod_bits[] = { - 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, - 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, - 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask - -image create bitmap file_uplevel -background white -foreground blue -data { -#define mod_width 14 -#define mod_height 15 -static unsigned char mod_bits[] = { - 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10, - 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, - 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; -} -maskdata $filemask +set file_dir_data { +#define file_width 18 +#define file_height 18 +static unsigned char file_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00, + 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00, + 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00, + 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +} +image create bitmap file_dir -background white -foreground blue \ + -data $file_dir_data -maskdata $file_dir_data +unset file_dir_data + +set file_uplevel_data { +#define up_width 15 +#define up_height 15 +static unsigned char up_bits[] = { + 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f, + 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00}; +} +image create bitmap file_uplevel -background white -foreground red \ + -data $file_uplevel_data -maskdata $file_uplevel_data +unset file_uplevel_data set ui_index .vpane.files.index.list set ui_workdir .vpane.files.workdir.list -- cgit v1.2.3 From c94dd1c8c2da1601c83443292b4ab34be3ab3358 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 02:52:06 -0500 Subject: git-gui: Display the current branch name in browsers. Rather than using HEAD for the current branch, use the actual name of the current branch in the browser. This way the user knows what a browser is browsing if they open up different browsers while on different branches. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 9892ec32ae..fc74d9e6ea 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2951,13 +2951,13 @@ proc reset_hard_wait {fd} { set next_browser_id 0 -proc new_browser {} { +proc new_browser {commit} { global next_browser_id cursor_ptr global browser_commit browser_status browser_stack browser_path browser_busy set w .browser[incr next_browser_id] set w_list $w.list.l - set browser_commit($w_list) HEAD + set browser_commit($w_list) $commit set browser_status($w_list) {Starting...} set browser_stack($w_list) {} set browser_path($w_list) $browser_commit($w_list): @@ -4714,7 +4714,7 @@ menu .mbar.repository .mbar.repository add command \ -label {Browse Current Branch} \ - -command new_browser \ + -command {new_browser $current_branch} \ -font font_ui .mbar.repository add separator -- cgit v1.2.3 From db45378165f499dcc4412b4f4b0f4ec5cbc65914 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 02:56:07 -0500 Subject: git-gui: Allow users to edit user.name, user.email from options. Users may need to be able to alter their user.name or user.email configuration settings. If they are mostly a git-gui user they should be able to view/set these important values from within the git-gui environment, rather than needing to edit a raw text file on their local filesystem. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index fc74d9e6ea..09c1b74e75 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -4270,6 +4270,9 @@ proc do_options {} { set optid 0 foreach option { + {t user.name {User Name}} + {t user.email {Email Address}} + {b merge.summary {Summarize Merge Commits}} {i-1..5 merge.verbosity {Merge Verbosity}} @@ -4676,6 +4679,9 @@ proc apply_config {} { set default_config(merge.summary) false set default_config(merge.verbosity) 2 +set default_config(user.name) {} +set default_config(user.email) {} + set default_config(gui.trustmtime) false set default_config(gui.diffcontext) 5 set default_config(gui.newbranchtemplate) {} -- cgit v1.2.3 From 463ca37b61b0d12aae8949c730bd38cc2149923f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 03:03:29 -0500 Subject: git-gui: Use -M and -C when running blame. Since we run blame incrementally in the background we might as well get as much data as we can from the file. Adding -M and -C definately makes it take longer to compute the revision annotations, but since they are streamed in and updated as they are discovered we'll get recent data almost immediately anyway. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 09c1b74e75..d5490cdde0 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3276,7 +3276,9 @@ proc read_blame_catfile {fd w commit path texts w_lno w_file} { if {[eof $fd]} { close $fd set blame_status($w) {Loading annotations...} - set fd [open "| git blame --incremental $commit -- $path" r] + set cmd [list git blame -M -C --incremental] + lappend cmd $commit -- $path + set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable "read_blame_incremental $fd $w $texts" } -- cgit v1.2.3 From 8f6c07b902b297329e63799d5332758530ec4d99 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 03:09:28 -0500 Subject: git-gui: Correctly handle spaces in filepaths. Anytime are about to open a pipe on what may be user data we need to make sure the value is escaped correctly into a Tcl list, so that the executed subprocess will receive the right arguments. For the most part we were already doing this correctly, but a handful of locations did not. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index d5490cdde0..160309b2c4 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3087,7 +3087,8 @@ proc ls_tree {w tree_id name} { lappend browser_stack($w) [list $tree_id $name] $w conf -state disabled - set fd [open "| git ls-tree -z $tree_id" r] + set cmd [list git ls-tree -z $tree_id] + set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation binary -encoding binary fileevent $fd readable [list read_ls_tree $fd $w] } @@ -3247,7 +3248,8 @@ proc show_blame {commit path} { wm title $w "[appname] ([reponame]): File Viewer" set blame_data($w,total_lines) 0 - set fd [open "| git cat-file blob $commit:$path" r] + set cmd [list git cat-file blob "$commit:$path"] + set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [list read_blame_catfile $fd $w $commit $path \ $texts $w.out.linenumber $w.out.file] -- cgit v1.2.3 From 915616e4ebaedc486a2dd235e70f25ceb39b4515 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 05:33:27 -0500 Subject: git-gui: Display original filename and line number in blame. When we annotate a file and show its line data, we're already asking for copy and movement detection (-M -C). This costs extra time, but gives extra data. Since we are asking for the extra data we really should show it to the user. Now the blame UI has two additional columns, one for the original filename (in the case of a move/copy between files) and one for the original line number of the current line of code. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 160309b2c4..f247d40a79 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3190,6 +3190,25 @@ proc show_blame {commit path} { $w.out add $w.out.date lappend texts $w.out.date + text $w.out.filename -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 40 \ + -width 20 \ + -font font_diff + $w.out add $w.out.filename + lappend texts $w.out.filename + + text $w.out.origlinenumber -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 40 \ + -width 5 \ + -font font_diff + $w.out.origlinenumber tag conf linenumber -justify right + $w.out add $w.out.origlinenumber + lappend texts $w.out.origlinenumber + text $w.out.linenumber -background white -borderwidth 0 \ -state disabled \ -wrap none \ @@ -3286,7 +3305,9 @@ proc read_blame_catfile {fd w commit path texts w_lno w_file} { } } -proc read_blame_incremental {fd w w_commit w_author w_date w_lno w_file} { +proc read_blame_incremental {fd w + w_commit w_author w_date w_filename w_olno + w_lno w_file} { global blame_status blame_data if {![winfo exists $w_commit]} { @@ -3297,6 +3318,8 @@ proc read_blame_incremental {fd w w_commit w_author w_date w_lno w_file} { $w_commit conf -state normal $w_author conf -state normal $w_date conf -state normal + $w_filename conf -state normal + $w_olno conf -state normal while {[gets $fd line] >= 0} { if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \ @@ -3308,6 +3331,7 @@ proc read_blame_incremental {fd w w_commit w_author w_date w_lno w_file} { } elseif {[string match {filename *} $line]} { set n $blame_data($w,line_count) set lno $blame_data($w,final_line) + set ol $blame_data($w,original_line) set file [string range $line 9 end] set commit $blame_data($w,commit) set abbrev [string range $commit 0 8] @@ -3327,14 +3351,20 @@ proc read_blame_incremental {fd w w_commit w_author w_date w_lno w_file} { $w_commit delete $lno.0 "$lno.0 lineend" $w_author delete $lno.0 "$lno.0 lineend" $w_date delete $lno.0 "$lno.0 lineend" + $w_filename delete $lno.0 "$lno.0 lineend" + $w_olno delete $lno.0 "$lno.0 lineend" $w_commit insert $lno.0 $abbrev $w_author insert $lno.0 $author $w_date insert $lno.0 $atime + $w_filename insert $lno.0 $file + $w_olno insert $lno.0 $ol linenumber + set blame_data($w,line$lno,commit) $commit incr n -1 incr lno + incr ol } } elseif {[regexp {^([a-z-]+) (.*)$} $line line header data]} { set blame_data($w,$blame_data($w,commit),$header) $data @@ -3344,6 +3374,8 @@ proc read_blame_incremental {fd w w_commit w_author w_date w_lno w_file} { $w_commit conf -state disabled $w_author conf -state disabled $w_date conf -state disabled + $w_filename conf -state disabled + $w_olno conf -state disabled if {[eof $fd]} { close $fd -- cgit v1.2.3 From e7fb6c69f7612d4e8545f1812a2830f97f183f87 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 05:51:49 -0500 Subject: git-gui: Install column headers in blame viewer. I started to get confused about what each column meant in the blame viewer, and I'm the guy who wrote the code! So now git-gui hints to the user about what each column is by drawing headers at the top. Unfortunately this meant I had to use those dreaded frame objects which seem to cause so much pain on Windows. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 95 ++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index f247d40a79..0fef7295ad 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3163,70 +3163,120 @@ proc show_blame {commit path} { -font font_uibold pack $w.path -anchor w -side top -fill x - text $w.out.commit -background white -borderwidth 0 \ + set hbg #e2effa + frame $w.out.commit -width 10 -height 10 + label $w.out.commit.l -text Commit \ + -background $hbg \ + -font font_uibold + text $w.out.commit.t \ + -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ - -width 8 \ + -width 9 \ -font font_diff + pack $w.out.commit.l -side top -fill x + pack $w.out.commit.t -fill both $w.out add $w.out.commit - lappend texts $w.out.commit + lappend texts $w.out.commit.t - text $w.out.author -background white -borderwidth 0 \ + frame $w.out.author -width 10 -height 10 + label $w.out.author.l -text Author \ + -background $hbg \ + -font font_uibold + text $w.out.author.t \ + -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 20 \ -font font_diff + pack $w.out.author.l -side top -fill x + pack $w.out.author.t -fill both $w.out add $w.out.author - lappend texts $w.out.author + lappend texts $w.out.author.t - text $w.out.date -background white -borderwidth 0 \ + frame $w.out.date -width 10 -height 10 + label $w.out.date.l -text Date \ + -background $hbg \ + -font font_uibold + text $w.out.date.t \ + -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width [string length "yyyy-mm-dd hh:mm:ss"] \ -font font_diff + pack $w.out.date.l -side top -fill x + pack $w.out.date.t -fill both $w.out add $w.out.date - lappend texts $w.out.date + lappend texts $w.out.date.t - text $w.out.filename -background white -borderwidth 0 \ + frame $w.out.filename -width 10 -height 10 + label $w.out.filename.l -text Filename \ + -background $hbg \ + -font font_uibold + text $w.out.filename.t \ + -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 20 \ -font font_diff + pack $w.out.filename.l -side top -fill x + pack $w.out.filename.t -fill both $w.out add $w.out.filename - lappend texts $w.out.filename + lappend texts $w.out.filename.t - text $w.out.origlinenumber -background white -borderwidth 0 \ + frame $w.out.origlinenumber -width 10 -height 10 + label $w.out.origlinenumber.l -text {Orig Line} \ + -background $hbg \ + -font font_uibold + text $w.out.origlinenumber.t \ + -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 5 \ -font font_diff - $w.out.origlinenumber tag conf linenumber -justify right + $w.out.origlinenumber.t tag conf linenumber -justify right + pack $w.out.origlinenumber.l -side top -fill x + pack $w.out.origlinenumber.t -fill both $w.out add $w.out.origlinenumber - lappend texts $w.out.origlinenumber + lappend texts $w.out.origlinenumber.t - text $w.out.linenumber -background white -borderwidth 0 \ + frame $w.out.linenumber -width 10 -height 10 + label $w.out.linenumber.l -text {Curr Line} \ + -background $hbg \ + -font font_uibold + text $w.out.linenumber.t \ + -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 5 \ -font font_diff - $w.out.linenumber tag conf linenumber -justify right + $w.out.linenumber.t tag conf linenumber -justify right + pack $w.out.linenumber.l -side top -fill x + pack $w.out.linenumber.t -fill both $w.out add $w.out.linenumber - lappend texts $w.out.linenumber + lappend texts $w.out.linenumber.t - text $w.out.file -background white -borderwidth 0 \ + frame $w.out.file -width 10 -height 10 + label $w.out.file.l -text {File Content} \ + -background $hbg \ + -font font_uibold + text $w.out.file.t \ + -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 80 \ -font font_diff + pack $w.out.file.l -side top -fill x + pack $w.out.file.t -fill both $w.out add $w.out.file - lappend texts $w.out.file + lappend texts $w.out.file.t label $w.status -textvariable blame_status($w) \ -anchor w \ @@ -3236,7 +3286,8 @@ proc show_blame {commit path} { -font font_ui pack $w.status -anchor w -side bottom -fill x - scrollbar $w.sby -orient v -command [list scrollbar2many $texts yview] + scrollbar $w.sby -orient v \ + -command [list scrollbar2many $texts yview] pack $w.sby -side right -fill y pack $w.out -side left -fill both -expand 1 @@ -3249,7 +3300,8 @@ proc show_blame {commit path} { $i tag conf in_sel \ -background [$i cget -foreground] \ -foreground [$i cget -background] - $i conf -yscrollcommand [list many2scrollbar $texts yview $w.sby] + $i conf -yscrollcommand \ + [list many2scrollbar $texts yview $w.sby] bind $i "blame_highlight $i @%x,%y $texts;break" bind_button3 $i " set cursorX %x @@ -3270,8 +3322,9 @@ proc show_blame {commit path} { set cmd [list git cat-file blob "$commit:$path"] set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary - fileevent $fd readable [list read_blame_catfile $fd $w $commit $path \ - $texts $w.out.linenumber $w.out.file] + fileevent $fd readable [list read_blame_catfile \ + $fd $w $commit $path \ + $texts $w.out.linenumber.t $w.out.file.t] } proc read_blame_catfile {fd w commit path texts w_lno w_file} { -- cgit v1.2.3 From 747c0cf93c75207da095709a92a615aae0553289 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 06:23:12 -0500 Subject: git-gui: Use a grid layout for the blame viewer. Using a panedwindow to display the blame viewer's individual columns just doesn't make sense. Most of the important data fits within the columns we have allocated, and those that don't the leading part fits and that's good enough. There are just too many columns within this viewer to let the user sanely control individual column widths. This change shouldn't really be an issue for most git-gui users as their displays should be large enough to accept this massive dump of data. We now also have a properly working horizontal scrollbar for the current file data area. This makes it easier to get away with a narrow window when screen space is limited, as you can still scroll around within the file content. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 124 ++++++++++++++++++++++++++++++------------------------------- 1 file changed, 61 insertions(+), 63 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 0fef7295ad..1f13f7f9b2 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3153,7 +3153,6 @@ proc show_blame {commit path} { set texts [list] toplevel $w - panedwindow $w.out -orient horizontal label $w.path -text "$commit:$path" \ -anchor w \ @@ -3161,122 +3160,126 @@ proc show_blame {commit path} { -borderwidth 1 \ -relief sunken \ -font font_uibold - pack $w.path -anchor w -side top -fill x + pack $w.path -side top -fill x set hbg #e2effa - frame $w.out.commit -width 10 -height 10 - label $w.out.commit.l -text Commit \ + frame $w.out + label $w.out.commit_l -text Commit \ + -relief solid \ + -borderwidth 1 \ -background $hbg \ -font font_uibold - text $w.out.commit.t \ + text $w.out.commit_t \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 9 \ -font font_diff - pack $w.out.commit.l -side top -fill x - pack $w.out.commit.t -fill both - $w.out add $w.out.commit - lappend texts $w.out.commit.t + lappend texts $w.out.commit_t - frame $w.out.author -width 10 -height 10 - label $w.out.author.l -text Author \ + label $w.out.author_l -text Author \ + -relief solid \ + -borderwidth 1 \ -background $hbg \ -font font_uibold - text $w.out.author.t \ + text $w.out.author_t \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 20 \ -font font_diff - pack $w.out.author.l -side top -fill x - pack $w.out.author.t -fill both - $w.out add $w.out.author - lappend texts $w.out.author.t + lappend texts $w.out.author_t - frame $w.out.date -width 10 -height 10 - label $w.out.date.l -text Date \ + label $w.out.date_l -text Date \ + -relief solid \ + -borderwidth 1 \ -background $hbg \ -font font_uibold - text $w.out.date.t \ + text $w.out.date_t \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width [string length "yyyy-mm-dd hh:mm:ss"] \ -font font_diff - pack $w.out.date.l -side top -fill x - pack $w.out.date.t -fill both - $w.out add $w.out.date - lappend texts $w.out.date.t + lappend texts $w.out.date_t - frame $w.out.filename -width 10 -height 10 - label $w.out.filename.l -text Filename \ + label $w.out.filename_l -text Filename \ + -relief solid \ + -borderwidth 1 \ -background $hbg \ -font font_uibold - text $w.out.filename.t \ + text $w.out.filename_t \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 20 \ -font font_diff - pack $w.out.filename.l -side top -fill x - pack $w.out.filename.t -fill both - $w.out add $w.out.filename - lappend texts $w.out.filename.t + lappend texts $w.out.filename_t - frame $w.out.origlinenumber -width 10 -height 10 - label $w.out.origlinenumber.l -text {Orig Line} \ + label $w.out.origlinenumber_l -text {Orig Line} \ + -relief solid \ + -borderwidth 1 \ -background $hbg \ -font font_uibold - text $w.out.origlinenumber.t \ + text $w.out.origlinenumber_t \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 5 \ -font font_diff - $w.out.origlinenumber.t tag conf linenumber -justify right - pack $w.out.origlinenumber.l -side top -fill x - pack $w.out.origlinenumber.t -fill both - $w.out add $w.out.origlinenumber - lappend texts $w.out.origlinenumber.t - - frame $w.out.linenumber -width 10 -height 10 - label $w.out.linenumber.l -text {Curr Line} \ + $w.out.origlinenumber_t tag conf linenumber -justify right + lappend texts $w.out.origlinenumber_t + + label $w.out.linenumber_l -text {Curr Line} \ + -relief solid \ + -borderwidth 1 \ -background $hbg \ -font font_uibold - text $w.out.linenumber.t \ + text $w.out.linenumber_t \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 5 \ -font font_diff - $w.out.linenumber.t tag conf linenumber -justify right - pack $w.out.linenumber.l -side top -fill x - pack $w.out.linenumber.t -fill both - $w.out add $w.out.linenumber - lappend texts $w.out.linenumber.t - - frame $w.out.file -width 10 -height 10 - label $w.out.file.l -text {File Content} \ + $w.out.linenumber_t tag conf linenumber -justify right + lappend texts $w.out.linenumber_t + + label $w.out.file_l -text {File Content} \ + -relief solid \ + -borderwidth 1 \ -background $hbg \ -font font_uibold - text $w.out.file.t \ + text $w.out.file_t \ -background white -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ -width 80 \ + -xscrollcommand [list $w.out.sbx set] \ -font font_diff - pack $w.out.file.l -side top -fill x - pack $w.out.file.t -fill both - $w.out add $w.out.file - lappend texts $w.out.file.t + lappend texts $w.out.file_t + + scrollbar $w.out.sbx -orient h -command [list $w.out.file_t xview] + scrollbar $w.out.sby -orient v \ + -command [list scrollbar2many $texts yview] + set labels [list] + foreach i $texts { + regsub {_t$} $i _l l + lappend labels $l + } + set file_col [expr {[llength $texts] - 1}] + eval grid $labels -sticky we + eval grid $texts $w.out.sby -sticky nsew + grid conf $w.out.sbx -column $file_col -sticky we + grid columnconfigure $w.out $file_col -weight 1 + grid rowconfigure $w.out 1 -weight 1 + pack $w.out -fill both -expand 1 label $w.status -textvariable blame_status($w) \ -anchor w \ @@ -3284,12 +3287,7 @@ proc show_blame {commit path} { -borderwidth 1 \ -relief sunken \ -font font_ui - pack $w.status -anchor w -side bottom -fill x - - scrollbar $w.sby -orient v \ - -command [list scrollbar2many $texts yview] - pack $w.sby -side right -fill y - pack $w.out -side left -fill both -expand 1 + pack $w.status -side bottom -fill x menu $w.ctxm -tearoff 0 $w.ctxm add command -label "Copy Commit" \ @@ -3301,7 +3299,7 @@ proc show_blame {commit path} { -background [$i cget -foreground] \ -foreground [$i cget -background] $i conf -yscrollcommand \ - [list many2scrollbar $texts yview $w.sby] + [list many2scrollbar $texts yview $w.out.sby] bind $i "blame_highlight $i @%x,%y $texts;break" bind_button3 $i " set cursorX %x @@ -3324,7 +3322,7 @@ proc show_blame {commit path} { fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [list read_blame_catfile \ $fd $w $commit $path \ - $texts $w.out.linenumber.t $w.out.file.t] + $texts $w.out.linenumber_t $w.out.file_t] } proc read_blame_catfile {fd w commit path texts w_lno w_file} { -- cgit v1.2.3 From 37f1db80a46cf753308bfc9b5de9dd3b3a551218 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 29 Jan 2007 06:56:00 -0500 Subject: git-gui: Assign background colors to each blame hunk. To help the user visually see which lines are associated with each other in the file we attempt to sign a unique background color to each commit and then render all text associated with that commit using that color. This works out OK for a file which has very few commits in it; but most files don't have that property. What we really need to do is look at what colors are used by our neighboring commits (if known yet) and pick a color which does not conflict with our neighbor. If we have run out of colors then we should force our neighbor to recolor too. Yes, its the graph coloring problem. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 1f13f7f9b2..ef353319ec 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3309,6 +3309,8 @@ proc show_blame {commit path} { " } + set blame_data($w,colors) {} + bind $w "focus $w" bind $w " array unset blame_status $w @@ -3366,6 +3368,15 @@ proc read_blame_incremental {fd w return } + set all [list \ + $w_commit \ + $w_author \ + $w_date \ + $w_filename \ + $w_olno \ + $w_lno \ + $w_file] + $w_commit conf -state normal $w_author conf -state normal $w_date conf -state normal @@ -3374,36 +3385,65 @@ proc read_blame_incremental {fd w while {[gets $fd line] >= 0} { if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \ - commit original_line final_line line_count]} { - set blame_data($w,commit) $commit + cmit original_line final_line line_count]} { + set blame_data($w,commit) $cmit set blame_data($w,original_line) $original_line set blame_data($w,final_line) $final_line set blame_data($w,line_count) $line_count + + if {[catch {set g $blame_data($w,$cmit,seen)}]} { + if {$blame_data($w,colors) eq {}} { + set blame_data($w,colors) { + yellow + red + pink + orange + green + grey + } + } + set c [lindex $blame_data($w,colors) 0] + set blame_data($w,colors) \ + [lrange $blame_data($w,colors) 1 end] + foreach t $all { + $t tag conf g$cmit -background $c + } + } else { + set blame_data($w,$cmit,seen) 1 + } } elseif {[string match {filename *} $line]} { set n $blame_data($w,line_count) set lno $blame_data($w,final_line) set ol $blame_data($w,original_line) set file [string range $line 9 end] - set commit $blame_data($w,commit) - set abbrev [string range $commit 0 8] + set cmit $blame_data($w,commit) + set abbrev [string range $cmit 0 8] - if {[catch {set author $blame_data($w,$commit,author)} err]} { - puts $err + if {[catch {set author $blame_data($w,$cmit,author)} err]} { set author {} } - if {[catch {set atime $blame_data($w,$commit,author-time)}]} { + if {[catch {set atime $blame_data($w,$cmit,author-time)}]} { set atime {} } else { set atime [clock format $atime -format {%Y-%m-%d %T}] } while {$n > 0} { - $w_commit delete $lno.0 "$lno.0 lineend" - $w_author delete $lno.0 "$lno.0 lineend" - $w_date delete $lno.0 "$lno.0 lineend" - $w_filename delete $lno.0 "$lno.0 lineend" - $w_olno delete $lno.0 "$lno.0 lineend" + if {![catch {set g g$blame_data($w,line$lno,commit)}]} { + foreach t $all { + $t tag remove $g $lno.0 "$lno.0 lineend + 1c" + } + } + + foreach t [list \ + $w_commit \ + $w_author \ + $w_date \ + $w_filename \ + $w_olno] { + $t delete $lno.0 "$lno.0 lineend" + } $w_commit insert $lno.0 $abbrev $w_author insert $lno.0 $author @@ -3411,7 +3451,12 @@ proc read_blame_incremental {fd w $w_filename insert $lno.0 $file $w_olno insert $lno.0 $ol linenumber - set blame_data($w,line$lno,commit) $commit + set g g$cmit + foreach t $all { + $t tag add $g $lno.0 "$lno.0 lineend + 1c" + } + + set blame_data($w,line$lno,commit) $cmit incr n -1 incr lno -- cgit v1.2.3 From 63faf4df6e174cc1e33a759d49133044c732ea32 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 15:59:39 -0500 Subject: git-gui: Update known branches during rescan. If the user has created (or deleted) a branch through an external tool, and uses Rescan, they probably are trying to make git-gui update to show their newly created branch. So now we load all known heads and update the branch menu during any rescan operation, just in-case the set of known branches was modified. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index ef353319ec..4116821d8d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -400,7 +400,7 @@ proc rescan {after {honor_trustmtime 1}} { global HEAD PARENT MERGE_HEAD commit_type global ui_index ui_workdir ui_status_value ui_comm global rescan_active file_states - global repo_config + global repo_config single_commit if {$rescan_active > 0 || ![lock_index read]} return @@ -427,6 +427,11 @@ proc rescan {after {honor_trustmtime 1}} { $ui_comm edit modified false } + if {!$single_commit} { + load_all_heads + populate_branch_menu + } + if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} { rescan_stage2 {} $after } else { -- cgit v1.2.3 From 9bccb782c35b864988e51d433271faba2ac3e96f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 17:07:59 -0500 Subject: git-gui: Support keyboard traversal in browser. Users want to navigate the file list shown in our branch browser windows using the keyboard. So we now support basic traversal with the arrow keys: Up/Down: Move the "selection bar" to focus on a different name. Return: Move into the subtree, or open the annotated file. M1-Right: Ditto. M1-Up: Move to the parent tree. M1-Left: Ditto. Probably the only feature missing from this is to key a leading part of the file name and jump directly to that file (or subtree). This change did require a bit of refactoring, to pull the navigation logic out of the mouse click procedure and into more generic routines which can also be used in bindings. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 137 ++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 38 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 4116821d8d..8db8789662 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2957,7 +2957,7 @@ proc reset_hard_wait {fd} { set next_browser_id 0 proc new_browser {commit} { - global next_browser_id cursor_ptr + global next_browser_id cursor_ptr M1B global browser_commit browser_status browser_stack browser_path browser_busy set w .browser[incr next_browser_id] @@ -3007,6 +3007,16 @@ proc new_browser {commit} { bind $w_list "browser_click 0 $w_list @%x,%y;break" bind $w_list "browser_click 1 $w_list @%x,%y;break" + bind $w_list <$M1B-Up> "browser_parent $w_list;break" + bind $w_list <$M1B-Left> "browser_parent $w_list;break" + bind $w_list "browser_move -1 $w_list;break" + bind $w_list "browser_move 1 $w_list;break" + bind $w_list <$M1B-Right> "browser_enter $w_list;break" + bind $w_list "browser_enter $w_list;break" + bind $w_list "browser_page -1 $w_list;break" + bind $w_list "browser_page 1 $w_list;break" + bind $w_list break + bind $w_list break bind $w "focus $w" bind $w " @@ -3022,52 +3032,100 @@ proc new_browser {commit} { ls_tree $w_list $browser_commit($w_list) {} } -proc browser_click {was_double_click w pos} { +proc browser_move {dir w} { + global browser_files browser_busy + + if {$browser_busy($w)} return + set lno [lindex [split [$w index in_sel.first] .] 0] + incr lno $dir + if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} { + $w tag remove in_sel 0.0 end + $w tag add in_sel $lno.0 [expr {$lno + 1}].0 + $w see $lno.0 + } +} + +proc browser_page {dir w} { + global browser_files browser_busy + + if {$browser_busy($w)} return + $w yview scroll $dir pages + set lno [expr {int( + [lindex [$w yview] 0] + * [llength $browser_files($w)] + + 1)}] + if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} { + $w tag remove in_sel 0.0 end + $w tag add in_sel $lno.0 [expr {$lno + 1}].0 + $w see $lno.0 + } +} + +proc browser_parent {w} { + global browser_files browser_status browser_path + global browser_stack browser_busy + + if {$browser_busy($w)} return + set info [lindex $browser_files($w) 0] + if {[lindex $info 0] eq {parent}} { + set parent [lindex $browser_stack($w) end-1] + set browser_stack($w) [lrange $browser_stack($w) 0 end-2] + if {$browser_stack($w) eq {}} { + regsub {:.*$} $browser_path($w) {:} browser_path($w) + } else { + regsub {/[^/]+$} $browser_path($w) {} browser_path($w) + } + set browser_status($w) "Loading $browser_path($w)..." + ls_tree $w [lindex $parent 0] [lindex $parent 1] + } +} + +proc browser_enter {w} { global browser_files browser_status browser_path global browser_commit browser_stack browser_busy if {$browser_busy($w)} return - set lno [lindex [split [$w index $pos] .] 0] + set lno [lindex [split [$w index in_sel.first] .] 0] set info [lindex $browser_files($w) [expr {$lno - 1}]] - - $w conf -state normal - $w tag remove sel 0.0 end - $w tag remove in_sel 0.0 end if {$info ne {}} { + switch -- [lindex $info 0] { + parent { + browser_parent $w + } + tree { + set name [lindex $info 2] + set escn [escape_path $name] + set browser_status($w) "Loading $escn..." + append browser_path($w) $escn + ls_tree $w [lindex $info 1] $name + } + blob { + set name [lindex $info 2] + set p {} + foreach n $browser_stack($w) { + append p [lindex $n 1] + } + append p $name + show_blame $browser_commit($w) $p + } + } + } +} + +proc browser_click {was_double_click w pos} { + global browser_files browser_busy + + if {$browser_busy($w)} return + set lno [lindex [split [$w index $pos] .] 0] + focus $w + + if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} { + $w tag remove in_sel 0.0 end $w tag add in_sel $lno.0 [expr {$lno + 1}].0 if {$was_double_click} { - switch -- [lindex $info 0] { - parent { - set parent [lindex $browser_stack($w) end-1] - set browser_stack($w) [lrange $browser_stack($w) 0 end-2] - if {$browser_stack($w) eq {}} { - regsub {:.*$} $browser_path($w) {:} browser_path($w) - } else { - regsub {/[^/]+$} $browser_path($w) {} browser_path($w) - } - set browser_status($w) "Loading $browser_path($w)..." - ls_tree $w [lindex $parent 0] [lindex $parent 1] - } - tree { - set name [lindex $info 2] - set escn [escape_path $name] - set browser_status($w) "Loading $escn..." - append browser_path($w) $escn - ls_tree $w [lindex $info 1] $name - } - blob { - set name [lindex $info 2] - set p {} - foreach n $browser_stack($w) { - append p [lindex $n 1] - } - append p $name - show_blame $browser_commit($w) $p - } - } + browser_enter $w } } - $w conf -state disabled } proc ls_tree {w tree_id name} { @@ -3079,7 +3137,6 @@ proc ls_tree {w tree_id name} { $w conf -state normal $w tag remove in_sel 0.0 end - $w tag remove sel 0.0 end $w delete 0.0 end if {$browser_stack($w) ne {}} { $w image create end \ @@ -3147,6 +3204,10 @@ proc read_ls_tree {fd w} { set browser_status($w) Ready. set browser_busy($w) 0 array unset browser_buffer $w + if {$n > 0} { + $w tag add in_sel 1.0 2.0 + focus -force $w + } } } -- cgit v1.2.3 From 42b922fcf6f3bd16c4579f2b9e1e52c79096eba2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 17:13:51 -0500 Subject: git-gui: Replace \ with \\ when showing paths. We already replace \n with \\n so that Tk widgets don't start a new display line with part of a file path which is just unlucky enough to contain an LF. But then its confusing to read a path whose name actually contains \n as literal characters. Escaping \ to \\ would make that case display as \\n, clarifying the output. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/git-gui.sh b/git-gui.sh index 8db8789662..1d225644aa 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1381,6 +1381,7 @@ proc mapdesc {state path} { } proc escape_path {path} { + regsub -all {\\} $path "\\\\" path regsub -all "\n" $path "\\n" path return $path } -- cgit v1.2.3 From cf25ddc8b31ef883cf23eeb8d2eae21535a98002 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 18:03:41 -0500 Subject: git-gui: Refactor single_commit to a proc. This is a minor code cleanup to make working with what used to be the $single_commit flag easier. Its also to better handle various UI configurations, depending on command line parameters given by the user, or perhaps user preferences. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 1d225644aa..8305720d53 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -93,6 +93,22 @@ proc is_Cygwin {} { return $_iscygwin } +proc is_enabled {option} { + global enabled_options + if {[catch {set on $enabled_options($option)}]} {return 0} + return $on +} + +proc enable_option {option} { + global enabled_options + set enabled_options($option) 1 +} + +proc disable_option {option} { + global enabled_options + set enabled_options($option) 0 +} + ###################################################################### ## ## config @@ -303,9 +319,9 @@ set _reponame [lindex [file split \ [file normalize [file dirname $_gitdir]]] \ end] -set single_commit 0 +enable_option multicommit if {[appname] eq {git-citool}} { - set single_commit 1 + disable_option multicommit } ###################################################################### @@ -400,7 +416,7 @@ proc rescan {after {honor_trustmtime 1}} { global HEAD PARENT MERGE_HEAD commit_type global ui_index ui_workdir ui_status_value ui_comm global rescan_active file_states - global repo_config single_commit + global repo_config if {$rescan_active > 0 || ![lock_index read]} return @@ -427,7 +443,7 @@ proc rescan {after {honor_trustmtime 1}} { $ui_comm edit modified false } - if {!$single_commit} { + if {[is_enabled multicommit]} { load_all_heads populate_branch_menu } @@ -1181,7 +1197,7 @@ proc commit_writetree {curHEAD msg} { proc commit_committree {fd_wt curHEAD msg} { global HEAD PARENT MERGE_HEAD commit_type - global single_commit all_heads current_branch + global all_heads current_branch global ui_status_value ui_comm selected_commit_type global file_states selected_paths rescan_active global repo_config @@ -1286,7 +1302,7 @@ proc commit_committree {fd_wt curHEAD msg} { $ui_comm edit reset $ui_comm edit modified false - if {$single_commit} do_quit + if {![is_enabled multicommit]} do_quit # -- Update in memory status # @@ -4902,11 +4918,11 @@ apply_config menu .mbar -tearoff 0 .mbar add cascade -label Repository -menu .mbar.repository .mbar add cascade -label Edit -menu .mbar.edit -if {!$single_commit} { +if {[is_enabled multicommit]} { .mbar add cascade -label Branch -menu .mbar.branch } .mbar add cascade -label Commit -menu .mbar.commit -if {!$single_commit} { +if {[is_enabled multicommit]} { .mbar add cascade -label Merge -menu .mbar.merge .mbar add cascade -label Fetch -menu .mbar.fetch .mbar add cascade -label Push -menu .mbar.push @@ -4933,7 +4949,7 @@ menu .mbar.repository -font font_ui .mbar.repository add separator -if {!$single_commit} { +if {[is_enabled multicommit]} { .mbar.repository add command -label {Database Statistics} \ -command do_stats \ -font font_ui @@ -5007,7 +5023,7 @@ menu .mbar.edit # -- Branch Menu # -if {!$single_commit} { +if {[is_enabled multicommit]} { menu .mbar.branch .mbar.branch add command -label {Create...} \ @@ -5214,7 +5230,7 @@ pack .branch.l1 -side left pack .branch.cb -side left -fill x pack .branch -side top -fill x -if {!$single_commit} { +if {[is_enabled multicommit]} { menu .mbar.merge .mbar.merge add command -label {Local Merge...} \ -command do_local_merge \ @@ -5686,7 +5702,7 @@ bind $ui_diff {catch {%W xview scroll -1 units};break} bind $ui_diff {catch {%W xview scroll 1 units};break} bind $ui_diff {focus %W} -if {!$single_commit} { +if {[is_enabled multicommit]} { bind . <$M1B-Key-n> do_create_branch bind . <$M1B-Key-N> do_create_branch } @@ -5783,7 +5799,7 @@ user.email settings into your personal # -- Only initialize complex UI if we are going to stay running. # -if {!$single_commit} { +if {[is_enabled multicommit]} { load_all_remotes load_all_heads @@ -5794,7 +5810,7 @@ if {!$single_commit} { # -- Only suggest a gc run if we are going to stay running. # -if {!$single_commit} { +if {[is_enabled multicommit]} { set object_limit 2000 if {[is_Windows]} {set object_limit 200} regexp {^([0-9]+) objects,} [exec git count-objects] _junk objects_current -- cgit v1.2.3 From 64a906f8611ee195efcde76f5587c8e08bac7a25 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 18:10:05 -0500 Subject: git-gui: Separate transport/branch menus from multicommit. These are now controlled by the transport and branch options, rather than the multicommit option. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 8305720d53..50bcd11235 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -320,8 +320,13 @@ set _reponame [lindex [file split \ end] enable_option multicommit +enable_option branch +enable_option transport + if {[appname] eq {git-citool}} { disable_option multicommit + disable_option branch + disable_option transport } ###################################################################### @@ -443,7 +448,7 @@ proc rescan {after {honor_trustmtime 1}} { $ui_comm edit modified false } - if {[is_enabled multicommit]} { + if {[is_enabled branch]} { load_all_heads populate_branch_menu } @@ -4918,11 +4923,11 @@ apply_config menu .mbar -tearoff 0 .mbar add cascade -label Repository -menu .mbar.repository .mbar add cascade -label Edit -menu .mbar.edit -if {[is_enabled multicommit]} { +if {[is_enabled branch]} { .mbar add cascade -label Branch -menu .mbar.branch } .mbar add cascade -label Commit -menu .mbar.commit -if {[is_enabled multicommit]} { +if {[is_enabled transport]} { .mbar add cascade -label Merge -menu .mbar.merge .mbar add cascade -label Fetch -menu .mbar.fetch .mbar add cascade -label Push -menu .mbar.push @@ -5023,7 +5028,7 @@ menu .mbar.edit # -- Branch Menu # -if {[is_enabled multicommit]} { +if {[is_enabled branch]} { menu .mbar.branch .mbar.branch add command -label {Create...} \ @@ -5230,7 +5235,7 @@ pack .branch.l1 -side left pack .branch.cb -side left -fill x pack .branch -side top -fill x -if {[is_enabled multicommit]} { +if {[is_enabled branch]} { menu .mbar.merge .mbar.merge add command -label {Local Merge...} \ -command do_local_merge \ @@ -5702,7 +5707,7 @@ bind $ui_diff {catch {%W xview scroll -1 units};break} bind $ui_diff {catch {%W xview scroll 1 units};break} bind $ui_diff {focus %W} -if {[is_enabled multicommit]} { +if {[is_enabled branch]} { bind . <$M1B-Key-n> do_create_branch bind . <$M1B-Key-N> do_create_branch } @@ -5799,7 +5804,7 @@ user.email settings into your personal # -- Only initialize complex UI if we are going to stay running. # -if {[is_enabled multicommit]} { +if {[is_enabled transport]} { load_all_remotes load_all_heads -- cgit v1.2.3 From db7f34d4c54c1162d976be4d1752642c85e063b4 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 17:47:17 -0500 Subject: git-gui: Optionally save commit buffer on exit. If the commit area does not exist, don't save the commit message to a file, or the window geometry. The reason I'm doing this is I want to make the main window entirely optional, such as if the user has asked us to show a blame from the command line. In such cases the commit area won't exist and trying to get its text would cause an error. If we are running without the commit message area, we cannot save our window geometry either, as the root window '.' won't be a normal commit window. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 54 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 50bcd11235..335b5f8b67 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -4126,34 +4126,36 @@ proc do_quit {} { if {$is_quitting} return set is_quitting 1 - # -- Stash our current commit buffer. - # - set save [gitdir GITGUI_MSG] - set msg [string trim [$ui_comm get 0.0 end]] - regsub -all -line {[ \r\t]+$} $msg {} msg - if {(![string match amend* $commit_type] - || [$ui_comm edit modified]) - && $msg ne {}} { - catch { - set fd [open $save w] - puts -nonewline $fd $msg - close $fd + if {[winfo exists $ui_comm]} { + # -- Stash our current commit buffer. + # + set save [gitdir GITGUI_MSG] + set msg [string trim [$ui_comm get 0.0 end]] + regsub -all -line {[ \r\t]+$} $msg {} msg + if {(![string match amend* $commit_type] + || [$ui_comm edit modified]) + && $msg ne {}} { + catch { + set fd [open $save w] + puts -nonewline $fd $msg + close $fd + } + } else { + catch {file delete $save} } - } else { - catch {file delete $save} - } - # -- Stash our current window geometry into this repository. - # - set cfg_geometry [list] - lappend cfg_geometry [wm geometry .] - lappend cfg_geometry [lindex [.vpane sash coord 0] 1] - lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0] - if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { - set rc_geometry {} - } - if {$cfg_geometry ne $rc_geometry} { - catch {exec git repo-config gui.geometry $cfg_geometry} + # -- Stash our current window geometry into this repository. + # + set cfg_geometry [list] + lappend cfg_geometry [wm geometry .] + lappend cfg_geometry [lindex [.vpane sash coord 0] 1] + lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0] + if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { + set rc_geometry {} + } + if {$cfg_geometry ne $rc_geometry} { + catch {exec git repo-config gui.geometry $cfg_geometry} + } } destroy . -- cgit v1.2.3 From 2ebba528dcb64ec1a408ec3b06cf9f928a7fb76d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 19:10:52 -0500 Subject: git-gui: View blame from the command line. Viewing annotated files is one of those tasks that is relatively difficult to do in a simple vt100 terminal emulator. The user really wants to be able to browse through a lot of information, and to interact with it by navigating through revisions. Now users can start our file viewer with annotations by running 'git gui blame commit path', thereby seeing the contents of the given file at the given commit. Right now I am being lazy by not allowing the user to omit the commit name (and have us thus assume HEAD). Signed-off-by: Shawn O. Pearce --- git-gui.sh | 220 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 136 insertions(+), 84 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 335b5f8b67..66d85dc6d0 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -319,16 +319,6 @@ set _reponame [lindex [file split \ [file normalize [file dirname $_gitdir]]] \ end] -enable_option multicommit -enable_option branch -enable_option transport - -if {[appname] eq {git-citool}} { - disable_option multicommit - disable_option branch - disable_option transport -} - ###################################################################### ## ## task management @@ -1307,7 +1297,7 @@ proc commit_committree {fd_wt curHEAD msg} { $ui_comm edit reset $ui_comm edit modified false - if {![is_enabled multicommit]} do_quit + if {[is_enabled singlecommit]} do_quit # -- Update in memory status # @@ -3236,12 +3226,17 @@ proc read_ls_tree {fd w} { proc show_blame {commit path} { global next_browser_id blame_status blame_data - set w .browser[incr next_browser_id] + if {[winfo ismapped .]} { + set w .browser[incr next_browser_id] + set tl $w + toplevel $w + } else { + set w {} + set tl . + } set blame_status($w) {Loading current file content...} set texts [list] - toplevel $w - label $w.path -text "$commit:$path" \ -anchor w \ -justify left \ @@ -3399,12 +3394,12 @@ proc show_blame {commit path} { set blame_data($w,colors) {} - bind $w "focus $w" - bind $w " - array unset blame_status $w + bind $tl "focus $tl" + bind $tl " + array unset blame_status {$w} array unset blame_data $w,* " - wm title $w "[appname] ([reponame]): File Viewer" + wm title $tl "[appname] ([reponame]): File Viewer" set blame_data($w,total_lines) 0 set cmd [list git cat-file blob "$commit:$path"] @@ -3442,7 +3437,9 @@ proc read_blame_catfile {fd w commit path texts w_lno w_file} { lappend cmd $commit -- $path set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary - fileevent $fd readable "read_blame_incremental $fd $w $texts" + set handler [list read_blame_incremental $fd $w] + append handler " $texts" + fileevent $fd readable $handler } } @@ -4916,10 +4913,36 @@ set font_descs { load_config 0 apply_config +###################################################################### +## +## feature option selection + +enable_option multicommit +enable_option branch +enable_option transport + +if {[appname] eq {git-citool}} { + enable_option singlecommit + + disable_option multicommit + disable_option branch + disable_option transport +} + +switch -- [lindex $argv 0] { +blame { + disable_option multicommit + disable_option branch + disable_option transport +} +} + ###################################################################### ## ## ui construction +set ui_comm {} + # -- Menu Bar # menu .mbar -tearoff 0 @@ -4928,7 +4951,9 @@ menu .mbar -tearoff 0 if {[is_enabled branch]} { .mbar add cascade -label Branch -menu .mbar.branch } -.mbar add cascade -label Commit -menu .mbar.commit +if {[is_enabled multicommit] || [is_enabled singlecommit]} { + .mbar add cascade -label Commit -menu .mbar.commit +} if {[is_enabled transport]} { .mbar add cascade -label Merge -menu .mbar.merge .mbar add cascade -label Fetch -menu .mbar.fetch @@ -4944,15 +4969,17 @@ menu .mbar.repository -label {Browse Current Branch} \ -command {new_browser $current_branch} \ -font font_ui +trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#" .mbar.repository add separator .mbar.repository add command \ -label {Visualize Current Branch} \ - -command {do_gitk {}} \ + -command {do_gitk $current_branch} \ -font font_ui +trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#" .mbar.repository add command \ -label {Visualize All Branches} \ - -command {do_gitk {--all}} \ + -command {do_gitk --all} \ -font font_ui .mbar.repository add separator @@ -5049,73 +5076,75 @@ if {[is_enabled branch]} { # -- Commit Menu # -menu .mbar.commit - -.mbar.commit add radiobutton \ - -label {New Commit} \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value new \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] +if {[is_enabled multicommit] || [is_enabled singlecommit]} { + menu .mbar.commit + + .mbar.commit add radiobutton \ + -label {New Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value new \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add radiobutton \ - -label {Amend Last Commit} \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value amend \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add radiobutton \ + -label {Amend Last Commit} \ + -command do_select_commit_type \ + -variable selected_commit_type \ + -value amend \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add separator + .mbar.commit add separator -.mbar.commit add command -label Rescan \ - -command do_rescan \ - -accelerator F5 \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label Rescan \ + -command do_rescan \ + -accelerator F5 \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Add To Commit} \ - -command do_add_selection \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label {Add To Commit} \ + -command do_add_selection \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Add All To Commit} \ - -command do_add_all \ - -accelerator $M1T-I \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label {Add All To Commit} \ + -command do_add_all \ + -accelerator $M1T-I \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Unstage From Commit} \ - -command do_unstage_selection \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label {Unstage From Commit} \ + -command do_unstage_selection \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add command -label {Revert Changes} \ - -command do_revert_selection \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label {Revert Changes} \ + -command do_revert_selection \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] -.mbar.commit add separator + .mbar.commit add separator -.mbar.commit add command -label {Sign Off} \ - -command do_signoff \ - -accelerator $M1T-S \ - -font font_ui + .mbar.commit add command -label {Sign Off} \ + -command do_signoff \ + -accelerator $M1T-S \ + -font font_ui -.mbar.commit add command -label Commit \ - -command do_commit \ - -accelerator $M1T-Return \ - -font font_ui -lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] + .mbar.commit add command -label Commit \ + -command do_commit \ + -accelerator $M1T-Return \ + -font font_ui + lappend disable_on_lock \ + [list .mbar.commit entryconf [.mbar.commit index last] -state] +} if {[is_MacOSX]} { # -- Apple Menu (Mac OS X only) @@ -5218,6 +5247,34 @@ if {$browser ne {}} { } unset browser doc_path doc_url +# -- Standard bindings +# +bind . do_quit +bind all <$M1B-Key-q> do_quit +bind all <$M1B-Key-Q> do_quit +bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} +bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} + +# -- Not a normal commit type invocation? Do that instead! +# +switch -- [lindex $argv 0] { +blame { + if {[llength $argv] == 3} { + set current_branch [lindex $argv 1] + show_blame $current_branch [lindex $argv 2] + return + } else { + puts stderr "usage: $argv0 blame commit path" + exit 1 + } +} +{} {} +default { + puts stderr "usage: $argv0 \[{blame}\]" + exit 1 +} +} + # -- Branch Control # frame .branch \ @@ -5714,7 +5771,6 @@ if {[is_enabled branch]} { bind . <$M1B-Key-N> do_create_branch } -bind . do_quit bind all do_rescan bind all <$M1B-Key-r> do_rescan bind all <$M1B-Key-R> do_rescan @@ -5723,10 +5779,6 @@ bind . <$M1B-Key-S> do_signoff bind . <$M1B-Key-i> do_add_all bind . <$M1B-Key-I> do_add_all bind . <$M1B-Key-Return> do_commit -bind all <$M1B-Key-q> do_quit -bind all <$M1B-Key-Q> do_quit -bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} -bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} foreach i [list $ui_index $ui_workdir] { bind $i "toggle_or_diff $i %x %y; break" bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break" -- cgit v1.2.3 From 258871d3058a3117d204009b7173bf8c7df2414d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 19:41:32 -0500 Subject: git-gui: Select subcommands like git does. If we are invoked as `git-foo`, then we should run the `foo` subcommand, as the user has made some sort of link from `git-foo` to our actual program code. So we should honor their request. If we are invoked as `git-gui foo`, the user has not made a link (or did, but is not using it right now) so we should execute the `foo` subcommand. We now can start the single commit UI mode via `git-citool` and also through `git gui citool`. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 66d85dc6d0..ff5f5a763d 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -4917,20 +4917,32 @@ apply_config ## ## feature option selection +if {[regexp {^git-(.+)$} [appname] _junk subcommand]} { + unset _junk +} else { + set subcommand gui +} +if {$subcommand eq {gui.sh}} { + set subcommand gui +} +if {$subcommand eq {gui} && [llength $argv] > 0} { + set subcommand [lindex $argv 0] + set argv [lrange $argv 1 end] +} + enable_option multicommit enable_option branch enable_option transport -if {[appname] eq {git-citool}} { - enable_option singlecommit - +switch -- $subcommand { +blame { disable_option multicommit disable_option branch disable_option transport } +citool { + enable_option singlecommit -switch -- [lindex $argv 0] { -blame { disable_option multicommit disable_option branch disable_option transport @@ -5257,20 +5269,30 @@ bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} # -- Not a normal commit type invocation? Do that instead! # -switch -- [lindex $argv 0] { +switch -- $subcommand { blame { - if {[llength $argv] == 3} { - set current_branch [lindex $argv 1] - show_blame $current_branch [lindex $argv 2] - return - } else { + if {[llength $argv] != 2} { puts stderr "usage: $argv0 blame commit path" exit 1 } + set current_branch [lindex $argv 0] + show_blame $current_branch [lindex $argv 1] + return +} +citool - +gui { + if {[llength $argv] != 0} { + puts -nonewline stderr "usage: $argv0" + if {$subcommand ne {gui} && [appname] ne "git-$subcommand"} { + puts -nonewline stderr " $subcommand" + } + puts stderr {} + exit 1 + } + # fall through to setup UI for commits } -{} {} default { - puts stderr "usage: $argv0 \[{blame}\]" + puts stderr "usage: $argv0 \[{blame|citool}\]" exit 1 } } -- cgit v1.2.3 From 24d2bf2f02c104b8402e2c19b383aecd1e2ccf17 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 19:44:49 -0500 Subject: git-gui: Relabel the Add All action. One user that I spoke with recently was confused why the 'Add All' button did not add all of his 'Changed But Not Updated' files. The particular files in question were new, and thus not known to Git. Since the 'Add All' routine only updates files which are already tracked, they were not added automatically. I suspect that calling this action 'Add Existing' would be less confusing, so I'm renaming it. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ff5f5a763d..6dcd64478e 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -5124,7 +5124,7 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] - .mbar.commit add command -label {Add All To Commit} \ + .mbar.commit add command -label {Add Existing To Commit} \ -command do_add_all \ -accelerator $M1T-I \ -font font_ui @@ -5423,7 +5423,7 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.rescan conf -state} -button .vpane.lower.commarea.buttons.incall -text {Add All} \ +button .vpane.lower.commarea.buttons.incall -text {Add Existing} \ -command do_add_all \ -font font_ui pack .vpane.lower.commarea.buttons.incall -side top -fill x -- cgit v1.2.3 From df6287ecd78206095796512bca80e87cc5a5e33b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 19:53:36 -0500 Subject: git-gui: Use git-config now over git-repo-config. Now that core Git has "renamed" git-repo-config to git-config, we should do the same. I don't know how long core Git will keep the repo-config command, and since git-gui's userbase is so small and almost entirely on some flavor of 1.5.0-rc2 or later, where the rename has already taken place, it should be OK to rename now. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 6dcd64478e..ae22338be3 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -140,7 +140,7 @@ proc load_config {include_global} { array unset global_config if {$include_global} { catch { - set fd_rc [open "| git repo-config --global --list" r] + set fd_rc [open "| git config --global --list" r] while {[gets $fd_rc line] >= 0} { if {[regexp {^([^=]+)=(.*)$} $line line name value]} { if {[is_many_config $name]} { @@ -156,7 +156,7 @@ proc load_config {include_global} { array unset repo_config catch { - set fd_rc [open "| git repo-config --list" r] + set fd_rc [open "| git config --list" r] while {[gets $fd_rc line] >= 0} { if {[regexp {^([^=]+)=(.*)$} $line line name value]} { if {[is_many_config $name]} { @@ -202,14 +202,14 @@ proc save_config {} { set value $global_config_new($name) if {$value ne $global_config($name)} { if {$value eq $default_config($name)} { - catch {exec git repo-config --global --unset $name} + catch {exec git config --global --unset $name} } else { regsub -all "\[{}\]" $value {"} value - exec git repo-config --global $name $value + exec git config --global $name $value } set global_config($name) $value if {$value eq $repo_config($name)} { - catch {exec git repo-config --unset $name} + catch {exec git config --unset $name} set repo_config($name) $value } } @@ -219,10 +219,10 @@ proc save_config {} { set value $repo_config_new($name) if {$value ne $repo_config($name)} { if {$value eq $global_config($name)} { - catch {exec git repo-config --unset $name} + catch {exec git config --unset $name} } else { regsub -all "\[{}\]" $value {"} value - exec git repo-config $name $value + exec git config $name $value } set repo_config($name) $value } @@ -4151,7 +4151,7 @@ proc do_quit {} { set rc_geometry {} } if {$cfg_geometry ne $rc_geometry} { - catch {exec git repo-config gui.geometry $cfg_geometry} + catch {exec git config gui.geometry $cfg_geometry} } } -- cgit v1.2.3 From 6910ae80d0e84f03151473ee55055c46b5480ec5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 21:39:27 -0500 Subject: git-gui: Redesign the display of annotated files. Using 180 columns worth of screen space to display just 20 columns of file data and 160 columns worth of annotation information is not practically useful. Users need/want to see the file data, and have the anotation associated with it displayed in a detail pane only when they have focused on a particular region of the file. Now our file viewer has a small 10-line high pane below the file which shows the commit message for the commit this line was blamed on. The columns have all been removed, except the current line number column as that has some real value when trying to locate an interesting block. To keep the user entertained we have a progress meter in the status bar of the viewer which lets them know how many lines have been annotated, and how much has been completed. We use a grey background on the line numbers for lines which we have obtained annotation from, and we color all lines in the current commit with a yellow background, so they stand out when scanning through the file. All other lines are kept with a white background, making the yellow really pop. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 359 +++++++++++++++++++++++++++---------------------------------- 1 file changed, 160 insertions(+), 199 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index ae22338be3..193b46d7f7 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3235,7 +3235,6 @@ proc show_blame {commit path} { set tl . } set blame_status($w) {Loading current file content...} - set texts [list] label $w.path -text "$commit:$path" \ -anchor w \ @@ -3245,84 +3244,7 @@ proc show_blame {commit path} { -font font_uibold pack $w.path -side top -fill x - set hbg #e2effa frame $w.out - label $w.out.commit_l -text Commit \ - -relief solid \ - -borderwidth 1 \ - -background $hbg \ - -font font_uibold - text $w.out.commit_t \ - -background white -borderwidth 0 \ - -state disabled \ - -wrap none \ - -height 40 \ - -width 9 \ - -font font_diff - lappend texts $w.out.commit_t - - label $w.out.author_l -text Author \ - -relief solid \ - -borderwidth 1 \ - -background $hbg \ - -font font_uibold - text $w.out.author_t \ - -background white -borderwidth 0 \ - -state disabled \ - -wrap none \ - -height 40 \ - -width 20 \ - -font font_diff - lappend texts $w.out.author_t - - label $w.out.date_l -text Date \ - -relief solid \ - -borderwidth 1 \ - -background $hbg \ - -font font_uibold - text $w.out.date_t \ - -background white -borderwidth 0 \ - -state disabled \ - -wrap none \ - -height 40 \ - -width [string length "yyyy-mm-dd hh:mm:ss"] \ - -font font_diff - lappend texts $w.out.date_t - - label $w.out.filename_l -text Filename \ - -relief solid \ - -borderwidth 1 \ - -background $hbg \ - -font font_uibold - text $w.out.filename_t \ - -background white -borderwidth 0 \ - -state disabled \ - -wrap none \ - -height 40 \ - -width 20 \ - -font font_diff - lappend texts $w.out.filename_t - - label $w.out.origlinenumber_l -text {Orig Line} \ - -relief solid \ - -borderwidth 1 \ - -background $hbg \ - -font font_uibold - text $w.out.origlinenumber_t \ - -background white -borderwidth 0 \ - -state disabled \ - -wrap none \ - -height 40 \ - -width 5 \ - -font font_diff - $w.out.origlinenumber_t tag conf linenumber -justify right - lappend texts $w.out.origlinenumber_t - - label $w.out.linenumber_l -text {Curr Line} \ - -relief solid \ - -borderwidth 1 \ - -background $hbg \ - -font font_uibold text $w.out.linenumber_t \ -background white -borderwidth 0 \ -state disabled \ @@ -3330,14 +3252,9 @@ proc show_blame {commit path} { -height 40 \ -width 5 \ -font font_diff + $w.out.linenumber_t tag conf annotated -background grey $w.out.linenumber_t tag conf linenumber -justify right - lappend texts $w.out.linenumber_t - label $w.out.file_l -text {File Content} \ - -relief solid \ - -borderwidth 1 \ - -background $hbg \ - -font font_uibold text $w.out.file_t \ -background white -borderwidth 0 \ -state disabled \ @@ -3346,22 +3263,17 @@ proc show_blame {commit path} { -width 80 \ -xscrollcommand [list $w.out.sbx set] \ -font font_diff - lappend texts $w.out.file_t scrollbar $w.out.sbx -orient h -command [list $w.out.file_t xview] scrollbar $w.out.sby -orient v \ - -command [list scrollbar2many $texts yview] - set labels [list] - foreach i $texts { - regsub {_t$} $i _l l - lappend labels $l - } - set file_col [expr {[llength $texts] - 1}] - eval grid $labels -sticky we - eval grid $texts $w.out.sby -sticky nsew - grid conf $w.out.sbx -column $file_col -sticky we - grid columnconfigure $w.out $file_col -weight 1 - grid rowconfigure $w.out 1 -weight 1 + -command [list scrollbar2many [list \ + $w.out.linenumber_t \ + $w.out.file_t \ + ] yview] + grid $w.out.linenumber_t $w.out.file_t $w.out.sby -sticky nsew + grid conf $w.out.sbx -column 1 -sticky we + grid columnconfigure $w.out 1 -weight 1 + grid rowconfigure $w.out 0 -weight 1 pack $w.out -fill both -expand 1 label $w.status -textvariable blame_status($w) \ @@ -3372,18 +3284,45 @@ proc show_blame {commit path} { -font font_ui pack $w.status -side bottom -fill x + frame $w.cm + text $w.cm.t \ + -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 10 \ + -width 80 \ + -xscrollcommand [list $w.cm.sbx set] \ + -yscrollcommand [list $w.cm.sby set] \ + -font font_diff + scrollbar $w.cm.sbx -orient h -command [list $w.cm.t xview] + scrollbar $w.cm.sby -orient v -command [list $w.cm.t yview] + pack $w.cm.sby -side right -fill y + pack $w.cm.sbx -side bottom -fill x + pack $w.cm.t -expand 1 -fill both + pack $w.cm -side bottom -fill x + menu $w.ctxm -tearoff 0 $w.ctxm add command -label "Copy Commit" \ -font font_ui \ -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY" - foreach i $texts { + foreach i [list $w.out.linenumber_t $w.out.file_t] { $i tag conf in_sel \ -background [$i cget -foreground] \ -foreground [$i cget -background] $i conf -yscrollcommand \ - [list many2scrollbar $texts yview $w.out.sby] - bind $i "blame_highlight $i @%x,%y $texts;break" + [list many2scrollbar [list \ + $w.out.linenumber_t \ + $w.out.file_t \ + ] yview $w.out.sby] + bind $i " + blame_highlight {$w} \\ + $w.cm.t \\ + $w.out.linenumber_t \\ + $w.out.file_t \\ + $i @%x,%y + break + " bind_button3 $i " set cursorX %x set cursorY %y @@ -3392,8 +3331,6 @@ proc show_blame {commit path} { " } - set blame_data($w,colors) {} - bind $tl "focus $tl" bind $tl " array unset blame_status {$w} @@ -3402,15 +3339,18 @@ proc show_blame {commit path} { wm title $tl "[appname] ([reponame]): File Viewer" set blame_data($w,total_lines) 0 + set blame_data($w,blame_lines) 0 + set blame_data($w,highlight_commit) {} + set blame_data($w,highlight_line) -1 set cmd [list git cat-file blob "$commit:$path"] set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [list read_blame_catfile \ $fd $w $commit $path \ - $texts $w.out.linenumber_t $w.out.file_t] + $w.cm.t $w.out.linenumber_t $w.out.file_t] } -proc read_blame_catfile {fd w commit path texts w_lno w_file} { +proc read_blame_catfile {fd w commit path w_cmit w_line w_file} { global blame_status blame_data if {![winfo exists $w_file]} { @@ -3419,55 +3359,38 @@ proc read_blame_catfile {fd w commit path texts w_lno w_file} { } set n $blame_data($w,total_lines) - foreach i $texts {$i conf -state normal} + $w_line conf -state normal + $w_file conf -state normal while {[gets $fd line] >= 0} { regsub "\r\$" $line {} line incr n - $w_lno insert end $n linenumber - $w_file insert end $line - foreach i $texts {$i insert end "\n"} + $w_line insert end "$n\n" linenumber + $w_file insert end "$line\n" } - foreach i $texts {$i conf -state disabled} + $w_line conf -state disabled + $w_file conf -state disabled set blame_data($w,total_lines) $n if {[eof $fd]} { close $fd - set blame_status($w) {Loading annotations...} + blame_incremental_status $w set cmd [list git blame -M -C --incremental] lappend cmd $commit -- $path set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary - set handler [list read_blame_incremental $fd $w] - append handler " $texts" - fileevent $fd readable $handler + fileevent $fd readable [list read_blame_incremental $fd $w \ + $w_cmit $w_line $w_file] } } -proc read_blame_incremental {fd w - w_commit w_author w_date w_filename w_olno - w_lno w_file} { +proc read_blame_incremental {fd w w_cmit w_line w_file} { global blame_status blame_data - if {![winfo exists $w_commit]} { + if {![winfo exists $w_file]} { catch {close $fd} return } - set all [list \ - $w_commit \ - $w_author \ - $w_date \ - $w_filename \ - $w_olno \ - $w_lno \ - $w_file] - - $w_commit conf -state normal - $w_author conf -state normal - $w_date conf -state normal - $w_filename conf -state normal - $w_olno conf -state normal - while {[gets $fd line] >= 0} { if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \ cmit original_line final_line line_count]} { @@ -3477,102 +3400,140 @@ proc read_blame_incremental {fd w set blame_data($w,line_count) $line_count if {[catch {set g $blame_data($w,$cmit,seen)}]} { - if {$blame_data($w,colors) eq {}} { - set blame_data($w,colors) { - yellow - red - pink - orange - green - grey - } - } - set c [lindex $blame_data($w,colors) 0] - set blame_data($w,colors) \ - [lrange $blame_data($w,colors) 1 end] - foreach t $all { - $t tag conf g$cmit -background $c - } - } else { + $w_line tag conf g$cmit + $w_file tag conf g$cmit + $w_line tag raise in_sel + $w_file tag raise in_sel set blame_data($w,$cmit,seen) 1 } } elseif {[string match {filename *} $line]} { + set file [string range $line 9 end] set n $blame_data($w,line_count) set lno $blame_data($w,final_line) - set ol $blame_data($w,original_line) - set file [string range $line 9 end] set cmit $blame_data($w,commit) - set abbrev [string range $cmit 0 8] - - if {[catch {set author $blame_data($w,$cmit,author)} err]} { - set author {} - } - - if {[catch {set atime $blame_data($w,$cmit,author-time)}]} { - set atime {} - } else { - set atime [clock format $atime -format {%Y-%m-%d %T}] - } while {$n > 0} { - if {![catch {set g g$blame_data($w,line$lno,commit)}]} { - foreach t $all { - $t tag remove $g $lno.0 "$lno.0 lineend + 1c" - } - } - - foreach t [list \ - $w_commit \ - $w_author \ - $w_date \ - $w_filename \ - $w_olno] { - $t delete $lno.0 "$lno.0 lineend" + if {[catch {set g g$blame_data($w,line$lno,commit)}]} { + $w_line tag add annotated $lno.0 "$lno.0 lineend + 1c" + } else { + $w_line tag remove g$g $lno.0 "$lno.0 lineend + 1c" + $w_file tag remove g$g $lno.0 "$lno.0 lineend + 1c" } - $w_commit insert $lno.0 $abbrev - $w_author insert $lno.0 $author - $w_date insert $lno.0 $atime - $w_filename insert $lno.0 $file - $w_olno insert $lno.0 $ol linenumber + set blame_data($w,line$lno,commit) $cmit + set blame_data($w,line$lno,file) $file + $w_line tag add g$cmit $lno.0 "$lno.0 lineend + 1c" + $w_file tag add g$cmit $lno.0 "$lno.0 lineend + 1c" - set g g$cmit - foreach t $all { - $t tag add $g $lno.0 "$lno.0 lineend + 1c" + if {$blame_data($w,highlight_line) == $lno} { + blame_showcommit $w $w_cmit $w_line $w_file $lno } - set blame_data($w,line$lno,commit) $cmit - incr n -1 incr lno - incr ol + incr blame_data($w,blame_lines) } } elseif {[regexp {^([a-z-]+) (.*)$} $line line header data]} { set blame_data($w,$blame_data($w,commit),$header) $data } } - $w_commit conf -state disabled - $w_author conf -state disabled - $w_date conf -state disabled - $w_filename conf -state disabled - $w_olno conf -state disabled - if {[eof $fd]} { close $fd set blame_status($w) {Annotation complete.} + } else { + blame_incremental_status $w } } -proc blame_highlight {w pos args} { - set lno [lindex [split [$w index $pos] .] 0] - foreach i $args { - $i tag remove in_sel 0.0 end - } +proc blame_incremental_status {w} { + global blame_status blame_data + + set blame_status($w) [format \ + "Loading annotations... %i of %i lines annotated (%2i%%)" \ + $blame_data($w,blame_lines) \ + $blame_data($w,total_lines) \ + [expr {100 * $blame_data($w,blame_lines) + / $blame_data($w,total_lines)}]] +} + +proc blame_highlight {w w_cmit w_line w_file cur_w pos} { + set lno [lindex [split [$cur_w index $pos] .] 0] if {$lno eq {}} return - foreach i $args { - $i tag add in_sel $lno.0 "$lno.0 + 1 line" + + $w_line tag remove in_sel 0.0 end + $w_file tag remove in_sel 0.0 end + $w_line tag add in_sel $lno.0 "$lno.0 + 1 line" + $w_file tag add in_sel $lno.0 "$lno.0 + 1 line" + + blame_showcommit $w $w_cmit $w_line $w_file $lno +} + +proc blame_showcommit {w w_cmit w_line w_file lno} { + global blame_data repo_config + + set cmit $blame_data($w,highlight_commit) + if {$cmit ne {}} { + $w_line tag conf g$cmit -background white + $w_file tag conf g$cmit -background white + $w_line tag raise annotated g$cmit } + + $w_cmit conf -state normal + $w_cmit delete 0.0 end + if {[catch {set cmit $blame_data($w,line$lno,commit)}]} { + set cmit {} + $w_cmit insert end "Computing..." + } else { + $w_line tag conf g$cmit -background yellow + $w_file tag conf g$cmit -background yellow + $w_line tag raise g$cmit annotated + + if {[catch {set msg $blame_data($w,$cmit,message)}]} { + set msg {} + catch { + set fd [open "| git cat-file commit $cmit" r] + fconfigure $fd -encoding binary -translation lf + if {[catch {set enc $repo_config(i18n.commitencoding)}]} { + set enc utf-8 + } + while {[gets $fd line] > 0} { + if {[string match {encoding *} $line]} { + set enc [string tolower [string range $line 9 end]] + } + } + fconfigure $fd -encoding $enc + set msg [string trim [read $fd]] + close $fd + } + set blame_data($w,$cmit,message) $msg + } + + set author_name {} + set author_email {} + set author_time {} + catch {set author_name $blame_data($w,$cmit,author)} + catch {set author_email $blame_data($w,$cmit,author-mail)} + catch {set author_time [clock format $blame_data($w,$cmit,author-time)]} + + set committer_name {} + set committer_email {} + set committer_time {} + catch {set committer_name $blame_data($w,$cmit,committer)} + catch {set committer_email $blame_data($w,$cmit,committer-mail)} + catch {set committer_time [clock format $blame_data($w,$cmit,committer-time)]} + + $w_cmit insert end "commit $cmit\n" + $w_cmit insert end "Author: $author_name $author_email $author_time\n" + $w_cmit insert end "Committer: $committer_name $committer_email $committer_time\n" + $w_cmit insert end "Original File: [escape_path $blame_data($w,line$lno,file)]\n" + $w_cmit insert end "\n" + $w_cmit insert end $msg + } + $w_cmit conf -state disabled + + set blame_data($w,highlight_line) $lno + set blame_data($w,highlight_commit) $cmit } proc blame_copycommit {w i pos} { -- cgit v1.2.3 From 1351ba13e599ef80bbfd9f9ef4ff22117418f030 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 22:41:51 -0500 Subject: git-gui: Jump to the first annotation block as soon as its available. To help clue users into the fact that annotation data arrives incrementally, and that they should try to locate the region they want while the tool is running, we jump to the first line of the first annotation if the user has not already clicked on a line they are interested in and if the window is still looking at the very top of the file. Since it takes a second (at least on my PowerBook) to even generate the first annotation for git-gui.sh, the user should have plenty of time to adjust the scrollbar or click on a line even before we get that first annotation record in, which allows the user to bypass our automatic jumping. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 193b46d7f7..53e890af0f 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3425,7 +3425,12 @@ proc read_blame_incremental {fd w w_cmit w_line w_file} { $w_line tag add g$cmit $lno.0 "$lno.0 lineend + 1c" $w_file tag add g$cmit $lno.0 "$lno.0 lineend + 1c" - if {$blame_data($w,highlight_line) == $lno} { + if {$blame_data($w,highlight_line) == -1} { + if {[lindex [$w_file yview] 0] == 0} { + $w_file see $lno.0 + blame_showcommit $w $w_cmit $w_line $w_file $lno + } + } elseif {$blame_data($w,highlight_line) == $lno} { blame_showcommit $w $w_cmit $w_line $w_file $lno } -- cgit v1.2.3 From 486ef5270ce3162ee14634db825884cc466a32bf Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 9 Feb 2007 01:59:38 -0500 Subject: git-gui: Improve annotated file display. Rather than trying to mark the background color of the line numbers to show which lines have annotated data loaded, we now show a ruler between the line numbers and the file data. This ruler is just 1 character wide and its background color is set to grey to denote which lines have annotation ready. I had to make this change as I kept loosing the annotation marker when a line was no longer colored as part of the current selection. We now color the lines blamed on the current commit in yellow, the lines in the commit which came after (descendant) in red (hotter, less tested) and the lines in the commit before (ancestor) in blue (cooler, better tested). Signed-off-by: Shawn O. Pearce --- git-gui.sh | 94 +++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 53e890af0f..d3cf57af5a 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3245,6 +3245,15 @@ proc show_blame {commit path} { pack $w.path -side top -fill x frame $w.out + text $w.out.loaded_t \ + -background white -borderwidth 0 \ + -state disabled \ + -wrap none \ + -height 40 \ + -width 1 \ + -font font_diff + $w.out.loaded_t tag conf annotated -background grey + text $w.out.linenumber_t \ -background white -borderwidth 0 \ -state disabled \ @@ -3252,7 +3261,6 @@ proc show_blame {commit path} { -height 40 \ -width 5 \ -font font_diff - $w.out.linenumber_t tag conf annotated -background grey $w.out.linenumber_t tag conf linenumber -justify right text $w.out.file_t \ @@ -3267,12 +3275,18 @@ proc show_blame {commit path} { scrollbar $w.out.sbx -orient h -command [list $w.out.file_t xview] scrollbar $w.out.sby -orient v \ -command [list scrollbar2many [list \ + $w.out.loaded_t \ $w.out.linenumber_t \ $w.out.file_t \ ] yview] - grid $w.out.linenumber_t $w.out.file_t $w.out.sby -sticky nsew - grid conf $w.out.sbx -column 1 -sticky we - grid columnconfigure $w.out 1 -weight 1 + grid \ + $w.out.linenumber_t \ + $w.out.loaded_t \ + $w.out.file_t \ + $w.out.sby \ + -sticky nsew + grid conf $w.out.sbx -column 2 -sticky we + grid columnconfigure $w.out 2 -weight 1 grid rowconfigure $w.out 0 -weight 1 pack $w.out -fill both -expand 1 @@ -3306,17 +3320,21 @@ proc show_blame {commit path} { -font font_ui \ -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY" - foreach i [list $w.out.linenumber_t $w.out.file_t] { + foreach i [list \ + $w.out.loaded_t \ + $w.out.linenumber_t \ + $w.out.file_t] { $i tag conf in_sel \ -background [$i cget -foreground] \ -foreground [$i cget -background] $i conf -yscrollcommand \ [list many2scrollbar [list \ + $w.out.loaded_t \ $w.out.linenumber_t \ $w.out.file_t \ ] yview $w.out.sby] bind $i " - blame_highlight {$w} \\ + blame_click {$w} \\ $w.cm.t \\ $w.out.linenumber_t \\ $w.out.file_t \\ @@ -3338,19 +3356,22 @@ proc show_blame {commit path} { " wm title $tl "[appname] ([reponame]): File Viewer" + set blame_data($w,commit_count) 0 + set blame_data($w,commit_list) {} set blame_data($w,total_lines) 0 set blame_data($w,blame_lines) 0 set blame_data($w,highlight_commit) {} set blame_data($w,highlight_line) -1 + set cmd [list git cat-file blob "$commit:$path"] set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [list read_blame_catfile \ $fd $w $commit $path \ - $w.cm.t $w.out.linenumber_t $w.out.file_t] + $w.cm.t $w.out.loaded_t $w.out.linenumber_t $w.out.file_t] } -proc read_blame_catfile {fd w commit path w_cmit w_line w_file} { +proc read_blame_catfile {fd w commit path w_cmit w_load w_line w_file} { global blame_status blame_data if {![winfo exists $w_file]} { @@ -3359,14 +3380,17 @@ proc read_blame_catfile {fd w commit path w_cmit w_line w_file} { } set n $blame_data($w,total_lines) + $w_load conf -state normal $w_line conf -state normal $w_file conf -state normal while {[gets $fd line] >= 0} { regsub "\r\$" $line {} line incr n + $w_load insert end "\n" $w_line insert end "$n\n" linenumber $w_file insert end "$line\n" } + $w_load conf -state disabled $w_line conf -state disabled $w_file conf -state disabled set blame_data($w,total_lines) $n @@ -3379,11 +3403,11 @@ proc read_blame_catfile {fd w commit path w_cmit w_line w_file} { set fd [open "| $cmd" r] fconfigure $fd -blocking 0 -translation lf -encoding binary fileevent $fd readable [list read_blame_incremental $fd $w \ - $w_cmit $w_line $w_file] + $w_load $w_cmit $w_line $w_file] } } -proc read_blame_incremental {fd w w_cmit w_line w_file} { +proc read_blame_incremental {fd w w_load w_cmit w_line w_file} { global blame_status blame_data if {![winfo exists $w_file]} { @@ -3399,12 +3423,14 @@ proc read_blame_incremental {fd w w_cmit w_line w_file} { set blame_data($w,final_line) $final_line set blame_data($w,line_count) $line_count - if {[catch {set g $blame_data($w,$cmit,seen)}]} { + if {[catch {set g $blame_data($w,$cmit,order)}]} { $w_line tag conf g$cmit $w_file tag conf g$cmit $w_line tag raise in_sel $w_file tag raise in_sel - set blame_data($w,$cmit,seen) 1 + set blame_data($w,$cmit,order) $blame_data($w,commit_count) + incr blame_data($w,commit_count) + lappend blame_data($w,commit_list) $cmit } } elseif {[string match {filename *} $line]} { set file [string range $line 9 end] @@ -3414,7 +3440,7 @@ proc read_blame_incremental {fd w w_cmit w_line w_file} { while {$n > 0} { if {[catch {set g g$blame_data($w,line$lno,commit)}]} { - $w_line tag add annotated $lno.0 "$lno.0 lineend + 1c" + $w_load tag add annotated $lno.0 "$lno.0 lineend + 1c" } else { $w_line tag remove g$g $lno.0 "$lno.0 lineend + 1c" $w_file tag remove g$g $lno.0 "$lno.0 lineend + 1c" @@ -3438,6 +3464,14 @@ proc read_blame_incremental {fd w w_cmit w_line w_file} { incr lno incr blame_data($w,blame_lines) } + + set hc $blame_data($w,highlight_commit) + if {$hc ne {} + && [expr {$blame_data($w,$hc,order) + 1}] + == $blame_data($w,$cmit,order)} { + blame_showcommit $w $w_cmit $w_line $w_file \ + $blame_data($w,highlight_line) + } } elseif {[regexp {^([a-z-]+) (.*)$} $line line header data]} { set blame_data($w,$blame_data($w,commit),$header) $data } @@ -3462,7 +3496,7 @@ proc blame_incremental_status {w} { / $blame_data($w,total_lines)}]] } -proc blame_highlight {w w_cmit w_line w_file cur_w pos} { +proc blame_click {w w_cmit w_line w_file cur_w pos} { set lno [lindex [split [$cur_w index $pos] .] 0] if {$lno eq {}} return @@ -3474,25 +3508,41 @@ proc blame_highlight {w w_cmit w_line w_file cur_w pos} { blame_showcommit $w $w_cmit $w_line $w_file $lno } +set blame_colors { + #ff4040 + #ff40ff + #4040ff +} + proc blame_showcommit {w w_cmit w_line w_file lno} { - global blame_data repo_config + global blame_colors blame_data repo_config set cmit $blame_data($w,highlight_commit) if {$cmit ne {}} { - $w_line tag conf g$cmit -background white - $w_file tag conf g$cmit -background white - $w_line tag raise annotated g$cmit + set idx $blame_data($w,$cmit,order) + set i 0 + foreach c $blame_colors { + set h [lindex $blame_data($w,commit_list) [expr {$idx - 1 + $i}]] + $w_line tag conf g$h -background white + $w_file tag conf g$h -background white + incr i + } } $w_cmit conf -state normal $w_cmit delete 0.0 end if {[catch {set cmit $blame_data($w,line$lno,commit)}]} { set cmit {} - $w_cmit insert end "Computing..." + $w_cmit insert end "Loading annotation..." } else { - $w_line tag conf g$cmit -background yellow - $w_file tag conf g$cmit -background yellow - $w_line tag raise g$cmit annotated + set idx $blame_data($w,$cmit,order) + set i 0 + foreach c $blame_colors { + set h [lindex $blame_data($w,commit_list) [expr {$idx - 1 + $i}]] + $w_line tag conf g$h -background $c + $w_file tag conf g$h -background $c + incr i + } if {[catch {set msg $blame_data($w,$cmit,message)}]} { set msg {} -- cgit v1.2.3 From d585e782b0bfb11173028091cb11c8459766135c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 9 Feb 2007 02:28:32 -0500 Subject: git-gui: Focus into blame panels on Mac OS. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index d3cf57af5a..f44e549099 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -3339,7 +3339,7 @@ proc show_blame {commit path} { $w.out.linenumber_t \\ $w.out.file_t \\ $i @%x,%y - break + focus $i " bind_button3 $i " set cursorX %x @@ -3349,6 +3349,7 @@ proc show_blame {commit path} { " } + bind $w.cm.t "focus $w.cm.t" bind $tl "focus $tl" bind $tl " array unset blame_status {$w} @@ -3428,6 +3429,7 @@ proc read_blame_incremental {fd w w_load w_cmit w_line w_file} { $w_file tag conf g$cmit $w_line tag raise in_sel $w_file tag raise in_sel + $w_file tag raise sel set blame_data($w,$cmit,order) $blame_data($w,commit_count) incr blame_data($w,commit_count) lappend blame_data($w,commit_list) $cmit -- cgit v1.2.3 From 0960f7d6db89fa8418486cd088759e54dd51d54e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 11 Feb 2007 17:19:38 -0500 Subject: git-gui: Stop deleting gitk preferences. Now that git 1.5.0 and later contains a version of gitk that uses correct geometry on Windows platforms, even if ~/.gitk exists, we should not delete the user's ~/.gitk to work around the bug. It is downright mean to remove a user's preferences for another app. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index f44e549099..ea16be43ac 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -4015,14 +4015,6 @@ set starting_gitk_msg {Starting gitk... please wait...} proc do_gitk {revs} { global env ui_status_value starting_gitk_msg - # -- On Windows gitk is severly broken, and right now it seems like - # nobody cares about fixing it. The only known workaround is to - # always delete ~/.gitk before starting the program. - # - if {[is_Windows]} { - catch {file delete [file join $env(HOME) .gitk]} - } - # -- Always start gitk through whatever we were loaded with. This # lets us bypass using shell process on Windows systems. # -- cgit v1.2.3