Merge branch 'master' of git://repo.or.cz/git-gui
* 'master' of git://repo.or.cz/git-gui: (50 commits) git-gui: Minor refactoring of merge command line in merge support git-gui: Use more modern looking icons in the tree browser git-gui: Don't offer to stage hunks from untracked files git-gui: Make sure remotes are loaded when picking revisions git-gui: Use progress bar while resetting/aborting files git-gui: Honor core.excludesfile when listing extra files git-gui: Unify wording to say "to stage" instead of "to add" git-gui: Don't kill modified commit message buffer with merge templates git-gui: Remove usernames from absolute SSH urls during merging git-gui: Format tracking branch merges as though they were pulls git-gui: Cleanup bindings within merge dialog git-gui: Replace merge dialog with our revision picker widget git-gui: Show ref last update times in revision chooser tooltips git-gui: Display commit/tag/remote info in tooltip of revision picker git-gui: Save remote urls obtained from config/remotes setup git-gui: Avoid unnecessary symbolic-ref call during checkout git-gui: Refactor current branch menu items to make i18n easier git-gui: Refactor diff popup into a procedure to ease i18n work git-gui: Paper bag fix quitting crash after commit git-gui: Clarify meaning of add tracked menu option ...maint
						commit
						b8de7f764e
					
				|  | @ -154,12 +154,10 @@ proc gitexec {args} { | |||
| } | ||||
|  | ||||
| proc reponame {} { | ||||
| 	global _reponame | ||||
| 	return $_reponame | ||||
| 	return $::_reponame | ||||
| } | ||||
|  | ||||
| proc is_MacOSX {} { | ||||
| 	global tcl_platform tk_library | ||||
| 	if {[tk windowingsystem] eq {aqua}} { | ||||
| 		return 1 | ||||
| 	} | ||||
|  | @ -167,17 +165,16 @@ proc is_MacOSX {} { | |||
| } | ||||
|  | ||||
| proc is_Windows {} { | ||||
| 	global tcl_platform | ||||
| 	if {$tcl_platform(platform) eq {windows}} { | ||||
| 	if {$::tcl_platform(platform) eq {windows}} { | ||||
| 		return 1 | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| proc is_Cygwin {} { | ||||
| 	global tcl_platform _iscygwin | ||||
| 	global _iscygwin | ||||
| 	if {$_iscygwin eq {}} { | ||||
| 		if {$tcl_platform(platform) eq {windows}} { | ||||
| 		if {$::tcl_platform(platform) eq {windows}} { | ||||
| 			if {[catch {set p [exec cygpath --windir]} err]} { | ||||
| 				set _iscygwin 0 | ||||
| 			} else { | ||||
|  | @ -367,16 +364,25 @@ proc _which {what} { | |||
| 	return {} | ||||
| } | ||||
|  | ||||
| proc _lappend_nice {cmd_var} { | ||||
| 	global _nice | ||||
| 	upvar $cmd_var cmd | ||||
|  | ||||
| 	if {![info exists _nice]} { | ||||
| 		set _nice [_which nice] | ||||
| 	} | ||||
| 	if {$_nice ne {}} { | ||||
| 		lappend cmd $_nice | ||||
| 	} | ||||
| } | ||||
|  | ||||
| proc git {args} { | ||||
| 	set opt [list exec] | ||||
|  | ||||
| 	while {1} { | ||||
| 		switch -- [lindex $args 0] { | ||||
| 		--nice { | ||||
| 			global _nice | ||||
| 			if {$_nice ne {}} { | ||||
| 				lappend opt $_nice | ||||
| 			} | ||||
| 			_lappend_nice opt | ||||
| 		} | ||||
|  | ||||
| 		default { | ||||
|  | @ -414,6 +420,7 @@ proc _open_stdout_stderr {cmd} { | |||
| 			error $err | ||||
| 		} | ||||
| 	} | ||||
| 	fconfigure $fd -eofchar {} | ||||
| 	return $fd | ||||
| } | ||||
|  | ||||
|  | @ -423,10 +430,7 @@ proc git_read {args} { | |||
| 	while {1} { | ||||
| 		switch -- [lindex $args 0] { | ||||
| 		--nice { | ||||
| 			global _nice | ||||
| 			if {$_nice ne {}} { | ||||
| 				lappend opt $_nice | ||||
| 			} | ||||
| 			_lappend_nice opt | ||||
| 		} | ||||
|  | ||||
| 		--stderr { | ||||
|  | @ -454,10 +458,7 @@ proc git_write {args} { | |||
| 	while {1} { | ||||
| 		switch -- [lindex $args 0] { | ||||
| 		--nice { | ||||
| 			global _nice | ||||
| 			if {$_nice ne {}} { | ||||
| 				lappend opt $_nice | ||||
| 			} | ||||
| 			_lappend_nice opt | ||||
| 		} | ||||
|  | ||||
| 		default { | ||||
|  | @ -524,7 +525,6 @@ if {$_git eq {}} { | |||
| 	error_popup "Cannot find git in PATH." | ||||
| 	exit 1 | ||||
| } | ||||
| set _nice [_which nice] | ||||
|  | ||||
| ###################################################################### | ||||
| ## | ||||
|  | @ -544,8 +544,34 @@ if {![regsub {^git version } $_git_version {} _git_version]} { | |||
| 	error_popup "Cannot parse Git version string:\n\n$_git_version" | ||||
| 	exit 1 | ||||
| } | ||||
|  | ||||
| set _real_git_version $_git_version | ||||
| regsub -- {-dirty$} $_git_version {} _git_version | ||||
| regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version | ||||
| regsub {\.rc[0-9]+$} $_git_version {} _git_version | ||||
| regsub {\.GIT$} $_git_version {} _git_version | ||||
|  | ||||
| if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} { | ||||
| 	catch {wm withdraw .} | ||||
| 	if {[tk_messageBox \ | ||||
| 		-icon warning \ | ||||
| 		-type yesno \ | ||||
| 		-default no \ | ||||
| 		-title "[appname]: warning" \ | ||||
| 		-message "Git version cannot be determined. | ||||
|  | ||||
| $_git claims it is version '$_real_git_version'. | ||||
|  | ||||
| [appname] requires at least Git 1.5.0 or later. | ||||
|  | ||||
| Assume '$_real_git_version' is version 1.5.0? | ||||
| "] eq {yes}} { | ||||
| 		set _git_version 1.5.0 | ||||
| 	} else { | ||||
| 		exit 1 | ||||
| 	} | ||||
| } | ||||
| unset _real_git_version | ||||
|  | ||||
| proc git-version {args} { | ||||
| 	global _git_version | ||||
|  | @ -601,6 +627,46 @@ You are using [git-version]: | |||
| 	exit 1 | ||||
| } | ||||
|  | ||||
| ###################################################################### | ||||
| ## | ||||
| ## feature option selection | ||||
|  | ||||
| if {[regexp {^git-(.+)$} [appname] _junk subcommand]} { | ||||
| 	unset _junk | ||||
| } else { | ||||
| 	set subcommand gui | ||||
| } | ||||
| if {$subcommand eq {gui.sh}} { | ||||
| 	set subcommand gui | ||||
| } | ||||
| if {$subcommand eq {gui} && [llength $argv] > 0} { | ||||
| 	set subcommand [lindex $argv 0] | ||||
| 	set argv [lrange $argv 1 end] | ||||
| } | ||||
|  | ||||
| enable_option multicommit | ||||
| enable_option branch | ||||
| enable_option transport | ||||
| disable_option bare | ||||
|  | ||||
| switch -- $subcommand { | ||||
| browser - | ||||
| blame { | ||||
| 	enable_option bare | ||||
|  | ||||
| 	disable_option multicommit | ||||
| 	disable_option branch | ||||
| 	disable_option transport | ||||
| } | ||||
| citool { | ||||
| 	enable_option singlecommit | ||||
|  | ||||
| 	disable_option multicommit | ||||
| 	disable_option branch | ||||
| 	disable_option transport | ||||
| } | ||||
| } | ||||
|  | ||||
| ###################################################################### | ||||
| ## | ||||
| ## repository setup | ||||
|  | @ -625,19 +691,24 @@ if {![file isdirectory $_gitdir]} { | |||
| 	error_popup "Git directory not found:\n\n$_gitdir" | ||||
| 	exit 1 | ||||
| } | ||||
| if {[lindex [file split $_gitdir] end] ne {.git}} { | ||||
| 	catch {wm withdraw .} | ||||
| 	error_popup "Cannot use funny .git directory:\n\n$_gitdir" | ||||
| 	exit 1 | ||||
| if {![is_enabled bare]} { | ||||
| 	if {[lindex [file split $_gitdir] end] ne {.git}} { | ||||
| 		catch {wm withdraw .} | ||||
| 		error_popup "Cannot use funny .git directory:\n\n$_gitdir" | ||||
| 		exit 1 | ||||
| 	} | ||||
| 	if {[catch {cd [file dirname $_gitdir]} err]} { | ||||
| 		catch {wm withdraw .} | ||||
| 		error_popup "No working directory [file dirname $_gitdir]:\n\n$err" | ||||
| 		exit 1 | ||||
| 	} | ||||
| } | ||||
| if {[catch {cd [file dirname $_gitdir]} err]} { | ||||
| 	catch {wm withdraw .} | ||||
| 	error_popup "No working directory [file dirname $_gitdir]:\n\n$err" | ||||
| 	exit 1 | ||||
| set _reponame [file split [file normalize $_gitdir]] | ||||
| if {[lindex $_reponame end] eq {.git}} { | ||||
| 	set _reponame [lindex $_reponame end-1] | ||||
| } else { | ||||
| 	set _reponame [lindex $_reponame end] | ||||
| } | ||||
| set _reponame [lindex [file split \ | ||||
| 	[file normalize [file dirname $_gitdir]]] \ | ||||
| 	end] | ||||
|  | ||||
| ###################################################################### | ||||
| ## | ||||
|  | @ -758,8 +829,9 @@ proc rescan {after {honor_trustmtime 1}} { | |||
|  | ||||
| 	array unset file_states | ||||
|  | ||||
| 	if {![$ui_comm edit modified] | ||||
| 		|| [string trim [$ui_comm get 0.0 end]] eq {}} { | ||||
| 	if {!$::GITGUI_BCK_exists && | ||||
| 		(![$ui_comm edit modified] | ||||
| 		|| [string trim [$ui_comm get 0.0 end]] eq {})} { | ||||
| 		if {[string match amend* $commit_type]} { | ||||
| 		} elseif {[load_message GITGUI_MSG]} { | ||||
| 		} elseif {[load_message MERGE_MSG]} { | ||||
|  | @ -800,6 +872,10 @@ proc rescan_stage2 {fd after} { | |||
| 	if {[file readable $info_exclude]} { | ||||
| 		lappend ls_others "--exclude-from=$info_exclude" | ||||
| 	} | ||||
| 	set user_exclude [get_config core.excludesfile] | ||||
| 	if {$user_exclude ne {} && [file readable $user_exclude]} { | ||||
| 		lappend ls_others "--exclude-from=$user_exclude" | ||||
| 	} | ||||
|  | ||||
| 	set buf_rdi {} | ||||
| 	set buf_rdf {} | ||||
|  | @ -827,6 +903,7 @@ proc load_message {file} { | |||
| 		if {[catch {set fd [open $f r]}]} { | ||||
| 			return 0 | ||||
| 		} | ||||
| 		fconfigure $fd -eofchar {} | ||||
| 		set content [string trim [read $fd]] | ||||
| 		close $fd | ||||
| 		regsub -all -line {[ \r\t]+$} $content {} content | ||||
|  | @ -1219,32 +1296,6 @@ static unsigned char file_merge_bits[] = { | |||
|    0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f}; | ||||
| } -maskdata $filemask | ||||
|  | ||||
| set file_dir_data { | ||||
| #define file_width 18 | ||||
| #define file_height 18 | ||||
| static unsigned char file_bits[] = { | ||||
|   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00, | ||||
|   0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00, | ||||
|   0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00, | ||||
|   0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, | ||||
|   0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||||
| } | ||||
| image create bitmap file_dir -background white -foreground blue \ | ||||
| 	-data $file_dir_data -maskdata $file_dir_data | ||||
| unset file_dir_data | ||||
|  | ||||
| set file_uplevel_data { | ||||
| #define up_width 15 | ||||
| #define up_height 15 | ||||
| static unsigned char up_bits[] = { | ||||
|   0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f, | ||||
|   0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, | ||||
|   0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00}; | ||||
| } | ||||
| image create bitmap file_uplevel -background white -foreground red \ | ||||
| 	-data $file_uplevel_data -maskdata $file_uplevel_data | ||||
| unset file_uplevel_data | ||||
|  | ||||
| set ui_index .vpane.files.index.list | ||||
| set ui_workdir .vpane.files.workdir.list | ||||
|  | ||||
|  | @ -1345,6 +1396,7 @@ set is_quitting 0 | |||
|  | ||||
| proc do_quit {} { | ||||
| 	global ui_comm is_quitting repo_config commit_type | ||||
| 	global GITGUI_BCK_exists GITGUI_BCK_i | ||||
|  | ||||
| 	if {$is_quitting} return | ||||
| 	set is_quitting 1 | ||||
|  | @ -1353,18 +1405,30 @@ proc do_quit {} { | |||
| 		# -- Stash our current commit buffer. | ||||
| 		# | ||||
| 		set save [gitdir GITGUI_MSG] | ||||
| 		set msg [string trim [$ui_comm get 0.0 end]] | ||||
| 		regsub -all -line {[ \r\t]+$} $msg {} msg | ||||
| 		if {(![string match amend* $commit_type] | ||||
| 			|| [$ui_comm edit modified]) | ||||
| 			&& $msg ne {}} { | ||||
| 			catch { | ||||
| 				set fd [open $save w] | ||||
| 				puts -nonewline $fd $msg | ||||
| 				close $fd | ||||
| 			} | ||||
| 		if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} { | ||||
| 			file rename -force [gitdir GITGUI_BCK] $save | ||||
| 			set GITGUI_BCK_exists 0 | ||||
| 		} else { | ||||
| 			catch {file delete $save} | ||||
| 			set msg [string trim [$ui_comm get 0.0 end]] | ||||
| 			regsub -all -line {[ \r\t]+$} $msg {} msg | ||||
| 			if {(![string match amend* $commit_type] | ||||
| 				|| [$ui_comm edit modified]) | ||||
| 				&& $msg ne {}} { | ||||
| 				catch { | ||||
| 					set fd [open $save w] | ||||
| 					puts -nonewline $fd $msg | ||||
| 					close $fd | ||||
| 				} | ||||
| 			} else { | ||||
| 				catch {file delete $save} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		# -- Remove our editor backup, its not needed. | ||||
| 		# | ||||
| 		after cancel $GITGUI_BCK_i | ||||
| 		if {$GITGUI_BCK_exists} { | ||||
| 			catch {file delete [gitdir GITGUI_BCK]} | ||||
| 		} | ||||
|  | ||||
| 		# -- Stash our current window geometry into this repository. | ||||
|  | @ -1566,43 +1630,6 @@ set font_descs { | |||
| load_config 0 | ||||
| apply_config | ||||
|  | ||||
| ###################################################################### | ||||
| ## | ||||
| ## feature option selection | ||||
|  | ||||
| if {[regexp {^git-(.+)$} [appname] _junk subcommand]} { | ||||
| 	unset _junk | ||||
| } else { | ||||
| 	set subcommand gui | ||||
| } | ||||
| if {$subcommand eq {gui.sh}} { | ||||
| 	set subcommand gui | ||||
| } | ||||
| if {$subcommand eq {gui} && [llength $argv] > 0} { | ||||
| 	set subcommand [lindex $argv 0] | ||||
| 	set argv [lrange $argv 1 end] | ||||
| } | ||||
|  | ||||
| enable_option multicommit | ||||
| enable_option branch | ||||
| enable_option transport | ||||
|  | ||||
| switch -- $subcommand { | ||||
| browser - | ||||
| blame { | ||||
| 	disable_option multicommit | ||||
| 	disable_option branch | ||||
| 	disable_option transport | ||||
| } | ||||
| citool { | ||||
| 	enable_option singlecommit | ||||
|  | ||||
| 	disable_option multicommit | ||||
| 	disable_option branch | ||||
| 	disable_option transport | ||||
| } | ||||
| } | ||||
|  | ||||
| ###################################################################### | ||||
| ## | ||||
| ## ui construction | ||||
|  | @ -1632,20 +1659,32 @@ if {[is_enabled transport]} { | |||
| menu .mbar.repository | ||||
|  | ||||
| .mbar.repository add command \ | ||||
| 	-label {Browse Current Branch} \ | ||||
| 	-label {Browse Current Branch's Files} \ | ||||
| 	-command {browser::new $current_branch} | ||||
| trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#" | ||||
| set ui_browse_current [.mbar.repository index last] | ||||
| .mbar.repository add command \ | ||||
| 	-label {Browse Branch Files...} \ | ||||
| 	-command browser_open::dialog | ||||
| .mbar.repository add separator | ||||
|  | ||||
| .mbar.repository add command \ | ||||
| 	-label {Visualize Current Branch} \ | ||||
| 	-label {Visualize Current Branch's History} \ | ||||
| 	-command {do_gitk $current_branch} | ||||
| trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#" | ||||
| set ui_visualize_current [.mbar.repository index last] | ||||
| .mbar.repository add command \ | ||||
| 	-label {Visualize All Branches} \ | ||||
| 	-label {Visualize All Branch History} \ | ||||
| 	-command {do_gitk --all} | ||||
| .mbar.repository add separator | ||||
|  | ||||
| proc current_branch_write {args} { | ||||
| 	global current_branch | ||||
| 	.mbar.repository entryconf $::ui_browse_current \ | ||||
| 		-label "Browse $current_branch's Files" | ||||
| 	.mbar.repository entryconf $::ui_visualize_current \ | ||||
| 		-label "Visualize $current_branch's History" | ||||
| } | ||||
| trace add variable current_branch write current_branch_write | ||||
|  | ||||
| if {[is_enabled multicommit]} { | ||||
| 	.mbar.repository add command -label {Database Statistics} \ | ||||
| 		-command do_stats | ||||
|  | @ -1766,12 +1805,12 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { | |||
| 	lappend disable_on_lock \ | ||||
| 		[list .mbar.commit entryconf [.mbar.commit index last] -state] | ||||
|  | ||||
| 	.mbar.commit add command -label {Add To Commit} \ | ||||
| 	.mbar.commit add command -label {Stage To Commit} \ | ||||
| 		-command do_add_selection | ||||
| 	lappend disable_on_lock \ | ||||
| 		[list .mbar.commit entryconf [.mbar.commit index last] -state] | ||||
|  | ||||
| 	.mbar.commit add command -label {Add Existing To Commit} \ | ||||
| 	.mbar.commit add command -label {Stage Changed Files To Commit} \ | ||||
| 		-command do_add_all \ | ||||
| 		-accelerator $M1T-I | ||||
| 	lappend disable_on_lock \ | ||||
|  | @ -1805,14 +1844,14 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { | |||
| if {[is_enabled branch]} { | ||||
| 	menu .mbar.merge | ||||
| 	.mbar.merge add command -label {Local Merge...} \ | ||||
| 		-command merge::dialog | ||||
| 		-command merge::dialog \ | ||||
| 		-accelerator $M1T-M | ||||
| 	lappend disable_on_lock \ | ||||
| 		[list .mbar.merge entryconf [.mbar.merge index last] -state] | ||||
| 	.mbar.merge add command -label {Abort Merge...} \ | ||||
| 		-command merge::reset_hard | ||||
| 	lappend disable_on_lock \ | ||||
| 		[list .mbar.merge entryconf [.mbar.merge index last] -state] | ||||
|  | ||||
| } | ||||
|  | ||||
| # -- Transport Menu | ||||
|  | @ -1844,33 +1883,6 @@ if {[is_MacOSX]} { | |||
| 	.mbar.edit add separator | ||||
| 	.mbar.edit add command -label {Options...} \ | ||||
| 		-command do_options | ||||
|  | ||||
| 	# -- Tools Menu | ||||
| 	# | ||||
| 	if {[is_Cygwin] && [file exists /usr/local/miga/lib/gui-miga]} { | ||||
| 	proc do_miga {} { | ||||
| 		if {![lock_index update]} return | ||||
| 		set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""] | ||||
| 		set miga_fd [open "|$cmd" r] | ||||
| 		fconfigure $miga_fd -blocking 0 | ||||
| 		fileevent $miga_fd readable [list miga_done $miga_fd] | ||||
| 		ui_status {Running miga...} | ||||
| 	} | ||||
| 	proc miga_done {fd} { | ||||
| 		read $fd 512 | ||||
| 		if {[eof $fd]} { | ||||
| 			close $fd | ||||
| 			unlock_index | ||||
| 			rescan ui_ready | ||||
| 		} | ||||
| 	} | ||||
| 	.mbar add cascade -label Tools -menu .mbar.tools | ||||
| 	menu .mbar.tools | ||||
| 	.mbar.tools add command -label "Migrate" \ | ||||
| 		-command do_miga | ||||
| 	lappend disable_on_lock \ | ||||
| 		[list .mbar.tools entryconf [.mbar.tools index last] -state] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| # -- Help Menu | ||||
|  | @ -1938,29 +1950,10 @@ proc usage {} { | |||
| # -- Not a normal commit type invocation?  Do that instead! | ||||
| # | ||||
| switch -- $subcommand { | ||||
| browser { | ||||
| 	set subcommand_args {rev?} | ||||
| 	switch [llength $argv] { | ||||
| 	0 { load_current_branch } | ||||
| 	1 { | ||||
| 		set current_branch [lindex $argv 0] | ||||
| 		if {[regexp {^[0-9a-f]{1,39}$} $current_branch]} { | ||||
| 			if {[catch { | ||||
| 					set current_branch \ | ||||
| 					[git rev-parse --verify $current_branch] | ||||
| 				} err]} { | ||||
| 				puts stderr $err | ||||
| 				exit 1 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	default usage | ||||
| 	} | ||||
| 	browser::new $current_branch | ||||
| 	return | ||||
| } | ||||
| browser - | ||||
| blame { | ||||
| 	set subcommand_args {rev? path?} | ||||
| 	set subcommand_args {rev? path} | ||||
| 	if {$argv eq {}} usage | ||||
| 	set head {} | ||||
| 	set path {} | ||||
| 	set is_path 0 | ||||
|  | @ -1979,12 +1972,18 @@ blame { | |||
| 		} elseif {$head eq {}} { | ||||
| 			if {$head ne {}} usage | ||||
| 			set head $a | ||||
| 			set is_path 1 | ||||
| 		} else { | ||||
| 			usage | ||||
| 		} | ||||
| 	} | ||||
| 	unset is_path | ||||
|  | ||||
| 	if {$head ne {} && $path eq {}} { | ||||
| 		set path $_prefix$head | ||||
| 		set head {} | ||||
| 	} | ||||
|  | ||||
| 	if {$head eq {}} { | ||||
| 		load_current_branch | ||||
| 	} else { | ||||
|  | @ -1999,8 +1998,26 @@ blame { | |||
| 		set current_branch $head | ||||
| 	} | ||||
|  | ||||
| 	if {$path eq {}} usage | ||||
| 	blame::new $head $path | ||||
| 	switch -- $subcommand { | ||||
| 	browser { | ||||
| 		if {$head eq {}} { | ||||
| 			if {$path ne {} && [file isdirectory $path]} { | ||||
| 				set head $current_branch | ||||
| 			} else { | ||||
| 				set head $path | ||||
| 				set path {} | ||||
| 			} | ||||
| 		} | ||||
| 		browser::new $head $path | ||||
| 	} | ||||
| 	blame   { | ||||
| 		if {$head eq {} && ![file exists $path]} { | ||||
| 			puts stderr "fatal: cannot stat path $path: No such file or directory" | ||||
| 			exit 1 | ||||
| 		} | ||||
| 		blame::new $head $path | ||||
| 	} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| citool - | ||||
|  | @ -2115,7 +2132,7 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x | |||
| lappend disable_on_lock \ | ||||
| 	{.vpane.lower.commarea.buttons.rescan conf -state} | ||||
|  | ||||
| button .vpane.lower.commarea.buttons.incall -text {Add Existing} \ | ||||
| button .vpane.lower.commarea.buttons.incall -text {Stage Changed} \ | ||||
| 	-command do_add_all | ||||
| pack .vpane.lower.commarea.buttons.incall -side top -fill x | ||||
| lappend disable_on_lock \ | ||||
|  | @ -2389,17 +2406,25 @@ lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] | |||
| $ctxm add separator | ||||
| $ctxm add command -label {Options...} \ | ||||
| 	-command do_options | ||||
| bind_button3 $ui_diff " | ||||
| 	set cursorX %x | ||||
| 	set cursorY %y | ||||
| 	if {\$ui_index eq \$current_diff_side} { | ||||
| 		$ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit} | ||||
| proc popup_diff_menu {ctxm x y X Y} { | ||||
| 	set ::cursorX $x | ||||
| 	set ::cursorY $y | ||||
| 	if {$::ui_index eq $::current_diff_side} { | ||||
| 		$ctxm entryconf $::ui_diff_applyhunk \ | ||||
| 			-state normal \ | ||||
| 			-label {Unstage Hunk From Commit} | ||||
| 	} elseif {{_O} eq [lindex $::file_states($::current_diff_path) 0]} { | ||||
| 		$ctxm entryconf $::ui_diff_applyhunk \ | ||||
| 			-state disabled \ | ||||
| 			-label {Stage Hunk For Commit} | ||||
| 	} else { | ||||
| 		$ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit} | ||||
| 		$ctxm entryconf $::ui_diff_applyhunk \ | ||||
| 			-state normal \ | ||||
| 			-label {Stage Hunk For Commit} | ||||
| 	} | ||||
| 	tk_popup $ctxm %X %Y | ||||
| " | ||||
| unset ui_diff_applyhunk | ||||
| 	tk_popup $ctxm $X $Y | ||||
| } | ||||
| bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y] | ||||
|  | ||||
| # -- Status Bar | ||||
| # | ||||
|  | @ -2460,6 +2485,8 @@ if {[is_enabled branch]} { | |||
| 	bind . <$M1B-Key-N> branch_create::dialog | ||||
| 	bind . <$M1B-Key-o> branch_checkout::dialog | ||||
| 	bind . <$M1B-Key-O> branch_checkout::dialog | ||||
| 	bind . <$M1B-Key-m> merge::dialog | ||||
| 	bind . <$M1B-Key-M> merge::dialog | ||||
| } | ||||
| if {[is_enabled transport]} { | ||||
| 	bind . <$M1B-Key-p> do_push_anywhere | ||||
|  | @ -2551,24 +2578,64 @@ if {[is_enabled transport]} { | |||
| 	populate_push_menu | ||||
| } | ||||
|  | ||||
| # -- Only suggest a gc run if we are going to stay running. | ||||
| # | ||||
| if {[is_enabled multicommit]} { | ||||
| 	set object_limit 2000 | ||||
| 	if {[is_Windows]} {set object_limit 200} | ||||
| 	regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current | ||||
| 	if {$objects_current >= $object_limit} { | ||||
| 		if {[ask_popup \ | ||||
| 			"This repository currently has $objects_current loose objects. | ||||
| if {[winfo exists $ui_comm]} { | ||||
| 	set GITGUI_BCK_exists [load_message GITGUI_BCK] | ||||
|  | ||||
| To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist. | ||||
|  | ||||
| Compress the database now?"] eq yes} { | ||||
| 			do_gc | ||||
| 	# -- If both our backup and message files exist use the | ||||
| 	#    newer of the two files to initialize the buffer. | ||||
| 	# | ||||
| 	if {$GITGUI_BCK_exists} { | ||||
| 		set m [gitdir GITGUI_MSG] | ||||
| 		if {[file isfile $m]} { | ||||
| 			if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} { | ||||
| 				catch {file delete [gitdir GITGUI_MSG]} | ||||
| 			} else { | ||||
| 				$ui_comm delete 0.0 end | ||||
| 				$ui_comm edit reset | ||||
| 				$ui_comm edit modified false | ||||
| 				catch {file delete [gitdir GITGUI_BCK]} | ||||
| 				set GITGUI_BCK_exists 0 | ||||
| 			} | ||||
| 		} | ||||
| 		unset m | ||||
| 	} | ||||
| 	unset object_limit _junk objects_current | ||||
|  | ||||
| 	proc backup_commit_buffer {} { | ||||
| 		global ui_comm GITGUI_BCK_exists | ||||
|  | ||||
| 		set m [$ui_comm edit modified] | ||||
| 		if {$m || $GITGUI_BCK_exists} { | ||||
| 			set msg [string trim [$ui_comm get 0.0 end]] | ||||
| 			regsub -all -line {[ \r\t]+$} $msg {} msg | ||||
|  | ||||
| 			if {$msg eq {}} { | ||||
| 				if {$GITGUI_BCK_exists} { | ||||
| 					catch {file delete [gitdir GITGUI_BCK]} | ||||
| 					set GITGUI_BCK_exists 0 | ||||
| 				} | ||||
| 			} elseif {$m} { | ||||
| 				catch { | ||||
| 					set fd [open [gitdir GITGUI_BCK] w] | ||||
| 					puts -nonewline $fd $msg | ||||
| 					close $fd | ||||
| 					set GITGUI_BCK_exists 1 | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			$ui_comm edit modified false | ||||
| 		} | ||||
|  | ||||
| 		set ::GITGUI_BCK_i [after 2000 backup_commit_buffer] | ||||
| 	} | ||||
|  | ||||
| 	backup_commit_buffer | ||||
| } | ||||
|  | ||||
| lock_index begin-read | ||||
| if {![winfo ismapped .]} { | ||||
| 	wm deiconify . | ||||
| } | ||||
| after 1 do_rescan | ||||
| if {[is_enabled multicommit]} { | ||||
| 	after 1000 hint_gc | ||||
| } | ||||
|  |  | |||
|  | @ -370,6 +370,7 @@ method _load {jump} { | |||
| 	$w_path conf -text [escape_path $path] | ||||
| 	if {$commit eq {}} { | ||||
| 		set fd [open $path r] | ||||
| 		fconfigure $fd -eofchar {} | ||||
| 	} else { | ||||
| 		set fd [git_read cat-file blob "$commit:$path"] | ||||
| 	} | ||||
|  | @ -770,15 +771,20 @@ method _showcommit {cur_w lno} { | |||
| 						set enc [string tolower [string range $line 9 end]] | ||||
| 					} | ||||
| 				} | ||||
| 				set msg [encoding convertfrom $enc [read $fd]] | ||||
| 				set msg [string trim $msg] | ||||
| 				set msg [read $fd] | ||||
| 				close $fd | ||||
|  | ||||
| 				set author_name [encoding convertfrom $enc $author_name] | ||||
| 				set committer_name [encoding convertfrom $enc $committer_name] | ||||
|  | ||||
| 				set header($cmit,author) $author_name | ||||
| 				set header($cmit,committer) $committer_name | ||||
| 				set enc [tcl_encoding $enc] | ||||
| 				if {$enc ne {}} { | ||||
| 					set msg [encoding convertfrom $enc $msg] | ||||
| 					set author_name [encoding convertfrom $enc $author_name] | ||||
| 					set committer_name [encoding convertfrom $enc $committer_name] | ||||
| 					set header($cmit,author) $author_name | ||||
| 					set header($cmit,committer) $committer_name | ||||
| 					set header($cmit,summary) \ | ||||
| 					[encoding convertfrom $enc $header($cmit,summary)] | ||||
| 				} | ||||
| 				set msg [string trim $msg] | ||||
| 			} | ||||
| 			set header($cmit,message) $msg | ||||
| 		} | ||||
|  | @ -873,6 +879,11 @@ method _open_tooltip {cur_w} { | |||
| 		set org [lindex $amov_data $lno] | ||||
| 	} | ||||
|  | ||||
| 	if {$dat eq {}} { | ||||
| 		_hide_tooltip $this | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	set cmit [lindex $dat 0] | ||||
| 	set tooltip_commit [list $cmit] | ||||
|  | ||||
|  |  | |||
|  | @ -3,6 +3,13 @@ | |||
|  | ||||
| class browser { | ||||
|  | ||||
| image create photo ::browser::img_parent  -data {R0lGODlhEAAQAIUAAPwCBBxSHBxOHMTSzNzu3KzCtBRGHCSKFIzCjLzSxBQ2FAxGHDzCLCyeHBQ+FHSmfAwuFBxKLDSCNMzizISyjJzOnDSyLAw+FAQSDAQeDBxWJAwmDAQOBKzWrDymNAQaDAQODAwaDDyKTFSyXFTGTEy6TAQCBAQKDAwiFBQyHAwSFAwmHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ1QIBwSCwaj0hiQCBICpcDQsFgGAaIguhhi0gohIsrQEDYMhiNrRfgeAQC5fMCAolIDhD2hFI5WC4YRBkaBxsOE2l/RxsHHA4dHmkfRyAbIQ4iIyQlB5NFGCAACiakpSZEJyinTgAcKSesACorgU4mJ6uxR35BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=} | ||||
| image create photo ::browser::img_rblob   -data {R0lGODlhEAAQAIUAAPwCBFxaXNze3Ly2rJSWjPz+/Ozq7GxqbJyanPT29HRydMzOzDQyNIyKjERCROTi3Pz69PTy7Pzy7PTu5Ozm3LyqlJyWlJSSjJSOhOzi1LyulPz27PTq3PTm1OzezLyqjIyKhJSKfOzaxPz29OzizLyidIyGdIyCdOTOpLymhOzavOTStMTCtMS+rMS6pMSynMSulLyedAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaQQIAQECgajcNkQMBkDgKEQFK4LFgLhkMBIVUKroWEYlEgMLxbBKLQUBwc52HgAQ4LBo049atWQyIPA3pEdFcQEhMUFYNVagQWFxgZGoxfYRsTHB0eH5UJCJAYICEinUoPIxIcHCQkIiIllQYEGCEhJicoKYwPmiQeKisrKLFKLCwtLi8wHyUlMYwM0tPUDH5BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=} | ||||
| image create photo ::browser::img_xblob   -data {R0lGODlhEAAQAIYAAPwCBFRWVFxaXNza3OTi3Nze3Ly2tJyanPz+/Ozq7GxubNzSxMzOzMTGxHRybDQyNLy+vHRydHx6fKSipISChIyKjGxqbERCRCwuLLy6vGRiZExKTCQiJAwKDLSytLy2rJSSlHx+fDw6PKyqrBQWFPTu5Ozm3LyulLS2tCQmJAQCBPTq3Ozi1MSynCwqLAQGBOTazOzizOzezLyqjBweHNzSvOzaxKyurHRuZNzOtLymhDw+PIyCdOzWvOTOpLyidNzKtOTStLyifMTCtMS+rLyedAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAfZgACCAAEChYeGg4oCAwQFjgYBBwGKggEECJkICQoIkwADCwwNDY2mDA4Lng8QDhESsLARExQVDhYXGBkWExIaGw8cHR4SCQQfFQ8eFgUgIQEiwiMSBMYfGB4atwEXDyQd0wQlJicPKAHoFyIpJCoeDgMrLC0YKBsX6i4kL+4OMDEyZijr5oLGNxUqUCioEcPGDAwjPNyI6MEDChQjcOSwsUDHgw07RIgI4KCkAgs8cvTw8eOBogAxQtXIASTISiEuBwUYMoRIixYnZggpUgTDywdIkWJIitRPIAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} | ||||
| image create photo ::browser::img_tree    -data {R0lGODlhEAAQAIYAAPwCBAQCBExKTBwWHMzKzOzq7ERCRExGTCwqLARqnAQ+ZHR2dKyqrNTOzHx2fCQiJMTi9NTu9HzC3AxmnAQ+XPTm7Dy67DymzITC3IzG5AxypHRydKymrMzOzOzu7BweHByy9AyGtFyy1IzG3NTu/ARupFRSVByazBR6rAyGvFyuzJTK3MTm9BR+tAxWhHS61MTi7Pz+/IymvCxulBRelAx2rHS63Pz6/PTy9PTu9Nza3ISitBRupFSixNTS1CxqnDQyNMzGzOTi5MTCxMTGxGxubGxqbLy2vLSutGRiZLy6vLSytKyurDQuNFxaXKSipDw6PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAfDgACCAAECg4eIAAMEBQYHCImDBgkKCwwNBQIBBw4Bhw8QERITFJYEFQUFnoIPFhcYoRkaFBscHR4Ggh8gIRciEiMQJBkltCa6JyUoKSkXKhIrLCQYuQAPLS4TEyUhKb0qLzDVAjEFMjMuNBMoNcw21QY3ODkFOjs82RM1PfDzFRU3fOggcM7Fj2pAgggRokOHDx9DhhAZUqQaISBGhjwMEvEIkiIHEgUAkgSJkiNLmFSMJChAEydPGBSBwvJQgAc0/QQCACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=} | ||||
| image create photo ::browser::img_symlink -data {R0lGODlhEAAQAIQAAPwCBCwqLLSytLy+vERGRFRWVDQ2NKSmpAQCBKyurMTGxISChJyanHR2dIyKjGxubHRydGRmZIyOjFxeXHx6fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAVbICACwWieY1CibCCsrBkMb0zchSEcNYskCtqBBzshFkOGQFk0IRqOxqPBODRHCMhCQKteRc9FI/KQWGOIyFYgkDC+gPR4snCcfRGKOIKIgSMQE31+f4OEYCZ+IQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} | ||||
| image create photo ::browser::img_unknown -data {R0lGODlhEAAQAIUAAPwCBFxaXIyKjNTW1Nze3LS2tJyanER2RGS+VPz+/PTu5GxqbPz69BQ6BCxeLFSqRPT29HRydMzOzDQyNERmPKSypCRWHIyKhERCRDyGPKz2nESiLBxGHCyCHGxubPz6/PTy7Ozi1Ly2rKSipOzm3LyqlKSWhCRyFOzizLymhNTKtNzOvOzaxOTStPz27OzWvOTOpLSupLyedMS+rMS6pMSulLyqjLymfLyifAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAamQIAQECgajcOkYEBoDgoBQyAJOCCuiENCsWBIh9aGw9F4HCARiXciRDQoBUnlYRlcIgsMG5CxXAgMGhscBRAEBRd7AB0eBBoIgxUfICEiikSPgyMMIAokJZcBkBybJgomIaBJAZoMpyCmqkMBFCcVCrgKKAwpoSorKqchKCwtvasIFBIhLiYvLzDHsxQNMcMKLDAwMqEz3jQ1NTY3ONyrE+jp6hN+QQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} | ||||
|  | ||||
| field w | ||||
| field browser_commit | ||||
| field browser_path | ||||
|  | @ -13,13 +20,13 @@ field browser_busy   1 | |||
|  | ||||
| field ls_buf     {}; # Buffered record output from ls-tree | ||||
|  | ||||
| constructor new {commit} { | ||||
| constructor new {commit {path {}}} { | ||||
| 	global cursor_ptr M1B | ||||
| 	make_toplevel top w | ||||
| 	wm title $top "[appname] ([reponame]): File Browser" | ||||
|  | ||||
| 	set browser_commit $commit | ||||
| 	set browser_path $browser_commit: | ||||
| 	set browser_path $browser_commit:$path | ||||
|  | ||||
| 	label $w.path \ | ||||
| 		-textvariable @browser_path \ | ||||
|  | @ -73,7 +80,11 @@ constructor new {commit} { | |||
|  | ||||
| 	bind $w_list <Visibility> [list focus $w_list] | ||||
| 	set w $w_list | ||||
| 	_ls $this $browser_commit | ||||
| 	if {$path ne {}} { | ||||
| 		_ls $this $browser_commit:$path $path | ||||
| 	} else { | ||||
| 		_ls $this $browser_commit $path | ||||
| 	} | ||||
| 	return $this | ||||
| } | ||||
|  | ||||
|  | @ -173,7 +184,7 @@ method _ls {tree_id {name {}}} { | |||
| 		$w image create end \ | ||||
| 			-align center -padx 5 -pady 1 \ | ||||
| 			-name icon0 \ | ||||
| 			-image file_uplevel | ||||
| 			-image ::browser::img_parent | ||||
| 		$w insert end {[Up To Parent]} | ||||
| 		lappend browser_files parent | ||||
| 	} | ||||
|  | @ -203,14 +214,21 @@ method _read {fd} { | |||
|  | ||||
| 		switch -- $type { | ||||
| 		blob { | ||||
| 			set image file_mod | ||||
| 			scan [lindex $info 0] %o mode | ||||
| 			if {$mode == 0120000} { | ||||
| 				set image ::browser::img_symlink | ||||
| 			} elseif {($mode & 0100) != 0} { | ||||
| 				set image ::browser::img_xblob | ||||
| 			} else { | ||||
| 				set image ::browser::img_rblob | ||||
| 			} | ||||
| 		} | ||||
| 		tree { | ||||
| 			set image file_dir | ||||
| 			set image ::browser::img_tree | ||||
| 			append path / | ||||
| 		} | ||||
| 		default { | ||||
| 			set image file_question | ||||
| 			set image ::browser::img_unknown | ||||
| 		} | ||||
| 		} | ||||
|  | ||||
|  | @ -239,3 +257,56 @@ method _read {fd} { | |||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| class browser_open { | ||||
|  | ||||
| field w              ; # widget path | ||||
| field w_rev          ; # mega-widget to pick the initial revision | ||||
|  | ||||
| constructor dialog {} { | ||||
| 	make_toplevel top w | ||||
| 	wm title $top "[appname] ([reponame]): Browse Branch Files" | ||||
| 	if {$top ne {.}} { | ||||
| 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]" | ||||
| 	} | ||||
|  | ||||
| 	label $w.header \ | ||||
| 		-text {Browse Branch Files} \ | ||||
| 		-font font_uibold | ||||
| 	pack $w.header -side top -fill x | ||||
|  | ||||
| 	frame $w.buttons | ||||
| 	button $w.buttons.browse -text Browse \ | ||||
| 		-default active \ | ||||
| 		-command [cb _open] | ||||
| 	pack $w.buttons.browse -side right | ||||
| 	button $w.buttons.cancel -text {Cancel} \ | ||||
| 		-command [list destroy $w] | ||||
| 	pack $w.buttons.cancel -side right -padx 5 | ||||
| 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10 | ||||
|  | ||||
| 	set w_rev [::choose_rev::new $w.rev {Revision}] | ||||
| 	$w_rev bind_listbox <Double-Button-1> [cb _open] | ||||
| 	pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 | ||||
|  | ||||
| 	bind $w <Visibility> [cb _visible] | ||||
| 	bind $w <Key-Escape> [list destroy $w] | ||||
| 	bind $w <Key-Return> [cb _open]\;break | ||||
| 	tkwait window $w | ||||
| } | ||||
|  | ||||
| method _open {} { | ||||
| 	if {[catch {$w_rev commit_or_die} err]} { | ||||
| 		return | ||||
| 	} | ||||
| 	set name [$w_rev get] | ||||
| 	destroy $w | ||||
| 	browser::new $name | ||||
| } | ||||
|  | ||||
| method _visible {} { | ||||
| 	grab $w | ||||
| 	$w_rev focus_filter | ||||
| } | ||||
|  | ||||
| } | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ field new_ref    ; # ref we are updating/creating | |||
|  | ||||
| field parent_w      .; # window that started us | ||||
| field merge_type none; # type of merge to apply to existing branch | ||||
| field merge_base   {}; # merge base if we have another ref involved | ||||
| field fetch_spec   {}; # refetch tracking branch if used? | ||||
| field checkout      1; # actually checkout the branch? | ||||
| field create        0; # create the branch if it doesn't exist? | ||||
|  | @ -65,14 +66,19 @@ method run {} { | |||
| 		set r_head [lindex $fetch_spec 2] | ||||
| 		regsub ^refs/heads/ $r_head {} r_name | ||||
|  | ||||
| 		set cmd [list git fetch $remote] | ||||
| 		if {$l_trck ne {}} { | ||||
| 			lappend cmd +$r_head:$l_trck | ||||
| 		} else { | ||||
| 			lappend cmd $r_head | ||||
| 		} | ||||
|  | ||||
| 		_toplevel $this {Refreshing Tracking Branch} | ||||
| 		set w_cons [::console::embed \ | ||||
| 			$w.console \ | ||||
| 			"Fetching $r_name from $remote"] | ||||
| 		pack $w.console -fill both -expand 1 | ||||
| 		$w_cons exec \ | ||||
| 			[list git fetch $remote +$r_head:$l_trck] \ | ||||
| 			[cb _finish_fetch] | ||||
| 		$w_cons exec $cmd [cb _finish_fetch] | ||||
|  | ||||
| 		bind $w <$M1B-Key-w> break | ||||
| 		bind $w <$M1B-Key-W> break | ||||
|  | @ -113,6 +119,9 @@ method _noop {} {} | |||
| method _finish_fetch {ok} { | ||||
| 	if {$ok} { | ||||
| 		set l_trck [lindex $fetch_spec 0] | ||||
| 		if {$l_trck eq {}} { | ||||
| 			set l_trck FETCH_HEAD | ||||
| 		} | ||||
| 		if {[catch {set new_hash [git rev-parse --verify "$l_trck^0"]} err]} { | ||||
| 			set ok 0 | ||||
| 			$w_cons insert "fatal: Cannot resolve $l_trck" | ||||
|  | @ -180,29 +189,25 @@ method _update_ref {} { | |||
| 		# No merge would be required, don't compute anything. | ||||
| 		# | ||||
| 	} else { | ||||
| 		set mrb {} | ||||
| 		catch {set mrb [git merge-base $new $cur]} | ||||
| 		switch -- $merge_type { | ||||
| 		ff { | ||||
| 			if {$mrb eq $new} { | ||||
| 				# The current branch is actually newer. | ||||
| 				# | ||||
| 				set new $cur | ||||
| 			} elseif {$mrb eq $cur} { | ||||
| 				# The current branch is older. | ||||
| 				# | ||||
| 				set reflog_msg "merge $new_expr: Fast-forward" | ||||
| 			} else { | ||||
| 				_error $this "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to $new_expr.\nA merge is required." | ||||
| 				return 0 | ||||
| 		catch {set merge_base [git merge-base $new $cur]} | ||||
| 		if {$merge_base eq $cur} { | ||||
| 			# The current branch is older. | ||||
| 			# | ||||
| 			set reflog_msg "merge $new_expr: Fast-forward" | ||||
| 		} else { | ||||
| 			switch -- $merge_type { | ||||
| 			ff { | ||||
| 				if {$merge_base eq $new} { | ||||
| 					# The current branch is actually newer. | ||||
| 					# | ||||
| 					set new $cur | ||||
| 					set new_hash $cur | ||||
| 				} else { | ||||
| 					_error $this "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to $new_expr.\nA merge is required." | ||||
| 					return 0 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		reset { | ||||
| 			if {$mrb eq $cur} { | ||||
| 				# The current branch is older. | ||||
| 				# | ||||
| 				set reflog_msg "merge $new_expr: Fast-forward" | ||||
| 			} else { | ||||
| 			reset { | ||||
| 				# The current branch will lose things. | ||||
| 				# | ||||
| 				if {[_confirm_reset $this $cur]} { | ||||
|  | @ -211,11 +216,11 @@ method _update_ref {} { | |||
| 					return 0 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		default { | ||||
| 			_error $this "Only 'ff' and 'reset' merge is currently supported." | ||||
| 			return 0 | ||||
| 		} | ||||
| 			default { | ||||
| 				_error $this "Merge strategy '$merge_type' not supported." | ||||
| 				return 0 | ||||
| 			} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | @ -243,7 +248,7 @@ method _checkout {} { | |||
| 	if {[lock_index checkout_op]} { | ||||
| 		after idle [cb _start_checkout] | ||||
| 	} else { | ||||
| 		_error $this "Index is already locked." | ||||
| 		_error $this "Staging area (index) is already locked." | ||||
| 		delete_this | ||||
| 	} | ||||
| } | ||||
|  | @ -270,7 +275,9 @@ The rescan will be automatically started now. | |||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if {[is_config_true gui.trustmtime]} { | ||||
| 	if {$curHEAD eq $new_hash} { | ||||
| 		_after_readtree $this | ||||
| 	} elseif {[is_config_true gui.trustmtime]} { | ||||
| 		_readtree $this | ||||
| 	} else { | ||||
| 		ui_status {Refreshing file status...} | ||||
|  | @ -378,22 +385,24 @@ method _after_readtree {} { | |||
| 	set rn [string length $rh] | ||||
| 	if {[string equal -length $rn $rh $new_ref]} { | ||||
| 		set new_branch [string range $new_ref $rn end] | ||||
| 		append log " to $new_branch" | ||||
|  | ||||
| 		if {[catch { | ||||
| 				git symbolic-ref -m $log HEAD $new_ref | ||||
| 			} err]} { | ||||
| 			_fatal $this $err | ||||
| 		if {$is_detached || $current_branch ne $new_branch} { | ||||
| 			append log " to $new_branch" | ||||
| 			if {[catch { | ||||
| 					git symbolic-ref -m $log HEAD $new_ref | ||||
| 				} err]} { | ||||
| 				_fatal $this $err | ||||
| 			} | ||||
| 			set current_branch $new_branch | ||||
| 			set is_detached 0 | ||||
| 		} | ||||
| 		set current_branch $new_branch | ||||
| 		set is_detached 0 | ||||
| 	} else { | ||||
| 		append log " to $new_expr" | ||||
|  | ||||
| 		if {[catch { | ||||
| 				_detach_HEAD $log $new_hash | ||||
| 			} err]} { | ||||
| 			_fatal $this $err | ||||
| 		if {$new_hash ne $HEAD} { | ||||
| 			append log " to $new_expr" | ||||
| 			if {[catch { | ||||
| 					_detach_HEAD $log $new_hash | ||||
| 				} err]} { | ||||
| 				_fatal $this $err | ||||
| 			} | ||||
| 		} | ||||
| 		set current_branch HEAD | ||||
| 		set is_detached 1 | ||||
|  |  | |||
|  | @ -16,10 +16,28 @@ field cur_specs [list]; # list of specs for $revtype | |||
| field spec_head       ; # list of all head specs | ||||
| field spec_trck       ; # list of all tracking branch specs | ||||
| field spec_tag        ; # list of all tag specs | ||||
| field tip_data        ; # array of tip commit info by refname | ||||
| field log_last        ; # array of reflog date by refname | ||||
|  | ||||
| constructor new {path {title {}}} { | ||||
| field tooltip_wm        {} ; # Current tooltip toplevel, if open | ||||
| field tooltip_t         {} ; # Text widget in $tooltip_wm | ||||
| field tooltip_timer     {} ; # Current timer event for our tooltip | ||||
|  | ||||
| proc new {path {title {}}} { | ||||
| 	return [_new $path 0 $title] | ||||
| } | ||||
|  | ||||
| proc new_unmerged {path {title {}}} { | ||||
| 	return [_new $path 1 $title] | ||||
| } | ||||
|  | ||||
| constructor _new {path unmerged_only title} { | ||||
| 	global current_branch is_detached | ||||
|  | ||||
| 	if {![info exists ::all_remotes]} { | ||||
| 		load_all_remotes | ||||
| 	} | ||||
|  | ||||
| 	set w $path | ||||
|  | ||||
| 	if {$title ne {}} { | ||||
|  | @ -86,13 +104,17 @@ constructor new {path {title {}}} { | |||
| 	listbox $w_list \ | ||||
| 		-font font_diff \ | ||||
| 		-width 50 \ | ||||
| 		-height 5 \ | ||||
| 		-height 10 \ | ||||
| 		-selectmode browse \ | ||||
| 		-exportselection false \ | ||||
| 		-xscrollcommand [cb _sb_set $w.list.sbx h] \ | ||||
| 		-yscrollcommand [cb _sb_set $w.list.sby v] | ||||
| 	pack $w_list -fill both -expand 1 | ||||
| 	grid $w.list -sticky nswe -padx {20 5} -columnspan 2 | ||||
| 	bind $w_list <Any-Motion>  [cb _show_tooltip @%x,%y] | ||||
| 	bind $w_list <Any-Enter>   [cb _hide_tooltip] | ||||
| 	bind $w_list <Any-Leave>   [cb _hide_tooltip] | ||||
| 	bind $w_list <Destroy>     [cb _hide_tooltip] | ||||
|  | ||||
| 	grid columnconfigure $w 1 -weight 1 | ||||
| 	if {$is_detached} { | ||||
|  | @ -105,21 +127,89 @@ constructor new {path {title {}}} { | |||
| 	bind $w_filter <Key-Return> [list focus $w_list]\;break | ||||
| 	bind $w_filter <Key-Down>   [list focus $w_list] | ||||
|  | ||||
| 	set fmt list | ||||
| 	append fmt { %(refname)} | ||||
| 	append fmt { [list} | ||||
| 	append fmt { %(objecttype)} | ||||
| 	append fmt { %(objectname)} | ||||
| 	append fmt { [concat %(taggername) %(authorname)]} | ||||
| 	append fmt { [concat %(taggerdate) %(authordate)]} | ||||
| 	append fmt { %(subject)} | ||||
| 	append fmt {] [list} | ||||
| 	append fmt { %(*objecttype)} | ||||
| 	append fmt { %(*objectname)} | ||||
| 	append fmt { %(*authorname)} | ||||
| 	append fmt { %(*authordate)} | ||||
| 	append fmt { %(*subject)} | ||||
| 	append fmt {]} | ||||
| 	set all_refn [list] | ||||
| 	set fr_fd [git_read for-each-ref \ | ||||
| 		--tcl \ | ||||
| 		--sort=-taggerdate \ | ||||
| 		--format=$fmt \ | ||||
| 		refs/heads \ | ||||
| 		refs/remotes \ | ||||
| 		refs/tags \ | ||||
| 		] | ||||
| 	fconfigure $fr_fd -translation lf -encoding utf-8 | ||||
| 	while {[gets $fr_fd line] > 0} { | ||||
| 		set line [eval $line] | ||||
| 		if {[lindex $line 1 0] eq {tag}} { | ||||
| 			if {[lindex $line 2 0] eq {commit}} { | ||||
| 				set sha1 [lindex $line 2 1] | ||||
| 			} else { | ||||
| 				continue | ||||
| 			} | ||||
| 		} elseif {[lindex $line 1 0] eq {commit}} { | ||||
| 			set sha1 [lindex $line 1 1] | ||||
| 		} else { | ||||
| 			continue | ||||
| 		} | ||||
| 		set refn [lindex $line 0] | ||||
| 		set tip_data($refn) [lrange $line 1 end] | ||||
| 		lappend cmt_refn($sha1) $refn | ||||
| 		lappend all_refn $refn | ||||
| 	} | ||||
| 	close $fr_fd | ||||
|  | ||||
| 	if {$unmerged_only} { | ||||
| 		set fr_fd [git_read rev-list --all ^$::HEAD] | ||||
| 		while {[gets $fr_fd sha1] > 0} { | ||||
| 			if {[catch {set rlst $cmt_refn($sha1)}]} continue | ||||
| 			foreach refn $rlst { | ||||
| 				set inc($refn) 1 | ||||
| 			} | ||||
| 		} | ||||
| 		close $fr_fd | ||||
| 	} else { | ||||
| 		foreach refn $all_refn { | ||||
| 			set inc($refn) 1 | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	set spec_head [list] | ||||
| 	foreach name [load_all_heads] { | ||||
| 		lappend spec_head [list $name refs/heads/$name] | ||||
| 		set refn refs/heads/$name | ||||
| 		if {[info exists inc($refn)]} { | ||||
| 			lappend spec_head [list $name $refn] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	set spec_trck [list] | ||||
| 	foreach spec [all_tracking_branches] { | ||||
| 		set name [lindex $spec 0] | ||||
| 		regsub ^refs/(heads|remotes)/ $name {} name | ||||
| 		lappend spec_trck [concat $name $spec] | ||||
| 		set refn [lindex $spec 0] | ||||
| 		if {[info exists inc($refn)]} { | ||||
| 			regsub ^refs/(heads|remotes)/ $refn {} name | ||||
| 			lappend spec_trck [concat $name $spec] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	set spec_tag [list] | ||||
| 	foreach name [load_all_tags] { | ||||
| 		lappend spec_tag [list $name refs/tags/$name] | ||||
| 		set refn refs/tags/$name | ||||
| 		if {[info exists inc($refn)]} { | ||||
| 			lappend spec_tag [list $name $refn] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 		  if {$is_detached}             { set revtype HEAD | ||||
|  | @ -364,4 +454,174 @@ method _sb_set {sb orient first last} { | |||
| 	$sb set $first $last | ||||
| } | ||||
|  | ||||
| method _show_tooltip {pos} { | ||||
| 	if {$tooltip_wm ne {}} { | ||||
| 		_open_tooltip $this | ||||
| 	} elseif {$tooltip_timer eq {}} { | ||||
| 		set tooltip_timer [after 1000 [cb _open_tooltip]] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| method _open_tooltip {} { | ||||
| 	global remote_url | ||||
|  | ||||
| 	set tooltip_timer {} | ||||
| 	set pos_x [winfo pointerx $w_list] | ||||
| 	set pos_y [winfo pointery $w_list] | ||||
| 	if {[winfo containing $pos_x $pos_y] ne $w_list} { | ||||
| 		_hide_tooltip $this | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	set pos @[join [list \ | ||||
| 		[expr {$pos_x - [winfo rootx $w_list]}] \ | ||||
| 		[expr {$pos_y - [winfo rooty $w_list]}]] ,] | ||||
| 	set lno [$w_list index $pos] | ||||
| 	if {$lno eq {}} { | ||||
| 		_hide_tooltip $this | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	set spec [lindex $cur_specs $lno] | ||||
| 	set refn [lindex $spec 1] | ||||
| 	if {$refn eq {}} { | ||||
| 		_hide_tooltip $this | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if {$tooltip_wm eq {}} { | ||||
| 		set tooltip_wm [toplevel $w_list.tooltip -borderwidth 1] | ||||
| 		wm overrideredirect $tooltip_wm 1 | ||||
| 		wm transient $tooltip_wm [winfo toplevel $w_list] | ||||
| 		set tooltip_t $tooltip_wm.label | ||||
| 		text $tooltip_t \ | ||||
| 			-takefocus 0 \ | ||||
| 			-highlightthickness 0 \ | ||||
| 			-relief flat \ | ||||
| 			-borderwidth 0 \ | ||||
| 			-wrap none \ | ||||
| 			-background lightyellow \ | ||||
| 			-foreground black | ||||
| 		$tooltip_t tag conf section_header -font font_uibold | ||||
| 		bind $tooltip_wm <Escape> [cb _hide_tooltip] | ||||
| 		pack $tooltip_t | ||||
| 	} else { | ||||
| 		$tooltip_t conf -state normal | ||||
| 		$tooltip_t delete 0.0 end | ||||
| 	} | ||||
|  | ||||
| 	set data $tip_data($refn) | ||||
| 	if {[lindex $data 0 0] eq {tag}} { | ||||
| 		set tag  [lindex $data 0] | ||||
| 		if {[lindex $data 1 0] eq {commit}} { | ||||
| 			set cmit [lindex $data 1] | ||||
| 		} else { | ||||
| 			set cmit {} | ||||
| 		} | ||||
| 	} elseif {[lindex $data 0 0] eq {commit}} { | ||||
| 		set tag  {} | ||||
| 		set cmit [lindex $data 0] | ||||
| 	} | ||||
|  | ||||
| 	$tooltip_t insert end [lindex $spec 0] | ||||
| 	set last [_reflog_last $this [lindex $spec 1]] | ||||
| 	if {$last ne {}} { | ||||
| 		$tooltip_t insert end "\n" | ||||
| 		$tooltip_t insert end "updated" | ||||
| 		$tooltip_t insert end " $last" | ||||
| 	} | ||||
| 	$tooltip_t insert end "\n" | ||||
|  | ||||
| 	if {$tag ne {}} { | ||||
| 		$tooltip_t insert end "\n" | ||||
| 		$tooltip_t insert end "tag" section_header | ||||
| 		$tooltip_t insert end "  [lindex $tag 1]\n" | ||||
| 		$tooltip_t insert end [lindex $tag 2] | ||||
| 		$tooltip_t insert end " ([lindex $tag 3])\n" | ||||
| 		$tooltip_t insert end [lindex $tag 4] | ||||
| 		$tooltip_t insert end "\n" | ||||
| 	} | ||||
|  | ||||
| 	if {$cmit ne {}} { | ||||
| 		$tooltip_t insert end "\n" | ||||
| 		$tooltip_t insert end "commit" section_header | ||||
| 		$tooltip_t insert end "  [lindex $cmit 1]\n" | ||||
| 		$tooltip_t insert end [lindex $cmit 2] | ||||
| 		$tooltip_t insert end " ([lindex $cmit 3])\n" | ||||
| 		$tooltip_t insert end [lindex $cmit 4] | ||||
| 	} | ||||
|  | ||||
| 	if {[llength $spec] > 2} { | ||||
| 		$tooltip_t insert end "\n" | ||||
| 		$tooltip_t insert end "remote" section_header | ||||
| 		$tooltip_t insert end "  [lindex $spec 2]\n" | ||||
| 		$tooltip_t insert end "url" | ||||
| 		$tooltip_t insert end " $remote_url([lindex $spec 2])\n" | ||||
| 		$tooltip_t insert end "branch" | ||||
| 		$tooltip_t insert end " [lindex $spec 3]" | ||||
| 	} | ||||
|  | ||||
| 	$tooltip_t conf -state disabled | ||||
| 	_position_tooltip $this | ||||
| } | ||||
|  | ||||
| method _reflog_last {name} { | ||||
| 	if {[info exists reflog_last($name)]} { | ||||
| 		return reflog_last($name) | ||||
| 	} | ||||
|  | ||||
| 	set last {} | ||||
| 	if {[catch {set last [file mtime [gitdir $name]]}] | ||||
| 	&& ![catch {set g [open [gitdir logs $name] r]}]} { | ||||
| 		fconfigure $g -translation binary | ||||
| 		while {[gets $g line] >= 0} { | ||||
| 			if {[regexp {> ([1-9][0-9]*) } $line line when]} { | ||||
| 				set last $when | ||||
| 			} | ||||
| 		} | ||||
| 		close $g | ||||
| 	} | ||||
|  | ||||
| 	if {$last ne {}} { | ||||
| 		set last [clock format $last -format {%a %b %e %H:%M:%S %Y}] | ||||
| 	} | ||||
| 	set reflog_last($name) $last | ||||
| 	return $last | ||||
| } | ||||
|  | ||||
| method _position_tooltip {} { | ||||
| 	set max_h [lindex [split [$tooltip_t index end] .] 0] | ||||
| 	set max_w 0 | ||||
| 	for {set i 1} {$i <= $max_h} {incr i} { | ||||
| 		set c [lindex [split [$tooltip_t index "$i.0 lineend"] .] 1] | ||||
| 		if {$c > $max_w} {set max_w $c} | ||||
| 	} | ||||
| 	$tooltip_t conf -width $max_w -height $max_h | ||||
|  | ||||
| 	set req_w [winfo reqwidth  $tooltip_t] | ||||
| 	set req_h [winfo reqheight $tooltip_t] | ||||
| 	set pos_x [expr {[winfo pointerx .] +  5}] | ||||
| 	set pos_y [expr {[winfo pointery .] + 10}] | ||||
|  | ||||
| 	set g "${req_w}x${req_h}" | ||||
| 	if {$pos_x >= 0} {append g +} | ||||
| 	append g $pos_x | ||||
| 	if {$pos_y >= 0} {append g +} | ||||
| 	append g $pos_y | ||||
|  | ||||
| 	wm geometry $tooltip_wm $g | ||||
| 	raise $tooltip_wm | ||||
| } | ||||
|  | ||||
| method _hide_tooltip {} { | ||||
| 	if {$tooltip_wm ne {}} { | ||||
| 		destroy $tooltip_wm | ||||
| 		set tooltip_wm {} | ||||
| 	} | ||||
| 	if {$tooltip_timer ne {}} { | ||||
| 		after cancel $tooltip_timer | ||||
| 		set tooltip_timer {} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| } | ||||
|  |  | |||
|  | @ -37,9 +37,14 @@ You are currently in the middle of a merge that has not been fully completed.  Y | |||
| 					set enc [string tolower [string range $line 9 end]] | ||||
| 				} | ||||
| 			} | ||||
| 			set msg [encoding convertfrom $enc [read $fd]] | ||||
| 			set msg [string trim $msg] | ||||
| 			set msg [read $fd] | ||||
| 			close $fd | ||||
|  | ||||
| 			set enc [tcl_encoding $enc] | ||||
| 			if {$enc ne {}} { | ||||
| 				set msg [encoding convertfrom $enc $msg] | ||||
| 			} | ||||
| 			set msg [string trim $msg] | ||||
| 		} err]} { | ||||
| 		error_popup "Error loading commit data for amend:\n\n$err" | ||||
| 		return | ||||
|  | @ -148,7 +153,7 @@ The rescan will be automatically started now. | |||
| 		U? { | ||||
| 			error_popup "Unmerged files cannot be committed. | ||||
|  | ||||
| File [short_path $path] has merge conflicts.  You must resolve them and add the file before committing. | ||||
| File [short_path $path] has merge conflicts.  You must resolve them and stage the file before committing. | ||||
| " | ||||
| 			unlock_index | ||||
| 			return | ||||
|  | @ -164,7 +169,7 @@ File [short_path $path] cannot be committed by this program. | |||
| 	if {!$files_ready && ![string match *merge $curType]} { | ||||
| 		info_popup {No changes to commit. | ||||
|  | ||||
| You must add at least 1 file before you can commit. | ||||
| You must stage at least 1 file before you can commit. | ||||
| } | ||||
| 		unlock_index | ||||
| 		return | ||||
|  | @ -209,7 +214,7 @@ A good commit message has the following format: | |||
| 	ui_status {Calling pre-commit hook...} | ||||
| 	set pch_error {} | ||||
| 	set fd_ph [open "| $pchook" r] | ||||
| 	fconfigure $fd_ph -blocking 0 -translation binary | ||||
| 	fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} | ||||
| 	fileevent $fd_ph readable \ | ||||
| 		[list commit_prehook_wait $fd_ph $curHEAD $msg] | ||||
| } | ||||
|  | @ -287,11 +292,18 @@ A rescan will be automatically started now. | |||
| 	# | ||||
| 	set msg_p [gitdir COMMIT_EDITMSG] | ||||
| 	set msg_wt [open $msg_p w] | ||||
| 	fconfigure $msg_wt -translation lf | ||||
| 	if {[catch {set enc $repo_config(i18n.commitencoding)}]} { | ||||
| 		set enc utf-8 | ||||
| 	} | ||||
| 	fconfigure $msg_wt -encoding binary -translation binary | ||||
| 	puts -nonewline $msg_wt [encoding convertto $enc $msg] | ||||
| 	set use_enc [tcl_encoding $enc] | ||||
| 	if {$use_enc ne {}} { | ||||
| 		fconfigure $msg_wt -encoding $use_enc | ||||
| 	} else { | ||||
| 		puts stderr "warning: Tcl does not support encoding '$enc'." | ||||
| 		fconfigure $msg_wt -encoding utf-8 | ||||
| 	} | ||||
| 	puts -nonewline $msg_wt $msg | ||||
| 	close $msg_wt | ||||
|  | ||||
| 	# -- Create the commit. | ||||
|  | @ -367,6 +379,10 @@ A rescan will be automatically started now. | |||
| 	$ui_comm delete 0.0 end | ||||
| 	$ui_comm edit reset | ||||
| 	$ui_comm edit modified false | ||||
| 	if {$::GITGUI_BCK_exists} { | ||||
| 		catch {file delete [gitdir GITGUI_BCK]} | ||||
| 		set ::GITGUI_BCK_exists 0 | ||||
| 	} | ||||
|  | ||||
| 	if {[is_enabled singlecommit]} do_quit | ||||
|  | ||||
|  |  | |||
|  | @ -87,3 +87,30 @@ proc do_fsck_objects {} { | |||
| 	lappend cmd --strict | ||||
| 	console::exec $w $cmd | ||||
| } | ||||
|  | ||||
| proc hint_gc {} { | ||||
| 	set object_limit 8 | ||||
| 	if {[is_Windows]} { | ||||
| 		set object_limit 1 | ||||
| 	} | ||||
|  | ||||
| 	set objects_current [llength [glob \ | ||||
| 		-directory [gitdir objects 42] \ | ||||
| 		-nocomplain \ | ||||
| 		-tails \ | ||||
| 		-- \ | ||||
| 		*]] | ||||
|  | ||||
| 	if {$objects_current >= $object_limit} { | ||||
| 		set objects_current [expr {$objects_current * 256}] | ||||
| 		set object_limit    [expr {$object_limit    * 256}] | ||||
| 		if {[ask_popup \ | ||||
| 			"This repository currently has approximately $objects_current loose objects. | ||||
|  | ||||
| To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist. | ||||
|  | ||||
| Compress the database now?"] eq yes} { | ||||
| 			do_gc | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -86,6 +86,7 @@ proc show_diff {path w {lno {}}} { | |||
| 		set max_sz [expr {128 * 1024}] | ||||
| 		if {[catch { | ||||
| 				set fd [open $path r] | ||||
| 				fconfigure $fd -eofchar {} | ||||
| 				set content [read $fd $max_sz] | ||||
| 				close $fd | ||||
| 				set sz [file size $path] | ||||
|  |  | |||
|  | @ -0,0 +1,276 @@ | |||
| # git-gui encoding support | ||||
| # Copyright (C) 2005 Paul Mackerras <paulus@samba.org> | ||||
| # (Copied from gitk, commit fd8ccbec4f0161) | ||||
|  | ||||
| # This list of encoding names and aliases is distilled from | ||||
| # http://www.iana.org/assignments/character-sets. | ||||
| # Not all of them are supported by Tcl. | ||||
| set encoding_aliases { | ||||
|     { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII | ||||
|       ISO646-US US-ASCII us IBM367 cp367 csASCII } | ||||
|     { ISO-10646-UTF-1 csISO10646UTF1 } | ||||
|     { ISO_646.basic:1983 ref csISO646basic1983 } | ||||
|     { INVARIANT csINVARIANT } | ||||
|     { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion } | ||||
|     { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom } | ||||
|     { NATS-SEFI iso-ir-8-1 csNATSSEFI } | ||||
|     { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD } | ||||
|     { NATS-DANO iso-ir-9-1 csNATSDANO } | ||||
|     { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD } | ||||
|     { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish } | ||||
|     { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames } | ||||
|     { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 } | ||||
|     { ISO-2022-KR csISO2022KR } | ||||
|     { EUC-KR csEUCKR } | ||||
|     { ISO-2022-JP csISO2022JP } | ||||
|     { ISO-2022-JP-2 csISO2022JP2 } | ||||
|     { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7 | ||||
|       csISO13JISC6220jp } | ||||
|     { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro } | ||||
|     { IT iso-ir-15 ISO646-IT csISO15Italian } | ||||
|     { PT iso-ir-16 ISO646-PT csISO16Portuguese } | ||||
|     { ES iso-ir-17 ISO646-ES csISO17Spanish } | ||||
|     { greek7-old iso-ir-18 csISO18Greek7Old } | ||||
|     { latin-greek iso-ir-19 csISO19LatinGreek } | ||||
|     { DIN_66003 iso-ir-21 de ISO646-DE csISO21German } | ||||
|     { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French } | ||||
|     { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 } | ||||
|     { ISO_5427 iso-ir-37 csISO5427Cyrillic } | ||||
|     { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 } | ||||
|     { BS_viewdata iso-ir-47 csISO47BSViewdata } | ||||
|     { INIS iso-ir-49 csISO49INIS } | ||||
|     { INIS-8 iso-ir-50 csISO50INIS8 } | ||||
|     { INIS-cyrillic iso-ir-51 csISO51INISCyrillic } | ||||
|     { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 } | ||||
|     { ISO_5428:1980 iso-ir-55 csISO5428Greek } | ||||
|     { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 } | ||||
|     { GB_2312-80 iso-ir-58 chinese csISO58GB231280 } | ||||
|     { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian | ||||
|       csISO60Norwegian1 } | ||||
|     { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 } | ||||
|     { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French } | ||||
|     { videotex-suppl iso-ir-70 csISO70VideotexSupp1 } | ||||
|     { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 } | ||||
|     { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 } | ||||
|     { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian } | ||||
|     { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 } | ||||
|     { greek7 iso-ir-88 csISO88Greek7 } | ||||
|     { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 } | ||||
|     { iso-ir-90 csISO90 } | ||||
|     { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a } | ||||
|     { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b | ||||
|       csISO92JISC62991984b } | ||||
|     { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd } | ||||
|     { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand } | ||||
|     { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add | ||||
|       csISO95JIS62291984handadd } | ||||
|     { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana } | ||||
|     { ISO_2033-1983 iso-ir-98 e13b csISO2033 } | ||||
|     { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS } | ||||
|     { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819 | ||||
|       CP819 csISOLatin1 } | ||||
|     { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 } | ||||
|     { T.61-7bit iso-ir-102 csISO102T617bit } | ||||
|     { T.61-8bit T.61 iso-ir-103 csISO103T618bit } | ||||
|     { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 } | ||||
|     { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 } | ||||
|     { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic } | ||||
|     { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 } | ||||
|     { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 } | ||||
|     { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr } | ||||
|     { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708 | ||||
|       arabic csISOLatinArabic } | ||||
|     { ISO_8859-6-E csISO88596E ISO-8859-6-E } | ||||
|     { ISO_8859-6-I csISO88596I ISO-8859-6-I } | ||||
|     { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118 | ||||
|       greek greek8 csISOLatinGreek } | ||||
|     { T.101-G2 iso-ir-128 csISO128T101G2 } | ||||
|     { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew | ||||
|       csISOLatinHebrew } | ||||
|     { ISO_8859-8-E csISO88598E ISO-8859-8-E } | ||||
|     { ISO_8859-8-I csISO88598I ISO-8859-8-I } | ||||
|     { CSN_369103 iso-ir-139 csISO139CSN369103 } | ||||
|     { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 } | ||||
|     { ISO_6937-2-add iso-ir-142 csISOTextComm } | ||||
|     { IEC_P27-1 iso-ir-143 csISO143IECP271 } | ||||
|     { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic | ||||
|       csISOLatinCyrillic } | ||||
|     { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian } | ||||
|     { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian } | ||||
|     { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 } | ||||
|     { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT } | ||||
|     { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba } | ||||
|     { ISO_6937-2-25 iso-ir-152 csISO6937Add } | ||||
|     { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 } | ||||
|     { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp } | ||||
|     { ISO_10367-box iso-ir-155 csISO10367Box } | ||||
|     { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 } | ||||
|     { latin-lap lap iso-ir-158 csISO158Lap } | ||||
|     { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 } | ||||
|     { DS_2089 DS2089 ISO646-DK dk csISO646Danish } | ||||
|     { us-dk csUSDK } | ||||
|     { dk-us csDKUS } | ||||
|     { JIS_X0201 X0201 csHalfWidthKatakana } | ||||
|     { KSC5636 ISO646-KR csKSC5636 } | ||||
|     { ISO-10646-UCS-2 csUnicode } | ||||
|     { ISO-10646-UCS-4 csUCS4 } | ||||
|     { DEC-MCS dec csDECMCS } | ||||
|     { hp-roman8 roman8 r8 csHPRoman8 } | ||||
|     { macintosh mac csMacintosh } | ||||
|     { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl | ||||
|       csIBM037 } | ||||
|     { IBM038 EBCDIC-INT cp038 csIBM038 } | ||||
|     { IBM273 CP273 csIBM273 } | ||||
|     { IBM274 EBCDIC-BE CP274 csIBM274 } | ||||
|     { IBM275 EBCDIC-BR cp275 csIBM275 } | ||||
|     { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 } | ||||
|     { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 } | ||||
|     { IBM280 CP280 ebcdic-cp-it csIBM280 } | ||||
|     { IBM281 EBCDIC-JP-E cp281 csIBM281 } | ||||
|     { IBM284 CP284 ebcdic-cp-es csIBM284 } | ||||
|     { IBM285 CP285 ebcdic-cp-gb csIBM285 } | ||||
|     { IBM290 cp290 EBCDIC-JP-kana csIBM290 } | ||||
|     { IBM297 cp297 ebcdic-cp-fr csIBM297 } | ||||
|     { IBM420 cp420 ebcdic-cp-ar1 csIBM420 } | ||||
|     { IBM423 cp423 ebcdic-cp-gr csIBM423 } | ||||
|     { IBM424 cp424 ebcdic-cp-he csIBM424 } | ||||
|     { IBM437 cp437 437 csPC8CodePage437 } | ||||
|     { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 } | ||||
|     { IBM775 cp775 csPC775Baltic } | ||||
|     { IBM850 cp850 850 csPC850Multilingual } | ||||
|     { IBM851 cp851 851 csIBM851 } | ||||
|     { IBM852 cp852 852 csPCp852 } | ||||
|     { IBM855 cp855 855 csIBM855 } | ||||
|     { IBM857 cp857 857 csIBM857 } | ||||
|     { IBM860 cp860 860 csIBM860 } | ||||
|     { IBM861 cp861 861 cp-is csIBM861 } | ||||
|     { IBM862 cp862 862 csPC862LatinHebrew } | ||||
|     { IBM863 cp863 863 csIBM863 } | ||||
|     { IBM864 cp864 csIBM864 } | ||||
|     { IBM865 cp865 865 csIBM865 } | ||||
|     { IBM866 cp866 866 csIBM866 } | ||||
|     { IBM868 CP868 cp-ar csIBM868 } | ||||
|     { IBM869 cp869 869 cp-gr csIBM869 } | ||||
|     { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 } | ||||
|     { IBM871 CP871 ebcdic-cp-is csIBM871 } | ||||
|     { IBM880 cp880 EBCDIC-Cyrillic csIBM880 } | ||||
|     { IBM891 cp891 csIBM891 } | ||||
|     { IBM903 cp903 csIBM903 } | ||||
|     { IBM904 cp904 904 csIBBM904 } | ||||
|     { IBM905 CP905 ebcdic-cp-tr csIBM905 } | ||||
|     { IBM918 CP918 ebcdic-cp-ar2 csIBM918 } | ||||
|     { IBM1026 CP1026 csIBM1026 } | ||||
|     { EBCDIC-AT-DE csIBMEBCDICATDE } | ||||
|     { EBCDIC-AT-DE-A csEBCDICATDEA } | ||||
|     { EBCDIC-CA-FR csEBCDICCAFR } | ||||
|     { EBCDIC-DK-NO csEBCDICDKNO } | ||||
|     { EBCDIC-DK-NO-A csEBCDICDKNOA } | ||||
|     { EBCDIC-FI-SE csEBCDICFISE } | ||||
|     { EBCDIC-FI-SE-A csEBCDICFISEA } | ||||
|     { EBCDIC-FR csEBCDICFR } | ||||
|     { EBCDIC-IT csEBCDICIT } | ||||
|     { EBCDIC-PT csEBCDICPT } | ||||
|     { EBCDIC-ES csEBCDICES } | ||||
|     { EBCDIC-ES-A csEBCDICESA } | ||||
|     { EBCDIC-ES-S csEBCDICESS } | ||||
|     { EBCDIC-UK csEBCDICUK } | ||||
|     { EBCDIC-US csEBCDICUS } | ||||
|     { UNKNOWN-8BIT csUnknown8BiT } | ||||
|     { MNEMONIC csMnemonic } | ||||
|     { MNEM csMnem } | ||||
|     { VISCII csVISCII } | ||||
|     { VIQR csVIQR } | ||||
|     { KOI8-R csKOI8R } | ||||
|     { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro } | ||||
|     { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro } | ||||
|     { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro } | ||||
|     { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro } | ||||
|     { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro } | ||||
|     { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro } | ||||
|     { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro } | ||||
|     { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro } | ||||
|     { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro } | ||||
|     { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro } | ||||
|     { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro } | ||||
|     { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro } | ||||
|     { IBM1047 IBM-1047 } | ||||
|     { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian } | ||||
|     { Amiga-1251 Ami1251 Amiga1251 Ami-1251 } | ||||
|     { UNICODE-1-1 csUnicode11 } | ||||
|     { CESU-8 csCESU-8 } | ||||
|     { BOCU-1 csBOCU-1 } | ||||
|     { UNICODE-1-1-UTF-7 csUnicode11UTF7 } | ||||
|     { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic | ||||
|       l8 } | ||||
|     { ISO-8859-15 ISO_8859-15 Latin-9 } | ||||
|     { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 } | ||||
|     { GBK CP936 MS936 windows-936 } | ||||
|     { JIS_Encoding csJISEncoding } | ||||
|     { Shift_JIS MS_Kanji csShiftJIS } | ||||
|     { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese | ||||
|       EUC-JP } | ||||
|     { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese } | ||||
|     { ISO-10646-UCS-Basic csUnicodeASCII } | ||||
|     { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 } | ||||
|     { ISO-Unicode-IBM-1261 csUnicodeIBM1261 } | ||||
|     { ISO-Unicode-IBM-1268 csUnicodeIBM1268 } | ||||
|     { ISO-Unicode-IBM-1276 csUnicodeIBM1276 } | ||||
|     { ISO-Unicode-IBM-1264 csUnicodeIBM1264 } | ||||
|     { ISO-Unicode-IBM-1265 csUnicodeIBM1265 } | ||||
|     { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 } | ||||
|     { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 } | ||||
|     { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 } | ||||
|     { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 } | ||||
|     { Adobe-Standard-Encoding csAdobeStandardEncoding } | ||||
|     { Ventura-US csVenturaUS } | ||||
|     { Ventura-International csVenturaInternational } | ||||
|     { PC8-Danish-Norwegian csPC8DanishNorwegian } | ||||
|     { PC8-Turkish csPC8Turkish } | ||||
|     { IBM-Symbols csIBMSymbols } | ||||
|     { IBM-Thai csIBMThai } | ||||
|     { HP-Legal csHPLegal } | ||||
|     { HP-Pi-font csHPPiFont } | ||||
|     { HP-Math8 csHPMath8 } | ||||
|     { Adobe-Symbol-Encoding csHPPSMath } | ||||
|     { HP-DeskTop csHPDesktop } | ||||
|     { Ventura-Math csVenturaMath } | ||||
|     { Microsoft-Publishing csMicrosoftPublishing } | ||||
|     { Windows-31J csWindows31J } | ||||
|     { GB2312 csGB2312 } | ||||
|     { Big5 csBig5 } | ||||
| } | ||||
|  | ||||
| proc tcl_encoding {enc} { | ||||
|     global encoding_aliases | ||||
|     set names [encoding names] | ||||
|     set lcnames [string tolower $names] | ||||
|     set enc [string tolower $enc] | ||||
|     set i [lsearch -exact $lcnames $enc] | ||||
|     if {$i < 0} { | ||||
| 	# look for "isonnn" instead of "iso-nnn" or "iso_nnn" | ||||
| 	if {[regsub {^iso[-_]} $enc iso encx]} { | ||||
| 	    set i [lsearch -exact $lcnames $encx] | ||||
| 	} | ||||
|     } | ||||
|     if {$i < 0} { | ||||
| 	foreach l $encoding_aliases { | ||||
| 	    set ll [string tolower $l] | ||||
| 	    if {[lsearch -exact $ll $enc] < 0} continue | ||||
| 	    # look through the aliases for one that tcl knows about | ||||
| 	    foreach e $ll { | ||||
| 		set i [lsearch -exact $lcnames $e] | ||||
| 		if {$i < 0} { | ||||
| 		    if {[regsub {^iso[-_]} $e iso ex]} { | ||||
| 			set i [lsearch -exact $lcnames $ex] | ||||
| 		    } | ||||
| 		} | ||||
| 		if {$i >= 0} break | ||||
| 	    } | ||||
| 	    break | ||||
| 	} | ||||
|     } | ||||
|     if {$i >= 0} { | ||||
| 	return [lindex $names $i] | ||||
|     } | ||||
|     return {} | ||||
| } | ||||
|  | @ -51,12 +51,15 @@ proc ask_popup {msg} { | |||
| 	if {[reponame] ne {}} { | ||||
| 		append title " ([reponame])" | ||||
| 	} | ||||
| 	return [tk_messageBox \ | ||||
| 		-parent . \ | ||||
| 	set cmd [list tk_messageBox \ | ||||
| 		-icon question \ | ||||
| 		-type yesno \ | ||||
| 		-title $title \ | ||||
| 		-message $msg] | ||||
| 	if {[winfo ismapped .]} { | ||||
| 		lappend cmd -parent . | ||||
| 	} | ||||
| 	eval $cmd | ||||
| } | ||||
|  | ||||
| proc hook_failed_popup {hook msg} { | ||||
|  |  | |||
|  | @ -360,7 +360,7 @@ proc revert_helper {txt paths} { | |||
| 		"[appname] ([reponame])" \ | ||||
| 		"Revert changes in $s? | ||||
|  | ||||
| Any unadded changes will be permanently lost by the revert." \ | ||||
| Any unstaged changes will be permanently lost by the revert." \ | ||||
| 		question \ | ||||
| 		1 \ | ||||
| 		{Do Nothing} \ | ||||
|  |  | |||
|  | @ -1,9 +1,12 @@ | |||
| # git-gui branch merge support | ||||
| # Copyright (C) 2006, 2007 Shawn Pearce | ||||
|  | ||||
| namespace eval merge { | ||||
| class merge { | ||||
|  | ||||
| proc _can_merge {} { | ||||
| field w         ; # top level window | ||||
| field w_rev     ; # mega-widget to pick the revision to merge | ||||
|  | ||||
| method _can_merge {} { | ||||
| 	global HEAD commit_type file_states | ||||
|  | ||||
| 	if {[string match amend* $commit_type]} { | ||||
|  | @ -42,7 +45,7 @@ The rescan will be automatically started now. | |||
|  | ||||
| File [short_path $path] has merge conflicts. | ||||
|  | ||||
| You must resolve them, add the file, and commit to complete the current merge.  Only then can you begin another merge. | ||||
| You must resolve them, stage the file, and commit to complete the current merge.  Only then can you begin another merge. | ||||
| " | ||||
| 			unlock_index | ||||
| 			return 0 | ||||
|  | @ -63,147 +66,93 @@ You should complete the current commit before starting a merge.  Doing so will h | |||
| 	return 1 | ||||
| } | ||||
|  | ||||
| proc _refs {w list} { | ||||
| 	set r {} | ||||
| 	foreach i [$w.source.l curselection] { | ||||
| 		lappend r [lindex [lindex $list $i] 0] | ||||
| method _rev {} { | ||||
| 	if {[catch {$w_rev commit_or_die}]} { | ||||
| 		return {} | ||||
| 	} | ||||
| 	return $r | ||||
| 	return [$w_rev get] | ||||
| } | ||||
|  | ||||
| proc _visualize {w list} { | ||||
| 	set revs [_refs $w $list] | ||||
| 	if {$revs eq {}} return | ||||
| 	lappend revs --not HEAD | ||||
| 	do_gitk $revs | ||||
| method _visualize {} { | ||||
| 	set rev [_rev $this] | ||||
| 	if {$rev ne {}} { | ||||
| 		do_gitk [list $rev --not HEAD] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| proc _start {w list} { | ||||
| 	global HEAD current_branch | ||||
| method _start {} { | ||||
| 	global HEAD current_branch remote_url | ||||
|  | ||||
| 	set cmd [list git merge] | ||||
| 	set names [_refs $w $list] | ||||
| 	set revcnt [llength $names] | ||||
| 	append cmd { } $names | ||||
|  | ||||
| 	if {$revcnt == 0} { | ||||
| 	set name [_rev $this] | ||||
| 	if {$name eq {}} { | ||||
| 		return | ||||
| 	} elseif {$revcnt == 1} { | ||||
| 		set unit branch | ||||
| 	} elseif {$revcnt <= 15} { | ||||
| 		set unit branches | ||||
| 	} | ||||
|  | ||||
| 		if {[tk_dialog \ | ||||
| 		$w.confirm_octopus \ | ||||
| 		[wm title $w] \ | ||||
| 		"Use octopus merge strategy? | ||||
| 	set spec [$w_rev get_tracking_branch] | ||||
| 	set cmit [$w_rev get_commit] | ||||
|  | ||||
| You are merging $revcnt branches at once.  This requires using the octopus merge driver, which may not succeed if there are file-level conflicts. | ||||
| " \ | ||||
| 		question \ | ||||
| 		0 \ | ||||
| 		{Cancel} \ | ||||
| 		{Use octopus} \ | ||||
| 		] != 1} return | ||||
| 	set fh [open [gitdir FETCH_HEAD] w] | ||||
| 	fconfigure $fh -translation lf | ||||
| 	if {$spec eq {}} { | ||||
| 		set remote . | ||||
| 		set branch $name | ||||
| 		set stitle $branch | ||||
| 	} else { | ||||
| 		tk_messageBox \ | ||||
| 			-icon error \ | ||||
| 			-type ok \ | ||||
| 			-title [wm title $w] \ | ||||
| 			-parent $w \ | ||||
| 			-message "Too many branches selected. | ||||
|  | ||||
| You have requested to merge $revcnt branches in an octopus merge.  This exceeds Git's internal limit of 15 branches per merge. | ||||
|  | ||||
| Please select fewer branches.  To merge more than 15 branches, merge the branches in batches. | ||||
| " | ||||
| 		return | ||||
| 		set remote $remote_url([lindex $spec 1]) | ||||
| 		if {[regexp {^[^:@]*@[^:]*:/} $remote]} { | ||||
| 			regsub {^[^:@]*@} $remote {} remote | ||||
| 		} | ||||
| 		set branch [lindex $spec 2] | ||||
| 		set stitle "$branch of $remote" | ||||
| 	} | ||||
| 	regsub ^refs/heads/ $branch {} branch | ||||
| 	puts $fh "$cmit\t\tbranch '$branch' of $remote" | ||||
| 	close $fh | ||||
|  | ||||
| 	set msg "Merging $current_branch, [join $names {, }]" | ||||
| 	set cmd [list git] | ||||
| 	lappend cmd merge | ||||
| 	lappend cmd --strategy=recursive | ||||
| 	lappend cmd [git fmt-merge-msg <[gitdir FETCH_HEAD]] | ||||
| 	lappend cmd HEAD | ||||
| 	lappend cmd $cmit | ||||
|  | ||||
| 	set msg "Merging $current_branch and $stitle" | ||||
| 	ui_status "$msg..." | ||||
| 	set cons [console::new "Merge" $msg] | ||||
| 	console::exec $cons $cmd \ | ||||
| 		[namespace code [list _finish $revcnt $cons]] | ||||
| 	set cons [console::new "Merge" "merge $stitle"] | ||||
| 	console::exec $cons $cmd [cb _finish $cons] | ||||
|  | ||||
| 	wm protocol $w WM_DELETE_WINDOW {} | ||||
| 	destroy $w | ||||
| } | ||||
|  | ||||
| proc _finish {revcnt w ok} { | ||||
| 	console::done $w $ok | ||||
| method _finish {cons ok} { | ||||
| 	console::done $cons $ok | ||||
| 	if {$ok} { | ||||
| 		set msg {Merge completed successfully.} | ||||
| 	} else { | ||||
| 		if {$revcnt != 1} { | ||||
| 			info_popup "Octopus merge failed. | ||||
|  | ||||
| Your merge of $revcnt branches has failed. | ||||
|  | ||||
| There are file-level conflicts between the branches which must be resolved manually. | ||||
|  | ||||
| The working directory will now be reset. | ||||
|  | ||||
| You can attempt this merge again by merging only one branch at a time." $w | ||||
|  | ||||
| 			set fd [git_read read-tree --reset -u HEAD] | ||||
| 			fconfigure $fd -blocking 0 -translation binary | ||||
| 			fileevent $fd readable \ | ||||
| 				[namespace code [list _reset_wait $fd]] | ||||
| 			ui_status {Aborting... please wait...} | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		set msg {Merge failed.  Conflict resolution is required.} | ||||
| 	} | ||||
| 	unlock_index | ||||
| 	rescan [list ui_status $msg] | ||||
| 	delete_this | ||||
| } | ||||
|  | ||||
| proc dialog {} { | ||||
| constructor dialog {} { | ||||
| 	global current_branch | ||||
| 	global M1B | ||||
|  | ||||
| 	if {![_can_merge]} return | ||||
|  | ||||
| 	set fmt {list %(objectname) %(*objectname) %(refname) %(subject)} | ||||
| 	set fr_fd [git_read for-each-ref \ | ||||
| 		--tcl \ | ||||
| 		--format=$fmt \ | ||||
| 		refs/heads \ | ||||
| 		refs/remotes \ | ||||
| 		refs/tags \ | ||||
| 		] | ||||
| 	fconfigure $fr_fd -translation binary | ||||
| 	while {[gets $fr_fd line] > 0} { | ||||
| 		set line [eval $line] | ||||
| 		set ref [lindex $line 2] | ||||
| 		regsub ^refs/(heads|remotes|tags)/ $ref {} ref | ||||
| 		set subj($ref) [lindex $line 3] | ||||
| 		lappend sha1([lindex $line 0]) $ref | ||||
| 		if {[lindex $line 1] ne {}} { | ||||
| 			lappend sha1([lindex $line 1]) $ref | ||||
| 		} | ||||
| 	if {![_can_merge $this]} { | ||||
| 		delete_this | ||||
| 		return | ||||
| 	} | ||||
| 	close $fr_fd | ||||
|  | ||||
| 	set to_show {} | ||||
| 	set fr_fd [git_read rev-list --all --not HEAD] | ||||
| 	while {[gets $fr_fd line] > 0} { | ||||
| 		if {[catch {set ref $sha1($line)}]} continue | ||||
| 		foreach n $ref { | ||||
| 			lappend to_show [list $n $line] | ||||
| 		} | ||||
| 	make_toplevel top w | ||||
| 	wm title $top "[appname] ([reponame]): Merge" | ||||
| 	if {$top ne {.}} { | ||||
| 		wm geometry $top "+[winfo rootx .]+[winfo rooty .]" | ||||
| 	} | ||||
| 	close $fr_fd | ||||
| 	set to_show [lsort -unique $to_show] | ||||
|  | ||||
| 	set w .merge_setup | ||||
| 	toplevel $w | ||||
| 	wm geometry $w "+[winfo rootx .]+[winfo rooty .]" | ||||
|  | ||||
| 	set _visualize [namespace code [list _visualize $w $to_show]] | ||||
| 	set _start [namespace code [list _start $w $to_show]] | ||||
| 	set _start [cb _start] | ||||
|  | ||||
| 	label $w.header \ | ||||
| 		-text "Merge Into $current_branch" \ | ||||
|  | @ -211,55 +160,51 @@ proc dialog {} { | |||
| 	pack $w.header -side top -fill x | ||||
|  | ||||
| 	frame $w.buttons | ||||
| 	button $w.buttons.visualize -text Visualize -command $_visualize | ||||
| 	button $w.buttons.visualize \ | ||||
| 		-text Visualize \ | ||||
| 		-command [cb _visualize] | ||||
| 	pack $w.buttons.visualize -side left | ||||
| 	button $w.buttons.create -text Merge -command $_start | ||||
| 	pack $w.buttons.create -side right | ||||
| 	button $w.buttons.merge \ | ||||
| 		-text Merge \ | ||||
| 		-command $_start | ||||
| 	pack $w.buttons.merge -side right | ||||
| 	button $w.buttons.cancel \ | ||||
| 		-text {Cancel} \ | ||||
| 		-command "unlock_index;destroy $w" | ||||
| 		-command [cb _cancel] | ||||
| 	pack $w.buttons.cancel -side right -padx 5 | ||||
| 	pack $w.buttons -side bottom -fill x -pady 10 -padx 10 | ||||
|  | ||||
| 	labelframe $w.source -text {Source Branches} | ||||
| 	listbox $w.source.l \ | ||||
| 		-height 10 \ | ||||
| 		-width 70 \ | ||||
| 		-font font_diff \ | ||||
| 		-selectmode extended \ | ||||
| 		-yscrollcommand [list $w.source.sby set] | ||||
| 	scrollbar $w.source.sby -command [list $w.source.l yview] | ||||
| 	pack $w.source.sby -side right -fill y | ||||
| 	pack $w.source.l -side left -fill both -expand 1 | ||||
| 	pack $w.source -fill both -expand 1 -pady 5 -padx 5 | ||||
|  | ||||
| 	foreach ref $to_show { | ||||
| 		set n [lindex $ref 0] | ||||
| 		if {[string length $n] > 20} { | ||||
| 			set n "[string range $n 0 16]..." | ||||
| 		} | ||||
| 		$w.source.l insert end [format {%s %-20s %s} \ | ||||
| 			[string range [lindex $ref 1] 0 5] \ | ||||
| 			$n \ | ||||
| 			$subj([lindex $ref 0])] | ||||
| 	} | ||||
|  | ||||
| 	bind $w.source.l <Key-K> [list event generate %W <Shift-Key-Up>] | ||||
| 	bind $w.source.l <Key-J> [list event generate %W <Shift-Key-Down>] | ||||
| 	bind $w.source.l <Key-k> [list event generate %W <Key-Up>] | ||||
| 	bind $w.source.l <Key-j> [list event generate %W <Key-Down>] | ||||
| 	bind $w.source.l <Key-h> [list event generate %W <Key-Left>] | ||||
| 	bind $w.source.l <Key-l> [list event generate %W <Key-Right>] | ||||
| 	bind $w.source.l <Key-v> $_visualize | ||||
| 	set w_rev [::choose_rev::new_unmerged $w.rev {Revision To Merge}] | ||||
| 	pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 | ||||
|  | ||||
| 	bind $w <$M1B-Key-Return> $_start | ||||
| 	bind $w <Visibility> "grab $w; focus $w.source.l" | ||||
| 	bind $w <Key-Escape> "unlock_index;destroy $w" | ||||
| 	wm protocol $w WM_DELETE_WINDOW "unlock_index;destroy $w" | ||||
| 	wm title $w "[appname] ([reponame]): Merge" | ||||
| 	bind $w <Key-Return> $_start | ||||
| 	bind $w <Key-Escape> [cb _cancel] | ||||
| 	wm protocol $w WM_DELETE_WINDOW [cb _cancel] | ||||
|  | ||||
| 	bind $w.buttons.merge <Visibility> [cb _visible] | ||||
| 	tkwait window $w | ||||
| } | ||||
|  | ||||
| method _visible {} { | ||||
| 	grab $w | ||||
| 	if {[is_config_true gui.matchtrackingbranch]} { | ||||
| 		$w_rev pick_tracking_branch | ||||
| 	} | ||||
| 	$w_rev focus_filter | ||||
| } | ||||
|  | ||||
| method _cancel {} { | ||||
| 	wm protocol $w WM_DELETE_WINDOW {} | ||||
| 	unlock_index | ||||
| 	destroy $w | ||||
| 	delete_this | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| namespace eval merge { | ||||
|  | ||||
| proc reset_hard {} { | ||||
| 	global HEAD commit_type file_states | ||||
|  | ||||
|  | @ -274,20 +219,24 @@ You must finish amending this commit. | |||
| 	if {![lock_index abort]} return | ||||
|  | ||||
| 	if {[string match *merge* $commit_type]} { | ||||
| 		set op merge | ||||
| 		set op_question "Abort merge? | ||||
|  | ||||
| Aborting the current merge will cause *ALL* uncommitted changes to be lost. | ||||
|  | ||||
| Continue with aborting the current merge?" | ||||
| 	} else { | ||||
| 		set op commit | ||||
| 		set op_question "Reset changes? | ||||
|  | ||||
| Resetting the changes will cause *ALL* uncommitted changes to be lost. | ||||
|  | ||||
| Continue with resetting the current changes?" | ||||
| 	} | ||||
|  | ||||
| 	if {[ask_popup "Abort $op? | ||||
|  | ||||
| Aborting the current $op will cause *ALL* uncommitted changes to be lost. | ||||
|  | ||||
| Continue with aborting the current $op?"] eq {yes}} { | ||||
| 		set fd [git_read read-tree --reset -u HEAD] | ||||
| 	if {[ask_popup $op_question] eq {yes}} { | ||||
| 		set fd [git_read --stderr read-tree --reset -u -v HEAD] | ||||
| 		fconfigure $fd -blocking 0 -translation binary | ||||
| 		fileevent $fd readable [namespace code [list _reset_wait $fd]] | ||||
| 		ui_status {Aborting... please wait...} | ||||
| 		$::main_status start {Aborting} {files reset} | ||||
| 	} else { | ||||
| 		unlock_index | ||||
| 	} | ||||
|  | @ -296,9 +245,12 @@ Continue with aborting the current $op?"] eq {yes}} { | |||
| proc _reset_wait {fd} { | ||||
| 	global ui_comm | ||||
|  | ||||
| 	read $fd | ||||
| 	$::main_status update_meter [read $fd] | ||||
|  | ||||
| 	fconfigure $fd -blocking 1 | ||||
| 	if {[eof $fd]} { | ||||
| 		close $fd | ||||
| 		set fail [catch {close $fd} err] | ||||
| 		$::main_status stop | ||||
| 		unlock_index | ||||
|  | ||||
| 		$ui_comm delete 0.0 end | ||||
|  | @ -310,7 +262,12 @@ proc _reset_wait {fd} { | |||
| 		catch {file delete [gitdir MERGE_MSG]} | ||||
| 		catch {file delete [gitdir GITGUI_MSG]} | ||||
|  | ||||
| 		if {$fail} { | ||||
| 			warn_popup "Abort failed.\n\n$err" | ||||
| 		} | ||||
| 		rescan {ui_status {Abort completed.  Ready.}} | ||||
| 	} else { | ||||
| 		fconfigure $fd -blocking 0 | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  |  | |||
|  | @ -57,6 +57,7 @@ proc all_tracking_branches {} { | |||
| proc load_all_remotes {} { | ||||
| 	global repo_config | ||||
| 	global all_remotes tracking_branches some_heads_tracking | ||||
| 	global remote_url | ||||
|  | ||||
| 	set some_heads_tracking 0 | ||||
| 	set all_remotes [list] | ||||
|  | @ -76,6 +77,10 @@ proc load_all_remotes {} { | |||
| 			catch { | ||||
| 				set fd [open [file join $rm_dir $name] r] | ||||
| 				while {[gets $fd line] >= 0} { | ||||
| 					if {[regexp {^URL:[ 	]*(.+)$} $line line url]} { | ||||
| 						set remote_url($name) $url | ||||
| 						continue | ||||
| 					} | ||||
| 					if {![regexp {^Pull:[ 	]*([^:]+):(.+)$} \ | ||||
| 						$line line src dst]} continue | ||||
| 					if {[string index $src 0] eq {+}} { | ||||
|  | @ -100,6 +105,7 @@ proc load_all_remotes {} { | |||
| 	foreach line [array names repo_config remote.*.url] { | ||||
| 		if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue | ||||
| 		lappend all_remotes $name | ||||
| 		set remote_url($name) $repo_config(remote.$name.url) | ||||
|  | ||||
| 		if {[catch {set fl $repo_config(remote.$name.fetch)}]} { | ||||
| 			set fl {} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano