-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommit.fish
More file actions
1188 lines (1072 loc) · 40.9 KB
/
Copy pathcommit.fish
File metadata and controls
1188 lines (1072 loc) · 40.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env fish
function say_ok
set_color green
echo -n "✓ "
set_color normal
echo $argv
end
function say_err
set_color red
echo -n "✗ "
set_color normal
echo $argv
end
function say_warn
set_color yellow
echo -n "! "
set_color normal
echo $argv
end
function say_info
set_color cyan
echo -n "→ "
set_color normal
echo $argv
end
set script_dir (dirname (status --current-filename))
set config_file "$script_dir/config.fish"
if not test -f "$config_file"
say_err "config.fish not found next to commit.fish."
echo "Copy config.example.fish to config.fish and edit it first."
exit 1
end
source "$config_file"
if not set -q PACK_NAME; or not set -q REPO_PATH; or not set -q SYNC_FOLDERS
say_err "config.fish is missing required variables (PACK_NAME, REPO_PATH, SYNC_FOLDERS)."
echo "Check config.example.fish for the expected format."
exit 1
end
if not set -q TOOL_VERSION
set TOOL_VERSION "1.5.0"
end
set dry_run 0
set script_start_time (date +%s)
set log_dir "$script_dir/logs"
set log_file "$log_dir/pack-commit.log"
set run_timestamp (date "+%Y-%m-%d %H:%M:%S")
function print_scheduled_tasks
set task_file "$log_dir/scheduled_tasks"
if test -f "$task_file"
set active_tasks
set printed_any 0
for line in (cat "$task_file")
set pid (echo $line | cut -d'|' -f1)
set time (echo $line | cut -d'|' -f2)
if kill -0 $pid 2>/dev/null
set -a active_tasks $line
if test $printed_any -eq 0
set_color cyan
echo "--- Scheduled Tasks ---"
set_color normal
set printed_any 1
end
say_info "Push scheduled for $time (PID: $pid)"
end
end
if test (count $active_tasks) -gt 0
printf "%s\n" $active_tasks > "$task_file"
else
rm -f "$task_file"
end
if test $printed_any -eq 1
echo ""
end
end
end
for arg in $argv
switch $arg
case "-h" "--help"
echo "Usage: commit.fish [options]"
echo ""
echo "Options:"
echo " -h, --help Show this help message and exit"
echo " --dry-run Show what would be synced and what changed"
echo " --stats Show a summary of logged commits and exit"
echo " --status Show current branch and unpushed commits"
echo " --info Combined view of --status and --stats"
echo " --version Show the tool's version and exit"
exit 0
case "--version"
echo "$PACK_NAME Commit Script — version $TOOL_VERSION"
exit 0
case "--dry-run"
set dry_run 1
case "--stats"
if not test -f "$log_file"
say_warn "No log file found yet at $log_file"
exit 0
end
set total_lines (count (cat "$log_file"))
set current_month (date "+%Y-%m")
set this_month_lines (grep -c "^\[$current_month" "$log_file" 2>/dev/null; or echo 0)
set first_line (head -n 1 "$log_file")
set last_line (tail -n 1 "$log_file")
set_color cyan
echo "=== $PACK_NAME — Commit Log Stats ==="
set_color normal
echo "Total logged commits: $total_lines"
echo "Commits this month: $this_month_lines"
echo ""
echo "First logged commit:"
echo " $first_line"
echo "Most recent commit:"
echo " $last_line"
exit 0
case "--status"
if not test -d "$REPO_PATH"
say_err "REPO_PATH does not exist: $REPO_PATH"
exit 1
end
if not test -d "$REPO_PATH/.git"
say_err "REPO_PATH is not a git repository: $REPO_PATH"
exit 1
end
cd "$REPO_PATH"
set_color cyan
echo "=== $PACK_NAME — Status ==="
set_color normal
say_info "Current branch: "(git rev-parse --abbrev-ref HEAD)
set status_pending (git rev-list --count '@{u}..HEAD' 2>/dev/null)
if test -n "$status_pending"; and test "$status_pending" -gt 0
say_warn "$status_pending unpushed commit(s):"
git log --oneline '@{u}..HEAD'
else
say_ok "No unpushed commits."
end
echo ""
set repo_changes (git status --short)
if test -n "$repo_changes"
say_warn "Uncommitted changes already in the repo folder:"
for line in $repo_changes
echo "$line"
end
else
say_ok "No uncommitted changes currently in the repo folder."
end
echo ""
print_scheduled_tasks
exit 0
case "--info"
if not test -d "$REPO_PATH"
say_err "REPO_PATH does not exist: $REPO_PATH"
exit 1
end
if not test -d "$REPO_PATH/.git"
say_err "REPO_PATH is not a git repository: $REPO_PATH"
exit 1
end
cd "$REPO_PATH"
set_color cyan
echo "=== $PACK_NAME — Info ==="
set_color normal
echo "Current branch: "(git rev-parse --abbrev-ref HEAD)
set info_pending (git rev-list --count '@{u}..HEAD' 2>/dev/null)
if test -n "$info_pending"; and test "$info_pending" -gt 0
say_warn "$info_pending unpushed commit(s):"
git log --oneline '@{u}..HEAD'
else
say_ok "No unpushed commits."
end
echo ""
set info_changes (git status --short)
if test -n "$info_changes"
say_warn "Uncommitted changes already in the repo folder:"
for line in $info_changes
echo "$line"
end
else
say_ok "No uncommitted changes currently in the repo folder."
end
echo ""
print_scheduled_tasks
set_color cyan
echo "--- Commit Log Stats ---"
set_color normal
if not test -f "$log_file"
say_warn "No log file found yet at $log_file"
else
set info_total_lines (count (cat "$log_file"))
set info_current_month (date "+%Y-%m")
set info_month_lines (grep -c "^\[$info_current_month" "$log_file" 2>/dev/null; or echo 0)
set info_first_line (head -n 1 "$log_file")
set info_last_line (tail -n 1 "$log_file")
echo "Total logged commits: $info_total_lines"
echo "Commits this month: $info_month_lines"
echo ""
echo "First logged commit:"
echo " $info_first_line"
echo "Most recent commit:"
echo " $info_last_line"
end
exit 0
end
end
if not test -d "$REPO_PATH"
say_err "REPO_PATH does not exist: $REPO_PATH"
exit 1
end
if not test -d "$REPO_PATH/.git"
say_err "REPO_PATH is not a git repository: $REPO_PATH"
exit 1
end
set malformed_folders 0
for folder in $SYNC_FOLDERS
set colon_count (string match -ar ":" -- "$folder" | count)
if test $colon_count -eq 0
say_err "Malformed SYNC_FOLDERS entry: $folder"
set malformed_folders 1
end
end
if test $malformed_folders -eq 1
exit 1
end
set missing_sources 0
for folder in $SYNC_FOLDERS
set src (echo $folder | cut -d ':' -f1)
if not test -d "$src"
say_err "Sync source does not exist: $src"
set missing_sources 1
end
end
if test $missing_sources -eq 1
exit 1
end
function get_last_sync_line
if not test -f "$log_file"
return
end
set last_line (tail -n 1 "$log_file")
set matches (string match -r '^\[([^\]]+)\]' -- "$last_line")
if test (count $matches) -lt 2
return
end
set log_ts $matches[2]
set log_epoch (date -d "$log_ts" +%s 2>/dev/null)
if test -z "$log_epoch"
return
end
set now_epoch (date +%s)
set diff (math $now_epoch - $log_epoch)
if test $diff -lt 60
set rel "just now"
else if test $diff -lt 3600
set mins (math -s0 "$diff / 60")
set rel "$mins min ago"
else if test $diff -lt 86400
set hours (math -s0 "$diff / 3600")
set rel "$hours hour(s) ago"
else
set days (math -s0 "$diff / 86400")
set rel "$days day(s) ago"
end
echo " Last synced: $rel ($log_ts)"
end
function print_banner
set_color cyan
echo ""
echo "========================================="
echo " $PACK_NAME — GitHub Commit Script"
echo " Syncs and commits pack changes"
echo " Made by $AUTHOR_NAME"
echo " Discord: $DISCORD_LINK"
echo " Version: $TOOL_VERSION"
get_last_sync_line
if test $dry_run -eq 1
echo " [DRY RUN — nothing will be committed or pushed]"
end
echo "========================================="
echo ""
set_color normal
end
function schedule_background_push
set target_time $argv[1]
set target_epoch (date -d "$target_time" +%s 2>/dev/null)
if test -z "$target_epoch"
return 1
end
set now_epoch (date +%s)
if test $target_epoch -le $now_epoch
set target_epoch (math $target_epoch + 86400)
end
set target_time_fmt (date -d @$target_epoch "+%H:%M")
say_ok "Push scheduled for $target_time_fmt in background."
say_info "You can continue working or making new commits, safely close this window anytime."
mkdir -p "$log_dir"
fish -c "
while true
set cur_epoch (date +%s)
if test \$cur_epoch -ge $target_epoch
break
end
sleep 1
end
cd '$REPO_PATH'
while true
set current_state (cat .git/ntr_sync_state 2>/dev/null)
if test -z \"\$current_state\"; or test \"\$current_state\" = \"IDLE\"
echo \"BG_RUNNING\" > .git/ntr_sync_state
sleep 1
set verify_state (cat .git/ntr_sync_state 2>/dev/null)
if test \"\$verify_state\" = \"BG_RUNNING\"
break
end
end
sleep 3
end
git fetch --quiet 2>/dev/null
if not git pull --rebase --quiet 2>/dev/null
echo '[\$(date \"+%Y-%m-%d %H:%M:%S\")] [$PACK_NAME] [SCHEDULED PUSH FAILED] Pull/rebase failed (possible conflict).' >> '$log_file'
echo \"IDLE\" > .git/ntr_sync_state
exit 1
end
set sched_msgs (git log --format='%s' '@{u}..HEAD' 2>/dev/null)
if git push --quiet 2>/dev/null
for msg in \$sched_msgs
echo '[\$(date \"+%Y-%m-%d %H:%M:%S\")] [$PACK_NAME] [SCHEDULED PUSH] '\$msg >> '$log_file'
end
else
echo '[\$(date \"+%Y-%m-%d %H:%M:%S\")] [$PACK_NAME] [SCHEDULED PUSH FAILED] Push failed (network or auth issue).' >> '$log_file'
end
echo \"IDLE\" > .git/ntr_sync_state
" >/dev/null 2>&1 &
set bg_pid $last_pid
echo "$bg_pid|$target_time_fmt" >> "$log_dir/scheduled_tasks"
disown
return 0
end
function print_branch
set_color yellow
echo "Current branch: $current_branch"
set_color normal
echo ""
end
function redraw_sync_status
clear
print_banner
print_branch
print_scheduled_tasks
echo ""
set_color yellow
echo "Files changed since last commit:"
set_color normal
git status --short
echo ""
end
function pause_continue
if test (count $argv) -gt 0
echo "$argv"
else
echo "Press Enter to continue."
end
read
end
function pause_close
echo ""
echo "Press Enter to close."
read
exit $argv[1]
end
function safe_pull_rebase
git fetch --quiet 2>/dev/null
set behind_count (git rev-list --count 'HEAD..@{u}' 2>/dev/null)
if test -z "$behind_count"; or test "$behind_count" -eq 0
return 0
end
set had_stash 0
set local_changes (git status --short)
if test -n "$local_changes"
say_info "Temporarily stashing uncommitted changes before pulling:"
for line in $local_changes
echo " $line"
end
git stash push -u -m "commit-script auto-stash before rebase" > /dev/null
set had_stash 1
end
git pull --rebase
set pull_status $status
if test $had_stash -eq 1
if git stash pop > /dev/null
say_ok "Restored stashed changes:"
for line in $local_changes
echo " $line"
end
else
say_err "Could not automatically restore your stashed changes."
end
end
return $pull_status
end
function do_sync
for folder in $SYNC_FOLDERS
set src (echo $folder | cut -d ':' -f1)
set dest (echo $folder | cut -d ':' -f2)
if test $dry_run -eq 1
echo "Would sync: $src -> $dest"
rsync -a --delete --dry-run "$src/" "$dest/"
else
rsync -a --delete "$src/" "$dest/"
end
end
end
function colorize_stat_line
set line $argv
set stat_parts (string split ", " -- $line)
set total_stat_parts (count $stat_parts)
for j in (seq 1 $total_stat_parts)
set part $stat_parts[$j]
if string match -q "*insertion*" -- $part
set_color green
echo -n "$part"
set_color normal
else if string match -q "*deletion*" -- $part
set_color red
echo -n "$part"
set_color normal
else
echo -n "$part"
end
if test $j -lt $total_stat_parts
echo -n ", "
end
end
echo ""
end
function compute_folder_tag
set files $argv
set touched
for folder in $SYNC_FOLDERS
set dest (echo $folder | cut -d ':' -f2)
set folder_name (basename "$dest")
for f in $files
if string match -q "$folder_name/*" -- "$f"
if not contains "$folder_name" $touched
set -a touched "$folder_name"
end
break
end
end
end
if test (count $touched) -gt 0
set tag "[" (string join ", " $touched) "]"
string join "" $tag
end
end
function get_head_folder_tag
compute_folder_tag (git diff-tree --no-commit-id --name-only -r HEAD)
end
function set_sync_state
echo $argv[1] > "$REPO_PATH/.git/ntr_sync_state"
end
function cleanup_sync_state --on-event fish_exit --on-signal INT
rm -f "$REPO_PATH/.git/ntr_sync_state"
end
clear
print_banner
cd "$REPO_PATH"
set_sync_state "ACTIVE"
if test -d "$REPO_PATH/.git/rebase-merge"; or test -d "$REPO_PATH/.git/rebase-apply"
say_err "A rebase is already in progress in $REPO_PATH."
pause_close 1
end
set orphaned_stash (git -C "$REPO_PATH" stash list 2>/dev/null | string match "*commit-script auto-stash*")
if test -n "$orphaned_stash"
say_err "Found a leftover stash from a previous interrupted run:"
for line in $orphaned_stash
echo " $line"
end
pause_close 1
end
set current_branch (git rev-parse --abbrev-ref HEAD)
print_branch
print_scheduled_tasks
set skip_pending_print 0
while true
set pending_count (git rev-list --count '@{u}..HEAD' 2>/dev/null)
if test -z "$pending_count"; or test "$pending_count" -eq 0
break
end
if test $skip_pending_print -eq 0
say_warn "You have $pending_count commit(s) not yet pushed from a previous run:"
git log --oneline --stat '@{u}..HEAD'
echo ""
end
set skip_pending_print 0
echo "Push these now? [Y/n], 'undo' (last), 'undo all', 'amend' (last), 'undo N'/'amend N' (e.g. 'amend 1'), or 'schedule HH:MM':"
read -P "> " pending_push_confirm
if string match -qr '^schedule ([0-9]{1,2}:[0-9]{2})$' -- "$pending_push_confirm"
set sched_time_str (string replace -r '^schedule ' '' -- "$pending_push_confirm")
if schedule_background_push "$sched_time_str"
echo ""
pause_continue "Press Enter to proceed to sync."
break
end
say_err "Couldn't parse time. Use HH:MM format."
set skip_pending_print 1
continue
end
if test "$pending_push_confirm" = "undo all"
git reset --soft "HEAD~$pending_count"
say_ok "Removed all pending commits (changes are kept, now staged)."
continue
end
if test "$pending_push_confirm" = "undo"
git reset --soft HEAD~1
say_ok "Removed the last local commit (changes kept staged)."
continue
end
if test "$pending_push_confirm" = "amend"
echo "Enter the corrected commit message:"
read -P "> " new_msg
if test -z "$new_msg"
say_warn "Cancelled."
set skip_pending_print 1
continue
end
set folder_tag (get_head_folder_tag)
if test -n "$folder_tag"
set new_msg "$folder_tag $new_msg"
end
git commit --amend -m "$new_msg"
say_ok "Commit message updated."
set skip_pending_print 1
continue
end
if string match -qr '^(undo|amend) ([0-9]+)$' -- "$pending_push_confirm"
set action (string replace -r '^(undo|amend) ([0-9]+)$' '$1' -- "$pending_push_confirm")
set target_idx (string replace -r '^(undo|amend) ([0-9]+)$' '$2' -- "$pending_push_confirm")
if test $target_idx -ge 1; and test $target_idx -le $pending_count
set session_commits (git rev-list --reverse --max-count=$pending_count HEAD)
set target_hash (string sub -l 7 $session_commits[$target_idx])
if test "$action" = "amend"
echo "Enter corrected message for commit $target_idx:"
read -P "> " new_msg
if test -z "$new_msg"; say_warn "Cancelled."; set skip_pending_print 1; continue; end
set staged_files (git diff-tree --no-commit-id --name-only -r $target_hash)
set folder_tag (compute_folder_tag $staged_files)
if test -n "$folder_tag"; and not string match -q "$folder_tag*" -- "$new_msg"
set new_msg "$folder_tag $new_msg"
end
if test $target_idx -eq $pending_count
if git commit -q --amend -m "$new_msg"
say_ok "Commit $target_idx amended."
else
say_err "Amend failed."
end
else
set had_rebase_stash 0
set check_status (git status --short)
if test (count $check_status) -gt 0
git stash push -q -u -m "commit-script temp stash before rebase"
set had_rebase_stash 1
end
if not env GIT_SEQUENCE_EDITOR="perl -pi -e 's/^pick $target_hash\w*/edit $target_hash/'" git rebase -i -q "HEAD~$pending_count" >/dev/null 2>&1
say_err "Rebase failed to start. Aborting."
git rebase --abort >/dev/null 2>&1
if test $had_rebase_stash -eq 1; git stash pop -q >/dev/null 2>&1; end
set skip_pending_print 1
continue
end
if not git commit -q --amend -m "$new_msg"
say_err "Amend failed. Aborting rebase."
git rebase --abort >/dev/null 2>&1
if test $had_rebase_stash -eq 1; git stash pop -q >/dev/null 2>&1; end
set skip_pending_print 1
continue
end
if not git rebase --continue >/dev/null 2>&1
say_err "Rebase failed to complete (possible conflict). Aborting."
git rebase --abort >/dev/null 2>&1
if test $had_rebase_stash -eq 1; git stash pop -q >/dev/null 2>&1; end
set skip_pending_print 1
continue
end
if test $had_rebase_stash -eq 1
git stash pop -q >/dev/null 2>&1
end
say_ok "Commit $target_idx amended."
end
set skip_pending_print 1
else
if test $target_idx -eq $pending_count
git reset -q --soft HEAD~1
say_ok "Commit $target_idx undone. Its changes are staged."
else
set had_rebase_stash 0
set check_status (git status --short)
if test (count $check_status) -gt 0
git stash push -q -u -m "commit-script temp stash before rebase"
set had_rebase_stash 1
end
set current_head (git rev-parse HEAD)
if not env GIT_SEQUENCE_EDITOR="perl -pi -e 's/^pick $target_hash\w*/drop/'" git rebase -i -q "HEAD~$pending_count" >/dev/null 2>&1
say_err "Failed to drop commit. Aborting rebase."
git rebase --abort >/dev/null 2>&1
if test $had_rebase_stash -eq 1; git stash pop -q >/dev/null 2>&1; end
set skip_pending_print 1
continue
end
set new_head (git rev-parse HEAD)
git reset -q --hard $current_head
git reset -q --soft $new_head
if test $had_rebase_stash -eq 1
git stash pop -q >/dev/null 2>&1
end
say_ok "Commit $target_idx undone. Its changes are staged."
end
set skip_pending_print 1
end
else
say_warn "Invalid commit number. Must be between 1 and $pending_count."
set skip_pending_print 1
end
continue
end
if not test "$pending_push_confirm" = "n"; and not test "$pending_push_confirm" = "N"
say_info "Pulling latest changes first..."
if not safe_pull_rebase
say_err "git pull --rebase failed, likely due to a conflict."
pause_close 1
end
set pending_msgs (git log --format="%s" '@{u}..HEAD')
git push
say_ok "Pending commits pushed."
mkdir -p "$log_dir"
for msg in $pending_msgs
echo "[$run_timestamp] [$PACK_NAME] [PENDING PUSH] $msg" >> "$log_file"
end
else
say_warn "Skipped."
end
echo ""
pause_continue "Press Enter to proceed to sync."
break
end
while true
clear
print_banner
print_branch
print_scheduled_tasks
set no_changes_choice ""
while true
do_sync
set changes (git status --short)
if test -n "$changes"
break
end
if test $dry_run -eq 1
say_info "No changes detected. (dry run)"
pause_close 0
end
say_warn "No changes detected since last commit."
echo "Press Enter to close, type 'r' to check again, or 'help' for usage info:"
set_sync_state "IDLE"
read -P "> " no_changes_choice
set printed_wait 0
while true
set check_state (cat "$REPO_PATH/.git/ntr_sync_state" 2>/dev/null)
if test "$check_state" != "BG_RUNNING"
break
end
if test $printed_wait -eq 0
echo ""
say_info "Waiting for background scheduled task to finish pushing..."
set printed_wait 1
end
sleep 2
end
set_sync_state "ACTIVE"
if test "$no_changes_choice" = "help"; or test "$no_changes_choice" = "--help"
echo ""
echo "Usage:"
echo " 'r' — re-sync folders and check for changes again"
echo " Enter — close the script"
echo ""
pause_continue
clear
print_banner
print_branch
print_scheduled_tasks
else if test -z "$no_changes_choice"
pause_close 0
else if test "$no_changes_choice" = "r"
clear
print_banner
print_branch
print_scheduled_tasks
end
end
echo ""
set_color yellow
echo "Files changed since last commit:"
set_color normal
git status --short
echo ""
set changed_file_count (count (git status --short))
if test $dry_run -eq 1
say_info "Dry run complete. No commits or pushes made."
pause_close 0
end
set valid_input 0
set do_resync 0
while test $valid_input -eq 0
echo "How many separate commits do you want to split these changes into? (default 1, type 'help' for info, 'r' to re-sync)"
read -P "> " commit_count
if test "$commit_count" = "r"
echo ""
say_info "Re-syncing..."
set do_resync 1
break
else if test "$commit_count" = "help"; or test "$commit_count" = "--help"
echo ""
echo "Usage:"
echo " Enter a number to split your changes into multiple commits."
echo " Press Enter for 1 commit containing everything."
echo ""
pause_continue
redraw_sync_status
else
if test -z "$commit_count"
set commit_count 1
set valid_input 1
else if string match -qr '^[0-9]+$' -- "$commit_count"
if test "$changed_file_count" -eq 1; and test "$commit_count" -gt 1
echo ""
say_warn "Only 1 file changed."
pause_continue
redraw_sync_status
else
set valid_input 1
end
else
echo ""
say_warn "Invalid input."
pause_continue
redraw_sync_status
end
end
end
if test $do_resync -eq 1
continue
end
git reset > /dev/null
set committed_messages
for i in (seq 1 $commit_count)
echo ""
set_color cyan
echo "--- Commit $i of $commit_count ---"
set_color normal
if test $commit_count -gt 1
set remaining_status (git status --short)
if test -z "$remaining_status"
set commit_paths "-A"
else
set remaining_paths
set idx 1
set total_remaining (count $remaining_status)
set idx_width (string length -- "$total_remaining")
echo "Remaining changed files:"
for line in $remaining_status
set fpath (string sub -s 4 -- $line)
set -a remaining_paths "$fpath"
set padded_idx (string pad -w $idx_width -- "$idx")
echo " $padded_idx) $line"
set idx (math $idx + 1)
end
set picker_valid 0
set lines_to_clear 0
while test $picker_valid -eq 0
if test $lines_to_clear -gt 0
tput cuu $lines_to_clear
tput ed
set lines_to_clear 0
end
echo "Enter numbers separated by spaces, or press Enter for everything remaining:"
read -P "> " picker_raw
if test -z "$picker_raw"
set commit_paths "-A"
set picker_valid 1
else
set commit_paths
set invalid_count 0
for n in (string split " " -- $picker_raw)
if string match -qr '^[0-9]+$' -- "$n"
set chosen $remaining_paths[$n]
if test -n "$chosen"
if not contains "$chosen" $commit_paths
set -a commit_paths "$chosen"
end
else
echo "Invalid number: $n, skipping."
set invalid_count (math $invalid_count + 1)
end
else
echo "Invalid input: $n, skipping."
set invalid_count (math $invalid_count + 1)
end
end
if test (count $commit_paths) -eq 0
echo "No valid selections were made."
pause_continue
set lines_to_clear (math 5 + $invalid_count)
else
set picker_valid 1
end
end
end
end
else
set commit_paths "-A"
end
set commit_msg ""
set skip_round 0
while test -z "$commit_msg"
echo "Enter commit message for commit $i (or type 'history' to reuse a recent one, 'skip' to skip this round):"
read -P "> " commit_msg_raw
if test "$commit_msg_raw" = "skip"
set skip_round 1
break
end
if test "$commit_msg_raw" = "history"
if test -f "$log_file"
set_color cyan
echo "Last 5 commit messages:"
set_color normal
set recent_msgs (tail -n 5 "$log_file")
set idx 1
for line in $recent_msgs
echo " $idx) $line"
set idx (math $idx + 1)
end
echo "Type a number to reuse one, or type a new message:"
read -P "> " history_choice
if string match -qr '^[0-9]+$' -- "$history_choice"
set chosen_line $recent_msgs[$history_choice]
if test -n "$chosen_line"
set commit_msg (string replace -r '^\[.*?\]\s*\[.*?\]\s*(\[(PENDING PUSH|SCHEDULED PUSH|SCHEDULED)\]\s*)?' '' -- "$chosen_line")
set valid_tags
for folder in $SYNC_FOLDERS
set -a valid_tags (basename (echo "$folder" | cut -d ':' -f2))
end
set tag_pattern (string join "|" $valid_tags)
set tag_regex '^\[('"$tag_pattern"')(,\s*('"$tag_pattern"'))*\]\s*'
set commit_msg (string replace -r $tag_regex '' -- "$commit_msg")
else
echo "Invalid number, try again."
end
else
set commit_msg "$history_choice"
end
else
echo "No log file yet."
end
else
set commit_msg "$commit_msg_raw"
end
end
if test $skip_round -eq 1
say_warn "Skipped commit $i."
continue
end
git add $commit_paths
set staged_files (git diff --cached --name-only)
set folder_tag (compute_folder_tag $staged_files)
if test -n "$folder_tag"
set commit_msg "$folder_tag $commit_msg"
end
set commit_output (git commit -m "$commit_msg")
set commit_status $status
if test $commit_status -eq 0
for line in $commit_output
if string match -q "*insertion*" -- $line; or string match -q "*deletion*" -- $line
colorize_stat_line "$line"
else
echo "$line"
end
end
set -a committed_messages "$commit_msg"
else
say_warn "Nothing was actually committed for this round."
end
end
set commit_num_made (count $committed_messages)
set goto_sync_check 0
while true
if test $commit_num_made -eq 0
say_warn "No commits left to push."
set goto_sync_check 1
break
end
echo ""
set_color yellow
echo "About to push $commit_num_made commit(s). [Y/n], 'undo' (last), 'undo all', 'amend' (last), 'undo N'/'amend N' (e.g. 'amend 1'), or 'schedule HH:MM':"
set_color normal
read -P "> " push_confirm
if string match -qr '^schedule ([0-9]{1,2}:[0-9]{2})$' -- "$push_confirm"
set sched_time_str (string replace -r '^schedule ' '' -- "$push_confirm")
if schedule_background_push "$sched_time_str"
for msg in $committed_messages
echo "[$run_timestamp] [$PACK_NAME] [SCHEDULED] $msg" >> "$log_file"
end
set goto_sync_check 1
break
end