Commits

Anonymous committed 4578c5c

git-gui: Automatically backup the user's commit buffer

A few users have been seeing crashes in Tk when using the undo key
binding to undo the last few keystroke events in the commit buffer.
Unfortunately that means the user loses their commit message and
must start over from scratch when the user restarts the process.

git-gui now saves the user's commit message buffer every couple of
seconds to a temporary file under .git (specifically .git/GITGUI_BCK).
At exit time we rename this file to .git/GITGUI_MSG if there is a
message, the file exists, and it is currently synchronized with the
Tk buffer. Otherwise we do our usual routine of saving the Tk buffer
to .git/GITGUI_MSG and delete .git/GITGUI_BCK, if it exists.

During startup we favor .git/GITGUI_BCK over .git/GITGUI_MSG. This
way a crash doesn't take out the user's message buffer but instead
will cause the user to lose only a few keystrokes. Most people do
not type more than 200 WPM, and with 30 possible saves per minute
we are unlikely to lose more than 7 words.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

Comments (0)

Files changed (2)

 
 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
 		# -- 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.
 	populate_push_menu
 }
 
+if {[winfo exists $ui_comm]} {
+	set GITGUI_BCK_exists [load_message GITGUI_BCK]
+
+	# -- 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
+	}
+
+	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 .
 	$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