]> git.donarmstrong.com Git - lilypond.git/blob - scripts/auxiliar/lily-git.tcl
Run grand-replace for 2012
[lilypond.git] / scripts / auxiliar / lily-git.tcl
1 #!/usr/bin/wish
2
3 # GUI interface for common LilyPond git repository commands
4 # Copyright 2009--2012 by Johannes Schindelin and Carl Sorensen
5 #
6
7 set version 0.64
8
9 # set to 1 to set up for translation, to 0 for other
10 set translator 0
11
12 # location of lilypond git
13 set lily_dir $env(HOME)/lilypond-git
14
15 if {$translator == 1} {
16     set windowTitle \
17         "LilyPond Translator's Git Interface version $version"
18     set updateButtonText "1. Update translation"
19     set initializeButtonText "1. Get translation"
20     set originHead "lilypond/translation"
21     set rebase 0
22 } else {
23     set windowTitle \
24         "LilyPond Contributor's Git Interface version $version"
25     set updateButtonText "1. Update source"
26     set initializeButtonText "1. Get source"
27     set originHead "master"
28     set rebase 1
29 }
30 package require Tk
31
32 ##  Submits the user data collected using the git config command
33
34 proc submituserdata {} {
35     exec git config --global user.name "$::username"
36     exec git config --global user.email "$::useremail"
37     destroy .b
38     return 0
39 }
40
41 ##  Request name and email from user
42
43 proc requestuserdata {} {
44     toplevel .b
45     grab .b
46     wm geometry .b -300-300
47     wm title .b "Contributor details"
48     grid [frame .b.c ] -column 0 -row 0 -sticky nwes
49     grid columnconfigure . 0 -weight 1; grid rowconfigure . 0 -weight 1
50
51     grid [entry .b.c.username -width 20 -textvariable username] -column 2 -row 2 -sticky we
52     grid [entry .b.c.useremail -width 20 -textvariable useremail] -column 2 -row 3 -sticky we
53     grid [button .b.c.submituserdata -text "Submit" -command submituserdata] -column 2 -row 4
54
55     grid [label .b.c.explbl -text "Please enter your name and email for future commits:"] -column 1 -row 1 -columnspan 3 -sticky we
56     grid [label .b.c.nmlbl -text "Name:"] -column 1 -row 2  -sticky w
57     grid [label .b.c.emlbl -text "Email:"] -column 1 -row 3 -sticky w
58
59     foreach w [winfo children .b.c] {grid configure $w -padx 5 -pady 5}
60     focus .b.c.username
61     bind .b <Return> {submituserdata}
62 }
63
64 ##  Checks the user's global .gitconfig for name and email and executes requestuserdata if either is not found
65
66 if {![file exists "$env(HOME)/.gitconfig"]} {
67     set fileholder [open "$env(HOME)/.gitconfig" a+]
68 } else {
69     set fileholder [open "$env(HOME)/.gitconfig" r]
70 }
71
72 set usercheck [split [read $fileholder] "\n"]
73 close $fileholder
74 if {![regexp "name" $usercheck] || ![regexp "email" $usercheck]} then {
75     requestuserdata
76     tkwait window .b
77 }
78
79 ##  Entry limit routine from jeff at hobbs org, downloaded from
80 ##  http://www.purl.org/net/hobbs/tcl/tclet/entrylimit.html
81
82 ## For those who aren't using Tk4 with these, make a bell noop:
83 if [string match {} [info commands bell]] { proc bell args {} }
84
85 proc forceLen {len name el op} {
86     global $name ${name}_len
87     if [string comp $el {}] {
88         set old  ${name}_len\($el)
89         set name $name\($el)
90     } else {
91         set old ${name}_len
92     }
93     if {[string length [set $name]] > $len} {
94         set $name [set $old]
95         bell;
96         return
97     }
98     set $old [set $name]
99 }
100
101 ## Here is a wish example to use the routines.  Remember that with
102 ## write traces, a valid value must be set for each variable both
103 ## before AND after the trace is established.
104
105 ## The order must be:
106 ## 1) variable init
107 ## 1) textvariable specification
108 ## 3) set trace
109 ## 4) variable reinit
110
111 set commit_header {}
112 trace variable commit_header w {forceLen 50}
113 set commit_header {}
114
115
116 ## End of entry limit code
117
118
119 # Helper functions
120
121 if {[file exists $lily_dir]} {
122     cd $lily_dir
123 }
124
125 set abort_dir "./aborted_edits"
126
127 proc write_to_output {s} {
128     .output.text insert insert $s
129     .output.text see end
130 }
131
132 proc write_file_to_output {f} {
133     if {[eof $f]} {
134         global git_command
135         fconfigure $f -blocking true
136         if {[catch {close $f} err]} {
137             tk_messageBox -type ok -message \
138                 "Command returned an error: $err\n\nCheck output text for details"
139         }
140         unset git_command
141     } else {
142         write_to_output [read $f 24]
143     }
144 }
145
146 proc git {args} {
147     global lily_dir git_command
148     set git_command [linsert $args 0 "|git" "--git-dir=$lily_dir/.git"]
149     set git_command "$git_command 2>@1"
150     .output.text insert end "$git_command\n"
151     set git [open $git_command r]
152     fconfigure $git -blocking false
153     fileevent $git readable [list write_file_to_output $git]
154     vwait git_command
155 }
156
157 proc config {args} {
158     global lily_dir
159     set p [open [linsert $args 0 "|git" --git-dir=$lily_dir/.git config] r]
160     set result [regsub "\n\$" [read $p] ""]
161     if {[catch {close $p} err]} {
162         tk_messageBox -type ok -message "config failed: $err"
163     }
164     return $result
165 }
166
167 proc config_quiet {args} {
168     global lily_dir
169     set p [open [linsert $args 0 "|git" --git-dir=$lily_dir/.git config] r]
170     set result [regsub "\n\$" [read $p] ""]
171     if {[catch {close $p} err]} {
172         set result ""
173     }
174     return $result
175 }
176
177 proc update_lilypond_rebase {} {
178     update_lilypond 1
179 }
180
181 proc commit {} {
182     global commit_message
183     global commit_canceled
184     set commit_canceled 0
185     get_commit_message
186     tkwait visibility .commitMessage
187     tkwait window .commitMessage
188     if {$commit_canceled != 1} {
189         if {$commit_message == ""} {
190             tk_messageBox -message "You must enter a commit message!" \
191                 -type ok -icon error
192         } else {
193             git commit -a -m $commit_message
194             git rebase --whitespace=fix HEAD^
195             set commit_message ""
196         }
197     }
198 }
199
200 proc commit_amend {} {
201     git commit -a --amend -C HEAD
202     git rebase --whitespace=fix HEAD^
203 }
204
205 proc update_lilypond_norebase {} {
206     update_lilypond 0
207 }
208
209 proc update_lilypond_with_rebase {} {
210     global rebase
211     update_lilypond $rebase
212 }
213
214 proc update_lilypond {rebase} {
215     global lily_dir
216     global originHead
217     global translator
218     . config -cursor watch
219     if {![file exists $lily_dir]} {
220         write_to_output "Cloning LilyPond (this can take some time) ...\n"
221         file mkdir $lily_dir
222         cd $lily_dir
223         git init
224         git config core.bare false
225         git remote add -t $originHead \
226             origin git://git.sv.gnu.org/lilypond.git
227         git fetch
228         git reset --hard origin/$originHead
229         git config branch.$originHead.remote origin
230         git config branch.$originHead.merge refs/heads/$originHead
231         .buttons.commitFrame.commit configure -state normal
232         .buttons.commitFrame.amend configure -state normal
233         .buttons.update configure -text buttonUpdateText
234         .buttons.patch configure -state normal
235         .buttons.panic configure -state normal
236         toggle_rebase
237     } else {
238         write_to_output "Updating LilyPond...\n"
239         git fetch origin
240         if {$rebase} {
241             git rebase origin/$originHead
242         } else {
243             git merge origin/$originHead
244         }
245     }
246     write_to_output "Done.\n"
247     . config -cursor ""
248 }
249
250 proc patch_from_origin {} {
251     global rebase
252     make_patch_from_origin $rebase
253     if {![llength [glob -nocomplain 0*.patch]]} {
254         tk_messageBox -type ok -message \
255             "No patches created; did you make a local commit?"
256     }
257 }
258
259 proc make_patch_from_origin {rebase} {
260     global lily_dir
261     global originHead
262     . config -cursor watch
263     update_lilypond $rebase
264     write_to_output "Creating patch...\n"
265     git format-patch origin/$originHead
266     write_to_output "Done.\n"
267     . config -cursor ""
268 }
269
270 proc abort_changes {} {
271     global abort_dir
272     global originHead
273     set answer [tk_messageBox -type okcancel \
274                     -message "This will copy all changed files to $abort_dir and reset the repository." \
275                     -default cancel]
276     switch -- $answer {
277         ok {
278             write_to_output "abort_dir: $abort_dir \n"
279             if {![file exists $abort_dir]} {
280                 set return_code [exec mkdir $abort_dir]
281             }
282             set return_code [catch {exec git diff origin/$originHead} gitdiff]
283             set return_code [regexp {diff --git a/(\S*)} $gitdiff match modified_file]
284             while {$return_code != 0} {
285                 write_to_output "Copying $modified_file to $abort_dir.\n"
286                 set return_code [catch {exec cp $modified_file $abort_dir} result]
287                 set return_code [regsub {diff --git a/(\S*)} $gitdiff "" gitdiff]
288                 set return_code [regexp {diff --git a/(\S*)} $gitdiff match modified_file]
289             }
290             set return_code [git reset --hard origin/$originHead]
291             write_to_output "Repository reset. \n"
292         }
293     }
294 }
295
296 proc toggle_rebase {} {
297     global rebase
298     global lily_dir
299     global originHead
300     global updateButtonText
301     global initializeButtonText
302     if {[file exists $lily_dir]} {
303         config --bool branch.$originHead.rebase $rebase
304         .buttons.update configure -text $updateButtonText
305     } else {
306         .buttons.update configure -text $initializeButtonText
307     }
308 }
309
310 proc clear_rebase {} {
311     global rebase
312     set rebase 0
313     toggle_rebase
314 }
315
316 proc set_rebase {} {
317     global rebase
318     set rebase 1
319     toggle_rebase
320 }
321
322 proc commitMessageOK {} {
323     global commit_message
324     global commit_header
325     set commit_body [.commitMessage.bottomFrame.commit_body get 1.0 end]
326     set commit_message "$commit_header\n\n$commit_body"
327     destroy .commitMessage
328 }
329
330 proc commitMessageCancel {} {
331     global commit_message
332     global commit_canceled
333     set commit_message ""
334     set commit_canceled 1
335     destroy .commitMessage
336 }
337
338
339 # Commit message input window
340 proc get_commit_message {} {
341     global commit_header
342     set commit_header ""
343     toplevel .commitMessage
344     frame .commitMessage.topFrame
345     label .commitMessage.topFrame.label \
346         -text "Enter commit message header:\n(50 chars max = width of box)"
347     entry .commitMessage.topFrame.commit_header \
348         -width 50 -relief solid -border 2 -textvariable commit_header
349     pack   .commitMessage.topFrame.label -side left
350     pack   .commitMessage.topFrame.commit_header -side left
351
352     frame  .commitMessage.bottomFrame
353     text   .commitMessage.bottomFrame.commit_body \
354         -width 75  -height 10 -relief solid -border 2 -wrap none
355
356     frame .commitMessage.bottomFrame.leftFrame
357     label .commitMessage.bottomFrame.leftFrame.label \
358         -text "Enter commit message body:\n(No limit -- Full description)"
359     button .commitMessage.bottomFrame.leftFrame.ok \
360         -text OK -default active -command commitMessageOK
361     button .commitMessage.bottomFrame.leftFrame.cancel -text Cancel -default active \
362         -command commitMessageCancel
363     wm withdraw .commitMessage
364     wm title .commitMessage "Git Commit Message"
365
366     pack .commitMessage.bottomFrame.leftFrame.label
367     pack .commitMessage.bottomFrame.leftFrame.ok
368     pack .commitMessage.bottomFrame.leftFrame.cancel
369
370     pack .commitMessage.bottomFrame.leftFrame -side left
371     pack .commitMessage.bottomFrame.commit_body -side left
372
373     pack .commitMessage.topFrame
374     pack .commitMessage.bottomFrame
375
376     wm transient .commitMessage .
377     wm deiconify .commitMessage
378 }
379
380
381 # GUI
382
383 wm title . $windowTitle
384
385 # Buttons
386
387 panedwindow .buttons
388
389 frame  .buttons.commitFrame
390 button .buttons.commitFrame.commit -text "2a. New local commit" -command commit
391 button .buttons.commitFrame.amend -text "2b. Amend previous commit" -command commit_amend
392 pack .buttons.commitFrame.commit -fill x
393 pack .buttons.commitFrame.amend -fill x
394
395 button .buttons.update -text $updateButtonText \
396     -command update_lilypond_with_rebase
397 button .buttons.patch -text "3. Make patch set" \
398     -command patch_from_origin
399 toggle_rebase
400 button .buttons.panic -text "Abort changes -- Reset to origin" \
401     -command abort_changes -fg Blue -bg Red
402 label   .buttons.spacer -text "                         "
403 if {![file exists $lily_dir]} {
404     .buttons.update configure \
405         -text $initializeButtonText
406     .buttons.commitFrame.commit configure -state disabled
407     .buttons.commitFrame.amend configure -state disabled
408     .buttons.patch configure -state disabled
409     .buttons.panic configure -state disabled
410 }
411
412 #  Operating buttons
413
414 pack .buttons.update -side left
415 pack .buttons.commitFrame -side left
416 pack .buttons.patch -side left
417 pack .buttons.spacer -side left
418 pack .buttons.panic -side right
419
420
421 # Output text box
422
423 panedwindow .output
424 label .output.label -text "Command output:"
425 text .output.text -width 80 -height 15 \
426     -xscrollcommand [list .output.horizontal set] \
427     -yscrollcommand [list .output.vertical set] \
428     -relief solid -border 2
429 scrollbar .output.horizontal -orient h -command [list .output.text xview]
430 scrollbar .output.vertical -orient v -command [list .output.text yview]
431 pack .output.label -side left
432 pack .output.horizontal -side bottom -fill x
433 pack .output.vertical -side right -fill y
434 pack .output.text -expand true -anchor nw -fill both
435
436 pack .buttons
437 pack .output
438
439 #grid .buttons -row 2 -column 1
440 #grid .output -row 3 -column 1 -sticky "w"