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 { } {
2016-10-06 15:04:42 +02:00
global HEAD PARENT MERGE_HEAD commit_type ui_comm commit_author
2007-05-08 05:35:48 +02:00
global repo_config
if { [ llength $PARENT ] == 0 } {
2007-07-21 14:21:34 +02:00
error_popup [ mc " T h e r e i s n o t h i n g t o a m e n d .
2007-05-08 05:35:48 +02:00
You are about to create the initial commit. There is no commit before this to amend.
2007-07-21 14:21:34 +02:00
" ]
2007-05-08 05:35:48 +02:00
return
}
repository_state curType curHEAD curMERGE_HEAD
if { $curType eq { merge } } {
2007-07-21 14:21:34 +02:00
error_popup [ mc " C a n n o t a m e n d w h i l e m e r g i n g .
2007-05-08 05:35:48 +02:00
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.
2007-07-21 14:21:34 +02:00
" ]
2007-05-08 05:35:48 +02:00
return
}
set msg { }
set parents [ list ]
if { [ catch {
2007-07-09 07:17:09 +02:00
set fd [ git_read cat-file commit $curHEAD ]
2007-05-08 05:35:48 +02:00
fconfigure $fd - encoding binary - translation lf
2008-12-06 18:24:35 +01:00
# By default commits are assumed to be in utf-8
set enc utf-8
2007-05-08 05:35:48 +02:00
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] ]
2016-04-11 08:57:39 +02:00
} elseif { [ regexp " a u t h o r ( . * ) \\ s < ( . * ) > \\ s ( \\ d . * $ ) " $line all name email time] } {
2016-10-06 15:04:42 +02:00
set commit_author [ list name $name email $email date $time ]
2007-05-08 05:35:48 +02:00
}
}
2007-07-19 07:13:29 +02:00
set msg [ read $fd ]
2007-05-08 05:35:48 +02:00
close $fd
2007-07-19 07:13:29 +02:00
set enc [ tcl_encoding $enc ]
if { $enc ne { } } {
set msg [ encoding convertfrom $enc $msg ]
}
set msg [ string trim $msg ]
2007-05-08 05:35:48 +02:00
} err ] } {
2007-09-14 07:50:09 +02:00
error_popup [ strcat [ mc " 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 " ]
2007-05-08 05:35:48 +02:00
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
2007-07-06 05:16:13 +02:00
rescan ui_ready
2007-05-08 05:35:48 +02:00
}
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] } {
2007-09-14 07:50:09 +02:00
error_popup [ strcat [ mc " 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 " ]
2007-05-08 05:35:48 +02:00
return { }
}
if { ! [ regexp { ^ ( . * ) [ 0-9 ] + [ - + 0 - 9 ] + $ } \
$me me GIT_COMMITTER_IDENT] } {
2007-09-14 07:50:09 +02:00
error_popup [ strcat [ mc " 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 " ]
2007-05-08 05:35:48 +02:00
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 { } {
2016-10-06 15:04:42 +02:00
global commit_type ui_comm commit_author
2007-05-08 05:35:48 +02:00
set commit_type normal
2016-10-06 15:04:42 +02:00
unset - nocomplain commit_author
2007-05-08 05:35:48 +02:00
$ui_comm delete 0.0 end
$ui_comm edit reset
$ui_comm edit modified false
2007-07-06 05:16:13 +02:00
rescan ui_ready
2007-05-08 05:35:48 +02:00
}
2009-01-23 22:18:13 +01:00
proc setup_commit_encoding { msg_wt { quiet 0 } } {
global repo_config
if { [ catch { set enc $repo_config ( i18n.commitencoding ) } ] } {
set enc utf-8
}
set use_enc [ tcl_encoding $enc ]
if { $use_enc ne { } } {
fconfigure $msg_wt - encoding $use_enc
} else {
if { ! $quiet } {
error_popup [ mc " w a r n i n g : T c l d o e s n o t s u p p o r t e n c o d i n g ' % s ' . " $enc ]
}
fconfigure $msg_wt - encoding utf-8
}
}
2007-05-08 05:35:48 +02:00
proc commit_tree { } {
global HEAD commit_type file_states ui_comm repo_config
2007-07-06 05:16:13 +02:00
global pch_error
2007-05-08 05:35:48 +02:00
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 } {
2007-07-21 14:21:34 +02:00
info_popup [ mc " L a s t s c a n n e d s t a t e d o e s n o t m a t c h r e p o s i t o r y s t a t e .
2007-05-08 05:35:48 +02:00
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.
2007-07-21 14:21:34 +02:00
" ]
2007-05-08 05:35:48 +02:00
unlock_index
2007-07-06 05:16:13 +02:00
rescan ui_ready
2007-05-08 05:35:48 +02:00
return
}
# -- At least one file should differ in the index.
#
set files_ready 0
foreach path [ array names file_states] {
2010-12-09 21:46:22 +01:00
set s $file_states ( $path )
switch - glob - - [ lindex $s 0 ] {
2007-05-08 05:35:48 +02:00
_ ? { continue }
A ? -
D ? -
2010-12-09 21:46:23 +01:00
T ? -
2007-05-08 05:35:48 +02:00
M ? { set files_ready 1 }
2008-08-30 23:00:49 +02:00
_U -
2007-05-08 05:35:48 +02:00
U ? {
2007-07-21 14:21:34 +02:00
error_popup [ mc " 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 .
2007-05-08 05:35:48 +02:00
2007-07-21 14:21:34 +02:00
File % s has merge conflicts. You must resolve them and stage the file before committing.
" [ s h o r t _ p a t h $ p a t h ] ]
2007-05-08 05:35:48 +02:00
unlock_index
return
}
default {
2007-07-21 14:21:34 +02:00
error_popup [ mc " U n k n o w n f i l e s t a t e % s d e t e c t e d .
2007-05-08 05:35:48 +02:00
2007-07-21 14:21:34 +02:00
File % s cannot be committed by this program.
" [ l i n d e x $ s 0 ] [ s h o r t _ p a t h $ p a t h ] ]
2007-05-08 05:35:48 +02:00
}
}
}
2008-09-12 20:43:49 +02:00
if { ! $files_ready && ! [ string match * merge $curType ] && ! [ is_enabled nocommit] } {
2007-07-21 14:21:34 +02:00
info_popup [ mc " N o c h a n g e s t o c o m m i t .
2007-05-08 05:35:48 +02:00
2007-07-28 22:17:10 +02:00
You must stage at least 1 file before you can commit.
2007-07-21 14:21:34 +02:00
" ]
2007-05-08 05:35:48 +02:00
unlock_index
return
}
2008-09-12 20:43:49 +02:00
if { [ is_enabled nocommitmsg] } { do_quit 0 }
2007-05-08 05:35:48 +02:00
# -- A message is required.
#
set msg [ string trim [ $ui_comm get 1.0 end] ]
regsub - all - line { [ \ t \ r] + $ } $msg { } msg
if { $msg eq { } } {
2007-07-21 14:21:34 +02:00
error_popup [ mc " P l e a s e s u p p l y a c o m m i t m e s s a g e .
2007-05-08 05:35:48 +02:00
A good commit message has the following format:
2007-11-22 16:20:08 +01:00
- First line: Describe in one sentence what you did.
2007-05-08 05:35:48 +02:00
- Second line: Blank
- Remaining lines: Describe why this change is good.
2007-07-21 14:21:34 +02:00
" ]
2007-05-08 05:35:48 +02:00
unlock_index
return
}
2008-01-20 20:11:52 +01:00
# -- Build the message file.
#
set msg_p [ gitdir GITGUI_EDITMSG]
set msg_wt [ open $msg_p w]
fconfigure $msg_wt - translation lf
2009-01-23 22:18:13 +01:00
setup_commit_encoding $msg_wt
2008-01-20 20:11:52 +01:00
puts $msg_wt $msg
close $msg_wt
2008-09-12 20:43:49 +02:00
if { [ is_enabled nocommit] } { do_quit 0 }
2007-05-08 05:35:48 +02:00
# -- Run the pre-commit hook.
#
2008-01-20 20:46:59 +01:00
set fd_ph [ githook_read pre-commit]
if { $fd_ph eq { } } {
2008-01-20 20:11:52 +01:00
commit_commitmsg $curHEAD $msg_p
2007-05-08 05:35:48 +02:00
return
}
2008-02-02 10:20:17 +01:00
ui_status [ mc " C a l l i n g p r e - c o m m i t h o o k . . . " ]
2007-05-08 05:35:48 +02:00
set pch_error { }
2007-07-17 07:50:10 +02:00
fconfigure $fd_ph - blocking 0 - translation binary - eofchar { }
2007-05-08 05:35:48 +02:00
fileevent $fd_ph readable \
2008-01-20 20:11:52 +01:00
[ list commit_prehook_wait $fd_ph $curHEAD $msg_p ]
2007-05-08 05:35:48 +02:00
}
2008-01-20 20:11:52 +01:00
proc commit_prehook_wait { fd_ph curHEAD msg_p} {
2007-07-06 05:16:13 +02:00
global pch_error
2007-05-08 05:35:48 +02:00
append pch_error [ read $fd_ph ]
fconfigure $fd_ph - blocking 1
if { [ eof $fd_ph ] } {
if { [ catch { close $fd_ph } ] } {
2008-01-20 20:11:52 +01:00
catch { file delete $msg_p }
2008-02-02 10:20:17 +01:00
ui_status [ mc " C o m m i t d e c l i n e d b y p r e - c o m m i t h o o k . " ]
2007-05-08 05:35:48 +02:00
hook_failed_popup pre-commit $pch_error
unlock_index
} else {
2008-01-20 20:11:52 +01:00
commit_commitmsg $curHEAD $msg_p
2007-05-08 05:35:48 +02:00
}
set pch_error { }
return
}
fconfigure $fd_ph - blocking 0
}
2008-01-20 20:11:52 +01:00
proc commit_commitmsg { curHEAD msg_p} {
2011-02-15 20:43:54 +01:00
global is_detached repo_config
2008-01-20 20:11:52 +01:00
global pch_error
2011-10-22 21:39:40 +02:00
if { $is_detached
&& ! [ file exists [ gitdir rebase-merge head-name] ]
&& [ is_config_true gui.warndetachedcommit] } {
2011-02-15 20:43:54 +01:00
set msg [ mc " Y o u a r e a b o u t t o c o m m i t o n a d e t a c h e d h e a d . \
This is a potentially dangerous thing to do because if you switch\
2012-08-18 22:24:00 +02:00
to another branch you will lose your changes and it can be difficult\
2011-02-15 20:43:54 +01:00
to retrieve them later from the reflog. You should probably cancel this\
commit and create a new branch to continue.\ n\
\ n \
Do you really want to proceed with your Commit? " ]
if { [ ask_popup $msg ] ne yes} {
unlock_index
return
}
}
2008-01-20 20:11:52 +01:00
# -- Run the commit-msg hook.
#
2008-01-20 20:46:59 +01:00
set fd_ph [ githook_read commit-msg $msg_p ]
if { $fd_ph eq { } } {
2008-01-20 20:11:52 +01:00
commit_writetree $curHEAD $msg_p
return
}
2008-02-02 10:20:17 +01:00
ui_status [ mc " C a l l i n g c o m m i t - m s g h o o k . . . " ]
2008-01-20 20:11:52 +01:00
set pch_error { }
fconfigure $fd_ph - blocking 0 - translation binary - eofchar { }
fileevent $fd_ph readable \
[ list commit_commitmsg_wait $fd_ph $curHEAD $msg_p ]
}
proc commit_commitmsg_wait { fd_ph curHEAD msg_p} {
global pch_error
append pch_error [ read $fd_ph ]
fconfigure $fd_ph - blocking 1
if { [ eof $fd_ph ] } {
if { [ catch { close $fd_ph } ] } {
catch { file delete $msg_p }
2008-02-02 10:20:17 +01:00
ui_status [ mc " C o m m i t d e c l i n e d b y c o m m i t - m s g h o o k . " ]
2008-01-20 20:11:52 +01:00
hook_failed_popup commit-msg $pch_error
unlock_index
} else {
commit_writetree $curHEAD $msg_p
}
set pch_error { }
return
}
fconfigure $fd_ph - blocking 0
}
proc commit_writetree { curHEAD msg_p} {
2008-02-02 10:20:17 +01:00
ui_status [ mc " C o m m i t t i n g c h a n g e s . . . " ]
2007-07-09 07:17:09 +02:00
set fd_wt [ git_read write-tree]
2007-05-08 05:35:48 +02:00
fileevent $fd_wt readable \
2008-01-20 20:11:52 +01:00
[ list commit_committree $fd_wt $curHEAD $msg_p ]
2007-05-08 05:35:48 +02:00
}
2008-01-20 20:11:52 +01:00
proc commit_committree { fd_wt curHEAD msg_p} {
2016-10-06 15:04:42 +02:00
global HEAD PARENT MERGE_HEAD commit_type commit_author
2007-07-06 05:16:13 +02:00
global current_branch
global ui_comm selected_commit_type
2007-05-08 05:35:48 +02:00
global file_states selected_paths rescan_active
global repo_config
2016-10-06 15:04:42 +02:00
global env
2007-05-08 05:35:48 +02:00
gets $fd_wt tree_id
2007-10-20 07:42:01 +02:00
if { [ catch { close $fd_wt } err] } {
2008-01-20 20:11:52 +01:00
catch { file delete $msg_p }
2007-09-14 07:50:09 +02:00
error_popup [ strcat [ mc " w r i t e - t r e e f a i l e d : " ] " \n \n $ e r r " ]
2008-02-02 10:20:17 +01:00
ui_status [ mc " C o m m i t f a i l e d . " ]
2007-05-08 05:35:48 +02:00
unlock_index
return
}
# -- Verify this wasn't an empty change.
#
if { $commit_type eq { normal } } {
2007-07-12 08:45:23 +02:00
set fd_ot [ git_read cat-file commit $PARENT ]
2007-07-12 08:31:28 +02:00
fconfigure $fd_ot - encoding binary - translation lf
set old_tree [ gets $fd_ot ]
close $fd_ot
if { [ string equal - length 5 { tree } $old_tree ]
&& [ string length $old_tree ] == 45 } {
set old_tree [ string range $old_tree 5 end]
} else {
2007-09-13 15:19:05 +02:00
error [ mc " C o m m i t % s a p p e a r s t o b e c o r r u p t " $PARENT ]
2007-07-12 08:31:28 +02:00
}
2007-05-08 05:35:48 +02:00
if { $tree_id eq $old_tree } {
2008-01-20 20:11:52 +01:00
catch { file delete $msg_p }
2007-07-21 14:21:34 +02:00
info_popup [ mc " N o c h a n g e s t o c o m m i t .
2007-05-08 05:35:48 +02:00
No files were modified by this commit and it was not a merge commit.
A rescan will be automatically started now.
2007-07-21 14:21:34 +02:00
" ]
2007-05-08 05:35:48 +02:00
unlock_index
2007-07-21 14:21:34 +02:00
rescan { ui_status [ mc " N o c h a n g e s t o c o m m i t . " ] }
2007-05-08 05:35:48 +02:00
return
}
}
2016-10-06 15:04:42 +02:00
if { [ info exists commit_author] } {
set old_author [ commit_author_ident $commit_author ]
2016-04-11 08:57:39 +02:00
}
2007-05-08 05:35:48 +02:00
# -- Create the commit.
#
set cmd [ list commit-tree $tree_id ]
2016-09-09 14:28:24 +02:00
if { [ is_config_true commit.gpgsign] } {
lappend cmd - S
}
2007-05-08 05:35:48 +02:00
foreach p [ concat $PARENT $MERGE_HEAD ] {
lappend cmd - p $p
}
lappend cmd < $msg_p
if { [ catch { set cmt_id [ eval git $cmd ] } err] } {
2008-01-20 20:11:52 +01:00
catch { file delete $msg_p }
2007-09-14 07:50:09 +02:00
error_popup [ strcat [ mc " c o m m i t - t r e e f a i l e d : " ] " \n \n $ e r r " ]
2008-02-02 10:20:17 +01:00
ui_status [ mc " C o m m i t f a i l e d . " ]
2007-05-08 05:35:48 +02:00
unlock_index
2016-10-06 15:04:42 +02:00
unset - nocomplain commit_author
commit_author_reset $old_author
2007-05-08 05:35:48 +02:00
return
}
2016-10-06 15:04:42 +02:00
if { [ info exists commit_author] } {
unset - nocomplain commit_author
commit_author_reset $old_author
}
2007-05-08 05:35:48 +02:00
# -- Update the HEAD ref.
#
set reflogm commit
if { $commit_type ne { normal } } {
append reflogm " ( $ c o m m i t _ t y p e ) "
}
2008-01-20 20:11:52 +01:00
set msg_fd [ open $msg_p r]
2009-01-23 22:18:13 +01:00
setup_commit_encoding $msg_fd 1
2008-01-20 20:11:52 +01:00
gets $msg_fd subject
close $msg_fd
2007-05-08 05:35:48 +02:00
append reflogm { : } $subject
if { [ catch {
git update-ref - m $reflogm HEAD $cmt_id $curHEAD
} err ] } {
2008-01-20 20:11:52 +01:00
catch { file delete $msg_p }
2007-09-14 07:50:09 +02:00
error_popup [ strcat [ mc " u p d a t e - r e f f a i l e d : " ] " \n \n $ e r r " ]
2008-02-02 10:20:17 +01:00
ui_status [ mc " C o m m i t f a i l e d . " ]
2007-05-08 05:35:48 +02:00
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] }
2012-08-18 22:28:00 +02:00
catch { file delete [ gitdir CHERRY_PICK_HEAD] }
2007-05-08 05:35:48 +02:00
# -- 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.
#
2008-01-20 20:46:59 +01:00
set fd_ph [ githook_read post-commit]
if { $fd_ph ne { } } {
2009-03-30 20:35:57 +02:00
global pch_error
set pch_error { }
2008-01-20 20:46:59 +01:00
fconfigure $fd_ph - blocking 0 - translation binary - eofchar { }
fileevent $fd_ph readable \
[ list commit_postcommit_wait $fd_ph $cmt_id ]
2007-05-08 05:35:48 +02:00
}
$ui_comm delete 0.0 end
$ui_comm edit reset
$ui_comm edit modified false
2007-07-21 10:57:57 +02:00
if { $::GITGUI_BCK_exists } {
catch { file delete [ gitdir GITGUI_BCK] }
2007-07-23 06:20:04 +02:00
set : : GITGUI_BCK_exists 0
2007-07-21 10:57:57 +02:00
}
2007-05-08 05:35:48 +02:00
2008-09-12 20:43:49 +02:00
if { [ is_enabled singlecommit] } { do_quit 0 }
2007-05-08 05:35:48 +02:00
# -- 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_ -
2008-08-22 22:10:27 +02:00
T_ -
2007-05-08 05:35:48 +02:00
D_ {
unset file_states( $path )
catch { unset selected_paths( $path ) }
}
DO {
set file_states( $path ) [ list _O [ lindex $s 1 ] { } { } ]
}
AM -
AD -
2010-12-09 21:46:23 +01:00
AT -
TM -
TD -
2007-05-08 05:35:48 +02:00
MM -
2010-12-09 21:46:23 +01:00
MT -
2007-05-08 05:35:48 +02:00
MD {
set file_states( $path ) [ list \
_ [ string index $m 1 ] \
[ lindex $s 1 ] \
[ lindex $s 3 ] \
{ } ]
}
}
}
display_all_files
unlock_index
reshow_diff
2007-07-21 14:21:34 +02:00
ui_status [ mc " C r e a t e d c o m m i t % s : % s " [ string range $cmt_id 0 7 ] $subject ]
2007-05-08 05:35:48 +02:00
}
2008-01-20 20:46:59 +01:00
proc commit_postcommit_wait { fd_ph cmt_id} {
2009-03-30 20:35:57 +02:00
global pch_error
2008-01-20 20:46:59 +01:00
append pch_error [ read $fd_ph ]
fconfigure $fd_ph - blocking 1
if { [ eof $fd_ph ] } {
if { [ catch { close $fd_ph } ] } {
hook_failed_popup post-commit $pch_error 0
}
unset pch_error
return
}
fconfigure $fd_ph - blocking 0
}
2016-10-06 15:04:42 +02:00
proc commit_author_ident { details } {
global env
array set author $details
set old [ array get env GIT_AUTHOR_* ]
set env( GIT_AUTHOR_NAME ) $author ( name )
set env( GIT_AUTHOR_EMAIL ) $author ( email )
set env( GIT_AUTHOR_DATE ) $author ( date )
return $old
}
proc commit_author_reset { details } {
global env
unset env( GIT_AUTHOR_NAME ) env( GIT_AUTHOR_EMAIL ) env( GIT_AUTHOR_DATE )
if { $details ne { } } {
array set env $details
}
}