{"thread":{"id":"44710","subject":"[PATCH 1/6] Enable ability to visualise the results of git cherry C1 C2","startedAt":"2016-12-15T11:29:00Z","lastAt":"2016-12-31T11:04:17Z","messageCount":9,"participants":["Pierre Dumuid","Paul Mackerras"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"307837","messageId":"20161215112847.14719-1-pmdumuid@gmail.com","threadId":"44710","inReplyTo":null,"subject":"[PATCH 1/6] Enable ability to visualise the results of git cherry C1 C2","fromName":"Pierre Dumuid","fromEmail":"pmdumuid@gmail.com","sentAt":"2016-12-15T11:28:42Z","receivedAt":"2016-12-15T11:29:00Z","isPatch":true,"sender":{"key":"pmdumuid@gmail.com","avatar":"https://gravatar.com/avatar/444092d6279d27223d36a513dc67aa506ff029da21975f874ecd41dd0f9b664a?d=mp&s=160"},"body":"It's a bit clunky but it works!!\n\nUsage:\n - mark commit one (e.g. v45)\n - Select commit two.\n - Switch the gdttype to the option, \"git-cherry between marked commit and:\"\n\nSigned-off-by: Pierre Dumuid <pmdumuid@gmail.com>\n---\n gitk | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 107 insertions(+), 3 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex a14d7a1..50d1ef4 100755\n--- a/gitk\n+++ b/gitk\n@@ -2319,7 +2319,9 @@ proc makewindow {} {\n \t\t[mc \"containing:\"] \\\n \t\t[mc \"touching paths:\"] \\\n \t\t[mc \"adding/removing string:\"] \\\n-\t\t[mc \"changing lines matching:\"]]\n+\t\t[mc \"changing lines matching:\"] \\\n+\t\t[mc \"git-cherry between marked commit and:\"] \\\n+\t       ]\n     trace add variable gdttype write gdttype_change\n     pack .tf.lbar.gdttype -side left -fill y\n \n@@ -4707,6 +4709,18 @@ proc gdttype_change {name ix op} {\n     global gdttype highlight_files findstring findpattern\n \n     stopfinding\n+\n+    if {$gdttype eq [mc \"git-cherry between marked commit and:\"]} {\n+\tif {$highlight_files ne {}} {\n+\t    set highlight_files {}\n+\t    hfiles_change\n+\t}\n+\tfindcom_change\n+\tupdate_gitcherrylist\n+\tdrawvisible\n+\treturn\n+    }\n+\n     if {$findstring ne {}} {\n \tif {$gdttype eq [mc \"containing:\"]} {\n \t    if {$highlight_files ne {}} {\n@@ -4733,6 +4747,9 @@ proc find_change {name ix op} {\n     stopfinding\n     if {$gdttype eq [mc \"containing:\"]} {\n \tfindcom_change\n+    } elseif {$gdttype eq [mc \"git-cherry between marked commit and:\"]} {\n+\tfindcom_change\n+\tupdate_gitcherrylist\n     } else {\n \tif {$highlight_files ne $findstring} {\n \t    set highlight_files $findstring\n@@ -4742,6 +4759,54 @@ proc find_change {name ix op} {\n     drawvisible\n }\n \n+proc update_gitcherrylist {} {\n+    global gitcherryids\n+    global markedid\n+    global findstring\n+    global fstring\n+    global currentid\n+    global iddrawn\n+\n+    unset -nocomplain gitcherryids\n+    set fs $findstring\n+\n+    if {$findstring eq {}} {\n+\t$fstring delete 0 end\n+\t$fstring insert 0 $currentid\n+    }\n+\n+    if {![info exists markedid]} {\n+\terror_popup [mc \"Please mark a git commit before using this find method!\"]\n+\treturn\n+    }\n+\n+    #puts [join [list \"Running cherry between: `\" $markedid \"` and `\" $findstring \"`\"] \"\"]\n+\n+    if {[catch {set cherryOutput [exec git cherry $markedid $findstring]}]} {\n+\tputs \"ERROR: An error occured running git-cherry!\"\n+\treturn\n+    }\n+\n+    set cherryLines [split $cherryOutput \"\\n\"]\n+    foreach cherryLine $cherryLines {\n+\tset op [lindex [split $cherryLine \" \"] 0]\n+\tset gitSha [lindex [split $cherryLine \" \"] 1]\n+\n+\t#puts [join [list \"line is: `\" $cherryLine \"`, op:`\" $op \"`, gitSha:`\" $gitSha \"`\"] \"\"]\n+\tif {$op eq \"+\"} {\n+\t    set gitcherryids($gitSha) 1\n+\t    if ([info exists iddrawn($gitSha)]) {\n+\t\tbolden $gitSha mainfontbold\n+\t    }\n+\n+\t}\n+    }\n+    # puts \"list is as follows\"\n+    #foreach {gitsha setBold} [array get gitcherryids] {\n+    # \tputs [concat $gitsha = $setBold]\n+    #}\n+}\n+\n proc findcom_change args {\n     global nhighlights boldnameids\n     global findpattern findtype findstring gdttype\n@@ -4802,6 +4867,9 @@ proc do_file_hl {serial} {\n \tset gdtargs [list \"-S$highlight_files\"]\n     } elseif {$gdttype eq [mc \"changing lines matching:\"]} {\n \tset gdtargs [list \"-G$highlight_files\"]\n+    } elseif {$gdttype eq [mc \"git-cherry between marked commit and:\"]} {\n+\t# Skipping opening the file handle, filehighlight\n+\treturn\n     } else {\n \t# must be \"containing:\", i.e. we're searching commit info\n \treturn\n@@ -4882,6 +4950,17 @@ proc doesmatch {f} {\n     }\n }\n \n+proc askcherryhighlight {row id} {\n+    global nhighlights gitcherryids\n+\n+    set isbold 0\n+    if {[info exists gitcherryids($id)]} {\n+\tset isbold 1\n+    }\n+\n+    set nhighlights($id) $isbold\n+}\n+\n proc askfindhighlight {row id} {\n     global nhighlights commitinfo iddrawn\n     global findloc\n@@ -6216,6 +6295,7 @@ proc drawcmitrow {row} {\n     global filehighlight fhighlights findpattern nhighlights\n     global hlview vhighlights\n     global highlight_related rhighlights\n+    global gdttype\n \n     if {$row >= $numcommits} return\n \n@@ -6226,6 +6306,11 @@ proc drawcmitrow {row} {\n     if {[info exists filehighlight] && ![info exists fhighlights($id)]} {\n \taskfilehighlight $row $id\n     }\n+\n+    if {$gdttype eq [mc \"git-cherry between marked commit and:\"] && ![info exists nhighlights($id)]} {\n+\taskcherryhighlight $row $id\n+    }\n+\n     if {$findpattern ne {} && ![info exists nhighlights($id)]} {\n \taskfindhighlight $row $id\n     }\n@@ -6776,7 +6861,9 @@ proc dofind {{dirn 1} {wrap 1}} {\n     }\n     set findcurline $findstartline\n     nowbusy finding [mc \"Searching\"]\n-    if {$gdttype ne [mc \"containing:\"] && ![info exists filehighlight]} {\n+    if {$gdttype eq [mc \"git-cherry between marked commit and:\"]} {\n+\t# Don't do anything related to open do_file_hl since we'll just have a list\n+    } elseif {$gdttype ne [mc \"containing:\"] && ![info exists filehighlight]} {\n \tafter cancel do_file_hl $fh_serial\n \tdo_file_hl $fh_serial\n     }\n@@ -6803,6 +6890,7 @@ proc findmore {} {\n     global findstartline findcurline findallowwrap\n     global find_dirn gdttype fhighlights fprogcoord\n     global curview varcorder vrownum varccommits vrowmod\n+    global gitcherryids\n \n     if {![info exists find_dirn]} {\n \treturn 0\n@@ -6848,7 +6936,23 @@ proc findmore {} {\n     set arow [lindex $vrownum($curview) $ai]\n     set ids [lindex $varccommits($curview,$a)]\n     set arowend [expr {$arow + [llength $ids]}]\n-    if {$gdttype eq [mc \"containing:\"]} {\n+\n+    if {$gdttype eq [mc \"git-cherry between marked commit and:\"]} {\n+\tfor {} {$n > 0} {incr n -1; incr l $find_dirn} {\n+\t    if {$l < $arow || $l >= $arowend} {\n+\t\tincr ai $find_dirn\n+\t\tset a [lindex $varcorder($curview) $ai]\n+\t\tset arow [lindex $vrownum($curview) $ai]\n+\t\tset ids [lindex $varccommits($curview,$a)]\n+\t\tset arowend [expr {$arow + [llength $ids]}]\n+\t    }\n+\t    set id [lindex $ids [expr {$l - $arow}]]\n+\t    if {[info exists gitcherryids($id)]} {\n+\t\tset found 1\n+\t    }\n+\t    if {$found} break\n+\t}\n+    } elseif {$gdttype eq [mc \"containing:\"]} {\n \tfor {} {$n > 0} {incr n -1; incr l $find_dirn} {\n \t    if {$l < $arow || $l >= $arowend} {\n \t\tincr ai $find_dirn\n-- \n2.10.2\n\n"},{"id":"307838","messageId":"20161215112847.14719-2-pmdumuid@gmail.com","threadId":"44710","inReplyTo":"20161215112847.14719-1-pmdumuid@gmail.com","subject":"[PATCH 2/6] Add ability to follow a remote branch with a dialog","fromName":"Pierre Dumuid","fromEmail":"pmdumuid@gmail.com","sentAt":"2016-12-15T11:28:43Z","receivedAt":"2016-12-15T11:29:03Z","isPatch":true,"sender":{"key":"pmdumuid@gmail.com","avatar":"https://gravatar.com/avatar/444092d6279d27223d36a513dc67aa506ff029da21975f874ecd41dd0f9b664a?d=mp&s=160"},"body":"A suggested name is provided when creating a new \"following\" branch.\n\nSigned-off-by: Pierre Dumuid <pmdumuid@gmail.com>\n---\n gitk | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----\n 1 file changed, 82 insertions(+), 4 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex 50d1ef4..36cba49 100755\n--- a/gitk\n+++ b/gitk\n@@ -2673,6 +2673,7 @@ proc makewindow {} {\n \t{mc \"Rename this branch\" command mvbranch}\n \t{mc \"Remove this branch\" command rmbranch}\n \t{mc \"Copy branch name\" command {clipboard clear; clipboard append $headmenuhead}}\n+\t{mc \"Follow this branch\"  command follow_remote_branch_dialog}\n     }\n     $headctxmenu configure -tearoff 0\n \n@@ -9947,23 +9948,100 @@ proc headmenu {x y id head} {\n     stopfinding\n     set headmenuid $id\n     set headmenuhead $head\n-    array set state {0 normal 1 normal 2 normal}\n+    array set state {0 normal 1 normal 2 normal 3 normal}\n     if {[string match \"remotes/*\" $head]} {\n \tset localhead [string range $head [expr [string last / $head] + 1] end]\n \tif {[info exists headids($localhead)]} {\n \t    set state(0) disabled\n \t}\n-\tarray set state {1 disabled 2 disabled}\n+\tarray set state {1 disabled 2 disabled 3 normal}\n     }\n     if {$head eq $mainhead} {\n-\tarray set state {0 disabled 2 disabled}\n+\tarray set state {0 disabled 2 disabled 3 disabled}\n+    } else {\n+\tset state(3) disabled\n     }\n-    foreach i {0 1 2} {\n+    foreach i {0 1 2 3} {\n \t$headctxmenu entryconfigure $i -state $state($i)\n     }\n     tk_popup $headctxmenu $x $y\n }\n \n+proc follow_remote_branch_dialog {} {\n+    global headmenuhead NS\n+\n+    # check the tree is clean first??\n+    nowbusy createFollowingBranch [mc \"Creating following branch\"]\n+    update\n+    dohidelocalchanges\n+\n+    set top .create_following_branch\n+    catch {destroy $top}\n+    ttk_toplevel $top\n+    make_transient $top .\n+\n+    ${NS}::label $top.title -text [mc \"Create following branch\"]\n+    grid $top.title - -pady 10\n+\n+    ${NS}::label $top.remote_branch_name_label -text [mc \"Remote Branch:\"]\n+    ${NS}::entry $top.remote_branch_name -width 40\n+    $top.remote_branch_name insert 0 $headmenuhead\n+    $top.remote_branch_name conf -state readonly\n+    grid $top.remote_branch_name_label $top.remote_branch_name -sticky w\n+\n+    ${NS}::label $top.new_branch_name_label -text [mc \"Name:\"]\n+    ${NS}::entry $top.new_branch_name -width 40\n+    set suggested_name $headmenuhead\n+    regsub {^remotes/[^/]*/} $suggested_name {} suggested_name\n+    $top.new_branch_name insert 0 $suggested_name\n+    grid $top.new_branch_name_label $top.new_branch_name -sticky w\n+\n+    set actionCreate [list follow_remote_branch_callback $top]\n+    set actionCancel \"catch {notbusy createFollowingBranch; destroy $top}\"\n+\n+    ${NS}::frame $top.buts\n+    ${NS}::button $top.buts.go  -text [mc \"Create\"] -command $actionCreate\n+    ${NS}::button $top.buts.can -text [mc \"Cancel\"] -command $actionCancel\n+    grid $top.buts.go $top.buts.can\n+    grid columnconfigure $top.buts 0 -weight 1 -uniform a\n+    grid columnconfigure $top.buts 1 -weight 1 -uniform a\n+    grid $top.buts - -pady 10 -sticky ew\n+\n+    bind $top <Key-Return> $actionCreate\n+    bind $top <Key-Escape> $actionCancel\n+\n+    focus $top.new_branch_name\n+}\n+\n+proc follow_remote_branch_callback {top} {\n+    global headids idheads NS\n+    set new_branch_name    [$top.new_branch_name get]\n+    set remote_branch_name [$top.remote_branch_name get]\n+    set cmdargs {}\n+\n+    if {$new_branch_name eq {}} {\n+\terror_popup [mc \"Please specify a name for the new branch\"] $top\n+\treturn\n+    }\n+    if {[info exists headids($new_branch_name)]} {\n+\terror_popup [mc \"The branch name you specified already exists, please specify a new name\"] $top\n+\treturn\n+    }\n+    catch {destroy $top}\n+\n+    lappend cmdargs $new_branch_name $remote_branch_name\n+\n+    if {[catch {\n+\teval exec git branch --track $cmdargs\n+    } err]} {\n+\tnotbusy createFollowingBranch\n+\terror_popup $err\n+    } else {\n+\tnotbusy createFollowingBranch\n+\tupdatecommits\n+    }\n+}\n+\n proc cobranch {} {\n     global headmenuid headmenuhead headids\n     global showlocalchanges\n-- \n2.10.2\n\n"},{"id":"307839","messageId":"20161215112847.14719-3-pmdumuid@gmail.com","threadId":"44710","inReplyTo":"20161215112847.14719-1-pmdumuid@gmail.com","subject":"[PATCH 3/6] Add a tree view to the local branches, remote branches and tags, where / is treated as a directory seperator.","fromName":"Pierre Dumuid","fromEmail":"pmdumuid@gmail.com","sentAt":"2016-12-15T11:28:44Z","receivedAt":"2016-12-15T11:29:09Z","isPatch":true,"sender":{"key":"pmdumuid@gmail.com","avatar":"https://gravatar.com/avatar/444092d6279d27223d36a513dc67aa506ff029da21975f874ecd41dd0f9b664a?d=mp&s=160"},"body":"Signed-off-by: Pierre Dumuid <pmdumuid@gmail.com>\n---\n gitk | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 117 insertions(+)\n\ndiff --git a/gitk b/gitk\nindex 36cba49..a894f1d 100755\n--- a/gitk\n+++ b/gitk\n@@ -2089,6 +2089,10 @@ proc makewindow {} {\n \t    {mc \"Reread re&ferences\" command rereadrefs}\n \t    {mc \"&List references\" command showrefs -accelerator F2}\n \t    {xx \"\" separator}\n+\t    {mc \"List Local Branches\"  command {show_tree_of_references_dialog \"localBranches\"}  -accelerator F6}\n+\t    {mc \"List Remote Branches\" command {show_tree_of_references_dialog \"remoteBranches\"} -accelerator F7}\n+\t    {mc \"List Tags\"            command {show_tree_of_references_dialog \"tags\"}           -accelerator F8}\n+\t    {xx \"\" separator}\n \t    {mc \"Start git &gui\" command {exec git gui &}}\n \t    {xx \"\" separator}\n \t    {mc \"&Quit\" command doquit -accelerator Meta1-Q}\n@@ -2601,6 +2605,9 @@ proc makewindow {} {\n     bind . <F5> updatecommits\n     bindmodfunctionkey Shift 5 reloadcommits\n     bind . <F2> showrefs\n+    bind . <F6> {show_tree_of_references_dialog \"localBranches\"}\n+    bind . <F7> {show_tree_of_references_dialog \"remoteBranches\"}\n+    bind . <F8> {show_tree_of_references_dialog \"tags\"}\n     bindmodfunctionkey Shift 4 {newview 0}\n     bind . <F4> edit_or_newview\n     bind . <$M1B-q> doquit\n@@ -10146,6 +10153,116 @@ proc rmbranch {} {\n     run refill_reflist\n }\n \n+# Display a tree view of local branches, remote branches, and tags according to view_type.\n+#\n+# @param string view_type\n+#    Must be one of \"localBranches\", \"remoteBranches\", or \"tags\".\n+#\n+proc show_tree_of_references_dialog {view_type} {\n+    global NS\n+    global treefilelist\n+    global headids tagids\n+\n+    switch -- $view_type {\n+\t\"localBranches\" {\n+\t    set dialogName \"Local Branches\"\n+\t    set top .show_tree_of_local_branches\n+\t    set listOfReferences [lsort [array names headids -regexp {^(?!remotes/)} ]]\n+\t    set truncateFrom 0\n+\t}\n+\t\"remoteBranches\" {\n+\t    set dialogName \"Remote Branches\"\n+\t    set top .show_tree_of_remote_branches\n+\t    set listOfReferences [lsort [array names headids -regexp {^remotes/} ]]\n+\t    set truncateFrom 8\n+\t}\n+\t\"tags\" {\n+\t    set dialogName \"Tags\"\n+\t    set top .show_tree_of_tags\n+\t    set listOfReferences [lsort [array names tagids]]\n+\t    set truncateFrom 0\n+\t}\n+    }\n+\n+    if {[winfo exists $top]} {\n+\traise $top\n+\treturn\n+    }\n+\n+    ttk_toplevel $top\n+    wm title $top [mc \"$dialogName: %s\" [file tail [pwd]]]\n+    wm geometry $top \"600x900\"\n+\n+    make_transient $top .\n+\n+    ## See http://www.tkdocs.com/tutorial/tree.html\n+    ttk::treeview $top.referenceList -xscrollcommand \"$top.horizontalScrollBar set\" -yscrollcommand \"$top.verticalScrollBar set\"\n+\n+    # Populate the dialog\n+    foreach reference $listOfReferences {\n+\t# The display name omits some leading characters (such as \"remotes/\")\n+\tset referenceDisplayName [string range $reference $truncateFrom end]\n+\n+\t# Split the branch/tag by slashes, and incrementally ensure that each leaf in the treeview exists..\n+\t# otherwise add it.\n+\tset treeLeaves [split $referenceDisplayName \"/\"]\n+\tfor {set i 0} {$i < [llength $treeLeaves]} {} {\n+\t    set leafReferenceId [join [lrange $treeLeaves 0 $i] \"/\"]\n+\t    if {![$top.referenceList exists $leafReferenceId]} {\n+\t\tif {$i > 0} {\n+\t\t    set parentLeafId [join [lrange $treeLeaves 0 $i-1] \"/\"]\n+\t\t} else {\n+\t\t    set parentLeafId {}\n+\t\t}\n+\t\t$top.referenceList insert $parentLeafId end -id $leafReferenceId -text [lindex $treeLeaves $i]\n+\t    }\n+\t    incr i\n+\t}\n+    }\n+\n+    ${NS}::scrollbar $top.verticalScrollBar   -command \"$top.referenceList yview\" -orient vertical\n+    ${NS}::scrollbar $top.horizontalScrollBar -command \"$top.referenceList xview\" -orient horizontal\n+\n+    grid $top.referenceList $top.verticalScrollBar -sticky nsew\n+    grid $top.horizontalScrollBar x -sticky ew\n+\n+    bind $top <Key-Escape> [list destroy $top]\n+\n+    bind $top.referenceList <<TreeviewSelect>> {callback_tree_of_references_item_selected %W; break}\n+\n+    grid columnconfigure $top 0 -weight 1\n+    grid rowconfigure    $top 0 -weight 1\n+}\n+\n+# Call back for selecting a branch / tag in the tree of references\n+#\n+# @param w\n+#\n+proc callback_tree_of_references_item_selected {w} {\n+    global headids tagids\n+\n+    set itemId [$w focus]\n+    switch -- $w {\n+\t\".show_tree_of_local_branches.referenceList\" {\n+\t    if {[info exists headids($itemId)]} {\n+\t\tselbyid $headids($itemId)\n+\t    }\n+\t}\n+\t\".show_tree_of_remote_branches.referenceList\" {\n+\t    set itemId \"remotes/$itemId\"\n+\t    if {[info exists headids($itemId)]} {\n+\t\tselbyid $headids($itemId)\n+\t    }\n+\t}\n+\t\".show_tree_of_tags.referenceList\" {\n+\t    if {[info exists tagids($itemId)]} {\n+\t\tselbyid $tagids($itemId)\n+\t    }\n+\t}\n+    }\n+}\n+\n+\n # Display a list of tags and heads\n proc showrefs {} {\n     global showrefstop bgcolor fgcolor selectbgcolor NS\n-- \n2.10.2\n\n"},{"id":"307840","messageId":"20161215112847.14719-5-pmdumuid@gmail.com","threadId":"44710","inReplyTo":"20161215112847.14719-1-pmdumuid@gmail.com","subject":"[PATCH 5/6] gitk: Add a \"Save file as\" menu item","fromName":"Pierre Dumuid","fromEmail":"pmdumuid@gmail.com","sentAt":"2016-12-15T11:28:46Z","receivedAt":"2016-12-15T11:29:31Z","isPatch":true,"sender":{"key":"pmdumuid@gmail.com","avatar":"https://gravatar.com/avatar/444092d6279d27223d36a513dc67aa506ff029da21975f874ecd41dd0f9b664a?d=mp&s=160"},"body":"Previously, there was no easy way to save a particular file from the\ncurrently selected revision.\n\nThis patch adds a menu item \"Save file as\" to the file list popup\nmenu, which opens a file selection dialog to determine the name under\nwhich a file should be saved.  The default filename is of the form\n\"[shortid] basename\".  If the current revision is the index, the\ndefault pattern is of the form \"[index] basename\".  This works for\nboth, the \"Patch\" and \"Tree\" view.  The menu item is disabled for the\n\"local uncommitted changes\" fake revision.\n\nSigned-off-by: Andreas Amann <andreas.amann@web.de>\nSigned-off-by: Pierre Dumuid <pmdumuid@gmail.com>\n---\n gitk | 36 ++++++++++++++++++++++++++++++++++++\n 1 file changed, 36 insertions(+)\n\ndiff --git a/gitk b/gitk\nindex 5f27716..0903d2d 100755\n--- a/gitk\n+++ b/gitk\n@@ -2693,6 +2693,7 @@ proc makewindow {} {\n \t{mc \"Highlight this too\" command {flist_hl 0}}\n \t{mc \"Highlight this only\" command {flist_hl 1}}\n \t{mc \"External diff\" command {external_diff}}\n+\t{mc \"Save file as\" command {save_file_as}}\n \t{mc \"Blame parent commit\" command {external_blame 1}}\n \t{mc \"Copy path\" command {clipboard clear; clipboard append $flist_menu_file}}\n     }\n@@ -3504,6 +3505,7 @@ proc sel_flist {w x y} {\n proc pop_flist_menu {w X Y x y} {\n     global ctext cflist cmitmode flist_menu flist_menu_file\n     global treediffs diffids\n+    global nullid\n \n     stopfinding\n     set l [lindex [split [$w index \"@$x,$y\"] \".\"] 0]\n@@ -3521,6 +3523,12 @@ proc pop_flist_menu {w X Y x y} {\n     }\n     # Disable \"External diff\" item in tree mode\n     $flist_menu entryconf 2 -state $xdiffstate\n+    set savefilestate \"normal\"\n+    if {[lindex $diffids 0] eq $nullid} {\n+\tset savefilestate \"disabled\"\n+    }\n+    # Disable \"Save file as\" item \"local uncommited changes\" revision\n+    $flist_menu entryconf 3 -state $savefilestate\n     tk_popup $flist_menu $X $Y\n }\n \n@@ -3632,6 +3640,34 @@ proc external_diff_get_one_file {diffid filename diffdir} {\n \t       \"revision $diffid\"]\n }\n \n+proc save_file_as {} {\n+    global nullid nullid2\n+    global flist_menu_file cmitmode\n+    global diffids\n+\n+    set diffid [lindex $diffids 0]\n+    if {$diffid == $nullid} {\n+\treturn\n+    } elseif {$diffid == $nullid2} {\n+\tset diffidtext [mc \"index\"]\n+\tset diffid \"\"\n+\tset whattext $diffidtext\n+    } else {\n+\tset diffidtext [shortids $diffid]\n+\tset whattext \"[mc \"revision\"] $diffidtext\"\n+    }\n+    set diffid $diffid:\n+    if {$cmitmode eq \"tree\"} {\n+\tset diffid $diffid./\n+    }\n+    set difffile \"\\[$diffidtext\\] [file tail $flist_menu_file]\"\n+    set difffile [tk_getSaveFile -initialfile $difffile -title [mc \"Save file as\"] -parent .]\n+    if {$difffile eq {}} {\n+\treturn\n+    }\n+    save_file_from_commit $diffid$flist_menu_file $difffile $whattext\n+}\n+\n proc external_diff {} {\n     global nullid nullid2\n     global flist_menu_file\n-- \n2.10.2\n\n"},{"id":"307841","messageId":"20161215112847.14719-6-pmdumuid@gmail.com","threadId":"44710","inReplyTo":"20161215112847.14719-1-pmdumuid@gmail.com","subject":"[PATCH 6/6] Rename 'remotes/' to 'r../' in heads","fromName":"Pierre Dumuid","fromEmail":"pmdumuid@gmail.com","sentAt":"2016-12-15T11:28:47Z","receivedAt":"2016-12-15T11:29:34Z","isPatch":true,"sender":{"key":"pmdumuid@gmail.com","avatar":"https://gravatar.com/avatar/444092d6279d27223d36a513dc67aa506ff029da21975f874ecd41dd0f9b664a?d=mp&s=160"},"body":"Signed-off-by: Pierre Dumuid <pmdumuid@gmail.com>\n---\n gitk | 19 ++++++++++++++-----\n 1 file changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex 0903d2d..6f50b06 100755\n--- a/gitk\n+++ b/gitk\n@@ -6731,22 +6731,28 @@ proc drawtags {id x xt y1} {\n     set yb [expr {$yt + $linespc - 1}]\n     set xvals {}\n     set wvals {}\n+    set newTags {}\n+\n     set i -1\n     foreach tag $marks {\n \tincr i\n+\tset newTag $tag\n+\tregsub {^remotes} $newTag \"r..\" newTag\n+\n \tif {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {\n-\t    set wid [font measure mainfontbold $tag]\n+\t    set wid [font measure mainfontbold $newTag]\n \t} else {\n-\t    set wid [font measure mainfont $tag]\n+\t    set wid [font measure mainfont $newTag]\n \t}\n \tlappend xvals $xt\n \tlappend wvals $wid\n+\tlappend newTags $newTag\n \tset xt [expr {$xt + $wid + $extra}]\n     }\n     set t [$canv create line $x $y1 [lindex $xvals end] $y1 \\\n \t       -width $lthickness -fill $reflinecolor -tags tag.$id]\n     $canv lower $t\n-    foreach tag $marks x $xvals wid $wvals {\n+    foreach tag $marks x $xvals wid $wvals newTag $newTags {\n \tset tag_quoted [string map {% %%} $tag]\n \tset xl [expr {$x + $delta}]\n \tset xr [expr {$x + $delta + $wid + $lthickness}]\n@@ -6778,7 +6784,10 @@ proc drawtags {id x xt y1} {\n \t    $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \\\n \t\t-width 1 -outline black -fill $col -tags tag.$id\n \t    if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {\n-\t        set rwid [font measure mainfont $remoteprefix]\n+\t\tset newRemotePrefix $remoteprefix\n+\t\tregsub {^remotes} $newRemotePrefix \"r..\" newRemotePrefix\n+\n+\t        set rwid [font measure mainfont $newRemotePrefix]\n \t\tset xi [expr {$x + 1}]\n \t\tset yti [expr {$yt + 1}]\n \t\tset xri [expr {$x + $rwid}]\n@@ -6786,7 +6795,7 @@ proc drawtags {id x xt y1} {\n \t\t\t-width 0 -fill $remotebgcolor -tags tag.$id\n \t    }\n \t}\n-\tset t [$canv create text $xl $y1 -anchor w -text $tag -fill $headfgcolor \\\n+\tset t [$canv create text $xl $y1 -anchor w -text $newTag -fill $headfgcolor \\\n \t\t   -font $font -tags [list tag.$id text]]\n \tif {$ntags >= 0} {\n \t    $canv bind $t <1> $tagclick\n-- \n2.10.2\n\n"},{"id":"307842","messageId":"20161215112847.14719-4-pmdumuid@gmail.com","threadId":"44710","inReplyTo":"20161215112847.14719-1-pmdumuid@gmail.com","subject":"[PATCH 4/6] Add DirDiffTool as additional option","fromName":"Pierre Dumuid","fromEmail":"pmdumuid@gmail.com","sentAt":"2016-12-15T11:28:45Z","receivedAt":"2016-12-15T11:29:35Z","isPatch":true,"sender":{"key":"pmdumuid@gmail.com","avatar":"https://gravatar.com/avatar/444092d6279d27223d36a513dc67aa506ff029da21975f874ecd41dd0f9b664a?d=mp&s=160"},"body":"Signed-off-by: Pierre Dumuid <pmdumuid@gmail.com>\n---\n gitk | 17 +++++++++++++++++\n 1 file changed, 17 insertions(+)\n\ndiff --git a/gitk b/gitk\nindex a894f1d..5f27716 100755\n--- a/gitk\n+++ b/gitk\n@@ -2661,6 +2661,9 @@ proc makewindow {} {\n \t{mc \"Diff this -> marked commit\" command {diffvsmark 0}}\n \t{mc \"Diff marked commit -> this\" command {diffvsmark 1}}\n \t{mc \"Revert this commit\" command revert}\n+\n+\t{mc \"DirDiffTool this -> selected\" command {externalDiffToolVsSel 0}}\n+\t{mc \"DirDiffTool selected -> this\" command {externalDiffToolVsSel 1}}\n     }\n     $rowctxmenu configure -tearoff 0\n \n@@ -9254,6 +9257,20 @@ proc diffvssel {dirn} {\n     doseldiff $oldid $newid\n }\n \n+proc externalDiffToolVsSel {diffDirection} {\n+    global rowmenuid selectedline\n+\n+    if {$selectedline eq {}} return\n+    if {$diffDirection} {\n+\tset oldid [commitonrow $selectedline]\n+\tset newid $rowmenuid\n+    } else {\n+\tset oldid $rowmenuid\n+\tset newid [commitonrow $selectedline]\n+    }\n+    [exec git difftool -d $oldid $newid]\n+}\n+\n proc diffvsmark {dirn} {\n     global rowmenuid markedid\n \n-- \n2.10.2\n\n"},{"id":"308550","messageId":"20161231083051.GA21019@fergus.ozlabs.ibm.com","threadId":"44710","inReplyTo":"20161215112847.14719-1-pmdumuid@gmail.com","subject":"Re: [PATCH 1/6] Enable ability to visualise the results of git cherry C1 C2","fromName":"Paul Mackerras","fromEmail":"paulus@ozlabs.org","sentAt":"2016-12-31T08:30:51Z","receivedAt":"2016-12-31T11:04:10Z","isPatch":true,"sender":{"key":"paulus@ozlabs.org","avatar":"https://avatars.githubusercontent.com/u/1606439?v=4"},"body":"On Thu, Dec 15, 2016 at 09:58:42PM +1030, Pierre Dumuid wrote:\n> It's a bit clunky but it works!!\n> \n> Usage:\n>  - mark commit one (e.g. v45)\n>  - Select commit two.\n>  - Switch the gdttype to the option, \"git-cherry between marked commit and:\"\n\nThis needs a better description.  \"Git-cherry between marked commit\nand\" is a description of an implementation not a description of what's\nbeing achieved.  Having read the git cherry man page, it seems like\nit's (Find commit) included in marked commit but not in this commit\n(or the other way around?).  We would need a terser description that\nthat, though.\n\n[...]\n\n> +proc update_gitcherrylist {} {\n> +    global gitcherryids\n> +    global markedid\n> +    global findstring\n> +    global fstring\n> +    global currentid\n> +    global iddrawn\n> +\n> +    unset -nocomplain gitcherryids\n> +    set fs $findstring\n> +\n> +    if {$findstring eq {}} {\n> +\t$fstring delete 0 end\n> +\t$fstring insert 0 $currentid\n> +    }\n> +\n> +    if {![info exists markedid]} {\n> +\terror_popup [mc \"Please mark a git commit before using this find method!\"]\n> +\treturn\n> +    }\n> +\n> +    #puts [join [list \"Running cherry between: `\" $markedid \"` and `\" $findstring \"`\"] \"\"]\n> +\n> +    if {[catch {set cherryOutput [exec git cherry $markedid $findstring]}]} {\n\nHow long could the git cherry take to run?  If it's more than a\nfraction of a second, then we would need to handle its output\nasynchronously like we do in [do_file_hl].\n\nPaul.\n"},{"id":"308551","messageId":"20161231090859.GC21019@fergus.ozlabs.ibm.com","threadId":"44710","inReplyTo":"20161215112847.14719-3-pmdumuid@gmail.com","subject":"Re: [PATCH 3/6] Add a tree view to the local branches, remote branches and tags, where / is treated as a directory seperator.","fromName":"Paul Mackerras","fromEmail":"paulus@ozlabs.org","sentAt":"2016-12-31T09:08:59Z","receivedAt":"2016-12-31T11:04:14Z","isPatch":true,"sender":{"key":"paulus@ozlabs.org","avatar":"https://avatars.githubusercontent.com/u/1606439?v=4"},"body":"On Thu, Dec 15, 2016 at 09:58:44PM +1030, Pierre Dumuid wrote:\n> Signed-off-by: Pierre Dumuid <pmdumuid@gmail.com>\n> ---\n>  gitk | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 117 insertions(+)\n\nNice idea in general... a few comments below.  Also, please don't put\nthe entire commit message in the subject line. :)\n\n> diff --git a/gitk b/gitk\n> index 36cba49..a894f1d 100755\n> --- a/gitk\n> +++ b/gitk\n> @@ -2089,6 +2089,10 @@ proc makewindow {} {\n>  \t    {mc \"Reread re&ferences\" command rereadrefs}\n>  \t    {mc \"&List references\" command showrefs -accelerator F2}\n>  \t    {xx \"\" separator}\n> +\t    {mc \"List Local Branches\"  command {show_tree_of_references_dialog \"localBranches\"}  -accelerator F6}\n> +\t    {mc \"List Remote Branches\" command {show_tree_of_references_dialog \"remoteBranches\"} -accelerator F7}\n> +\t    {mc \"List Tags\"            command {show_tree_of_references_dialog \"tags\"}           -accelerator F8}\n> +\t    {xx \"\" separator}\n>  \t    {mc \"Start git &gui\" command {exec git gui &}}\n>  \t    {xx \"\" separator}\n>  \t    {mc \"&Quit\" command doquit -accelerator Meta1-Q}\n> @@ -2601,6 +2605,9 @@ proc makewindow {} {\n>      bind . <F5> updatecommits\n>      bindmodfunctionkey Shift 5 reloadcommits\n>      bind . <F2> showrefs\n> +    bind . <F6> {show_tree_of_references_dialog \"localBranches\"}\n> +    bind . <F7> {show_tree_of_references_dialog \"remoteBranches\"}\n> +    bind . <F8> {show_tree_of_references_dialog \"tags\"}\n>      bindmodfunctionkey Shift 4 {newview 0}\n>      bind . <F4> edit_or_newview\n>      bind . <$M1B-q> doquit\n> @@ -10146,6 +10153,116 @@ proc rmbranch {} {\n>      run refill_reflist\n>  }\n>  \n> +# Display a tree view of local branches, remote branches, and tags according to view_type.\n> +#\n> +# @param string view_type\n> +#    Must be one of \"localBranches\", \"remoteBranches\", or \"tags\".\n> +#\n> +proc show_tree_of_references_dialog {view_type} {\n> +    global NS\n> +    global treefilelist\n> +    global headids tagids\n> +\n> +    switch -- $view_type {\n> +\t\"localBranches\" {\n> +\t    set dialogName \"Local Branches\"\n> +\t    set top .show_tree_of_local_branches\n> +\t    set listOfReferences [lsort [array names headids -regexp {^(?!remotes/)} ]]\n> +\t    set truncateFrom 0\n> +\t}\n> +\t\"remoteBranches\" {\n> +\t    set dialogName \"Remote Branches\"\n> +\t    set top .show_tree_of_remote_branches\n> +\t    set listOfReferences [lsort [array names headids -regexp {^remotes/} ]]\n> +\t    set truncateFrom 8\n> +\t}\n> +\t\"tags\" {\n> +\t    set dialogName \"Tags\"\n> +\t    set top .show_tree_of_tags\n> +\t    set listOfReferences [lsort [array names tagids]]\n> +\t    set truncateFrom 0\n> +\t}\n> +    }\n> +\n> +    if {[winfo exists $top]} {\n> +\traise $top\n> +\treturn\n> +    }\n> +\n> +    ttk_toplevel $top\n> +    wm title $top [mc \"$dialogName: %s\" [file tail [pwd]]]\n> +    wm geometry $top \"600x900\"\n\nDo you really need to do this?  A fixed size like this is inevitably\ngoing to be too big for some users and too small for others.\n\n> +\n> +    make_transient $top .\n> +\n> +    ## See http://www.tkdocs.com/tutorial/tree.html\n> +    ttk::treeview $top.referenceList -xscrollcommand \"$top.horizontalScrollBar set\" -yscrollcommand \"$top.verticalScrollBar set\"\n\nWe still have the option for people to run without ttk, in case\nsomeone is still using an old Tcl/Tk version or just doesn't like the\nttk widgets.  However, there isn't an equivalent of ttk::treeview in\nthe older Tk widget set.  It would be OK to omit the new menu entries\nor to disable them if $use_ttk is false, but I don't want to have menu\nentries that will always cause gitk to blow up when $use_ttk is false.\n\nWe possibly should consider converting the file list view to use a\nttk::treeview when $use_ttk is true.\n\nPaul.\n"},{"id":"308552","messageId":"20161231085358.GB21019@fergus.ozlabs.ibm.com","threadId":"44710","inReplyTo":"20161215112847.14719-2-pmdumuid@gmail.com","subject":"Re: [PATCH 2/6] Add ability to follow a remote branch with a dialog","fromName":"Paul Mackerras","fromEmail":"paulus@ozlabs.org","sentAt":"2016-12-31T08:53:59Z","receivedAt":"2016-12-31T11:04:17Z","isPatch":true,"sender":{"key":"paulus@ozlabs.org","avatar":"https://avatars.githubusercontent.com/u/1606439?v=4"},"body":"On Thu, Dec 15, 2016 at 09:58:43PM +1030, Pierre Dumuid wrote:\n> A suggested name is provided when creating a new \"following\" branch.\n> \n> Signed-off-by: Pierre Dumuid <pmdumuid@gmail.com>\n> ---\n>  gitk | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----\n>  1 file changed, 82 insertions(+), 4 deletions(-)\n> \n> diff --git a/gitk b/gitk\n> index 50d1ef4..36cba49 100755\n> --- a/gitk\n> +++ b/gitk\n> @@ -2673,6 +2673,7 @@ proc makewindow {} {\n>  \t{mc \"Rename this branch\" command mvbranch}\n>  \t{mc \"Remove this branch\" command rmbranch}\n>  \t{mc \"Copy branch name\" command {clipboard clear; clipboard append $headmenuhead}}\n> +\t{mc \"Follow this branch\"  command follow_remote_branch_dialog}\n>      }\n>      $headctxmenu configure -tearoff 0\n>  \n> @@ -9947,23 +9948,100 @@ proc headmenu {x y id head} {\n>      stopfinding\n>      set headmenuid $id\n>      set headmenuhead $head\n> -    array set state {0 normal 1 normal 2 normal}\n> +    array set state {0 normal 1 normal 2 normal 3 normal}\n>      if {[string match \"remotes/*\" $head]} {\n>  \tset localhead [string range $head [expr [string last / $head] + 1] end]\n>  \tif {[info exists headids($localhead)]} {\n>  \t    set state(0) disabled\n>  \t}\n> -\tarray set state {1 disabled 2 disabled}\n> +\tarray set state {1 disabled 2 disabled 3 normal}\n\nYou set array(3) to \"normal\" just above, no need to do it again.\n\n>      }\n>      if {$head eq $mainhead} {\n> -\tarray set state {0 disabled 2 disabled}\n> +\tarray set state {0 disabled 2 disabled 3 disabled}\n> +    } else {\n> +\tset state(3) disabled\n>      }\n\nAs far as I can see, this will always end up with state(3) set to\n\"disabled\", won't it?  Is that really what you want?\n\nPaul.\n"}]}