{"thread":{"id":"15290","subject":"[PATCH (GIT-GUI) 0/8] Add mergetool functionality to git-gui.","startedAt":"2008-08-30T20:52:21Z","lastAt":"2008-09-24T19:08:01Z","messageCount":21,"participants":["Alexander Gavrilov","Johannes Sixt","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"89257","messageId":"200808310052.21595.angavrilov@gmail.com","threadId":"15290","inReplyTo":null,"subject":"[PATCH (GIT-GUI) 0/8] Add mergetool functionality to git-gui.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T20:52:21Z","receivedAt":"2008-08-30T20:52:21Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"The original git-mergetool program is intended to be used from\nthe command line, and thus does not integrate well with GUI\nusage. For instance, it presents the user with its own interactive\ninterface, based on text-mode prompts.\n\nThis series implements mergetool functionality within git-gui,\nreplacing the default 'Stage Hunk/Line' context menu items\nfor unresolved files with conflict resolution commands.\n\nIt also enhances auto-selection behavior to minimize the\nnecessary number of clicks on the items of the file lists,\nand adds special handling of diffs for conflicts involving\ndeletion or symlinks.\n\n\nCustom merge tools are not supported as I'm not sure of\nthe best way to evaluate command line patterns that are\nintended for the shell from Tcl.\n\n\n\nPATCHES:\n\n\tgit-gui: Don't allow staging files with conflicts.\n\t---\n\t git-gui.sh |    8 ++++++++\n\t 1 files changed, 8 insertions(+), 0 deletions(-)\n\n\tgit-gui: Support resolving conflicts via the diff context menu.\n\t---\n\t git-gui.sh        |  152 ++++++++++++++++++++++++++++++++---------------------\n\t lib/mergetool.tcl |   98 ++++++++++++++++++++++++++++++++++\n\t 2 files changed, 190 insertions(+), 60 deletions(-)\n\t create mode 100644 lib/mergetool.tcl\n\n\tgit-gui: Support calling merge tools.\n\t---\n\t git-gui.sh        |    7 ++\n\t lib/mergetool.tcl |  252 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n\t lib/option.tcl    |    1 +\n\t 3 files changed, 260 insertions(+), 0 deletions(-)\n\n\tgit-gui: Support more merge tools.\n\t---\n\t lib/mergetool.tcl |   27 +++++++++++++++++++++++++++\n\t 1 files changed, 27 insertions(+), 0 deletions(-)\n\n\tgit-gui: Support conflict states _U & UT.\n\t---\n\t git-gui.sh     |    6 ++++--\n\t lib/commit.tcl |    1 +\n\t lib/diff.tcl   |    2 +-\n\t lib/index.tcl  |    1 +\n\t 4 files changed, 7 insertions(+), 3 deletions(-)\n\n\tgit-gui: Reimplement and enhance auto-selection of diffs.\n\t---\n\t git-gui.sh        |  122 +++++++++++++++++++++++++++++++++++++++++------------\n\t lib/diff.tcl      |   18 +++++---\n\t lib/mergetool.tcl |    8 +---\n\t 3 files changed, 109 insertions(+), 39 deletions(-)\n\n\tgit-gui: Make F5 reselect a diff, if an untracked file is selected.\n\t---\n\t git-gui.sh |   44 +++++++++++++++++++++++++++++++++++---------\n\t 1 files changed, 35 insertions(+), 9 deletions(-)\n\n\tgit-gui: Show special diffs for complex conflict cases.\n\t---\n\t lib/diff.tcl |   94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n\t 1 files changed, 89 insertions(+), 5 deletions(-)\n"},{"id":"89260","messageId":"200808310054.19732.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200808310052.21595.angavrilov@gmail.com","subject":"[PATCH (GIT-GUI) 1/8] git-gui: Don't allow staging files with conflicts.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T20:54:19Z","receivedAt":"2008-08-30T20:54:19Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"Prevent staging files with conflict markers by clicking\non the icon in the 'Unstaged Changes' list. Instead, pretend\nthat the user clicked the name, and show the diff.\n\nOriginally it made some sense to allow staging conflicting\nfiles, because git-gui did not provide any tools to resolve\nthem from within the GUI. But now that I'm adding mergetool\ncapabilities, it is more likely to cause accidental and\nnon-undoable errors.\n\nSigned-off-by: Alexander Gavrilov <angavrilov@gmail.com>\n---\n git-gui.sh |    8 ++++++++\n 1 files changed, 8 insertions(+), 0 deletions(-)\n\ndiff --git a/git-gui.sh b/git-gui.sh\nindex ec08b5a..9df4f8c 100755\n--- a/git-gui.sh\n+++ b/git-gui.sh\n@@ -1838,6 +1838,14 @@ proc toggle_or_diff {w x y} {\n \t$ui_index tag remove in_sel 0.0 end\n \t$ui_workdir tag remove in_sel 0.0 end\n \n+\t# Do not stage files with conflicts\n+\tif {[info exists file_states($path)]} {\n+\t\tset state [lindex $file_states($path) 0]\n+\t\tif {[string index $state 0] eq {U}} {\n+\t\t\tset col 1\n+\t\t}\n+\t}\n+\n \tif {$col == 0 && $y > 1} {\n \t\tset i [expr {$lno-1}]\n \t\tset ll [expr {[llength $file_lists($w)]-1}]\n-- \n1.6.0.20.g6148bc\n"},{"id":"89258","messageId":"200808310055.45679.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200808310054.19732.angavrilov@gmail.com","subject":"[PATCH (GIT-GUI) 2/8] git-gui: Support resolving conflicts via the diff context menu.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T20:55:45Z","receivedAt":"2008-08-30T20:55:45Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"If the file has merge conflicts, show a special version of the\ndiff context menu, which includes conflict resolution commands\ninstead of Stage Hunk/Line. This patch only supports resolving\nby discarding all sides except one.\n\nDiscarding is the only way to resolve conflicts involving symlinks\nand/or deletion, excluding manual editing.\n\nSigned-off-by: Alexander Gavrilov <angavrilov@gmail.com>\n---\n git-gui.sh        |  152 ++++++++++++++++++++++++++++++++---------------------\n lib/mergetool.tcl |   98 ++++++++++++++++++++++++++++++++++\n 2 files changed, 190 insertions(+), 60 deletions(-)\n create mode 100644 lib/mergetool.tcl\n\ndiff --git a/git-gui.sh b/git-gui.sh\nindex 9df4f8c..90d597e 100755\n--- a/git-gui.sh\n+++ b/git-gui.sh\n@@ -2721,6 +2721,51 @@ $ui_diff tag raise sel\n \n # -- Diff Body Context Menu\n #\n+\n+proc create_common_diff_popup {ctxm} {\n+\t$ctxm add command \\\n+\t\t-label [mc \"Show Less Context\"] \\\n+\t\t-command show_less_context\n+\tlappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n+\t$ctxm add command \\\n+\t\t-label [mc \"Show More Context\"] \\\n+\t\t-command show_more_context\n+\tlappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n+\t$ctxm add separator\n+\t$ctxm add command \\\n+\t\t-label [mc Refresh] \\\n+\t\t-command reshow_diff\n+\tlappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n+\t$ctxm add command \\\n+\t\t-label [mc Copy] \\\n+\t\t-command {tk_textCopy $ui_diff}\n+\tlappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n+\t$ctxm add command \\\n+\t\t-label [mc \"Select All\"] \\\n+\t\t-command {focus $ui_diff;$ui_diff tag add sel 0.0 end}\n+\tlappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n+\t$ctxm add command \\\n+\t\t-label [mc \"Copy All\"] \\\n+\t\t-command {\n+\t\t\t$ui_diff tag add sel 0.0 end\n+\t\t\ttk_textCopy $ui_diff\n+\t\t\t$ui_diff tag remove sel 0.0 end\n+\t\t}\n+\tlappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n+\t$ctxm add separator\n+\t$ctxm add command \\\n+\t\t-label [mc \"Decrease Font Size\"] \\\n+\t\t-command {incr_font_size font_diff -1}\n+\tlappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n+\t$ctxm add command \\\n+\t\t-label [mc \"Increase Font Size\"] \\\n+\t\t-command {incr_font_size font_diff 1}\n+\tlappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n+\t$ctxm add separator\n+\t$ctxm add command -label [mc \"Options...\"] \\\n+\t\t-command do_options\n+}\n+\n set ctxm .vpane.lower.diff.body.ctxm\n menu $ctxm -tearoff 0\n $ctxm add command \\\n@@ -2734,73 +2779,60 @@ $ctxm add command \\\n set ui_diff_applyline [$ctxm index last]\n lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]\n $ctxm add separator\n-$ctxm add command \\\n-\t-label [mc \"Show Less Context\"] \\\n-\t-command show_less_context\n-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n-$ctxm add command \\\n-\t-label [mc \"Show More Context\"] \\\n-\t-command show_more_context\n-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n-$ctxm add separator\n-$ctxm add command \\\n-\t-label [mc Refresh] \\\n-\t-command reshow_diff\n-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n-$ctxm add command \\\n-\t-label [mc Copy] \\\n-\t-command {tk_textCopy $ui_diff}\n-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n-$ctxm add command \\\n-\t-label [mc \"Select All\"] \\\n-\t-command {focus $ui_diff;$ui_diff tag add sel 0.0 end}\n-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n-$ctxm add command \\\n-\t-label [mc \"Copy All\"] \\\n-\t-command {\n-\t\t$ui_diff tag add sel 0.0 end\n-\t\ttk_textCopy $ui_diff\n-\t\t$ui_diff tag remove sel 0.0 end\n-\t}\n-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n-$ctxm add separator\n-$ctxm add command \\\n-\t-label [mc \"Decrease Font Size\"] \\\n-\t-command {incr_font_size font_diff -1}\n-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n-$ctxm add command \\\n-\t-label [mc \"Increase Font Size\"] \\\n-\t-command {incr_font_size font_diff 1}\n-lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]\n-$ctxm add separator\n-$ctxm add command -label [mc \"Options...\"] \\\n-\t-command do_options\n-proc popup_diff_menu {ctxm x y X Y} {\n+create_common_diff_popup $ctxm\n+\n+set ctxmmg .vpane.lower.diff.body.ctxmmg\n+menu $ctxmmg -tearoff 0\n+$ctxmmg add command \\\n+\t-label [mc \"Use Remote Version\"] \\\n+\t-command {merge_resolve_one 3}\n+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]\n+$ctxmmg add command \\\n+\t-label [mc \"Use Local Version\"] \\\n+\t-command {merge_resolve_one 2}\n+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]\n+$ctxmmg add command \\\n+\t-label [mc \"Revert To Base\"] \\\n+\t-command {merge_resolve_one 1}\n+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]\n+$ctxmmg add separator\n+create_common_diff_popup $ctxmmg\n+\n+proc popup_diff_menu {ctxm ctxmmg x y X Y} {\n \tglobal current_diff_path file_states\n \tset ::cursorX $x\n \tset ::cursorY $y\n-\tif {$::ui_index eq $::current_diff_side} {\n-\t\tset l [mc \"Unstage Hunk From Commit\"]\n-\t\tset t [mc \"Unstage Line From Commit\"]\n+\tif {[info exists file_states($current_diff_path)]} {\n+\t\tset state [lindex $file_states($current_diff_path) 0]\n \t} else {\n-\t\tset l [mc \"Stage Hunk For Commit\"]\n-\t\tset t [mc \"Stage Line For Commit\"]\n-\t}\n-\tif {$::is_3way_diff\n-\t\t|| $current_diff_path eq {}\n-\t\t|| ![info exists file_states($current_diff_path)]\n-\t\t|| {_O} eq [lindex $file_states($current_diff_path) 0]\n-\t\t|| {_T} eq [lindex $file_states($current_diff_path) 0]\n-\t\t|| {T_} eq [lindex $file_states($current_diff_path) 0]} {\n-\t\tset s disabled\n+\t\tset state {__}\n+\t}\n+\tif {[string index $state 0] eq {U}} {\n+\t\ttk_popup $ctxmmg $X $Y\n \t} else {\n-\t\tset s normal\n+\t\tif {$::ui_index eq $::current_diff_side} {\n+\t\t\tset l [mc \"Unstage Hunk From Commit\"]\n+\t\t\tset t [mc \"Unstage Line From Commit\"]\n+\t\t} else {\n+\t\t\tset l [mc \"Stage Hunk For Commit\"]\n+\t\t\tset t [mc \"Stage Line For Commit\"]\n+\t\t}\n+\t\tif {$::is_3way_diff\n+\t\t\t|| $current_diff_path eq {}\n+\t\t\t|| {__} eq $state\n+\t\t\t|| {_O} eq $state\n+\t\t\t|| {_T} eq $state\n+\t\t\t|| {T_} eq $state} {\n+\t\t\tset s disabled\n+\t\t} else {\n+\t\t\tset s normal\n+\t\t}\n+\t\t$ctxm entryconf $::ui_diff_applyhunk -state $s -label $l\n+\t\t$ctxm entryconf $::ui_diff_applyline -state $s -label $t\n+\t\ttk_popup $ctxm $X $Y\n \t}\n-\t$ctxm entryconf $::ui_diff_applyhunk -state $s -label $l\n-\t$ctxm entryconf $::ui_diff_applyline -state $s -label $t\n-\ttk_popup $ctxm $X $Y\n }\n-bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y]\n+bind_button3 $ui_diff [list popup_diff_menu $ctxm $ctxmmg %x %y %X %Y]\n \n # -- Status Bar\n #\ndiff --git a/lib/mergetool.tcl b/lib/mergetool.tcl\nnew file mode 100644\nindex 0000000..7945d74\n--- /dev/null\n+++ b/lib/mergetool.tcl\n@@ -0,0 +1,98 @@\n+# git-gui merge conflict resolution\n+# parts based on git-mergetool (c) 2006 Theodore Y. Ts'o\n+\n+proc merge_resolve_one {stage} {\n+\tglobal current_diff_path\n+\n+\tswitch -- $stage {\n+\t\t1 { set target [mc \"the base version\"] }\n+\t\t2 { set target [mc \"this branch\"] }\n+\t\t3 { set target [mc \"the other branch\"] }\n+\t}\n+\n+\tset op_question [mc \"Force resolution to %s?\n+Note that the diff shows only conflicting changes.\n+\n+%s will be overwritten.\n+\n+This operation can be undone only by restarting the merge.\" \\\n+\t\t$target [short_path $current_diff_path]]\n+\n+\tif {[ask_popup $op_question] eq {yes}} {\n+\t\tmerge_load_stages $current_diff_path [list merge_force_stage $stage]\n+\t}\n+}\n+\n+proc merge_add_resolution {path} {\n+\tglobal current_diff_path\n+\n+\tif {$path eq $current_diff_path} {\n+\t\tset after {reshow_diff;}\n+\t} else {\n+\t\tset after {}\n+\t}\n+\n+\tupdate_index \\\n+\t\t[mc \"Adding resolution for %s\" [short_path $path]] \\\n+\t\t[list $path] \\\n+\t\t[concat $after [list ui_ready]]\n+}\n+\n+proc merge_force_stage {stage} {\n+\tglobal current_diff_path merge_stages\n+\n+\tif {$merge_stages($stage) ne {}} {\n+\t\tgit checkout-index -f --stage=$stage -- $current_diff_path\n+\t} else {\n+\t\tfile delete -- $current_diff_path\n+\t}\n+\n+\tmerge_add_resolution $current_diff_path\n+}\n+\n+proc merge_load_stages {path cont} {\n+\tglobal merge_stages_fd merge_stages merge_stages_buf\n+\n+\tif {[info exists merge_stages_fd]} {\n+\t\tcatch { kill_file_process $merge_stages_fd }\n+\t\tcatch { close $merge_stages_fd }\n+\t}\n+\n+\tset merge_stages(0) {}\n+\tset merge_stages(1) {}\n+\tset merge_stages(2) {}\n+\tset merge_stages(3) {}\n+\tset merge_stages_buf {}\n+\n+\tset merge_stages_fd [eval git_read ls-files -u -z -- $path]\n+\n+\tfconfigure $merge_stages_fd -blocking 0 -translation binary -encoding binary\n+\tfileevent $merge_stages_fd readable [list read_merge_stages $merge_stages_fd $cont]\n+}\n+\n+proc read_merge_stages {fd cont} {\n+\tglobal merge_stages_buf merge_stages_fd merge_stages\n+\n+\tappend merge_stages_buf [read $fd]\n+\tset pck [split $merge_stages_buf \"\\0\"]\n+\tset merge_stages_buf [lindex $pck end]\n+\n+\tif {[eof $fd] && $merge_stages_buf ne {}} {\n+\t\tlappend pck {}\n+\t\tset merge_stages_buf {}\n+\t}\n+\n+\tforeach p [lrange $pck 0 end-1] {\n+\t\tset fcols [split $p \"\\t\"]\n+\t\tset cols  [split [lindex $fcols 0] \" \"]\n+\t\tset stage [lindex $cols 2]\n+\t\t\n+\t\tset merge_stages($stage) [lrange $cols 0 1]\n+\t}\n+\n+\tif {[eof $fd]} {\n+\t\tclose $fd\n+\t\tunset merge_stages_fd\n+\t\teval $cont\n+\t}\n+}\n-- \n1.6.0.20.g6148bc\n"},{"id":"89261","messageId":"200808310056.51324.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200808310055.45679.angavrilov@gmail.com","subject":"[PATCH (GIT-GUI) 3/8] git-gui: Support calling merge tools.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T20:56:51Z","receivedAt":"2008-08-30T20:56:51Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"Adds an item to the diff context menu in conflict mode,\nwhich invokes a merge tool for the selected file. Tool\ncommand-line handling code was ported from git-mergetool.\n\nAutomatic default tool selection and custom merge tools\nare not supported. If merge.tool is not set, git-gui\ndefaults to meld.\n\nThis implementation uses a checkout-index hack in order\nto retrieve all stages with autocrlf and filters properly\napplied. It requires temporarily moving the original\nconflict file out of the way.\n\nSigned-off-by: Alexander Gavrilov <angavrilov@gmail.com>\n---\n git-gui.sh        |    7 ++\n lib/mergetool.tcl |  252 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n lib/option.tcl    |    1 +\n 3 files changed, 260 insertions(+), 0 deletions(-)\n\ndiff --git a/git-gui.sh b/git-gui.sh\nindex 90d597e..8d4f715 100755\n--- a/git-gui.sh\n+++ b/git-gui.sh\n@@ -657,6 +657,8 @@ proc apply_config {} {\n }\n \n set default_config(branch.autosetupmerge) true\n+set default_config(merge.tool) {}\n+set default_config(merge.keepbackup) true\n set default_config(merge.diffstat) true\n set default_config(merge.summary) false\n set default_config(merge.verbosity) 2\n@@ -2784,6 +2786,11 @@ create_common_diff_popup $ctxm\n set ctxmmg .vpane.lower.diff.body.ctxmmg\n menu $ctxmmg -tearoff 0\n $ctxmmg add command \\\n+\t-label [mc \"Run Merge Tool\"] \\\n+\t-command {merge_resolve_tool}\n+lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]\n+$ctxmmg add separator\n+$ctxmmg add command \\\n \t-label [mc \"Use Remote Version\"] \\\n \t-command {merge_resolve_one 3}\n lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]\ndiff --git a/lib/mergetool.tcl b/lib/mergetool.tcl\nindex 7945d74..ba9e8ce 100644\n--- a/lib/mergetool.tcl\n+++ b/lib/mergetool.tcl\n@@ -96,3 +96,255 @@ proc read_merge_stages {fd cont} {\n \t\teval $cont\n \t}\n }\n+\n+proc merge_resolve_tool {} {\n+\tglobal current_diff_path\n+\n+\tmerge_load_stages $current_diff_path [list merge_resolve_tool2]\n+}\n+\n+proc merge_resolve_tool2 {} {\n+\tglobal current_diff_path merge_stages\n+\n+\t# Validate the stages\n+\tif {$merge_stages(2) eq {} ||\n+\t    [lindex $merge_stages(2) 0] eq {120000} ||\n+\t    [lindex $merge_stages(2) 0] eq {160000} ||\n+\t    $merge_stages(3) eq {} ||\n+\t    [lindex $merge_stages(3) 0] eq {120000} ||\n+\t    [lindex $merge_stages(3) 0] eq {160000}\n+\t} {\n+\t\terror_popup [mc \"Cannot resolve deletion or link conflicts using a tool\"]\n+\t\treturn\n+\t}\n+\n+\tif {![file exists $current_diff_path]} {\n+\t\terror_popup [mc \"Conflict file does not exist\"]\n+\t\treturn\n+\t}\n+\n+\t# Determine the tool to use\n+\tset tool [get_config merge.tool]\n+\tif {$tool eq {}} { set tool meld }\n+\n+\tset merge_tool_path [get_config \"mergetool.$tool.path\"]\n+\tif {$merge_tool_path eq {}} {\n+\t\tswitch -- $tool {\n+\t\temerge { set merge_tool_path \"emacs\" }\n+\t\tdefault { set merge_tool_path $tool }\n+\t\t}\n+\t}\n+\n+\t# Make file names\n+\tset filebase [file rootname $current_diff_path]\n+\tset fileext  [file extension $current_diff_path]\n+\tset basename [lindex [file split $current_diff_path] end]\n+\n+\tset MERGED   $current_diff_path\n+\tset BASE     \"./$MERGED.BASE$fileext\"\n+\tset LOCAL    \"./$MERGED.LOCAL$fileext\"\n+\tset REMOTE   \"./$MERGED.REMOTE$fileext\"\n+\tset BACKUP   \"./$MERGED.BACKUP$fileext\"\n+\n+\tset base_stage $merge_stages(1)\n+\n+\t# Build the command line\n+\tswitch -- $tool {\n+\tkdiff3 {\n+\t\tif {$base_stage ne {}} {\n+\t\t\tset cmdline [list \"$merge_tool_path\" --auto --L1 \"$MERGED (Base)\" \\\n+\t\t\t\t--L2 \"$MERGED (Local)\" --L3 \"$MERGED (Remote)\" -o \"$MERGED\" \"$BASE\" \"$LOCAL\" \"$REMOTE\"]\n+\t\t} else {\n+\t\t\tset cmdline [list \"$merge_tool_path\" --auto --L1 \"$MERGED (Local)\" \\\n+\t\t\t\t--L2 \"$MERGED (Remote)\" -o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"]\n+\t\t}\n+\t}\n+\ttkdiff {\n+\t\tif {$base_stage ne {}} {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -a \"$BASE\" -o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"]\n+\t\t} else {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -o \"$MERGED\" \"$LOCAL\" \"$REMOTE\"]\n+\t\t}\n+\t}\n+\tmeld {\n+\t\tset cmdline [list \"$merge_tool_path\" \"$LOCAL\" \"$MERGED\" \"$REMOTE\"]\n+\t}\n+\tgvimdiff {\n+\t\tset cmdline [list \"$merge_tool_path\" -f \"$LOCAL\" \"$MERGED\" \"$REMOTE\"]\n+\t}\n+\txxdiff {\n+\t\tif {$base_stage ne {}} {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -X --show-merged-pane \\\n+\t\t\t\t\t    -R {Accel.SaveAsMerged: \"Ctrl-S\"} \\\n+\t\t\t\t\t    -R {Accel.Search: \"Ctrl+F\"} \\\n+\t\t\t\t\t    -R {Accel.SearchForward: \"Ctrl-G\"} \\\n+\t\t\t\t\t    --merged-file \"$MERGED\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"]\n+\t\t} else {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -X --show-merged-pane \\\n+\t\t\t\t\t    -R {Accel.SaveAsMerged: \"Ctrl-S\"} \\\n+\t\t\t\t\t    -R {Accel.Search: \"Ctrl+F\"} \\\n+\t\t\t\t\t    -R {Accel.SearchForward: \"Ctrl-G\"} \\\n+\t\t\t\t\t    --merged-file \"$MERGED\" \"$LOCAL\" \"$REMOTE\"]\n+\t\t}\n+\t}\n+\topendiff {\n+\t\tif {$base_stage ne {}} {\n+\t\t\tset cmdline [list \"$merge_tool_path\" \"$LOCAL\" \"$REMOTE\" -ancestor \"$BASE\" -merge \"$MERGED\"]\n+\t\t} else {\n+\t\t\tset cmdline [list \"$merge_tool_path\" \"$LOCAL\" \"$REMOTE\" -merge \"$MERGED\"]\n+\t\t}\n+\t}\n+\tecmerge {\n+\t\tif {$base_stage ne {}} {\n+\t\t\tset cmdline [list \"$merge_tool_path\" \"$BASE\" \"$LOCAL\" \"$REMOTE\" --default --mode=merge3 --to=\"$MERGED\"]\n+\t\t} else {\n+\t\t\tset cmdline [list \"$merge_tool_path\" \"$LOCAL\" \"$REMOTE\" --default --mode=merge2 --to=\"$MERGED\"]\n+\t\t}\n+\t}\n+\temerge {\n+\t\tif {$base_stage ne {}} {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -f emerge-files-with-ancestor-command \\\n+\t\t\t\t\t\"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$basename\"]\n+\t\t} else {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -f emerge-files-command \\\n+\t\t\t\t\t\"$LOCAL\" \"$REMOTE\" \"$basename\"]\n+\t\t}\n+\t}\n+\tvimdiff {\n+\t\terror_popup [mc \"Not a GUI merge tool: '%s'\" $tool]\n+\t\treturn\n+\t}\n+\tdefault {\n+\t\terror_popup [mc \"Unsupported merge tool '%s'\" $tool]\n+\t\treturn\n+\t}\n+\t}\n+\n+\tmerge_tool_start $cmdline $MERGED $BACKUP [list $BASE $LOCAL $REMOTE]\n+}\n+\n+proc delete_temp_files {files} {\n+\tforeach fname $files {\n+\t\tfile delete $fname\n+\t}\n+}\n+\n+proc merge_tool_get_stages {target stages} {\n+\tglobal merge_stages\n+\n+\tset i 1\n+\tforeach fname $stages {\n+\t\tif {$merge_stages($i) eq {}} {\n+\t\t\tfile delete $fname\n+\t\t} else {\n+\t\t\t# A hack to support autocrlf properly\n+\t\t\tgit checkout-index -f --stage=$i -- $target\n+\t\t\tfile rename -force -- $target $fname\n+\t\t}\n+\t\tincr i\n+\t}\n+}\n+\n+proc merge_tool_start {cmdline target backup stages} {\n+\tglobal merge_stages mtool_target mtool_tmpfiles mtool_fd mtool_mtime\n+\n+\tif {[info exists mtool_fd]} {\n+\t\tif {[ask_popup [mc \"Merge tool is already running, terminate it?\"]] eq {yes}} {\n+\t\t\tcatch { kill_file_process $mtool_fd }\n+\t\t\tcatch { close $mtool_fd }\n+\t\t\tunset mtool_fd\n+\n+\t\t\tset old_backup [lindex $mtool_tmpfiles end]\n+\t\t\tfile rename -force -- $old_backup $mtool_target\n+\t\t\tdelete_temp_files $mtool_tmpfiles\n+\t\t} else {\n+\t\t\treturn\n+\t\t}\n+\t}\n+\n+\t# Save the original file\n+\tfile rename -force -- $target $backup\n+\n+\t# Get the blobs; it destroys $target\n+\tif {[catch {merge_tool_get_stages $target $stages} err]} {\n+\t\tfile rename -force -- $backup $target\n+\t\tdelete_temp_files $stages\n+\t\terror_popup [mc \"Error retrieving versions:\\n%s\" $err]\n+\t\treturn\n+\t}\n+\n+\t# Restore the conflict file\n+\tfile copy -force -- $backup $target\n+\n+\t# Initialize global state\n+\tset mtool_target $target\n+\tset mtool_mtime [file mtime $target]\n+\tset mtool_tmpfiles $stages\n+\n+\tlappend mtool_tmpfiles $backup\n+\n+\t# Force redirection to avoid interpreting output on stderr\n+\t# as an error, and launch the tool\n+\tlappend cmdline {2>@1}\n+\n+\tif {[catch { set mtool_fd [_open_stdout_stderr $cmdline] } err]} {\n+\t\tdelete_temp_files $mtool_tmpfiles\n+\t\terror_popup [mc \"Could not start the merge tool:\\n\\n%s\" $err]\n+\t\treturn\n+\t}\n+\n+\tui_status [mc \"Running merge tool...\"]\n+\n+\tfconfigure $mtool_fd -blocking 0 -translation binary -encoding binary\n+\tfileevent $mtool_fd readable [list read_mtool_output $mtool_fd]\n+}\n+\n+proc read_mtool_output {fd} {\n+\tglobal mtool_fd mtool_tmpfiles\n+\n+\tread $fd\n+\tif {[eof $fd]} {\n+\t\tunset mtool_fd\n+\n+\t\tfconfigure $fd -blocking 1\n+\t\tmerge_tool_finish $fd\n+\t}\n+}\n+\n+proc merge_tool_finish {fd} {\n+\tglobal mtool_tmpfiles mtool_target mtool_mtime\n+\n+\tset backup [lindex $mtool_tmpfiles end]\n+\tset failed 0\n+\n+\t# Check the return code\n+\tif {[catch {close $fd} err]} {\n+\t\tset failed 1\n+\t\tif {$err ne {child process exited abnormally}} {\n+\t\t\terror_popup [strcat [mc \"Merge tool failed.\"] \"\\n\\n$err\"]\n+\t\t}\n+\t}\n+\n+\t# Check the modification time of the target file\n+\tif {!$failed && [file mtime $mtool_target] eq $mtool_mtime} {\n+\t\tif {[ask_popup [mc \"File %s unchanged, still accept as resolved?\" \\\n+\t\t\t\t[short_path $mtool_target]]] ne {yes}} {\n+\t\t\tset failed 1\n+\t\t}\n+\t}\n+\n+\t# Finish\n+\tif {$failed} {\n+\t\tfile rename -force -- $backup $mtool_target\n+\t\tdelete_temp_files $mtool_tmpfiles\n+\t\tui_status [mc \"Merge tool failed.\"]\n+\t} else {\n+\t\tif {[is_config_true merge.keepbackup]} {\n+\t\t\tfile rename -force -- $backup \"$mtool_target.orig\"\n+\t\t}\n+\n+\t\tdelete_temp_files $mtool_tmpfiles\n+\n+\t\tmerge_add_resolution $mtool_target\n+\t}\n+}\ndiff --git a/lib/option.tcl b/lib/option.tcl\nindex eb958ff..dd979e2 100644\n--- a/lib/option.tcl\n+++ b/lib/option.tcl\n@@ -119,6 +119,7 @@ proc do_options {} {\n \t\t{b merge.summary {mc \"Summarize Merge Commits\"}}\n \t\t{i-1..5 merge.verbosity {mc \"Merge Verbosity\"}}\n \t\t{b merge.diffstat {mc \"Show Diffstat After Merge\"}}\n+\t\t{t merge.tool {mc \"Use Merge Tool\"}}\n \n \t\t{b gui.trustmtime  {mc \"Trust File Modification Timestamps\"}}\n \t\t{b gui.pruneduringfetch {mc \"Prune Tracking Branches During Fetch\"}}\n-- \n1.6.0.20.g6148bc\n"},{"id":"89259","messageId":"200808310059.47327.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200808310056.51324.angavrilov@gmail.com","subject":"[PATCH (GIT-GUI) 4/8] git-gui: Support more merge tools.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T20:59:47Z","receivedAt":"2008-08-30T20:59:47Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"Add native support for Araxis Merge, WinMerge and Perforce merge.\n\nCustom merge tools are not implemented by mergetool.tcl; besides,\nnative support allows constructing the command lines in a more\nintelligent way.\n\nSigned-off-by: Alexander Gavrilov <angavrilov@gmail.com>\n---\n lib/mergetool.tcl |   27 +++++++++++++++++++++++++++\n 1 files changed, 27 insertions(+), 0 deletions(-)\n\ndiff --git a/lib/mergetool.tcl b/lib/mergetool.tcl\nindex ba9e8ce..5f3da1f 100644\n--- a/lib/mergetool.tcl\n+++ b/lib/mergetool.tcl\n@@ -131,6 +131,7 @@ proc merge_resolve_tool2 {} {\n \tif {$merge_tool_path eq {}} {\n \t\tswitch -- $tool {\n \t\temerge { set merge_tool_path \"emacs\" }\n+\t\taraxis { set merge_tool_path \"compare\" }\n \t\tdefault { set merge_tool_path $tool }\n \t\t}\n \t}\n@@ -210,6 +211,31 @@ proc merge_resolve_tool2 {} {\n \t\t\t\t\t\"$LOCAL\" \"$REMOTE\" \"$basename\"]\n \t\t}\n \t}\n+\twinmerge {\n+\t\tif {$base_stage ne {}} {\n+\t\t\t# This tool does not support 3-way merges.\n+\t\t\t# Use the 'conflict file' resolution feature instead.\n+\t\t\tset cmdline [list \"$merge_tool_path\" -e -ub \"$MERGED\"]\n+\t\t} else {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -e -ub -wl \\\n+\t\t\t\t-dl \"Theirs File\" -dr \"Mine File\" \"$REMOTE\" \"$LOCAL\" \"$MERGED\"]\n+\t\t}\n+\t}\n+\taraxis {\n+\t\tif {$base_stage ne {}} {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -wait -merge -3 -a1 \\\n+\t\t\t\t-title1:\"'$MERGED (Base)'\" -title2:\"'$MERGED (Local)'\" \\\n+\t\t\t\t-title3:\"'$MERGED (Remote)'\" \\\n+\t\t\t\t\"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"]\n+\t\t} else {\n+\t\t\tset cmdline [list \"$merge_tool_path\" -wait -2 \\\n+\t\t\t\t -title1:\"'$MERGED (Local)'\" -title2:\"'$MERGED (Remote)'\" \\\n+\t\t\t\t \"$LOCAL\" \"$REMOTE\" \"$MERGED\"]\n+\t\t}\n+\t}\n+\tp4merge {\n+\t\tset cmdline [list \"$merge_tool_path\" \"$BASE\" \"$REMOTE\" \"$LOCAL\" \"$MERGED\"]\n+\t}\n \tvimdiff {\n \t\terror_popup [mc \"Not a GUI merge tool: '%s'\" $tool]\n \t\treturn\n@@ -236,6 +262,7 @@ proc merge_tool_get_stages {target stages} {\n \tforeach fname $stages {\n \t\tif {$merge_stages($i) eq {}} {\n \t\t\tfile delete $fname\n+\t\t\tcatch { close [open $fname w] }\n \t\t} else {\n \t\t\t# A hack to support autocrlf properly\n \t\t\tgit checkout-index -f --stage=$i -- $target\n-- \n1.6.0.20.g6148bc\n"},{"id":"89265","messageId":"200808310100.49998.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200808310059.47327.angavrilov@gmail.com","subject":"[PATCH (GIT-GUI) 5/8] git-gui: Support conflict states _U & UT.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T21:00:49Z","receivedAt":"2008-08-30T21:00:49Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"Support _U (local deleted, remote modified) and\nUT (file type changed in conflict) modes.\n\nNote that 'file type changed' does not refer to\nchanges in the executable bit, instead it denotes\nreplacing a file with a link, or vice versa.\n\nSigned-off-by: Alexander Gavrilov <angavrilov@gmail.com>\n---\n git-gui.sh     |    6 ++++--\n lib/commit.tcl |    1 +\n lib/diff.tcl   |    2 +-\n lib/index.tcl  |    1 +\n 4 files changed, 7 insertions(+), 3 deletions(-)\n\ndiff --git a/git-gui.sh b/git-gui.sh\nindex 8d4f715..a3000b6 100755\n--- a/git-gui.sh\n+++ b/git-gui.sh\n@@ -1669,10 +1669,12 @@ foreach i {\n \t\t{D_ {mc \"Staged for removal\"}}\n \t\t{DO {mc \"Staged for removal, still present\"}}\n \n+\t\t{_U {mc \"Requires merge resolution\"}}\n \t\t{U_ {mc \"Requires merge resolution\"}}\n \t\t{UU {mc \"Requires merge resolution\"}}\n \t\t{UM {mc \"Requires merge resolution\"}}\n \t\t{UD {mc \"Requires merge resolution\"}}\n+\t\t{UT {mc \"Requires merge resolution\"}}\n \t} {\n \tset text [eval [lindex $i 1]]\n \tif {$max_status_desc < [string length $text]} {\n@@ -1843,7 +1845,7 @@ proc toggle_or_diff {w x y} {\n \t# Do not stage files with conflicts\n \tif {[info exists file_states($path)]} {\n \t\tset state [lindex $file_states($path) 0]\n-\t\tif {[string index $state 0] eq {U}} {\n+\t\tif {[string first {U} $state] >= 0} {\n \t\t\tset col 1\n \t\t}\n \t}\n@@ -2814,7 +2816,7 @@ proc popup_diff_menu {ctxm ctxmmg x y X Y} {\n \t} else {\n \t\tset state {__}\n \t}\n-\tif {[string index $state 0] eq {U}} {\n+\tif {[string first {U} $state] >= 0} {\n \t\ttk_popup $ctxmmg $X $Y\n \t} else {\n \t\tif {$::ui_index eq $::current_diff_side} {\ndiff --git a/lib/commit.tcl b/lib/commit.tcl\nindex f4ab707..2977315 100644\n--- a/lib/commit.tcl\n+++ b/lib/commit.tcl\n@@ -151,6 +151,7 @@ The rescan will be automatically started now.\n \t\tD? -\n \t\tT_ -\n \t\tM? {set files_ready 1}\n+\t\t_U -\n \t\tU? {\n \t\t\terror_popup [mc \"Unmerged files cannot be committed.\n \ndiff --git a/lib/diff.tcl b/lib/diff.tcl\nindex 52b79e4..a7fcdc6 100644\n--- a/lib/diff.tcl\n+++ b/lib/diff.tcl\n@@ -166,7 +166,7 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {\n \t\tlappend cmd diff-index\n \t\tlappend cmd --cached\n \t} elseif {$w eq $ui_workdir} {\n-\t\tif {[string index $m 0] eq {U}} {\n+\t\tif {[string first {U} $m] >= 0} {\n \t\t\tlappend cmd diff\n \t\t} else {\n \t\t\tlappend cmd diff-files\ndiff --git a/lib/index.tcl b/lib/index.tcl\nindex 7c27f2a..06a4fb0 100644\n--- a/lib/index.tcl\n+++ b/lib/index.tcl\n@@ -164,6 +164,7 @@ proc write_update_index {fd pathList totalCnt batch after} {\n \t\t_O -\n \t\tAM {set new A_}\n \t\t_T {set new T_}\n+\t\t_U -\n \t\tU? {\n \t\t\tif {[file exists $path]} {\n \t\t\t\tset new M_\n-- \n1.6.0.20.g6148bc\n"},{"id":"89263","messageId":"200808310102.56795.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200808310100.49998.angavrilov@gmail.com","subject":"[PATCH (GIT-GUI) 6/8] git-gui: Reimplement and enhance auto-selection of diffs.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T21:02:56Z","receivedAt":"2008-08-30T21:02:56Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"Generalize the next_diff system, and implement auto-reselection\nfor merge tool resolution and reshow_diff. Also add auto-selection\nof diffs after rescan, if no diff is already selected.\n\nNew auto-select rules:\n\n- Rescan auto-selects the first conflicting file, or if none\n  a modified tracked file, if nothing was selected previously.\n- Resolving a conflict auto-selects the nearest conflicting\n  file, or nothing if everything is resolved.\n- Staging the last remaining hunk auto-selects the nearest\n  modified staged file.\n- Staging a file through its icon auto-selects the nearest file.\n\nSigned-off-by: Alexander Gavrilov <angavrilov@gmail.com>\n---\n git-gui.sh        |  122 +++++++++++++++++++++++++++++++++++++++++------------\n lib/diff.tcl      |   18 +++++---\n lib/mergetool.tcl |    8 +---\n 3 files changed, 109 insertions(+), 39 deletions(-)\n\ndiff --git a/git-gui.sh b/git-gui.sh\nindex a3000b6..f84f436 100755\n--- a/git-gui.sh\n+++ b/git-gui.sh\n@@ -1326,6 +1326,8 @@ proc rescan_done {fd buf after} {\n \tunlock_index\n \tdisplay_all_files\n \tif {$current_diff_path ne {}} reshow_diff\n+\tif {$current_diff_path eq {}} select_first_diff\n+\n \tuplevel #0 $after\n }\n \n@@ -1821,7 +1823,98 @@ proc do_commit {} {\n \n proc next_diff {} {\n \tglobal next_diff_p next_diff_w next_diff_i\n-\tshow_diff $next_diff_p $next_diff_w $next_diff_i\n+\tshow_diff $next_diff_p $next_diff_w {}\n+}\n+\n+proc find_anchor_pos {lst name} {\n+\tset lid [lsearch -sorted -exact $lst $name]\n+\n+\tif {$lid == -1} {\n+\t\tset lid 0\n+\t\tforeach lname $lst {\n+\t\t\tif {$lname >= $name} break\n+\t\t\tincr lid\n+\t\t}\n+\t}\n+\n+\treturn $lid\n+}\n+\n+proc find_file_from {flist idx delta path mmask} {\n+\tglobal file_states\n+\n+\tset len [llength $flist]\n+\twhile {$idx >= 0 && $idx < $len} {\n+\t\tset name [lindex $flist $idx]\n+\n+\t\tif {$name ne $path && [info exists file_states($name)]} {\n+\t\t\tset state [lindex $file_states($name) 0]\n+\n+\t\t\tif {$mmask eq {} || [regexp $mmask $state]} {\n+\t\t\t\treturn $idx\n+\t\t\t}\n+\t\t}\n+\n+\t\tincr idx $delta\n+\t}\n+\n+\treturn {}\n+}\n+\n+proc find_next_diff {w path {lno {}} {mmask {}}} {\n+\tglobal next_diff_p next_diff_w next_diff_i\n+\tglobal file_lists ui_index ui_workdir\n+\n+\tset flist $file_lists($w)\n+\tif {$lno eq {}} {\n+\t\tset lno [find_anchor_pos $flist $path]\n+\t} else {\n+\t\tincr lno -1\n+\t}\n+\n+\tif {$mmask ne {} && ![regexp {(^\\^)|(\\$$)} $mmask]} {\n+\t\tif {$w eq $ui_index} {\n+\t\t\tset mmask \"^$mmask\"\n+\t\t} else {\n+\t\t\tset mmask \"$mmask\\$\"\n+\t\t}\n+\t}\n+\n+\tset idx [find_file_from $flist $lno 1 $path $mmask]\n+\tif {$idx eq {}} {\n+\t\tincr lno -1\n+\t\tset idx [find_file_from $flist $lno -1 $path $mmask]\n+\t}\n+\n+\tif {$idx ne {}} {\n+\t\tset next_diff_w $w\n+\t\tset next_diff_p [lindex $flist $idx]\n+\t\tset next_diff_i [expr {$idx+1}]\n+\t\treturn 1\n+\t} else {\n+\t\treturn 0\n+\t}\n+}\n+\n+proc next_diff_after_action {w path {lno {}} {mmask {}}} {\n+\tglobal current_diff_path\n+\n+\tif {$path ne $current_diff_path} {\n+\t\treturn {}\n+\t} elseif {[find_next_diff $w $path $lno $mmask]} {\n+\t\treturn {next_diff;}\n+\t} else {\n+\t\treturn {reshow_diff;}\n+\t}\n+}\n+\n+proc select_first_diff {} {\n+\tglobal ui_workdir\n+\n+\tif {[find_next_diff $ui_workdir {} 1 {^_?U}] ||\n+\t    [find_next_diff $ui_workdir {} 1 {[^O]$}]} {\n+\t\tnext_diff\n+\t}\n }\n \n proc toggle_or_diff {w x y} {\n@@ -1851,32 +1944,7 @@ proc toggle_or_diff {w x y} {\n \t}\n \n \tif {$col == 0 && $y > 1} {\n-\t\tset i [expr {$lno-1}]\n-\t\tset ll [expr {[llength $file_lists($w)]-1}]\n-\n-\t\tif {$i == $ll && $i == 0} {\n-\t\t\tset after {reshow_diff;}\n-\t\t} else {\n-\t\t\tglobal next_diff_p next_diff_w next_diff_i\n-\n-\t\t\tset next_diff_w $w\n-\n-\t\t\tif {$i < $ll} {\n-\t\t\t\tset i [expr {$i + 1}]\n-\t\t\t\tset next_diff_i $i\n-\t\t\t} else {\n-\t\t\t\tset next_diff_i $i\n-\t\t\t\tset i [expr {$i - 1}]\n-\t\t\t}\n-\n-\t\t\tset next_diff_p [lindex $file_lists($w) $i]\n-\n-\t\t\tif {$next_diff_p ne {} && $current_diff_path ne {}} {\n-\t\t\t\tset after {next_diff;}\n-\t\t\t} else {\n-\t\t\t\tset after {}\n-\t\t\t}\n-\t\t}\n+\t\tset after [next_diff_after_action $w $path $lno]\n \n \t\tif {$w eq $ui_index} {\n \t\t\tupdate_indexinfo \\\ndiff --git a/lib/diff.tcl b/lib/diff.tcl\nindex a7fcdc6..95998b4 100644\n--- a/lib/diff.tcl\n+++ b/lib/diff.tcl\n@@ -24,10 +24,16 @@ proc reshow_diff {} {\n \tset p $current_diff_path\n \tif {$p eq {}} {\n \t\t# No diff is being shown.\n-\t} elseif {$current_diff_side eq {}\n-\t\t|| [catch {set s $file_states($p)}]\n-\t\t|| [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {\n+\t} elseif {$current_diff_side eq {}} {\n \t\tclear_diff\n+\t} elseif {[catch {set s $file_states($p)}]\n+\t\t|| [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {\n+\n+\t\tif {[find_next_diff $current_diff_side $p {} {[^O]}]} {\n+\t\t\tnext_diff\n+\t\t} else {\n+\t\t\tclear_diff\n+\t\t}\n \t} else {\n \t\tset save_pos [lindex [$ui_diff yview] 0]\n \t\tshow_diff $p $current_diff_side {} $save_pos\n@@ -71,6 +77,7 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {\n \t}\n \tif {$lno >= 1} {\n \t\t$w tag add in_diff $lno.0 [expr {$lno + 1}].0\n+\t\t$w see $lno.0\n \t}\n \n \tset s $file_states($path)\n@@ -366,10 +373,9 @@ proc apply_hunk {x y} {\n \t}\n \tunlock_index\n \tdisplay_file $current_diff_path $mi\n+\t# This should trigger shift to the next changed file\n \tif {$o eq {_}} {\n-\t\tclear_diff\n-\t} else {\n-\t\tset current_diff_path $current_diff_path\n+\t\treshow_diff\n \t}\n }\n \ndiff --git a/lib/mergetool.tcl b/lib/mergetool.tcl\nindex 5f3da1f..79c58bc 100644\n--- a/lib/mergetool.tcl\n+++ b/lib/mergetool.tcl\n@@ -24,13 +24,9 @@ This operation can be undone only by restarting the merge.\" \\\n }\n \n proc merge_add_resolution {path} {\n-\tglobal current_diff_path\n+\tglobal current_diff_path ui_workdir\n \n-\tif {$path eq $current_diff_path} {\n-\t\tset after {reshow_diff;}\n-\t} else {\n-\t\tset after {}\n-\t}\n+\tset after [next_diff_after_action $ui_workdir $path {} {^_?U}]\n \n \tupdate_index \\\n \t\t[mc \"Adding resolution for %s\" [short_path $path]] \\\n-- \n1.6.0.20.g6148bc\n"},{"id":"89266","messageId":"200808310104.11048.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200808310102.56795.angavrilov@gmail.com","subject":"[PATCH (GIT-GUI) 7/8] git-gui: Make F5 reselect a diff, if an untracked file is selected.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T21:04:10Z","receivedAt":"2008-08-30T21:04:10Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"If an untracked file is selected, F5 and other manual rescan synonyms\nwould try to select a tracked file instead. Also, clicking on an icon\nin the unstaged changes list skips over untracked files, unless the\nfile clicked is untracked itself.\n\nThe objective is to make it easier to ignore untracked files showing\nup in the Unstaged Changes list, and ensure that no modifications\nto tracked objects are left unstaged.\n\nSigned-off-by: Alexander Gavrilov <angavrilov@gmail.com>\n---\n git-gui.sh |   44 +++++++++++++++++++++++++++++++++++---------\n 1 files changed, 35 insertions(+), 9 deletions(-)\n\ndiff --git a/git-gui.sh b/git-gui.sh\nindex f84f436..fde44a6 100755\n--- a/git-gui.sh\n+++ b/git-gui.sh\n@@ -1817,6 +1817,10 @@ proc do_rescan {} {\n \trescan ui_ready\n }\n \n+proc ui_do_rescan {} {\n+\trescan {force_first_diff; ui_ready}\n+}\n+\n proc do_commit {} {\n \tcommit_tree\n }\n@@ -1917,6 +1921,18 @@ proc select_first_diff {} {\n \t}\n }\n \n+proc force_first_diff {} {\n+\tglobal current_diff_path\n+\n+\tif {[info exists file_states($current_diff_path)]} {\n+\t\tset state [lindex $file_states($current_diff_path) 0]\n+\n+\t\tif {[string index $state 1] ne {O}} return\n+\t}\n+\n+\tselect_first_diff\n+}\n+\n proc toggle_or_diff {w x y} {\n \tglobal file_states file_lists current_diff_path ui_index ui_workdir\n \tglobal last_clicked selected_paths\n@@ -1938,13 +1954,23 @@ proc toggle_or_diff {w x y} {\n \t# Do not stage files with conflicts\n \tif {[info exists file_states($path)]} {\n \t\tset state [lindex $file_states($path) 0]\n-\t\tif {[string first {U} $state] >= 0} {\n-\t\t\tset col 1\n-\t\t}\n+\t} else {\n+\t\tset state {__}\n+\t}\n+\n+\tif {[string first {U} $state] >= 0} {\n+\t\tset col 1\n \t}\n \n+\t# Restage the file, or simply show the diff\n \tif {$col == 0 && $y > 1} {\n-\t\tset after [next_diff_after_action $w $path $lno]\n+\t\tif {[string index $state 1] eq {O}} {\n+\t\t\tset mmask {}\n+\t\t} else {\n+\t\t\tset mmask {[^O]}\n+\t\t}\n+\n+\t\tset after [next_diff_after_action $w $path $lno $mmask]\n \n \t\tif {$w eq $ui_index} {\n \t\t\tupdate_indexinfo \\\n@@ -2208,7 +2234,7 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {\n \t.mbar.commit add separator\n \n \t.mbar.commit add command -label [mc Rescan] \\\n-\t\t-command do_rescan \\\n+\t\t-command ui_do_rescan \\\n \t\t-accelerator F5\n \tlappend disable_on_lock \\\n \t\t[list .mbar.commit entryconf [.mbar.commit index last] -state]\n@@ -2564,7 +2590,7 @@ pack .vpane.lower.commarea.buttons.l -side top -fill x\n pack .vpane.lower.commarea.buttons -side left -fill y\n \n button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \\\n-\t-command do_rescan\n+\t-command ui_do_rescan\n pack .vpane.lower.commarea.buttons.rescan -side top -fill x\n lappend disable_on_lock \\\n \t{.vpane.lower.commarea.buttons.rescan conf -state}\n@@ -2985,9 +3011,9 @@ if {[is_enabled transport]} {\n \tbind . <$M1B-Key-P> do_push_anywhere\n }\n \n-bind .   <Key-F5>     do_rescan\n-bind .   <$M1B-Key-r> do_rescan\n-bind .   <$M1B-Key-R> do_rescan\n+bind .   <Key-F5>     ui_do_rescan\n+bind .   <$M1B-Key-r> ui_do_rescan\n+bind .   <$M1B-Key-R> ui_do_rescan\n bind .   <$M1B-Key-s> do_signoff\n bind .   <$M1B-Key-S> do_signoff\n bind .   <$M1B-Key-t> do_add_selection\n-- \n1.6.0.20.g6148bc\n"},{"id":"89264","messageId":"200808310105.23106.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200808310104.11048.angavrilov@gmail.com","subject":"[PATCH (GIT-GUI) 8/8] git-gui: Show special diffs for complex conflict cases.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-08-30T21:05:22Z","receivedAt":"2008-08-30T21:05:22Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"Add special handling for displaying diffs of modified/deleted,\nand symlink/mode conflicts. Currently the display is completely\nunusable for deciding how to resolve the conflict.\n\nNew display modes:\n\n1) Deleted/Modified conflict: e.g.\n\tLOCAL: deleted\n\tREMOTE:\n\t[diff :1:$path :3:$path]\n\n2) Conflict involving symlinks:\n\tLOCAL:\n\t[diff :1:$path :2:$path]\n\tREMOTE:\n\t[diff :1:$path :3:$path]\n\nIn order to be able to display multiple diffs, this\npatch adds a queue of commands to call.\n\nSigned-off-by: Alexander Gavrilov <angavrilov@gmail.com>\n---\n lib/diff.tcl |   94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 files changed, 89 insertions(+), 5 deletions(-)\n\ndiff --git a/lib/diff.tcl b/lib/diff.tcl\nindex 95998b4..c67b020 100644\n--- a/lib/diff.tcl\n+++ b/lib/diff.tcl\n@@ -65,6 +65,7 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {\n \tglobal is_3way_diff diff_active repo_config\n \tglobal ui_diff ui_index ui_workdir\n \tglobal current_diff_path current_diff_side current_diff_header\n+\tglobal current_diff_queue\n \n \tif {$diff_active || ![lock_index read]} return\n \n@@ -82,13 +83,69 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {\n \n \tset s $file_states($path)\n \tset m [lindex $s 0]\n-\tset is_3way_diff 0\n-\tset diff_active 1\n \tset current_diff_path $path\n \tset current_diff_side $w\n-\tset current_diff_header {}\n+\tset current_diff_queue {}\n \tui_status [mc \"Loading diff of %s...\" [escape_path $path]]\n \n+\tif {[string first {U} $m] >= 0} {\n+\t\tmerge_load_stages $path [list show_unmerged_diff $scroll_pos]\n+\t} elseif {$m eq {_O}} {\n+\t\tshow_other_diff $path $w $m $scroll_pos\n+\t} else {\n+\t\tstart_show_diff $scroll_pos\n+\t}\n+}\n+\n+proc show_unmerged_diff {scroll_pos} {\n+\tglobal current_diff_path current_diff_side\n+\tglobal merge_stages ui_diff\n+\tglobal current_diff_queue\n+\n+\tif {$merge_stages(2) eq {}} {\n+\t\tlappend current_diff_queue \\\n+\t\t\t[list \"LOCAL: deleted\\nREMOTE:\\n\" d======= \\\n+\t\t\t    [list \":1:$current_diff_path\" \":3:$current_diff_path\"]]\n+\t} elseif {$merge_stages(3) eq {}} {\n+\t\tlappend current_diff_queue \\\n+\t\t\t[list \"REMOTE: deleted\\nLOCAL:\\n\" d======= \\\n+\t\t\t    [list \":1:$current_diff_path\" \":2:$current_diff_path\"]]\n+\t} elseif {[lindex $merge_stages(1) 0] eq {120000}\n+\t\t|| [lindex $merge_stages(2) 0] eq {120000}\n+\t\t|| [lindex $merge_stages(3) 0] eq {120000}} {\n+\t\tlappend current_diff_queue \\\n+\t\t\t[list \"LOCAL:\\n\" d======= \\\n+\t\t\t    [list \":1:$current_diff_path\" \":2:$current_diff_path\"]]\n+\t\tlappend current_diff_queue \\\n+\t\t\t[list \"REMOTE:\\n\" d======= \\\n+\t\t\t    [list \":1:$current_diff_path\" \":3:$current_diff_path\"]]\n+\t} else {\n+\t\tstart_show_diff $scroll_pos\n+\t\treturn\n+\t}\n+\n+\tadvance_diff_queue $scroll_pos\n+}\n+\n+proc advance_diff_queue {scroll_pos} {\n+\tglobal current_diff_queue ui_diff\n+\n+\tset item [lindex $current_diff_queue 0]\n+\tset current_diff_queue [lrange $current_diff_queue 1 end]\n+\n+\t$ui_diff conf -state normal\n+\t$ui_diff insert end [lindex $item 0] [lindex $item 1]\n+\t$ui_diff conf -state disabled\n+\n+\tstart_show_diff $scroll_pos [lindex $item 2]\n+}\n+\n+proc show_other_diff {path w m scroll_pos} {\n+\tglobal file_states file_lists\n+\tglobal is_3way_diff diff_active repo_config\n+\tglobal ui_diff ui_index ui_workdir\n+\tglobal current_diff_path current_diff_side current_diff_header\n+\n \t# - Git won't give us the diff, there's nothing to compare to!\n \t#\n \tif {$m eq {_O}} {\n@@ -167,6 +224,22 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {\n \t\tui_ready\n \t\treturn\n \t}\n+}\n+\n+proc start_show_diff {scroll_pos {add_opts {}}} {\n+\tglobal file_states file_lists\n+\tglobal is_3way_diff diff_active repo_config\n+\tglobal ui_diff ui_index ui_workdir\n+\tglobal current_diff_path current_diff_side current_diff_header\n+\n+\tset path $current_diff_path\n+\tset w $current_diff_side\n+\n+\tset s $file_states($path)\n+\tset m [lindex $s 0]\n+\tset is_3way_diff 0\n+\tset diff_active 1\n+\tset current_diff_header {}\n \n \tset cmd [list]\n \tif {$w eq $ui_index} {\n@@ -188,8 +261,12 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {\n \tif {$w eq $ui_index} {\n \t\tlappend cmd [PARENT]\n \t}\n-\tlappend cmd --\n-\tlappend cmd $path\n+\tif {$add_opts ne {}} {\n+\t\teval lappend cmd $add_opts\n+\t} else {\n+\t\tlappend cmd --\n+\t\tlappend cmd $path\n+\t}\n \n \tif {[catch {set fd [eval git_read --nice $cmd]} err]} {\n \t\tset diff_active 0\n@@ -209,6 +286,7 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {\n proc read_diff {fd scroll_pos} {\n \tglobal ui_diff diff_active\n \tglobal is_3way_diff current_diff_header\n+\tglobal current_diff_queue\n \n \t$ui_diff conf -state normal\n \twhile {[gets $fd line] >= 0} {\n@@ -293,6 +371,12 @@ proc read_diff {fd scroll_pos} {\n \n \tif {[eof $fd]} {\n \t\tclose $fd\n+\n+\t\tif {$current_diff_queue ne {}} {\n+\t\t\tadvance_diff_queue $scroll_pos\n+\t\t\treturn\n+\t\t}\n+\n \t\tset diff_active 0\n \t\tunlock_index\n \t\tif {$scroll_pos ne {}} {\n-- \n1.6.0.20.g6148bc\n"},{"id":"90073","messageId":"48C5161E.3020402@viscovery.net","threadId":"15290","inReplyTo":"200808310054.19732.angavrilov@gmail.com","subject":"Re: [PATCH (GIT-GUI) 1/8] git-gui: Don't allow staging files with conflicts.","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-09-08T12:10:06Z","receivedAt":"2008-09-08T12:10:06Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Alexander Gavrilov schrieb:\n> Prevent staging files with conflict markers by clicking\n> on the icon in the 'Unstaged Changes' list. Instead, pretend\n> that the user clicked the name, and show the diff.\n> \n> Originally it made some sense to allow staging conflicting\n> files, because git-gui did not provide any tools to resolve\n> them from within the GUI. But now that I'm adding mergetool\n> capabilities, it is more likely to cause accidental and\n> non-undoable errors.\n\nI know I'm a bit late... I just got the first impression of the\nconsequences of this patch. And I don't like them.\n\nI did a merge with conflicts, and the result was resolved to my liking by\nrerere. (And even if rerere did not kick in - I'd have resolved the\nconflicts in an external editor anyway.) Now I want to stage the file -\nbut I can't. :-(\n\nNo, I can't \"Run merge tool\", because I don't have one.\nNo, I don't want to \"Use remote version\" or \"Use local version\" or \"Revert\nto base\".\nYes, I want to stage the file _as_is_!!\n\nPlease help.\n\n-- Hannes\n"},{"id":"90076","messageId":"bb6f213e0809080525t5da18021jf8f564a748545468@mail.gmail.com","threadId":"15290","inReplyTo":"48C5161E.3020402@viscovery.net","subject":"Re: [PATCH (GIT-GUI) 1/8] git-gui: Don't allow staging files with conflicts.","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-09-08T12:25:00Z","receivedAt":"2008-09-08T12:25:00Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"> I know I'm a bit late... I just got the first impression of the\n> consequences of this patch. And I don't like them.\n>\n> I did a merge with conflicts, and the result was resolved to my liking by\n> rerere. (And even if rerere did not kick in - I'd have resolved the\n> conflicts in an external editor anyway.) Now I want to stage the file -\n> but I can't. :-(\n>\n> No, I can't \"Run merge tool\", because I don't have one.\n> No, I don't want to \"Use remote version\" or \"Use local version\" or \"Revert\n> to base\".\n> Yes, I want to stage the file _as_is_!!\n>\n> Please help.\n\nI'll make a patch today to add a new item to the popup menu.\n\nI don't use rerere much, so I didn't notice this problem myself until\nyesterday evening.\n\nAlexander\n"},{"id":"90925","messageId":"1221651652-3712-1-git-send-email-johannes.sixt@telecom.at","threadId":"15290","inReplyTo":"200808310052.21595.angavrilov@gmail.com","subject":"[PATCH/RFC 0/2] git-gui: issues with merge tool series","fromName":"Johannes Sixt","fromEmail":"johannes.sixt@telecom.at","sentAt":"2008-09-17T11:40:50Z","receivedAt":"2008-09-17T11:40:50Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"I've now worked a while with the latest git-gui that includes the\ncapability to run a merge tool, and I must say that there is room\nfor improvement.\n\nI greatly appreciate that a merge tool can be invoked. This is really\nhelpful.\n\nBut...  I've nevertheless two *big* issues with that:\n\n1. The inability to stage a conflicted file by clicking the icon is\n   *very* disruptive. The new menu entry \"Stage Working Copy\" is\n   really only a workaround, and it shows.\n\n2. The fact that a file was automatically staged after the merge\n   tool was exited has caused headaches that the merge tool should\n   actually cure. Because of this behavior, I could not postpone a\n   conflict resolution once I have started the merge tool, nor can\n   I re-run the tool if I forgot to resolve a conflict.\n\nThis patch series addresses these issues.\n\nFurthermore, the operations \"Use local version\" and \"Use remote\nversion\" could be much more useful. They could serve as \"merge\nshortcuts\": These operations should only use the \"local\" or \"remote\"\npart inside the conflict markers, and not also reset the part that\nis not visible. In the current form I found them useful only in the\nmodify/delete conflict cases, where I picked the \"delete\" part.\n\nHTH,\n-- Hannes\n"},{"id":"90927","messageId":"1221651652-3712-2-git-send-email-johannes.sixt@telecom.at","threadId":"15290","inReplyTo":"1221651652-3712-1-git-send-email-johannes.sixt@telecom.at","subject":"[PATCH/RFC 1/2] Revert \"git-gui: Don't allow staging files with conflicts.\"","fromName":"Johannes Sixt","fromEmail":"johannes.sixt@telecom.at","sentAt":"2008-09-17T11:40:51Z","receivedAt":"2008-09-17T11:40:51Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"This reverts commits 617ceee653bd7377f662bfc6d3085f321efab7e4 (Don't allow\nstaging files with conflicts) and 2fe5b2ee42897a3acc78e5ddaace3775eb2713ca\n(Restore ability to Stage Working Copy for conflicts).\n\nThe inability to use the icon in front of the file name to stage a file,\neven a conflicted one, is too disruptive for the workflow. It was intended\nas a safety valve against accidentally staging the conflicted file, which\nwould remove the conflict stages from the index and inhibit the use of\nthe merge tool. The new menu item \"Stage Working Copy\" that the second\nreverted commit introduced helped a bit, but it clearly was just a\nworkaround.\n\nSigned-off-by: Johannes Sixt <johannes.sixt@telecom.at>\n---\n git-gui.sh        |   10 ----------\n lib/mergetool.tcl |    6 ------\n 2 files changed, 0 insertions(+), 16 deletions(-)\n\ndiff --git a/git-gui.sh b/git-gui.sh\nindex 1044ab9..3b1532c 100755\n--- a/git-gui.sh\n+++ b/git-gui.sh\n@@ -1954,17 +1954,12 @@ proc toggle_or_diff {w x y} {\n \t$ui_index tag remove in_sel 0.0 end\n \t$ui_workdir tag remove in_sel 0.0 end\n \n-\t# Do not stage files with conflicts\n \tif {[info exists file_states($path)]} {\n \t\tset state [lindex $file_states($path) 0]\n \t} else {\n \t\tset state {__}\n \t}\n \n-\tif {[string first {U} $state] >= 0} {\n-\t\tset col 1\n-\t}\n-\n \t# Restage the file, or simply show the diff\n \tif {$col == 0 && $y > 1} {\n \t\tif {[string index $state 1] eq {O}} {\n@@ -2902,11 +2897,6 @@ $ctxmmg add command \\\n \t-command {merge_resolve_one 1}\n lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]\n $ctxmmg add separator\n-$ctxmmg add command \\\n-\t-label [mc \"Stage Working Copy\"] \\\n-\t-command {merge_resolve_one 0}\n-lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]\n-$ctxmmg add separator\n create_common_diff_popup $ctxmmg\n \n proc popup_diff_menu {ctxm ctxmmg x y X Y} {\ndiff --git a/lib/mergetool.tcl b/lib/mergetool.tcl\nindex a44a725..965cfe4 100644\n--- a/lib/mergetool.tcl\n+++ b/lib/mergetool.tcl\n@@ -5,12 +5,6 @@ proc merge_resolve_one {stage} {\n \tglobal current_diff_path\n \n \tswitch -- $stage {\n-\t\t0 {\t# Stage without confirmation, to minimize\n-\t\t\t# disruption of the rerere workflow\n-\t\t\tmerge_add_resolution $current_diff_path\n-\t\t\treturn\n-\t\t}\n-\n \t\t1 { set targetquestion [mc \"Force resolution to the base version?\"] }\n \t\t2 { set targetquestion [mc \"Force resolution to this branch?\"] }\n \t\t3 { set targetquestion [mc \"Force resolution to the other branch?\"] }\n-- \n1.6.0.1.1210.gb7ffe\n"},{"id":"90926","messageId":"1221651652-3712-3-git-send-email-johannes.sixt@telecom.at","threadId":"15290","inReplyTo":"1221651652-3712-2-git-send-email-johannes.sixt@telecom.at","subject":"[PATCH/RFC 2/2] git-gui: Do not automatically stage file after merge tool finishes","fromName":"Johannes Sixt","fromEmail":"johannes.sixt@telecom.at","sentAt":"2008-09-17T11:40:52Z","receivedAt":"2008-09-17T11:40:52Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"If a merge tool was invoked on a conflicted file and the tool completed,\nthen the conflicted file was staged automatically. However, the fact that\nthe user closed the merge tool cannot be understood as the unequivocal\nsign that the conflict was completely resolved. For example, the user\ncould have decided to postpone the resolution of the conflict, or could\nhave accidentally closed the tool. We better leave the file unstaged and\nlet the user stage it explicitly.\n\nSigned-off-by: Johannes Sixt <johannes.sixt@telecom.at>\n---\n lib/mergetool.tcl |    2 --\n 1 files changed, 0 insertions(+), 2 deletions(-)\n\ndiff --git a/lib/mergetool.tcl b/lib/mergetool.tcl\nindex 965cfe4..1a96189 100644\n--- a/lib/mergetool.tcl\n+++ b/lib/mergetool.tcl\n@@ -367,7 +367,5 @@ proc merge_tool_finish {fd} {\n \t\t}\n \n \t\tdelete_temp_files $mtool_tmpfiles\n-\n-\t\tmerge_add_resolution $mtool_target\n \t}\n }\n-- \n1.6.0.1.1210.gb7ffe\n"},{"id":"90929","messageId":"bb6f213e0809170525q286f5d42xfd734660dc935cbf@mail.gmail.com","threadId":"15290","inReplyTo":"1221651652-3712-3-git-send-email-johannes.sixt@telecom.at","subject":"Re: [PATCH/RFC 2/2] git-gui: Do not automatically stage file after merge tool finishes","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-09-17T12:25:47Z","receivedAt":"2008-09-17T12:25:47Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"On Wed, Sep 17, 2008 at 3:40 PM, Johannes Sixt <johannes.sixt@telecom.at> wrote:\n> If a merge tool was invoked on a conflicted file and the tool completed,\n> then the conflicted file was staged automatically. However, the fact that\n> the user closed the merge tool cannot be understood as the unequivocal\n> sign that the conflict was completely resolved. For example, the user\n> could have decided to postpone the resolution of the conflict, or could\n> have accidentally closed the tool. We better leave the file unstaged and\n> let the user stage it explicitly.\n\nIt completely reproduces the logic that git-mergetool uses. Namely, if\nthe file is unchanged, it asks explicitly, and if the tool returns a\nnon-zero exit code, it does not stage at all.\n\nYou also cannot simply remove merge_add_resolution, because then it\nwould leave the diff view stale.\n"},{"id":"90935","messageId":"bb6f213e0809170550q5be339d1s825f95b1165e6507@mail.gmail.com","threadId":"15290","inReplyTo":"1221651652-3712-1-git-send-email-johannes.sixt@telecom.at","subject":"Re: [PATCH/RFC 0/2] git-gui: issues with merge tool series","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-09-17T12:50:17Z","receivedAt":"2008-09-17T12:50:17Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"On Wed, Sep 17, 2008 at 3:40 PM, Johannes Sixt <johannes.sixt@telecom.at> wrote:\n> 1. The inability to stage a conflicted file by clicking the icon is\n>   *very* disruptive. The new menu entry \"Stage Working Copy\" is\n>   really only a workaround, and it shows.\n\nI can see two ways to fix it:\n\n1) Allow that icon to work only if the diff is currently displayed,\nand also ask for confirmation if there are any conflict markers\npresent.\n\n   Problem: What should it do with modify/delete conflicts, which\ndon't have any conflict markers?\n\n2) Much harder: implement complete one-click undo. This involves\nsaving information from the index somewhere, and forcing such items to\nremain in the 'staged' list, even if the index isn't different from\nthe tree version any more.\n\n   By the way, is there a simple way to re-create a conflict file from\nthe saved multistage index entries?\n\n\nProbably the best fix is some combination of these two.\n\n\n> Furthermore, the operations \"Use local version\" and \"Use remote\n> version\" could be much more useful. They could serve as \"merge\n> shortcuts\": These operations should only use the \"local\" or \"remote\"\n> part inside the conflict markers, and not also reset the part that\n> is not visible. In the current form I found them useful only in the\n> modify/delete conflict cases, where I picked the \"delete\" part.\n\nThat was simply a reimplementation of git-mergetool with proper GUI.\nIt cannot do per-hunk resolution either...\n\n\nAlexander\n"},{"id":"90976","messageId":"200809172340.16367.johannes.sixt@telecom.at","threadId":"15290","inReplyTo":"bb6f213e0809170550q5be339d1s825f95b1165e6507@mail.gmail.com","subject":"Re: [PATCH/RFC 0/2] git-gui: issues with merge tool series","fromName":"Johannes Sixt","fromEmail":"johannes.sixt@telecom.at","sentAt":"2008-09-17T21:40:16Z","receivedAt":"2008-09-17T21:40:16Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"On Mittwoch, 17. September 2008, Alexander Gavrilov wrote:\n> On Wed, Sep 17, 2008 at 3:40 PM, Johannes Sixt <johannes.sixt@telecom.at> \nwrote:\n> > 1. The inability to stage a conflicted file by clicking the icon is\n> >   *very* disruptive. The new menu entry \"Stage Working Copy\" is\n> >   really only a workaround, and it shows.\n>\n> I can see two ways to fix it:\n>\n> 1) Allow that icon to work only if the diff is currently displayed,\n> and also ask for confirmation if there are any conflict markers\n> present.\n\nGood.\n\n>    Problem: What should it do with modify/delete conflicts, which\n> don't have any conflict markers?\n\nStage the file, i.e. pick the 'modify' part (perhaps with confirmation). \nRationale: That's what the user sees in the diff pane. Additionally, keep \nthe \"Use local/remote version\" entries so that the 'delete' part can be \nchosen from the context menu.\n\n> 2) Much harder: implement complete one-click undo. This involves\n> saving information from the index somewhere, and forcing such items to\n> remain in the 'staged' list, even if the index isn't different from\n> the tree version any more.\n>\n>    By the way, is there a simple way to re-create a conflict file from\n> the saved multistage index entries?\n\nThere's 0cf8581e (checkout -m: recreate merge when checking out of unmerged \nindex) in 'next'. But that does not address how the unmerged entries get into \nthe index.\n\n-- Hannes\n"},{"id":"90978","messageId":"200809180224.03833.angavrilov@gmail.com","threadId":"15290","inReplyTo":"200809172340.16367.johannes.sixt@telecom.at","subject":"Re: [PATCH/RFC 0/2] git-gui: issues with merge tool series","fromName":"Alexander Gavrilov","fromEmail":"angavrilov@gmail.com","sentAt":"2008-09-17T22:24:03Z","receivedAt":"2008-09-17T22:24:03Z","isPatch":true,"sender":{"key":"angavrilov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42666?v=4"},"body":"On Thursday 18 September 2008 01:40:16 Johannes Sixt wrote:\n> On Mittwoch, 17. September 2008, Alexander Gavrilov wrote:\n> >    By the way, is there a simple way to re-create a conflict file from\n> > the saved multistage index entries?\n> \n> There's 0cf8581e (checkout -m: recreate merge when checking out of unmerged \n> index) in 'next'. But that does not address how the unmerged entries get into \n> the index.\n\nIf everything else fails, the entries can be fetched from the index and\ncached in RAM by git-gui itself. Then it will be able to put them back\nvia update-index --index-info.\n\nAlexander\n"},{"id":"91497","messageId":"20080924174823.GY3669@spearce.org","threadId":"15290","inReplyTo":"bb6f213e0809170550q5be339d1s825f95b1165e6507@mail.gmail.com","subject":"Re: [PATCH/RFC 0/2] git-gui: issues with merge tool series","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-09-24T17:48:23Z","receivedAt":"2008-09-24T17:48:23Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Alexander Gavrilov <angavrilov@gmail.com> wrote:\n> On Wed, Sep 17, 2008 at 3:40 PM, Johannes Sixt <johannes.sixt@telecom.at> wrote:\n> > 1. The inability to stage a conflicted file by clicking the icon is\n> >   *very* disruptive. The new menu entry \"Stage Working Copy\" is\n> >   really only a workaround, and it shows.\n> \n> I can see two ways to fix it:\n> \n> 1) Allow that icon to work only if the diff is currently displayed,\n> and also ask for confirmation if there are any conflict markers\n> present.\n> \n>    Problem: What should it do with modify/delete conflicts, which\n> don't have any conflict markers?\n\nI'm inclined to say we should just let the icon work like it used\nto, stage the whole working copy.  That's exactly what it does on\na non-merge, and thus is what it should do on a merge.\n\nAs a safety feature we can scan the working copy for merge marks if\nwe are currently in a merge and prompt the user for confirmation\nif it looks like they are merging an unmerged working copy.\n \n> 2) Much harder: implement complete one-click undo. This involves\n> saving information from the index somewhere, and forcing such items to\n> remain in the 'staged' list, even if the index isn't different from\n> the tree version any more.\n> \n>    By the way, is there a simple way to re-create a conflict file from\n> the saved multistage index entries?\n\nRun git-merge-file after extracting the stages to temporary files.\nYou can also jam the stages back into the index with update-index\n--index-info.\n\n-- \nShawn.\n"},{"id":"91498","messageId":"20080924175033.GZ3669@spearce.org","threadId":"15290","inReplyTo":"bb6f213e0809170525q286f5d42xfd734660dc935cbf@mail.gmail.com","subject":"Re: [PATCH/RFC 2/2] git-gui: Do not automatically stage file after merge tool finishes","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-09-24T17:50:33Z","receivedAt":"2008-09-24T17:50:33Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Alexander Gavrilov <angavrilov@gmail.com> wrote:\n> On Wed, Sep 17, 2008 at 3:40 PM, Johannes Sixt <johannes.sixt@telecom.at> wrote:\n> > If a merge tool was invoked on a conflicted file and the tool completed,\n> > then the conflicted file was staged automatically. However, the fact that\n> > the user closed the merge tool cannot be understood as the unequivocal\n> > sign that the conflict was completely resolved. For example, the user\n> > could have decided to postpone the resolution of the conflict, or could\n> > have accidentally closed the tool. We better leave the file unstaged and\n> > let the user stage it explicitly.\n> \n> It completely reproduces the logic that git-mergetool uses. Namely, if\n> the file is unchanged, it asks explicitly, and if the tool returns a\n> non-zero exit code, it does not stage at all.\n> \n> You also cannot simply remove merge_add_resolution, because then it\n> would leave the diff view stale.\n\nIs it just a matter of calling reshow_diff instead?\n\n-- \nShawn.\n"},{"id":"91512","messageId":"200809242108.01616.johannes.sixt@telecom.at","threadId":"15290","inReplyTo":"20080924175033.GZ3669@spearce.org","subject":"[PATCH/RFC 2/2 v2] git-gui: Do not automatically stage file after merge tool finishes","fromName":"Johannes Sixt","fromEmail":"johannes.sixt@telecom.at","sentAt":"2008-09-24T19:08:01Z","receivedAt":"2008-09-24T19:08:01Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"If a merge tool was invoked on a conflicted file and the tool completed,\nthen the conflicted file was staged automatically. However, the fact that\nthe user closed the merge tool cannot be understood as the unequivocal\nsign that the conflict was completely resolved. For example, the user\ncould have decided to postpone the resolution of the conflict, or could\nhave accidentally closed the tool. We better leave the file unstaged and\nlet the user stage it explicitly.\n\nSigned-off-by: Johannes Sixt <johannes.sixt@telecom.at>\n---\nOn Mittwoch, 24. September 2008, Shawn O. Pearce wrote:\n> Alexander Gavrilov <angavrilov@gmail.com> wrote:\n> > You also cannot simply remove merge_add_resolution, because then it\n> > would leave the diff view stale.\n>\n> Is it just a matter of calling reshow_diff instead?\n\nIndeed. I'd tried do_rescan, but reshow_diff does it, too, and it doesn't\nmess with the file lists!\n\nThis version of the patch also removes the check whether the merge tool\nhad modified the file, because now we don't do anything with the file\nanyway.\n\n-- Hannes\n\n lib/mergetool.tcl |   10 +---------\n 1 files changed, 1 insertions(+), 9 deletions(-)\n\ndiff --git a/lib/mergetool.tcl b/lib/mergetool.tcl\nindex 965cfe4..f292f30 100644\n--- a/lib/mergetool.tcl\n+++ b/lib/mergetool.tcl\n@@ -348,14 +348,6 @@ proc merge_tool_finish {fd} {\n \t\t}\n \t}\n \n-\t# Check the modification time of the target file\n-\tif {!$failed && [file mtime $mtool_target] eq $mtool_mtime} {\n-\t\tif {[ask_popup [mc \"File %s unchanged, still accept as resolved?\" \\\n-\t\t\t\t[short_path $mtool_target]]] ne {yes}} {\n-\t\t\tset failed 1\n-\t\t}\n-\t}\n-\n \t# Finish\n \tif {$failed} {\n \t\tfile rename -force -- $backup $mtool_target\n@@ -368,6 +360,6 @@ proc merge_tool_finish {fd} {\n \n \t\tdelete_temp_files $mtool_tmpfiles\n \n-\t\tmerge_add_resolution $mtool_target\n+\t\treshow_diff\n \t}\n }\n-- \n1.6.0.2.295.g3898d\n"}]}