-
Notifications
You must be signed in to change notification settings - Fork 0
/
qtool
executable file
·2018 lines (1669 loc) · 48 KB
/
qtool
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
#!/bin/bash
prog_name=$0
my_email="peterx@redhat.com"
my_fullname="Peter Xu"
my_sob_line="Signed-off-by: ${my_fullname} <${my_email}>"
linux_dir="$HOME/git/linux"
# This should be mostly correct if the image was built by virt-builder
linux_root="/dev/vda3"
debug=false
os=$(uname -s)
if [[ $os == Linux ]]; then
ncpus=$(lscpu | grep "^CPU(s)" | awk '{print $2}')
realpath_bin=realpath
elif [[ $os == Darwin ]]; then
ncpus=$(sysctl hw.logicalcpu | sed 's/.*: //')
realpath_bin=grealpath
else
echo "Unknown OS: $os"
exit 1
fi
# we count the user of the fake editor, when reach zero, we unset
# the fake editor. To gain ref, use git_set_fake_editor(), to
# release ref, use git_unset_fake_editor()
fake_editor_refcount=0
usage ()
{
cat <<EOF
usage: $prog_name <cmd>
supportted cmds:
apply-patches: apply whole patchset using b4
auto-bisect: automatic bisection of bad codes
build-initrd: build initrd.img from directory
create-initrd-root: create basic initramfs under directory specified
dump-kstack: dump the kernel stack of all threads of a process
fake-editor: this is a fake editor, it pauses, wait for a
SIGUSR1 and then quit with errno=0
flamegraph-record: capture perf data for flamegraph
flamegraph-convert: convert perf data into perf.svg flamegraph
generate-cscope: generate cscope tags for current dir
git-apply-series: apply a patchset saved by mutt
git-commit-set-cc: auto-generate CC list for commit
(this will remove all existing CC list!)
git-commit-get-cc: list all CC list for specific commit
git-fix-commit: fix specific git commit (rebase)
git-self-review: do self review of git commits
git-compile-check: check compile for commit range
git-tag-cleanup: cleanup useless git tags created by git-publish
grub-update: update grub configurations
init-dev: init dev environment for host
install-dev: install packages for dev
isolate-cpus: do further isolation of CPUs
kernel-backtrace: dump backtrace for kernel function
kernel-boot: choose which version of kernel to boot in host
kernel-grep-function: find kernel function with key
kernel-install: install the kernel (to specific VM)
kernel-quit: stop the running kernel...
kernel-run: run specific kernel using QEMU
kernel-cleanup: remove kernel of specific version
kexec: use kexec facilities to direct boot into a new kernel
mail-find: search content in mailbox
numa-info: dump numa info
prepare-send: prepare one script to send patches
pci: some PCI related operations
qemu-build: build QEMU repository.
qemu-docker-tests: do proper docker tests for the tree
send-patch: send patches using git-send-email
ssh-host: SSH to host, with line buffering
statistics: calculate simple statistics for array of numbers
status-report: generate status report
tmux-init: init the basic and default tmux session
vpnc-dial-in: dial in VPN
vm-start-kernel: start a VM (using 'q') booting a just-cooked kernel
vm-image-commit: commit recent VM changes to backing store
ping-vpn-servers: test network connection of servers
EOF
exit 1
}
debug ()
{
if $debug; then
echo -n "[DEBUG_QTOOL] " >&2
echo $@ >&2
fi
}
err ()
{
echo "ERROR: $@"
exit 1
}
COLOR_RED='\033[0;31m'
COLOR_GREEN='\033[0;32m'
COLOR_YELLOW='\033[0;33m'
COLOR_NC='\033[0m' # No Color
echo_color()
{
local color="$1"
shift 1
local prefix=""
case $color in
red|bad) prefix="$COLOR_RED" ;;
green|good) prefix="$COLOR_GREEN" ;;
yellow|warn) prefix="$COLOR_YELLOW" ;;
*) echo "PANIC"; exit 1 ;;
esac
echo -en "$prefix"
echo -n $@
echo -e "$COLOR_NC"
}
read_yes()
{
local answer
read answer
case $answer in
yes|y) return 0 ;;
*) return 1;;
esac
}
is_number()
{
[[ "$1" =~ ^[0-9]+$ ]]
}
is_quit()
{
[[ "$1" == "quit" || "$1" == "q" || \
"$1" == "exit" || "$1" == "" ]]
}
wait_until_file_exists ()
{
while [[ ! -f "$1" ]]; do
sleep 0.1
done
}
wait_pid_quit ()
{
while kill -0 $1 &> /dev/null; do
sleep 0.1
done
}
do_qemu_build ()
{
local current=$(pwd)
local pdir=$(dirname $current)
local top=$(basename $current)
local args="$1"
local target_list="x86_64-softmmu,aarch64-softmmu,ppc64-softmmu,x86_64-linux-user,i386-softmmu"
if [[ "$args" == "--help" ]]; then
cat <<EOF
usage: $prog_name qemu-build [arch_list]
Configure/Build QEMU under $QEMU_REPO/bin/ directory.
EOF
return 1
fi
if [[ -n "$args" ]]; then
if [[ "$args" == "all" ]]; then
target_list=""
else
target_list="$args"
fi
fi
if [[ -n "$target_list" ]]; then
target_list="--target-list=$target_list"
fi
if [[ ! "$top" == bin* ]]; then
echo "please run this under QEMU_ROOT/bin*"
return 1
fi
../configure $target_list \
--enable-debug --enable-spice \
--enable-usb-redir
if [[ $? != 0 ]]; then
echo "configure failed, stop here"
return 1
fi
make -j8
if [[ $? != 0 ]]; then
echo "build failed... stop here"
return 1
fi
}
do_qemu_docker_tests()
{
# entries="docker-test-quick@centos6"
# entries="$entries docker-test-mingw@fedora"
# entries="$entries docker-test-build@min-glib"
# entries="$entries docker-test-block@fedora"
if [[ -z "$1" ]]; then
echo "Please specify base commit ID"
return 1
fi
base_commit="$1"
git log -1 $base_commit &> /dev/null
if [[ $? != 0 ]]; then
echo "Base commit '$base_commit' is incorrect"
return 1
fi
set -e
echo "=================================="
echo "Testing checkpatch"
echo "=================================="
./scripts/checkpatch.pl ${base_commit}..HEAD
echo "=================================="
echo "Testing docker builds for mingw"
echo "=================================="
time make SHOW_ENV=1 J=$ncpus docker-test-mingw@fedora
echo "=================================="
echo "Testing make check"
echo "=================================="
pushd bin && time make check -j$ncpus && popd
set +e
return 0
}
do_init_dev ()
{
local files=".inputrc .bashrc .gitconfig .vimrc"
local host="$1"
if [[ -z "$host" ]]; then
cat <<EOF
usage: $prog_name init-dev <host>
Init dev environment for <host>. <host> is required.
EOF
return 1
fi
echo "copy SSH public key..."
ssh-copy-id $host || err "failed copy SSH public key"
echo "copy config files..."
all=""
for file in $files; do
all="$all $HOME/$file"
done
scp $all $host: || err "copy file $path failed"
if ! ssh $host ls "~/bin" &> /dev/null; then
echo "init ~/bin..."
ssh $host git clone https://github.com/xzpeter/small-stuffs.git bin \
|| err "create ~/bin failed"
else
echo "detected ~/bin, skip."
fi
ssh $host sudo ln -sf "~/bin/rh/qtool" /usr/local/bin \
|| err "create qtool softlink failed"
echo "install dev packages..."
ssh $host sudo qtool install-dev || err "install packages failed"
echo "copy bash complete for qtool"
ssh $host sudo cp "~/bin/tools/qtool_completion.sh" /etc/bash_completion.d/ \
|| err "copy bash complete for qtool failed"
echo "All done!"
}
initrd_img="initrd.img"
do_build_initrd ()
{
local dir="$1"
local err=0
if [[ -z "$dir" ]]; then
cat <<EOF
usage: $prog_name build-initrd <dir>
Build $initrd_img file by packing dir.
EOF
exit 1
fi
# check /init script
if [[ ! -f "$dir/init" ]]; then
echo "please provide $dir/init file (at least!)"
return 1
fi
local img="$PWD/$initrd_img"
pushd $dir &> /dev/null
find . | cpio -H newc -o | gzip > $img
if [[ $? == 0 ]]; then
echo "$initrd_img created at $img"
err=0
else
echo "failed to create $initrd_img"
err=1
fi
popd &> /dev/null
return $err
}
# before run this, we should have the following directories under PWD:
# "kernel": as INSTALL_PATH for kernel make
# "initramfs": rootfs for the system to be booted. to create one,
# check: http://jootamam.net/howto-initramfs-image.htm
# "modules": as INSTALL_MOD_PATH for kernel make (currently not used)
do_kernel_run()
{
# specify "-S" to halt QEMU when boot
extra_params="$@"
kernel_dir="$PWD/kernel"
initrd_dir="$PWD/initramfs"
qemu_bin=$(which qemu-kvm 2>/dev/null)
if [[ $? != 0 ]]; then
echo "please install qemu-kvm first"
return 1
fi
if [[ ! -d "$kernel_dir" ]]; then
echo "cannot find kernel dir $kernel_dir"
return 1
fi
if [[ ! -d "$initrd_dir" ]]; then
echo "cannot find initramfs dir $initrd_dir"
return 1
fi
# build initrd every time!
if ! do_build_initrd $initrd_dir; then
return 1
fi
# try to find the kernel
kernel_file=$(find $kernel_dir -name "vmlinuz*")
lines=$(echo "$kernel_file" | wc -l)
if [[ "$lines" != 1 ]]; then
echo "found more than one kernel under $kernel_dir?"
return 1
fi
$qemu_bin -enable-kvm -m 1024 -kernel $kernel_file \
-initrd $initrd_img -nographic \
-append "console=ttyS0" -s $extra_params
# after this, we should be able to debug the kernel using:
# $ gdb $VMLINUX_PATH
# (gdb) set architecture i386:x86-64:intel
# (gdb) target remote localhost:1234
}
do_kernel_quit ()
{
pkill -f "$prog_name kernel-run"
}
init_script_def='#!/bin/sh
# Based on: http://jootamam.net/howto-initramfs-image.htm
# Create all the symlinks to /bin/busybox
/bin/busybox --install -s /bin
# Mount things needed by this script
mount -t proc proc /proc
mount -t sysfs sysfs /sys
# Disable kernel messages from popping onto the screen
# echo 0 > /proc/sys/kernel/printk
# Create device nodes
mknod /dev/null c 1 3
mknod /dev/tty c 5 0
mdev -s
# Function for parsing command line options with "=" in them
# get_opt("init=/sbin/init") will return "/sbin/init"
get_opt() {
echo "$@" | cut -d "=" -f 2
}
# Defaults
init="/sbin/init"
root="/dev/vda1"
# Process command line options
for i in $(cat /proc/cmdline); do
case $i in
root\=*)
root=$(get_opt $i)
;;
init\=*)
init=$(get_opt $i)
;;
esac
done
# Mount the root device
mount "${root}" /newroot
# Check if $init exists and is executable
if [[ -x "/newroot/${init}" ]] ; then
# Unmount all other mounts so that the ram used by
# the initramfs can be cleared after switch_root
umount /sys /proc
#Switch to the new root and execute init
exec switch_root /newroot "${init}"
fi
# This will only be run if the exec above failed
echo "Failed to switch_root, dropping to a shell"
exec sh
'
do_create_initrd_root ()
{
local dir_name="$1"
if [[ -z "$dir_name" ]]; then
cat <<EOF
usage: $prog_name create-initrd-root <dir_name>
This command will create one minimum rootfs layout under <dir_name>
specified.
EOF
return 1
fi
if [[ -e "$dir_name" ]]; then
echo -n "Path $dir_name exists, "
echo "please manually remove it before hand"
return 1
fi
local busybox_path=$(which busybox 2>&1)
if [[ $? != 0 ]]; then
echo "Failed to find busybox, please install it before hand"
return 1
fi
mkdir $dir_name || err "create dir $dir_name failed"
cd $dir_name
local dir_list="bin sbin etc proc sys newroot"
local name=""
echo "Creating directories: $dir_list"
for name in $dir_list; do
mkdir $name
done
echo "Creating mdev.conf"
touch etc/mdev.conf
echo "Installing busybox"
cp $busybox_path bin/busybox
chmod a+x bin/busybox
ln -s busybox bin/sh
echo "Creating /init"
echo "$init_script_def" > init
chmod a+x init
echo "Initramfs root created under $dir_name successfully."
return 0
}
__host_ssh_reachable ()
{
ssh $1 ls &> /dev/null
}
do_kernel_boot_install()
{
local version="$1"
if ssh $vm grubby --info ALL | \
grep "kernel=\"/boot/vmlinuz-${version}\""; then
echo "Detected kernel ${version} in grub, skip install"
else
echo "Installing kernel ${version} in grub"
ssh $vm grubby --add-kernel /boot/vmlinuz-$version \
--title "Linux-${version}"
fi
}
do_kernel_install ()
{
# set this if we want to copy the kernels to VM
local vm="$1"
shift 1
local params="$@"
local set_default=false
local kernel_only=false
local output_dir="output"
if [[ -z "$vm" ]]; then
cat <<EOF
usage: $prog_name kernel-install <vm> [option [option...]]
This will install kernel and modules into <vm>.
<vm>: should be the name of the VM, with SSH pub key copied before
hand.
Supported options:
set_default: set this parameter non-empty to set new kernel as
default one
kernel_only: only install compressed kernel to /boot
EOF
return 1
fi
if ! __host_ssh_reachable $vm; then
echo "VM $vm not reachable by SSH"
return 1
fi
if ! ssh $vm which rsync &> /dev/null; then
echo "Please install rsync in the guest first"
return 1
fi
local param=""
for param in $params; do
case $param in
set_default) set_default=true ;;
kernel_only) kernel_only=true ;;
*) echo "unknown option: $param"; return 1 ;;
esac
done
rm -rf $output_dir
mkdir -p $output_dir
##############################
# kernel
##############################
# get kernel version
local kernel_version=""
kernel_version=$(make kernelrelease)
echo "Kernel version to install: '$kernel_version'"
echo "Remote machine hostname: '$vm'"
echo "removing old kernel (if exists)"
ssh $vm rm -f /boot/vmlinuz-${kernel_version} /boot/System.map-${kernel_version}
echo "installing kernel"
scp arch/x86/boot/bzImage $vm:/boot/vmlinuz-${kernel_version}
scp System.map $vm:/boot/System.map-${kernel_version}
if $kernel_only; then
echo "doing kernel-only install, done."
return 0
fi
##############################
# modules
##############################
echo "Preparing modules..."
make modules_install INSTALL_MOD_PATH=$output_dir >/dev/null
# cleanup "build" and "source" in case they are soft links
local module_dir="$output_dir/lib/modules/${kernel_version}"
if [[ ! -d $module_dir ]]; then
echo "failed to find module directory: $module_dir"
return 1
fi
echo "removing old modules (if exists)"
# the initramfs
ssh $vm rm -f /boot/*${kernel_version}.img
ssh $vm rm -rf /lib/modules/${kernel_version}
echo "installing modules"
rsync -avl $module_dir $vm:/lib/modules > /dev/null
echo "generating initramfs"
ssh $vm dracut -H --kver $kernel_version
echo "updating grub2"
do_kernel_boot_install "$kernel_version"
if $set_default; then
echo "setting default boot kernel to $kernel_version"
do_kernel_boot_select $vm $kernel_version
fi
echo "all done"
}
do_kernel_boot_select ()
{
local vm="$1"
local version="$2"
if [[ -z "$vm" || -z "$version" ]]; then
cat <<EOF
usage: $prog_name kernel-boot <host> <kernel_version>
Modify grub configuration to boot into <kernel_version> next time.
EOF
return 1
fi
if ! __host_ssh_reachable $vm; then
echo "host $vm not reachable"
return 1
fi
ssh $vm grubby --set-default /boot/vmlinuz-${version}
}
qtool_def_bisect_script=".qtool.bisect"
__auto_bisect_help()
{
cat <<EOF
usage:
# $prog_name auto-bisect generate
Generate the default bisect file $qtool_def_bisect_script
# $prog_name auto-bisect run
Run the bisect script $qtool_def_bisect_script
The script file should contain the following functions:
qtool_bisect_prepare: build codes into binaries, and install
qtool_bisect_run: run scripts to trigger the bug, return 0
means test pass, ~0 means test fail
The logic of bisection will be:
1. qtool_bisect_prepare
2. qtool_bisect_run --> result
3. update result to git
4. finished ? (END) : (goto 1.)
EOF
return 1
}
__auto_bisect_generate_script ()
{
if [[ -e $1 ]]; then
echo "found existing script at $1, please remove before new"
return 1
fi
cat > $1 <<EOF
#!/bin/bash
qtool_bisect_prepare ()
{
echo "Preparing for run..."
# do everything needed for the new run, should include but not
# limited to: build, install, config, etc.
# return 0 on success, 1 on error
return 0
}
qtool_bisect_run ()
{
echo "Running bisect script..."
# run script to see whether test pass
# return 0 on test pass, 1 on fail, 2 on error
return 0
}
EOF
}
__auto_bisect_run()
{
local round=1
local result=0
source $1
while true; do
echo "###############################"
echo "# Round $round of prepare"
echo "###############################"
date
if ! qtool_bisect_prepare; then
echo "failed prepare bisect"
return 1
fi
echo "###############################"
echo "# Round $round of run"
echo "###############################"
date
qtool_bisect_run
result=$?
case $result in
1) git_answer=bad ;;
0) git_answer=good ;;
*)
echo "error happened during running bisect script"
return 1
;;
esac
echo "#######################################"
echo "# Round $round of git update: $git_answer"
echo "#######################################"
git bisect $git_answer | grep -q "first bad"
if [[ $? == 0 ]]; then
# we got the bad one
echo "found bad commit, stop here"
git bisect log | tail -1
break
fi
round=$(( $round + 1 ))
done
return 0
}
do_auto_bisect ()
{
local cmd="$1"
local script="$qtool_def_bisect_script"
if [[ -z "$cmd" ]]; then
__auto_bisect_help
return 1
fi
case $cmd in
generate) __auto_bisect_generate_script $script ;;
run) __auto_bisect_run $script ;;
*) __auto_bisect_help ;;
esac
return $?
}
# Tries to translate a nickname into email address
nick_to_email()
{
local nick="$1"
local alias_book="$HOME/.mutt/aliases"
local info=""
info=$(grep -w $nick $alias_book 2>/dev/null | head -1)
if [[ -z "$info" ]]; then
return
fi
info=${info##*<}
info=${info%%>*}
echo $info
}
do_send_patch ()
{
local maillist="$1"
# always CC myself.
local cclist="$2,$my_email"
shift 2
local patchlist="$@"
local tolist=""
local param=""
if [[ -z "$patchlist" ]]; then
cat <<EOF
usage: $prog_name send-patch <mailing lists> <cc list> <patch1> ...
<mailing list> and <cc list> items should be splitted by comma
(",").
Send patch list to specific mailing list, with all the people CCed
in the CC list.
EOF
return 1
fi
maillist="${maillist//,/ }"
cclist="${cclist//,/ }"
local mlist=""
for mlist in ${maillist}; do
case $mlist in
qemu*) tolist="$tolist qemu-devel@nongnu.org" ;;
rhvirt*) tolist="$tolist rhvirt-patches@redhat.com" ;;
libvir*) tolist="$tolist libvir-list@redhat.com" ;;
kvm) tolist="$tolist kvm@vger.kernel.org" ;;
lkml|kernel)
tolist="$tolist linux-kernel@vger.kernel.org" ;;
# this is specific email address
*@*) tolist="$tolist $mlist" ;;
*) echo "Unknown mailing list: $mlist"; return 1 ;;
esac
done
local item=""
if [[ -z "$tolist" ]]; then
echo "To: cannot be empty"
return 1
fi
echo "Sending To:"
echo
for item in $tolist; do
echo " $item"
param="$param --to=$item"
done
echo
echo "CC To:"
echo
if [[ -n "$cclist" ]]; then
for item in $cclist; do
if [[ "$item" != "*@*" ]]; then
# assume this is a nick
local email=$(nick_to_email $item)
if [[ -n "$email" ]]; then
item="$email"
fi
fi
echo " $item"
param="$param --cc=$item"
done
else
echo " (nobody)"
fi
echo
echo "Patches to send:"
echo
for item in $patchlist; do
echo " $item"
param="$param $item"
done
echo
echo -n "Ready to send? (y/n) "
read y
if [[ $y != y ]]; then
echo "Aborted."
return 0
fi
echo "Sending..."
git send-email --suppress-cc=all $param
echo "Done."
}
do_ssh_host()
{
local host="$1"
if [[ -z "$host" ]]; then
cat <<EOF
usage: $prog_name <host>
SSH to <host> with line buffering. This might be easy to use when
ping has a very big latency from local to <host>.
EOF
return 1
fi
local line=""
cat | ssh $host
}
# this must be done before everything, since we are calling err() to
# quit when check failed
git_root_check()
{
if [[ ! -d ".git" ]]; then
err "Please run command under root dir of git repo"
fi
}
git_no_modify_check ()
{
if git status | grep -qw modified; then
err "Detected modified files, please clean first"
fi
}
git_config_core_editor_check ()
{
local editor=""
if [[ $fake_editor_refcount != 0 ]]; then
# we are in, no need to check
return
fi
editor=$(git config --local core.editor 2>/dev/null)
if [[ -n "$editor" ]]; then
err "Please make sure local core.editor not set"
fi
}
git_fake_editor_check()
{
if pgrep -fa fake-editor &> /dev/null; then
err "Please stop all fake-editor"
fi
}
git_commit_check()
{
local commit="$1"
if [[ -z "$commit" ]]; then
err "commit empty"
fi
if ! git show -s --oneline $commit &> /dev/null; then
err "commit $commit does not exist"
fi
}
git_rebase_pre_check_all()
{
git_root_check
git_no_modify_check
git_config_core_editor_check
git_fake_editor_check
}
git_using_fake_editor()
{
local editor=$(git config --local core.editor 2>/dev/null)
return [[ "$editor" == *fake-editor* ]]
}
git_set_fake_editor()
{
fake_editor_refcount=$(( $fake_editor_refcount + 1 ))
debug "fake editor cnt: $fake_editor_refcount"
if [[ $fake_editor_refcount == 1 ]]; then
debug "setting fake editor"
git config --local core.editor "$prog_name fake-editor"
fi
}
git_unset_fake_editor()
{
fake_editor_refcount=$(( $fake_editor_refcount - 1 ))
debug "fake editor cnt: $fake_editor_refcount"
if [[ $fake_editor_refcount == 0 ]]; then
debug "unsetting fake editor"
git config --local --unset core.editor
fi
}
# this is the file to store which file we are editing on. Current we
# still do not support concurrent operations. However, is that
# matter? I suppose in most cases not, since we are simulating a
# human being from his typing, man!
fake_editing_file=".fake_editor.current_path"
# To play the trick, we trap USR1 signal. When script is ready to do
# anything, we can just signal this process with USR1 to quit