2007-05-08 05:35:48 +02:00
# git-gui misc. commit reading/writing support
# Copyright (C) 2006, 2007 Shawn Pearce
proc load_last_commit { } {
global HEAD PARENT MERGE_HEAD commit_type ui_comm
global repo_config
if { [ llength $PARENT ] == 0 } {
error_popup { There is nothing to amend.
You are about to create the initial commit. There is no commit before this to amend.
}
return
}
repository_state curType curHEAD curMERGE_HEAD
if { $curType eq { merge } } {
error_popup { Cannot amend while merging.
You are currently in the middle of a merge that has not been fully completed. You cannot amend the prior commit unless you first abort the current merge activity.
}
return
}
set msg { }
set parents [ list ]
if { [ catch {
set fd [ open " | g i t c a t - f i l e c o m m i t $ c u r H E A D " r]
fconfigure $fd - encoding binary - translation lf
if { [ catch { set enc $repo_config ( i18n.commitencoding ) } ] } {
set enc utf-8
}
while { [ gets $fd line] > 0 } {
if { [ string match { parent * } $line ] } {
lappend parents [ string range $line 7 end]
} elseif { [ string match { encoding * } $line ] } {
set enc [ string tolower [ string range $line 9 end] ]
}
}
set msg [ encoding convertfrom $enc [ read $fd ] ]
set msg [ string trim $msg ]
close $fd
} err ] } {
error_popup " E r r o r l o a d i n g c o m m i t d a t a f o r a m e n d : \n \n $ e r r "
return
}
set HEAD $curHEAD
set PARENT $parents
set MERGE_HEAD [ list ]
switch - - [ llength $parents ] {
0 { set commit_type amend-initial}
1 { set commit_type amend}
default { set commit_type amend-merge}
}
$ui_comm delete 0.0 end
$ui_comm insert end $msg
$ui_comm edit reset
$ui_comm edit modified false
rescan { set ui_status_value { Ready. } }
}
set GIT_COMMITTER_IDENT { }
proc committer_ident { } {
global GIT_COMMITTER_IDENT
if { $GIT_COMMITTER_IDENT eq { } } {
if { [ catch { set me [ git var GIT_COMMITTER_IDENT] } err] } {
error_popup " U n a b l e t o o b t a i n y o u r i d e n t i t y : \n \n $ e r r "
return { }
}
if { ! [ regexp { ^ ( . * ) [ 0-9 ] + [ - + 0 - 9 ] + $ } \
$me me GIT_COMMITTER_IDENT] } {
error_popup " I n v a l i d G I T _ C O M M I T T E R _ I D E N T : \n \n $ m e "
return { }
}
}
return $GIT_COMMITTER_IDENT
}
proc do_signoff { } {
global ui_comm
set me [ committer_ident ]
if { $me eq { } } return
set sob " S i g n e d - o f f - b y : $ m e "
set last [ $ui_comm get { end - 1 c linestart} { end - 1 c} ]
if { $last ne $sob } {
$ui_comm edit separator
if { $last ne { }
&& ! [ regexp { ^ [ A-Z ] [ A-Za-z ] * - [ A-Za-z- ] + : * } $last ] } {
$ui_comm insert end " \n "
}
$ui_comm insert end " \n $ s o b "
$ui_comm edit separator
$ui_comm see end
}
}
proc create_new_commit { } {
global commit_type ui_comm
set commit_type normal
$ui_comm delete 0.0 end
$ui_comm edit reset
$ui_comm edit modified false
rescan { set ui_status_value { Ready. } }
}
proc commit_tree { } {
global HEAD commit_type file_states ui_comm repo_config
global ui_status_value pch_error
if { [ committer_ident ] eq { } } return
if { ! [ lock_index update] } return
# -- Our in memory state should match the repository.
#
repository_state curType curHEAD curMERGE_HEAD
if { [ string match amend* $commit_type ]
&& $curType eq { normal }
&& $curHEAD eq $HEAD } {
} elseif { $commit_type ne $curType || $HEAD ne $curHEAD } {
info_popup { Last scanned state does not match repository state.
Another Git program has modified this repository since the last scan. A rescan must be performed before another commit can be created.
The rescan will be automatically started now.
}
unlock_index
rescan { set ui_status_value { Ready. } }
return
}
# -- At least one file should differ in the index.
#
set files_ready 0
foreach path [ array names file_states] {
switch - glob - - [ lindex $file_states ( $path ) 0 ] {
_ ? { continue }
A ? -
D ? -
M ? { set files_ready 1 }
U ? {
error_popup " U n m e r g e d f i l e s c a n n o t b e c o m m i t t e d .
File [ short_path $path ] has merge conflicts. You must resolve them and add the file before committing.
"
unlock_index
return
}
default {
error_popup " U n k n o w n f i l e s t a t e [ l i n d e x $ s 0 ] d e t e c t e d .
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.
}
unlock_index
return
}
# -- A message is required.
#
set msg [ string trim [ $ui_comm get 1.0 end] ]
regsub - all - line { [ \ t \ r] + $ } $msg { } msg
if { $msg eq { } } {
error_popup { Please supply a commit message.
A good commit message has the following format:
- First line: Describe in one sentance what you did.
- Second line: Blank
- Remaining lines: Describe why this change is good.
}
unlock_index
return
}
# -- Run the pre-commit hook.
#
set pchook [ gitdir hooks pre-commit]
# On Cygwin [file executable] might lie so we need to ask
# the shell if the hook is executable. Yes that's annoying.
#
if { [ is_Cygwin ] && [ file isfile $pchook ] } {
set pchook [ list sh - c [ concat \
" i f t e s t - x \" $ p c h o o k \" ; " \
" t h e n e x e c \" $ p c h o o k \" 2 > & 1 ; " \
" f i " ] ]
} elseif { [ file executable $pchook ] } {
set pchook [ list $pchook | & cat]
} else {
commit_writetree $curHEAD $msg
return
}
set ui_status_value { Calling pre-commit hook...}
set pch_error { }
set fd_ph [ open " | $ p c h o o k " r]
fconfigure $fd_ph - blocking 0 - translation binary
fileevent $fd_ph readable \
[ list commit_prehook_wait $fd_ph $curHEAD $msg ]
}
proc commit_prehook_wait { fd_ph curHEAD msg} {
global pch_error ui_status_value
append pch_error [ read $fd_ph ]
fconfigure $fd_ph - blocking 1
if { [ eof $fd_ph ] } {
if { [ catch { close $fd_ph } ] } {
set ui_status_value { Commit declined by pre-commit hook.}
hook_failed_popup pre-commit $pch_error
unlock_index
} else {
commit_writetree $curHEAD $msg
}
set pch_error { }
return
}
fconfigure $fd_ph - blocking 0
}
proc commit_writetree { curHEAD msg} {
global ui_status_value
set ui_status_value { Committing changes...}
set fd_wt [ open " | g i t w r i t e - t r e e " r]
fileevent $fd_wt readable \
[ list commit_committree $fd_wt $curHEAD $msg ]
}
proc commit_committree { fd_wt curHEAD msg} {
global HEAD PARENT MERGE_HEAD commit_type
global all_heads current_branch
global ui_status_value ui_comm selected_commit_type
global file_states selected_paths rescan_active
global repo_config
gets $fd_wt tree_id
if { $tree_id eq { } || [ catch { close $fd_wt } err] } {
error_popup " w r i t e - t r e e f a i l e d : \n \n $ e r r "
set ui_status_value { Commit failed.}
unlock_index
return
}
# -- Verify this wasn't an empty change.
#
if { $commit_type eq { normal } } {
set old_tree [ git rev-parse " $ P A R E N T ^ { t r e e } " ]
if { $tree_id eq $old_tree } {
info_popup { No changes to commit.
No files were modified by this commit and it was not a merge commit.
A rescan will be automatically started now.
}
unlock_index
rescan { set ui_status_value { No changes to commit.} }
return
}
}
# -- Build the message.
#
set msg_p [ gitdir COMMIT_EDITMSG]
set msg_wt [ open $msg_p w]
if { [ catch { set enc $repo_config ( i18n.commitencoding ) } ] } {
set enc utf-8
}
fconfigure $msg_wt - encoding binary - translation binary
puts - nonewline $msg_wt [ encoding convertto $enc $msg ]
close $msg_wt
# -- Create the commit.
#
set cmd [ list commit-tree $tree_id ]
foreach p [ concat $PARENT $MERGE_HEAD ] {
lappend cmd - p $p
}
lappend cmd < $msg_p
if { [ catch { set cmt_id [ eval git $cmd ] } err] } {
error_popup " c o m m i t - t r e e f a i l e d : \n \n $ e r r "
set ui_status_value { Commit failed.}
unlock_index
return
}
# -- Update the HEAD ref.
#
set reflogm commit
if { $commit_type ne { normal } } {
append reflogm " ( $ c o m m i t _ t y p e ) "
}
set i [ string first " \n " $msg ]
if { $i >= 0 } {
set subject [ string range $msg 0 [ expr { $i - 1 } ] ]
} else {
set subject $msg
}
append reflogm { : } $subject
if { [ catch {
git update-ref - m $reflogm HEAD $cmt_id $curHEAD
} err ] } {
error_popup " u p d a t e - r e f f a i l e d : \n \n $ e r r "
set ui_status_value { Commit failed.}
unlock_index
return
}
# -- Cleanup after ourselves.
#
catch { file delete $msg_p }
catch { file delete [ gitdir MERGE_HEAD] }
catch { file delete [ gitdir MERGE_MSG] }
catch { file delete [ gitdir SQUASH_MSG] }
catch { file delete [ gitdir GITGUI_MSG] }
# -- Let rerere do its thing.
#
2007-07-08 23:41:24 +02:00
if { [ get_config rerere.enabled] eq { } } {
set rerere [ file isdirectory [ gitdir rr-cache] ]
} else {
set rerere [ is_config_true rerere.enabled]
}
if { $rerere } {
2007-05-08 05:35:48 +02:00
catch { git rerere}
}
# -- Run the post-commit hook.
#
set pchook [ gitdir hooks post-commit]
if { [ is_Cygwin ] && [ file isfile $pchook ] } {
set pchook [ list sh - c [ concat \
" i f t e s t - x \" $ p c h o o k \" ; " \
" t h e n e x e c \" $ p c h o o k \" ; " \
" f i " ] ]
} elseif { ! [ file executable $pchook ] } {
set pchook { }
}
if { $pchook ne { } } {
catch { exec $pchook & }
}
$ui_comm delete 0.0 end
$ui_comm edit reset
$ui_comm edit modified false
if { [ is_enabled singlecommit] } do_quit
# -- Make sure our current branch exists.
#
if { $commit_type eq { initial } } {
lappend all_heads $current_branch
set all_heads [ lsort - unique $all_heads ]
populate_branch_menu
}
# -- Update in memory status
#
set selected_commit_type new
set commit_type normal
set HEAD $cmt_id
set PARENT $cmt_id
set MERGE_HEAD [ list ]
foreach path [ array names file_states] {
set s $file_states ( $path )
set m [ lindex $s 0 ]
switch - glob - - $m {
_O -
_M -
_D { continue }
__ -
A_ -
M_ -
D_ {
unset file_states( $path )
catch { unset selected_paths( $path ) }
}
DO {
set file_states( $path ) [ list _O [ lindex $s 1 ] { } { } ]
}
AM -
AD -
MM -
MD {
set file_states( $path ) [ list \
_ [ string index $m 1 ] \
[ lindex $s 1 ] \
[ lindex $s 3 ] \
{ } ]
}
}
}
display_all_files
unlock_index
reshow_diff
set ui_status_value \
" C r e a t e d c o m m i t [ s t r i n g r a n g e $ c m t _ i d 0 7 ] : $ s u b j e c t "
}