mirror of
https://github.com/tj/git-extras.git
synced 2026-09-11 07:56:18 -04:00
Compare commits
626 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cf28ec559 | ||
|
|
09b77e8ed6 | ||
|
|
4c38988ba9 | ||
|
|
dbf68bac59 | ||
|
|
03569856db | ||
|
|
d11e45f0b1 | ||
|
|
fd2e03cf55 | ||
|
|
5a14757944 | ||
|
|
b450b36036 | ||
|
|
7f36d37283 | ||
|
|
184d37e980 | ||
|
|
e92878b81b | ||
|
|
b9e1d88f2c | ||
|
|
99207eee8e | ||
|
|
9d6319becb | ||
|
|
2bdeb59a73 | ||
|
|
c5a0fdf660 | ||
|
|
8d1c970031 | ||
|
|
c06fad59d2 | ||
|
|
65dd36ae62 | ||
|
|
967e339f91 | ||
|
|
ae3d30fb6e | ||
|
|
032090e847 | ||
|
|
5367101f2e | ||
|
|
6ed2643252 | ||
|
|
b9bd309a87 | ||
|
|
6f4cf0c1bb | ||
|
|
f5c48e4842 | ||
|
|
daf9d4148e | ||
|
|
eb34655aea | ||
|
|
1f1320f4d6 | ||
|
|
4d05955bc5 | ||
|
|
b57eb8b92a | ||
|
|
cf6d40d446 | ||
|
|
14586c7da0 | ||
|
|
5c9a7a2533 | ||
|
|
215382b12d | ||
|
|
13cbdfc51e | ||
|
|
3ddc315a76 | ||
|
|
c63b46a33b | ||
|
|
d4cd272556 | ||
|
|
783812b627 | ||
|
|
c49ca70814 | ||
|
|
18ecffef5e | ||
|
|
aadf5f874b | ||
|
|
18d1017325 | ||
|
|
a841845d9a | ||
|
|
8eed5f49ad | ||
|
|
64687599ae | ||
|
|
8ce63003c0 | ||
|
|
1a9b0c2ab4 | ||
|
|
82cc37d0f5 | ||
|
|
df53711fc7 | ||
|
|
e691826b4b | ||
|
|
fdaca2cd26 | ||
|
|
3db275cbe2 | ||
|
|
1345c5977d | ||
|
|
2f4b57d52b | ||
|
|
c23da8ae9c | ||
|
|
f1f50ac5ba | ||
|
|
0989b773de | ||
|
|
65203312c1 | ||
|
|
a2c424aada | ||
|
|
d291b2775c | ||
|
|
fb0bc6950b | ||
|
|
21a086c009 | ||
|
|
8eb1d72f07 | ||
|
|
233686d2a2 | ||
|
|
5c3fb1b062 | ||
|
|
5ca9360109 | ||
|
|
16d1445271 | ||
|
|
4d2d393142 | ||
|
|
ffee88a246 | ||
|
|
da03b7b133 | ||
|
|
f0671e2733 | ||
|
|
2c161aae02 | ||
|
|
f0b9e18e54 | ||
|
|
c32869de37 | ||
|
|
e1a4e7195d | ||
|
|
b766a655ce | ||
|
|
e34dea9b7a | ||
|
|
3bac05e690 | ||
|
|
a7ad306a6b | ||
|
|
dcb8bd6c54 | ||
|
|
a1bf09e8e0 | ||
|
|
615ea971cb | ||
|
|
e692b9619a | ||
|
|
a17d2fb15d | ||
|
|
52897f73b9 | ||
|
|
5f19424a50 | ||
|
|
b54d8c7c50 | ||
|
|
9049967232 | ||
|
|
cdbcef3227 | ||
|
|
a4b637346d | ||
|
|
1843afb38a | ||
|
|
28b48c7432 | ||
|
|
2c2bd07693 | ||
|
|
e39b1ab6a6 | ||
|
|
058cb0763e | ||
|
|
3d964687fc | ||
|
|
ca18c6e552 | ||
|
|
cf9fe2d23f | ||
|
|
c13ab52138 | ||
|
|
58c5b50514 | ||
|
|
5020890c34 | ||
|
|
4d8060e8cb | ||
|
|
7a65311b1c | ||
|
|
186bbdcb87 | ||
|
|
bfef91f30a | ||
|
|
78c5ca28a0 | ||
|
|
90221f2149 | ||
|
|
263dcb4a79 | ||
|
|
cc1c0e1a38 | ||
|
|
04bf403861 | ||
|
|
5fe4226865 | ||
|
|
bddb987383 | ||
|
|
df1997c0dc | ||
|
|
954ba1fb7c | ||
|
|
e4152db482 | ||
|
|
dee7c7f1d0 | ||
|
|
e793f8979b | ||
|
|
d2dde69d14 | ||
|
|
49650eda62 | ||
|
|
76cd9bdf45 | ||
|
|
f95e339c03 | ||
|
|
db71be578e | ||
|
|
cb29c62c65 | ||
|
|
47e940272a | ||
|
|
8af43892f6 | ||
|
|
34609bd800 | ||
|
|
0b5d2cce55 | ||
|
|
d7fb4495f1 | ||
|
|
5e543cb620 | ||
|
|
3a258cbaad | ||
|
|
ba9e6f0c5c | ||
|
|
5d7ed18c50 | ||
|
|
2d221c1b67 | ||
|
|
f5bcbe5b0d | ||
|
|
6d73f74f83 | ||
|
|
bbf90c4623 | ||
|
|
2fca5edb7d | ||
|
|
d7f0fcc546 | ||
|
|
df825715ba | ||
|
|
2d5b12c01f | ||
|
|
753555744b | ||
|
|
d5eaa1895b | ||
|
|
b372bab52a | ||
|
|
a008308267 | ||
|
|
5d0fe32617 | ||
|
|
75d0dbff6d | ||
|
|
9c3343d1ee | ||
|
|
82daeb7c94 | ||
|
|
73fbf8e08d | ||
|
|
36e6de10c8 | ||
|
|
e1138b8190 | ||
|
|
589ef33e35 | ||
|
|
e4e7310c49 | ||
|
|
4611b24320 | ||
|
|
04eb5c04e6 | ||
|
|
e29dcbb023 | ||
|
|
d34fa527a0 | ||
|
|
35100b4e6f | ||
|
|
cd348dad21 | ||
|
|
aea25d2d4b | ||
|
|
4fff03f429 | ||
|
|
7c4152ce83 | ||
|
|
960ec8d2fd | ||
|
|
1426002037 | ||
|
|
193a6dd2a8 | ||
|
|
2d1aa6daea | ||
|
|
71af8cec42 | ||
|
|
3ac3c54b06 | ||
|
|
584e0283f5 | ||
|
|
62aa91f221 | ||
|
|
25b5d734c7 | ||
|
|
06aa5dbb1f | ||
|
|
8058b1718f | ||
|
|
956781ac1b | ||
|
|
3b9ea70e09 | ||
|
|
6e6f59b264 | ||
|
|
01e2494294 | ||
|
|
e3c8359e4e | ||
|
|
9accb6bb04 | ||
|
|
66961ad491 | ||
|
|
81665422b2 | ||
|
|
48ef95d104 | ||
|
|
bf240df083 | ||
|
|
d597f684b1 | ||
|
|
7acc1de7fa | ||
|
|
8a6e39d589 | ||
|
|
006549d661 | ||
|
|
233b3f0dd4 | ||
|
|
fdf89cd93f | ||
|
|
95c54f6cdb | ||
|
|
34237934d4 | ||
|
|
8c744569c4 | ||
|
|
458cb9fdad | ||
|
|
12b27415ea | ||
|
|
0701c9e4d7 | ||
|
|
42fd8a9e72 | ||
|
|
311edadd55 | ||
|
|
d8dd44d8eb | ||
|
|
41df8dc7f3 | ||
|
|
48beb58670 | ||
|
|
5e07a96c57 | ||
|
|
3ef09dac72 | ||
|
|
799aa6e19a | ||
|
|
aa6b547f55 | ||
|
|
f334dff09c | ||
|
|
055110dd35 | ||
|
|
745c7ab500 | ||
|
|
7cb5c55e13 | ||
|
|
0d7334a63f | ||
|
|
5754b81047 | ||
|
|
2aadd3ed41 | ||
|
|
d2a09c1753 | ||
|
|
c7756455cb | ||
|
|
4fcd233bf2 | ||
|
|
a73c19c14b | ||
|
|
6bf2b0bc4c | ||
|
|
d17f02979e | ||
|
|
716459f79d | ||
|
|
02263ca949 | ||
|
|
613b909765 | ||
|
|
f556430122 | ||
|
|
9390d450aa | ||
|
|
3d6f9456a3 | ||
|
|
27ee8f0132 | ||
|
|
678a0c2e71 | ||
|
|
11a2801231 | ||
|
|
d7565ee844 | ||
|
|
02bbdba792 | ||
|
|
f513f58d9e | ||
|
|
c036c9cc22 | ||
|
|
42458d838b | ||
|
|
b2794c18a1 | ||
|
|
760428fa46 | ||
|
|
76a3c1f28d | ||
|
|
2025ebfb96 | ||
|
|
5467c2ff5a | ||
|
|
843b958c9a | ||
|
|
7aa178acc9 | ||
|
|
2d722c93b8 | ||
|
|
b772251074 | ||
|
|
777fcade17 | ||
|
|
e370b19190 | ||
|
|
82b33ac4c7 | ||
|
|
fff5fde917 | ||
|
|
edffba3c2a | ||
|
|
11640196b2 | ||
|
|
6b0ef19caf | ||
|
|
d914ed3a89 | ||
|
|
b2743d785f | ||
|
|
2370a4e27b | ||
|
|
2fdd664594 | ||
|
|
b33ec65e5f | ||
|
|
3f2ad3fa8f | ||
|
|
e0101470ca | ||
|
|
86bb1a6764 | ||
|
|
96890821b0 | ||
|
|
0c0be92445 | ||
|
|
cc9b972e25 | ||
|
|
760e64b864 | ||
|
|
f6fa554077 | ||
|
|
bc8876b063 | ||
|
|
c30ac80d72 | ||
|
|
4de7b34c9f | ||
|
|
0edcf8b7e6 | ||
|
|
5feb0db212 | ||
|
|
703a8481c6 | ||
|
|
3413eeb058 | ||
|
|
a7b7f05b5b | ||
|
|
a07c5ece5d | ||
|
|
296f2a7ad2 | ||
|
|
e9708ba9f4 | ||
|
|
3c91de7a1e | ||
|
|
3c632d9e32 | ||
|
|
3821b2c537 | ||
|
|
ffad35c8db | ||
|
|
d28ad34bcb | ||
|
|
b8e58ee9ba | ||
|
|
b225ed4934 | ||
|
|
30383f9551 | ||
|
|
1f72512ef0 | ||
|
|
b8b02d8ef1 | ||
|
|
d8b1007f68 | ||
|
|
64993dbc7f | ||
|
|
4a4c5296f3 | ||
|
|
206fe7ccfb | ||
|
|
f1eab6a8a7 | ||
|
|
cd8fe4f6e2 | ||
|
|
bebcbd11ca | ||
|
|
fb1e5db6d7 | ||
|
|
910242c92b | ||
|
|
48b2c8fd37 | ||
|
|
d86abec6a2 | ||
|
|
1894f453f6 | ||
|
|
c35c84a279 | ||
|
|
a62a67ef5f | ||
|
|
d0c40851c0 | ||
|
|
52ada403ef | ||
|
|
b9786c763f | ||
|
|
6880198fda | ||
|
|
687a5607dc | ||
|
|
7b1a062e93 | ||
|
|
6a15a5339b | ||
|
|
a424d0915e | ||
|
|
458727bafb | ||
|
|
d7b527c1ae | ||
|
|
0fad3cfd03 | ||
|
|
7d9e05062e | ||
|
|
c8d9374491 | ||
|
|
f3028db073 | ||
|
|
d73bfbb98b | ||
|
|
42e1f9d5c6 | ||
|
|
b7841ad3aa | ||
|
|
4ea6cf0221 | ||
|
|
8f60fb29f4 | ||
|
|
96b95392ac | ||
|
|
16c1f0d3dc | ||
|
|
c675d0afb8 | ||
|
|
4c82bbecc3 | ||
|
|
faa56b1bcb | ||
|
|
e6caa931a5 | ||
|
|
6b45d4322b | ||
|
|
e92d54cadb | ||
|
|
444722cf38 | ||
|
|
b2b5c97105 | ||
|
|
d3772eb469 | ||
|
|
41732e4113 | ||
|
|
18bcfcd38e | ||
|
|
aa5a380996 | ||
|
|
68e6a15401 | ||
|
|
05ce91fb9e | ||
|
|
d2a159ed25 | ||
|
|
f66f7e484f | ||
|
|
fca42774f8 | ||
|
|
ef13ed06a3 | ||
|
|
fa35cdab99 | ||
|
|
8ed7da3f02 | ||
|
|
54f8205478 | ||
|
|
19531065ca | ||
|
|
4f5b5ccd48 | ||
|
|
ad8c77bfa3 | ||
|
|
8490a50304 | ||
|
|
aa174b3905 | ||
|
|
c006e5ece8 | ||
|
|
171d06965e | ||
|
|
6d9f861d0e | ||
|
|
041e3fd8da | ||
|
|
35d544b8c6 | ||
|
|
da34968ec3 | ||
|
|
35d44c97cc | ||
|
|
317eea64db | ||
|
|
2ef6ea4b96 | ||
|
|
c41f0164cc | ||
|
|
3cc866dd97 | ||
|
|
2929c41637 | ||
|
|
64e76db350 | ||
|
|
74fe4f6aba | ||
|
|
2e610158a8 | ||
|
|
65be1e5f8f | ||
|
|
9683fb6510 | ||
|
|
964663f1a4 | ||
|
|
e9668cc04b | ||
|
|
8a487271cb | ||
|
|
1a4cd4ca3d | ||
|
|
e116406ff1 | ||
|
|
34ba1eb083 | ||
|
|
6a455e7d29 | ||
|
|
6afc2fb4a8 | ||
|
|
c56b61d2bd | ||
|
|
c386733efd | ||
|
|
ecf0f4847f | ||
|
|
e031dc54e9 | ||
|
|
8e519e3e66 | ||
|
|
c52ba59d10 | ||
|
|
9ee5d06469 | ||
|
|
ec417833ee | ||
|
|
b4b81e290e | ||
|
|
c92f03d303 | ||
|
|
af06d49c33 | ||
|
|
e7270b3667 | ||
|
|
87cff6d5cb | ||
|
|
aaf9ef343b | ||
|
|
73cc666030 | ||
|
|
eaeeef3b80 | ||
|
|
73bb1e0f59 | ||
|
|
238038139d | ||
|
|
85e1c4ff0a | ||
|
|
a0d7039482 | ||
|
|
49a34037ad | ||
|
|
2dc9c26e36 | ||
|
|
2f3772b09a | ||
|
|
4a7fcb7d1c | ||
|
|
6e77d950eb | ||
|
|
572ae0dd46 | ||
|
|
a0a32abc0b | ||
|
|
edf7b060d2 | ||
|
|
2076ac9c38 | ||
|
|
4c9d59059e | ||
|
|
97de52a3be | ||
|
|
a994411e58 | ||
|
|
f06ba9f70b | ||
|
|
5a79873bd6 | ||
|
|
27f42beb81 | ||
|
|
6ccd2fa439 | ||
|
|
9dca93eeb2 | ||
|
|
11cc90d5f6 | ||
|
|
90495d0d5e | ||
|
|
f2136ec433 | ||
|
|
a04f27b255 | ||
|
|
f27875d5bc | ||
|
|
cb66d36434 | ||
|
|
d4ff718ac3 | ||
|
|
23fcac8c4f | ||
|
|
fa8165ba7e | ||
|
|
db9ca55c71 | ||
|
|
672e330f47 | ||
|
|
852e630314 | ||
|
|
54199b8d08 | ||
|
|
edc4814ced | ||
|
|
8f133a32ba | ||
|
|
4eb4e7ffdb | ||
|
|
acf4123234 | ||
|
|
9e843da98d | ||
|
|
e8ee36733f | ||
|
|
db3389a3cc | ||
|
|
3583825137 | ||
|
|
31079e0a60 | ||
|
|
45eed8f166 | ||
|
|
c038741194 | ||
|
|
63eaba4572 | ||
|
|
f2a3e50cab | ||
|
|
91b3e47243 | ||
|
|
b85fc019ca | ||
|
|
2c9b071fde | ||
|
|
6e83793dda | ||
|
|
2a5c08aaf8 | ||
|
|
5afaa731e0 | ||
|
|
bea25d1ceb | ||
|
|
739ca052b4 | ||
|
|
629fc03581 | ||
|
|
dd0337c3cf | ||
|
|
6b4188404b | ||
|
|
79ae65fe64 | ||
|
|
f6cf59c380 | ||
|
|
362a0c29ae | ||
|
|
be50c2f3f5 | ||
|
|
3917c774ec | ||
|
|
b708a8a4d3 | ||
|
|
80cd52701e | ||
|
|
265b03e400 | ||
|
|
4b9f1619f6 | ||
|
|
6438dee914 | ||
|
|
2a65a7aa8b | ||
|
|
2c689db6d7 | ||
|
|
2067d26276 | ||
|
|
aefa3c9ff6 | ||
|
|
3b2f6611d6 | ||
|
|
ecff5e1a3c | ||
|
|
eb33c9bf5a | ||
|
|
bd8fa031cc | ||
|
|
4d5b53aea2 | ||
|
|
69e96cba95 | ||
|
|
e836b6ce4c | ||
|
|
16bb0df9f9 | ||
|
|
bb7d389084 | ||
|
|
7a66b157f0 | ||
|
|
2fe53ac791 | ||
|
|
d0636d2b07 | ||
|
|
41ebe0e0bf | ||
|
|
6b8729a819 | ||
|
|
29f6d0e0e0 | ||
|
|
83349426d1 | ||
|
|
45647fdab3 | ||
|
|
67c2bf595f | ||
|
|
4d4b87ccb8 | ||
|
|
292149c46f | ||
|
|
704b1b4fdd | ||
|
|
a6a1754093 | ||
|
|
b0d3a77ac0 | ||
|
|
46ea733983 | ||
|
|
f4e313d3f2 | ||
|
|
1924eb9197 | ||
|
|
952d1eb76f | ||
|
|
f2934bd0bd | ||
|
|
677f570f34 | ||
|
|
227c19bb67 | ||
|
|
1f605df053 | ||
|
|
17d1a471a4 | ||
|
|
f00c1389d6 | ||
|
|
4f35f8cad8 | ||
|
|
5ea4a626a6 | ||
|
|
4bb273a802 | ||
|
|
4a47ea034b | ||
|
|
7b9fbc4389 | ||
|
|
2883b67644 | ||
|
|
f788c789f3 | ||
|
|
3e8c3ab0c4 | ||
|
|
ff991ac99e | ||
|
|
a14547e92f | ||
|
|
203f5b48ad | ||
|
|
7bdfb1f55f | ||
|
|
cf3e1e7e7d | ||
|
|
fd36fc0ea2 | ||
|
|
eb9ddbe8a9 | ||
|
|
256044c04f | ||
|
|
0cd55d88bf | ||
|
|
bba1f964b8 | ||
|
|
7015501161 | ||
|
|
ff0b1cfb0b | ||
|
|
d90b02ce22 | ||
|
|
5b62b4a884 | ||
|
|
f7d127b360 | ||
|
|
1d7d4c2446 | ||
|
|
a85d5f57bb | ||
|
|
1a414f5512 | ||
|
|
e24c1302f4 | ||
|
|
883199093c | ||
|
|
6a3d61ff12 | ||
|
|
c4e94a4130 | ||
|
|
a921e88d76 | ||
|
|
a56e14488e | ||
|
|
55cefa9021 | ||
|
|
3484df9ca4 | ||
|
|
66288296b2 | ||
|
|
5bffcb4c89 | ||
|
|
4867571df0 | ||
|
|
a2250dd205 | ||
|
|
f053fc48db | ||
|
|
ebdebadd3b | ||
|
|
fa5017a4a5 | ||
|
|
d4de567ca7 | ||
|
|
f2587339cf | ||
|
|
2dc5d5104e | ||
|
|
500ea2b119 | ||
|
|
df06f3e3bd | ||
|
|
defa89c794 | ||
|
|
58d13f88d4 | ||
|
|
85248595e3 | ||
|
|
0b49719ae4 | ||
|
|
3080b81bcc | ||
|
|
c03e0ad841 | ||
|
|
ac9e35735b | ||
|
|
a26f1e6f8e | ||
|
|
7d3ab83ec3 | ||
|
|
eae05bbb98 | ||
|
|
820b7cd41b | ||
|
|
1712db2146 | ||
|
|
c3c0bdfbbb | ||
|
|
4b09e94853 | ||
|
|
26447e5bbe | ||
|
|
f4f7c1e3b5 | ||
|
|
6d1982d6cf | ||
|
|
ecdd665f1c | ||
|
|
a214c93ad4 | ||
|
|
044b06b6cb | ||
|
|
5b0a9d22b6 | ||
|
|
eecfb9af47 | ||
|
|
39df4dfa64 | ||
|
|
f355fb4c94 | ||
|
|
7a89f7c6e4 | ||
|
|
99238bdf0a | ||
|
|
f29dad042c | ||
|
|
87b7c959b4 | ||
|
|
088ea058d9 | ||
|
|
c4db208ca4 | ||
|
|
44dc1b9dba | ||
|
|
9f806bb828 | ||
|
|
49fab7267b | ||
|
|
3730339067 | ||
|
|
a815bb0fbd | ||
|
|
13fa83b24e | ||
|
|
eb27b24b73 | ||
|
|
5f961fe8e3 | ||
|
|
a5edb6d8b7 | ||
|
|
7e10554506 | ||
|
|
700580aae6 | ||
|
|
6e7766f5a4 | ||
|
|
3b7637ce40 | ||
|
|
edebba89b9 | ||
|
|
8b992cc9bc | ||
|
|
7393fb41ed | ||
|
|
b7d507567b | ||
|
|
2590998ee0 | ||
|
|
8545d97b8d | ||
|
|
424ec4d2a5 | ||
|
|
1cb4979ed7 | ||
|
|
a347e49178 | ||
|
|
e4aaf9f697 | ||
|
|
792b697450 | ||
|
|
4e1cd73e59 | ||
|
|
b7a1a0d8b2 | ||
|
|
d681b972cc | ||
|
|
28819e388e | ||
|
|
f30005acfd | ||
|
|
691d640b2d | ||
|
|
0f76863d9b | ||
|
|
a4f85ae6ff | ||
|
|
787ee2c997 | ||
|
|
d34b6ae0bc | ||
|
|
f5c26c7fb3 | ||
|
|
731e9d8db9 | ||
|
|
8b58e570cb | ||
|
|
ba6d991851 | ||
|
|
bd6a1fcafe | ||
|
|
317e546b0a | ||
|
|
a6732ef6db | ||
|
|
3802e83e79 | ||
|
|
658f133691 | ||
|
|
6f416b20c3 | ||
|
|
f7514bebbf | ||
|
|
d024755718 | ||
|
|
0ab9b47371 | ||
|
|
4325238aee | ||
|
|
9785199036 | ||
|
|
c27a3505dc | ||
|
|
ee14220f66 | ||
|
|
1b94ca130c | ||
|
|
fd2151f9bb | ||
|
|
6add6488d7 | ||
|
|
22a44eb206 | ||
|
|
420c678526 | ||
|
|
c53edf00da | ||
|
|
ebf7903d0e |
30
.editorconfig
Normal file
30
.editorconfig
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.patch]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.html]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.bats]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[tests/*.sh]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
2
.github/.ignore_words
vendored
Normal file
2
.github/.ignore_words
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
gool
|
||||||
|
Cant
|
||||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Note
|
||||||
|
|
||||||
|
* Mark the PR as draft until it's ready to be reviewed.
|
||||||
|
* Please update the documentation to reflect the changes made in the PR.
|
||||||
|
* If the command is covered by tests under ./tests, please add/update tests for any changes unless you have a good reason.
|
||||||
|
* Make a new commit to resolve conversations instead of `push -f`.
|
||||||
|
* To resolve merge conflicts, merge from the `main` branch instead of rebasing over `main`.
|
||||||
|
* Please wait for the reviewer to mark a conversation as resolved.
|
||||||
|
|
||||||
|
-->
|
||||||
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
116
.github/workflows/ci.yml
vendored
Normal file
116
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
name: ci
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out code.
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: 'Get Changed Files'
|
||||||
|
id: 'files'
|
||||||
|
run: |
|
||||||
|
CHANGED_FILES=$(git diff --name-only "${BASE_SHA:-$BEFORE_SHA}...${HEAD_SHA:-$GITHUB_REF}")
|
||||||
|
echo "::notice::Changed files: $CHANGED_FILES"
|
||||||
|
{
|
||||||
|
echo "added_modified<<EOF"
|
||||||
|
echo "$CHANGED_FILES"
|
||||||
|
echo "EOF"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
env:
|
||||||
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||||
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
|
BEFORE_SHA: "${{ github.event.before }}"
|
||||||
|
- name: 'Check Generated Manuals'
|
||||||
|
run: |
|
||||||
|
failed=0
|
||||||
|
while IFS= read -r source; do
|
||||||
|
case "$source" in
|
||||||
|
man/git-*.md)
|
||||||
|
stem=${source%.md}
|
||||||
|
for generated in "$stem.1" "$stem.html"; do
|
||||||
|
if ! grep -Fqx -- "$generated" <<< "$CHANGED_FILES"; then
|
||||||
|
echo "::error file=$source::$generated must be updated with $source"
|
||||||
|
failed=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <<< "$CHANGED_FILES"
|
||||||
|
exit "$failed"
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: "${{ steps.files.outputs.added_modified }}"
|
||||||
|
- uses: 'actions/setup-go@v7'
|
||||||
|
with:
|
||||||
|
go-version: '1.20'
|
||||||
|
- name: 'Install EditorConfig Lint'
|
||||||
|
run: go install 'github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@latest'
|
||||||
|
- name: 'Check EditorConfig Lint'
|
||||||
|
run: echo "$FILES" | xargs ~/go/bin/editorconfig-checker
|
||||||
|
env:
|
||||||
|
# NOTE: use env to pass the output in order to avoid possible injection attacks
|
||||||
|
FILES: "${{ steps.files.outputs.added_modified }}"
|
||||||
|
- name: checkstyle
|
||||||
|
run: ./scripts/checkstyle.py
|
||||||
|
- name: Shellcheck
|
||||||
|
run: shellcheck --severity=error bin/* ./*.sh
|
||||||
|
|
||||||
|
typo:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out code.
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
- name: Install codespell
|
||||||
|
run: python3 -m pip install codespell==2.4
|
||||||
|
- name: spell check
|
||||||
|
run: |
|
||||||
|
git grep --cached -l '' . | \
|
||||||
|
grep -v -e 'History\.md' -e 'AUTHORS' -e 'man/.*\.1' -e 'man/.*\.html' | \
|
||||||
|
xargs codespell --ignore-words=.github/.ignore_words
|
||||||
|
|
||||||
|
test-bats:
|
||||||
|
name: 'Test with Bats'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Setup Bats
|
||||||
|
id: setup-bats
|
||||||
|
uses: bats-core/bats-action@4.0.0
|
||||||
|
with:
|
||||||
|
bats-version: 'v1.8.1'
|
||||||
|
- name: Test with Bats
|
||||||
|
env:
|
||||||
|
BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }}
|
||||||
|
TERM: xterm
|
||||||
|
run: bats ./tests
|
||||||
|
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macos-latest
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
steps:
|
||||||
|
- name: Check out code.
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
- name: Linux Install
|
||||||
|
if: matrix.platform == 'ubuntu-latest'
|
||||||
|
run: sudo apt-get install -y bsdmainutils
|
||||||
|
- name: Script
|
||||||
|
run: ./check_integrity.sh
|
||||||
|
- name: Brew release
|
||||||
|
if: matrix.platform == 'macos-latest'
|
||||||
|
run: |
|
||||||
|
mkdir ../release && git archive --format=tar.gz HEAD > ../release/git-extras-release.tar.gz
|
||||||
|
cd ../release
|
||||||
|
tar -xzf git-extras-release.tar.gz && make PREFIX=$(pwd) INSTALL_VIA=brew
|
||||||
|
./bin/git-extras update | grep "brew upgrade git-extras"
|
||||||
18
.github/workflows/stale.yaml
vendored
Normal file
18
.github/workflows/stale.yaml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
name: "Maintenance: Close Stale PRs"
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "30 1 * * *"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: "write"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
if: github.repository_owner == 'tj'
|
||||||
|
steps:
|
||||||
|
- uses: "actions/stale@v11"
|
||||||
|
with:
|
||||||
|
close-pr-message: "This PR was closed because it has been stalled for 365 days with no activity. Feel free to make a new PR if you wish to continue"
|
||||||
|
days-before-pr-stale: 350
|
||||||
|
days-before-pr-close: 15
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
coverage/
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "vendor/bats-all"]
|
||||||
|
path = vendor/bats-all
|
||||||
|
url = https://github.com/hyperupcall/bats-all
|
||||||
5
.pytest.ini
Normal file
5
.pytest.ini
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
[pytest]
|
||||||
|
minversion = 7.4
|
||||||
|
addopts = -ra -q
|
||||||
|
testpaths = tests
|
||||||
|
faulthandler_timeout = 5
|
||||||
10
.travis.yml
10
.travis.yml
|
|
@ -1,10 +0,0 @@
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
script:
|
|
||||||
- ./check_integrity.sh $(find bin | cut -b 5- | xargs)
|
|
||||||
# Test for the brew release
|
|
||||||
- mkdir ../release && git archive --format=tar.gz HEAD > ../release/git-extras-release.tar.gz
|
|
||||||
- cd ../release
|
|
||||||
- tar -xzf git-extras-release.tar.gz && make PREFIX=$(pwd) INSTALL_VIA=brew
|
|
||||||
- ./bin/git-extras update | grep "brew upgrade git-extras"
|
|
||||||
249
AUTHORS
249
AUTHORS
|
|
@ -1,191 +1,296 @@
|
||||||
git-extras is written and maintained by TJ Holowaychuk and
|
git-extras is written and maintained by various contributors:
|
||||||
various contributors:
|
|
||||||
|
|
||||||
Development Lead
|
|
||||||
````````````````
|
|
||||||
- TJ Holowaychuk <tj@vision-media.ca>
|
|
||||||
|
|
||||||
Maintainers
|
Maintainers
|
||||||
```````````
|
```````````
|
||||||
|
- TJ Holowaychuk <tj@vision-media.ca>
|
||||||
- Hemanth.HM <hemanth.hm@gmail.com>
|
- Hemanth.HM <hemanth.hm@gmail.com>
|
||||||
- Nimit Kalra <me@nimit.io>
|
- Nimit Kalra <me@nimit.io>
|
||||||
- Nicolai Skogheim <nicolai.skogheim@gmail.com>
|
- Nicolai Skogheim <nicolai.skogheim@gmail.com>
|
||||||
- spacewander <spacewanderlzx@gmail.com>
|
- spacewander <spacewanderlzx@gmail.com>
|
||||||
|
- Edwin Kofler <edwin@kofler.dev>
|
||||||
|
- Leroy <vanpipy@gmail.com>
|
||||||
|
|
||||||
Patches and Suggestions
|
Patches and Suggestions
|
||||||
```````````````````````
|
```````````````````````
|
||||||
- Jonhnny Weslley
|
- Jonhnny Weslley
|
||||||
|
- Tobias Fendin
|
||||||
- LeeW
|
- LeeW
|
||||||
|
- timfeirg
|
||||||
- Niklas Schlimm
|
- Niklas Schlimm
|
||||||
- nickl-
|
- nickl-
|
||||||
- timfeirg
|
|
||||||
- CJ
|
- CJ
|
||||||
|
- Paul Wise
|
||||||
- Damien Tardy-Panis
|
- Damien Tardy-Panis
|
||||||
- Mark Eissler
|
- Mark Eissler
|
||||||
|
- Yuriy VG
|
||||||
- Leila Muhtasib
|
- Leila Muhtasib
|
||||||
- Richard Fearn
|
- Richard Fearn
|
||||||
|
- Bill Wood
|
||||||
- Jesús Espino
|
- Jesús Espino
|
||||||
- Kenneth Reitz
|
- Rémy HUBSCHER
|
||||||
- David Baumgold
|
- go2null
|
||||||
- Andrew Janke
|
- Andrew Janke
|
||||||
|
- David Baumgold
|
||||||
- Jan Schulz
|
- Jan Schulz
|
||||||
|
- Kenneth Reitz
|
||||||
- Mark Pitman
|
- Mark Pitman
|
||||||
|
- Peter Benjamin
|
||||||
|
- Günther Grill
|
||||||
- Luke Childs
|
- Luke Childs
|
||||||
- Sasha Khamkov
|
- Sasha Khamkov
|
||||||
|
- equt
|
||||||
|
- oikarinen
|
||||||
|
- vyas
|
||||||
|
- Don Harper
|
||||||
|
- Pierre Ayoub
|
||||||
- Robin Winslow
|
- Robin Winslow
|
||||||
|
- Ross Smith II
|
||||||
- Yi EungJun
|
- Yi EungJun
|
||||||
- grindhold
|
- grindhold
|
||||||
- NANRI
|
- wyattscarpenter
|
||||||
- Chris Hall
|
|
||||||
- Damian Krzeminski
|
|
||||||
- Aggelos Orfanakos
|
- Aggelos Orfanakos
|
||||||
- Camille Reynders
|
- Camille Reynders
|
||||||
- Rico Sta. Cruz
|
- Carlos Prado
|
||||||
- Rémy HUBSCHER
|
- Chris Hall
|
||||||
|
- Damian Krzeminski
|
||||||
|
- NANRI
|
||||||
- Alexander Krasnukhin
|
- Alexander Krasnukhin
|
||||||
- Takuma Yamaguchi
|
|
||||||
- Phally
|
- Phally
|
||||||
- Julio Napurí
|
- Rico Sta. Cruz
|
||||||
|
- Takuma Yamaguchi
|
||||||
|
- thomas menzel
|
||||||
|
- vr8ce
|
||||||
|
- Brian J Brennan
|
||||||
|
- Dominik Gedon
|
||||||
- Hogan Long
|
- Hogan Long
|
||||||
- Ivan Malopinsky
|
- Ivan Malopinsky
|
||||||
- Brian J Brennan
|
- John Bachir
|
||||||
- wooorm
|
|
||||||
- Philipp Klose
|
|
||||||
- Jonathan "Duke" Leto
|
- Jonathan "Duke" Leto
|
||||||
|
- Julio Napurí
|
||||||
|
- Justin Dugger
|
||||||
- Nils Winkler
|
- Nils Winkler
|
||||||
|
- Philipp Klose
|
||||||
|
- Raphael Boidol
|
||||||
|
- Richard Russon
|
||||||
|
- Sam Bostock
|
||||||
|
- Vladimir Jimenez
|
||||||
- gisphm
|
- gisphm
|
||||||
- William Montgomery
|
- jacobherrington
|
||||||
- David Rogers
|
- overengineer
|
||||||
- Ye Lin Aung
|
|
||||||
- Andre Cerqueira
|
|
||||||
- Gert Van Gool
|
|
||||||
- Kylie McClain
|
|
||||||
- Curtis McEnroe
|
|
||||||
- Guillaume Seren
|
|
||||||
- go2null
|
|
||||||
- Hozefa
|
|
||||||
- Paul Schreiber
|
|
||||||
- Patryk Małek
|
|
||||||
- Newell Zhu
|
|
||||||
- Marc Harter
|
|
||||||
- Nate Jones
|
|
||||||
- phigoro
|
- phigoro
|
||||||
- soffolk
|
- wooorm
|
||||||
- Alexis GRIMALDI
|
- Alexis GRIMALDI
|
||||||
- Domenico Rotiroti
|
|
||||||
- Devin Withers
|
|
||||||
- Jean Jordaan
|
|
||||||
- Matiss
|
|
||||||
- Allan Odgaard
|
- Allan Odgaard
|
||||||
|
- Andre Cerqueira
|
||||||
|
- Austin Ziegler
|
||||||
|
- Beth Skurrie
|
||||||
|
- Brice Dutheil
|
||||||
|
- Curtis McEnroe
|
||||||
|
- Daniele Paolella
|
||||||
|
- David Rogers
|
||||||
|
- Devin Withers
|
||||||
|
- Domenico Rotiroti
|
||||||
- Evan Grim
|
- Evan Grim
|
||||||
- Tom Vincent
|
|
||||||
- Justin Guenther
|
|
||||||
- Richard Littauer
|
|
||||||
- Florian H
|
- Florian H
|
||||||
|
- Gert Van Gool
|
||||||
|
- Guillaume Seren
|
||||||
|
- Hozefa
|
||||||
|
- Igor Ostapenko
|
||||||
|
- Jean Jordaan
|
||||||
|
- Joshua Li
|
||||||
|
- Justin Guenther
|
||||||
|
- Kylie McClain
|
||||||
|
- Marc Harter
|
||||||
|
- Matiss
|
||||||
|
- Nate Jones
|
||||||
|
- Newell Zhu
|
||||||
|
- Patryk Małek
|
||||||
|
- Paul Schreiber
|
||||||
|
- Richard Littauer
|
||||||
|
- Tjaart van der Walt
|
||||||
|
- Tom Vincent
|
||||||
- Wil Moore III
|
- Wil Moore III
|
||||||
- Stefan VanBuren
|
- William Montgomery
|
||||||
- Stephen Mathieson
|
- Ye Lin Aung
|
||||||
- Steve Mao
|
- luozexuan
|
||||||
- Todd Wolfson
|
- roxchgt
|
||||||
- Tom Ashworth
|
- soffolk
|
||||||
- Tony
|
- 0xflotus
|
||||||
- TweeKane
|
- Abdullah
|
||||||
- Valérian Galliat
|
|
||||||
- Xiaopei Li
|
|
||||||
- creynders
|
|
||||||
- Adam Parkin
|
- Adam Parkin
|
||||||
- eszabpt
|
|
||||||
- jykntr
|
|
||||||
- meza
|
|
||||||
- neydroid
|
|
||||||
- nulltask
|
|
||||||
- zeroDivisible
|
|
||||||
- Étienne BERSAC
|
|
||||||
- ☃ pitr
|
|
||||||
- dead-horse
|
|
||||||
- Adriaan Zonnenberg
|
- Adriaan Zonnenberg
|
||||||
- Akim Demaille
|
- Akim Demaille
|
||||||
|
- Alessandro Pagiaro
|
||||||
- Alex McHale
|
- Alex McHale
|
||||||
|
- Aloxaf
|
||||||
- Amir Tocker
|
- Amir Tocker
|
||||||
- Ammar Najjar
|
- Ammar Najjar
|
||||||
|
- Amory Meltzer
|
||||||
- Andreas Duering
|
- Andreas Duering
|
||||||
- Andrei Petcu
|
- Andrei Petcu
|
||||||
- Andrew Griffiths
|
- Andrew Griffiths
|
||||||
- Andrew Marcinkevičius
|
- Andrew Marcinkevičius
|
||||||
- Andrew Starr-Bochicchio
|
- Andrew Starr-Bochicchio
|
||||||
|
- Andrew Sullivan Cant
|
||||||
- Andrey Elizarov
|
- Andrey Elizarov
|
||||||
- Angel Aguilera
|
- Angel Aguilera
|
||||||
- Antoine Beaupré
|
- Antoine Beaupré
|
||||||
- Aurélien Scoubeau
|
- Aurélien Scoubeau
|
||||||
- Balazs Nadasdi
|
- Balazs Nadasdi
|
||||||
- Ben Parnell
|
- Ben Parnell
|
||||||
|
- Beni Ben Zikry
|
||||||
- Brandon Zylstra
|
- Brandon Zylstra
|
||||||
- Brian Goad
|
- Brian Goad
|
||||||
|
- Brian Murrell
|
||||||
- Bruno Sutic
|
- Bruno Sutic
|
||||||
|
- Caleb Maclennan
|
||||||
- Carl Casbolt
|
- Carl Casbolt
|
||||||
- Carlos Rodríguez
|
- Carlos Rodríguez
|
||||||
|
- Christophe Badoit
|
||||||
- Ciro Nunes
|
- Ciro Nunes
|
||||||
|
- CleanMachine1
|
||||||
|
- CodeByZach
|
||||||
- Craig MacGregor
|
- Craig MacGregor
|
||||||
|
- Dan Goodliffe
|
||||||
- Dan Jackson
|
- Dan Jackson
|
||||||
- Dan Kilman
|
- Dan Kilman
|
||||||
- Daniel Bruder
|
- Daniel Bruder
|
||||||
|
- Daniel Lublin
|
||||||
- Daniel Schildt
|
- Daniel Schildt
|
||||||
- Dave James Miller
|
- Dave James Miller
|
||||||
- David Hartmann
|
- David Hartmann
|
||||||
|
- Dominic Barnes
|
||||||
- Dung Quang
|
- Dung Quang
|
||||||
|
- Edward Betts
|
||||||
|
- Eli Schwartz
|
||||||
- Emil Kjelsrud
|
- Emil Kjelsrud
|
||||||
|
- Erik Skultety
|
||||||
|
- François M
|
||||||
- George Crabtree
|
- George Crabtree
|
||||||
|
- Gerrit-K
|
||||||
- Greg Allen
|
- Greg Allen
|
||||||
|
- Guilhem Saurel
|
||||||
- Guillermo Rauch
|
- Guillermo Rauch
|
||||||
- Gunnlaugur Thor Briem
|
- Gunnlaugur Thor Briem
|
||||||
|
- Hasse Ramlev Hansen
|
||||||
|
- Heiko Becker
|
||||||
|
- Hugo Ruiz-Mireles
|
||||||
|
- Isaac Mungai
|
||||||
- J.C. Yamokoski
|
- J.C. Yamokoski
|
||||||
|
- Jack O. Wasey
|
||||||
- James Manning
|
- James Manning
|
||||||
|
- James Zhu
|
||||||
- Jan Krueger
|
- Jan Krueger
|
||||||
|
- Jared Baur
|
||||||
- Jarod Stewart
|
- Jarod Stewart
|
||||||
- Jason Young
|
- Jason Young
|
||||||
- Jens K. Mueller
|
- Jens K. Mueller
|
||||||
- Jeremy Lin
|
- Jeremy Lin
|
||||||
- Jesse Sipprell
|
- Jesse Sipprell
|
||||||
- Jianjin Fan
|
- Jianjin Fan
|
||||||
|
- Jobin Kurian
|
||||||
- Johannes Ewald
|
- Johannes Ewald
|
||||||
- John Evans
|
- John Evans
|
||||||
- John Hoffmann
|
- John Hoffmann
|
||||||
- Jon Ander Peñalba
|
- Jon Ander Peñalba
|
||||||
|
- Josh McKinney
|
||||||
- Joshua Appelman
|
- Joshua Appelman
|
||||||
- Justin Dugger
|
- José María Gutiérrez @TheTechOddBug
|
||||||
|
- Katrin Leinweber
|
||||||
- Kevin Woo
|
- Kevin Woo
|
||||||
- Konstantin Schukraft
|
- Konstantin Schukraft
|
||||||
|
- Kurban Mallachiev
|
||||||
- Leandro López
|
- Leandro López
|
||||||
|
- Lone
|
||||||
|
- Luis Miguel Hernanz
|
||||||
- Maarten Winter
|
- Maarten Winter
|
||||||
|
- Marco Glasbergen
|
||||||
|
- Matan Rosenberg
|
||||||
- Mathieu D. (MatToufoutu)
|
- Mathieu D. (MatToufoutu)
|
||||||
|
- Matt
|
||||||
- Matt Colyer
|
- Matt Colyer
|
||||||
|
- Matt Headley
|
||||||
- Matthew Avant
|
- Matthew Avant
|
||||||
|
- Mattias Andersson
|
||||||
- Michael Komitee
|
- Michael Komitee
|
||||||
- Michael Matuzak
|
- Michael Matuzak
|
||||||
|
- Michele Bologna
|
||||||
|
- Montak Oleg
|
||||||
- Moritz Grauel
|
- Moritz Grauel
|
||||||
|
- Nate Eagleson
|
||||||
- Nate Fischer
|
- Nate Fischer
|
||||||
- Nathan Rajlich
|
- Nathan Rajlich
|
||||||
- Nick Campbell
|
- Nick Campbell
|
||||||
- Nick Payne
|
- Nick Payne
|
||||||
|
- Nicolas Kosinski
|
||||||
- Niklas Fiekas
|
- Niklas Fiekas
|
||||||
|
- Oliver Kopp
|
||||||
|
- Orestis
|
||||||
|
- Peter Bittner
|
||||||
|
- Piotr Górski
|
||||||
- Prayag Verma
|
- Prayag Verma
|
||||||
|
- Przemek Kitszel
|
||||||
- R. Martinho Fernandes
|
- R. Martinho Fernandes
|
||||||
- Raphael Fleischlin
|
- Raphael Fleischlin
|
||||||
- Rasmus Wriedt Larsen
|
- Rasmus Wriedt Larsen
|
||||||
|
- René
|
||||||
|
- Revisor
|
||||||
|
- Ricardo Fernández Serrata
|
||||||
- Riceball LEE
|
- Riceball LEE
|
||||||
- Rob Kennedy
|
- Rob Kennedy
|
||||||
|
- Robin von Bülow
|
||||||
|
- Russ
|
||||||
- Ryan Bohn
|
- Ryan Bohn
|
||||||
|
- Sachin Gupta
|
||||||
- Sam Thursfield
|
- Sam Thursfield
|
||||||
|
- Sandro
|
||||||
|
- Sascha Andres
|
||||||
|
- SchleimKeim
|
||||||
- Sean Molenaar
|
- Sean Molenaar
|
||||||
- Xavier Krantz
|
- Sebastian Gniazdowski
|
||||||
- Oliver Kopp
|
- Sebastián Mancilla
|
||||||
- Dominic Barnes
|
- Simon Tate
|
||||||
- Edward Betts
|
- Stefan VanBuren
|
||||||
- Luis Miguel Hernanz
|
- Stephen Mathieson
|
||||||
|
- Steve Mao
|
||||||
|
- Stu Feldt
|
||||||
|
- SukkaW
|
||||||
|
- Tim Preston
|
||||||
- Timothy Hwang
|
- Timothy Hwang
|
||||||
- Marco Glasbergen
|
- Tin Lai
|
||||||
- Daniel Lublin
|
- Todd Wolfson
|
||||||
- Matt
|
- Tom Andrade
|
||||||
- Sam Bostock
|
- Tom Ashworth
|
||||||
|
- Tony
|
||||||
|
- TweeKane
|
||||||
|
- Valérian Galliat
|
||||||
|
- Vitaly Chikunov
|
||||||
|
- Wei Lee
|
||||||
|
- Wei Wu
|
||||||
|
- Wen Sun
|
||||||
|
- Wiktor Żurawik
|
||||||
|
- Xavier Krantz
|
||||||
|
- Xiaopei Li
|
||||||
- Zeeshan Ahmed
|
- Zeeshan Ahmed
|
||||||
- Isaac Mungai
|
- ax1036
|
||||||
|
- creynders
|
||||||
|
- dead-horse
|
||||||
|
- eszabpt
|
||||||
|
- fengkx
|
||||||
|
- johnpyp
|
||||||
|
- jykntr
|
||||||
|
- kang
|
||||||
|
- meza
|
||||||
|
- neydroid
|
||||||
|
- nulltask
|
||||||
|
- phanium
|
||||||
|
- sgleizes
|
||||||
|
- tfendin
|
||||||
|
- tiemonl
|
||||||
|
- zentarul
|
||||||
|
- zeroDivisible
|
||||||
|
- zhiyanfoo
|
||||||
|
- Áron Hoffmann
|
||||||
|
- Étienne BERSAC
|
||||||
|
- ☃ pitr
|
||||||
|
- 单元源
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,34 @@
|
||||||
## Your new git-extra command should support
|
# Contributing
|
||||||
|
|
||||||
* OSX, Linux, BSD (You may need to browse their man page)<sup>*</sup>
|
Thanks for contributing! Please read this document before you make a PR.
|
||||||
* Bash 3.2+ (If you aren't sure, see [the Bash changelog](http://tldp.org/LDP/abs/html/bash2.html))
|
|
||||||
* Git 2.1+
|
|
||||||
|
|
||||||
<sup>*</sup>If you aren't able to test your new command on a platform,
|
## Supported Platforms
|
||||||
make that clear in your PR and someone else may be able to test it on their system.
|
|
||||||
|
|
||||||
## To submit a new command, you should
|
Any changes must support the following platforms:
|
||||||
|
|
||||||
Let's assume your new command is named `foo`.
|
- macOS
|
||||||
|
- Linux
|
||||||
|
- OpenBSD (You may need to browse their man page)
|
||||||
|
|
||||||
1. Write a bash script under `./bin` called `git-foo`.
|
Your change must also be compatible with the dependency constraints that we specify in [Installation](./Installation.md). If you aren't sure if a feature is compatible, check the manual or release notes. For example, the Bash changelog is [here](https://git.savannah.gnu.org/cgit/bash.git/tree/NEWS?h=devel).
|
||||||
|
|
||||||
|
If you aren't able to test your new command on a platform, make that clear in your PR; someone else may be able to test it on their system.
|
||||||
|
|
||||||
|
## Adding a New Command
|
||||||
|
|
||||||
|
Let's say you wish to add a new command. Assuming your new command is named `foo`:
|
||||||
|
|
||||||
|
1. Write a bash script under `./bin` called `git-foo`. The script should be started with `#!/usr/bin/env bash`.
|
||||||
2. Read `./man/Readme.md` and write documentation for `git-foo`.
|
2. Read `./man/Readme.md` and write documentation for `git-foo`.
|
||||||
3. Don't forget to introduce it in `Commands.md`.
|
3. Don't forget to introduce it in `Commands.md`.
|
||||||
4. Update `./etc/git-extras-completion.zsh`. Just follow existing code.
|
4. Update `./etc/git-extras-completion.zsh`. Just follow existing code.
|
||||||
5. (Optional) Update `./etc/bash_completion.sh`.
|
5. (Optional) Update `./etc/bash_completion.sh`.
|
||||||
6. Run `./check_integrity.sh foo` to check if all done.
|
6. (Optional) Update `./etc/git-extras.fish`.
|
||||||
|
7. (Optional) Add a test under `./tests`.
|
||||||
|
8. Run `./check_integrity.sh foo` to check if all done. (You may also run `./check_integrity.sh` to check all commands.)
|
||||||
|
|
||||||
You are welcome to open up an issue to discuss new commands or features before opening a pull request.
|
You are welcome to open up an issue to discuss new commands or features before opening a pull request.
|
||||||
|
|
||||||
|
## Submitting a pull request
|
||||||
|
|
||||||
|
Please follow the suggestion in `./.github/PULL_REQUEST_TEMPLATE.md`.
|
||||||
|
|
|
||||||
451
Commands.md
451
Commands.md
|
|
@ -1,53 +1,62 @@
|
||||||
|
|
||||||
|
- [`git abort`](#git-abort)
|
||||||
- [`git alias`](#git-alias)
|
- [`git alias`](#git-alias)
|
||||||
- [`git archive-file`](#git-archive-file)
|
- [`git archive-file`](#git-archive-file)
|
||||||
- [`git authors`](#git-authors)
|
- [`git authors`](#git-authors)
|
||||||
- [`git back`](#git-back)
|
- [`git browse`](#git-browse)
|
||||||
- [`git bug`](#git-featurerefactorbugchore)
|
- [`git browse-ci`](#git-browse-ci)
|
||||||
- [`git bulk`](#git-bulk)
|
- [`git bulk`](#git-bulk)
|
||||||
|
- [`git brv`](#git-brv)
|
||||||
- [`git changelog`](#git-changelog)
|
- [`git changelog`](#git-changelog)
|
||||||
- [`git chore`](#git-featurerefactorbugchore)
|
|
||||||
- [`git clear`](#git-clear)
|
- [`git clear`](#git-clear)
|
||||||
- [`git clear-soft`](#git-clear-soft)
|
- [`git clear-soft`](#git-clear-soft)
|
||||||
|
- [`git coauthor`](#git-coauthor)
|
||||||
- [`git commits-since`](#git-commits-since)
|
- [`git commits-since`](#git-commits-since)
|
||||||
|
- [`git continue`](#git-continue)
|
||||||
- [`git contrib`](#git-contrib)
|
- [`git contrib`](#git-contrib)
|
||||||
- [`git count`](#git-count)
|
- [`git count`](#git-count)
|
||||||
|
- [`git cp`](#git-cp)
|
||||||
- [`git create-branch`](#git-create-branch)
|
- [`git create-branch`](#git-create-branch)
|
||||||
- [`git delete-branch`](#git-delete-branch)
|
- [`git delete-branch`](#git-delete-branch)
|
||||||
|
- [`git delete-gone-branches`](#git-delete-gone-branches)
|
||||||
- [`git delete-merged-branches`](#git-delete-merged-branches)
|
- [`git delete-merged-branches`](#git-delete-merged-branches)
|
||||||
|
- [`git delete-squashed-branches`](#git-delete-squashed-branches)
|
||||||
- [`git delete-submodule`](#git-delete-submodule)
|
- [`git delete-submodule`](#git-delete-submodule)
|
||||||
- [`git delete-tag`](#git-delete-tag)
|
- [`git delete-tag`](#git-delete-tag)
|
||||||
- [`git delta`](#git-delta)
|
- [`git delta`](#git-delta)
|
||||||
- [`git effort`](#git-effort)
|
- [`git effort`](#git-effort)
|
||||||
- [`git extras`](#git-extras)
|
- [`git extras`](#git-extras)
|
||||||
- [`git feature`](#git-featurerefactorbugchore)
|
- [`git feature`](#git-feature)
|
||||||
- [`git force-clone`](#git-force-clone)
|
- [`git force-clone`](#git-force-clone)
|
||||||
- [`git fork`](#git-fork)
|
- [`git fork`](#git-fork)
|
||||||
- [`git fresh-branch`](#git-fresh-branch)
|
- [`git fresh-branch`](#git-fresh-branch)
|
||||||
|
- [`git get`](#git-get)
|
||||||
- [`git gh-pages`](#git-gh-pages)
|
- [`git gh-pages`](#git-gh-pages)
|
||||||
- [`git graft`](#git-graft)
|
- [`git graft`](#git-graft)
|
||||||
- [`git guilt`](#git-guilt)
|
- [`git guilt`](#git-guilt)
|
||||||
- [`git ignore`](#git-ignore)
|
- [`git ignore`](#git-ignore)
|
||||||
- [`git ignore-io`](#git-ignore-io)
|
- [`git ignore-io`](#git-ignore-io)
|
||||||
- [`git info`](#git-info)
|
- [`git info`](#git-info)
|
||||||
- [`git line-summary`](#git-line-summary)
|
|
||||||
- [`git local-commits`](#git-local-commits)
|
- [`git local-commits`](#git-local-commits)
|
||||||
- [`git lock`](#git-lock)
|
- [`git lock`](#git-lock)
|
||||||
- [`git locked`](#git-locked)
|
- [`git locked`](#git-locked)
|
||||||
|
- [`git magic`](#git-magic)
|
||||||
- [`git merge-into`](#git-merge-into)
|
- [`git merge-into`](#git-merge-into)
|
||||||
- [`git merge-repo`](#git-merge-repo)
|
- [`git merge-repo`](#git-merge-repo)
|
||||||
- [`git missing`](#git-missing)
|
- [`git missing`](#git-missing)
|
||||||
- [`git mr`](#git-mr)
|
- [`git mr`](#git-mr)
|
||||||
- [`git obliterate`](#git-obliterate)
|
- [`git obliterate`](#git-obliterate)
|
||||||
|
- [`git paste`](#git-paste)
|
||||||
- [`git pr`](#git-pr)
|
- [`git pr`](#git-pr)
|
||||||
- [`git psykorebase`](#git-psykorebase)
|
- [`git psykorebase`](#git-psykorebase)
|
||||||
- [`git pull-request`](#git-pull-request)
|
- [`git pull-request`](#git-pull-request)
|
||||||
- [`git reauthor`](#git-reauthor)
|
- [`git reauthor`](#git-reauthor)
|
||||||
- [`git rebase-patch`](#git-rebase-patch)
|
- [`git rebase-patch`](#git-rebase-patch)
|
||||||
- [`git refactor`](#git-featurerefactorbugchore)
|
|
||||||
- [`git release`](#git-release)
|
- [`git release`](#git-release)
|
||||||
- [`git rename-branch`](#git-rename-branch)
|
- [`git rename-branch`](#git-rename-branch)
|
||||||
|
- [`git rename-file`](#git-rename-file)
|
||||||
- [`git rename-tag`](#git-rename-tag)
|
- [`git rename-tag`](#git-rename-tag)
|
||||||
|
- [`git rename-remote`](#git-rename-remote)
|
||||||
- [`git repl`](#git-repl)
|
- [`git repl`](#git-repl)
|
||||||
- [`git reset-file`](#git-reset-file)
|
- [`git reset-file`](#git-reset-file)
|
||||||
- [`git root`](#git-root)
|
- [`git root`](#git-root)
|
||||||
|
|
@ -66,6 +75,9 @@
|
||||||
- [`git touch`](#git-touch)
|
- [`git touch`](#git-touch)
|
||||||
- [`git undo`](#git-undo)
|
- [`git undo`](#git-undo)
|
||||||
- [`git unlock`](#git-unlock)
|
- [`git unlock`](#git-unlock)
|
||||||
|
- [`git utimes`](#git-utimes)
|
||||||
|
- [`git unwip`](#git-unwip)
|
||||||
|
- [`git wip`](#git-wip)
|
||||||
|
|
||||||
## git extras
|
## git extras
|
||||||
|
|
||||||
|
|
@ -74,7 +86,7 @@ The main `git-extras` command.
|
||||||
Output the current `--version`:
|
Output the current `--version`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git extras
|
$ git extras --version
|
||||||
```
|
```
|
||||||
|
|
||||||
List available commands:
|
List available commands:
|
||||||
|
|
@ -90,11 +102,11 @@ $ git extras update
|
||||||
```
|
```
|
||||||
## git gh-pages
|
## git gh-pages
|
||||||
|
|
||||||
Sets up the `gh-pages` branch. (See [GitHub Pages](http://pages.github.com/) documentation.)
|
Sets up the `gh-pages` branch. (See [GitHub Pages](https://pages.github.com/) documentation.)
|
||||||
|
|
||||||
## git feature|refactor|bug|chore
|
## git feature
|
||||||
|
|
||||||
Create/Merge the given feature, refactor, bug or chore branch `name`:
|
Create/Merge the given feature branch `name`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git feature dependencies
|
$ git feature dependencies
|
||||||
|
|
@ -123,8 +135,6 @@ $ git feature finish dependencies
|
||||||
|
|
||||||
_Note_: If a remote is setup to track the branch, it will be deleted.
|
_Note_: If a remote is setup to track the branch, it will be deleted.
|
||||||
|
|
||||||
All of this works with `feature`, `bug`, `chore` or `refactor`.
|
|
||||||
|
|
||||||
## git contrib
|
## git contrib
|
||||||
|
|
||||||
Output `author`'s contributions to a project:
|
Output `author`'s contributions to a project:
|
||||||
|
|
@ -142,17 +152,20 @@ visionmedia (18):
|
||||||
|
|
||||||
## git summary
|
## git summary
|
||||||
|
|
||||||
Outputs a repo summary:
|
Outputs a repo or path summary:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git summary
|
$ git summary
|
||||||
|
|
||||||
project : git-extras
|
project : git-extras
|
||||||
repo age : 10 months ago
|
repo age : 10 months ago
|
||||||
commits : 163
|
branch : master
|
||||||
active : 60 days
|
last active : 3 weeks ago
|
||||||
files : 93
|
active on : 93 days
|
||||||
authors :
|
commits : 163
|
||||||
|
files : 93
|
||||||
|
uncommitted : 3
|
||||||
|
authors :
|
||||||
97 Tj Holowaychuk 59.5%
|
97 Tj Holowaychuk 59.5%
|
||||||
37 Jonhnny Weslley 22.7%
|
37 Jonhnny Weslley 22.7%
|
||||||
8 Kenneth Reitz 4.9%
|
8 Kenneth Reitz 4.9%
|
||||||
|
|
@ -169,8 +182,8 @@ authors :
|
||||||
1 Guillermo Rauch 0.6%
|
1 Guillermo Rauch 0.6%
|
||||||
```
|
```
|
||||||
|
|
||||||
This command can also take a *commitish*, and will print a summary for commits in
|
This command can also take a *committish*, and will print a summary for commits in
|
||||||
the commitish range:
|
the committish range:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git summary v42..
|
$ git summary v42..
|
||||||
|
|
@ -213,9 +226,19 @@ project : git-extras
|
||||||
1 Daniel Schildt 0.0%
|
1 Daniel Schildt 0.0%
|
||||||
```
|
```
|
||||||
|
|
||||||
## git line-summary
|
The `--line` option can also take a path, which will print a filtered summary for that folder or file.
|
||||||
|
|
||||||
WARNING: git line-summary has been replaced by [`git summary --line`](#git-summary) and will be removed in a future release.
|
The option `--output-style` tries to put as much summary information of the repo into defined styled way as possible.
|
||||||
|
This is how the `tabular` output style and `oneline` output style look like
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git summary --output-style tabular
|
||||||
|
# Repo | Age | Last active | Active on | Commits | Uncommitted | Branch
|
||||||
|
git-extras | 13 years | 7 hours ago | 807 days | 1703 | 3 | master
|
||||||
|
|
||||||
|
$ git summary --output-style oneline
|
||||||
|
git-extras / age: 13 years / last active: 7 hours ago / active on 807 days / commits: 1703 / uncommitted: 3 / branch: master
|
||||||
|
```
|
||||||
|
|
||||||
## git effort
|
## git effort
|
||||||
|
|
||||||
|
|
@ -225,8 +248,6 @@ project : git-extras
|
||||||
node (master): git effort --above 15 {src,lib}/*
|
node (master): git effort --above 15 {src,lib}/*
|
||||||
```
|
```
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
If you wish to ignore files with commits `<=` a value you may use `--above`:
|
If you wish to ignore files with commits `<=` a value you may use `--above`:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -255,17 +276,40 @@ $ git effort bin/* lib/*
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
usage: git bulk [-g] ([-a]|[-w <ws-name>]) <git command>
|
usage: git bulk [-g] ([-a]|[-w <ws-name>]) <git command>
|
||||||
git bulk --addworkspace <ws-name> <ws-root-directory>
|
git bulk --addworkspace <ws-name> <ws-root-directory> (--from <URL or file>)
|
||||||
git bulk --removeworkspace <ws-name>
|
git bulk --removeworkspace <ws-name>
|
||||||
git bulk --addcurrent <ws-name>
|
git bulk --addcurrent <ws-name>
|
||||||
git bulk --purge
|
git bulk --purge
|
||||||
git bulk --listall
|
git bulk --listall
|
||||||
```
|
```
|
||||||
|
|
||||||
Register a workspace so that `git bulk` knows about it (notice that <ws-root-directory> must be absolute path):
|
Register a workspace so that `git bulk` knows about it (it will be registered in your `.gitconfig`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git bulk --addworkspace personal ~/workspaces/personal
|
$ git bulk --addworkspace personal ~/workspaces/personal
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice that `<ws-root-directory>` must be an absolute path (or an environment variable pointing to an absolute path).
|
||||||
|
In the case of a **single quoted environment variable**, it will be dereferenced at `git-bulk` runtime, suitable for dynamic workspaces (*e.g.*, defined in your `.bashrc`).
|
||||||
|
As an illustration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git bulk --addworkspace personal '$PERSONAL_WORKSPACE'
|
||||||
|
```
|
||||||
|
|
||||||
|
With option `--from` the URL to a single repository or a file containing multiple URLs can be added and they will be cloned directly into the workspace. Suitable for the initial setup of a multi-repo project.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git bulk --addworkspace projectX ~/workspaces/projectx --from https://github.com/x/project-x.git
|
||||||
|
|
||||||
|
# OR with a file containing many repositories on each line:
|
||||||
|
$ git bulk --addworkspace projectX ~/workspaces/projectx --from ~/workspaces/repositories.txt
|
||||||
|
```
|
||||||
|
with `repositories.txt` be like:
|
||||||
|
```
|
||||||
|
https://github.com/x/project-x-1.git
|
||||||
|
https://github.com/x/project-x-2.git
|
||||||
|
https://github.com/x/project-x-3.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Register the current directory as a workspace to `git bulk`
|
Register the current directory as a workspace to `git bulk`
|
||||||
|
|
@ -279,6 +323,7 @@ $ git bulk --addcurrent personal
|
||||||
```bash
|
```bash
|
||||||
$ git bulk --listall
|
$ git bulk --listall
|
||||||
bulkworkspaces.personal /Users/niklasschlimm/workspaces/personal
|
bulkworkspaces.personal /Users/niklasschlimm/workspaces/personal
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Run a git command on the repositories of the current workspace:
|
Run a git command on the repositories of the current workspace:
|
||||||
|
|
@ -318,21 +363,78 @@ $ git bulk --removeworkspace personal
|
||||||
$ git bulk --purge
|
$ git bulk --purge
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git brv
|
||||||
|
|
||||||
|
Pretty listing of branches sorted by the date of their last commit.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git brv
|
||||||
|
2020-01-14 adds-git-brv fork/adds-git-brv 1ca0d76 Fixes #700: Adds git-brv
|
||||||
|
2020-01-08 master origin/master 265b03e Merge pull request #816 from spacewander/git-sed-pathspec
|
||||||
|
```
|
||||||
|
|
||||||
|
When your repo has many branches, it can be more convenient to see this list in reverse. This can be set as the default by setting the git-extras.brv.reverse git option to true.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git brv --reverse
|
||||||
|
2020-01-08 master origin/master 265b03e Merge pull request #816 from spacewander/git-sed-pathspec
|
||||||
|
2020-01-14 adds-git-brv fork/adds-git-brv 1ca0d76 Fixes #700: Adds git-brv
|
||||||
|
```
|
||||||
|
|
||||||
## git repl
|
## git repl
|
||||||
|
|
||||||
Git read-eval-print-loop. Let's you run `git` commands without typing 'git'.
|
Git read-eval-print-loop. Lets you run `git` commands without typing 'git'.
|
||||||
|
|
||||||
Commands can be prefixed with an exclamation mark (!) to be interpreted as
|
Commands can be prefixed with an exclamation mark (!) to be interpreted as
|
||||||
a regular command.
|
a regular command.
|
||||||
|
|
||||||
Type `exit` or `quit` to end the repl session.
|
Type `exit`, `quit`, or `q` to end the repl session.
|
||||||
|
|
||||||
|
Any arguments to git repl will be taken as the first command to execute in
|
||||||
|
the repl.
|
||||||
|
|
||||||
|
### CONFIGURATION
|
||||||
|
|
||||||
|
Commands entered in a repl session will be saved to a history file and be available in
|
||||||
|
future sessions, similar to a shell or programming language repl. By default,
|
||||||
|
there is one global history file, ~/.git_repl_history. You can specify that your projects
|
||||||
|
each have their own independent history file. This file will be saved in .git_repl_history
|
||||||
|
at the top level of the repo, and will need to be added to the repo or global .gitignore.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# remove the --global flag to configure only an individual project to have its own history file
|
||||||
|
git config --global git-extras.repl.use-local-history "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can specify a default command to run when hitting enter:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config --global git-extras.repl.on-enter-command "git status -sb"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can configure which character is used at the end of the prompt: (default `>`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config --global git-extras.repl.prompt-character "±"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can specify the prefix for the prompt, or remove it (default `git`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config --global git-extras.repl.prefix ""
|
||||||
|
```
|
||||||
|
|
||||||
|
You can have the name of the current git repo shown in the prompt (default `false`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config --global git-extras.repl.show-project-name "true"
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git repl
|
$ git repl
|
||||||
git version 2.9.2
|
git version 2.34.1
|
||||||
git-extras version 3.0.0
|
git-extras version 7.3.0
|
||||||
type 'ls' to ls files below current directory,
|
Type 'ls' to ls files below current directory; '!command' to execute any command or just 'subcommand' to execute any git subcommand; 'quit', 'exit', 'q', ^D, or ^C to exit the git repl.
|
||||||
'!command' to execute any command or just 'subcommand' to execute any git subcommand
|
|
||||||
|
|
||||||
git (master)> ls-files
|
git (master)> ls-files
|
||||||
History.md
|
History.md
|
||||||
|
|
@ -351,6 +453,30 @@ Straight from the shell!
|
||||||
git (master)> quit
|
git (master)> quit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git coauthor
|
||||||
|
|
||||||
|
Add a co-author to the last commit
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git coauthor user user@email.com
|
||||||
|
|
||||||
|
[master b62ceae] Add documentation files
|
||||||
|
Date: Sat Aug 17 17:33:53 2019 -0500
|
||||||
|
2 files changed, 145 insertions(+), 0 deletions(-)
|
||||||
|
create mode 100644 README.md
|
||||||
|
create mode 100644 CONTRIBUTING.md
|
||||||
|
|
||||||
|
$ git log -1
|
||||||
|
|
||||||
|
commit b62ceae2685e6ece071f3c3754e9b77fd0a35c88 (HEAD -> master)
|
||||||
|
Author: user person <userperson@email.com>
|
||||||
|
Date: Sat Aug 17 17:33:53 2019 -0500
|
||||||
|
|
||||||
|
Add documentation files
|
||||||
|
|
||||||
|
Co-authored-by: user <user@email.com>
|
||||||
|
```
|
||||||
|
|
||||||
## git commits-since
|
## git commits-since
|
||||||
|
|
||||||
List commits since `date` (defaults to "last week"):
|
List commits since `date` (defaults to "last week"):
|
||||||
|
|
@ -480,12 +606,26 @@ Rename a branch locally, and sync to remote via `git push`.
|
||||||
|
|
||||||
```
|
```
|
||||||
# renames any branch
|
# renames any branch
|
||||||
$ git rename-branch new-name old-name
|
$ git rename-branch old-name new-name
|
||||||
|
|
||||||
# renames current branch
|
# renames current branch
|
||||||
$ git rename-branch new-name
|
$ git rename-branch new-name
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git rename-file
|
||||||
|
|
||||||
|
Rename a file or directory and ensure Git recognizes the change, regardless of filesystem case-sensitivity.
|
||||||
|
It combines the functionality of the `mv` command and `git mv`. This is particularly useful for renaming files or directories
|
||||||
|
to change only their case, which might not be detected by Git on case-insensitive filesystems.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rename a file
|
||||||
|
git rename-file old_filename new_filename
|
||||||
|
|
||||||
|
# Rename a directory
|
||||||
|
git rename-file old_directory new_directory
|
||||||
|
```
|
||||||
|
|
||||||
## git rename-tag
|
## git rename-tag
|
||||||
|
|
||||||
Rename a tag (locally and remotely).
|
Rename a tag (locally and remotely).
|
||||||
|
|
@ -509,6 +649,19 @@ $ git tag
|
||||||
test2
|
test2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git rename-remote
|
||||||
|
|
||||||
|
Rename a git remote regardless of name conflict, and then list current git remotes.
|
||||||
|
```
|
||||||
|
$ git remote -v
|
||||||
|
origin git@myserver.com:myuser/foo.git (fetch)
|
||||||
|
origin git@myserver.com:myuser/foo.git (push)
|
||||||
|
upstream git@myserver.com:myuser/bar.git (fetch)
|
||||||
|
upstream git@myserver.com:myuser/bar.git (push)
|
||||||
|
$ git-rename-remote upstream origin
|
||||||
|
origin git@myserver.com:myuser/bar.git (fetch)
|
||||||
|
origin git@myserver.com:myuser/bar.git (push)
|
||||||
|
```
|
||||||
|
|
||||||
## git reauthor
|
## git reauthor
|
||||||
|
|
||||||
|
|
@ -602,6 +755,7 @@ To show just the global or just the local file's contents, you can use the follo
|
||||||
|
|
||||||
* `-g` or `--global` to show just the global file
|
* `-g` or `--global` to show just the global file
|
||||||
* `-l` or `--local` to show just the local file
|
* `-l` or `--local` to show just the local file
|
||||||
|
* `-p` or `--private` to show just the repository's file
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git ignore -g
|
$ git ignore -g
|
||||||
|
|
@ -620,14 +774,14 @@ build
|
||||||
|
|
||||||
## git ignore-io
|
## git ignore-io
|
||||||
|
|
||||||
Generate sample gitignore file from [gitignore.io](https://www.gitignore.io)
|
Generate sample gitignore file from [gitignore.io](https://www.toptal.com/developers/gitignore)
|
||||||
|
|
||||||
Without option, `git ignore-io <type>` shows the sample gitignore of specified types on screen.
|
Without option, `git ignore-io <type>` shows the sample gitignore of specified types on screen.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git ignore-io vim
|
$ git ignore-io vim
|
||||||
|
|
||||||
# Created by https://www.gitignore.io/api/vim
|
# Created by https://www.toptal.com/developers/gitignore/api/vim
|
||||||
|
|
||||||
### Vim ###
|
### Vim ###
|
||||||
[._]*.s[a-w][a-z]
|
[._]*.s[a-w][a-z]
|
||||||
|
|
@ -688,6 +842,13 @@ $ git info
|
||||||
myBranch
|
myBranch
|
||||||
* master
|
* master
|
||||||
|
|
||||||
|
## Submodule(s):
|
||||||
|
|
||||||
|
a234567 path2submodule1/submodule1 (branch/tag)
|
||||||
|
+ b234567 path2submodule2/submodule2 (branch/tag)
|
||||||
|
- c234567 path2submodule3/submodule3 (branch/tag)
|
||||||
|
e234567 path2submodule4/submodule4 (branch/tag)
|
||||||
|
|
||||||
## Most Recent Commit:
|
## Most Recent Commit:
|
||||||
|
|
||||||
commit e3952df2c172c6f3eb533d8d0b1a6c77250769a7
|
commit e3952df2c172c6f3eb533d8d0b1a6c77250769a7
|
||||||
|
|
@ -695,8 +856,6 @@ $ git info
|
||||||
|
|
||||||
Added git-info command.
|
Added git-info command.
|
||||||
|
|
||||||
Type 'git log' for more commits, or 'git show <commit id>' for full commit details.
|
|
||||||
|
|
||||||
## Configuration (.git/config):
|
## Configuration (.git/config):
|
||||||
|
|
||||||
color.diff=auto
|
color.diff=auto
|
||||||
|
|
@ -722,6 +881,14 @@ If you wish to omit the config section, you may use `--no-config`:
|
||||||
$ git info --no-config
|
$ git info --no-config
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git cp
|
||||||
|
|
||||||
|
Copy a file to another one keeping its history and allowing for merge conflicts handling.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git cp README.md README.rst
|
||||||
|
```
|
||||||
|
|
||||||
## git create-branch
|
## git create-branch
|
||||||
|
|
||||||
Create local branch `name`:
|
Create local branch `name`:
|
||||||
|
|
@ -766,6 +933,21 @@ Delete local and remote tag `name`:
|
||||||
$ git delete-tag 0.0.1
|
$ git delete-tag 0.0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git delete-gone-branches
|
||||||
|
|
||||||
|
Deletes all local branches whose remote-tracking branch has been deleted (i.e. branches shown as `[gone]` in `git branch -vv`). Use `--dry-run` to preview what would be deleted.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git delete-gone-branches
|
||||||
|
Deleted branch feature-123 (was abc1234).
|
||||||
|
Deleted branch feature-456 (was def5678).
|
||||||
|
|
||||||
|
$ git delete-gone-branches --dry-run
|
||||||
|
Would delete the following branches:
|
||||||
|
feature-123
|
||||||
|
feature-456
|
||||||
|
```
|
||||||
|
|
||||||
## git delete-merged-branches
|
## git delete-merged-branches
|
||||||
|
|
||||||
Deletes branches that are listed in `git branch --merged`.
|
Deletes branches that are listed in `git branch --merged`.
|
||||||
|
|
@ -778,14 +960,34 @@ Deleted feature/dashboard (was 923befa).
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git delete-squashed-branches
|
||||||
|
|
||||||
|
Deletes branches that have been "squashed-merged" into a specified branch; this branch will be checked out as a side-effect. If no branch is specified, then it will default to the current checked out branch.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ (feature-branch) git delete-squashed-branches main
|
||||||
|
Deleted branch dependabot/bundler/kramdown-2.3.1 (was 1d3fb00).
|
||||||
|
Deleted branch dependabot/bundler/rexml-3.2.5 (was a7e4052).
|
||||||
|
$ (main) git ...
|
||||||
|
```
|
||||||
|
|
||||||
## git fresh-branch
|
## git fresh-branch
|
||||||
|
|
||||||
Create empty local branch `name`:
|
Create empty local branch `docs`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git fresh-branch docs
|
$ git fresh-branch docs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git get
|
||||||
|
|
||||||
|
Clone repository into a subdirectory of the configured path, `"$HOME/some-dir"`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git config --global --add git-extras.get.clone-path "$HOME/some-dir"
|
||||||
|
$ git get 'https://github.com/hyperupcall/bake'
|
||||||
|
```
|
||||||
|
|
||||||
## git guilt
|
## git guilt
|
||||||
|
|
||||||
Calculate the change in blame between two revisions
|
Calculate the change in blame between two revisions
|
||||||
|
|
@ -840,9 +1042,7 @@ $ git squash HEAD~3 "Work on a feature"
|
||||||
|
|
||||||
Populates the file matching `authors|contributors -i` with the authors of commits, according to the number of commits per author.
|
Populates the file matching `authors|contributors -i` with the authors of commits, according to the number of commits per author.
|
||||||
|
|
||||||
Opens the file in `$EDITOR` when set.
|
See the ["MAPPING AUTHORS" section](https://git-scm.com/docs/git-shortlog#_mapping_authors) of **git-shortlog**(1) to coalesce together commits by the same person.
|
||||||
|
|
||||||
See the ["MAPPING AUTHORS" section](http://git-scm.com/docs/git-shortlog#_mapping_authors) of **git-shortlog**(1) to coalesce together commits by the same person.
|
|
||||||
|
|
||||||
Updating AUTHORS file:
|
Updating AUTHORS file:
|
||||||
|
|
||||||
|
|
@ -880,15 +1080,6 @@ nickl-
|
||||||
Leila Muhtasib
|
Leila Muhtasib
|
||||||
```
|
```
|
||||||
|
|
||||||
## git back
|
|
||||||
|
|
||||||
Removes the latest commits, and add their changes to your staging area.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git back # Removes the latest commit.
|
|
||||||
$ git back 3 # Remove the latest 3 commits.
|
|
||||||
```
|
|
||||||
|
|
||||||
## git changelog
|
## git changelog
|
||||||
|
|
||||||
Generates a changelog from git(1) tags (annotated or lightweight) and commit messages. Existing changelog files with filenames that begin with _Change_ or _History_ will be identified automatically with a case insensitive match pattern and existing content will be appended to the new output generated--this behavior can be disabled by specifying the prune option (-p|--prune-old). The generated file will be opened in **$EDITOR** when set.
|
Generates a changelog from git(1) tags (annotated or lightweight) and commit messages. Existing changelog files with filenames that begin with _Change_ or _History_ will be identified automatically with a case insensitive match pattern and existing content will be appended to the new output generated--this behavior can be disabled by specifying the prune option (-p|--prune-old). The generated file will be opened in **$EDITOR** when set.
|
||||||
|
|
@ -993,6 +1184,13 @@ git undo 3
|
||||||
|
|
||||||
Run grep as directed but replace the given files with the pattern.
|
Run grep as directed but replace the given files with the pattern.
|
||||||
|
|
||||||
|
For example,
|
||||||
|
```bash
|
||||||
|
$ git sed 'this' 'that'
|
||||||
|
$ git sed 'this' 'that' g
|
||||||
|
$ git sed 'this' 'that' -- path/ path2/
|
||||||
|
```
|
||||||
|
|
||||||
## git setup
|
## git setup
|
||||||
|
|
||||||
Set up a git repository (if one doesn't exist), add all files, and make an initial commit. `dir` defaults to the current working directory.
|
Set up a git repository (if one doesn't exist), add all files, and make an initial commit. `dir` defaults to the current working directory.
|
||||||
|
|
@ -1005,6 +1203,14 @@ Internally this script uses `rsync` and not `scp` as the name suggests.
|
||||||
|
|
||||||
`git-rscp` - The reverse of `git-scp`. Copies specific files from the working directory of a remote repository to the current working directory.
|
`git-rscp` - The reverse of `git-scp`. Copies specific files from the working directory of a remote repository to the current working directory.
|
||||||
|
|
||||||
|
The following options are available (must precede `<remote>`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
-v, --verbose Print what will be synced before syncing
|
||||||
|
-i, --interactive Prompt for confirmation before syncing (implies --verbose)
|
||||||
|
-n, --dry-run Show what would be synced, change nothing (implies --verbose)
|
||||||
|
```
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
Copy unstaged files to remote. Useful when you want to make quick test without making any commits
|
Copy unstaged files to remote. Useful when you want to make quick test without making any commits
|
||||||
|
|
@ -1161,11 +1367,11 @@ List all commits on the local branch that have not yet been sent to origin. Any
|
||||||
|
|
||||||
## git archive-file
|
## git archive-file
|
||||||
|
|
||||||
Creates an zip archive of the current git repository. The name of the archive will depend on the current HEAD of your git repository.
|
Creates a zip archive of the current git repository. The name of the archive will depend on the current HEAD of your git repository.
|
||||||
|
|
||||||
## git missing
|
## git missing
|
||||||
|
|
||||||
Print out which commits are on one branch or the other but not both.
|
Print out which commits are on one branch or the other but not both. Optionally, you can specify a path to limit the comparison to a specific directory or file.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git missing master
|
$ git missing master
|
||||||
|
|
@ -1173,6 +1379,12 @@ $ git missing master
|
||||||
> 97ef387 only on master
|
> 97ef387 only on master
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git missing master -- src/
|
||||||
|
< ed52989 only on current branch, in src/ directory
|
||||||
|
> 7988c4b only on master, in src/ directory
|
||||||
|
```
|
||||||
|
|
||||||
## git lock
|
## git lock
|
||||||
|
|
||||||
Lock a local file `filename`:
|
Lock a local file `filename`:
|
||||||
|
|
@ -1216,8 +1428,8 @@ $ git reset-file .htaccess dc82b19
|
||||||
|
|
||||||
## git mr
|
## git mr
|
||||||
|
|
||||||
Checks out a merge request from GitLab. Usage: `git mr <ID|URL> [REMOTE]`.
|
Checks out a merge request from GitLab, or a pull request from Forgejo/Codeberg.
|
||||||
Default remote is `origin`.
|
Usage: `git mr <ID|URL> [REMOTE]`. Default remote is `origin`.
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
$ git mr 51
|
$ git mr 51
|
||||||
|
|
@ -1235,9 +1447,42 @@ From gitlab.com:owner/repository
|
||||||
Switched to branch 'mr/51'
|
Switched to branch 'mr/51'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A Forgejo/Codeberg pull request URL is also supported:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
$ git mr https://codeberg.org/owner/repository/pulls/51
|
||||||
|
From codeberg.org:owner/repository
|
||||||
|
* [new ref] refs/pull/51/head -> mr/51
|
||||||
|
Switched to branch 'mr/51'
|
||||||
|
```
|
||||||
|
|
||||||
Just like [git pr](#git-pr), `git mr` accepts a `clean` argument to trash all
|
Just like [git pr](#git-pr), `git mr` accepts a `clean` argument to trash all
|
||||||
`mr/` branches. Ensure current branch is not one.
|
`mr/` branches. Ensure current branch is not one.
|
||||||
|
|
||||||
|
## git paste
|
||||||
|
|
||||||
|
Sends commits to a pastebin site using pastebinit.
|
||||||
|
|
||||||
|
By default it sends the commits between your current branch
|
||||||
|
and the branch your current branch is based on (the upstream branch).
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
$ git paste
|
||||||
|
https://paste.debian.net/1234567/
|
||||||
|
```
|
||||||
|
|
||||||
|
All options are passed to `git format-patch --stdout`
|
||||||
|
so you can also pass options understood by `git-rev-parse(1)`
|
||||||
|
in order to select a different set of commits.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
$ git paste @^
|
||||||
|
https://paste.debian.net/1234567/
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [pastebinit documentation](https://manpages.debian.org/pastebinit)
|
||||||
|
for information about how to chose a different pastebin site to the default.
|
||||||
|
|
||||||
## git pr
|
## git pr
|
||||||
|
|
||||||
Checks out a pull request from GitHub
|
Checks out a pull request from GitHub
|
||||||
|
|
@ -1386,3 +1631,99 @@ Sync local branch with origin/master
|
||||||
```bash
|
```bash
|
||||||
$ git sync origin master
|
$ git sync origin master
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## git browse
|
||||||
|
|
||||||
|
Opens the current git repository website in your default web browser.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git browse
|
||||||
|
|
||||||
|
$ git browse upstream
|
||||||
|
```
|
||||||
|
|
||||||
|
## git browse-ci
|
||||||
|
|
||||||
|
Opens the current git repository CI website (e.g. GitHub Actions, GitLab CI,
|
||||||
|
Bitbucket Pipelines) in your default web browser.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git browse-ci
|
||||||
|
|
||||||
|
$ git browse-ci upstream
|
||||||
|
```
|
||||||
|
|
||||||
|
## git utimes
|
||||||
|
|
||||||
|
Change files modification time to their last commit date. Does not touch files that are in the working tree or index.
|
||||||
|
|
||||||
|
The `--newer` flag preserves the original modification time of files that were committed from the local repo, by only touching files that are newer than their last commit date.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git-extras$ ls -l bin | head
|
||||||
|
total 308
|
||||||
|
-rwxr-xr-x 1 vt vt 489 Jul 28 2015 git-alias
|
||||||
|
-rwxr-xr-x 1 vt vt 1043 Nov 8 13:56 git-archive-file
|
||||||
|
-rwxr-xr-x 1 vt vt 970 Nov 8 13:56 git-authors
|
||||||
|
-rwxr-xr-x 1 vt vt 267 Nov 8 13:56 git-back
|
||||||
|
-rwxr-xr-x 1 vt vt 899 Nov 8 13:56 git-browse
|
||||||
|
-rwxr-xr-x 1 vt vt 1932 Nov 8 13:56 git-brv
|
||||||
|
-rwxr-xr-x 1 vt vt 6282 Nov 8 13:56 git-bulk
|
||||||
|
-rwxr-xr-x 1 vt vt 18561 Nov 8 13:56 git-changelog
|
||||||
|
-rwxr-xr-x 1 vt vt 215 Nov 8 13:56 git-clear
|
||||||
|
|
||||||
|
git-extras$ git utimes --newer
|
||||||
|
+ touch -d 2020-05-22T10:40:29+08:00 bin/git-archive-file
|
||||||
|
+ touch -d 2017-05-05T16:02:09+08:00 bin/git-authors
|
||||||
|
+ touch -d 2020-02-23T11:41:54+08:00 bin/git-back
|
||||||
|
+ touch -d 2020-06-23T09:31:21+10:00 bin/git-browse
|
||||||
|
+ touch -d 2020-01-15T10:46:19+01:00 bin/git-brv
|
||||||
|
+ touch -d 2019-12-21T13:35:59+08:00 bin/git-bulk
|
||||||
|
+ touch -d 2019-09-05T12:41:38+08:00 bin/git-changelog
|
||||||
|
+ touch -d 2016-11-19T16:41:19+00:00 bin/git-clear
|
||||||
|
[...]
|
||||||
|
|
||||||
|
git-extras$ ls -l bin | head
|
||||||
|
total 308
|
||||||
|
-rwxr-xr-x 1 vt vt 489 Jul 28 2015 git-alias
|
||||||
|
-rwxr-xr-x 1 vt vt 1043 May 22 05:40 git-archive-file
|
||||||
|
-rwxr-xr-x 1 vt vt 970 May 5 2017 git-authors
|
||||||
|
-rwxr-xr-x 1 vt vt 267 Feb 23 2020 git-back
|
||||||
|
-rwxr-xr-x 1 vt vt 899 Jun 23 02:31 git-browse
|
||||||
|
-rwxr-xr-x 1 vt vt 1932 Jan 15 2020 git-brv
|
||||||
|
-rwxr-xr-x 1 vt vt 6282 Dec 21 2019 git-bulk
|
||||||
|
-rwxr-xr-x 1 vt vt 18561 Sep 5 2019 git-changelog
|
||||||
|
-rwxr-xr-x 1 vt vt 215 Nov 19 2016 git-clear
|
||||||
|
```
|
||||||
|
|
||||||
|
Note above, that because of the `--newer` flag, the file `git-alias` was not touched since its modified date is earlier than the commit date.
|
||||||
|
|
||||||
|
## git abort
|
||||||
|
|
||||||
|
Abort current revert, rebase, merge or cherry-pick, without the need to find exact command in history.
|
||||||
|
|
||||||
|
## git magic
|
||||||
|
|
||||||
|
Commits changes with a generated message.
|
||||||
|
|
||||||
|
## git continue
|
||||||
|
|
||||||
|
Continue current revert, rebase, merge or cherry-pick, without the need to find exact command in history.
|
||||||
|
|
||||||
|
## git wip
|
||||||
|
|
||||||
|
Create a Work In Progress(WIP) commit, which will include all changes in the
|
||||||
|
working directory. (i.e., changes to existing files, new files, removed files)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git wip
|
||||||
|
```
|
||||||
|
|
||||||
|
## git unwip
|
||||||
|
|
||||||
|
Undo a Work In Progress(WIP) commit and put all of those changes back into the
|
||||||
|
working directory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git unwip
|
||||||
|
```
|
||||||
|
|
|
||||||
640
History.md
640
History.md
|
|
@ -1,3 +1,643 @@
|
||||||
|
|
||||||
|
7.5.0 / 2026-05-09
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Use standard prefix for home dir installs (#1248)
|
||||||
|
* refactor(abort): common `gitdir` (#1244)
|
||||||
|
* Fix man page usage docs (#1241)
|
||||||
|
* Fix `git-changelog` to handle whitespace in command arguments (#1232)
|
||||||
|
* chore(deps): bump bats-core/bats-action from 3.0.1 to 4.0.0 (#1230)
|
||||||
|
* add details when multiple active operations found (#1229)
|
||||||
|
* Add persistent history to `git-repl` (#1226)
|
||||||
|
* Support passing ref to `git-commits-since` (#1228)
|
||||||
|
* ability to specify command when hitting enter (#1223)
|
||||||
|
* Improve `git-repl` prompt (#1224)
|
||||||
|
* feat: `delete-branch` multiple unique branch names completions (#1221)
|
||||||
|
* chore(deps): bump actions/checkout from 5 to 6 (#1219)
|
||||||
|
* Made Git Magic use `--force-with-lease` (#1218)
|
||||||
|
* chore(deps): bump actions/stale from 9 to 10 (#1214)
|
||||||
|
* chore(deps): bump actions/setup-go from 5 to 6 (#1215)
|
||||||
|
* chore(deps): bump actions/setup-python from 5 to 6 (#1216)
|
||||||
|
* Fix minor typo in `git rename-branch` man page (#1211)
|
||||||
|
* Improvements to Bash completion (#1210)
|
||||||
|
* Fix use of `git-whatchanged` to `git-log` (#1212)
|
||||||
|
* chore(deps): bump actions/checkout from 4 to 5 (#1209)
|
||||||
|
* Bump version to 7.5.0-dev (#1207)
|
||||||
|
|
||||||
|
7.4.0 / 2025-06-19
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Format comparisons, functions, and redirections to be consistent (#1201)
|
||||||
|
* add git-wip and git-unwip (#669)
|
||||||
|
* chore(deps): bump bats-core/bats-action from 3.0.0 to 3.0.1
|
||||||
|
* Mostly finish pytest to Bats conversion (#1200)
|
||||||
|
* Implement half of tests in Bats (#1187)
|
||||||
|
* Add stale bot for old PRs (#1186)
|
||||||
|
* feat(git-bulk): add new option to not follow hidden directories (#1195)
|
||||||
|
* Feat: allow git-summary showing full path of repository (#1193)
|
||||||
|
* feat(git-bulk): add new option to no follow symlinks (#1194)
|
||||||
|
* docs(git-bulk): Add zsh completion (#1190)
|
||||||
|
* fix(git-bulk): fix workspace selection when cd fails (#1197)
|
||||||
|
* fix(git-bulk): quiet find errors by default (#1196)
|
||||||
|
* fix(git-bulk): fix a bad integer expression (#1198)
|
||||||
|
* chore(deps): bump astral-sh/ruff-action from 2 to 3 (#1189)
|
||||||
|
* Fix all ShellCheck errors and add to CI (#1179)
|
||||||
|
* chore(deps): bump astral-sh/ruff-action from 1 to 2 (#1188)
|
||||||
|
* fix(ci): use poetry (#1183)
|
||||||
|
* Delete etc/test.fish (#1185)
|
||||||
|
* feat: add git-continue (#1176)
|
||||||
|
* feat: add ruff linter with ci check (#1182)
|
||||||
|
* fix(ci): missing dollar sign (#1184)
|
||||||
|
* fix(github-actions): changed files output for editorcondig-checker (#1180)
|
||||||
|
* Revert "feat: add ruff linter with ci check (#1178)" (#1181)
|
||||||
|
* feat: add ruff linter with ci check (#1178)
|
||||||
|
* Support `GITHUB_TOKEN` var for `git-fork` and `git-pull-request` (#1177)
|
||||||
|
* Bump version to 7.4.0-dev (#1175)
|
||||||
|
|
||||||
|
7.3.0 / 2024-10-20
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Fix stripping trailing forward slash for git-get (#1172)
|
||||||
|
* Change git-cp to use cleaner branch approach (#1169)
|
||||||
|
* Improve warning for `git clear` (#1168)
|
||||||
|
* Enhance `git-repl` (#1160)
|
||||||
|
* Update some documentation that was out of sync (#1164)
|
||||||
|
* Use filetimes in check_integrity (#1162)
|
||||||
|
* Update git-alias.md: add brs to prevent incorrect line behavior (#1161)
|
||||||
|
* Update git-bulk.md: use correct stylization in synopsis (#1163)
|
||||||
|
* Update git-repl.md: typo: "let's" for "lets" (#1158)
|
||||||
|
* Update instructions for the OpenSUSE installation (#1157)
|
||||||
|
* Add pathspec support in `git-missing` (#1156)
|
||||||
|
* feat: add rename-file command (#1149)
|
||||||
|
* Update Commands.md (#1148)
|
||||||
|
* fix: git-summary commit count (#1147)
|
||||||
|
* tests: update dependencies (#1142)
|
||||||
|
* chore(deps): bump masesgroup/retrieve-changed-files from 2 to 3 (#1144)
|
||||||
|
* chore(deps): bump actions/setup-go from 4 to 5 (#1143)
|
||||||
|
* ci: add dependabot update for GitHub actions (#1141)
|
||||||
|
* ci: update actions (#1140)
|
||||||
|
* Bump version to 7.3.0-dev (#1139)
|
||||||
|
|
||||||
|
7.2.0 / 2024-04-21
|
||||||
|
==================
|
||||||
|
|
||||||
|
* refactor: test utils and simplify the case code (#1136)
|
||||||
|
* Add --proceed flag to git-delete-squashed-branches (#1134) (#1135)
|
||||||
|
* test(browse-ci): add unit tests (#1130)
|
||||||
|
* test(git-browse): add unit tests (#1127)
|
||||||
|
* feat: add reverse option to git-brv (#1123)
|
||||||
|
* fix(utimes): change /dev/stdin to -
|
||||||
|
* fix(utimes): Support filenames with backspaces, etc. (#1122)
|
||||||
|
* chore: add poetry to handle the tests of the git extras (#1121)
|
||||||
|
* Fix typo in ci.yml (#1120)
|
||||||
|
* Fix: harden utimes, use single quotes (#1109)
|
||||||
|
* Fix: patch git-utimes (fixes #1118) (#1119)
|
||||||
|
* add pull request manner (#1113)
|
||||||
|
* maintenance: Add my name as maintainer in AUTHORS (#1117)
|
||||||
|
* test(git-authors): add unit test (#1098)
|
||||||
|
* Add more comprensive dependencies (#1111)
|
||||||
|
* fix: git-standup find error (tj#1106) (#1110)
|
||||||
|
* Improve defaults for testing suite (#1104)
|
||||||
|
* feat: Speed up utimes (#1108)
|
||||||
|
* Overhaul CONTRIBUTING document (#1103)
|
||||||
|
* Bump version to 7.2.0-dev (#1099)
|
||||||
|
|
||||||
|
7.1.0 / 2023-10-28
|
||||||
|
==================
|
||||||
|
|
||||||
|
* git-cp: don't stop execution when merge.ff is not set (#1096)
|
||||||
|
* Allow `git cp` to create destination folder automatically (#1091)
|
||||||
|
* fix: Use `-iname` and expressions instead of `-iregex` (#1094)
|
||||||
|
* change installation source from master to main (#1092)
|
||||||
|
* test(git-archive-file): add unit test (#1084)
|
||||||
|
* ci: Use Go v1.20 for running `editorconfig-checker` (#1086)
|
||||||
|
* test(git-alias): add its unit test (#1077)
|
||||||
|
* makefile: Allow bypassing conflict check (#1080)
|
||||||
|
* fix: Restore zsh completions to working completion (#1079)
|
||||||
|
* fix: add missing quote in git-extras-completion.zsh (#1078)
|
||||||
|
* feat: Implement completion for `git coauthor` (#1074)
|
||||||
|
* fix: Move improvements via ShellCheck (#1075)
|
||||||
|
* feat(auto-test): CI with pytest (#1066)
|
||||||
|
* git-feature: add configurable branch separator (#1072)
|
||||||
|
* git-psykorebase: determine branch names with bash pattern substitution (#1073)
|
||||||
|
* git-cp: continue to merge when merge.ff is only (#1070)
|
||||||
|
* git archive-file: change base ARCHIVE_NAME (#1071)
|
||||||
|
* readme: Add secondary location of screencast (#1065)
|
||||||
|
* fix: Improve Bash hygiene (#1056)
|
||||||
|
* feat: Implement `git-get` command (#1045)
|
||||||
|
* Corrected grammar in bin/git-cp (#1061)
|
||||||
|
* Improve `git-ignore` command (#1047)
|
||||||
|
* I have made two improvements to the git-bulk: (#1054)
|
||||||
|
* Type properly `__gitex_command_successful` calls and capitalizate github to GitHub (#1049)
|
||||||
|
* Add typo checker
|
||||||
|
* Fix comment typo in git-authors (#1050)
|
||||||
|
* tweak: Fix typos (#1046)
|
||||||
|
* Makefile: init SYSCONFDIR before using it (#1044)
|
||||||
|
* Makefile: tune MANPREFIX configuration for FreeBSD (#1043)
|
||||||
|
* Makefile: set bash COMPL_DIR to FreeBSD expected defaults (#1042)
|
||||||
|
* Bump version to 7.1.0-dev (#1041)
|
||||||
|
|
||||||
|
7.0.0 / 2023-04-20
|
||||||
|
==================
|
||||||
|
|
||||||
|
* git-delete-tag: should not be quoted
|
||||||
|
* Add the current branch git summary (#1034)
|
||||||
|
* chore: Improve Bash variously (#1032)
|
||||||
|
* Change the oneline option to a tebular version in the git summary (#1031)
|
||||||
|
* chore: Remove `X`-style equality comparisons (#1028)
|
||||||
|
* chore: Various Bash improvements (#1029)
|
||||||
|
* fix: No longer pollute env with `GREP_OPTIONS`
|
||||||
|
* tweak editor config (#1027)
|
||||||
|
* revert the completion dir change on MacOS (#1026)
|
||||||
|
* Update gitignore.io urls to Toptal urls (#1025)
|
||||||
|
* Add EditorConfig file (#1022)
|
||||||
|
* Make the bulk output slimmer (#1020)
|
||||||
|
* chore: Fix more Bash inconsistencies (#1021)
|
||||||
|
* Repo status overview (#1017)
|
||||||
|
* Replace egrep with grep -E (#1019)
|
||||||
|
* chore: More Bash improvements and removing deprecations (#1016)
|
||||||
|
* chore: Improve quoting and builtin usage (#1015)
|
||||||
|
* Add summary fields (#1013)
|
||||||
|
* Fix quiet mode of the bulk command (#1014)
|
||||||
|
* git-summary: remove stray \ in grep call (#1010)
|
||||||
|
* Allow to run git-extras within git-bulk (#1008)
|
||||||
|
* Make git-summary accept multiple paths for the --lines argument (#1005)
|
||||||
|
* Makefile: ensure bash is used instead of /bin/sh (#1004)
|
||||||
|
* fix base branch in the pull-request command (#1003)
|
||||||
|
* Update docs for git abort change (#1000)
|
||||||
|
* Bump version to 6.6.0-dev (#997)
|
||||||
|
|
||||||
|
6.5.0 / 2022-10-06
|
||||||
|
==================
|
||||||
|
|
||||||
|
* git root: show '.' when using with -r in the root dir (#995)
|
||||||
|
* git standup: work as expected when no configured (#996)
|
||||||
|
* rephrase question depending on switch (#984)
|
||||||
|
* Include revert conflicts in git abort (#992)
|
||||||
|
* fix(git-effort):fork: retry: Resource temporarily unavailable #979 (#990)
|
||||||
|
* git-sync --force will sync without interaction (#989)
|
||||||
|
* fix(git-cp): keep the history of the files
|
||||||
|
* git-bulk: previous refactor redirected $PWD to stderr by mistake
|
||||||
|
* Fix lazy loading bash_completion from XDG_DATA_DIRS
|
||||||
|
* Minor changes to man/git-standup.md
|
||||||
|
* Replaced -g and -A with -F gpg|authordate
|
||||||
|
* fix(bin/git-browse): fix commit hash
|
||||||
|
* fix(bin/git-browse): fix bitbucket url
|
||||||
|
* feat: open website at file & line number or range
|
||||||
|
* #970 standup added -A flag for author date
|
||||||
|
* update docs due to formatting error in git-utimes man page
|
||||||
|
* update per shellcheck advice
|
||||||
|
* update git-utimes.{1,html} to add --newer flag
|
||||||
|
* Don't read git-extras.standup.implicit-week twice in git standup
|
||||||
|
* quote mod_s and git_s integer vars
|
||||||
|
* updates per request of spacewander
|
||||||
|
* Update Commands.md
|
||||||
|
* allow modification date updates for symbolic links
|
||||||
|
* add --newer flag and ignore files in the working tree or index
|
||||||
|
* Documented config git-extras.standup.implicit-week
|
||||||
|
* Minor change to git standup -w documentation
|
||||||
|
* Improved check if -w or -d was given to git standup
|
||||||
|
* Renamed config git-extras.standup.implicit-week from git-extras.standup-implicit-week
|
||||||
|
* Documented weekend behaviour of git standup -w
|
||||||
|
* docs(instllation): replace `git.io`
|
||||||
|
* Bump version to 6.5.0-dev
|
||||||
|
* Added config git-extras.standup-implicit-week for git standup
|
||||||
|
* Document git standup -w option
|
||||||
|
|
||||||
|
6.4.0 / 2022-04-22
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Merge pull request #964 from spacewander/bro
|
||||||
|
* git-browse/browse-ci: use powershell.exe only when it is available
|
||||||
|
* Merge pull request #944 from SimonTate/feature/sem-ver-prefix
|
||||||
|
* Merge pull request #957 from jackwasey/quiet-or-stderr
|
||||||
|
* git-release: Add prefix to semver
|
||||||
|
* Merge pull request #959 from pbnj/feat/git-browse-ci
|
||||||
|
* fix: fix stderr & stdout redirect
|
||||||
|
* Merge pull request #962 from spacewander/gicc
|
||||||
|
* git-ignore-io: make sure .gi_list is created before access
|
||||||
|
* git-ignore: the directory of target file may not exist
|
||||||
|
* fix(git-browse-ci): set pipefail
|
||||||
|
* docs: fix docs
|
||||||
|
* chore: update docs & completions
|
||||||
|
* feat: implement git-browse-ci
|
||||||
|
* quiet option, errors to stderr
|
||||||
|
* Merge pull request #956 from tfendin/xargs-conflicting-args
|
||||||
|
* Removed xargs from git-effort
|
||||||
|
* Merge pull request #955 from katrinleinweber/summary-highlight-path-opt
|
||||||
|
* Highlight path option more
|
||||||
|
* Merge pull request #951 from tfendin/local-commits-all-remotes
|
||||||
|
* Updated manual for git local-commits, must track a branch
|
||||||
|
* Merge pull request #946 from tfendin/default_branch_logic
|
||||||
|
* Made local-commits work with all remotes
|
||||||
|
* Updated Readme.md after review
|
||||||
|
* Merge pull request #949 from tfendin/git-ignore-ensure-newline
|
||||||
|
* Added section about main branch to the Readme file
|
||||||
|
* Merge pull request #947 from tfendin/git-unlock-synopsis-fix
|
||||||
|
* Merge pull request #948 from tfendin/git-ignore-core-excludesFile
|
||||||
|
* Clarified comment of when newline should be added
|
||||||
|
* Updated git-extras manual after review.
|
||||||
|
* git-ignore: ensure new patterns comes after a newline
|
||||||
|
* Fixed bug in git-ignore which ignored config core.excludeFiles
|
||||||
|
* Spell corr in git-unlock synopsis
|
||||||
|
* Consider init.defaultBranch in git_extra_default_branch, changed default branch to main.
|
||||||
|
* Merge pull request #885 from spacewander/add_pkg
|
||||||
|
* doc: add packaging status
|
||||||
|
* Merge pull request #940 from overengineer/feature/magic
|
||||||
|
* Ignored error when git restore --staged fails when there is no commit
|
||||||
|
* Merge branch 'master' into merge/magic
|
||||||
|
* Added git-magic command
|
||||||
|
* Bump version to 6.4.0-dev
|
||||||
|
|
||||||
|
6.3.0 / 2021-10-02
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Merge pull request #942 from spacewander/ffgd
|
||||||
|
* git-delete-squashed-branches: fail fast if can't checkout
|
||||||
|
* Merge pull request #939 from SimonTate/feature/force-clear
|
||||||
|
* git-clear: add force option
|
||||||
|
* Merge pull request #936 from tfendin/multiline_synopsis_fix
|
||||||
|
* Merge pull request #937 from tfendin/dedup_email_case_insensitive
|
||||||
|
* Merge pull request #934 from tfendin/no-merge-summary
|
||||||
|
* Adopted git --dedup-by-email to the project coding style
|
||||||
|
* summary --dedup-by-email now compares email adresses case insensitive.
|
||||||
|
* Updated lines under Synopsis in the manuals so they are commonly formatted
|
||||||
|
* Add incompability check for git summary --line --no-merges, split synopsis in its manual.
|
||||||
|
* Merge pull request #935 from ax1036/git-un-lock
|
||||||
|
* fix git (un)lock with spaced filenames
|
||||||
|
* Updated completion for git summary
|
||||||
|
* Updated documentation for git-summary --no-merges
|
||||||
|
* Added option --no-merges to git summary
|
||||||
|
* Merge pull request #932 from spacewander/git-su
|
||||||
|
* git-summary: support filtering `--line` with path
|
||||||
|
* Merge pull request #865 from pkitszel/pkitszel-git-abort
|
||||||
|
* Add git-abort
|
||||||
|
* Merge pull request #928 from duckunix/master
|
||||||
|
* Update point to github docs for forks from API
|
||||||
|
* update working
|
||||||
|
* git-fork | Update man/md/html pages to talk about need of the github personal access token
|
||||||
|
* Updated documentation (AUTHORS)
|
||||||
|
* bin/git-fork | updating for feedback from PR #928
|
||||||
|
* git-fork | update to match REST-API auth standards as of 2021-06
|
||||||
|
* Merge pull request #926 from nicokosi/patch-1
|
||||||
|
* Update install doc: Homebrew is not just for macOS
|
||||||
|
* Merge pull request #924 from allejo/feature/delete-squashed-branches
|
||||||
|
* delete-squashed-branches: Make side-effect clear in docs
|
||||||
|
* delete-squashed-branches: Make branch checkout side effect clear
|
||||||
|
* Add delete-squashed-branches command
|
||||||
|
* Merge pull request #923 from CleanMachine1/patch-1
|
||||||
|
* Picture is broken
|
||||||
|
* Merge pull request #920 from matan129/patch-1
|
||||||
|
* Ignore checked-out branches on different worktrees
|
||||||
|
* Merge pull request #917 from heirecka/remove-git-line-summary-man-page
|
||||||
|
* Bump version to 6.3.0-dev
|
||||||
|
* doc: Remove man page for git-line-summary
|
||||||
|
|
||||||
|
6.2.0 / 2021-03-26
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Merge pull request #915 from spacewander/typo
|
||||||
|
* Merge pull request #914 from spacewander/xch
|
||||||
|
* Merge pull request #916 from spacewander/utimes
|
||||||
|
* fix(git-utimes): make sure it work under OS X.
|
||||||
|
* chore: fix typo
|
||||||
|
* fix(git-ignore): use $XDG_CONFIG_HOME only when it is defined
|
||||||
|
* Merge pull request #910 from 0xflotus/patch-1
|
||||||
|
* fixed small error
|
||||||
|
* Merge pull request #908 from nodeg/fix_installation
|
||||||
|
* Fix grammer
|
||||||
|
* Correct macOS spelling
|
||||||
|
* Correct openSUSE spelling and update Version
|
||||||
|
* Merge pull request #907 from bbenzikry/zinit-docs
|
||||||
|
* Update zinit docs
|
||||||
|
* Merge pull request #906 from prestontim/master
|
||||||
|
* Remove bug|refactor|chore from documentation
|
||||||
|
* Merge pull request #899 from spacewander/git-summary
|
||||||
|
* git-summary: reject invalid option
|
||||||
|
* ci: use checkout@v2
|
||||||
|
* Merge pull request #897 from spacewander/ga
|
||||||
|
* ci: move to GitHub Action
|
||||||
|
* Merge pull request #882 from vt-alt/git-utimes
|
||||||
|
* Merge pull request #896 from Aloxaf/fix_completion
|
||||||
|
* fix: zsh completion of git-coauthor command
|
||||||
|
* Merge pull request #895 from vanpipy/feature/optional-start-point-when-creeate-branch
|
||||||
|
* doc: add the usage of the parameter `--from` for create-branch and feature
|
||||||
|
* feat(feature): checkout from start point if exists
|
||||||
|
* feat(create-branch): add optional --from to set the start point
|
||||||
|
* Merge pull request #894 from alessandro308/merge-into-stashed
|
||||||
|
* Merge-into: add stash before to checkout
|
||||||
|
* Merge pull request #892 from equt/#891
|
||||||
|
* Merge pull request #888 from equt/master
|
||||||
|
* Merge pull request #889 from vanpipy/feature/delete-tag
|
||||||
|
* [ fix #891 ] Cast branch output if HEAD not exists
|
||||||
|
* [ feat ] Quote the default_path var
|
||||||
|
* [ fix ] Quote env var
|
||||||
|
* [ fix ] Quote the XDG_CACHE_HOME
|
||||||
|
* [ feat ] Use default global ignore file
|
||||||
|
* [ refactor ] Optimize var not existing check
|
||||||
|
* [ fix ] Fix hardcode ignore list file path
|
||||||
|
* [ feat ] Add XDG_CACHE_HOME check
|
||||||
|
* Merge pull request #890 from zhujian0805/master
|
||||||
|
* adding a format for git-contrib
|
||||||
|
* git-utimes: Change files modification time to their last commit date
|
||||||
|
* feat(delete-tag): get default remote from git config if exists
|
||||||
|
* Merge pull request #884 from elonderin/fix-squash-help
|
||||||
|
* squash help: re-ran make for extras and squash
|
||||||
|
* squash help: ran make man/git-extras.{html,1}
|
||||||
|
* squash help: ran make man/git-squash.{html,1}
|
||||||
|
* squash help: corrects option name in PR
|
||||||
|
* Merge remote-tracking branch 'u/master' into HEAD
|
||||||
|
* Merge pull request #886 from TheTechOddBug/master
|
||||||
|
* Fixing summary for --squash-msg option.
|
||||||
|
* Updates git-squash.md: fixes help which was unclear and had remnants from copied template it appears
|
||||||
|
* Bump version to 6.2.0-dev
|
||||||
|
|
||||||
|
6.1.0 / 2020-09-26
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Merge pull request #878 from spacewander/default_br
|
||||||
|
* Merge pull request #880 from spacewander/fix_bsd_sed_regex
|
||||||
|
* git-info: fix regex for bsd sed
|
||||||
|
* Merge pull request #879 from spacewander/pass_arg
|
||||||
|
* feat: pass origin argument down to git execution
|
||||||
|
* change: migrate default branch
|
||||||
|
* Merge pull request #872 from yuravg/pr_typo
|
||||||
|
* doc: fix typo
|
||||||
|
* Merge pull request #869 from spacewander/typo
|
||||||
|
* git-back: removed
|
||||||
|
* fix typo
|
||||||
|
* Merge pull request #868 from spacewander/zsh_completion
|
||||||
|
* sort completion in alphabetical order
|
||||||
|
* remove incorrect completion
|
||||||
|
* git-info: add zsh completion
|
||||||
|
* Merge pull request #867 from yuravg/pr2source
|
||||||
|
* doc: fix argument description
|
||||||
|
* git-info: moved argument checking
|
||||||
|
* git-info: fix namespace
|
||||||
|
* doc: update derived git-info files
|
||||||
|
* doc: add description for git-info
|
||||||
|
* Add bash completion for git-info
|
||||||
|
* git-info: colorized headers
|
||||||
|
* git-info: extend configuration and commit information
|
||||||
|
* git-info: remove notes about git-log, git-show
|
||||||
|
* git-info: add info about submodules
|
||||||
|
* git-info: fix indentation, remove comment
|
||||||
|
* Merge pull request #864 from v-y-a-s/pr
|
||||||
|
* Grammar correction
|
||||||
|
* Remove $user
|
||||||
|
* corrections
|
||||||
|
* Update doc
|
||||||
|
* fix config message name
|
||||||
|
* Remove comments
|
||||||
|
* PR test
|
||||||
|
* Merge pull request #860 from soraxas/implement-fish-completions
|
||||||
|
* implement fish completions file
|
||||||
|
* Merge pull request #853 from bethesque/feat/git-browse-with-multiple-remotes
|
||||||
|
* feat(browse): automatically select origin remote if one exists, fallback to first otherwise
|
||||||
|
* Merge pull request #854 from bethesque/fix/git-browse-sed-on-mac
|
||||||
|
* fix(browse): update sed pattern to work on mac
|
||||||
|
* Bump version to 6.1.0-dev
|
||||||
|
|
||||||
|
6.0.0 / 2020-06-22
|
||||||
|
==================
|
||||||
|
|
||||||
|
* doc: remove gitter
|
||||||
|
* git-cp: remove trailing space
|
||||||
|
* Merge pull request #849 from vr8hub/undodoc
|
||||||
|
* Merge pull request #850 from vr8hub/newundo
|
||||||
|
* Tighten numeric regex
|
||||||
|
* Cleanup code
|
||||||
|
* Modify to work when only a single commit, add parameter checks
|
||||||
|
* Correct undo documentation to match code order of parameters
|
||||||
|
* Merge pull request #847 from Amorymeltzer/patch-1
|
||||||
|
* docs: Note flag for git extra --version
|
||||||
|
* Merge pull request #846 from spacewander/note
|
||||||
|
* doc: add a note about the package maintainer.
|
||||||
|
* Merge pull request #844 from ihoro/installation-via-freebsd-pkg
|
||||||
|
* Merge pull request #843 from spacewander/remove_git_feature
|
||||||
|
* break change: remove docs and other stuff of git-feature's alias
|
||||||
|
* Merge pull request #845 from alerque/remove-feature-aliases
|
||||||
|
* Remove scripts that are mere alias wrappers for git-feature
|
||||||
|
* docs: mention installation via FreeBSD pkg
|
||||||
|
* Merge pull request #840 from spacewander/archive-invalid
|
||||||
|
* Merge pull request #841 from sgleizes/bugfix/git-release-zsh-completion
|
||||||
|
* Fix missing newline escapes in git-release zsh completion
|
||||||
|
* git-archive-file: rename invalid chars in the output filename
|
||||||
|
* Merge pull request #837 from jldugger/jldugger/pr-remote-default
|
||||||
|
* Merge pull request #836 from jldugger/jldugger/feature-config
|
||||||
|
* add a default for the remote to fetch a pr from
|
||||||
|
* add a config setting for prefix
|
||||||
|
* Merge pull request #829 from cpradog/master
|
||||||
|
* update zsh completion for bug and refactor commands
|
||||||
|
* updated zsh completion script
|
||||||
|
* updated git-feature documentation
|
||||||
|
* add squash option to git-feature
|
||||||
|
* add squash option to git-feature
|
||||||
|
* Merge pull request #826 from Natim/feature/825/add-git-cp
|
||||||
|
* Simplify the code.
|
||||||
|
* Signal if the destination already exists.
|
||||||
|
* @spacewander review.
|
||||||
|
* Merge pull request #827 from Natim/fix-man-page-for-git-psykorebase
|
||||||
|
* Fix checks.
|
||||||
|
* Update man page export for git-psykorebase with ronn-ng 0.9.0
|
||||||
|
* @mschneiderwind review.
|
||||||
|
* Add documentation.
|
||||||
|
* Add first version of git-cp
|
||||||
|
* Merge pull request #824 from mapitman/master
|
||||||
|
* Fix documentation for git-browse
|
||||||
|
* Merge pull request #823 from mapitman/master
|
||||||
|
* Implement suggested changes from code review
|
||||||
|
* Add git-browse command
|
||||||
|
* Merge pull request #820 from spacewander/deprecate_git_back
|
||||||
|
* change: deprecate git-back
|
||||||
|
* Merge pull request #817 from bric3/adds-git-brv
|
||||||
|
* Fixes #770: Adds git-brv
|
||||||
|
* Merge pull request #816 from spacewander/git-sed-pathspec
|
||||||
|
* git-sed: limit paths via pathspec
|
||||||
|
* Bump version to 5.2.0-dev.
|
||||||
|
|
||||||
|
5.1.0 / 2019-12-21
|
||||||
|
==================
|
||||||
|
|
||||||
|
* git-bulk: support cloning from a relative path
|
||||||
|
* git-standuo: clarify the -a option
|
||||||
|
* git-paste: check pastebinit before running it
|
||||||
|
* git-coauthor: quote variable in the comparison
|
||||||
|
* git-bulk: don't disappoint shellcheck
|
||||||
|
* Merge pull request #812 from spacewander/git-sed-escape
|
||||||
|
* git-sed: escape special characters for tr
|
||||||
|
* Merge pull request #811 from eli-schwartz/portability
|
||||||
|
* build: do not require the nonstandard and unpredictable 'which' utility
|
||||||
|
* add repositories to workspace from source (#804)
|
||||||
|
* Merge pull request #810 from roxchgt/read-not-need-repo
|
||||||
|
* read COMMANDS_WIHOUT_REPO from not_need_git_repo file
|
||||||
|
* Merge pull request #806 from spacewander/retire_make_with_msys
|
||||||
|
* Merge pull request #808 from drasill/master
|
||||||
|
* git-ignore: support non-default .git directory
|
||||||
|
* retire the msysgit installation script
|
||||||
|
* Merge pull request #802 from spacewander/fix-git-bulk-arg-count
|
||||||
|
* git-bulk: line break is need in the SYNOPSIS doc section
|
||||||
|
* fix bug in allowedargcount() to not execute command if wrong args count
|
||||||
|
* Merge pull request #801 from spacewander/fix-delete-submodule
|
||||||
|
* git-delete-submodule: don't remove the history of deleted submodule
|
||||||
|
* Merge pull request #798 from wolviecb/signed_release
|
||||||
|
* Add flags -s and -u for signed tags
|
||||||
|
* Merge pull request #795 from spacewander/git-squash-commit-msg
|
||||||
|
* Merge pull request #793 from spacewander/git-standup-group-by-branch
|
||||||
|
* git-squash: miss completion script change
|
||||||
|
* git-squash: clean up shellcheck warnings
|
||||||
|
* git-squash: add --squash-messages to concat commit messages into one.
|
||||||
|
* git-standup: add option to limit the number of commit
|
||||||
|
* Merge pull request #789 from go2null/delete-empty-gitmodules
|
||||||
|
* git-delete-submodule: comment the DUMMY prefix and ignore the output of grep -v.
|
||||||
|
* git-standup: add separate newline between the output
|
||||||
|
* add PR changes
|
||||||
|
* add --force option to continue on error
|
||||||
|
* improve readability
|
||||||
|
* display git submodule status output before exiting
|
||||||
|
* check git submodule status before declaring success
|
||||||
|
* reorganize flow
|
||||||
|
* delete empty .gitmodules
|
||||||
|
* git-standup: add option '-B' to group the commits by branch
|
||||||
|
* Merge pull request #792 from spacewander/git-summary-cleanup
|
||||||
|
* git-summary: clean up other shellcheck warnings
|
||||||
|
* git-summary: fix incorrect active days when commits range is given
|
||||||
|
* git-summary: remove useless result function.
|
||||||
|
* Merge pull request #790 from spacewander/git-summary-merge-email
|
||||||
|
* git-summary: add --dedup-by-email to remove duplicate users
|
||||||
|
* Merge pull request #788 from spacewander/doc_git_summary_line
|
||||||
|
* git-summary: add missing example of --line option
|
||||||
|
* Merge pull request #787 from rvbuelow/patch-1
|
||||||
|
* Merge pull request #786 from mbologna/patch-1
|
||||||
|
* Use GIT_DIR environment variable to to set .git directory
|
||||||
|
* Docs: add OpenSUSE distribution information
|
||||||
|
* Merge pull request #784 from spacewander/git-effort-col-len
|
||||||
|
* git-effort: adjust column limit according to the paths
|
||||||
|
* Merge pull request #708 from pabs3/git-paste
|
||||||
|
* Regenerate docs
|
||||||
|
* Add git-paste for sending patches to pastebin
|
||||||
|
* Merge pull request #778 from pabs3/cleanups
|
||||||
|
* Switch from using /tmp to using mktemp
|
||||||
|
* Switch http URLs to https where possible
|
||||||
|
* Fix typos
|
||||||
|
* Fix an indefinite article in the documentation
|
||||||
|
* Merge pull request #776 from spacewander/update_git_pr_doc
|
||||||
|
* Merge pull request #777 from fengkx/zplugin-install-completion
|
||||||
|
* docs(Installation): :memo: add src in zplugin ice modifier to source the completion file
|
||||||
|
* git-pr: mention the limitation of -m option
|
||||||
|
* Merge pull request #772 from jacobherrington/add-git-coauthor
|
||||||
|
* Add conditional logic for linebreaks
|
||||||
|
* Ensure arguments exist
|
||||||
|
* Add git coauthor command
|
||||||
|
* improve the quality of the integrity check.
|
||||||
|
* Merge pull request #774 from jacobherrington/patch-2
|
||||||
|
* Bump version to 5.1.0-dev.
|
||||||
|
* Link to the contributing document
|
||||||
|
|
||||||
|
5.0.0 / 2019-08-16
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Merge pull request #769 from spacewander/no_line_summary
|
||||||
|
* git-line-summary: should be the history
|
||||||
|
* Merge pull request #768 from bric3/fix-more-character-encoding-issues
|
||||||
|
* git-guilt: protect against encoding issues with LC_ALL=C
|
||||||
|
* Merge pull request #767 from spacewander/rename_branch_order
|
||||||
|
* git-rename-branch: change branch argument order
|
||||||
|
* Merge pull request #765 from spacewander/column_dependency
|
||||||
|
* Makefile: check dependencies before installation.
|
||||||
|
* Installation.md: add dependencies section.
|
||||||
|
* man/Readme.md: we don't need to update git-extras docs separately
|
||||||
|
* Merge pull request #763 from spacewander/strict_check_integrity
|
||||||
|
* check_integrity.sh: check more strickly.
|
||||||
|
* Merge pull request #762 from spacewander/pr_merge
|
||||||
|
* Merge pull request #761 from btmurrell/preference-for-create-branch
|
||||||
|
* implements remote pref for create-branch
|
||||||
|
* git-pr: add -m|--merge option to check out a merge commit
|
||||||
|
* impl for checking remote pref
|
||||||
|
* Merge pull request #754 from spacewander/prompt_passwd_before_2fa
|
||||||
|
* git-fork: prompt for password before 2FA code.
|
||||||
|
* check_integrity.sh: improve readability.
|
||||||
|
* Merge pull request #753 from tiemonl/GH-752_update_reauthor_documentation
|
||||||
|
* GH-752 updated documentation to show rename workaround
|
||||||
|
* Merge pull request #751 from timfeirg/master
|
||||||
|
* git-mv-remote: rename a remote regardless of any existing remotes
|
||||||
|
* Installation instructions added for CRUX (#746)
|
||||||
|
* Merge pull request #723 from bittner/feature/git-undo-soft-leave-changes-staged
|
||||||
|
* Merge pull request #744 from spacewander/git-guilt-identation
|
||||||
|
* git-guilt: avoid exceeding 80 columns.
|
||||||
|
* Bump version to 4.8.0-dev.
|
||||||
|
* Make `git undo -s` restore the staging area
|
||||||
|
|
||||||
|
4.7.0 / 2019-02-09
|
||||||
|
==================
|
||||||
|
|
||||||
|
* git-pr: emphasized that the remote is required when pulling multiple PRs.
|
||||||
|
* Merge pull request #743 from spacewander/allow_multiple_pr
|
||||||
|
* git-pr: accepted multiple GitHub URL or ID with remote.
|
||||||
|
* git-pr: added <[remote]:pr number> option.
|
||||||
|
* Merge pull request #740 from spacewander/tweak_man_readme
|
||||||
|
* doc: clarify the current working directory when building the docs.
|
||||||
|
* Merge pull request #739 from sleagon/master
|
||||||
|
* feat: Add -s(--soft) for git sync
|
||||||
|
* Merge pull request #738 from StuartFeldt/add-symlinks-to-bulk-command
|
||||||
|
* Adding support for symlink directories in bulk command
|
||||||
|
* Merge pull request #737 from smancill/standup-ensure-colors
|
||||||
|
* git-standup: ensure color usage
|
||||||
|
* Merge pull request #736 from spacewander/git_force_clone_not_need_git_repo
|
||||||
|
* misc: sort command list in alphabetical order
|
||||||
|
* git-force-clone: should not need git repo
|
||||||
|
* Merge pull request #734 from zentarul/master
|
||||||
|
* Fix missing double quotes after %DEBUG%
|
||||||
|
* install.cmd: tweak comments
|
||||||
|
* Merge pull request #732 from sachin-gupta/sg-mods
|
||||||
|
* Fix: Dev: `/E` option required with MORE for working on higher version of Windows 10 (**_Build 17134_**)
|
||||||
|
* Merge pull request #729 from orestisf1993/patch-1
|
||||||
|
* git-rename-branch: Don't fail if remote doesn't exist
|
||||||
|
* Bump version to 4.7.0-dev.
|
||||||
|
|
||||||
|
4.6.0 / 2018-08-11
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Merge pull request #727 from spacewander/strip_caret
|
||||||
|
* Makefile: change the installed manpage destination in FreeBSD (#725)
|
||||||
|
* git-changelog: should remove ^0 suffix from the commit description.
|
||||||
|
* Merge pull request #722 from spacewander/git_changelog_start_commit
|
||||||
|
* git-changelog: add --start-commit option, like --start-tag but for commit
|
||||||
|
* Merge pull request #721 from spacewander/git_changelog_list_newline
|
||||||
|
* git-changelog: add missing newline to the plain output
|
||||||
|
* Merge pull request #713 from pabs3/docs
|
||||||
|
* Merge pull request #717 from flatcap/ignore-private
|
||||||
|
* optimisation of mkdir
|
||||||
|
* Ensure info dir exists
|
||||||
|
* add: git-ignore -p (private to repo)
|
||||||
|
* Merge pull request #716 from francoism90/patch-1
|
||||||
|
* Added Arch Linux
|
||||||
|
* Merge pull request #714 from spacewander/fix_detect_sed_i_support
|
||||||
|
* Documentation rebuild: git-sed.* flags argument
|
||||||
|
* Documentation rebuild: git-clear.html whitespace removal
|
||||||
|
* Documentation rebuild: git-extras.* updates
|
||||||
|
* Documentation rebuild: index.txt updates
|
||||||
|
* Documentation rebuild: dates and headers
|
||||||
|
* Remove one of the documentation building tools
|
||||||
|
* Remove some trailing whitespace
|
||||||
|
* Merge pull request #715 from tjaartvdwalt/master
|
||||||
|
* Major/Minor release should reset minor/patch versions to 0
|
||||||
|
* Fix greedy match of version number
|
||||||
|
* git-sed: fix previous bsd sed workaround
|
||||||
|
* Merge pull request #712 from spacewander/detect_sed_i_support
|
||||||
|
* git-sed: detect sed -i support and work around for the BSD sed
|
||||||
|
* Merge pull request #709 from pabs3/git-sed
|
||||||
|
* git-sed: discover a separator when the / character is used in arguments
|
||||||
|
* git-sed: pass the -r option to the xargs command
|
||||||
|
* git-sed: Allow the flags to be passed as a third argument
|
||||||
|
* Merge pull request #706 from MontakOleg/delete-branch
|
||||||
|
* Fix deleting branches without upstream
|
||||||
|
* Merge pull request #704 from ramlev/master
|
||||||
|
* Remove double path delimiters in zsh description
|
||||||
|
* Merge pull request #703 from psprint/master
|
||||||
|
* Installation.md: Zsh-plugin method which doesn't require root access
|
||||||
|
* Bump version to 4.6.0-dev.
|
||||||
|
|
||||||
4.5.0 / 2018-02-18
|
4.5.0 / 2018-02-18
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,31 @@
|
||||||
# Installing git-extras
|
# Installation
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Some commands require extra dependencies which are unavailable in some platforms.
|
||||||
|
You may need to install them manually. They are:
|
||||||
|
|
||||||
|
* bash 4.0+
|
||||||
|
* Git 2.17+
|
||||||
|
* `column`
|
||||||
|
* `awk`
|
||||||
|
* `find`
|
||||||
|
* `tput`
|
||||||
|
* cURL (only required for `git-fork`, `git-ignore-io`, `git-pull-request`)
|
||||||
|
* `ps` (only required for `git-changelog`)
|
||||||
|
* `rsync` (only required for `git-rscp`, `git-scp`)
|
||||||
|
* `xargs` (only required for `git-delete-merged-branches`, `git-force-clone`, `git-sed`, `git-scp`)
|
||||||
|
|
||||||
|
If `bash --version` shows a lower version, you need to update it.
|
||||||
|
For example, the default bash in Mac is usually too old and you may need to update it via `brew install bash`.
|
||||||
|
|
||||||
## Installing with a package manager
|
## Installing with a package manager
|
||||||
|
|
||||||
|
Note that only the Homebrew package is maintained by the git-extras developers directly.
|
||||||
|
Other packages are maintained by the distribution's packagers or third-party volunteers.
|
||||||
|
|
||||||
|
[](https://repology.org/project/git-extras/versions)
|
||||||
|
|
||||||
### Debian
|
### Debian
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -15,6 +39,20 @@ $ sudo $apt_pref install git-extras
|
||||||
$ sudo dnf install git-extras
|
$ sudo dnf install git-extras
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### openSUSE
|
||||||
|
|
||||||
|
Substitute your openSUSE version in the command below (in this case we are considering openSUSE Leap 15.6):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ sudo zypper ar https://download.opensuse.org/repositories/devel:/tools:/scm/15.6/devel:tools:scm.repo
|
||||||
|
```
|
||||||
|
|
||||||
|
and install it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ sudo zypper in -y git-extras
|
||||||
|
```
|
||||||
|
|
||||||
### RHEL/CentOS (requires [EPEL](https://fedoraproject.org/wiki/EPEL))
|
### RHEL/CentOS (requires [EPEL](https://fedoraproject.org/wiki/EPEL))
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -33,7 +71,11 @@ $ sudo apt-get install git-extras
|
||||||
$ nix-env -i git-extras
|
$ nix-env -i git-extras
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mac OS X with Homebrew
|
### CRUX
|
||||||
|
|
||||||
|
[Abdullah](https://github.com/AWAN) has written a [Pkgfile](https://abdullah.today/ports/git-extras/Pkgfile) for his beloved [distro](https://crux.nu).
|
||||||
|
|
||||||
|
### Homebrew
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ brew install git-extras
|
$ brew install git-extras
|
||||||
|
|
@ -41,10 +83,16 @@ $ brew install git-extras
|
||||||
|
|
||||||
Installing from Homebrew will not give you the option omit certain `git-extras` if they conflict with existing git aliases. To have this option, build from source.
|
Installing from Homebrew will not give you the option omit certain `git-extras` if they conflict with existing git aliases. To have this option, build from source.
|
||||||
|
|
||||||
|
### Arch Linux
|
||||||
|
|
||||||
|
* [git-extras](https://aur.archlinux.org/packages/git-extras/)
|
||||||
|
* [git-extras-git](https://aur.archlinux.org/packages/git-extras-git/)
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
First, please install `Git for Windows 2.x` from 'https://github.com/git-for-windows/git/releases'.
|
First, please install `Git for Windows 2.x` from 'https://github.com/git-for-windows/git/releases'.
|
||||||
Second, clone the `git-extras` repo into any folder you like.
|
Second, clone the `git-extras` repo into any folder you like.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/tj/git-extras.git
|
git clone https://github.com/tj/git-extras.git
|
||||||
# checkout the latest tag (optional)
|
# checkout the latest tag (optional)
|
||||||
|
|
@ -63,16 +111,24 @@ in `c:\git`:
|
||||||
install.cmd "C:\git"
|
install.cmd "C:\git"
|
||||||
```
|
```
|
||||||
|
|
||||||
Last, to use `git line-summary`, `git summary` and `git ignore-io`, you need to copy
|
Last, to use `git summary` and `git ignore-io`, you need to copy
|
||||||
`column.exe` from a [msys2][1] installation from `folder-your-msys2-installed/usr/bin`
|
`column.exe` from a [msys2][1] installation from `folder-your-msys2-installed/usr/bin`
|
||||||
to `folder-your-git-installed/usr/bin` or wait for git 2.7.1, which will include column.exe.
|
to `folder-your-git-installed/usr/bin` or wait for git 2.7.1, which will include column.exe.
|
||||||
|
|
||||||
|
### FreeBSD
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pkg install git-extras
|
||||||
|
```
|
||||||
|
|
||||||
### BSD
|
### BSD
|
||||||
|
|
||||||
Use the instructions to build from source below. Make sure you are using `gmake` (GNU `make`) instead of `make`.
|
Use the instructions to build from source below. Make sure you are using `gmake` (GNU `make`) instead of `make`.
|
||||||
|
|
||||||
## Building from source
|
## Building from source
|
||||||
|
|
||||||
|
Read [Dependencies](#dependencies) and ensure they are installed.
|
||||||
|
|
||||||
Obtain the git-extras source by cloning [its GitHub repo](https://github.com/tj/git-extras.git) or downloading a tarball of a [release](https://github.com/tj/git-extras/releases). Then install it by doing `make install` from the source tree.
|
Obtain the git-extras source by cloning [its GitHub repo](https://github.com/tj/git-extras.git) or downloading a tarball of a [release](https://github.com/tj/git-extras/releases). Then install it by doing `make install` from the source tree.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -88,7 +144,7 @@ alternate location, specify a `PREFIX` when calling `make`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Non-root users can install under their home directory
|
# Non-root users can install under their home directory
|
||||||
make install PREFIX=$HOME/software
|
make install PREFIX=$HOME/.local
|
||||||
|
|
||||||
# For third-party software kept under /opt
|
# For third-party software kept under /opt
|
||||||
make install PREFIX=/opt
|
make install PREFIX=/opt
|
||||||
|
|
@ -98,7 +154,7 @@ If you want to relocate the bulk of the installation but still have configuratio
|
||||||
files installed to the system `/etc` dir or other alternate location, you can
|
files installed to the system `/etc` dir or other alternate location, you can
|
||||||
specify `SYSCONFDIR` in addition to `PREFIX`.
|
specify `SYSCONFDIR` in addition to `PREFIX`.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
$ sudo make install PREFIX=/usr/local SYSCONFDIR=/etc
|
$ sudo make install PREFIX=/usr/local SYSCONFDIR=/etc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -109,9 +165,26 @@ See the Makefile for details on advanced configuration options.
|
||||||
One-liner:
|
One-liner:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -sSL http://git.io/git-extras-setup | sudo bash /dev/stdin
|
curl -sSL https://raw.githubusercontent.com/tj/git-extras/main/install.sh | sudo bash /dev/stdin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Installing as Zsh plugin
|
||||||
|
|
||||||
|
[ZInit](https://github.com/zdharma/zinit) can install git-extras by using:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
zinit ice as"program" pick"$ZPFX/bin/git-*" src"etc/git-extras-completion.zsh" make"PREFIX=$ZPFX"
|
||||||
|
zinit light tj/git-extras
|
||||||
|
|
||||||
|
# or with the for syntax + async load
|
||||||
|
zinit lucid wait'0a' for \
|
||||||
|
as"program" pick"$ZPFX/bin/git-*" src"etc/git-extras-completion.zsh" make"PREFIX=$ZPFX" tj/git-extras
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
`$ZPFX` is `~/.zinit/polaris` by default. Use `zinit update tj/git-extras` to update.
|
||||||
|
This method installs in `$HOME`, so you don't need to ask administrator to install package.
|
||||||
|
|
||||||
## Updating
|
## Updating
|
||||||
|
|
||||||
If you installed git-extras with a package manager, use that package manager's tools to update it.
|
If you installed git-extras with a package manager, use that package manager's tools to update it.
|
||||||
|
|
@ -120,4 +193,4 @@ If you installed git-extras from source, you can run `git-extras update` to upda
|
||||||
|
|
||||||
Enjoy `git-extras`!
|
Enjoy `git-extras`!
|
||||||
|
|
||||||
[1]: http://sourceforge.net/projects/msys2/
|
[1]: https://sourceforge.net/projects/msys2/
|
||||||
|
|
|
||||||
87
Makefile
87
Makefile
|
|
@ -1,9 +1,28 @@
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
BINPREFIX ?= "$(PREFIX)/bin"
|
BINPREFIX ?= "$(PREFIX)/bin"
|
||||||
MANPREFIX ?= "$(PREFIX)/share/man/man1"
|
|
||||||
SYSCONFDIR ?= $(PREFIX)/etc
|
SYSCONFDIR ?= $(PREFIX)/etc
|
||||||
|
SHELL := bash
|
||||||
|
|
||||||
|
OS = $(shell uname)
|
||||||
|
ifeq ($(OS), FreeBSD)
|
||||||
|
MANPREFIX ?= "$(PREFIX)/man/man1"
|
||||||
|
ifeq ($(MANPREFIX), /usr/local)
|
||||||
|
MANPREFIX = "/usr/local/man/man1"
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
MANPREFIX ?= "$(PREFIX)/share/man/man1"
|
||||||
|
endif
|
||||||
|
ifeq ($(OS), Darwin)
|
||||||
|
COMPL_DIR ?= "$(DESTDIR)$(SYSCONFDIR)/bash_completion.d"
|
||||||
|
else ifeq ($(OS), FreeBSD)
|
||||||
|
COMPL_DIR ?= "$(DESTDIR)$(SYSCONFDIR)/bash_completion.d"
|
||||||
|
else
|
||||||
|
COMPL_DIR ?= "$(DESTDIR)$(SYSCONFDIR)/bash-completion/completions"
|
||||||
|
endif
|
||||||
|
|
||||||
BINS = $(wildcard bin/git-*)
|
BINS = $(wildcard bin/git-*)
|
||||||
MANS = $(wildcard man/git-*.md)
|
MANS = $(wildcard man/git-*.md)
|
||||||
|
MAN_BINS = $(filter-out man/git-extras.md, $(MANS))
|
||||||
MAN_HTML = $(MANS:.md=.html)
|
MAN_HTML = $(MANS:.md=.html)
|
||||||
MAN_PAGES = $(MANS:.md=.1)
|
MAN_PAGES = $(MANS:.md=.1)
|
||||||
CODE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
CODE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
|
@ -17,26 +36,33 @@ default: install
|
||||||
|
|
||||||
docs: $(MAN_HTML) $(MAN_PAGES)
|
docs: $(MAN_HTML) $(MAN_PAGES)
|
||||||
|
|
||||||
install:
|
check:
|
||||||
|
@echo "Check dependencies before installation"
|
||||||
|
@./check_dependencies.sh
|
||||||
|
@echo
|
||||||
|
|
||||||
|
install: check
|
||||||
@if [ "$(INSTALL_VIA)" = brew ]; then \
|
@if [ "$(INSTALL_VIA)" = brew ]; then \
|
||||||
git apply brew-release.patch || { echo "Can't apply brew release patch"; exit 1; } \
|
git apply brew-release.patch || { echo "Can't apply brew release patch"; exit 1; } \
|
||||||
fi
|
fi
|
||||||
@mkdir -p $(DESTDIR)$(MANPREFIX)
|
mkdir -p $(DESTDIR)$(MANPREFIX)
|
||||||
@mkdir -p $(DESTDIR)$(BINPREFIX)
|
mkdir -p $(DESTDIR)$(BINPREFIX)
|
||||||
@echo "... installing bins to $(DESTDIR)$(BINPREFIX)"
|
@echo '... installing bins to '$(DESTDIR)$(BINPREFIX)
|
||||||
@echo "... installing man pages to $(DESTDIR)$(MANPREFIX)"
|
@echo '... installing man pages to '$(DESTDIR)$(MANPREFIX)
|
||||||
$(eval TEMPFILE := $(shell mktemp -q $${TMPDIR:-/tmp}/git-extras.XXXXXX 2>/dev/null || mktemp -q))
|
$(eval TEMPFILE := $(shell mktemp -q $${TMPDIR:-/tmp}/git-extras.XXXXXX 2>/dev/null || mktemp -q))
|
||||||
@# chmod from rw-------(default) to rwxrwxr-x, so that users can exec the scripts
|
@# chmod from rw-------(default) to rwxrwxr-x, so that users can exec the scripts
|
||||||
@chmod 775 $(TEMPFILE)
|
@chmod 775 $(TEMPFILE)
|
||||||
$(eval EXISTED_ALIASES := $(shell \
|
$(eval EXISTED_ALIASES := $(shell \
|
||||||
git config --get-regexp 'alias.*' | awk '{print "git-" substr($$1, 7)}'))
|
git config --get-regexp 'alias\..*' | awk '{print "git-" substr($$1, 7)}'))
|
||||||
@$(foreach COMMAND, $(COMMANDS), \
|
@$(foreach COMMAND, $(COMMANDS), \
|
||||||
disable=''; \
|
should_install='yes'; \
|
||||||
if test ! -z "$(filter $(COMMAND), $(EXISTED_ALIASES))"; then \
|
if [ ! -z "$(filter $(COMMAND), $(EXISTED_ALIASES))" ] && [ "$$SKIP_CONFLICT_CHECK" != yes ]; then \
|
||||||
read -p "$(COMMAND) conflicts with an alias, still install it and disable the alias? [y/n]" answer; \
|
read -p "$(COMMAND) conflicts with an alias, still install it? [y/n]: " answer; \
|
||||||
test "$$answer" = 'n' -o "$$answer" = 'N' && disable="true"; \
|
if [ "$$answer" = 'n' ] || [ "$$answer" = 'N' ]; then \
|
||||||
|
should_install="no"; \
|
||||||
|
fi; \
|
||||||
fi; \
|
fi; \
|
||||||
if test -z "$$disable"; then \
|
if [ "$$should_install" = 'yes' ]; then \
|
||||||
echo "... installing $(COMMAND)"; \
|
echo "... installing $(COMMAND)"; \
|
||||||
head -1 bin/$(COMMAND) > $(TEMPFILE); \
|
head -1 bin/$(COMMAND) > $(TEMPFILE); \
|
||||||
cat $(LIB) >> $(TEMPFILE); \
|
cat $(LIB) >> $(TEMPFILE); \
|
||||||
|
|
@ -56,13 +82,35 @@ install:
|
||||||
cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX); \
|
cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX); \
|
||||||
echo "cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX)"; \
|
echo "cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX)"; \
|
||||||
fi
|
fi
|
||||||
@mkdir -p $(DESTDIR)$(SYSCONFDIR)/bash_completion.d
|
@mkdir -p $(COMPL_DIR)
|
||||||
cp -f etc/bash_completion.sh $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/git-extras
|
cp -f etc/bash_completion.sh $(COMPL_DIR)/git-extras
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "If you are a zsh user, you may want to 'source $(CODE_DIR)/etc/git-extras-completion.zsh'" \
|
@echo "If you are a zsh user, you may want to 'source $(CODE_DIR)etc/git-extras-completion.zsh'" \
|
||||||
"and put this line into ~/.zshrc to enable zsh completion"
|
"and put this line into ~/.zshrc to enable zsh completion"
|
||||||
|
@echo "If you are a fish user, you may want to copy or link '$(CODE_DIR)etc/git-extras.fish'" \
|
||||||
|
"to '~/.config/fish/completions/'"
|
||||||
|
|
||||||
man/%.html: man/%.md
|
man/index.txt: $(MANS)
|
||||||
|
echo '# manuals' > $@.tmp
|
||||||
|
for file in $(sort $^) ; do \
|
||||||
|
extra=$${file%.md} ; \
|
||||||
|
extra=$${extra#man/} ; \
|
||||||
|
echo "$$extra(1) $$extra" >> $@.tmp ; \
|
||||||
|
done
|
||||||
|
mv -f $@.tmp $@
|
||||||
|
|
||||||
|
man/git-extras.md: $(MAN_BINS)
|
||||||
|
ln=$$(awk '/## COMMANDS/{print NR};' $@) ; \
|
||||||
|
awk "NR <= $$ln+1" $@ > $@.tmp
|
||||||
|
for file in $(sort $^) ; do \
|
||||||
|
head -n1 $$file | \
|
||||||
|
sed 's/^/ - **/;s/ -- /** /' >> $@.tmp ; \
|
||||||
|
done
|
||||||
|
ln=$$(awk '/## AUTHOR/{print NR};' $@) ; \
|
||||||
|
awk "NR >= $$ln-1" $@ >> $@.tmp
|
||||||
|
mv -f $@.tmp $@
|
||||||
|
|
||||||
|
man/%.html: man/%.md man/index.txt
|
||||||
ronn \
|
ronn \
|
||||||
--manual "Git Extras" \
|
--manual "Git Extras" \
|
||||||
--html \
|
--html \
|
||||||
|
|
@ -84,7 +132,7 @@ uninstall:
|
||||||
echo "... uninstalling $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN))"; \
|
echo "... uninstalling $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN))"; \
|
||||||
rm -f $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN)); \
|
rm -f $(DESTDIR)$(MANPREFIX)/$(notdir $(MAN)); \
|
||||||
)
|
)
|
||||||
rm -f $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/git-extras
|
rm -f $(COMPL_DIR)/git-extras
|
||||||
|
|
||||||
clean: docclean
|
clean: docclean
|
||||||
|
|
||||||
|
|
@ -92,4 +140,7 @@ docclean:
|
||||||
rm -f man/*.1
|
rm -f man/*.1
|
||||||
rm -f man/*.html
|
rm -f man/*.html
|
||||||
|
|
||||||
.PHONY: default docs clean docclean install uninstall
|
test:
|
||||||
|
bats ./tests
|
||||||
|
|
||||||
|
.PHONY: default docs check install uninstall clean docclean test
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
MANPREFIX ?= /doc/git/html
|
|
||||||
BINS = $(wildcard bin/git-*)
|
|
||||||
MANS = $(wildcard man/git-*.md)
|
|
||||||
MAN_HTML = $(MANS:.md=.html)
|
|
||||||
|
|
||||||
install:
|
|
||||||
@mkdir -p $(DESTDIR)$(MANPREFIX)
|
|
||||||
@mkdir -p $(DESTDIR)/bin
|
|
||||||
@echo "... installing bins to $(DESTDIR)/bin"
|
|
||||||
@echo "... installing man pages to $(DESTDIR)$(MANPREFIX)"
|
|
||||||
@$(foreach BIN, $(BINS), \
|
|
||||||
echo "... installing `basename $(BIN)`"; \
|
|
||||||
cp -f $(BIN) $(DESTDIR)/$(BIN); \
|
|
||||||
)
|
|
||||||
cp -f man/git-*.html $(DESTDIR)$(MANPREFIX)
|
|
||||||
@mkdir -p $(DESTDIR)/etc/bash_completion.d
|
|
||||||
cp -f etc/bash_completion.sh $(DESTDIR)/etc/bash_completion.d/git-extras
|
|
||||||
|
|
||||||
uninstall:
|
|
||||||
@$(foreach BIN, $(BINS), \
|
|
||||||
echo "... uninstalling $(DESTDIR)/$(BIN)"; \
|
|
||||||
rm -f $(DESTDIR)/$(BIN); \
|
|
||||||
)
|
|
||||||
@$(foreach MAN, $(MAN_HTML), \
|
|
||||||
echo "... uninstalling $(DESTDIR)$(MANPREFIX)/`basename $(MAN)`"; \
|
|
||||||
rm -f "$(DESTDIR)$(MANPREFIX)/`basename $(MAN)`"; \
|
|
||||||
)
|
|
||||||
rm -f $(DESTDIR)/etc/bash_completion.d/git-extras
|
|
||||||
|
|
||||||
.PHONY: install uninstall
|
|
||||||
23
Readme.md
23
Readme.md
|
|
@ -1,21 +1,34 @@
|
||||||
# Git Extras
|
# Git Extras
|
||||||
|
|
||||||
[](https://gitter.im/tj/git-extras)
|
|
||||||
|
|
||||||
Little git extras.
|
Little git extras.
|
||||||
|
|
||||||
## Screencasts
|
## Screencasts
|
||||||
|
|
||||||
Just getting started? Check out these screencasts:
|
Just getting started? Check out these screencasts:
|
||||||
|
|
||||||
* [introduction](https://vimeo.com/45506445) -- covering git-ignore, git-setup, git-changelog, git-release, git-effort and more
|
* [introduction](https://vimeo.com/45506445) ([archive.org link](https://web.archive.org/web/20230219181235id_/vod-progressive.akamaized.net/exp=1676834145~acl=%2Fvimeo-prod-skyfire-std-us%2F01%2F4101%2F1%2F45506445%2F107111328.mp4~hmac=065b68f23c4b6a222463097b36d1c346995a9559baa9b819972da95550018471/vimeo-prod-skyfire-std-us/01/4101/1/45506445/107111328.mp4)) -- covers `git-ignore`, `git-setup`, `git-changelog`, `git-release`, `git-effort` and more
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
See [Installation](Installation.md) page.
|
See the [Installation](Installation.md) page.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
Go to [Commands](Commands.md) page for basic usage and examples.
|
Go to the [Commands](Commands.md) page for basic usage and examples.
|
||||||
|
|
||||||
__GIT utilities__ -- repo summary, repl, changelog population, author commit percentages and more
|
__GIT utilities__ -- repo summary, repl, changelog population, author commit percentages and more
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Interested in contributing? Awesome!
|
||||||
|
|
||||||
|
Please read [Contributing](CONTRIBUTING.md) before you make a PR, thanks!
|
||||||
|
|
||||||
|
## The change of the default branch
|
||||||
|
|
||||||
|
As of Git Extras 6.4 the assumed default branch name changed from `master` to `main`.
|
||||||
|
This affects the Git Extras commands `git archive-file`, `git delete-merged-branches`, `git delta`, `git pull-request`, `git show-merged-branches`, `git show-unmerged-branches`, and `git squash`.
|
||||||
|
|
||||||
|
To change the default branch name to `master`: change either the configuration `git-extras.default-branch` or `init.defaultBranch` to `master`; the former takes precedence.
|
||||||
|
|
||||||
|
For example, `git config git-extras.default-branch master`.
|
||||||
|
|
|
||||||
45
bin/git-abort
Executable file
45
bin/git-abort
Executable file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# git rev-parse emits an error if not in a git repo so only need to bail out
|
||||||
|
gitdir="$(git rev-parse --git-dir)" || exit
|
||||||
|
|
||||||
|
discover_op() {
|
||||||
|
local op
|
||||||
|
for op in cherry_pick merge rebase revert ; do
|
||||||
|
if [ -f "${gitdir}/${op^^}_HEAD" ]; then
|
||||||
|
echo "${op/_/-}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_op() {
|
||||||
|
local op="$1"
|
||||||
|
if [ -z "$op" ]; then
|
||||||
|
echo "No active operation found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$(echo "$op" | wc -l)" -gt 1 ]]; then
|
||||||
|
echo "Multiple active operations found:" >&2
|
||||||
|
for o in $op; do
|
||||||
|
echo " - $o: HEAD: $(cat "${gitdir}/${o}_HEAD")" >&2
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
discover_action() {
|
||||||
|
local action=${1/git-/}
|
||||||
|
if [ "$action" != "abort" ] && [ "$action" != "continue" ]; then
|
||||||
|
echo "Invalid action: $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "$action"
|
||||||
|
}
|
||||||
|
|
||||||
|
action=$(discover_action "$(basename "$0")")
|
||||||
|
op=$(discover_op)
|
||||||
|
validate_op "$op"
|
||||||
|
|
||||||
|
git "$op" "--$action"
|
||||||
|
|
@ -1,16 +1,45 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
options=""
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<HERE
|
cat <<HERE
|
||||||
usage: git alias # list all aliases
|
usage: git alias [options] # list all aliases
|
||||||
or: git alias <search-pattern> # show aliases matching pattern
|
or: git alias [options] <search-pattern> # show aliases matching pattern
|
||||||
or: git alias <alias-name> <command> # alias a command
|
or: git alias [options] <alias-name> <command> # alias a command
|
||||||
|
options:
|
||||||
|
|
||||||
|
--global Show or create alias in the system config
|
||||||
|
--local Show or create alias in the repository config
|
||||||
HERE
|
HERE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if [[ "$1" == "--local" || "$1" == "--global" ]]; then
|
||||||
|
options=$1
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
case $# in
|
case $# in
|
||||||
0) git config --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort ;;
|
0)
|
||||||
1) git alias | grep -e "$1" ;;
|
if [[ -z "$options" ]]; then
|
||||||
2) git config --global alias."$1" "$2" ;;
|
git config --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort
|
||||||
|
else
|
||||||
|
git config "$options" --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
if [[ -z "$options" ]]; then
|
||||||
|
git config --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort | grep -e "$1"
|
||||||
|
else
|
||||||
|
git config "$options" --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort | grep -e "$1"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
if [[ -z "$options" ]]; then
|
||||||
|
git config alias."$1" "$2"
|
||||||
|
else
|
||||||
|
git config "$options" alias."$1" "$2"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
*) >&2 echo "error: too many arguments." && usage && exit 1 ;;
|
*) >&2 echo "error: too many arguments." && usage && exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,37 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# extract current branch name
|
# extract current branch name
|
||||||
BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null)
|
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||||
|
|
||||||
# get name of the most top folder of current directory, used for the
|
# get name of the most top folder of current directory, used for the
|
||||||
# output filename
|
# output filename
|
||||||
ARCHIVE_NAME=$(basename $(pwd))
|
ARCHIVE_NAME=$(basename "$(git rev-parse --show-toplevel)")
|
||||||
|
|
||||||
if [[ $BRANCH = tags* ]]; then
|
if [[ $BRANCH = tags* ]]; then
|
||||||
BRANCH=$(git describe)
|
BRANCH=$(git describe)
|
||||||
echo Building for tag \"$BRANCH\"
|
echo Building for tag "\"$BRANCH\""
|
||||||
FILENAME=$ARCHIVE_NAME.$BRANCH.zip
|
FILENAME=$ARCHIVE_NAME.$BRANCH.zip
|
||||||
else
|
else
|
||||||
echo Building archive on branch \"$BRANCH\"
|
echo Building archive on branch "\"$BRANCH\""
|
||||||
# get a version string, so archives will not be overwritten when creating
|
# get a version string, so archives will not be overwritten when creating
|
||||||
# many of them
|
# many of them
|
||||||
VERSION=$(git describe --always --long)
|
VERSION=$(git describe --always --long)
|
||||||
# if not on master append branch name into the filename
|
# if not on master append branch name into the filename
|
||||||
if [ "$BRANCH" = "master" ]; then
|
if [ "$BRANCH" = "$(git_extra_default_branch)" ]; then
|
||||||
FILENAME=$ARCHIVE_NAME.$VERSION.zip
|
FILENAME=$ARCHIVE_NAME.$VERSION.zip
|
||||||
else
|
else
|
||||||
FILENAME=$ARCHIVE_NAME.$VERSION.$BRANCH.zip
|
FILENAME=$ARCHIVE_NAME.$VERSION.$BRANCH.zip
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# rename invalid chars for the file path
|
||||||
|
FILENAME=${FILENAME//\//-}
|
||||||
|
FILENAME=${FILENAME//\\/-}
|
||||||
# combine path and filename
|
# combine path and filename
|
||||||
OUTPUT=$(pwd)/$FILENAME
|
OUTPUT=$PWD/$FILENAME
|
||||||
|
|
||||||
# building archive
|
# building archive
|
||||||
git archive --format zip --output $OUTPUT $BRANCH
|
git archive --format zip --output "$OUTPUT" "$BRANCH"
|
||||||
|
|
||||||
# also display size of the resulting file
|
# also display size of the resulting file
|
||||||
echo Saved to \"$FILENAME\" \(`du -h $OUTPUT | cut -f1`\)
|
echo Saved to \""$FILENAME"\" \("$(du -h "$OUTPUT" | cut -f1)"\)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
LIST=false
|
LIST=false
|
||||||
NO_EMAIL=false
|
NO_EMAIL=false
|
||||||
FILE=""
|
FILE=""
|
||||||
EDITOR=$(git var GIT_EDITOR)
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
|
|
@ -22,9 +21,9 @@ done
|
||||||
|
|
||||||
if ! $LIST; then
|
if ! $LIST; then
|
||||||
FILE=$1
|
FILE=$1
|
||||||
if test "$FILE" = ""; then
|
if [ -z "$FILE" ]; then
|
||||||
FILE=$(ls | egrep 'authors|contributors' -i|head -n1)
|
FILE=$(find . -mindepth 1 -maxdepth 1 \( -iname '*authors*' -o -iname '*contributors*' \) | head -n1)
|
||||||
if test "$FILE" = ""; then
|
if [ -z "$FILE" ]; then
|
||||||
FILE='AUTHORS'
|
FILE='AUTHORS'
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
@ -36,11 +35,11 @@ fi
|
||||||
|
|
||||||
authors() {
|
authors() {
|
||||||
if $NO_EMAIL; then
|
if $NO_EMAIL; then
|
||||||
# eamil will be used to uniq authors.
|
# email will be used to uniq authors.
|
||||||
git shortlog -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++' \
|
git shortlog HEAD -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++' \
|
||||||
| awk -F'<' '{gsub(/ +$/, "", $1); print $1}'
|
| awk -F'<' '{gsub(/ +$/, "", $1); print $1}'
|
||||||
else
|
else
|
||||||
git shortlog -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++'
|
git shortlog HEAD -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++'
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,6 +50,5 @@ authors() {
|
||||||
if $LIST; then
|
if $LIST; then
|
||||||
authors
|
authors
|
||||||
else
|
else
|
||||||
authors >> $FILE
|
authors >> "$FILE"
|
||||||
test -n "$EDITOR" && $EDITOR $FILE
|
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
11
bin/git-back
11
bin/git-back
|
|
@ -1,11 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
if test $# -eq 0; then
|
|
||||||
git reset --soft HEAD~1
|
|
||||||
else
|
|
||||||
if `echo $1 | grep -q [^[:digit:]]`; then
|
|
||||||
echo "$1 is not a number" 1>&2
|
|
||||||
else
|
|
||||||
git reset --soft HEAD~$1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
92
bin/git-browse
Executable file
92
bin/git-browse
Executable file
|
|
@ -0,0 +1,92 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
remote=${1:-""}
|
||||||
|
branch=""
|
||||||
|
filename=${2:-""}
|
||||||
|
line1=${3:-""}
|
||||||
|
line2=${4:-""}
|
||||||
|
|
||||||
|
# get remote name
|
||||||
|
if [[ $remote == "" ]]; then
|
||||||
|
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
|
||||||
|
remote=$(git config branch."${branch}".remote || echo "origin")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $remote == "" ]]; then
|
||||||
|
echo "Remote not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
remote_url=$(git remote get-url "$remote") || exit $?
|
||||||
|
|
||||||
|
if [[ $remote_url = git@* ]]; then
|
||||||
|
url=$(echo "$remote_url" | sed -E -e 's/:/\//' -e 's/\.git$//' -e 's/.*@(.*)/http:\/\/\1/')
|
||||||
|
elif [[ $remote_url = http* ]]; then
|
||||||
|
url=${remote_url%.git}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# construct urls
|
||||||
|
commit_hash=$(git rev-parse HEAD 2>/dev/null)
|
||||||
|
commit_or_branch=${commit_hash:-${branch}}
|
||||||
|
|
||||||
|
if [[ $remote_url =~ gitlab ]]; then
|
||||||
|
# construct gitlab urls
|
||||||
|
# https://gitlab.com/<user_or_group>/<repo>/-/blob/<commit_or_branch>/<filename>#L<line1>-<line2>
|
||||||
|
if [[ -n ${filename} ]]; then
|
||||||
|
url="${url}/-/blob/${commit_or_branch}/${filename}"
|
||||||
|
if [[ -n "${line1}" ]]; then
|
||||||
|
url="${url}#L${line1}"
|
||||||
|
if [[ -n "${line2}" ]]; then
|
||||||
|
url="${url}-${line2}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [[ $remote_url =~ github ]]; then
|
||||||
|
# construct github urls
|
||||||
|
# https://github.com/<user_or_org>/<repo>/blob/<commit_or_branch>/<filename>#L<line1>-L<line2>
|
||||||
|
if [[ -n "${filename}" ]]; then
|
||||||
|
url="${url}/blob/${commit_or_branch}/${filename}"
|
||||||
|
if [[ -n "${line1}" ]]; then
|
||||||
|
url="${url}#L${line1}"
|
||||||
|
if [[ -n "${line2}" ]]; then
|
||||||
|
url="${url}-L${line2}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [[ $remote_url =~ bitbucket ]]; then
|
||||||
|
# construct bitbucket urls
|
||||||
|
# https://bitbucket.org/<user_or_org>/<repo>/src/<commit_or_branch>/<filename>#lines-<line1>:<line2>
|
||||||
|
if [[ -n ${filename} ]]; then
|
||||||
|
url=${url}/src/${commit_or_branch}/${filename}
|
||||||
|
if [[ -n "${line1}" ]]; then
|
||||||
|
url="${url}#lines-${line1}"
|
||||||
|
if [[ -n "${line2}" ]]; then
|
||||||
|
url="${url}:${line2}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# open url
|
||||||
|
case "$OSTYPE" in
|
||||||
|
darwin*)
|
||||||
|
# MacOS
|
||||||
|
open "$url"
|
||||||
|
;;
|
||||||
|
msys)
|
||||||
|
# Git-Bash on Windows
|
||||||
|
start "$url"
|
||||||
|
;;
|
||||||
|
linux*)
|
||||||
|
# Handle WSL on Windows
|
||||||
|
if uname -a | grep -i -q Microsoft && command -v powershell.exe; then
|
||||||
|
powershell.exe -NoProfile start "$url"
|
||||||
|
else
|
||||||
|
xdg-open "$url"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# fall back to xdg-open for BSDs, etc.
|
||||||
|
xdg-open "$url"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
60
bin/git-browse-ci
Executable file
60
bin/git-browse-ci
Executable file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e -o pipefail
|
||||||
|
if [[ $1 == "" ]]
|
||||||
|
then
|
||||||
|
branch=$(git rev-parse --abbrev-ref HEAD &> /dev/null)
|
||||||
|
remote=$(git config branch."${branch}".remote || echo "origin")
|
||||||
|
else
|
||||||
|
remote=$1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $remote ]]
|
||||||
|
then
|
||||||
|
echo "Remote not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
remote_url=$(git remote get-url "${remote}") || exit $?
|
||||||
|
|
||||||
|
if [[ $remote_url = git@* ]]
|
||||||
|
then
|
||||||
|
url=$(echo "${remote_url}" | sed -E -e 's/:/\//' -e 's/\.git$//' -e 's/.*@(.*)/http:\/\/\1/')
|
||||||
|
elif [[ $remote_url = http* ]]
|
||||||
|
then
|
||||||
|
url=${remote_url%.git}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $url =~ github ]]
|
||||||
|
then
|
||||||
|
ci_url=${url}/actions
|
||||||
|
elif [[ $url =~ gitlab ]]
|
||||||
|
then
|
||||||
|
ci_url=${url}/-/pipelines
|
||||||
|
elif [[ $url =~ bitbucket ]]
|
||||||
|
then
|
||||||
|
ci_url=${url}/addon/pipelines/home
|
||||||
|
fi
|
||||||
|
case "$OSTYPE" in
|
||||||
|
darwin*)
|
||||||
|
# MacOS
|
||||||
|
open "${ci_url}"
|
||||||
|
;;
|
||||||
|
msys)
|
||||||
|
# Git-Bash on Windows
|
||||||
|
start "${ci_url}"
|
||||||
|
;;
|
||||||
|
linux*)
|
||||||
|
# Handle WSL on Windows
|
||||||
|
if uname -a | grep -i -q Microsoft && command -v powershell.exe
|
||||||
|
then
|
||||||
|
powershell.exe -NoProfile start "${ci_url}"
|
||||||
|
else
|
||||||
|
xdg-open "${ci_url}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# fall back to xdg-open for BSDs, etc.
|
||||||
|
xdg-open "${ci_url}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
81
bin/git-brv
Executable file
81
bin/git-brv
Executable file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# A sorted prettier branch -vv
|
||||||
|
|
||||||
|
if ! (( BASH_VERSINFO[0] > 4 ||
|
||||||
|
BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 2 )); then
|
||||||
|
printf >&2 'This script requires bash 4.2 or newer\n'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# allow the user to set a default reverse option in an environment variable
|
||||||
|
reverse=$(git config --get git-extras.brv.reverse || echo false)
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-r | --reverse)
|
||||||
|
reverse=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
shopt -s checkwinsize
|
||||||
|
COLUMNS=$(tput cols)
|
||||||
|
color_branch_local=$(git config --get-color color.branch.local normal)
|
||||||
|
color_branch_current=$(git config --get-color color.branch.current green)
|
||||||
|
color_diff_commit=$(git config --get-color color.diff.commit yellow)
|
||||||
|
color_branch_upstream=$(git config --get-color color.branch.upstream blue)
|
||||||
|
reset=$(tput sgr0)
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
declare -A upstream date hash message
|
||||||
|
eval "$(
|
||||||
|
git for-each-ref --format='upstream[%(refname:short)]=%(upstream:short)' \
|
||||||
|
--shell 'refs/heads/**'
|
||||||
|
)"
|
||||||
|
|
||||||
|
for b in "${!upstream[@]}"; do
|
||||||
|
blen=${#b} ulen=${#upstream[$b]}
|
||||||
|
(( bwidth = blen > bwidth ? blen : bwidth ))
|
||||||
|
(( uwidth = ulen > uwidth ? ulen : uwidth ))
|
||||||
|
IFS=/ read -r 'date[$b]' 'hash[$b]' 'message[$b]' < <(
|
||||||
|
git log --no-walk=unsorted --format=%ct/%h/%s "$b" --
|
||||||
|
)
|
||||||
|
hlen=${#hash[$b]}
|
||||||
|
(( hwidth = hlen > hwidth ? hlen : hwidth ))
|
||||||
|
done
|
||||||
|
|
||||||
|
mapfile -t ordered < <(
|
||||||
|
# the reverse option of git-brv causes the sort to be sorted normally rather than in reverse
|
||||||
|
reverse_opt=""
|
||||||
|
if [[ $reverse = false ]]; then
|
||||||
|
reverse_opt="-r"
|
||||||
|
fi
|
||||||
|
for b in "${!date[@]}"; do
|
||||||
|
printf '%d\t%s\n' "${date[$b]}" "$b"
|
||||||
|
done | sort -n $reverse_opt | cut -f2-
|
||||||
|
)
|
||||||
|
|
||||||
|
current=$(git symbolic-ref -q --short HEAD)
|
||||||
|
|
||||||
|
for b in "${ordered[@]}"; do
|
||||||
|
branch_color=$color_branch_local
|
||||||
|
if [[ $b = "$current" ]]; then
|
||||||
|
branch_color=$color_branch_current
|
||||||
|
fi
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
msg=${message[$b]:0:COLUMNS-bwidth-uwidth-hwidth-14}
|
||||||
|
else
|
||||||
|
msg=${message[$b]}
|
||||||
|
fi
|
||||||
|
printf '%(%Y-%m-%d)T %s%*s%s %s%*s%s %s%*s%s %s\n' \
|
||||||
|
"${date[$b]}" \
|
||||||
|
"$branch_color" "-$bwidth" "$b" "$reset" \
|
||||||
|
"$color_branch_upstream" "-$uwidth" "${upstream[$b]}" "$reset" \
|
||||||
|
"$color_diff_commit" "-$hwidth" "${hash[$b]}" "$reset" \
|
||||||
|
"$msg"
|
||||||
|
done
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
ALIAS=bug
|
|
||||||
git feature -a $ALIAS "$@"
|
|
||||||
175
bin/git-bulk
175
bin/git-bulk
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
invers=$(tput rev)
|
inverse=$(tput rev)
|
||||||
reset=$(tput sgr0)
|
reset=$(tput sgr0)
|
||||||
txtbld=$(tput bold)
|
txtbld=$(tput bold)
|
||||||
bldred=${txtbld}$(tput setaf 1)
|
bldred=${txtbld}$(tput setaf 1)
|
||||||
|
|
@ -8,115 +8,176 @@ bldred=${txtbld}$(tput setaf 1)
|
||||||
guardedmode=false
|
guardedmode=false
|
||||||
singlemode=false
|
singlemode=false
|
||||||
allwsmode=false
|
allwsmode=false
|
||||||
|
quiet=false
|
||||||
|
no_follow_symlinks=false
|
||||||
|
no_follow_hidden=false
|
||||||
|
|
||||||
#
|
#
|
||||||
# print usage message
|
# print usage message
|
||||||
#
|
#
|
||||||
usage() {
|
usage() {
|
||||||
echo 1>&2 "usage: git bulk [-g] ([-a]|[-w <ws-name>]) <git command>"
|
echo 1>&2 "usage: git bulk [--no-follow-symlinks] [--no-follow-hidden] [-q|--quiet] [-g] ([-a]|[-w <ws-name>]) <git command>"
|
||||||
echo 1>&2 " git bulk --addworkspace <ws-name> <ws-root-directory>"
|
echo 1>&2 " git bulk --addworkspace <ws-name> <ws-root-directory> (--from <URL or file>)"
|
||||||
echo 1>&2 " git bulk --removeworkspace <ws-name>"
|
echo 1>&2 " git bulk --removeworkspace <ws-name>"
|
||||||
echo 1>&2 " git bulk --addcurrent <ws-name>"
|
echo 1>&2 " git bulk --addcurrent <ws-name>"
|
||||||
echo 1>&2 " git bulk --purge"
|
echo 1>&2 " git bulk --purge"
|
||||||
echo 1>&2 " git bulk --listall"
|
echo 1>&2 " git bulk --listall"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cdfail() {
|
||||||
|
echo 1>&2 "failed to change directory: $1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
# add another workspace to global git config
|
# add another workspace to global git config
|
||||||
function addworkspace { git config --global bulkworkspaces."$wsname" "$wsdir"; }
|
addworkspace() {
|
||||||
|
git config --global bulkworkspaces."$wsname" "$wsdir";
|
||||||
|
if [ -n "$source" ]; then
|
||||||
|
if [ ! -d "$wsdir" ]; then echo 1>&2 "Path of workspace doesn't exist, make it first."; exit 1; fi
|
||||||
|
regex='http(s)?://|ssh://|(git@)?.*:.*/.*'
|
||||||
|
if [[ "$source" =~ $regex ]]; then
|
||||||
|
pushd "$wsdir" > /dev/null || cdfail "$wsdir"
|
||||||
|
git clone "$source"
|
||||||
|
popd > /dev/null || cdfail "$OLDPWD"
|
||||||
|
else
|
||||||
|
source=$(realpath "$source" 2>/dev/null)
|
||||||
|
if [ -f "$source" ]; then
|
||||||
|
pushd "$wsdir" > /dev/null || cdfail "$wsdir"
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
# the git clone command to take the complete line in the repository.txt as separate argument. This facilitated the cloning of the repository with a custom folder name.
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
git clone $line;
|
||||||
|
fi
|
||||||
|
done < "$source"
|
||||||
|
popd > /dev/null || cdfail "$OLDPWD"
|
||||||
|
else
|
||||||
|
echo 1>&2 "format of URL or file unknown"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# add current directory
|
# add current directory
|
||||||
function addcurrent { git config --global bulkworkspaces."$wsname" "$PWD"; }
|
addcurrent() { git config --global bulkworkspaces."$wsname" "$PWD"; }
|
||||||
|
|
||||||
# remove workspace from global git config
|
# remove workspace from global git config
|
||||||
function removeworkspace { checkWSName && git config --global --unset bulkworkspaces."$wsname"; }
|
removeworkspace() { checkWSName && git config --global --unset bulkworkspaces."$wsname"; }
|
||||||
|
|
||||||
# remove workspace from global git config
|
# remove workspace from global git config
|
||||||
function purge { git config --global --remove-section bulkworkspaces; }
|
purge() { git config --global --remove-section bulkworkspaces; }
|
||||||
|
|
||||||
# list all current workspace locations defined
|
# list all current workspace locations defined
|
||||||
function listall { git config --global --get-regexp bulkworkspaces; }
|
listall() { git config --global --get-regexp bulkworkspaces; }
|
||||||
|
|
||||||
# guarded execution of a git command in one specific repository
|
# guarded execution of a git command in one specific repository
|
||||||
function guardedExecution () {
|
guardedExecution () {
|
||||||
|
if [ "${quiet?}" != "true" ] || $guardedmode; then
|
||||||
|
echo 1>&2 "${bldred}->${reset} executing ${inverse}git $gitcommand${reset} in repository ${leadingpath%/*}/${bldred}${curdir##*/}${reset}"
|
||||||
|
fi
|
||||||
|
|
||||||
if $guardedmode; then
|
if $guardedmode; then
|
||||||
echo -n "${invers}git $gitcommand${reset} -> execute here (y/n)? "
|
echo 1>&2 -n " Execute command here (y/n)? "
|
||||||
read -n 1 -r </dev/tty; echo
|
read -n 1 -r </dev/tty; echo 1>&2
|
||||||
if [[ $REPLY =~ ^[Yy]$ ]]; then atomicExecution; fi
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
git "$@"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
atomicExecution
|
git "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# atomic git command execution with log
|
|
||||||
function atomicExecution () {
|
|
||||||
echo "${bldred}->${reset} executing ${invers}git $gitcommand${reset}" && git $gitcommand
|
|
||||||
}
|
|
||||||
|
|
||||||
# check if the passed command is known as a core git command
|
# check if the passed command is known as a core git command
|
||||||
function checkGitCommand () {
|
checkGitCommand () {
|
||||||
if git help -a | grep -o -q "\b${corecommand}\b"; then
|
if git help -a | grep -o -q "\b${corecommand}\b"; then
|
||||||
echo "Core command \"$corecommand\" accepted."
|
echo 1>&2 "Core command \"$corecommand\" accepted."
|
||||||
else
|
else
|
||||||
if git config --get-regexp alias | grep -o -q "\.${corecommand} "; then
|
if git config --get-regexp alias | grep -o -q "\.${corecommand} "; then
|
||||||
echo "Alias ${corecommand} accepted."
|
echo 1>&2 "Alias ${corecommand} accepted."
|
||||||
else
|
else
|
||||||
usage && echo "error: unknown GIT command: $corecommand" && exit 1
|
usage && echo 1>&2 "error: unknown GIT command: $corecommand" && exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# check if workspace name is registered
|
# check if workspace name is registered
|
||||||
function checkWSName () {
|
checkWSName () {
|
||||||
while read workspace; do
|
while read -r workspace; do
|
||||||
parseWsName "$workspace"
|
parseWsName "$workspace"
|
||||||
if [[ $rwsname == "$wsname" ]]; then return; fi
|
if [[ $rwsname == "$wsname" ]]; then return; fi
|
||||||
done <<< "$(echo "$(listall)")"
|
done <<< "$(listall)"
|
||||||
# when here the ws name was not found
|
# when here the ws name was not found
|
||||||
usage && echo "error: unknown workspace name: $wsname" && exit 1
|
usage && echo 1>&2 "error: unknown workspace name: $wsname" && exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# parse out wsname from workspacespec
|
# parse out wsname from workspacespec
|
||||||
function parseWsName () {
|
parseWsName () {
|
||||||
local wsspec="$1"
|
local wsspec="$1"
|
||||||
|
# Get the workspace value from its specification in the `.gitconfig`.
|
||||||
|
# May be an absolute path or a variable name of the form: `$VARNAME`
|
||||||
rwsdir=${wsspec#* }
|
rwsdir=${wsspec#* }
|
||||||
|
if [[ ${rwsdir:0:1} == '$' ]]; then
|
||||||
|
# Dereference the `rwsdir` value which is a variable name.
|
||||||
|
rwsdir_varname=${rwsdir:1}
|
||||||
|
rwsdir=${!rwsdir_varname}
|
||||||
|
if [[ -z "${rwsdir}" ]]; then
|
||||||
|
echo 1>&2 "error: bad environment variable: $rwsdir_varname" && exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
rwsname=${wsspec#*.} && rwsname=${rwsname%% *}
|
rwsname=${wsspec#*.} && rwsname=${rwsname%% *}
|
||||||
}
|
}
|
||||||
|
|
||||||
# detects the wsname of the current directory
|
# detects the wsname of the current directory
|
||||||
function wsnameToCurrent () {
|
wsnameToCurrent () {
|
||||||
while read workspace; do
|
while read -r workspace; do
|
||||||
if [ -z "$workspace" ]; then continue; fi
|
if [ -z "$workspace" ]; then continue; fi
|
||||||
parseWsName "$workspace"
|
parseWsName "$workspace"
|
||||||
if echo "$PWD" | grep -o -q "$rwsdir"; then wsname="$rwsname" && return; fi
|
if echo "$PWD" | grep -o -q "$rwsdir"; then wsname="$rwsname" && return; fi
|
||||||
done <<< "$(echo "$(listall)")"
|
done <<< "$(listall)"
|
||||||
# when here then not in workspace dir
|
# when here then not in workspace dir
|
||||||
echo "error: you are not in a workspace directory. your registered workspaces are:" && \
|
echo 1>&2 "error: you are not in a workspace directory. your registered workspaces are:" && \
|
||||||
wslist="$(echo "$(listall)")" && echo "${wslist:-'<no workspaces defined yet>'}" && exit 1
|
wslist="$(listall)" && echo 1>&2 "${wslist:-'<no workspaces defined yet>'}" && exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# helper to check number of arguments
|
# helper to check number of arguments.
|
||||||
function allowedargcount () {
|
allowedargcount () {
|
||||||
( test "$paramcount" -ne "$1" ) && ( usage && echo 1>&2 "error: wrong number of arguments" && exit 1 )
|
if [ "$paramcount" -ne "${1:-0}" ] && [ "$paramcount" -ne "${2:-0}" ]; then
|
||||||
|
echo 1>&2 "error: wrong number of arguments" && usage;
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# execute the bulk operation
|
# execute the bulk operation
|
||||||
function executBulkOp () {
|
executBulkOp () {
|
||||||
checkGitCommand
|
checkGitCommand
|
||||||
if ! $allwsmode && ! $singlemode; then wsnameToCurrent; fi # by default git bulk works within the 'current' workspace
|
if ! $allwsmode && ! $singlemode; then wsnameToCurrent; fi # by default git bulk works within the 'current' workspace
|
||||||
listall | while read workspacespec; do
|
listall | while read -r workspacespec; do
|
||||||
parseWsName "$workspacespec"
|
parseWsName "$workspacespec"
|
||||||
if [[ -n $wsname ]] && [[ $rwsname != "$wsname" ]]; then continue; fi
|
if [[ -n $wsname ]] && [[ $rwsname != "$wsname" ]]; then continue; fi
|
||||||
eval cd "\"$rwsdir\""
|
cd "$rwsdir" || exit 1
|
||||||
local actual=$(pwd)
|
local actual=$PWD
|
||||||
echo "Executing bulk operation in workspace ${invers}$actual${reset}"
|
[ "${quiet?}" != "true" ] && echo 1>&2 "Executing bulk operation in workspace ${inverse}$actual${reset}"
|
||||||
eval find . -name ".git" | while read line; do
|
|
||||||
|
# build `find` flags depending on command-line options
|
||||||
|
local find_flags=()
|
||||||
|
if [[ "$no_follow_symlinks" == true ]]; then
|
||||||
|
find_flags+=(-P)
|
||||||
|
else
|
||||||
|
find_flags+=(-L)
|
||||||
|
fi
|
||||||
|
# find all git repositories under the workspace on which we want to operate
|
||||||
|
readarray allGitFolders < <(find "${find_flags[@]}" . -name ".git" 2>/dev/null)
|
||||||
|
|
||||||
|
for line in "${allGitFolders[@]}"; do
|
||||||
local gitrepodir=${line::${#line}-5} # cut the .git part of find results to have the root git directory of that repository
|
local gitrepodir=${line::${#line}-5} # cut the .git part of find results to have the root git directory of that repository
|
||||||
eval cd "\"$gitrepodir\"" # into git repo location
|
cd "$gitrepodir" || exit 1 # into git repo location
|
||||||
local curdir=$(pwd)
|
local curdir=$PWD
|
||||||
local leadingpath=${curdir#${actual}}
|
local leadingpath=${curdir#"${actual}"}
|
||||||
echo "Current repository: ${leadingpath%/*}/${bldred}${curdir##*/}${reset}"
|
# do not execute if we do not want to consider a ".git" directory under a hidden directory
|
||||||
guardedExecution
|
if [ $no_follow_hidden = false ] || ! [[ "$leadingpath" =~ "/." ]]; then
|
||||||
eval cd "\"$rwsdir\"" # back to origin location of last find command
|
guardedExecution "$@"
|
||||||
|
fi
|
||||||
|
cd "$rwsdir" || exit 1 # back to origin location of last find command
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
@ -129,22 +190,27 @@ if [[ $paramcount -le 0 ]]; then usage; fi
|
||||||
# parse command parameters
|
# parse command parameters
|
||||||
while [ "${#}" -ge 1 ] ; do
|
while [ "${#}" -ge 1 ] ; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
--quiet|-q) quiet='true' ;;
|
||||||
--listall|--purge)
|
--listall|--purge)
|
||||||
butilcommand="${1:2}" && break ;;
|
butilcommand="${1:2}" && break ;;
|
||||||
--removeworkspace|--addcurrent|--addworkspace)
|
--removeworkspace|--addcurrent|--addworkspace)
|
||||||
butilcommand="${1:2}" && wsname="$2" && wsdir="$3" && break ;;
|
butilcommand="${1:2}" && wsname="$2" && wsdir="$3" && if [ "$4" = "--from" ]; then source="$5"; fi && break ;;
|
||||||
|
--no-follow-symlinks)
|
||||||
|
no_follow_symlinks=true ;;
|
||||||
|
--no-follow-hidden)
|
||||||
|
no_follow_hidden=true ;;
|
||||||
-a)
|
-a)
|
||||||
allwsmode=true ;;
|
allwsmode=true ;;
|
||||||
-g)
|
-g)
|
||||||
guardedmode=true ;;
|
guardedmode=true ;;
|
||||||
-w)
|
-w)
|
||||||
singlemode=true && shift && wsname="$1" && checkWSName ;;
|
singlemode=true && shift && wsname="$1" && checkWSName ;;
|
||||||
-*)
|
|
||||||
usage && echo 1>&2 "error: unknown argument $1" && exit 1 ;;
|
|
||||||
--*)
|
--*)
|
||||||
usage && echo 1>&2 "error: unknown argument $1" && exit 1 ;;
|
usage && echo 1>&2 "error: unknown argument $1" && exit 1 ;;
|
||||||
|
-*)
|
||||||
|
usage && echo 1>&2 "error: unknown argument $1" && exit 1 ;;
|
||||||
*) # git core commands
|
*) # git core commands
|
||||||
butilcommand="executBulkOp" && corecommand="$1" && gitcommand="$@" && break ;;
|
butilcommand="executBulkOp" && corecommand="$1" && gitcommand="$*" && break ;;
|
||||||
esac && shift
|
esac && shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
@ -152,13 +218,14 @@ done
|
||||||
if $allwsmode && $singlemode; then echo 1>&2 "error: options -w and -a are incompatible" && exit 1; fi
|
if $allwsmode && $singlemode; then echo 1>&2 "error: options -w and -a are incompatible" && exit 1; fi
|
||||||
|
|
||||||
# if single mode check the supplied workspace name
|
# if single mode check the supplied workspace name
|
||||||
if $singlemode; then echo "Selected single workspace mode in workspace: $wsname" && checkWSName; fi
|
if $singlemode; then echo 1>&2 "Selected single workspace mode in workspace: $wsname" && checkWSName; fi
|
||||||
|
|
||||||
# check right number of arguments
|
# check right number of arguments
|
||||||
case $butilcommand in
|
case $butilcommand in
|
||||||
listall|purge) allowedargcount 1;;
|
listall|purge) allowedargcount 1;;
|
||||||
addcurrent|removeworkspace) allowedargcount 2;;
|
addcurrent|removeworkspace) allowedargcount 2;;
|
||||||
addworkspace) allowedargcount 3;;
|
addworkspace) allowedargcount 3 5;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
$butilcommand # run user command
|
# pass the origin arguments to the 'executBulkOp'
|
||||||
|
$butilcommand "$@" # run user command
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,15 @@ GIT_MERGELOG_FORMAT="$(git config changelog.mergeformat)"
|
||||||
GIT_EDITOR="$(git var GIT_EDITOR)"
|
GIT_EDITOR="$(git var GIT_EDITOR)"
|
||||||
PROGNAME="git-changelog"
|
PROGNAME="git-changelog"
|
||||||
|
|
||||||
|
git_editor() {
|
||||||
|
if test -z "${GIT_EDITOR:+set}"
|
||||||
|
then
|
||||||
|
GIT_EDITOR="$(git var GIT_EDITOR)" || return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval "$GIT_EDITOR" '"$@"'
|
||||||
|
}
|
||||||
|
|
||||||
_usage() {
|
_usage() {
|
||||||
cat << EOF
|
cat << EOF
|
||||||
usage: $PROGNAME options [file]
|
usage: $PROGNAME options [file]
|
||||||
|
|
@ -22,11 +31,12 @@ tags exist, then all commits are output; if tags exist, then only the most-
|
||||||
recent commits are output up to the last identified tag.
|
recent commits are output up to the last identified tag.
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
-a, --all Retrieve all commits (ignores --start-tag, --final-tag)
|
-a, --all Retrieve all commits (ignores --start-tag/commit, --final-tag)
|
||||||
-l, --list Display commits as a list, with no titles
|
-l, --list Display commits as a list, with no titles
|
||||||
-t, --tag Tag label to use for most-recent (untagged) commits
|
-t, --tag Tag label to use for most-recent (untagged) commits
|
||||||
-f, --final-tag Newest tag to retrieve commits from in a range
|
-f, --final-tag Newest tag to retrieve commits from in a range
|
||||||
-s, --start-tag Oldest tag to retrieve commits from in a range
|
-s, --start-tag Oldest tag to retrieve commits from in a range
|
||||||
|
--start-commit Like --start-tag but use commit instead of tag
|
||||||
-n, --no-merges Suppress commits from merged branches
|
-n, --no-merges Suppress commits from merged branches
|
||||||
-m, --merges-only Only uses merge commits (uses both subject and body of commit)
|
-m, --merges-only Only uses merge commits (uses both subject and body of commit)
|
||||||
-p, --prune-old Replace existing Changelog entirely with new content
|
-p, --prune-old Replace existing Changelog entirely with new content
|
||||||
|
|
@ -145,6 +155,8 @@ _fetchCommitRange() {
|
||||||
local start_tag="$2"
|
local start_tag="$2"
|
||||||
local final_tag="$3"
|
local final_tag="$3"
|
||||||
|
|
||||||
|
# This shellcheck disable is applied to the whole if-body
|
||||||
|
# shellcheck disable=SC2086
|
||||||
if [[ "$list_all" == true ]]; then
|
if [[ "$list_all" == true ]]; then
|
||||||
git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}"
|
git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}"
|
||||||
elif [[ -n "$final_tag" && "$start_tag" == "null" ]]; then
|
elif [[ -n "$final_tag" && "$start_tag" == "null" ]]; then
|
||||||
|
|
@ -160,7 +172,7 @@ _formatCommitPlain() {
|
||||||
local start_tag="$1"
|
local start_tag="$1"
|
||||||
local final_tag="$2"
|
local final_tag="$2"
|
||||||
|
|
||||||
printf "%s" "$(_fetchCommitRange "false" "$start_tag" "$final_tag")"
|
printf "%s\n" "$(_fetchCommitRange "false" "$start_tag" "$final_tag")"
|
||||||
}
|
}
|
||||||
|
|
||||||
_formatCommitPretty() {
|
_formatCommitPretty() {
|
||||||
|
|
@ -187,7 +199,8 @@ commitList() {
|
||||||
local title_tag="$1"; shift
|
local title_tag="$1"; shift
|
||||||
local start_tag="$1"; shift
|
local start_tag="$1"; shift
|
||||||
local final_tag="$1"; shift
|
local final_tag="$1"; shift
|
||||||
local list_style="${1:-false}" # enable/disable list format
|
local list_style="${1:-false}"; shift # enable/disable list format
|
||||||
|
local start_commit="$1"; shift
|
||||||
local changelog="$FILE"
|
local changelog="$FILE"
|
||||||
local title_date="$(date +'%Y-%m-%d')"
|
local title_date="$(date +'%Y-%m-%d')"
|
||||||
local tags_list=()
|
local tags_list=()
|
||||||
|
|
@ -195,6 +208,19 @@ commitList() {
|
||||||
local defaultIFS="$IFS"
|
local defaultIFS="$IFS"
|
||||||
local IFS="$defaultIFS"
|
local IFS="$defaultIFS"
|
||||||
|
|
||||||
|
if [[ -n "$start_commit" && "$final_tag" == "null" \
|
||||||
|
&& "$start_tag" == "null" ]]; then
|
||||||
|
# if there is not tag after $start_commit,
|
||||||
|
# output directly without fetch all tags
|
||||||
|
if [[ "$list_style" == true ]]; then
|
||||||
|
_formatCommitPlain "${start_commit}~"
|
||||||
|
else
|
||||||
|
_formatCommitPretty "$title_tag" "$title_date" "$start_commit~"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# Tags look like this:
|
# Tags look like this:
|
||||||
#
|
#
|
||||||
|
|
@ -218,7 +244,7 @@ commitList() {
|
||||||
# fetch our tags
|
# fetch our tags
|
||||||
local _ref _date _tag _tab='%x09'
|
local _ref _date _tag _tab='%x09'
|
||||||
local _tag_regex='tag: *'
|
local _tag_regex='tag: *'
|
||||||
while IFS=$'\t' read _ref _date _tag; do
|
while IFS=$'\t' read -r _ref _date _tag; do
|
||||||
[[ -z "${_tag}" ]] && continue
|
[[ -z "${_tag}" ]] && continue
|
||||||
# strip out tags form ()
|
# strip out tags form ()
|
||||||
# git v2.2.0+ supports '%D', like '%d' without the " (", ")" wrapping. One day we should use it instead.
|
# git v2.2.0+ supports '%D', like '%d' without the " (", ")" wrapping. One day we should use it instead.
|
||||||
|
|
@ -265,7 +291,8 @@ commitList() {
|
||||||
for (( i=0; i<"${_tags_list_keys_length}"; i++ )); do
|
for (( i=0; i<"${_tags_list_keys_length}"; i++ )); do
|
||||||
local __curr_tag="${tags_list_keys[$i]}"
|
local __curr_tag="${tags_list_keys[$i]}"
|
||||||
local __prev_tag="${tags_list_keys[$i+1]:-null}"
|
local __prev_tag="${tags_list_keys[$i+1]:-null}"
|
||||||
local __curr_date="$(_valueForKeyFakeAssocArray "${__curr_tag}" "${tags_list[*]}")"
|
local __curr_date
|
||||||
|
__curr_date="$(_valueForKeyFakeAssocArray "${__curr_tag}" "${tags_list[*]}")"
|
||||||
__curr_date="${__curr_date##*=>}"
|
__curr_date="${__curr_date##*=>}"
|
||||||
|
|
||||||
# output latest commits, up until the most-recent tag, these are all
|
# output latest commits, up until the most-recent tag, these are all
|
||||||
|
|
@ -290,7 +317,23 @@ commitList() {
|
||||||
# find the specified start tag, break when found
|
# find the specified start tag, break when found
|
||||||
if [[ -n "$start_tag" ]]; then
|
if [[ -n "$start_tag" ]]; then
|
||||||
[[ "$start_tag" == "${__curr_tag}" ]] && _start_tag_found=true
|
[[ "$start_tag" == "${__curr_tag}" ]] && _start_tag_found=true
|
||||||
[[ "$start_tag" != "${__curr_tag}" && "${_start_tag_found}" == true ]] && break
|
if [[ "${_start_tag_found}" == true ]]; then
|
||||||
|
if [[ -n "$start_commit" ]]; then
|
||||||
|
|
||||||
|
# output commits after start_commit to its closest tag
|
||||||
|
if [[ "$list_style" == true ]]; then
|
||||||
|
_formatCommitPlain "$start_commit~" "${__curr_tag}"
|
||||||
|
else
|
||||||
|
_formatCommitPretty "${__curr_tag}" "${__curr_date}" \
|
||||||
|
"$start_commit~" "${__curr_tag}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ "$start_tag" != "${__curr_tag}" ]] && break
|
||||||
|
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# output commits made between prev_tag and curr_tag, these are all of the
|
# output commits made between prev_tag and curr_tag, these are all of the
|
||||||
|
|
@ -316,8 +359,9 @@ commitListPlain() {
|
||||||
local list_all="${1:-false}"
|
local list_all="${1:-false}"
|
||||||
local start_tag="$2"
|
local start_tag="$2"
|
||||||
local final_tag="$3"
|
local final_tag="$3"
|
||||||
|
local start_commit="$4"
|
||||||
|
|
||||||
commitList "$list_all" "" "$start_tag" "$final_tag" "true"
|
commitList "$list_all" "" "$start_tag" "$final_tag" "true" "$start_commit"
|
||||||
}
|
}
|
||||||
|
|
||||||
commitListPretty() {
|
commitListPretty() {
|
||||||
|
|
@ -325,9 +369,11 @@ commitListPretty() {
|
||||||
local title_tag="$2"
|
local title_tag="$2"
|
||||||
local start_tag="$3"
|
local start_tag="$3"
|
||||||
local final_tag="$4"
|
local final_tag="$4"
|
||||||
|
local start_commit="$5"
|
||||||
local title_date="$(date +'%Y-%m-%d')"
|
local title_date="$(date +'%Y-%m-%d')"
|
||||||
|
|
||||||
commitList "$list_all" "$title_tag" "$start_tag" "$final_tag"
|
commitList "$list_all" "$title_tag" "$start_tag" "$final_tag" "false" \
|
||||||
|
"$start_commit"
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit() {
|
_exit() {
|
||||||
|
|
@ -341,7 +387,7 @@ _exit() {
|
||||||
# The format of `ps` is different between Windows and other platforms,
|
# The format of `ps` is different between Windows and other platforms,
|
||||||
# so we need to calculate the total column number(COL_NUM) of header first.
|
# so we need to calculate the total column number(COL_NUM) of header first.
|
||||||
# Why don't we just use the last column?
|
# Why don't we just use the last column?
|
||||||
# Because the body of CMD column may contain space and be treated as multiple fileds.
|
# Because the body of CMD column may contain space and be treated as multiple fields.
|
||||||
pid_list=( $(ps -f |
|
pid_list=( $(ps -f |
|
||||||
awk -v ppid=$$ 'NR == 1 {
|
awk -v ppid=$$ 'NR == 1 {
|
||||||
COL_NUM = NF
|
COL_NUM = NF
|
||||||
|
|
@ -357,7 +403,7 @@ _exit() {
|
||||||
local _pid
|
local _pid
|
||||||
for _pid in "${pid_list[@]}"; do
|
for _pid in "${pid_list[@]}"; do
|
||||||
echo "killing: ${_pid}"
|
echo "killing: ${_pid}"
|
||||||
kill -TERM ${_pid}
|
kill -TERM "${_pid}"
|
||||||
done
|
done
|
||||||
|
|
||||||
wait; stty sane; exit 1
|
wait; stty sane; exit 1
|
||||||
|
|
@ -374,6 +420,7 @@ main() {
|
||||||
"list_style:false"
|
"list_style:false"
|
||||||
"title_tag:$DEF_TAG_RECENT"
|
"title_tag:$DEF_TAG_RECENT"
|
||||||
"start_tag:"
|
"start_tag:"
|
||||||
|
"start_commit:"
|
||||||
"final_tag:"
|
"final_tag:"
|
||||||
"output_file:"
|
"output_file:"
|
||||||
"use_stdout:false"
|
"use_stdout:false"
|
||||||
|
|
@ -410,6 +457,10 @@ main() {
|
||||||
option=( $(_setValueForKeyFakeAssocArray "start_tag" "$2" "${option[*]}") )
|
option=( $(_setValueForKeyFakeAssocArray "start_tag" "$2" "${option[*]}") )
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--start-commit )
|
||||||
|
option=( $(_setValueForKeyFakeAssocArray "start_commit" "$2" "${option[*]}") )
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-n | --no-merges )
|
-n | --no-merges )
|
||||||
GIT_LOG_OPTS='--no-merges'
|
GIT_LOG_OPTS='--no-merges'
|
||||||
CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
|
CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
|
||||||
|
|
@ -440,13 +491,40 @@ main() {
|
||||||
[[ -z "$CUR_GIT_LOG_FORMAT" ]] && CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
|
[[ -z "$CUR_GIT_LOG_FORMAT" ]] && CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
|
||||||
|
|
||||||
local _tag="$(_valueForKeyFakeAssocArray "start_tag" "${option[*]}")"
|
local _tag="$(_valueForKeyFakeAssocArray "start_tag" "${option[*]}")"
|
||||||
if [[ -n "${_tag}" ]]; then
|
local start_commit="$(_valueForKeyFakeAssocArray "start_commit" "${option[*]}")"
|
||||||
|
|
||||||
|
if [[ -n "$start_commit" ]]; then
|
||||||
|
if [[ -n "${_tag}" ]]; then
|
||||||
|
_error "--start-tag could not use with --start-commit!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
start_tag="$(git describe --tags --contains "$start_commit" 2>/dev/null || echo 'null')"
|
||||||
|
if [[ -z "$start_tag" ]]; then
|
||||||
|
_error "Could find the associative tag for the start-commit!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# remove suffix from the $start_tag when no tag matched exactly
|
||||||
|
start_tag="${start_tag%%~*}"
|
||||||
|
# also remove "^0" added sometimes when tag matched exactly
|
||||||
|
start_tag="${start_tag%%^0}"
|
||||||
|
|
||||||
|
elif [[ -n "${_tag}" ]]; then
|
||||||
start_tag="$(git describe --tags --abbrev=0 "${_tag}" 2>/dev/null)"
|
start_tag="$(git describe --tags --abbrev=0 "${_tag}" 2>/dev/null)"
|
||||||
if [[ -z "$start_tag" ]]; then
|
if [[ -z "$start_tag" ]]; then
|
||||||
_error "Specified start-tag does not exist!"
|
_error "Specified start-tag does not exist!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${_tag}" ]]; then
|
||||||
|
if [[ -n "$start_commit" ]]; then
|
||||||
|
_error "--start-tag could not use with --start-commit!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
unset _tag
|
unset _tag
|
||||||
|
|
||||||
local _tag="$(_valueForKeyFakeAssocArray "final_tag" "${option[*]}")"
|
local _tag="$(_valueForKeyFakeAssocArray "final_tag" "${option[*]}")"
|
||||||
|
|
@ -462,26 +540,29 @@ main() {
|
||||||
#
|
#
|
||||||
# generate changelog
|
# generate changelog
|
||||||
#
|
#
|
||||||
local tmpfile="$(git_extra_mktemp)"
|
local tmpfile changelog title_tag
|
||||||
local changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")"
|
tmpfile="$(git_extra_mktemp)"
|
||||||
local title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")"
|
changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")"
|
||||||
|
title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")"
|
||||||
|
|
||||||
if [[ "$(_valueForKeyFakeAssocArray "list_style" "${option[*]}")" == true ]]; then
|
if [[ "$(_valueForKeyFakeAssocArray "list_style" "${option[*]}")" == true ]]; then
|
||||||
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
|
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
|
||||||
commitListPlain "true" >> "$tmpfile"
|
commitListPlain "true" >> "$tmpfile"
|
||||||
else
|
else
|
||||||
commitListPlain "false" "$start_tag" "$final_tag" >> "$tmpfile"
|
commitListPlain "false" "$start_tag" "$final_tag" \
|
||||||
|
"$start_commit" >> "$tmpfile"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
|
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
|
||||||
commitListPretty "true" "$title_tag" >> "$tmpfile"
|
commitListPretty "true" "$title_tag" >> "$tmpfile"
|
||||||
else
|
else
|
||||||
commitListPretty "false" "$title_tag" "$start_tag" "$final_tag" >> "$tmpfile"
|
commitListPretty "false" "$title_tag" "$start_tag" "$final_tag" \
|
||||||
|
"$start_commit" >> "$tmpfile"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z "$changelog" ]]; then
|
if [[ -z "$changelog" ]]; then
|
||||||
changelog="$(ls | egrep 'change|history' -i | head -n1)"
|
changelog="$(find . -mindepth 1 -maxdepth 1 \( -iname '*change*' -o -iname '*history*' \) | head -n1)"
|
||||||
if [[ -z "$changelog" ]]; then
|
if [[ -z "$changelog" ]]; then
|
||||||
changelog="History.md";
|
changelog="History.md";
|
||||||
fi
|
fi
|
||||||
|
|
@ -500,12 +581,11 @@ main() {
|
||||||
else
|
else
|
||||||
cp -f "$tmpfile" "$changelog"
|
cp -f "$tmpfile" "$changelog"
|
||||||
rm -f "$tmpfile"
|
rm -f "$tmpfile"
|
||||||
# Use `eval` to handle GIT_EDITOR which contains space and options,
|
|
||||||
# like ""C:\Program Files (x86)\Notepad++\notepad++.exe" -multiInst ".
|
if [[ -n "$GIT_EDITOR" ]]; then
|
||||||
# Thanks @JanSchulz to inspire me this solution
|
git_editor "$changelog" || _exit
|
||||||
[[ -n "$GIT_EDITOR" ]] && eval $GIT_EDITOR "$changelog"
|
else
|
||||||
if [[ $? -ne 0 ]]; then
|
less "$changelog" || _exit
|
||||||
_exit
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
ALIAS=chore
|
|
||||||
git feature -a $ALIAS "$@"
|
|
||||||
|
|
@ -1,7 +1,43 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
PROGNAME="git-clear"
|
||||||
|
FORCE=0
|
||||||
|
|
||||||
echo -n "Sure? - This command may delete files that cannot be recovered, including those in .gitignore [y/N]: "
|
_usage() {
|
||||||
read ans
|
cat << EOF
|
||||||
if [ "$ans" == "y" ]
|
usage: $PROGNAME options
|
||||||
then git clean -d -f -x && git reset --hard
|
usage: $PROGNAME -h|help|?
|
||||||
|
|
||||||
|
clear git repository
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-f, --force Force clear without questioning user
|
||||||
|
-h, --help, ? Show this message
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read arguments
|
||||||
|
while [ "$1" != "" ]; do
|
||||||
|
case $1 in
|
||||||
|
-f|--force)
|
||||||
|
FORCE=1
|
||||||
|
;;
|
||||||
|
-h|--help|?)
|
||||||
|
_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# Only wait for answer if not forced by user
|
||||||
|
if [[ $FORCE == 0 ]]; then
|
||||||
|
echo -n "Sure? - THIS COMMAND MAY DELETE FILES THAT CANNOT BE RECOVERED, including those in .gitignore [y/N]: "
|
||||||
|
read -r clean
|
||||||
|
else
|
||||||
|
clean=y
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$clean" = "y" ]; then
|
||||||
|
git clean -d -f -x && git reset --hard
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
echo -n "Sure? - This command may delete files that cannot be recovered. Files and directories in .gitignore will be preserved [y/N]: "
|
echo -n "Sure? - This command may delete files that cannot be recovered. Files and directories in .gitignore will be preserved [y/N]: "
|
||||||
read ans
|
read -r answer
|
||||||
if [ "$ans" == "y" ]
|
if [ "$answer" = "y" ]
|
||||||
then git clean -d -f && git reset --hard
|
then git clean -d -f && git reset --hard
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
17
bin/git-coauthor
Executable file
17
bin/git-coauthor
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e -o pipefail
|
||||||
|
|
||||||
|
coauthor=$1
|
||||||
|
email=$2
|
||||||
|
|
||||||
|
test -z "$coauthor" && echo "co-author required." 1>&2 && exit 1
|
||||||
|
test -z "$email" && echo "co-author email required." 1>&2 && exit 1
|
||||||
|
|
||||||
|
old_message="$(git log --format=%B -n1)"
|
||||||
|
if [[ "$old_message" == *"Co-authored-by:"* ]]; then
|
||||||
|
git commit --amend -m "$old_message
|
||||||
|
Co-authored-by: $coauthor <$email>"
|
||||||
|
else
|
||||||
|
git commit --amend -m "$old_message" -m "Co-authored-by: $coauthor <$email>"
|
||||||
|
fi
|
||||||
|
|
@ -1,6 +1,36 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
SINCE="last week"
|
REF=""
|
||||||
test $# -ne 0 && SINCE=$@
|
SINCE=""
|
||||||
echo "... commits since $SINCE" >&2
|
|
||||||
git log --pretty='%an - %s' --after="@{$SINCE}"
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-r|--ref)
|
||||||
|
if [[ -z "$2" ]]; then
|
||||||
|
echo "error: option $1 requires an argument" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
REF="$2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# non-flag args accumulation, e.g. "last " + "week" -> "last week"
|
||||||
|
SINCE="${SINCE:+$SINCE }$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$REF" && -n "$SINCE" ]]; then
|
||||||
|
echo "error: '--ref <ref>' and '<date>' are mutually exclusive" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SINCE="${SINCE:-last week}"
|
||||||
|
|
||||||
|
if [[ -n "$REF" ]]; then
|
||||||
|
git log --pretty='%h %an - %s' "$REF..HEAD"
|
||||||
|
else
|
||||||
|
git log --pretty='%h %an - %s' --after="@{$SINCE}"
|
||||||
|
fi
|
||||||
|
|
|
||||||
1
bin/git-continue
Symbolic link
1
bin/git-continue
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
git-abort
|
||||||
|
|
@ -4,4 +4,4 @@ user="$*"
|
||||||
|
|
||||||
test -z "$user" && echo "user name required." 1>&2 && exit 1
|
test -z "$user" && echo "user name required." 1>&2 && exit 1
|
||||||
|
|
||||||
git shortlog --author="$user"
|
git shortlog --format=format:"%h %s" --author="$user"
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ if test "$1" = "--all"; then
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo total `git rev-list --count HEAD`
|
echo total "$(git rev-list --count HEAD)"
|
||||||
|
|
|
||||||
78
bin/git-cp
Executable file
78
bin/git-cp
Executable file
|
|
@ -0,0 +1,78 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PROGRAM=$0
|
||||||
|
CURRENT_FILENAME=""
|
||||||
|
DESTINATION_FILENAME=""
|
||||||
|
|
||||||
|
function usage()
|
||||||
|
{
|
||||||
|
echo 1>&2 "USAGE: ${PROGRAM} CURRENT_FILENAME DESTINATION_FILENAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]
|
||||||
|
do
|
||||||
|
key="$1"
|
||||||
|
|
||||||
|
if [[ "$CURRENT_FILENAME" == "" ]]; then
|
||||||
|
CURRENT_FILENAME=$key
|
||||||
|
elif [[ "$DESTINATION_FILENAME" == "" ]]; then
|
||||||
|
DESTINATION_FILENAME=$key
|
||||||
|
else
|
||||||
|
usage
|
||||||
|
exit 30 # Error during arguments parsing
|
||||||
|
fi
|
||||||
|
shift # past argument or value
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$DESTINATION_FILENAME" == "" ]]; then
|
||||||
|
usage
|
||||||
|
exit 20 # Missing arguments CURRENT_FILENAME
|
||||||
|
elif [[ "$CURRENT_FILENAME" == "" ]]; then
|
||||||
|
usage
|
||||||
|
exit 10 # Missing arguments CURRENT_FILENAME
|
||||||
|
else
|
||||||
|
if [ -e "$DESTINATION_FILENAME" ]; then
|
||||||
|
echo 1>&2 "$DESTINATION_FILENAME already exists."
|
||||||
|
echo 1>&2 "Make sure to remove the destination first."
|
||||||
|
exit 40
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DESTINATION_FILENAME" == */ ]]; then
|
||||||
|
echo 1>&2 "$DESTINATION_FILENAME is not a file path."
|
||||||
|
exit 80
|
||||||
|
fi
|
||||||
|
if [[ "$DESTINATION_FILENAME" == */* ]]; then
|
||||||
|
DESTINATION_DIR="${DESTINATION_FILENAME%/*}"
|
||||||
|
if ! mkdir -p "$DESTINATION_DIR"; then
|
||||||
|
echo 1>&2 "Failed to create destination directory: $DESTINATION_DIR"
|
||||||
|
exit 160
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "Copying $CURRENT_FILENAME into $DESTINATION_FILENAME"
|
||||||
|
|
||||||
|
# Pre-check that the source and destination will work
|
||||||
|
git mv --dry-run "${CURRENT_FILENAME}" "${DESTINATION_FILENAME}"
|
||||||
|
|
||||||
|
# Make a new branch and switch to it
|
||||||
|
BRANCH_NAME="git-cp-$(date +%s)"
|
||||||
|
git checkout -b "$BRANCH_NAME"
|
||||||
|
|
||||||
|
# Move the original file to the new destination, in this branch
|
||||||
|
git mv "${CURRENT_FILENAME}" "${DESTINATION_FILENAME}"
|
||||||
|
git commit -nm "--Duplicate $CURRENT_FILENAME history into $DESTINATION_FILENAME"
|
||||||
|
|
||||||
|
# Restore the original file to keep it in the history
|
||||||
|
git checkout HEAD~ "${CURRENT_FILENAME}"
|
||||||
|
git commit -nm "--Restore $CURRENT_FILENAME"
|
||||||
|
|
||||||
|
# Switch to the original branch and merge this back in.
|
||||||
|
git checkout -
|
||||||
|
git merge --no-ff "$BRANCH_NAME" -m "Copy $CURRENT_FILENAME into $DESTINATION_FILENAME"
|
||||||
|
|
||||||
|
# We're now done with the branch, so delete it.
|
||||||
|
# We shouldn't need -D here, as we've already merged it back in.
|
||||||
|
git branch -d "$BRANCH_NAME"
|
||||||
|
fi
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
test $# -eq 0 && echo "branch argument required." 1>&2 && exit 1
|
||||||
|
|
||||||
|
# preference takes lowest priority; look for remote from prefs first
|
||||||
|
REMOTE_PREF=$(git config git-extras.create-branch.remote)
|
||||||
|
if [ -n "$REMOTE_PREF" ]; then
|
||||||
|
REMOTE=$REMOTE_PREF
|
||||||
|
fi
|
||||||
|
|
||||||
while test $# != 0
|
while test $# != 0
|
||||||
do
|
do
|
||||||
case $1 in
|
case $1 in
|
||||||
|
|
@ -12,29 +20,57 @@ do
|
||||||
REMOTE=origin
|
REMOTE=origin
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
--from)
|
||||||
|
if [[ -n $2 ]]; then
|
||||||
|
START_POINT=$2
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
BRANCH=$1
|
BRANCH=$1
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# handle ambiguous `-r` option argument by shift
|
||||||
if [[ -z $BRANCH ]] && [[ -n $REMOTE ]]
|
if [[ -z $BRANCH ]] && [[ -n $REMOTE ]]
|
||||||
then
|
then
|
||||||
BRANCH=$REMOTE
|
BRANCH=$REMOTE
|
||||||
REMOTE=origin
|
REMOTE=origin
|
||||||
fi
|
fi
|
||||||
|
|
||||||
test -z $BRANCH && echo "branch required." 1>&2 && exit 1
|
test -z "$BRANCH" && echo "branch argument required." 1>&2 && exit 1
|
||||||
|
|
||||||
if [[ -n $REMOTE ]]
|
if [[ -n $REMOTE ]]
|
||||||
then
|
then
|
||||||
git ls-remote --exit-code $REMOTE &>/dev/null
|
stderr=$(git_extra_mktemp)
|
||||||
if [ $? = 0 ]
|
git ls-remote --exit-code "$REMOTE" 1>/dev/null 2>"$stderr"
|
||||||
|
REMOTE_EXIT=$?
|
||||||
|
REMOTE_ERROR=$(<"$stderr")
|
||||||
|
rm -f "$stderr"
|
||||||
|
if [ $REMOTE_EXIT -eq 0 ]
|
||||||
then
|
then
|
||||||
git push $REMOTE HEAD:refs/heads/$BRANCH
|
if [[ -n $START_POINT ]]; then
|
||||||
git fetch $REMOTE
|
git fetch "$REMOTE"
|
||||||
git checkout --track -b $BRANCH $REMOTE/$BRANCH
|
git checkout --track -b "$BRANCH" "$START_POINT"
|
||||||
|
git push "$REMOTE" "HEAD:refs/heads/$BRANCH"
|
||||||
|
else
|
||||||
|
git push "$REMOTE" "HEAD:refs/heads/$BRANCH"
|
||||||
|
git fetch "$REMOTE"
|
||||||
|
git checkout --track -b "$BRANCH" "$REMOTE/$BRANCH"
|
||||||
|
fi
|
||||||
exit $?
|
exit $?
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo " Error connecting to remote '$REMOTE': $REMOTE_ERROR"
|
||||||
|
echo
|
||||||
|
exit $REMOTE_EXIT
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git checkout -b $BRANCH
|
if [[ -n $START_POINT ]]
|
||||||
|
then
|
||||||
|
git checkout -b "$BRANCH" "$START_POINT"
|
||||||
|
else
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,16 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Assert there is at least one branch provided
|
# Assert there is at least one branch provided
|
||||||
test -z $1 && echo "branch required." 1>&2 && exit 1
|
test -z "$1" && echo "branch required." 1>&2 && exit 1
|
||||||
|
|
||||||
for branch in "$@"
|
for branch in "$@"
|
||||||
do
|
do
|
||||||
remote=$(git config branch.$branch.remote)
|
remote=$(git config "branch.$branch.remote" || echo "origin")
|
||||||
test -z $remote && remote="origin"
|
ref=$(git config "branch.$branch.merge" || echo "refs/heads/$branch")
|
||||||
ref=$(git config branch.$branch.merge)
|
|
||||||
test -z $ref && ref="refs/heads/$branch"
|
|
||||||
|
|
||||||
git branch -D $branch
|
git branch -D "$branch" || true
|
||||||
# Avoid deleting local upstream
|
# Avoid deleting local upstream
|
||||||
[ "$remote" == "." ] && continue
|
[ "$remote" = "." ] && continue
|
||||||
git branch -d -r $remote/$branch
|
git branch -d -r "$remote/$branch" || continue
|
||||||
git push $remote :$ref
|
git push "$remote" ":$ref"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
60
bin/git-delete-gone-branches
Executable file
60
bin/git-delete-gone-branches
Executable file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
dry_run=false
|
||||||
|
force=false
|
||||||
|
prune=false
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-n|--dry-run)
|
||||||
|
dry_run=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-f|--force)
|
||||||
|
force=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-p|--prune)
|
||||||
|
prune=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
echo "Usage: git delete-gone-branches [-n|--dry-run] [-f|--force] [-p|--prune]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$prune" = true ]; then
|
||||||
|
git fetch --prune
|
||||||
|
fi
|
||||||
|
|
||||||
|
gone_branches=$(git for-each-ref --format \
|
||||||
|
'%(if:equals=gone)%(upstream:track,nobracket)%(then)%(refname:short)%(end)' refs/heads/ | sed '/^$/d')
|
||||||
|
|
||||||
|
if [ -z "$gone_branches" ]; then
|
||||||
|
echo "No branches with a gone remote found."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$dry_run" = true ]; then
|
||||||
|
echo "Would delete the following branches:"
|
||||||
|
echo "$gone_branches"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "The following branches will be deleted:"
|
||||||
|
echo "$gone_branches"
|
||||||
|
|
||||||
|
if [ "$force" = false ]; then
|
||||||
|
read -r -p "Delete these branches? [y/N] " confirm
|
||||||
|
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$gone_branches" | xargs git branch -D
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
branches=$(git branch --no-color --merged | grep -v "\*" | grep -v master | grep -v svn)
|
branches=$(git branch --no-color --merged | grep -vE "^(\*|\+)" | grep -v "$(git_extra_default_branch)" | grep -v svn)
|
||||||
if [ -n "$branches" ]
|
if [ -n "$branches" ]
|
||||||
then
|
then
|
||||||
echo "$branches" | xargs -n 1 git branch -d
|
echo "$branches" | xargs git branch -d
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
28
bin/git-delete-squashed-branches
Executable file
28
bin/git-delete-squashed-branches
Executable file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
proceed=false
|
||||||
|
if [[ $# -gt 0 && ("$1" == "--proceed" || "$1" == "-p") ]]; then
|
||||||
|
proceed=true
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
targetBranch=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
else
|
||||||
|
targetBranch=$1
|
||||||
|
git checkout "$targetBranch"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git for-each-ref refs/heads/ "--format=%(refname:short)" | while read -r branch; do
|
||||||
|
mergeBase=$(git merge-base "$targetBranch" "$branch")
|
||||||
|
|
||||||
|
if [[ $(git cherry "$targetBranch" "$(git commit-tree "$(git rev-parse "$branch^{tree}")" -p "$mergeBase" -m _)") == "-"* ]]; then
|
||||||
|
if [[ $proceed == true ]]; then
|
||||||
|
git branch -D "$branch" || true
|
||||||
|
else
|
||||||
|
git branch -D "$branch"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
@ -1,24 +1,50 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
test -z "$1" && echo "submodule required" 1>&2 && exit 1
|
abort() {
|
||||||
cd "$(git root)"
|
error="$1" && shift
|
||||||
test ! -f .gitmodules && echo ".gitmodules file not found" 1>&2 && exit 2
|
echo "ERROR: $*" 1>&2
|
||||||
|
test -z "$FORCE" && exit "$error"
|
||||||
|
}
|
||||||
|
|
||||||
NAME="$(echo "$1" | sed 's/\/$//g')"
|
# Don't abort on failures. This allows to cleanup after a previous failure.
|
||||||
test -z \
|
[ "$1" = '--force' ] && FORCE=1 && shift
|
||||||
"$(git config --file=.gitmodules submodule."$NAME".url)" \
|
|
||||||
&& echo "submodule not found" 1>&2 && exit 3
|
|
||||||
|
|
||||||
# 1. Delete the relevant section from .git/config and clean submodule files
|
test -z "$1" && abort 1 'Submodule required'
|
||||||
git submodule deinit -f "$NAME" || exit 4
|
cd "$(git root)" || abort 5 'Cannot change to repository root'
|
||||||
rmdir "$NAME"
|
test ! -f '.gitmodules' && abort 2 '.gitmodules file not found'
|
||||||
rm -rf .git/modules/"$NAME"
|
|
||||||
# 2. Delete the relevant line from .gitmodules
|
NAME="${1%/}"
|
||||||
git config --file=.gitmodules --remove-section submodule."$NAME"
|
test -z "$(git config --file='.gitmodules' "submodule.$NAME.url")" \
|
||||||
git add .gitmodules
|
&& abort 3 'Submodule not found'
|
||||||
# 3. Run git rm --cached path_to_submodule
|
|
||||||
git rm --cached -rf "$NAME"
|
# 1. Handle the .git directory
|
||||||
# 4. Need to confirm and commit the changes for yourself
|
# 1.a. Delete the relevant section from .git/config
|
||||||
echo
|
git submodule deinit -f "$NAME" || abort 4 "Failed to deinitialize $NAME"
|
||||||
echo "Now submodule $NAME is deleted."
|
# 1.b. Delete empty submodule directory
|
||||||
echo 'Confirm with `git submodule status` and commit the changes for yourself.'
|
git rm -f "$NAME"
|
||||||
|
|
||||||
|
# 2. Handle .gitmodules file
|
||||||
|
# 2.a. Delete the relevant line from .gitmodules
|
||||||
|
git config --file='.gitmodules' --remove-section "submodule.$NAME" 2>/dev/null || :
|
||||||
|
# 2.b and stage changes
|
||||||
|
git add '.gitmodules'
|
||||||
|
# 2.c. Delete empty .gitmodules
|
||||||
|
[ "$(wc -l '.gitmodules' | cut -d' ' -f1)" = '0' ] && git rm -f '.gitmodules'
|
||||||
|
|
||||||
|
# 3. Need to confirm and commit the changes for yourself
|
||||||
|
git_status_text="$(git submodule status 2>&1)"
|
||||||
|
git_status_exit=$?
|
||||||
|
if [ "$git_status_exit" -eq 0 ] \
|
||||||
|
&& printf '%s' "DUMMY$git_status_text" | grep -v "$NAME" > /dev/null; then
|
||||||
|
# grep fails when piping in an empty string, so we add a DUMMY prefix
|
||||||
|
|
||||||
|
echo "Successfully deleted $NAME."
|
||||||
|
else
|
||||||
|
abort 6 "Failed to delete $NAME with error:\n$git_status_text"
|
||||||
|
fi
|
||||||
|
printf '\n%s\n' '== git submodule status =='
|
||||||
|
printf '%s\n' "$git_status_text"
|
||||||
|
printf '%s\n' '=========================='
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
echo 'Confirm the output of `git submodule status` above (if any)' \
|
||||||
|
'and then commit the changes.'
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,16 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Assert there is at least one tag provided
|
# Assert there is at least one tag provided
|
||||||
test -z $1 && echo "tag required." 1>&2 && exit 1
|
test -z "$1" && echo "tag required." 1>&2 && exit 1
|
||||||
|
|
||||||
|
# Detect the default remote exists or not
|
||||||
|
default_remote=$(git config git-extras.default-remote)
|
||||||
|
|
||||||
|
if [[ -n "$default_remote" ]]; then
|
||||||
|
origin="$default_remote"
|
||||||
|
else
|
||||||
|
origin=origin
|
||||||
|
fi
|
||||||
|
|
||||||
# Concatenate all the tag references
|
# Concatenate all the tag references
|
||||||
local_tags=""
|
local_tags=""
|
||||||
|
|
@ -13,5 +22,7 @@ do
|
||||||
done
|
done
|
||||||
|
|
||||||
# Delete all the tags
|
# Delete all the tags
|
||||||
|
# shellcheck disable=SC2086
|
||||||
git tag -d $local_tags
|
git tag -d $local_tags
|
||||||
git push origin $origin_refs
|
# shellcheck disable=SC2086
|
||||||
|
git push "$origin" $origin_refs
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
branch=master
|
branch=$(git_extra_default_branch)
|
||||||
filter=ACM
|
filter=ACM
|
||||||
|
|
||||||
if test $# -eq 1; then
|
if test $# -eq 1; then
|
||||||
|
|
@ -12,4 +12,4 @@ else
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git diff --name-only --diff-filter=$filter $branch
|
git diff --name-only --diff-filter="$filter" "$branch"
|
||||||
104
bin/git-effort
104
bin/git-effort
|
|
@ -59,14 +59,14 @@ active_days() {
|
||||||
|
|
||||||
color_for() {
|
color_for() {
|
||||||
if [ "$to_tty" = true ]; then
|
if [ "$to_tty" = true ]; then
|
||||||
if [ $1 -gt 200 ]; then color="$(tputq setaf 1)$(tputq bold)"
|
if [ "$1" -gt 200 ]; then color="$(tputq setaf 1)$(tputq bold)"
|
||||||
elif [ $1 -gt 150 ]; then color="$(tputq setaf 1)" # red
|
elif [ "$1" -gt 150 ]; then color="$(tputq setaf 1)" # red
|
||||||
elif [ $1 -gt 125 ]; then color="$(tputq setaf 2)$(tputq bold)"
|
elif [ "$1" -gt 125 ]; then color="$(tputq setaf 2)$(tputq bold)"
|
||||||
elif [ $1 -gt 100 ]; then color="$(tputq setaf 2)" # green
|
elif [ "$1" -gt 100 ]; then color="$(tputq setaf 2)" # green
|
||||||
elif [ $1 -gt 75 ]; then color="$(tputq setaf 5)$(tputq bold)"
|
elif [ "$1" -gt 75 ]; then color="$(tputq setaf 5)$(tputq bold)"
|
||||||
elif [ $1 -gt 50 ]; then color="$(tputq setaf 5)" # purplish
|
elif [ "$1" -gt 50 ]; then color="$(tputq setaf 5)" # purplish
|
||||||
elif [ $1 -gt 25 ]; then color="$(tputq setaf 3)$(tputq bold)"
|
elif [ "$1" -gt 25 ]; then color="$(tputq setaf 3)$(tputq bold)"
|
||||||
elif [ $1 -gt 10 ]; then color="$(tputq setaf 3)" # yellow
|
elif [ "$1" -gt 10 ]; then color="$(tputq setaf 3)" # yellow
|
||||||
else color="$(tputq sgr0)" # default color
|
else color="$(tputq sgr0)" # default color
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
|
@ -84,37 +84,38 @@ effort() {
|
||||||
local color reset_color commits len dot f_dot i msg active
|
local color reset_color commits len dot f_dot i msg active
|
||||||
reset_color=""
|
reset_color=""
|
||||||
test "$to_tty" = true && reset_color="$(tputq sgr0)"
|
test "$to_tty" = true && reset_color="$(tputq sgr0)"
|
||||||
commit_dates=`dates "$path"`
|
if ! commit_dates=$(dates "$path"); then
|
||||||
[ $? -gt 0 ] && exit 255
|
exit 255
|
||||||
|
fi
|
||||||
|
|
||||||
# Ensure it's not just an empty line
|
# Ensure it's not just an empty line
|
||||||
if [ -z "`head -c 1 <<<$(echo $commit_dates)`" ]
|
if [ -z "$(head -c 1 <<<"$commit_dates")" ]
|
||||||
then
|
then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
commits=`wc -l <<<"$(echo "$commit_dates")"`
|
commits=$(wc -l <<<"$commit_dates")
|
||||||
color='90'
|
color='90'
|
||||||
|
|
||||||
# ignore <= --above
|
# ignore <= --above
|
||||||
test $commits -le $above && exit 0
|
test "$commits" -le "$above" && exit 0
|
||||||
|
|
||||||
# commits
|
# commits
|
||||||
color_for $(( $commits - $above ))
|
color_for $(( commits - above ))
|
||||||
len=${#path}
|
len=${#path}
|
||||||
dot="."
|
dot="."
|
||||||
f_dot="$path"
|
f_dot="$path"
|
||||||
i=0 ; while test $i -lt $(( $columns - $len )) ; do
|
i=0 ; while test $i -lt $(( columns - len )) ; do
|
||||||
f_dot=$f_dot$dot
|
f_dot=$f_dot$dot
|
||||||
i=$(($i+1))
|
i=$((i+1))
|
||||||
done
|
done
|
||||||
|
|
||||||
msg=$(printf " ${color}%s %-10d" "$f_dot" $commits)
|
msg=$(printf " ${color}%s %-10d" "$f_dot" "$commits")
|
||||||
|
|
||||||
# active days
|
# active days
|
||||||
active=`active_days "$commit_dates"`
|
active=$(active_days "$commit_dates")
|
||||||
color_for $(( $active - $above ))
|
color_for $(( active - above ))
|
||||||
msg="$msg $(printf "${color} %d${reset_color}\n" $active)"
|
msg="$msg $(printf "${color} %d${reset_color}\n" "$active")"
|
||||||
echo "$msg"
|
echo "$msg"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,7 +137,16 @@ sort_effort() {
|
||||||
clear
|
clear
|
||||||
echo " "
|
echo " "
|
||||||
heading
|
heading
|
||||||
< $tmp sort -rn -k 2
|
< "$tmp" sort -rn -k 2
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# get processor count, default 8
|
||||||
|
#
|
||||||
|
procs() {
|
||||||
|
if ! (nproc --all || getconf NPROCESSORS_ONLN || getconf _NPROCESSORS_ONLN || grep -c processor /proc/cpuinfo) 2>/dev/null; then
|
||||||
|
echo 8
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
declare -a paths=()
|
declare -a paths=()
|
||||||
|
|
@ -177,50 +187,48 @@ fi
|
||||||
args_to_git_log="${args_to_git_log#\ \'\'}"
|
args_to_git_log="${args_to_git_log#\ \'\'}"
|
||||||
export args_to_git_log
|
export args_to_git_log
|
||||||
|
|
||||||
# set column width to match longest filename
|
|
||||||
columns=$(( $(git ls-files | awk '{ print length }' | sort -rn | head -1 ) + 5 ))
|
|
||||||
export columns
|
|
||||||
|
|
||||||
# [path ...]
|
# [path ...]
|
||||||
|
|
||||||
if test "${#paths}" -eq 0; then
|
if test "${#paths}" -eq 0; then
|
||||||
save_ifs=$IFS
|
save_ifs=$IFS
|
||||||
IFS=`echo -en "\n\b"`
|
IFS=$(echo -en "\n\b")
|
||||||
paths=(`git ls-files`)
|
paths=($(git ls-files))
|
||||||
IFS=$save_ifs
|
IFS=$save_ifs
|
||||||
unset save_ifs
|
unset save_ifs
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# set column width to match longest filename
|
||||||
|
max=0
|
||||||
|
for path in "${paths[@]}"; do
|
||||||
|
cur=${#path}
|
||||||
|
if [[ $max -lt $cur ]]; then
|
||||||
|
max=$cur
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
columns=$(( max + 5 ))
|
||||||
|
export columns
|
||||||
|
|
||||||
# hide cursor
|
# hide cursor
|
||||||
|
|
||||||
hide_cursor
|
hide_cursor
|
||||||
trap show_cursor_and_cleanup INT
|
trap show_cursor_and_cleanup INT
|
||||||
|
|
||||||
# export functions so subshells can call them
|
|
||||||
export -f effort
|
|
||||||
export -f color_for
|
|
||||||
export -f active_days
|
|
||||||
export -f dates
|
|
||||||
export -f tputq
|
|
||||||
export to_tty
|
|
||||||
export above
|
|
||||||
export log_args
|
|
||||||
|
|
||||||
|
|
||||||
bash_params=
|
|
||||||
# If bash exits successfully with --import-functions,
|
|
||||||
# then we need to pass it (FreeBSD probably)
|
|
||||||
bash --import-functions -c ":" 1>/dev/null 2>&1
|
|
||||||
if [ $? -eq 0 ] ; then
|
|
||||||
bash_params="--import-functions"
|
|
||||||
fi
|
|
||||||
|
|
||||||
heading
|
heading
|
||||||
|
|
||||||
# send paths to effort
|
# send paths to effort
|
||||||
printf "%s\0" "${paths[@]}" | xargs -0 -n 1 -P 4 -I % bash $bash_params -c "effort \"%\"" | tee $tmp
|
nPaths=${#paths[@]}
|
||||||
|
nProcs=$(procs)
|
||||||
|
nJobs="\j"
|
||||||
|
for ((i=0; i<nPaths; ++i))
|
||||||
|
do
|
||||||
|
while (( ${nJobs@P} >= nProcs )); do
|
||||||
|
wait -n
|
||||||
|
done
|
||||||
|
effort "${paths[i]}" &
|
||||||
|
done|tee "$tmp"
|
||||||
|
|
||||||
# if more than one path, sort and print
|
# if more than one path, sort and print
|
||||||
test "$(wc -l $tmp | awk '{print $1}')" -gt 1 && sort_effort
|
test "$(wc -l "$tmp" | awk '{print $1}')" -gt 1 && sort_effort
|
||||||
echo
|
echo
|
||||||
|
|
||||||
show_cursor_and_cleanup
|
show_cursor_and_cleanup
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
VERSION="4.5.0"
|
VERSION="7.6.0-dev"
|
||||||
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/master/install.sh"
|
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
local bin="$(which git-extras)"
|
local bin="$(command -v git-extras)"
|
||||||
local prefix=${bin%/*/*}
|
local prefix=${bin%/*/*}
|
||||||
local orig=$PWD
|
local orig=$PWD
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateForWindows() {
|
updateForWindows() {
|
||||||
local bin="$(which git-extras)"
|
local bin="$(command -v git-extras)"
|
||||||
local prefix=${bin%/*/*}
|
local prefix=${bin%/*/*}
|
||||||
local orig=$PWD
|
local orig=$PWD
|
||||||
|
|
||||||
|
|
|
||||||
136
bin/git-feature
136
bin/git-feature
|
|
@ -1,55 +1,105 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
branch_prefix=feature
|
branch_prefix=$(git config --get git-extras.feature.prefix)
|
||||||
|
|
||||||
|
if [ -z "$branch_prefix" ]; then
|
||||||
|
branch_prefix="feature"
|
||||||
|
fi
|
||||||
|
|
||||||
|
branch_separator=$(git config --get git-extras.feature.separator)
|
||||||
|
|
||||||
|
if [ -z "$branch_separator" ]; then
|
||||||
|
branch_separator="/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
merge_mode="--no-ff"
|
||||||
|
finish=false
|
||||||
declare -a argv
|
declare -a argv
|
||||||
while test $# != 0
|
while test $# != 0
|
||||||
do
|
do
|
||||||
case $1 in
|
case $1 in
|
||||||
-a|--alias )
|
-a|--alias )
|
||||||
if [[ -n $2 ]]
|
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||||
then
|
then
|
||||||
shift # shift -a|-alias
|
shift # shift -a|-alias
|
||||||
branch_prefix=$1
|
branch_prefix=$1
|
||||||
else
|
else
|
||||||
argv+=($1) # treat tail '-a' as <name>
|
echo >&2 "option $1 requires a value"
|
||||||
fi
|
exit 1
|
||||||
;;
|
fi
|
||||||
-r|--remote )
|
;;
|
||||||
if [[ -n $2 ]]
|
-r|--remote )
|
||||||
then
|
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||||
remote=$2
|
then
|
||||||
shift
|
remote=$2
|
||||||
else
|
shift
|
||||||
remote="origin"
|
else
|
||||||
fi
|
remote="origin"
|
||||||
;;
|
fi
|
||||||
* )
|
;;
|
||||||
argv+=($1)
|
-s|--separator )
|
||||||
;;
|
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||||
esac
|
then
|
||||||
|
branch_separator=$2
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
echo >&2 "option $1 requires a value"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--squash )
|
||||||
|
merge_mode="--squash"
|
||||||
|
;;
|
||||||
|
--from )
|
||||||
|
start_point=$2
|
||||||
shift
|
shift
|
||||||
|
;;
|
||||||
|
-- )
|
||||||
|
# terminate argument parsing
|
||||||
|
shift
|
||||||
|
argv+=("$@")
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
finish )
|
||||||
|
finish=true
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
argv+=("$1")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
concatargs(){
|
concatargs() {
|
||||||
str=$(IFS='-'; echo "$*")
|
str=$(IFS='-'; echo "$*")
|
||||||
branch="$branch_prefix"/$str
|
branch="$branch_prefix$branch_separator$str"
|
||||||
}
|
}
|
||||||
|
|
||||||
if test "${argv[0]}" = "finish"; then
|
test -z "${argv[0]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
|
||||||
test -z "${argv[1]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
|
|
||||||
branch="$branch_prefix"/"${argv[1]}"
|
concatargs "${argv[@]}"
|
||||||
git merge --no-ff "$branch" && git delete-branch "$branch"
|
|
||||||
|
if "${finish}"
|
||||||
|
then
|
||||||
|
git merge ${merge_mode} "$branch" && git delete-branch "$branch"
|
||||||
else
|
else
|
||||||
test -z "${argv[0]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
|
if [[ -n $remote ]] && [[ -z $start_point ]]
|
||||||
if test -n "${argv[1]}"; then
|
then
|
||||||
concatargs "${argv[@]}"
|
git create-branch -r "$remote" "$branch"
|
||||||
else
|
fi
|
||||||
branch="$branch_prefix"/"${argv[0]}"
|
|
||||||
fi
|
if [[ -z $remote ]] && [[ -z $start_point ]]
|
||||||
if [[ -n $remote ]]
|
then
|
||||||
then
|
git create-branch "$branch"
|
||||||
git create-branch -r $remote $branch
|
fi
|
||||||
else
|
|
||||||
git create-branch $branch
|
if [[ -n $remote ]] && [[ -n $start_point ]]
|
||||||
fi
|
then
|
||||||
|
git create-branch -r "$remote" --from "$start_point" "$branch"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $remote ]] && [[ -n $start_point ]]
|
||||||
|
then
|
||||||
|
git create-branch --from "$start_point" "$branch"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ repository from scratch.
|
||||||
changed files will be reset, local branches and other remotes will be removed.
|
changed files will be reset, local branches and other remotes will be removed.
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
-b, --branch The branch to pull from the remote (default: master)
|
-b, --branch The branch to pull from the remote
|
||||||
-h, --help Display this help message
|
-h, --help Display this help message
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
|
@ -63,21 +63,21 @@ main() {
|
||||||
|
|
||||||
if [ -d "${destination_path}/.git" ]; then
|
if [ -d "${destination_path}/.git" ]; then
|
||||||
(
|
(
|
||||||
cd ${destination_path}
|
cd "${destination_path}"
|
||||||
|
|
||||||
# Delete all remotes
|
# Delete all remotes
|
||||||
for remote in `git remote`; do
|
for remote in $(git remote); do
|
||||||
git remote rm ${remote}
|
git remote rm "${remote}"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Add origin
|
# Add origin
|
||||||
git remote add origin ${remote_url}
|
git remote add origin "${remote_url}"
|
||||||
git fetch origin
|
git fetch origin
|
||||||
|
|
||||||
# Set default branch
|
# Set default branch
|
||||||
if [ -z "${branch:-}" ]; then
|
if [ -z "${branch:-}" ]; then
|
||||||
branch=`LC_ALL=C git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$'`
|
branch=$(LC_ALL=C git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$')
|
||||||
git remote set-head origin ${branch}
|
git remote set-head origin "${branch}"
|
||||||
else
|
else
|
||||||
git remote set-head origin -a
|
git remote set-head origin -a
|
||||||
fi
|
fi
|
||||||
|
|
@ -87,19 +87,20 @@ main() {
|
||||||
git reset --hard HEAD
|
git reset --hard HEAD
|
||||||
|
|
||||||
# Get on the desired branch
|
# Get on the desired branch
|
||||||
git checkout ${branch}
|
git checkout "${branch}"
|
||||||
git reset --hard origin/${branch}
|
git reset --hard "origin/${branch}"
|
||||||
|
|
||||||
# Delete all other branches
|
# Delete all other branches
|
||||||
branches=`git branch | grep -v \* | xargs`
|
# shellcheck disable=SC2063
|
||||||
|
branches=$(git branch | grep -v '*' | xargs)
|
||||||
if [ -n "${branches}" ]; then
|
if [ -n "${branches}" ]; then
|
||||||
git branch -D ${branches}
|
git branch -D "${branches}"
|
||||||
fi
|
fi
|
||||||
)
|
)
|
||||||
elif [ -n "${branch:-}" ]; then
|
elif [ -n "${branch:-}" ]; then
|
||||||
git clone -b ${branch} ${remote_url} ${destination_path}
|
git clone -b "${branch}" "${remote_url}" "${destination_path}"
|
||||||
else
|
else
|
||||||
git clone ${remote_url} ${destination_path}
|
git clone "${remote_url}" "${destination_path}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
27
bin/git-fork
27
bin/git-fork
|
|
@ -12,14 +12,18 @@ test -z "$url" && abort "github repo needs to be specified as an argument"
|
||||||
|
|
||||||
# validate user
|
# validate user
|
||||||
echo "Enter your github username"
|
echo "Enter your github username"
|
||||||
read user
|
read -r user
|
||||||
[ -n "$user" ] || abort "git username required"
|
[ -n "$user" ] || abort "git username required"
|
||||||
echo "Enter github two-factor authentication code (leave blank if not set up)"
|
# personal access token
|
||||||
read MFA_CODE
|
# config name is github-personal-access-token '_' is not allowed in git config
|
||||||
|
|
||||||
|
github_personal_access_token=$(git config --default "$GITHUB_TOKEN" git-extras.github-personal-access-token)
|
||||||
|
|
||||||
|
test -z "$github_personal_access_token" && abort "GITHUB_TOKEN, or git config git-extras.github-personal-access-token required"
|
||||||
|
|
||||||
# extract owner + project from repo url
|
# extract owner + project from repo url
|
||||||
project=${url##*/}
|
project=${url##*/}
|
||||||
owner=${url%/$project}
|
owner=${url%/"$project"}
|
||||||
project=${project%.git}
|
project=${project%.git}
|
||||||
if [[ $owner == git@* ]]; then
|
if [[ $owner == git@* ]]; then
|
||||||
owner=${owner##*:}
|
owner=${owner##*:}
|
||||||
|
|
@ -28,19 +32,20 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# validate
|
# validate
|
||||||
[ -z "$project" -o -z "$owner" ] && abort "github repo needs to be specified as an argument"
|
[[ -z "$project" || -z "$owner" ]] && abort "github repo needs to be specified as an argument"
|
||||||
|
|
||||||
# create fork
|
# create fork
|
||||||
curl -qsf \
|
if ! curl -qsf \
|
||||||
-X POST \
|
-X POST \
|
||||||
-u "$user" \
|
-u "$user:$github_personal_access_token" \
|
||||||
-H "X-GitHub-OTP: $MFA_CODE" \
|
-H "X-GitHub-OTP: $MFA_CODE" \
|
||||||
"https://api.github.com/repos/$owner/$project/forks"
|
"https://api.github.com/repos/$owner/$project/forks"
|
||||||
|
then
|
||||||
[ $? = 0 ] || abort "fork failed"
|
abort "fork failed"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Add GitHub remote branch via SSH (you will be prompted to verify the server's credentials)? (y/n)"
|
echo "Add GitHub remote branch via SSH (you will be prompted to verify the server's credentials)? (y/n)"
|
||||||
read use_ssh
|
read -r use_ssh
|
||||||
# Check if user has ssh configured with GitHub
|
# Check if user has ssh configured with GitHub
|
||||||
if [ -n "$use_ssh" ] && ssh -T git@github.com 2>&1 | grep -qi 'success'; then
|
if [ -n "$use_ssh" ] && ssh -T git@github.com 2>&1 | grep -qi 'success'; then
|
||||||
remote_prefix="git@github.com:"
|
remote_prefix="git@github.com:"
|
||||||
|
|
@ -56,7 +61,7 @@ else
|
||||||
# clone forked repo into current dir
|
# clone forked repo into current dir
|
||||||
git clone "${remote_prefix}${user}/${project}.git" "$project"
|
git clone "${remote_prefix}${user}/${project}.git" "$project"
|
||||||
# add reference to origin fork so can merge in upstream changes
|
# add reference to origin fork so can merge in upstream changes
|
||||||
cd "$project"
|
cd "$project" || exit
|
||||||
git remote add upstream "${remote_prefix}${owner}/${project}.git"
|
git remote add upstream "${remote_prefix}${owner}/${project}.git"
|
||||||
git fetch upstream
|
git fetch upstream
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
branch=$1
|
branch=$1
|
||||||
|
|
||||||
test -z $branch && echo "branch required." 1>&2 && exit 1
|
test -z "$branch" && echo "branch required." 1>&2 && exit 1
|
||||||
|
|
||||||
changes=`git status --porcelain`
|
changes=$(git status --porcelain)
|
||||||
|
|
||||||
clean()
|
clean()
|
||||||
{
|
{
|
||||||
git symbolic-ref HEAD refs/heads/$branch
|
git symbolic-ref HEAD "refs/heads/$branch"
|
||||||
rm .git/index
|
rm .git/index
|
||||||
git clean -fdx
|
git clean -fdx
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ ! -z "$changes" ]; then
|
if [ -n "$changes" ]; then
|
||||||
read -p "All untracked changes will be lost. Continue [y/N]? " res
|
read -rp "All untracked changes will be lost. Continue [y/N]? " res
|
||||||
case $res in
|
case $res in
|
||||||
[Yy]* ) ;;
|
[Yy]* ) ;;
|
||||||
* ) exit 0;;
|
* ) exit 0;;
|
||||||
|
|
|
||||||
40
bin/git-get
Executable file
40
bin/git-get
Executable file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
_usage() {
|
||||||
|
printf '%s\n' "usage: ${0##*/} <url>
|
||||||
|
usage: ${0##*/} --help
|
||||||
|
|
||||||
|
Clone a repository in a particular directory."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
_usage
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for arg; do
|
||||||
|
if [ "$arg" = '-h' ] || [ "$arg" = '--help' ]; then
|
||||||
|
_usage
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
url=$1
|
||||||
|
if ! shift; then
|
||||||
|
printf 'ERROR: Failed to shift' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
clone_path=$(git config --get git-extras.get.clone-path)
|
||||||
|
|
||||||
|
if [ -z "$clone_path" ]; then
|
||||||
|
printf 'ERROR: %s\n' "Git configuration key 'git-extras.get.clone-path' must be set to a directory to clone under" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
dirname=${url%/}
|
||||||
|
dirname=${dirname%.git}
|
||||||
|
dirname=${dirname##*/}
|
||||||
|
|
||||||
|
mkdir -p "$clone_path"
|
||||||
|
git clone "$url" "$clone_path/$dirname" "$@"
|
||||||
|
|
@ -4,10 +4,10 @@ echo 'setting up gh-pages'
|
||||||
echo '-------------------'
|
echo '-------------------'
|
||||||
|
|
||||||
echo 'Tell me your github account username: '
|
echo 'Tell me your github account username: '
|
||||||
read username
|
read -r username
|
||||||
|
|
||||||
echo 'Now, tell me your repository name: '
|
echo 'Now, tell me your repository name: '
|
||||||
read repository
|
read -r repository
|
||||||
|
|
||||||
git stash \
|
git stash \
|
||||||
&& git checkout -b 'gh-pages' \
|
&& git checkout -b 'gh-pages' \
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
src=$1
|
src=$1
|
||||||
dst=$2
|
dst=$2
|
||||||
|
|
||||||
test -z $src && echo "source branch required." 1>&2 && exit 1
|
test -z "$src" && echo "source branch required." 1>&2 && exit 1
|
||||||
test -z $dst && echo "destination branch required." 1>&2 && exit 1
|
test -z "$dst" && echo "destination branch required." 1>&2 && exit 1
|
||||||
|
|
||||||
git checkout $dst \
|
git checkout "$dst" \
|
||||||
&& git merge --no-ff $src \
|
&& git merge --no-ff "$src" \
|
||||||
&& git branch -d $src
|
&& git branch -d "$src"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
for param in $*
|
for param in "$@"
|
||||||
do
|
do
|
||||||
case $param in
|
case $param in
|
||||||
-h)
|
-h)
|
||||||
|
|
@ -31,7 +31,7 @@ do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
cd "$(git-root)" # cd for git blame
|
cd "$(git-root)" || exit # cd for git blame
|
||||||
MERGED_LOG=$(git_extra_mktemp)
|
MERGED_LOG=$(git_extra_mktemp)
|
||||||
if [[ $EMAIL == '-e' ]]
|
if [[ $EMAIL == '-e' ]]
|
||||||
then
|
then
|
||||||
|
|
@ -44,11 +44,13 @@ for file in $(git diff --name-only "$@")
|
||||||
do
|
do
|
||||||
test -n "$DEBUG" && echo "git blame $file"
|
test -n "$DEBUG" && echo "git blame $file"
|
||||||
# $1 - since $2 - until
|
# $1 - since $2 - until
|
||||||
|
# shellcheck disable=SC2086
|
||||||
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null |
|
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null |
|
||||||
sed -n "$PATTERN" | sort | uniq -c | sed 's/^\(.\)/- \1/' >> $MERGED_LOG
|
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/- \1/' >> "$MERGED_LOG"
|
||||||
# if $2 not given, use current commit as "until"
|
# if $2 not given, use current commit as "until"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
git blame $NOT_WHITESPACE --line-porcelain "${2-@}" -- "$file" 2> /dev/null |
|
git blame $NOT_WHITESPACE --line-porcelain "${2-@}" -- "$file" 2> /dev/null |
|
||||||
sed -n "$PATTERN" | sort | uniq -c | sed 's/^\(.\)/+ \1/' >> $MERGED_LOG
|
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/+ \1/' >> "$MERGED_LOG"
|
||||||
done
|
done
|
||||||
|
|
||||||
DEBUG="$DEBUG" awk '
|
DEBUG="$DEBUG" awk '
|
||||||
|
|
@ -71,20 +73,15 @@ END {
|
||||||
printf("%d %s\n", contributors[people], people)
|
printf("%d %s\n", contributors[people], people)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' $MERGED_LOG | sort -nr | # only gawk supports built-in sort function
|
}' "$MERGED_LOG" | sort -nr | # only gawk supports built-in sort function
|
||||||
while read line
|
while read -r line
|
||||||
do
|
do
|
||||||
people=${line#* }
|
people=${line#* }
|
||||||
num=${line%% *}
|
num=${line%% *}
|
||||||
|
|
||||||
if [[ $num -gt 0 ]]
|
if [[ $num -gt 0 ]]
|
||||||
then
|
then
|
||||||
if [[ ${#people} -ge 29 ]]
|
printf "%-29s \033[00;32m" "$people"
|
||||||
then
|
|
||||||
printf "%-29s \033[00;32m" "$people"
|
|
||||||
else
|
|
||||||
printf "%-29s\t\033[00;32m" "$people"
|
|
||||||
fi
|
|
||||||
if [[ $num -ge 50 ]]
|
if [[ $num -ge 50 ]]
|
||||||
then
|
then
|
||||||
len=${#num}
|
len=${#num}
|
||||||
|
|
@ -92,7 +89,7 @@ do
|
||||||
do
|
do
|
||||||
printf "+"
|
printf "+"
|
||||||
done
|
done
|
||||||
printf "(%s)" $num
|
printf "(%s)" "$num"
|
||||||
else
|
else
|
||||||
for (( i = 0; i < num; i++ ))
|
for (( i = 0; i < num; i++ ))
|
||||||
do
|
do
|
||||||
|
|
@ -100,12 +97,7 @@ do
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [[ ${#people} -ge 29 ]]
|
printf "%-29s \033[00;31m" "$people"
|
||||||
then
|
|
||||||
printf "%-29s \033[00;31m" "$people"
|
|
||||||
else
|
|
||||||
printf "%-29s\t\033[00;31m" "$people"
|
|
||||||
fi
|
|
||||||
if [[ $num -le -50 ]]
|
if [[ $num -le -50 ]]
|
||||||
then
|
then
|
||||||
len=${#num}
|
len=${#num}
|
||||||
|
|
@ -113,7 +105,7 @@ do
|
||||||
do
|
do
|
||||||
printf "-"
|
printf "-"
|
||||||
done
|
done
|
||||||
printf "(%s)" $num
|
printf "(%s)" "$num"
|
||||||
else
|
else
|
||||||
for (( i = 0; i > num; i-- ))
|
for (( i = 0; i > num; i-- ))
|
||||||
do
|
do
|
||||||
|
|
|
||||||
269
bin/git-ignore
269
bin/git-ignore
|
|
@ -1,6 +1,28 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
function show_contents {
|
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
|
||||||
|
|
||||||
|
# awk script that takes 2 input files. The first one is newline deilmeted
|
||||||
|
# string of patterns to look for, second one is the file to remove them from.
|
||||||
|
# outfile has to be passed with -v upon call and is the file result is
|
||||||
|
# written to.
|
||||||
|
read -r -d '' AWK_SCRIPT <<'EOF'
|
||||||
|
# process the first "file" (newline split argument list)
|
||||||
|
NR==FNR { a[b++]=$0; next; }
|
||||||
|
# process file
|
||||||
|
{
|
||||||
|
found=0;
|
||||||
|
for (i=0; i != b; i++) {
|
||||||
|
if (a[i] == $0) { found=1; }
|
||||||
|
}
|
||||||
|
if (found == 1) { print "... removing '"$0"'"; }
|
||||||
|
else { print $0 > outfile }
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
show_contents() {
|
||||||
local file="${2/#~/$HOME}"
|
local file="${2/#~/$HOME}"
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
echo "$1 gitignore: $2" && cat "$file"
|
echo "$1 gitignore: $2" && cat "$file"
|
||||||
|
|
@ -9,58 +31,255 @@ function show_contents {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function show_global {
|
cd_to_git_root() {
|
||||||
show_contents Global `git config --global core.excludesfile`
|
local error_level="$1"
|
||||||
|
|
||||||
|
if ! git rev-parse --git-dir &>/dev/null; then
|
||||||
|
if [ "$error_level" = '--warn' ]; then
|
||||||
|
echo "Warning: Not currently in a Git repository" >&2
|
||||||
|
elif [ "$error_level" = '--error' ]; then
|
||||||
|
echo "Error: Not currently in a Git repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local result=
|
||||||
|
if result=$(git rev-parse --show-toplevel 2>/dev/null); then
|
||||||
|
cd "$result" || exit
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_global {
|
global_ignore() {
|
||||||
local global_gitignore=$(git config --global core.excludesfile)
|
if ! git config --global core.excludesFile 2>/dev/null; then
|
||||||
|
if [ -f "$HOME/.gitignore" ]; then
|
||||||
|
echo "$HOME/.gitignore"
|
||||||
|
else
|
||||||
|
echo "${XDG_CONFIG_HOME:-$HOME/.config}/git/ignore"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_global() {
|
||||||
|
show_contents Global "$(global_ignore)"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_global() {
|
||||||
|
local global_gitignore
|
||||||
|
global_gitignore="$(global_ignore)"
|
||||||
if [ -z "$global_gitignore" ]; then
|
if [ -z "$global_gitignore" ]; then
|
||||||
echo "Can't find global .gitignore."
|
echo "Can't find global .gitignore."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Use 'git config --global --add core.excludesfile ~/.gitignore-global' to set the path to your global gitignore file to '~/.gitignore-global'."
|
echo "Use 'git config --global --add core.excludesfile ~/.gitignore-global' to set the path to your global gitignore file to '~/.gitignore-global'."
|
||||||
echo ""
|
echo ""
|
||||||
else
|
else
|
||||||
add_patterns `git config --global core.excludesfile` "$@"
|
add_patterns "$global_gitignore" "$@"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function show_local {
|
show_local() {
|
||||||
cd "$(git root)"
|
cd_to_git_root --warn
|
||||||
show_contents Local .gitignore
|
show_contents Local .gitignore
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_local {
|
add_local() {
|
||||||
cd "$(git root)"
|
cd_to_git_root --warn
|
||||||
add_patterns .gitignore "$@"
|
add_patterns .gitignore "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_patterns {
|
show_private() {
|
||||||
|
cd_to_git_root --error
|
||||||
|
show_contents Private "${GIT_DIR}/info/exclude"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_private() {
|
||||||
|
cd_to_git_root --error
|
||||||
|
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
|
||||||
|
add_patterns "${GIT_DIR}/info/exclude" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_patterns() {
|
||||||
echo "Adding pattern(s) to: $1"
|
echo "Adding pattern(s) to: $1"
|
||||||
local file="${1/#~/$HOME}"
|
local file="${1/#~/$HOME}"
|
||||||
|
dir_name=$(dirname "$file")
|
||||||
|
if [ ! -d "$dir_name" ]; then
|
||||||
|
mkdir -p "$dir_name"
|
||||||
|
fi
|
||||||
|
if [ -s "$file" ]; then
|
||||||
|
# If the content of $file doesn't end with a newline, add one
|
||||||
|
test "$(tail -c 1 "$file")" != "" && echo "" >> "$file"
|
||||||
|
fi
|
||||||
for pattern in "${@:2}"; do
|
for pattern in "${@:2}"; do
|
||||||
echo "... adding '$pattern'"
|
echo "... adding '$pattern'"
|
||||||
(test -f "$file" && test "$pattern" && grep -q -F -x -- "$pattern" "$file") || echo "$pattern" >> "$file"
|
(test -f "$file" && test "$pattern" && grep -q -F -x -- "$pattern" "$file") || echo "$pattern" >> "$file"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
edit_file() {
|
||||||
|
local file="${2/#~/$HOME}"
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
echo "Editing $1 gitignore: ($2)" && eval "$(git var GIT_EDITOR) $(printf '%q' "$file")"
|
||||||
|
echo "Done."
|
||||||
|
else
|
||||||
|
echo "There is no $1 .gitignore yet." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: git ignore [OPTION]... [PATTERN]...
|
||||||
|
Modify or display local global or private gitignore files.
|
||||||
|
|
||||||
|
OPTIONs set context (local, global, private) and action (display, add,
|
||||||
|
remove, edit). In case of collision, last flag overwrites preceding ones.
|
||||||
|
Defaults are: local context, action is display (see man git ignore for
|
||||||
|
more details).
|
||||||
|
Context examples:
|
||||||
|
local: .../repo/.gitignore
|
||||||
|
private: .../repo/.git/info/exclude
|
||||||
|
global: \$HOME/.gitignore
|
||||||
|
|
||||||
|
PATTERNs are the gitignore patterns as specified in git documentation.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-l, --local Set context to local gitignore
|
||||||
|
-g, --global Set context to global gitignore
|
||||||
|
-p, --private Set context to private gitignore
|
||||||
|
-e, --edit Set action to edit
|
||||||
|
-r, --remove Set action to remove
|
||||||
|
-h, --help Print this message
|
||||||
|
-- End of options delimiter
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_patterns() {
|
||||||
|
declare -a args
|
||||||
|
local tmpfile
|
||||||
|
args=( "${@:3}" )
|
||||||
|
local file="${2/#~/$HOME}"
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
tmpfile="$(mktemp -q "${TMPDIR:-/tmp}"/git-extras-ignore.XXXXXX 2>/dev/null)"
|
||||||
|
echo "Removing patterns from $1 gitignore: $2"
|
||||||
|
awk -v outfile="$tmpfile" "${AWK_SCRIPT}"\
|
||||||
|
<(printf '%s\n' "${args[@]}") "$file"
|
||||||
|
cat "$tmpfile" > "$file"
|
||||||
|
rm "$tmpfile"
|
||||||
|
else
|
||||||
|
echo "There is no $1 .gitignore yet"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# can have only 1 action and 1 level, if conflicting flags are passed
|
||||||
|
# latter ones overwrite former
|
||||||
|
# sanity of args not tested (e.g. there should be no args if -e is supplied)
|
||||||
if test $# -eq 0; then
|
if test $# -eq 0; then
|
||||||
show_global
|
show_global
|
||||||
echo "---------------------------------"
|
echo "---------------------------------"
|
||||||
show_local
|
show_local
|
||||||
|
echo "---------------------------------"
|
||||||
|
show_private
|
||||||
else
|
else
|
||||||
case "$1" in
|
# parse flags and args
|
||||||
-l|--local)
|
level="none"
|
||||||
test $# -gt 1 && add_local "${@:2}" && echo
|
action="none"
|
||||||
show_local
|
declare -a args
|
||||||
;;
|
while [ $# -gt 0 ]; do
|
||||||
-g|--global)
|
case "$1" in
|
||||||
test $# -gt 1 && add_global "${@:2}" && echo
|
-h|--help)
|
||||||
show_global
|
_usage
|
||||||
;;
|
exit 0
|
||||||
*)
|
;;
|
||||||
add_local "$@"
|
-l|--local)
|
||||||
;;
|
level="local"
|
||||||
esac
|
;;
|
||||||
fi
|
-g|--global)
|
||||||
|
level="global"
|
||||||
|
;;
|
||||||
|
-p|--private)
|
||||||
|
level="private"
|
||||||
|
;;
|
||||||
|
-e|--edit)
|
||||||
|
action="edit"
|
||||||
|
;;
|
||||||
|
-r|--remove)
|
||||||
|
action="remove"
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
args+=("$@")
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
args+=("$1")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
# compatible with previous version
|
||||||
|
if [ "$action" = "none" ]; then
|
||||||
|
case "$level" in
|
||||||
|
local)
|
||||||
|
test ${#args[@]} -ne 0 && add_local "${args[@]}" && echo
|
||||||
|
show_local
|
||||||
|
;;
|
||||||
|
global)
|
||||||
|
test ${#args[@]} -ne 0 && add_global "${args[@]}" && echo
|
||||||
|
show_global
|
||||||
|
;;
|
||||||
|
private)
|
||||||
|
test ${#args[@]} -ne 0 && add_private "${args[@]}" && echo
|
||||||
|
show_private
|
||||||
|
;;
|
||||||
|
none)
|
||||||
|
add_local "${args[@]}"
|
||||||
|
# maybe show_local here too?
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
# open file in editor
|
||||||
|
elif [ "$action" = "edit" ]; then
|
||||||
|
case "$level" in
|
||||||
|
local|none)
|
||||||
|
cd_to_git_root --warn
|
||||||
|
edit_file 'local' .gitignore && echo
|
||||||
|
;;
|
||||||
|
global)
|
||||||
|
global_gitignore="$(global_ignore)"
|
||||||
|
edit_file global "$global_gitignore" && echo
|
||||||
|
;;
|
||||||
|
private)
|
||||||
|
cd_to_git_root --error
|
||||||
|
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
|
||||||
|
edit_file private "${GIT_DIR}/info/exclude" && echo
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
# remove entries
|
||||||
|
else
|
||||||
|
if [ ${#args[@]} -eq 0 ]; then
|
||||||
|
echo "Nothing to remove."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
case "$level" in
|
||||||
|
local|none)
|
||||||
|
cd_to_git_root --warn
|
||||||
|
remove_patterns 'local' .gitignore "${args[@]}" && echo
|
||||||
|
show_local
|
||||||
|
;;
|
||||||
|
global)
|
||||||
|
global_gitignore="$(global_ignore)"
|
||||||
|
remove_patterns global "$global_gitignore" "${args[@]}" && echo
|
||||||
|
show_global
|
||||||
|
;;
|
||||||
|
private)
|
||||||
|
cd_to_git_root --error
|
||||||
|
remove_patterns private "${GIT_DIR}/info/exclude" "${args[@]}" && echo
|
||||||
|
show_private
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
gitignore_io_url="https://www.gitignore.io/api/"
|
gitignore_io_url="https://www.toptal.com/developers/gitignore/api/"
|
||||||
default_path="$HOME/.gi_list"
|
default_path="$HOME/.gi_list"
|
||||||
|
if [[ -n "$XDG_CACHE_HOME" ]]; then
|
||||||
|
default_path="$XDG_CACHE_HOME/git-extras/gi_list"
|
||||||
|
mkdir -p "$XDG_CACHE_HOME/git-extras"
|
||||||
|
fi
|
||||||
|
|
||||||
update_gi_list() {
|
update_gi_list() {
|
||||||
curl -L -s "${gitignore_io_url}/list" > ~/.gi_list
|
curl -L -s "${gitignore_io_url}/list" > "$default_path"
|
||||||
}
|
}
|
||||||
|
|
||||||
print_in_alphabetical_order() {
|
print_in_alphabetical_order() {
|
||||||
|
|
@ -13,7 +17,7 @@ print_in_alphabetical_order() {
|
||||||
for ignorable in $(echo "$gi_list" | sort);
|
for ignorable in $(echo "$gi_list" | sort);
|
||||||
do
|
do
|
||||||
first_character=${ignorable:0:1}
|
first_character=${ignorable:0:1}
|
||||||
if [[ $first_character = $previous_first_character ]]; then
|
if [[ $first_character = "$previous_first_character" ]]; then
|
||||||
printf " %s" "$ignorable"
|
printf " %s" "$ignorable"
|
||||||
elif [[ $first = true ]]; then
|
elif [[ $first = true ]]; then
|
||||||
previous_first_character=$first_character
|
previous_first_character=$first_character
|
||||||
|
|
@ -42,12 +46,11 @@ search() {
|
||||||
}
|
}
|
||||||
|
|
||||||
print_last_modified_time() {
|
print_last_modified_time() {
|
||||||
gi_list_date=$(stat -c "%y" "$default_path" 2> /dev/null)
|
if ! gi_list_date=$(stat -c "%y" "$default_path" 2> /dev/null); then
|
||||||
if [ "$?" -ne 0 ]; then
|
if ! gi_list_date=$(stat -f "%t%Sm" "$default_path" 2> /dev/null); then
|
||||||
gi_list_date=$(stat -f "%t%Sm" "$default_path" 2> /dev/null)
|
if ! gi_list_date=$(date -r "$default_path" +%s 2> /dev/null); then
|
||||||
if [ "$?" -ne 0 ]; then
|
gi_list_date=0
|
||||||
gi_list_date=$(date -r "$default_path" +%s 2> /dev/null)
|
fi
|
||||||
[ "$?" -ne 0 ] && gi_list_date=0
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo "Last update time: $gi_list_date"
|
echo "Last update time: $gi_list_date"
|
||||||
|
|
@ -73,15 +76,15 @@ show_usage() {
|
||||||
echo " [-l|--list-in-table] Print available types in table format"
|
echo " [-l|--list-in-table] Print available types in table format"
|
||||||
echo " [-L|--list-alphabetically] Print available types in alphabetical order "
|
echo " [-L|--list-alphabetically] Print available types in alphabetical order "
|
||||||
echo " [-s|--search] <word> Search word in available types"
|
echo " [-s|--search] <word> Search word in available types"
|
||||||
echo " [-t|--show-update-time] Show the last modified time of ~/.gi_list (where the list of available types is stored)"
|
echo " [-t|--show-update-time] Show the last modified time of $default_path (where the list of available types is stored)"
|
||||||
echo " [-u|--update-list] Update ~/.gi_list"
|
echo " [-u|--update-list] Update $default_path"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
check_list_exist() {
|
check_list_exist() {
|
||||||
if ! [ -f "$default_path" ]; then
|
if ! [ -f "$default_path" ]; then
|
||||||
echo "-----Initial gitignore.io list----"
|
echo "-----Initial gitignore.io list----"
|
||||||
update_gi_list &
|
update_gi_list
|
||||||
echo "-----Save to $default_path-----"
|
echo "-----Save to $default_path-----"
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
60
bin/git-info
60
bin/git-info
|
|
@ -1,11 +1,40 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
GREEN="$(tput setaf 2)"
|
||||||
|
NORMAL="$(tput sgr0)"
|
||||||
|
if [ "$1" = "--color" ] || [ "$2" = "--color" ] || \
|
||||||
|
[ "$1" = "-c" ] || [ "$2" = "-c" ] ; then
|
||||||
|
COLOR_TITLE="$GREEN"
|
||||||
|
else
|
||||||
|
COLOR_TITLE="$NORMAL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
HIDE_CONFIG=
|
||||||
|
if [ "$1" != "--no-config" ] && [ "$2" != "--no-config" ]; then
|
||||||
|
HIDE_CONFIG=1
|
||||||
|
fi
|
||||||
|
|
||||||
get_config() {
|
get_config() {
|
||||||
git config --list
|
cmd_get_config="$(git config --get-all git-extras.info.config-grep)"
|
||||||
|
if [ -z "$cmd_get_config" ]; then
|
||||||
|
git config --list
|
||||||
|
else
|
||||||
|
eval "$cmd_get_config"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
most_recent_commit() {
|
most_recent_commit() {
|
||||||
git log --max-count=1 --pretty=short
|
cmd_get_log="$(git config --get-all git-extras.info.log)"
|
||||||
|
if [ -z "$cmd_get_log" ]; then
|
||||||
|
git log --max-count=1 --pretty=short
|
||||||
|
else
|
||||||
|
eval "$cmd_get_log"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
submodules() {
|
||||||
|
# short sha1
|
||||||
|
git submodule status | sed 's/\([^abcdef0-9]\{0,2\}\)\([abcdef0-9]\{7\}\)\([abcdef0-9]\{33\}\)\(.*\)/\1\2\4/'
|
||||||
}
|
}
|
||||||
|
|
||||||
local_branches() {
|
local_branches() {
|
||||||
|
|
@ -21,27 +50,30 @@ remote_urls() {
|
||||||
}
|
}
|
||||||
|
|
||||||
echon() {
|
echon() {
|
||||||
echo "$@"
|
echo "$@"
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
|
||||||
# Show info similar to svn
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echon "## Remote URLs:"
|
echon "${COLOR_TITLE}## Remote URLs:${NORMAL}"
|
||||||
echon "$(remote_urls)"
|
echon "$(remote_urls)"
|
||||||
|
|
||||||
echon "## Remote Branches:"
|
echon "${COLOR_TITLE}## Remote Branches:${NORMAL}"
|
||||||
echon "$(remote_branches)"
|
echon "$(remote_branches)"
|
||||||
|
|
||||||
echon "## Local Branches:"
|
echon "${COLOR_TITLE}## Local Branches:${NORMAL}"
|
||||||
echon "$(local_branches)"
|
echon "$(local_branches)"
|
||||||
|
|
||||||
echon "## Most Recent Commit:"
|
SUBMODULES_LOG=$(submodules)
|
||||||
echon "$(most_recent_commit)"
|
if [ -n "$SUBMODULES_LOG" ]; then
|
||||||
echon "Type 'git log' for more commits, or 'git show <commit id>' for full commit details."
|
echon "${COLOR_TITLE}## Submodule(s):${NORMAL}"
|
||||||
|
echon "$SUBMODULES_LOG"
|
||||||
|
fi
|
||||||
|
|
||||||
if test "$1" != "--no-config"; then
|
echon "${COLOR_TITLE}## Most Recent Commit:${NORMAL}"
|
||||||
echon "## Configuration (.git/config):"
|
echon "$(most_recent_commit)"
|
||||||
|
|
||||||
|
if [ -n "$HIDE_CONFIG" ]; then
|
||||||
|
echon "${COLOR_TITLE}## Configuration (.git/config):${NORMAL}"
|
||||||
echon "$(get_config)"
|
echon "$(get_config)"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
project=${PWD##*/}
|
|
||||||
|
|
||||||
#
|
|
||||||
# list the last modified author for each line
|
|
||||||
#
|
|
||||||
single_file() {
|
|
||||||
while read data
|
|
||||||
do
|
|
||||||
if [[ $(file "$data") = *text* ]]; then
|
|
||||||
git blame --line-porcelain "$data" 2>/dev/null | grep "^author\ " | LC_ALL=C sed -n 's/^author //p';
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# list the author for all file
|
|
||||||
#
|
|
||||||
lines() {
|
|
||||||
git ls-files | single_file
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# count the line count
|
|
||||||
#
|
|
||||||
count() {
|
|
||||||
lines | wc -l
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# sort by author modified lines
|
|
||||||
#
|
|
||||||
authors() {
|
|
||||||
lines | sort | uniq -c | sort -rn
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# list as percentage for author modified lines
|
|
||||||
#
|
|
||||||
result() {
|
|
||||||
authors | awk '
|
|
||||||
{ args[NR] = $0; sum += $0 }
|
|
||||||
END {
|
|
||||||
for (i = 1; i <= NR; ++i) {
|
|
||||||
printf " %s, %2.1f%%\n", args[i], 100 * args[i] / sum
|
|
||||||
}
|
|
||||||
}
|
|
||||||
' | column -t -s,
|
|
||||||
}
|
|
||||||
|
|
||||||
# summary
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo " project : $project"
|
|
||||||
echo " lines : $(count)"
|
|
||||||
echo " authors :"
|
|
||||||
result
|
|
||||||
echo
|
|
||||||
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
ref=$(git symbolic-ref HEAD)
|
git log "@{upstream}..@" "$@"
|
||||||
branch=${ref#refs/heads/}
|
|
||||||
|
|
||||||
git log origin/${branch}..${branch} $*
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
filename=$1
|
filename="$1"
|
||||||
test -z $filename && echo "filename required." 1>&2 && exit 1
|
test -z "$filename" && echo "filename required." 1>&2 && exit 1
|
||||||
git update-index --skip-worktree $filename
|
git update-index --skip-worktree "$filename"
|
||||||
|
|
|
||||||
87
bin/git-magic
Executable file
87
bin/git-magic
Executable file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
# Bash unofficial strict mode
|
||||||
|
set -eo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
|
USAGE='git-magic [-a] [-m msg] [-e] [-p] [-f]'
|
||||||
|
|
||||||
|
ALL=false
|
||||||
|
PUSH=false
|
||||||
|
FORCE=''
|
||||||
|
ARGS=()
|
||||||
|
|
||||||
|
while getopts "m:eapfh" arg; do
|
||||||
|
case "${arg}" in
|
||||||
|
m)
|
||||||
|
ARGS+=("-m")
|
||||||
|
ARGS+=("${OPTARG}")
|
||||||
|
;;
|
||||||
|
e)
|
||||||
|
ARGS+=("-e")
|
||||||
|
;;
|
||||||
|
a)
|
||||||
|
ALL=true
|
||||||
|
;;
|
||||||
|
p)
|
||||||
|
PUSH=true
|
||||||
|
;;
|
||||||
|
f)
|
||||||
|
FORCE='--force-with-lease'
|
||||||
|
;;
|
||||||
|
h)
|
||||||
|
echo "$USAGE"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
?)
|
||||||
|
echo "${USAGE}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
if [[ $# != 0 ]]; then
|
||||||
|
echo "Unknown arguments: $*"
|
||||||
|
echo "${USAGE}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -- "${ARGS[@]}" # restore positional parameters
|
||||||
|
|
||||||
|
if [[ $ALL == true ]]; then
|
||||||
|
|
||||||
|
# Check if there is no changes to stage
|
||||||
|
if [[ -z $(git status --porcelain) ]]; then
|
||||||
|
echo "No changes to commit"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get confirmation from user
|
||||||
|
git status
|
||||||
|
echo "Everything will be added"
|
||||||
|
read -rp "Press enter to continue"
|
||||||
|
|
||||||
|
# Restore staging area so that, for example,
|
||||||
|
# add and modify will not be separate entries in status
|
||||||
|
git restore --staged . || true
|
||||||
|
git add .
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Commit with generated message
|
||||||
|
git commit --no-edit "$@" -m "$(git status --porcelain -uno)"
|
||||||
|
|
||||||
|
if [[ $PUSH == true ]]; then
|
||||||
|
git push $FORCE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --no-edit by default. use option -e to override this
|
||||||
|
|
||||||
|
# Arguments are passed with quoted "$@" to avoid misparsing
|
||||||
|
|
||||||
|
# Generated message comes after user provided arguments
|
||||||
|
# so that user can insert title before it
|
||||||
|
|
@ -9,7 +9,16 @@ usage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_branch=$(current_branch)
|
cur_branch=$(current_branch)
|
||||||
if [ "${!#}" == '--ff-only' ]; then
|
|
||||||
|
stashed=0
|
||||||
|
if [ -n "$(git status --porcelain)" ];
|
||||||
|
then
|
||||||
|
echo "Local modifications detected, stashing"
|
||||||
|
stashed=1
|
||||||
|
git stash
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${!#}" = '--ff-only' ]; then
|
||||||
case $# in
|
case $# in
|
||||||
2 ) # dest --ff
|
2 ) # dest --ff
|
||||||
git push "$(git rev-parse --show-toplevel)" "$cur_branch":"$1";;
|
git push "$(git rev-parse --show-toplevel)" "$cur_branch":"$1";;
|
||||||
|
|
@ -32,3 +41,8 @@ else
|
||||||
usage
|
usage
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ $stashed -eq 1 ];
|
||||||
|
then
|
||||||
|
git stash pop;
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ branch=$2
|
||||||
prefix=$3
|
prefix=$3
|
||||||
flat=0
|
flat=0
|
||||||
|
|
||||||
if test $prefix = "."; then
|
if test "$prefix" = "."; then
|
||||||
prefix=$(mktemp -u 'git-merge-repo.XXXXXXX')
|
prefix=$(mktemp -u 'git-merge-repo.XXXXXXX')
|
||||||
flat=1
|
flat=1
|
||||||
fi
|
fi
|
||||||
|
|
@ -16,10 +16,10 @@ message=$(git log -1 --pretty=%B)
|
||||||
|
|
||||||
if test $flat -eq 1; then
|
if test $flat -eq 1; then
|
||||||
git stash -u
|
git stash -u
|
||||||
mv -i $prefix/* ./
|
mv -i "$prefix"/* ./
|
||||||
git undo
|
git undo
|
||||||
git add .
|
git add .
|
||||||
git commit -am "$message"
|
git commit -am "$message"
|
||||||
git stash apply
|
git stash apply
|
||||||
rm -drf $prefix
|
rm -drf "$prefix"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo 1>&2 "usage: git missing [<first branch>] <second branch> [<git log options>]"
|
echo 1>&2 "usage: git missing [<first branch>] <second branch> [<git log options>] [[--] <path>...]"
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ "${#}" -lt 1 ]
|
if [ "${#}" -lt 1 ]
|
||||||
|
|
@ -12,9 +12,20 @@ fi
|
||||||
|
|
||||||
declare -a git_log_args=()
|
declare -a git_log_args=()
|
||||||
declare -a branches=()
|
declare -a branches=()
|
||||||
|
declare -a pathspec=()
|
||||||
|
declare parse_path=false
|
||||||
|
|
||||||
for arg in "$@" ; do
|
for arg in "$@" ; do
|
||||||
|
|
||||||
|
if [[ $parse_path == true ]]; then
|
||||||
|
pathspec+=("$@")
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
|
--)
|
||||||
|
parse_path=true
|
||||||
|
;;
|
||||||
--*)
|
--*)
|
||||||
git_log_args+=( "$arg" )
|
git_log_args+=( "$arg" )
|
||||||
;;
|
;;
|
||||||
|
|
@ -38,4 +49,4 @@ else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git log "${git_log_args[@]}" "$firstbranch"..."$secondbranch" --format="%m %h %s" --left-right
|
git log "${git_log_args[@]}" "$firstbranch"..."$secondbranch" --format="%m %h %s" --left-right -- "${pathspec[@]}"
|
||||||
|
|
|
||||||
22
bin/git-mr
22
bin/git-mr
|
|
@ -7,22 +7,30 @@ if [ -z "${1-}" ] ; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
remote_ref_kind=merge-requests
|
||||||
|
|
||||||
if test "$1" = "clean"; then
|
if test "$1" = "clean"; then
|
||||||
git for-each-ref refs/heads/mr/* --format='%(refname)' | while read ref; do
|
git for-each-ref refs/heads/mr/* --format='%(refname)' | while read -r ref; do
|
||||||
git branch -D ${ref#refs/heads/}
|
git branch -D "${ref#refs/heads/}"
|
||||||
done
|
done
|
||||||
exit 0
|
exit 0
|
||||||
elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then
|
elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then
|
||||||
remote=${BASH_REMATCH[1]}.git
|
remote=${BASH_REMATCH[1]}.git
|
||||||
id=${BASH_REMATCH[3]}
|
id=${BASH_REMATCH[3]}
|
||||||
|
elif [[ $1 =~ ^(https?://[^/]+/(.+))/pulls/([0-9]+).*$ ]]; then
|
||||||
|
# Forgejo/Codeberg pull request URL, e.g.
|
||||||
|
# https://codeberg.org/owner/repository/pulls/453
|
||||||
|
remote=${BASH_REMATCH[1]}.git
|
||||||
|
id=${BASH_REMATCH[3]}
|
||||||
|
remote_ref_kind=pull
|
||||||
else
|
else
|
||||||
id=$1
|
id=$1
|
||||||
remote=${2:-origin}
|
remote=${2:-origin}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
branch=mr/$id
|
branch=mr/$id
|
||||||
remote_ref=refs/merge-requests/$id/head
|
remote_ref=refs/$remote_ref_kind/$id/head
|
||||||
git fetch -fu $remote $remote_ref:$branch
|
git fetch -fu "$remote" "$remote_ref:$branch"
|
||||||
git checkout $branch
|
git checkout "$branch"
|
||||||
git config --local --replace branch.$branch.merge $remote_ref
|
git config --local --replace "branch.$branch.merge" "$remote_ref"
|
||||||
git config --local --replace branch.$branch.remote $remote
|
git config --local --replace "branch.$branch.remote" "$remote"
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,15 @@ do
|
||||||
file="$file"' '"$i"
|
file="$file"' '"$i"
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
test -n "$*" && range="$*"
|
test -n "$*" && range=("$@")
|
||||||
|
|
||||||
test -z "$file" && echo "file required." 1>&2 && exit 1
|
test -z "$file" && echo "file required." 1>&2 && exit 1
|
||||||
if [ -z "$range" ]
|
if [ -z "${range[*]}" ]
|
||||||
then
|
then
|
||||||
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
|
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
|
||||||
--prune-empty --tag-name-filter cat -- --all
|
--prune-empty --tag-name-filter cat -- --all
|
||||||
else
|
else
|
||||||
# don't quote $range so that we can forward multiple rev-list arguments
|
# $range is an array so that we can forward multiple rev-list arguments
|
||||||
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
|
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
|
||||||
--prune-empty --tag-name-filter cat -- $range
|
--prune-empty --tag-name-filter cat -- "${range[@]}"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
11
bin/git-paste
Executable file
11
bin/git-paste
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
if ! command -v pastebinit &>/dev/null; then
|
||||||
|
echo >&2 "To run 'git paste', you need to install pastebinit in your system"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
test $# -ne 0 || set -- '@{u}'
|
||||||
|
git format-patch --stdout "$@" | pastebinit -f diff
|
||||||
83
bin/git-pr
83
bin/git-pr
|
|
@ -1,22 +1,77 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Based on https://gist.github.com/gnarf/5406589 and https://gist.github.com/jhnns/d654d9d6da6d3b749986
|
# Based on https://gist.github.com/gnarf/5406589 and https://gist.github.com/jhnns/d654d9d6da6d3b749986
|
||||||
|
TO_MERGE=
|
||||||
|
|
||||||
|
pull() {
|
||||||
|
local remote="$1"
|
||||||
|
local id="$2"
|
||||||
|
local branch="$3"
|
||||||
|
local ref="refs/pull/$id/head"
|
||||||
|
|
||||||
|
if [ -n "$TO_MERGE" ]; then
|
||||||
|
ref="refs/pull/$id/merge"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git fetch -fu "$remote" "$ref:$branch" && \
|
||||||
|
git checkout "$branch" && \
|
||||||
|
git config --local --replace "branch.$branch.merge" "$ref" && \
|
||||||
|
git config --local --replace "branch.$branch.remote" "$remote"
|
||||||
|
}
|
||||||
|
|
||||||
|
pull_pr_if_matched() {
|
||||||
|
if [[ $1 =~ ^(.*):([0-9]+)|(https?://[^/]+/.+)/pull/([0-9]+).*$ ]]; then
|
||||||
|
if [[ -n ${BASH_REMATCH[2]} ]]; then
|
||||||
|
remote="${BASH_REMATCH[1]:-origin}"
|
||||||
|
id="${BASH_REMATCH[2]}"
|
||||||
|
else
|
||||||
|
remote="${BASH_REMATCH[3]}.git"
|
||||||
|
id="${BASH_REMATCH[4]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
branch=pr/$id
|
||||||
|
pull "$remote" "$id" "$branch"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$1 doesn't match the pr id pattern."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
-m|--merge)
|
||||||
|
TO_MERGE=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# set the argument back
|
||||||
|
set -- "$@" "$arg"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
test -z "$1" && echo "pr number required." 1>&2 && exit 1
|
||||||
|
|
||||||
if test "$1" = "clean"; then
|
if test "$1" = "clean"; then
|
||||||
git for-each-ref refs/heads/pr/* --format='%(refname)' | while read ref; do
|
git for-each-ref refs/heads/pr/* --format='%(refname)' | while read -r ref; do
|
||||||
git branch -D ${ref#refs/heads/}
|
git branch -D "${ref#refs/heads/}"
|
||||||
done
|
done
|
||||||
exit 0
|
|
||||||
elif [[ $1 =~ ^(https?://[^/]+/(.+))/pull/([0-9]+).*$ ]]; then
|
elif [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||||
remote=${BASH_REMATCH[1]}.git
|
remote_pref=${2:-$(git config --get git-extras.pr.remote)}
|
||||||
id=${BASH_REMATCH[3]}
|
remote=${remote_pref:-origin}
|
||||||
branch=pr/$id
|
|
||||||
else
|
|
||||||
test -z $1 && echo "pr number required." 1>&2 && exit 1
|
|
||||||
remote=${2:-origin}
|
|
||||||
id=$1
|
id=$1
|
||||||
branch=pr/$id
|
branch=pr/$id
|
||||||
|
pull "$remote" "$id" "$branch"
|
||||||
|
|
||||||
|
else
|
||||||
|
rc=1
|
||||||
|
while [ "$1" != "" ]; do
|
||||||
|
pull_pr_if_matched "$1"
|
||||||
|
rc=$?
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
exit "$rc"
|
||||||
fi
|
fi
|
||||||
git fetch -fu $remote refs/pull/$id/head:$branch && \
|
|
||||||
git checkout $branch && \
|
|
||||||
git config --local --replace branch.$branch.merge refs/pull/$id/head && \
|
|
||||||
git config --local --replace branch.$branch.remote $remote;
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ SECONDARY_BRANCH=""
|
||||||
FF="--ff"
|
FF="--ff"
|
||||||
CONTINUE="no"
|
CONTINUE="no"
|
||||||
|
|
||||||
function current_branch() {
|
current_branch() {
|
||||||
git rev-parse --abbrev-ref HEAD
|
git rev-parse --abbrev-ref HEAD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ function usage()
|
||||||
echo " -c|--continue: Continue after the user updates conflicts."
|
echo " -c|--continue: Continue after the user updates conflicts."
|
||||||
}
|
}
|
||||||
|
|
||||||
while [[ $# > 0 ]]
|
while [[ $# -gt 0 ]]
|
||||||
do
|
do
|
||||||
key="$1"
|
key="$1"
|
||||||
|
|
||||||
|
|
@ -50,19 +50,17 @@ fi
|
||||||
|
|
||||||
if [[ "$CONTINUE" == "yes" ]]; then
|
if [[ "$CONTINUE" == "yes" ]]; then
|
||||||
TARGET_BRANCH=$(current_branch)
|
TARGET_BRANCH=$(current_branch)
|
||||||
set $(echo "$TARGET_BRANCH" | sed -e "s/-rebased-on-top-of-/\n/g")
|
SECONDARY_BRANCH=${TARGET_BRANCH%"-rebased-on-top-of-"*}
|
||||||
if [[ $# != 2 ]]; then
|
PRIMARY_BRANCH=${TARGET_BRANCH#*"-rebased-on-top-of-"}
|
||||||
|
if [[ "${SECONDARY_BRANCH}-rebased-on-top-of-${PRIMARY_BRANCH}" != $TARGET_BRANCH ]]; then
|
||||||
echo "Couldn't continue rebasing on ${TARGET_BRANCH}"
|
echo "Couldn't continue rebasing on ${TARGET_BRANCH}"
|
||||||
exit 30 # Impossible to detect PRIMARY_BRANCH AND SECONDARY_BRANCH
|
exit 30 # Impossible to detect PRIMARY_BRANCH AND SECONDARY_BRANCH
|
||||||
fi
|
fi
|
||||||
|
|
||||||
SECONDARY_BRANCH=$1
|
|
||||||
PRIMARY_BRANCH=$2
|
|
||||||
|
|
||||||
echo "Continuing rebasing of $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
|
echo "Continuing rebasing of $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
|
||||||
git commit || exit 51
|
git commit || exit 51
|
||||||
git branch -d ${SECONDARY_BRANCH} || exit 52
|
git branch -d "${SECONDARY_BRANCH}" || exit 52
|
||||||
git branch -m ${TARGET_BRANCH} ${SECONDARY_BRANCH} || exit 53
|
git branch -m "${TARGET_BRANCH}" "${SECONDARY_BRANCH}" || exit 53
|
||||||
|
|
||||||
elif [[ "$PRIMARY_BRANCH" == "" ]]; then
|
elif [[ "$PRIMARY_BRANCH" == "" ]]; then
|
||||||
usage
|
usage
|
||||||
|
|
@ -71,14 +69,13 @@ else
|
||||||
echo "Rebasing $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
|
echo "Rebasing $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
|
||||||
TARGET_BRANCH="${SECONDARY_BRANCH}-rebased-on-top-of-${PRIMARY_BRANCH}"
|
TARGET_BRANCH="${SECONDARY_BRANCH}-rebased-on-top-of-${PRIMARY_BRANCH}"
|
||||||
|
|
||||||
git checkout ${PRIMARY_BRANCH} || exit 41
|
git checkout "${PRIMARY_BRANCH}" || exit 41
|
||||||
git checkout -b ${TARGET_BRANCH} || exit 42
|
git checkout -b "${TARGET_BRANCH}" || exit 42
|
||||||
git merge ${SECONDARY_BRANCH} ${FF} \
|
|
||||||
-m "Psycho-rebased branch ${SECONDARY_BRANCH} on top of ${PRIMARY_BRANCH}"
|
|
||||||
|
|
||||||
if [[ $? == 0 ]]; then
|
if git merge "${SECONDARY_BRANCH}" ${FF} \
|
||||||
git branch -d ${SECONDARY_BRANCH} || exit 43
|
-m "Psycho-rebased branch ${SECONDARY_BRANCH} on top of ${PRIMARY_BRANCH}"; then
|
||||||
git branch -m ${TARGET_BRANCH} ${SECONDARY_BRANCH} || exit 44
|
git branch -d "${SECONDARY_BRANCH}" || exit 43
|
||||||
|
git branch -m "${TARGET_BRANCH}" "${SECONDARY_BRANCH}" || exit 44
|
||||||
else
|
else
|
||||||
echo "Resolve the conflict and run ``${PROGRAM} --continue``."
|
echo "Resolve the conflict and run ``${PROGRAM} --continue``."
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
|
|
@ -28,15 +28,12 @@ json() {
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
# user
|
# personal access token
|
||||||
|
# config name is github-personal-access-token '_' is not allowed in git config
|
||||||
|
|
||||||
user=$(git config user.email)
|
github_personal_access_token=$(git config --default "$GITHUB_TOKEN" git-extras.github-personal-access-token)
|
||||||
|
|
||||||
if [ -z "$user" ]; then
|
test -z "$github_personal_access_token" && abort "GITHUB_TOKEN or git config git-extras.github-personal-access-token required"
|
||||||
user="$EMAIL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
test -z "$user" && abort "git config user.email required"
|
|
||||||
|
|
||||||
# branch
|
# branch
|
||||||
|
|
||||||
|
|
@ -46,7 +43,7 @@ if [ -z "$remote" ]; then
|
||||||
echo 'no upstream found, push to origin as default'
|
echo 'no upstream found, push to origin as default'
|
||||||
remote="origin"
|
remote="origin"
|
||||||
fi
|
fi
|
||||||
[ "$remote" == "." ] && abort "the upstream should be a remote branch."
|
[ "$remote" = "." ] && abort "the upstream should be a remote branch."
|
||||||
|
|
||||||
# make sure it's pushed
|
# make sure it's pushed
|
||||||
|
|
||||||
|
|
@ -67,7 +64,7 @@ echo " create pull-request for $project '$branch'"
|
||||||
echo
|
echo
|
||||||
printf " title: " && read -r title
|
printf " title: " && read -r title
|
||||||
printf " body: " && read -r body
|
printf " body: " && read -r body
|
||||||
printf " base [master]: " && read -r base
|
printf " base [%s]: " "$(git_extra_default_branch)" && read -r base
|
||||||
printf " GitHub two-factor authentication code (leave blank if not set up): " && read -r mfa_code
|
printf " GitHub two-factor authentication code (leave blank if not set up): " && read -r mfa_code
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
|
@ -75,12 +72,14 @@ echo
|
||||||
|
|
||||||
if [ -z "$base" ]
|
if [ -z "$base" ]
|
||||||
then
|
then
|
||||||
base="master"
|
base="$(git_extra_default_branch)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
body=$(json "$title" "$body" "$branch" "$base")
|
body=$(json "$title" "$body" "$branch" "$base")
|
||||||
|
|
||||||
curl -u "$user" \
|
curl \
|
||||||
-H "X-GitHub-OTP: $mfa_code" \
|
-X POST \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
-H "Authorization: token $github_personal_access_token" \
|
||||||
|
-H "X-GitHub-OTP: $mfa_code" \
|
||||||
"https://api.github.com/repos/$project/pulls" -d "$body"
|
"https://api.github.com/repos/$project/pulls" -d "$body"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,26 +17,26 @@ fi
|
||||||
# Use a temporary index.
|
# Use a temporary index.
|
||||||
index=$(git_extra_mktemp)
|
index=$(git_extra_mktemp)
|
||||||
cleanup() {
|
cleanup() {
|
||||||
rm $index
|
rm "$index"
|
||||||
exit 2
|
exit 2
|
||||||
}
|
}
|
||||||
trap cleanup 2
|
trap cleanup INT
|
||||||
|
|
||||||
# Go back in history while parent commits are available.
|
# Go back in history while parent commits are available.
|
||||||
echo "Trying to find a commit the patch applies to..."
|
echo "Trying to find a commit the patch applies to..."
|
||||||
rev=$(git rev-parse HEAD)
|
rev=$(git rev-parse HEAD)
|
||||||
while [ $? = 0 ]
|
while [ $? = 0 ]
|
||||||
do
|
do
|
||||||
GIT_INDEX_FILE=$index git read-tree $rev
|
GIT_INDEX_FILE=$index git read-tree "$rev"
|
||||||
|
|
||||||
# Try to apply the patch.
|
# Try to apply the patch.
|
||||||
GIT_INDEX_FILE=$index git apply --cached $1 >/dev/null 2>&1
|
GIT_INDEX_FILE=$index git apply --cached "$1" &>/dev/null
|
||||||
patch_failed=$?
|
patch_failed=$?
|
||||||
|
|
||||||
# Do it again, but show the error, if the problem is the patch itself.
|
# Do it again, but show the error, if the problem is the patch itself.
|
||||||
if [ $patch_failed = 128 ]
|
if [ $patch_failed = 128 ]
|
||||||
then
|
then
|
||||||
GIT_INDEX_FILE=$index git apply --index --check $1
|
GIT_INDEX_FILE=$index git apply --index --check "$1"
|
||||||
exit $patch_failed
|
exit $patch_failed
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -45,19 +45,19 @@ do
|
||||||
then
|
then
|
||||||
# Manufacture a commit.
|
# Manufacture a commit.
|
||||||
tree=$(GIT_INDEX_FILE=$index git write-tree)
|
tree=$(GIT_INDEX_FILE=$index git write-tree)
|
||||||
commit=$(git commit-tree $tree -p $rev -m "$1")
|
commit=$(git commit-tree "$tree" -p "$rev" -m "$1")
|
||||||
rm $index
|
rm "$index"
|
||||||
|
|
||||||
echo "Patch applied to $(git rev-parse --short $rev) as $(git rev-parse --short $commit)"
|
echo "Patch applied to $(git rev-parse --short "$rev") as $(git rev-parse --short "$commit")"
|
||||||
|
|
||||||
git cherry-pick $commit
|
git cherry-pick "$commit"
|
||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rev=$(git rev-parse --verify -q $rev^)
|
rev=$(git rev-parse --verify -q "$rev^")
|
||||||
done
|
done
|
||||||
|
|
||||||
# No compatible commit found. Restore.
|
# No compatible commit found. Restore.
|
||||||
echo "Failed to find a commit the patch applies to."
|
echo "Failed to find a commit the patch applies to."
|
||||||
rm $index
|
rm "$index"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
ALIAS=refactor
|
|
||||||
git feature -a $ALIAS "$@"
|
|
||||||
|
|
@ -4,17 +4,17 @@ set -e
|
||||||
hook() {
|
hook() {
|
||||||
local hook=.git/hooks/$1.sh
|
local hook=.git/hooks/$1.sh
|
||||||
# compat without extname
|
# compat without extname
|
||||||
if test ! -f $hook; then
|
if test ! -f "$hook"; then
|
||||||
hook=.git/hooks/$1
|
hook=.git/hooks/$1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -f $hook; then
|
if test -f "$hook"; then
|
||||||
echo "... $1"
|
echo "... $1"
|
||||||
shift
|
shift
|
||||||
if test -x $hook; then
|
if test -x "$hook"; then
|
||||||
$hook "$@"
|
$hook "$@"
|
||||||
else
|
else
|
||||||
. $hook "$@"
|
. "$hook" "$@"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
@ -34,12 +34,29 @@ if test $# -gt 0; then
|
||||||
-c) need_changelog=true;;
|
-c) need_changelog=true;;
|
||||||
-r) remote=$2; shift ;;
|
-r) remote=$2; shift ;;
|
||||||
-m) msg=$2; shift ;;
|
-m) msg=$2; shift ;;
|
||||||
|
-s)
|
||||||
|
test -n "$keyid" &&
|
||||||
|
exit_with_msg "Please use '-s' OR '-u'"
|
||||||
|
sign=true
|
||||||
|
;;
|
||||||
|
-u)
|
||||||
|
test -n "$sign" &&
|
||||||
|
exit_with_msg "Please use '-s' OR '-u'"
|
||||||
|
keyid=$2
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--semver)
|
--semver)
|
||||||
test -z "$2" &&
|
test -z "$2" &&
|
||||||
exit_with_msg "major/minor/patch required for --semver option"
|
exit_with_msg "major/minor/patch required for --semver option"
|
||||||
semver=$2
|
semver=$2
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--prefix)
|
||||||
|
test -z "$2" &&
|
||||||
|
exit_with_msg "prefix string required for --prefix option"
|
||||||
|
prefix="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--no-empty-commit) no_empty_commit=true;;
|
--no-empty-commit) no_empty_commit=true;;
|
||||||
--) shift; hook_args="$hook_args $*"; break;;
|
--) shift; hook_args="$hook_args $*"; break;;
|
||||||
*) test -z "$version" && version=$1 ;;
|
*) test -z "$version" && version=$1 ;;
|
||||||
|
|
@ -58,7 +75,7 @@ if test $# -gt 0; then
|
||||||
latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)")
|
latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)")
|
||||||
|
|
||||||
if [[ ! "$latest_tag" =~ \
|
if [[ ! "$latest_tag" =~ \
|
||||||
^([^0-9]*)([0-9]|[1-9][0-9]+)\.([0-9]|[1-9][0-9]+)\.([0-9]|[1-9][0-9]+)(.*) ]]; then
|
^$prefix([^0-9]*)([1-9][0-9]+|[0-9])\.([1-9][0-9]+|[0-9])\.([1-9][0-9]+|[0-9])(.*) ]]; then
|
||||||
echo "the latest tag doesn't match semver format requirement" 1>&2
|
echo "the latest tag doesn't match semver format requirement" 1>&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -75,9 +92,9 @@ if test $# -gt 0; then
|
||||||
(( ++version ))
|
(( ++version ))
|
||||||
|
|
||||||
case "$semver" in
|
case "$semver" in
|
||||||
major ) version="${BASH_REMATCH[1]}$version.${BASH_REMATCH[3]}.${BASH_REMATCH[4]}${BASH_REMATCH[5]}" ;;
|
major ) version="$prefix${BASH_REMATCH[1]}$version.0.0${BASH_REMATCH[5]}" ;;
|
||||||
minor ) version="${BASH_REMATCH[1]}${BASH_REMATCH[2]}.$version.${BASH_REMATCH[4]}${BASH_REMATCH[5]}" ;;
|
minor ) version="$prefix${BASH_REMATCH[1]}${BASH_REMATCH[2]}.$version.0${BASH_REMATCH[5]}" ;;
|
||||||
patch ) version="${BASH_REMATCH[1]}${BASH_REMATCH[2]}.${BASH_REMATCH[3]}.$version${BASH_REMATCH[5]}" ;;
|
patch ) version="$prefix${BASH_REMATCH[1]}${BASH_REMATCH[2]}.${BASH_REMATCH[3]}.$version${BASH_REMATCH[5]}" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -101,8 +118,17 @@ if test $# -gt 0; then
|
||||||
git commit -a -m "$msg" --allow-empty
|
git commit -a -m "$msg" --allow-empty
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
declare -a sign_args
|
||||||
|
if [ "$sign" = true ]; then
|
||||||
|
sign_args=("-s")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$keyid" ]; then
|
||||||
|
sign_args=("-u" "$keyid")
|
||||||
|
fi
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
git tag $version -a -m "$msg" \
|
git tag "${sign_args[@]}" $version -a -m "$msg" \
|
||||||
&& git push $remote --tags \
|
&& git push $remote --tags \
|
||||||
&& git push $remote \
|
&& git push $remote \
|
||||||
&& hook post-release $hook_args \
|
&& hook post-release $hook_args \
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,17 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Assert there is at least one branch provided
|
# Assert there is at least one branch provided
|
||||||
test -z $1 && echo "new branch name required." 1>&2 && exit 1
|
test -z "$1" && echo "new branch name required." 1>&2 && exit 1
|
||||||
|
|
||||||
new_branch="$1"
|
if [ -z "$2" ]; then
|
||||||
old_branch="${2-$(git symbolic-ref --short -q HEAD)}"
|
new_branch="$1"
|
||||||
remote=$(git config branch."$old_branch".remote)
|
old_branch="$(git symbolic-ref --short -q HEAD)"
|
||||||
|
else
|
||||||
|
new_branch="$2"
|
||||||
|
old_branch="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
remote=$(git config branch."$old_branch".remote; true)
|
||||||
|
|
||||||
git branch -m "$old_branch" "$new_branch"
|
git branch -m "$old_branch" "$new_branch"
|
||||||
# check if the branch is tracking a remote branch
|
# check if the branch is tracking a remote branch
|
||||||
|
|
|
||||||
101
bin/git-rename-file
Executable file
101
bin/git-rename-file
Executable file
|
|
@ -0,0 +1,101 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Usage function
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: git rename-file [OPTIONS] <source> <destination>
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Rename a file or directory and ensure Git recognizes the change, regardless of filesystem case-sensitivity.
|
||||||
|
It combines the functionality of the "mv" command and "git mv". This is particularly useful for renaming files or directories
|
||||||
|
to change only their case, which might not be detected by Git on case-insensitive filesystems.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Show this help message and exit.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
git rename-file old_filename new_filename
|
||||||
|
git rename-file old_directory new_directory
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for help option
|
||||||
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for correct number of arguments
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Error: Incorrect number of arguments."
|
||||||
|
echo ""
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Assign variables
|
||||||
|
SOURCE="$1"
|
||||||
|
DESTINATION="$2"
|
||||||
|
TEMP_NAME="${SOURCE}.temp"
|
||||||
|
|
||||||
|
# Function to check if a file or directory exists in a case-sensitive manner
|
||||||
|
check_case_sensitive_exists() {
|
||||||
|
local path="$1"
|
||||||
|
local dir
|
||||||
|
local base
|
||||||
|
|
||||||
|
dir=$(dirname "$path")
|
||||||
|
base=$(basename "$path")
|
||||||
|
|
||||||
|
if [ -e "$dir" ]; then
|
||||||
|
if (cd "$dir" && find . -maxdepth 1 -name "$base" | grep -q "$base"); then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if source exists and is under version control
|
||||||
|
if ! check_case_sensitive_exists "$SOURCE"; then
|
||||||
|
echo "Error: Source '$SOURCE' does not exist."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git ls-files --error-unmatch "$SOURCE" > /dev/null 2>&1; then
|
||||||
|
echo "Error: Source '$SOURCE' is not under version control. If file or directory is new, it must at least be staged."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if destination already exists
|
||||||
|
if check_case_sensitive_exists "$DESTINATION"; then
|
||||||
|
echo "Error: Destination '$DESTINATION' already exists."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the destination directory exists
|
||||||
|
DEST_DIR=$(dirname "$DESTINATION")
|
||||||
|
if ! check_case_sensitive_exists "$DEST_DIR"; then
|
||||||
|
echo "Error: Destination directory '$DEST_DIR' does not exist."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a rollback function
|
||||||
|
rollback() {
|
||||||
|
echo "Rolling back changes..."
|
||||||
|
if [ -e "$TEMP_NAME" ]; then
|
||||||
|
git mv -f "$TEMP_NAME" "$SOURCE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Trap errors to trigger rollback
|
||||||
|
trap 'rollback' ERR
|
||||||
|
|
||||||
|
# Move the file to a temporary name within the Git repository
|
||||||
|
git mv "$SOURCE" "$TEMP_NAME"
|
||||||
|
|
||||||
|
# Move the temporary file to the desired destination
|
||||||
|
git mv "$TEMP_NAME" "$DESTINATION"
|
||||||
|
|
||||||
|
echo "Successfully renamed '$SOURCE' to '$DESTINATION'."
|
||||||
20
bin/git-rename-remote
Executable file
20
bin/git-rename-remote
Executable file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
old=$1
|
||||||
|
new=$2
|
||||||
|
|
||||||
|
test -z "$old" && echo "old remote name required." 1>&2 && exit 1
|
||||||
|
test -z "$new" && echo "new remote name required." 1>&2 && exit 1
|
||||||
|
|
||||||
|
if ! git config --get "remote.$old.fetch" > /dev/null; then
|
||||||
|
echo "remote $old doesn't exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git config --get "remote.$new.fetch" > /dev/null; then
|
||||||
|
git remote remove "$new"
|
||||||
|
fi
|
||||||
|
git remote rename "$old $new"
|
||||||
|
git remote -v
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
old=$1
|
old=$1
|
||||||
new=$2
|
new=$2
|
||||||
|
|
||||||
test -z $old && echo "old tag name required." 1>&2 && exit 1
|
test -z "$old" && echo "old tag name required." 1>&2 && exit 1
|
||||||
test -z $new && echo "new tag name required." 1>&2 && exit 1
|
test -z "$new" && echo "new tag name required." 1>&2 && exit 1
|
||||||
|
|
||||||
git tag $new $old
|
git tag "$new" "$old"
|
||||||
git tag -d $old
|
git tag -d "$old"
|
||||||
git push origin $new
|
git push origin "$new"
|
||||||
git push origin :refs/tags/$old
|
git push origin ":refs/tags/$old"
|
||||||
|
|
|
||||||
80
bin/git-repl
80
bin/git-repl
|
|
@ -1,43 +1,99 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
shopt -s extglob
|
||||||
|
|
||||||
git version
|
git version
|
||||||
echo "git-extras version ""$(git-extras -v)"
|
echo "git-extras version ""$(git-extras -v)"
|
||||||
echo "type 'ls' to ls files below current directory, '!command' to execute any command or just 'subcommand' to execute any git subcommand"
|
echo "Type 'ls' to ls files below current directory; '!command' to execute any command or just 'subcommand' to execute any git subcommand; 'quit', 'exit', 'q', ^D, or ^C to exit the git repl."
|
||||||
|
|
||||||
|
HISTIGNORE=${HISTIGNORE:-+([[:space:]])}
|
||||||
|
HISTCONTROL=${HISTCONTROL:-ignoredups}
|
||||||
|
use_local_history=$(git config --get --default 'false' git-extras.repl.use-local-history)
|
||||||
|
if [[ "$use_local_history" == "true" ]]; then
|
||||||
|
HISTFILE="$(git rev-parse --show-toplevel)/.git_extras_repl_history"
|
||||||
|
else
|
||||||
|
HISTFILE=${XDG_STATE_HOME:-$HOME/.local/state}/git_extras_repl_history
|
||||||
|
fi
|
||||||
|
|
||||||
|
# file doesn't exist, is empty, or contains only whitespace
|
||||||
|
if [[ ! -f "$HISTFILE" ]] || [[ ! -s "$HISTFILE" ]] || ! grep -q '[^[:space:]]' "$HISTFILE"; then
|
||||||
|
# `history -r` .... `history -a` are not happy with an empty initial file in bash 3.2.57, which is what MacOS 26 ships with
|
||||||
|
echo '!echo welcome to git-repl!' >> "$HISTFILE"
|
||||||
|
fi
|
||||||
|
history -r
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
# Current branch
|
# Current branch
|
||||||
cur=`git symbolic-ref HEAD 2> /dev/null | cut -d/ -f3-`
|
cur=$(git symbolic-ref HEAD 2> /dev/null | cut -d/ -f3-)
|
||||||
|
|
||||||
# Prompt
|
# Prompt
|
||||||
if test -n "$cur"; then
|
if test -n "$cur"; then
|
||||||
prompt="git ($cur)> "
|
cur_string=" ($cur)"
|
||||||
else
|
else
|
||||||
prompt="git> "
|
cur_string=""
|
||||||
|
fi
|
||||||
|
if test -n "$exit_status" && test "$exit_status" -ne 0; then
|
||||||
|
es_string=$' \e[31m['"$exit_status"$']\e[0m'
|
||||||
|
else
|
||||||
|
es_string=""
|
||||||
|
fi
|
||||||
|
prompt_character=$(git config --get --default '>' git-extras.repl.prompt-character)
|
||||||
|
|
||||||
|
prefix=$(git config --get --default 'git' git-extras.repl.prefix)
|
||||||
|
|
||||||
|
show_project_name=$(git config --get --default 'false' git-extras.repl.show-project-name)
|
||||||
|
if [[ "$show_project_name" == "true" ]]; then
|
||||||
|
project_name=" $(basename "$(git rev-parse --show-toplevel)" .git)"
|
||||||
|
else
|
||||||
|
project_name=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Readline
|
prompt_base="$prefix$project_name$cur_string$es_string$prompt_character"
|
||||||
read -e -r -p "$prompt" cmd
|
prompt_base_stripped=$(echo "$prompt_base" | awk '{$1=$1};1')
|
||||||
|
prompt="$prompt_base_stripped "
|
||||||
|
|
||||||
# EOF
|
# Use arguments as a command if any are provided.
|
||||||
test $? -ne 0 && break
|
if [ $# -ne 0 ]; then
|
||||||
|
cmd=$*
|
||||||
|
set --
|
||||||
|
echo "$prompt" "$cmd" # It is as though you had entered a command.
|
||||||
|
else
|
||||||
|
# Readline
|
||||||
|
read -e -r -p "$prompt" cmd
|
||||||
|
# Check for EOF, and end the program if so (handles ^D).
|
||||||
|
test $? -ne 0 && break
|
||||||
|
fi
|
||||||
|
|
||||||
# History
|
# Add command to history if it is not all whitespace
|
||||||
history -s "$cmd"
|
if [[ ! "$cmd" =~ ^[[:space:]]*$ ]]; then
|
||||||
|
history -s "$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
# Built-in commands
|
# Built-in commands
|
||||||
case $cmd in
|
case $cmd in
|
||||||
ls) cmd=ls-files;;
|
ls) cmd=ls-files;;
|
||||||
"") continue;;
|
"")
|
||||||
quit|exit) break;;
|
on_enter_cmd=$(git config --get --default '' git-extras.repl.on-enter-command)
|
||||||
|
if test -n "$on_enter_cmd"; then
|
||||||
|
cmd="$on_enter_cmd"
|
||||||
|
else
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
quit|exit|q) break;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
history -a
|
||||||
|
|
||||||
if [[ $cmd == !* ]]; then
|
if [[ $cmd == !* ]]; then
|
||||||
|
# shellcheck disable=SC2086
|
||||||
eval ${cmd:1}
|
eval ${cmd:1}
|
||||||
elif [[ $cmd == git* ]]; then
|
elif [[ $cmd == git* ]]; then
|
||||||
|
# shellcheck disable=SC2086
|
||||||
eval $cmd
|
eval $cmd
|
||||||
else
|
else
|
||||||
eval git "$cmd"
|
eval git "$cmd"
|
||||||
fi
|
fi
|
||||||
|
exit_status=$?
|
||||||
done
|
done
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ file="$1"
|
||||||
commit="$2"
|
commit="$2"
|
||||||
|
|
||||||
if [[ -f $file ]]; then
|
if [[ -f $file ]]; then
|
||||||
git rm --cached -q -f -- $file
|
git rm --cached -q -f -- "$file"
|
||||||
if [[ -z $commit ]]; then
|
if [[ -z $commit ]]; then
|
||||||
git checkout HEAD -- $file
|
git checkout HEAD -- "$file"
|
||||||
echo "Reset '$1' to HEAD"
|
echo "Reset '$1' to HEAD"
|
||||||
else
|
else
|
||||||
git checkout $commit -- $file
|
git checkout "$commit" -- "$file"
|
||||||
echo "Reset '$1' to $commit"
|
echo "Reset '$1' to $commit"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "File '$1' not found in `pwd`"
|
echo "File '$1' not found in $PWD"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,13 @@ git_root() {
|
||||||
|
|
||||||
# get the relative path of current path according to root of repo
|
# get the relative path of current path according to root of repo
|
||||||
git_root_relative() {
|
git_root_relative() {
|
||||||
git rev-parse --show-prefix
|
rel=$(git rev-parse --show-prefix)
|
||||||
|
if [ -z "$rel" ]; then
|
||||||
|
# git rev-parse --show-prefix will output empty string when we are in the root dir
|
||||||
|
echo "."
|
||||||
|
else
|
||||||
|
echo "$rel"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
if test $# -eq 0; then
|
if test $# -eq 0; then
|
||||||
|
|
|
||||||
171
bin/git-scp
171
bin/git-scp
|
|
@ -18,7 +18,7 @@ function _test_git_scp()
|
||||||
function set_remote()
|
function set_remote()
|
||||||
{
|
{
|
||||||
remote=$1
|
remote=$1
|
||||||
if [ $(git remote | grep -c -- ^$remote$) -eq 0 ]
|
if [ "$(git remote | grep -c -- "^$remote$")" -eq 0 ]
|
||||||
then
|
then
|
||||||
COLOR_RED
|
COLOR_RED
|
||||||
echo "Remote $remote does not exist in your git config"
|
echo "Remote $remote does not exist in your git config"
|
||||||
|
|
@ -39,8 +39,9 @@ function php_lint()
|
||||||
test ! -f "$i" && continue
|
test ! -f "$i" && continue
|
||||||
case "$i" in
|
case "$i" in
|
||||||
*\.php|*\.phtml)
|
*\.php|*\.phtml)
|
||||||
php -l "$i" > /dev/null
|
if ! php -l "$i" > /dev/null; then
|
||||||
[ $? -gt 0 ] && error_count[${#error_count[@]}]="$i"
|
error_count[${#error_count[@]}]="$i"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
@ -58,17 +59,17 @@ function php_lint()
|
||||||
|
|
||||||
function _dos2unix()
|
function _dos2unix()
|
||||||
{
|
{
|
||||||
command -v dos2unix > /dev/null && dos2unix $@
|
command -v dos2unix > /dev/null && dos2unix "$@"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sanitize()
|
function _sanitize()
|
||||||
{
|
{
|
||||||
git config --get-all extras.scp.sanitize | while read i
|
git config --get-all extras.scp.sanitize | while read -r i
|
||||||
do
|
do
|
||||||
case $i in
|
case $i in
|
||||||
php_lint) php_lint $@;; # git config --global --add extras.scp.sanitize php_lint
|
php_lint) php_lint "$@";; # git config --global --add extras.scp.sanitize php_lint
|
||||||
dos2unix) _dos2unix $@;; # git config --global --add extras.scp.sanitize dos2unix
|
dos2unix) _dos2unix "$@";; # git config --global --add extras.scp.sanitize dos2unix
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
return $?
|
return $?
|
||||||
|
|
@ -76,14 +77,26 @@ function _sanitize()
|
||||||
|
|
||||||
function scp_and_stage
|
function scp_and_stage
|
||||||
{
|
{
|
||||||
set_remote $1
|
local verbose=0 interactive=0 dry_run=0
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
case "$1" in
|
||||||
|
-v|--verbose) verbose=1; shift;;
|
||||||
|
-i|--interactive) verbose=1; interactive=1; shift;;
|
||||||
|
-n|--dry-run) verbose=1; dry_run=1; shift;;
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
set_remote "$1"
|
||||||
shift
|
shift
|
||||||
|
|
||||||
local refhead="$(git rev-parse --quiet --verify $1)"
|
local refhead
|
||||||
|
refhead="$(git rev-parse --quiet --verify "$1")"
|
||||||
if [ -n "$refhead" ]
|
if [ -n "$refhead" ]
|
||||||
then
|
then
|
||||||
shift
|
shift
|
||||||
[ $(git branch --contains "$refhead" | grep -c '\*') -eq 0 ] &&
|
[ "$(git branch --contains "$refhead" | grep -c '\*')" -eq 0 ] &&
|
||||||
_error "refhead provided is not part of current branch"
|
_error "refhead provided is not part of current branch"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -92,46 +105,117 @@ function scp_and_stage
|
||||||
list=$(git ls-files "$@")" "$(git ls-files -o "$@")
|
list=$(git ls-files "$@")" "$(git ls-files -o "$@")
|
||||||
elif [ -n "$refhead" ]
|
elif [ -n "$refhead" ]
|
||||||
then
|
then
|
||||||
git diff --stat $refhead
|
[ "$verbose" -eq 1 ] && git --no-pager diff --stat "$refhead"
|
||||||
list=$(git diff $refhead --name-only)
|
list=$(git diff "$refhead" --name-only)
|
||||||
else
|
else
|
||||||
git diff
|
[ "$verbose" -eq 1 ] && git --no-pager diff
|
||||||
list=$(git diff --name-only)
|
list=$(git diff --name-only)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
deleted=$(for i in $list; do [ -f $i ] || echo $i; done)
|
deleted=$(for i in $list; do [ -f "$i" ] || echo "$i"; done)
|
||||||
list=$(for i in $list; do [ -f $i ] && echo $i; done)
|
list=$(for i in $list; do [ -f "$i" ] && echo "$i"; done)
|
||||||
|
|
||||||
|
if [ "$interactive" -eq 1 ] && [ "$dry_run" -eq 0 ] && { [ -n "$list" ] || [ -n "$deleted" ]; }
|
||||||
|
then
|
||||||
|
local reply
|
||||||
|
read -r -p "Proceed with sync to $remote? [y/N] " reply
|
||||||
|
case "$reply" in
|
||||||
|
y|Y|yes|YES) ;;
|
||||||
|
*) _info "Aborted, nothing synced."; exit 0;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
local status=0
|
||||||
|
|
||||||
if [ -n "$list" ]
|
if [ -n "$list" ]
|
||||||
then
|
then
|
||||||
local _TMP=${0///}
|
local _TMP=${0///}
|
||||||
echo "$list" > $_TMP &&
|
# shellcheck disable=SC2086
|
||||||
_sanitize $list &&
|
echo "$list" > "$_TMP"
|
||||||
_info Pushing to $remote \($(git config remote.$remote.url)\) &&
|
if [ "$dry_run" -eq 1 ] || _sanitize $list
|
||||||
rsync -rlDv --files-from=$_TMP ./ "$(git config remote.$remote.url)/" &&
|
then
|
||||||
git add --force $list &&
|
_info "Pushing to $remote ($(git config "remote.$remote.url"))"
|
||||||
rm $_TMP
|
if [ "$dry_run" -eq 1 ]
|
||||||
|
then
|
||||||
|
rsync -rlDvn --files-from="$_TMP" ./ "$(git config "remote.$remote.url")/"
|
||||||
|
else
|
||||||
|
rsync -rlDv --files-from="$_TMP" ./ "$(git config "remote.$remote.url")/" &&
|
||||||
|
git add --force $list
|
||||||
|
fi
|
||||||
|
status=$?
|
||||||
|
else
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
rm "$_TMP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
deleted=$(for i in $deleted; do echo $(git config remote.$remote.url | cut -d: -f2)/$i; done)
|
deleted=$(for i in $deleted; do echo "$(git config "remote.$remote.url" | cut -d: -f2)/$i"; done)
|
||||||
|
|
||||||
[ -n "$deleted" ] &&
|
if [ -n "$deleted" ]
|
||||||
COLOR_RED &&
|
then
|
||||||
echo Deleted remote files &&
|
COLOR_RED
|
||||||
ssh $(git config remote.$remote.url | cut -d: -f1) -t "rm $deleted" &&
|
echo Deleted remote files
|
||||||
echo "$deleted"
|
if [ "$dry_run" -eq 1 ]
|
||||||
COLOR_RESET
|
then
|
||||||
|
echo "$deleted"
|
||||||
|
else
|
||||||
|
if ssh "$(git config "remote.$remote.url" | cut -d: -f1)" -t "rm $deleted"
|
||||||
|
then
|
||||||
|
echo "$deleted"
|
||||||
|
else
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
COLOR_RESET
|
||||||
|
fi
|
||||||
|
|
||||||
|
return "$status"
|
||||||
}
|
}
|
||||||
|
|
||||||
function reverse_scp()
|
function reverse_scp()
|
||||||
{
|
{
|
||||||
set_remote $1
|
local verbose=0 interactive=0 dry_run=0
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
case "$1" in
|
||||||
|
-v|--verbose) verbose=1; shift;;
|
||||||
|
-i|--interactive) verbose=1; interactive=1; shift;;
|
||||||
|
-n|--dry-run) verbose=1; dry_run=1; shift;;
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
set_remote "$1"
|
||||||
shift
|
shift
|
||||||
|
|
||||||
local _TMP=${0///}
|
local _TMP=${0///}
|
||||||
echo $@ > $_TMP &&
|
echo "$@" > "$_TMP"
|
||||||
rsync -rlDv --files-from=$_TMP "$(git config remote.$remote.url)/" ./ &&
|
|
||||||
rm $_TMP
|
local status=0
|
||||||
|
if [ "$verbose" -eq 1 ]
|
||||||
|
then
|
||||||
|
rsync -rlDvn --files-from="$_TMP" "$(git config "remote.$remote.url")/" ./
|
||||||
|
status=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$dry_run" -eq 1 ]
|
||||||
|
then
|
||||||
|
rm "$_TMP"
|
||||||
|
return "$status"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$interactive" -eq 1 ]
|
||||||
|
then
|
||||||
|
local reply
|
||||||
|
read -r -p "Proceed with copy from $remote? [y/N] " reply
|
||||||
|
case "$reply" in
|
||||||
|
y|Y|yes|YES) ;;
|
||||||
|
*) _info "Aborted, nothing copied."; rm "$_TMP"; exit 0;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
rsync -rlDv --files-from="$_TMP" "$(git config "remote.$remote.url")/" ./ &&
|
||||||
|
rm "$_TMP"
|
||||||
}
|
}
|
||||||
|
|
||||||
function _info()
|
function _info()
|
||||||
|
|
@ -145,13 +229,18 @@ function _usage()
|
||||||
{
|
{
|
||||||
echo "Usage:
|
echo "Usage:
|
||||||
git scp -h|help|?
|
git scp -h|help|?
|
||||||
git scp <remote> [ref|file..] # scp and stage your files to specified remote
|
git scp [options] <remote> [ref|file..] # scp and stage your files to specified remote
|
||||||
git scp <remote> [<ref>] # show diff relative to <ref> and upload unstaged files to <remote>
|
git scp [options] <remote> [<ref>] # show diff relative to <ref> and upload unstaged files to <remote>
|
||||||
git rscp <remote> [<file|directory>] # copy <remote> files to current working directory
|
git rscp [options] <remote> [<file|directory>] # copy <remote> files to current working directory
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-v, --verbose print what will be synced before syncing
|
||||||
|
-i, --interactive prompt for confirmation before syncing (implies --verbose)
|
||||||
|
-n, --dry-run show what would be synced, change nothing (implies --verbose; no staging, no remote writes/deletes)
|
||||||
"
|
"
|
||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
-v|verbose|--verbose) grep -A100 '^#* OPTIONS #*$' $0;;
|
-v|verbose|--verbose) grep -A100 '^#* OPTIONS #*$' "$0" ;;
|
||||||
esac
|
esac
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|
@ -161,18 +250,18 @@ function _error()
|
||||||
[ $# -eq 0 ] && _usage && exit 0
|
[ $# -eq 0 ] && _usage && exit 0
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo ERROR: "$@"
|
echo "ERROR: $*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
### OPTIONS ###
|
### OPTIONS ###
|
||||||
case $(basename $0) in
|
case $(basename "$0") in
|
||||||
git-scp)
|
git-scp)
|
||||||
case $1 in
|
case $1 in
|
||||||
''|-h|'?'|help|--help) shift; _test_git_scp; _usage $@;;
|
''|-h|'?'|help|--help) shift; _test_git_scp; _usage "$@";;
|
||||||
*) scp_and_stage $@;;
|
*) scp_and_stage "$@";;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
git-rscp) reverse_scp $@;;
|
git-rscp) reverse_scp "$@";;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
51
bin/git-sed
51
bin/git-sed
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
usage: git sed [ -c ] [ -f <flags> ] <search> <replacement>
|
usage: git sed [ -c ] [ -f <flags> ] <search> <replacement> [ <flags> ]
|
||||||
|
|
||||||
Run git grep and then send results to sed for replacement with the
|
Run git grep and then send results to sed for replacement with the
|
||||||
given flags, if -f is provided.
|
given flags, if they are provided via -f or as the third argument.
|
||||||
|
|
||||||
Also runs git commit if -c is provided.
|
Also runs git commit if -c is provided.
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -16,7 +16,8 @@ do_commit() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
while [ "X$1" != "X" ]; do
|
pathspec=
|
||||||
|
while [ "$1" != "" ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-c|--commit)
|
-c|--commit)
|
||||||
if git status --porcelain | grep .; then
|
if git status --porcelain | grep .; then
|
||||||
|
|
@ -32,7 +33,7 @@ actual command:
|
||||||
}
|
}
|
||||||
;;
|
;;
|
||||||
-f|--flags)
|
-f|--flags)
|
||||||
if [ "X$2" = "X" ]; then
|
if [ "$2" = "" ]; then
|
||||||
usage
|
usage
|
||||||
echo "missing argument for $1"
|
echo "missing argument for $1"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -44,16 +45,22 @@ actual command:
|
||||||
usage
|
usage
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
|
--)
|
||||||
|
pathspec="$*"
|
||||||
|
break
|
||||||
|
;;
|
||||||
-*)
|
-*)
|
||||||
usage
|
usage
|
||||||
echo "unknown flag: $1"
|
echo "unknown flag: $1"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
if [ "X$search" = "X" ]; then
|
if [ "$search" = "" ]; then
|
||||||
search="$1"
|
search="$1"
|
||||||
elif [ "X$replacement" = "X" ]; then
|
elif [ "$replacement" = "" ]; then
|
||||||
replacement="$1"
|
replacement="$1"
|
||||||
|
elif [ "$flags" = "" ]; then
|
||||||
|
flags="$1"
|
||||||
else
|
else
|
||||||
usage
|
usage
|
||||||
echo "too many arguments: $1"
|
echo "too many arguments: $1"
|
||||||
|
|
@ -64,6 +71,34 @@ actual command:
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
command="git grep -lz '$search' | xargs -0 sed -i 's/$search/$replacement/$flags'"
|
all="$search$replacement$flags"
|
||||||
git grep -lz "$search" | xargs -0 sed -i "s/$search/$replacement/$flags"
|
case "$all" in
|
||||||
|
*/*)
|
||||||
|
ascii="$(for((i=32;i<=127;i++)) do printf '%b' "\\$(printf '%03o' "$i")"; done)"
|
||||||
|
escaped="${all//-/\\-}"
|
||||||
|
escaped="${escaped//[/\\[}"
|
||||||
|
sep="$(printf '%s' "$ascii" | tr -d "$escaped")"
|
||||||
|
sep="$(printf %.1s "$sep")"
|
||||||
|
if [ "$sep" = "" ] ; then
|
||||||
|
echo 'could not find an unused character for sed separator character'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
sep=/
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
r=$(xargs -r false < /dev/null > /dev/null 2>&1 && echo r)
|
||||||
|
need_bak=$(sed -i s/hello/world/ "$(git_extra_mktemp)" > /dev/null 2>&1 || echo true)
|
||||||
|
|
||||||
|
if [ "$need_bak" ]; then
|
||||||
|
command="git grep -lz '$search' $pathspec | xargs -0$r sed -i '' 's$sep$search$sep$replacement$sep$flags'"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
git grep -lz "$search" $pathspec | xargs -0"$r" sed -i '' "s$sep$search$sep$replacement$sep$flags"
|
||||||
|
else
|
||||||
|
command="git grep -lz '$search' $pathspec | xargs -0$r sed -i 's$sep$search$sep$replacement$sep$flags'"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
git grep -lz "$search" $pathspec | xargs -0"$r" sed -i "s$sep$search$sep$replacement$sep$flags"
|
||||||
|
fi
|
||||||
do_commit
|
do_commit
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
COMMIT_MESSAGE='Initial commit'
|
COMMIT_MESSAGE='Initial commit'
|
||||||
|
|
||||||
if [ "$1" == "-m" ]; then
|
if [ "$1" = "-m" ]; then
|
||||||
COMMIT_MESSAGE=$2
|
COMMIT_MESSAGE=$2
|
||||||
shift; shift
|
shift; shift
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
git branch --no-color --merged | grep -v "\*" | grep -v master | tr -d ' '
|
git branch --no-color --merged | grep -v "\*" | grep -v "$(git_extra_default_branch)" | tr -d ' '
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
git branch --no-color --no-merged | grep -v "\*" | grep -v master | tr -d ' '
|
git branch --no-color --no-merged | grep -v "\*" | grep -v "$(git_extra_default_branch)" | tr -d ' '
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,26 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
SQUASH_MSG=
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--squash-msg)
|
||||||
|
SQUASH_MSG=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# set the argument back
|
||||||
|
set -- "$@" "$arg"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
src="$1"
|
src="$1"
|
||||||
msg="$2"
|
msg="$2"
|
||||||
|
if [[ -n "$msg" ]] && [[ -n "$SQUASH_MSG" ]]; then
|
||||||
|
>&2 echo "When commit message is given, --squash-msg is not allowed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
is_branch() {
|
is_branch() {
|
||||||
git show-ref --verify --quiet "refs/heads/$src"
|
git show-ref --verify --quiet "refs/heads/$src"
|
||||||
|
|
@ -12,7 +31,8 @@ is_commit_reference() {
|
||||||
}
|
}
|
||||||
|
|
||||||
is_on_current_branch() {
|
is_on_current_branch() {
|
||||||
local commit_sha=`git rev-parse "$src"`
|
local commit_sha
|
||||||
|
commit_sha=$(git rev-parse "$src")
|
||||||
git rev-list HEAD |
|
git rev-list HEAD |
|
||||||
grep -q -- "$commit_sha"
|
grep -q -- "$commit_sha"
|
||||||
}
|
}
|
||||||
|
|
@ -23,8 +43,8 @@ commit_if_msg_provided() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt_continuation_if_squashing_master() {
|
prompt_continuation_if_squashing_default_branch() {
|
||||||
if [[ $src =~ ^master$ ]]; then
|
if [[ $src == $(git_extra_default_branch) ]]; then
|
||||||
read -p "Warning: squashing '$src'! Continue [y/N]? " -r
|
read -p "Warning: squashing '$src'! Continue [y/N]? " -r
|
||||||
if ! [[ $REPLY =~ ^[Yy]$ ]]; then
|
if ! [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
echo "Exiting"
|
echo "Exiting"
|
||||||
|
|
@ -34,19 +54,26 @@ prompt_continuation_if_squashing_master() {
|
||||||
}
|
}
|
||||||
|
|
||||||
squash_branch() {
|
squash_branch() {
|
||||||
prompt_continuation_if_squashing_master
|
prompt_continuation_if_squashing_default_branch
|
||||||
|
if [ -n "$SQUASH_MSG" ]; then
|
||||||
|
base=$(git merge-base "$src" @)
|
||||||
|
msg=$(git log "$base".."$src" --format="%s%n%n%b" --no-merges --reverse)
|
||||||
|
fi
|
||||||
git merge --squash "$src" || exit 1 # quits if `git merge` fails
|
git merge --squash "$src" || exit 1 # quits if `git merge` fails
|
||||||
commit_if_msg_provided
|
commit_if_msg_provided
|
||||||
}
|
}
|
||||||
|
|
||||||
squash_current_branch() {
|
squash_current_branch() {
|
||||||
|
if [ -n "$SQUASH_MSG" ]; then
|
||||||
|
msg=$(git log "$src"..@ --format="%s%n%n%b" --no-merges --reverse)
|
||||||
|
fi
|
||||||
git reset --soft "$src" || exit 1 # quits if `git reset` fails
|
git reset --soft "$src" || exit 1 # quits if `git reset` fails
|
||||||
commit_if_msg_provided
|
commit_if_msg_provided
|
||||||
}
|
}
|
||||||
|
|
||||||
if `is_branch`; then
|
if is_branch; then
|
||||||
squash_branch
|
squash_branch
|
||||||
elif `is_commit_reference` && `is_on_current_branch`; then
|
elif is_commit_reference && is_on_current_branch; then
|
||||||
squash_current_branch
|
squash_current_branch
|
||||||
else
|
else
|
||||||
echo "Source branch or commit reference required." 1>&2 && exit 1
|
echo "Source branch or commit reference required." 1>&2 && exit 1
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ EOF
|
||||||
|
|
||||||
error() {
|
error() {
|
||||||
if [[ -n "$1" ]]; then
|
if [[ -n "$1" ]]; then
|
||||||
local msg=$( echo "error: $1" )
|
echo "error: $1" >&2
|
||||||
echo -e "${msg}" >&2
|
|
||||||
fi
|
fi
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -73,7 +72,7 @@ parse_options() {
|
||||||
done
|
done
|
||||||
|
|
||||||
ID="$1"
|
ID="$1"
|
||||||
MSG="${@:2}"
|
MSG="${*:2}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
117
bin/git-standup
117
bin/git-standup
|
|
@ -5,17 +5,21 @@
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOS
|
cat <<EOS
|
||||||
Usage:
|
Usage:
|
||||||
git standup [-a <author name>] [-w <weekstart-weekend>] [-d <days-ago>] [-m <max-dir-depth>] [-g] [-h] [-f]
|
git standup [-a <author name>] [-w <weekstart-weekend>|-d <days-ago>] [-m <max-dir-depth>] [-D date-format] [-L] [-h] [-f] [-B] [-n <number-of-commits] [-F <gpg|authordate>]
|
||||||
|
|
||||||
-a - Specify author to restrict search to
|
-a - Specify author to restrict search to, default to current git user.
|
||||||
|
Use "-a all" if you don't want the restriction.
|
||||||
-w - Specify weekday range to limit search to
|
-w - Specify weekday range to limit search to
|
||||||
-m - Specify the depth of recursive directory search
|
-m - Specify the depth of recursive directory search
|
||||||
-L - Toggle inclusion of symbolic links in recursive directory search
|
-L - Toggle inclusion of symbolic links in recursive directory search
|
||||||
-d - Specify the number of days back to include
|
-d - Specify the number of days back to include
|
||||||
-D - Specify the date format for "git log" (default: relative)
|
-D - Specify the date format for "git log" (default: relative)
|
||||||
-h - Display this help screen
|
-h - Display this help screen
|
||||||
-g - Show if commit is GPG signed (G) or not (N)
|
|
||||||
-f - Fetch the latest commits beforehand
|
-f - Fetch the latest commits beforehand
|
||||||
|
-B - Display the commits in branch groups
|
||||||
|
-n - Limit the number of commits displayed per group
|
||||||
|
-F gpg - Show if commit is GPG signed (G) or not (N)
|
||||||
|
-F authordate - Print author date instead of commit date
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
git standup -a "John Doe" -w "MON-FRI" -m 3
|
git standup -a "John Doe" -w "MON-FRI" -m 3
|
||||||
|
|
@ -31,7 +35,7 @@ in_git_repo=$?
|
||||||
|
|
||||||
# Use colors, but only if connected to a terminal, and that terminal
|
# Use colors, but only if connected to a terminal, and that terminal
|
||||||
# supports them.
|
# supports them.
|
||||||
if which tput >/dev/null 2>&1; then
|
if command -v tput &>/dev/null; then
|
||||||
ncolors=$(tput colors)
|
ncolors=$(tput colors)
|
||||||
fi
|
fi
|
||||||
if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
|
if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
|
||||||
|
|
@ -43,6 +47,7 @@ if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
|
||||||
BOLD=$(tput bold)
|
BOLD=$(tput bold)
|
||||||
UNDERLINE=$(tput smul)
|
UNDERLINE=$(tput smul)
|
||||||
NORMAL=$(tput sgr0)
|
NORMAL=$(tput sgr0)
|
||||||
|
COLOR=always
|
||||||
else
|
else
|
||||||
RED=""
|
RED=""
|
||||||
GREEN=""
|
GREEN=""
|
||||||
|
|
@ -52,13 +57,18 @@ else
|
||||||
BOLD=""
|
BOLD=""
|
||||||
UNDERLINE=""
|
UNDERLINE=""
|
||||||
NORMAL=""
|
NORMAL=""
|
||||||
|
COLOR=never
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Only enable exit-on-error after the non-critical colorization stuff,
|
# Only enable exit-on-error after the non-critical colorization stuff,
|
||||||
# which may fail on systems lacking tput or terminfo
|
# which may fail on systems lacking tput or terminfo
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
while getopts "hgfd:a:w:m:D:L" opt; do
|
RANGE_SPECIFIED=
|
||||||
|
COMMIT_DATE_FORMAT=%cd
|
||||||
|
USE_GPG_FORMAT=no
|
||||||
|
|
||||||
|
while getopts "hgfF:Bd:a:w:m:D:n:L" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
h)
|
h)
|
||||||
usage
|
usage
|
||||||
|
|
@ -72,7 +82,8 @@ while getopts "hgfd:a:w:m:D:L" opt; do
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
d)
|
d)
|
||||||
test -n "$SINCE" && warn "-d option is conflict with -w"
|
test -n "$RANGE_SPECIFIED" && warn "-d option is conflict with -w"
|
||||||
|
RANGE_SPECIFIED=yes
|
||||||
if [ "$OPTARG" -lt 1 ]; then
|
if [ "$OPTARG" -lt 1 ]; then
|
||||||
>&2 echo "Specify days less than one is invalid"
|
>&2 echo "Specify days less than one is invalid"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -80,10 +91,11 @@ while getopts "hgfd:a:w:m:D:L" opt; do
|
||||||
SINCE="$OPTARG days ago"
|
SINCE="$OPTARG days ago"
|
||||||
;;
|
;;
|
||||||
w)
|
w)
|
||||||
if [ -n "$SINCE" ]; then
|
if [ -n "$RANGE_SPECIFIED" ]; then
|
||||||
warn "-w option is conflict with -d"
|
warn "-w option is conflict with -d"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
RANGE_SPECIFIED=yes
|
||||||
|
|
||||||
week_range=${OPTARG}
|
week_range=${OPTARG}
|
||||||
week_start="${week_range%%-*}"
|
week_start="${week_range%%-*}"
|
||||||
|
|
@ -115,7 +127,29 @@ while getopts "hgfd:a:w:m:D:L" opt; do
|
||||||
GIT_DATE_FORMAT=${OPTARG}
|
GIT_DATE_FORMAT=${OPTARG}
|
||||||
;;
|
;;
|
||||||
g)
|
g)
|
||||||
GIT_PRETTY_FORMAT="%C(yellow)gpg: %G?%Creset"
|
warn "-g option is deprecated, use '-F gpg' instead"
|
||||||
|
USE_GPG_FORMAT=yes
|
||||||
|
;;
|
||||||
|
B)
|
||||||
|
GROUP_BY_BRANCHES=true
|
||||||
|
;;
|
||||||
|
n)
|
||||||
|
MAX_COMMIT_NUM=${OPTARG}
|
||||||
|
;;
|
||||||
|
F)
|
||||||
|
case $OPTARG in
|
||||||
|
gpg)
|
||||||
|
USE_GPG_FORMAT=yes
|
||||||
|
;;
|
||||||
|
authordate)
|
||||||
|
COMMIT_DATE_FORMAT=%ad
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
warn "Invalid argument for -F: $OPTARG"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
;;
|
;;
|
||||||
\?)
|
\?)
|
||||||
usage
|
usage
|
||||||
|
|
@ -127,7 +161,7 @@ done
|
||||||
shift $((OPTIND-1))
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
if [[ $# -gt 0 ]]; then
|
if [[ $# -gt 0 ]]; then
|
||||||
warn "please upgrate to new-style interface. Run 'git help standup' to get more info."
|
warn "please upgrade to new-style interface. Run 'git help standup' to get more info."
|
||||||
if [[ $# -gt 3 ]] ; then
|
if [[ $# -gt 3 ]] ; then
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -137,30 +171,76 @@ if [[ $# -gt 0 ]]; then
|
||||||
UNTIL=$3
|
UNTIL=$3
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AUTHOR=${AUTHOR:="$(git config user.name)"}
|
AUTHOR=${AUTHOR:="$(git config user.name || echo '')"}
|
||||||
SINCE=${SINCE:=yesterday}
|
if [ -z "${AUTHOR}" ]; then
|
||||||
UNTIL=${UNTIL:=today}
|
warn "please configure an author with 'git config user.name' or specify an author via '-a'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
FETCH_LAST_COMMIT=${FETCH_LAST_COMMIT:=false}
|
FETCH_LAST_COMMIT=${FETCH_LAST_COMMIT:=false}
|
||||||
MAXDEPTH=${MAXDEPTH:=2}
|
MAXDEPTH=${MAXDEPTH:=2}
|
||||||
GIT_PRETTY_FORMAT="%Cred%h%Creset - %s %Cgreen(%cd) %C(bold blue)<%an>%Creset $GIT_PRETTY_FORMAT"
|
GIT_PRETTY_FORMAT="%Cred%h%Creset - %s %Cgreen(${COMMIT_DATE_FORMAT}) %C(bold blue)<%an>%Creset"
|
||||||
GIT_DATE_FORMAT=${GIT_DATE_FORMAT:=relative}
|
GIT_DATE_FORMAT=${GIT_DATE_FORMAT:=relative}
|
||||||
|
if [[ "$USE_GPG_FORMAT" == 'yes' ]]; then
|
||||||
|
GIT_PRETTY_FORMAT="$GIT_PRETTY_FORMAT %C(yellow)gpg: %G?%Creset"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle config of implicit week
|
||||||
|
IMPLICIT_WEEK=$(git config --get git-extras.standup.implicit-week || echo '')
|
||||||
|
if [[ -z "$RANGE_SPECIFIED" ]] && [[ -n "${IMPLICIT_WEEK}" ]]; then
|
||||||
|
week_start=${IMPLICIT_WEEK%%-*}
|
||||||
|
week_end=${IMPLICIT_WEEK##*-}
|
||||||
|
shopt -s nocasematch
|
||||||
|
if [[ "$week_start" == "$(LC_ALL=C date +%a)" ]]; then
|
||||||
|
SINCE="last $week_end"
|
||||||
|
fi
|
||||||
|
UNTIL=today
|
||||||
|
else
|
||||||
|
SINCE=${SINCE:=yesterday}
|
||||||
|
UNTIL=${UNTIL:=today}
|
||||||
|
fi
|
||||||
|
|
||||||
GIT_LOG_COMMAND="git --no-pager log \
|
GIT_LOG_COMMAND="git --no-pager log \
|
||||||
--all
|
|
||||||
--no-merges
|
--no-merges
|
||||||
--since \"$SINCE\"
|
--since \"$SINCE\"
|
||||||
--until \"$UNTIL\"
|
--until \"$UNTIL\"
|
||||||
--author=\"$AUTHOR\"
|
--author=\"$AUTHOR\"
|
||||||
--abbrev-commit
|
--abbrev-commit
|
||||||
--oneline
|
--oneline
|
||||||
|
--color=$COLOR
|
||||||
--pretty=format:'$GIT_PRETTY_FORMAT'
|
--pretty=format:'$GIT_PRETTY_FORMAT'
|
||||||
--date='$GIT_DATE_FORMAT'"
|
--date='$GIT_DATE_FORMAT'"
|
||||||
|
if [[ -n "$MAX_COMMIT_NUM" ]]; then
|
||||||
|
GIT_LOG_COMMAND="$GIT_LOG_COMMAND --max-count=$MAX_COMMIT_NUM"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git_output() {
|
||||||
|
if [ "$GROUP_BY_BRANCHES" = true ]; then
|
||||||
|
local branches
|
||||||
|
branches=$(git branch --sort=-committerdate | awk '{print substr($0, 3)}')
|
||||||
|
for branch in $branches; do
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
if output=$(eval $GIT_LOG_COMMAND "$branch"); then
|
||||||
|
if [[ -n "$output" ]] ; then
|
||||||
|
echo "${GREEN}${branch}${NORMAL}"
|
||||||
|
echo "$output"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# TODO optimize:return if the latest commit of a branch is earlier than the 'since' day
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
eval $GIT_LOG_COMMAND --all
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
## For when the command has been run in a non-repo directory
|
## For when the command has been run in a non-repo directory
|
||||||
if [[ $in_git_repo != 0 ]]; then
|
if [[ $in_git_repo != 0 ]]; then
|
||||||
## Set delimiter to newline for the loop
|
## Set delimiter to newline for the loop
|
||||||
IFS=$'\n'
|
IFS=$'\n'
|
||||||
## Recursively search for git repositories
|
## Recursively search for git repositories
|
||||||
|
# shellcheck disable=SC2086
|
||||||
PROJECT_DIRS=$(find $INCLUDE_LINKS . -maxdepth "$MAXDEPTH" -mindepth 0 -name .git)
|
PROJECT_DIRS=$(find $INCLUDE_LINKS . -maxdepth "$MAXDEPTH" -mindepth 0 -name .git)
|
||||||
|
|
||||||
# Fetch the latest commits, if required
|
# Fetch the latest commits, if required
|
||||||
|
|
@ -188,11 +268,12 @@ if [[ $in_git_repo != 0 ]]; then
|
||||||
pushd "$DIR" > /dev/null
|
pushd "$DIR" > /dev/null
|
||||||
## Show the detail only if it is a git repository
|
## Show the detail only if it is a git repository
|
||||||
if [[ -d ".git" || -f ".git" ]] ; then
|
if [[ -d ".git" || -f ".git" ]] ; then
|
||||||
if GITOUT=$(eval ${GIT_LOG_COMMAND}); then
|
if GITOUT=$(git_output); then
|
||||||
## Only output if there is some activities
|
## Only output if there is some activities
|
||||||
if [[ ! -z "$GITOUT" ]] ; then
|
if [[ -n "$GITOUT" ]] ; then
|
||||||
echo "${BOLD}${UNDERLINE}${YELLOW}$(basename "$DIR")${NORMAL}"
|
echo "${BOLD}${UNDERLINE}${YELLOW}$(basename "$DIR")${NORMAL}"
|
||||||
echo "$GITOUT"
|
echo "$GITOUT"
|
||||||
|
echo ""
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "Repository under $DIR could not be queried." >&2
|
echo "Repository under $DIR could not be queried." >&2
|
||||||
|
|
@ -206,8 +287,8 @@ else
|
||||||
git fetch --all > /dev/null 2>&1
|
git fetch --all > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if GITOUT=$(eval ${GIT_LOG_COMMAND}); then
|
if GITOUT=$(git_output); then
|
||||||
if [[ ! -z "$GITOUT" ]] ; then
|
if [[ -n "$GITOUT" ]] ; then
|
||||||
echo "$GITOUT"
|
echo "$GITOUT"
|
||||||
else
|
else
|
||||||
if [[ $AUTHOR = '.*' ]] ; then
|
if [[ $AUTHOR = '.*' ]] ; then
|
||||||
|
|
|
||||||
230
bin/git-summary
230
bin/git-summary
|
|
@ -3,24 +3,79 @@
|
||||||
|
|
||||||
cd "$(git root)" || { echo "Can't cd to top level directory";exit 1; }
|
cd "$(git root)" || { echo "Can't cd to top level directory";exit 1; }
|
||||||
|
|
||||||
commit=""
|
PROJECT_FULL_PATH=
|
||||||
test $# -ne 0 && commit=$@
|
SUMMARY_BY_LINE=
|
||||||
project=${PWD##*/}
|
DEDUP_BY_EMAIL=
|
||||||
|
MERGES_ARG=
|
||||||
|
OUTPUT_STYLE=
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--full-path)
|
||||||
|
PROJECT_FULL_PATH=1
|
||||||
|
;;
|
||||||
|
--line)
|
||||||
|
SUMMARY_BY_LINE=1
|
||||||
|
;;
|
||||||
|
--dedup-by-email)
|
||||||
|
DEDUP_BY_EMAIL=1
|
||||||
|
;;
|
||||||
|
--no-merges)
|
||||||
|
MERGES_ARG="--no-merges"
|
||||||
|
;;
|
||||||
|
--output-style)
|
||||||
|
OUTPUT_STYLE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
>&2 echo "unknown argument $arg found"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# set the argument back
|
||||||
|
set -- "$@" "$arg"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$DEDUP_BY_EMAIL" ] && [ -n "$SUMMARY_BY_LINE" ]; then
|
||||||
|
>&2 echo "--dedup-by-email used with --line is not supported"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$MERGES_ARG" ] && [ -n "$SUMMARY_BY_LINE" ]; then
|
||||||
|
>&2 echo "--no-merges used with --line is not supported"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
commit="HEAD"
|
||||||
|
if [ -n "$SUMMARY_BY_LINE" ]; then
|
||||||
|
paths=( "$@" )
|
||||||
|
else
|
||||||
|
[ $# -ne 0 ] && commit=$*
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$PROJECT_FULL_PATH" ]]; then
|
||||||
|
project=${PWD/${HOME}/\~}
|
||||||
|
else
|
||||||
|
project=${PWD##*/}
|
||||||
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# get date for the given <commit>
|
# get date for the given <commit>
|
||||||
#
|
#
|
||||||
|
commit_date() {
|
||||||
date() {
|
# the $1 can be empty
|
||||||
git log --pretty='format: %ai' $1 | cut -d ' ' -f 2
|
# shellcheck disable=SC2086
|
||||||
|
git log $MERGES_ARG --pretty='format: %ai' "$1" | cut -d ' ' -f 2
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# get active days for the given <commit>
|
# get active days for the given <commit>
|
||||||
#
|
#
|
||||||
|
|
||||||
active_days() {
|
active_days() {
|
||||||
date $1 | sort -r | uniq | awk '
|
commit_date "$1" | sort -r | uniq | awk '
|
||||||
{ sum += 1 }
|
{ sum += 1 }
|
||||||
END { print sum }
|
END { print sum }
|
||||||
'
|
'
|
||||||
|
|
@ -29,28 +84,66 @@ active_days() {
|
||||||
#
|
#
|
||||||
# get the commit total
|
# get the commit total
|
||||||
#
|
#
|
||||||
|
|
||||||
commit_count() {
|
commit_count() {
|
||||||
git log --oneline $commit | wc -l | tr -d ' '
|
# shellcheck disable=SC2086
|
||||||
|
git rev-list $MERGES_ARG --count "$commit"
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# total file count
|
# total file count
|
||||||
#
|
#
|
||||||
|
|
||||||
file_count() {
|
file_count() {
|
||||||
git ls-files | wc -l | tr -d ' '
|
git ls-files | wc -l | tr -d ' '
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# remove duplicate authors who belong to the same email address
|
||||||
|
#
|
||||||
|
dedup_by_email() {
|
||||||
|
# in:
|
||||||
|
# 27 luo zexuan <LuoZexuan@xxx.com>
|
||||||
|
# 7 罗泽轩 <luozexuan@xxx.com>
|
||||||
|
# out:
|
||||||
|
# 34 luo zexuan
|
||||||
|
LC_ALL=C awk '
|
||||||
|
{
|
||||||
|
sum += $1
|
||||||
|
last_field = tolower($NF)
|
||||||
|
if (last_field in emails) {
|
||||||
|
emails[last_field] += $1
|
||||||
|
} else {
|
||||||
|
email = last_field
|
||||||
|
emails[email] = $1
|
||||||
|
# set commits/email to empty
|
||||||
|
$1=$NF=""
|
||||||
|
sub(/^[[:space:]]+/, "", $0)
|
||||||
|
sub(/[[:space:]]+$/, "", $0)
|
||||||
|
name = $0
|
||||||
|
if (name in names) {
|
||||||
|
# when the same name is associated with existed email,
|
||||||
|
# merge the previous email into the later one.
|
||||||
|
emails[email] += emails[names[name]]
|
||||||
|
emails[names[name]] = 0
|
||||||
|
}
|
||||||
|
names[name] = email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
for (name in names) {
|
||||||
|
email = names[name]
|
||||||
|
printf "%6d\t%s\n", emails[email], name
|
||||||
|
}
|
||||||
|
}' | sort -rn -k 1
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# list authors
|
# list authors
|
||||||
#
|
#
|
||||||
|
format_authors() {
|
||||||
authors() {
|
|
||||||
# a rare unicode character is used as separator to avoid conflicting with
|
# a rare unicode character is used as separator to avoid conflicting with
|
||||||
# author name. However, Linux column utility will escape tab if separator
|
# author name. However, Linux column utility will escape tab if separator
|
||||||
# specified, so we do unesaping after it.
|
# specified, so we do unesaping after it.
|
||||||
git shortlog -n -s $commit | LC_ALL=C awk '
|
LC_ALL=C awk '
|
||||||
{ args[NR] = $0; sum += $0 }
|
{ args[NR] = $0; sum += $0 }
|
||||||
END {
|
END {
|
||||||
for (i = 1; i <= NR; ++i) {
|
for (i = 1; i <= NR; ++i) {
|
||||||
|
|
@ -63,29 +156,104 @@ authors() {
|
||||||
#
|
#
|
||||||
# fetch repository age from oldest commit
|
# fetch repository age from oldest commit
|
||||||
#
|
#
|
||||||
|
|
||||||
repository_age() {
|
repository_age() {
|
||||||
git log --reverse --pretty=oneline --format="%ar" | head -n 1 | LC_ALL=C sed 's/ago//'
|
git log --reverse --pretty=oneline --format="%ar" | head -n 1 | LC_ALL=C sed 's/ago//'
|
||||||
}
|
}
|
||||||
|
|
||||||
# summary
|
#
|
||||||
|
# fetch repository age of the latest commit
|
||||||
|
#
|
||||||
|
last_active() {
|
||||||
|
git log --pretty=oneline --format="%ar" -n 1
|
||||||
|
}
|
||||||
|
|
||||||
if test "$1" = "--line"; then
|
#
|
||||||
shift
|
# list the last modified author for each line
|
||||||
git line-summary "$@"
|
#
|
||||||
echo
|
single_file() {
|
||||||
else
|
while read -r data
|
||||||
|
do
|
||||||
|
if [[ $(file "$data") = *text* ]]; then
|
||||||
|
git blame --line-porcelain "$data" 2>/dev/null | grep "^author " | LC_ALL=C sed -n 's/^author //p';
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
echo
|
current_branch_name() {
|
||||||
echo " project : $project"
|
git rev-parse --abbrev-ref HEAD
|
||||||
echo " repo age :" $(repository_age)
|
}
|
||||||
echo " active :" $(active_days) days
|
|
||||||
echo " commits :" $(commit_count)
|
#
|
||||||
if test "$commit" = ""; then
|
# list the author for all file
|
||||||
echo " files :" $(file_count)
|
#
|
||||||
|
lines() {
|
||||||
|
git ls-files -- "$@" | single_file
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# get the number of the lines
|
||||||
|
#
|
||||||
|
line_count() {
|
||||||
|
lines "$@" | wc -l
|
||||||
|
}
|
||||||
|
|
||||||
|
uncommitted_changes_count() {
|
||||||
|
git status --porcelain | wc -l
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
COLUMN_CMD_DELIMTER="¬" # Hopefully, this symbol is not used in branch names... I use it as a separator for columns
|
||||||
|
SP="$COLUMN_CMD_DELIMTER|"
|
||||||
|
|
||||||
|
print_summary_by_line() {
|
||||||
|
if [ "$OUTPUT_STYLE" = "tabular" ]; then
|
||||||
|
tabular_headers="# Repo $SP Lines"
|
||||||
|
echo -e "$tabular_headers\n$project $SP $(line_count "${paths[@]}")" | column -t -s "$COLUMN_CMD_DELIMTER"
|
||||||
|
elif [ "$OUTPUT_STYLE" = "oneline" ]; then
|
||||||
|
echo "$project / lines: $(line_count "${paths[@]}")"
|
||||||
|
elif [ -n "$SUMMARY_BY_LINE" ]; then
|
||||||
|
echo
|
||||||
|
echo " project : $project"
|
||||||
|
echo " lines : $(line_count "${paths[@]}")"
|
||||||
|
echo " authors :"
|
||||||
|
lines "${paths[@]}" | sort | uniq -c | sort -rn | format_authors
|
||||||
fi
|
fi
|
||||||
echo " authors : "
|
}
|
||||||
authors
|
|
||||||
echo
|
|
||||||
|
|
||||||
|
print_summary() {
|
||||||
|
if [ "$OUTPUT_STYLE" = "tabular" ]; then
|
||||||
|
tabular_headers="# Repo $SP Age $SP Last active $SP Active on $SP Commits $SP Uncommitted $SP Branch"
|
||||||
|
echo -e "$tabular_headers\n$project $SP $(repository_age) $SP $(last_active) $SP $(active_days "$commit") days $SP $(commit_count "$commit") $SP $(uncommitted_changes_count) $SP $(current_branch_name)" | column -t -s "$COLUMN_CMD_DELIMTER"
|
||||||
|
elif [ "$OUTPUT_STYLE" = "oneline" ]; then
|
||||||
|
echo "$project / age: $(repository_age) / last active: $(last_active) / active on $(active_days "$commit") days / commits: $(commit_count "$commit") / uncommitted: $(uncommitted_changes_count) / branch: $(current_branch_name)"
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo " project : $project"
|
||||||
|
echo " repo age : $(repository_age)"
|
||||||
|
echo " branch: : $(current_branch_name)"
|
||||||
|
echo " last active : $(last_active)"
|
||||||
|
echo " active on : $(active_days "$commit") days"
|
||||||
|
echo " commits : $(commit_count "$commit")"
|
||||||
|
|
||||||
|
# The file count doesn't support passing a git ref so ignore it if a ref is given
|
||||||
|
if [ "$commit" = "HEAD" ]; then
|
||||||
|
echo " files : $(file_count)"
|
||||||
|
fi
|
||||||
|
echo " uncommitted : $(uncommitted_changes_count)"
|
||||||
|
echo " authors : "
|
||||||
|
if [ -n "$DEDUP_BY_EMAIL" ]; then
|
||||||
|
# the $commit can be empty
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
git shortlog $MERGES_ARG -n -s -e "$commit" | dedup_by_email | format_authors
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
git shortlog $MERGES_ARG -n -s "$commit" | format_authors
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "$SUMMARY_BY_LINE" ]; then
|
||||||
|
print_summary_by_line
|
||||||
|
else
|
||||||
|
print_summary
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
42
bin/git-sync
42
bin/git-sync
|
|
@ -7,10 +7,12 @@ function _usage()
|
||||||
Usage:
|
Usage:
|
||||||
${command} [<remote> <branch>]
|
${command} [<remote> <branch>]
|
||||||
${command} -h | --help
|
${command} -h | --help
|
||||||
|
${command} -s | --soft
|
||||||
|
${command} -f | --force
|
||||||
|
|
||||||
Sync local branch with <remote>/<branch>.
|
Sync local branch with <remote>/<branch>.
|
||||||
When <remote> and <branch> are not specified on the command line, upstream of local branch will be used by default.
|
When <remote> and <branch> are not specified on the command line, upstream of local branch will be used by default.
|
||||||
All changes and untracked files and directories will be removed.
|
All changes and untracked files and directories will be removed unless you add -s(--soft).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
Sync with upstream of local branch:
|
Sync with upstream of local branch:
|
||||||
|
|
@ -18,6 +20,12 @@ Examples:
|
||||||
|
|
||||||
Sync with origin/master:
|
Sync with origin/master:
|
||||||
${command} origin master
|
${command} origin master
|
||||||
|
|
||||||
|
Sync without cleaning untracked files:
|
||||||
|
${command} -s
|
||||||
|
|
||||||
|
Sync without interaction:
|
||||||
|
${command} -f
|
||||||
EOS
|
EOS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,6 +37,12 @@ function main()
|
||||||
_usage
|
_usage
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
|
-s | --soft)
|
||||||
|
local soft="true"
|
||||||
|
;;
|
||||||
|
-f | --force)
|
||||||
|
local force="YES"
|
||||||
|
;;
|
||||||
* )
|
* )
|
||||||
if [ "${remote}" = "" ]; then
|
if [ "${remote}" = "" ]; then
|
||||||
local remote="$1"
|
local remote="$1"
|
||||||
|
|
@ -45,14 +59,14 @@ function main()
|
||||||
done
|
done
|
||||||
|
|
||||||
local remote_branch
|
local remote_branch
|
||||||
if [ "${remote}" = "" ]; then
|
if [ -z "${remote}" ]; then
|
||||||
if ! remote_branch="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null)"; then
|
if ! remote_branch="$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null)"; then
|
||||||
echo "There is no upstream information of local branch."
|
echo "There is no upstream information of local branch."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
local branch="$(git rev-parse --abbrev-ref --symbolic-full-name @)"
|
local branch="$(git rev-parse --abbrev-ref --symbolic-full-name @)"
|
||||||
local remote=$(git config "branch.${branch}.remote")
|
local remote=$(git config "branch.${branch}.remote")
|
||||||
elif [ "${branch}" = "" ]; then
|
elif [ -z "${branch}" ]; then
|
||||||
echo -e "Error: too few arguments.\n"
|
echo -e "Error: too few arguments.\n"
|
||||||
_usage
|
_usage
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -60,12 +74,22 @@ function main()
|
||||||
remote_branch="${remote}/${branch}"
|
remote_branch="${remote}/${branch}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -n "Are you sure you want to clean all changes & sync with '${remote_branch}'? [y/N]: "
|
if [ "${force}" != "YES" ]; then
|
||||||
local res
|
if [ "${soft}" = "true" ]; then
|
||||||
read res
|
echo -n "Are you sure you want to sync with '${remote_branch}'? [y/N]: "
|
||||||
case "${res}" in
|
else
|
||||||
|
echo -n "Are you sure you want to clean all changes & sync with '${remote_branch}'? [y/N]: "
|
||||||
|
fi
|
||||||
|
local force
|
||||||
|
read -r force
|
||||||
|
fi
|
||||||
|
case "${force}" in
|
||||||
"Y" | "y" | "yes" | "Yes" | "YES" )
|
"Y" | "y" | "yes" | "Yes" | "YES" )
|
||||||
git fetch "${remote}" "${branch}" && git reset --hard "${remote_branch}" && git clean -d -f -x
|
if [ "${soft}" = "true" ]; then
|
||||||
|
git fetch "${remote}" "${branch}" && git reset --hard "${remote_branch}"
|
||||||
|
else
|
||||||
|
git fetch "${remote}" "${branch}" && git reset --hard "${remote_branch}" && git clean -d -f -x
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
* )
|
* )
|
||||||
echo "Canceled."
|
echo "Canceled."
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ USAGE(){
|
||||||
|
|
||||||
test $# -lt 1 && USAGE
|
test $# -lt 1 && USAGE
|
||||||
|
|
||||||
for filename in $@; do
|
for filename in "$@"; do
|
||||||
touch "$filename" \
|
touch "$filename" \
|
||||||
&& git add "$filename"
|
&& git add "$filename"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
55
bin/git-undo
55
bin/git-undo
|
|
@ -1,6 +1,27 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
back="^"
|
cstr="commits"
|
||||||
|
ccnt=$(git rev-list --count HEAD)
|
||||||
|
if [ "$ccnt" -eq 1 ]; then cstr="commit"; fi
|
||||||
|
parm3=""
|
||||||
|
|
||||||
|
function _undo()
|
||||||
|
{
|
||||||
|
type=${1:-soft}
|
||||||
|
undo_cnt=${2:-1}
|
||||||
|
reset=${3:-""}
|
||||||
|
|
||||||
|
if [ "$undo_cnt" -gt "$ccnt" ]; then
|
||||||
|
echo "Only $ccnt $cstr, cannot undo $undo_cnt"
|
||||||
|
elif [ "$type" = "hard" ] && [ "$ccnt" -eq "$undo_cnt" ]; then
|
||||||
|
echo "Cannot hard undo all commits"
|
||||||
|
elif [ "$type" = "soft" ] && [ "$ccnt" -eq 1 ]; then
|
||||||
|
git update-ref -d HEAD
|
||||||
|
else
|
||||||
|
git reset "--$type" "HEAD~$undo_cnt"
|
||||||
|
fi
|
||||||
|
if [ "$reset" != "" ]; then git reset; fi
|
||||||
|
}
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-h)
|
-h)
|
||||||
|
|
@ -12,9 +33,8 @@ EOL
|
||||||
read -r res
|
read -r res
|
||||||
case "${res}" in
|
case "${res}" in
|
||||||
"Y" | "y")
|
"Y" | "y")
|
||||||
test $2 -gt 1 > /dev/null 2>&1 && back="~$2"
|
parm1=hard
|
||||||
git reset --hard HEAD$back
|
parm2=${2:-1}
|
||||||
exit $?
|
|
||||||
;;
|
;;
|
||||||
* )
|
* )
|
||||||
exit 0
|
exit 0
|
||||||
|
|
@ -22,16 +42,31 @@ EOL
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
--hard)
|
--hard)
|
||||||
test $2 -gt 1 > /dev/null 2>&1 && back="~$2"
|
parm1=hard
|
||||||
git reset --hard HEAD$back && exit 0;
|
parm2=${2:-1}
|
||||||
;;
|
;;
|
||||||
-s|--soft)
|
-s|--soft)
|
||||||
test $2 -gt 1 > /dev/null 2>&1 && back="~$2"
|
parm1=soft
|
||||||
|
parm2=${2:-1}
|
||||||
|
parm3=reset
|
||||||
|
;;
|
||||||
|
"")
|
||||||
|
parm1=soft
|
||||||
|
parm2=1
|
||||||
|
;;
|
||||||
|
*[!0-9]*)
|
||||||
|
echo "Invalid parameter: $1"
|
||||||
|
exit 1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
test $1 -gt 1 > /dev/null 2>&1 && back="~$1"
|
parm1=soft
|
||||||
|
parm2="$1"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
git reset --soft HEAD$back
|
if [[ ! $parm2 =~ ^[1-9][0-9]*$ ]]; then
|
||||||
git reset
|
echo "Invalid undo count: $parm2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_undo "$parm1" "$parm2" "$parm3"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
filename=$1
|
filename="$1"
|
||||||
test -z $filename && echo "filename required." 1>&2 && exit 1
|
test -z "$filename" && echo "filename required." 1>&2 && exit 1
|
||||||
git update-index --no-skip-worktree $filename
|
git update-index --no-skip-worktree "$filename"
|
||||||
|
|
|
||||||
14
bin/git-unwip
Executable file
14
bin/git-unwip
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Based on scripts from git-utils:
|
||||||
|
# * https://github.com/ddollar/git-utils/blob/master/git-unwip
|
||||||
|
|
||||||
|
# Check if the last commit is a 'WIP' commit
|
||||||
|
LAST_COMMIT=`git log -1 --pretty=%B | tr -d '[:space:]'`
|
||||||
|
|
||||||
|
if [ 'WIP' != $LAST_COMMIT ]; then
|
||||||
|
echo 'Last commit is not a WIP commit, so it will not be unWIP-ed.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
git undo --soft
|
||||||
131
bin/git-utimes
Executable file
131
bin/git-utimes
Executable file
|
|
@ -0,0 +1,131 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Change files modification time to their last commit date
|
||||||
|
#
|
||||||
|
|
||||||
|
# Bash unofficial strict mode
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
if [[ "${1:-}" == "--newer" ]]; then
|
||||||
|
op=le
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
op=eq
|
||||||
|
fi
|
||||||
|
|
||||||
|
# BSD systems
|
||||||
|
if date -j &>/dev/null; then
|
||||||
|
stat_flags="-f %m"
|
||||||
|
date_flags="-r"
|
||||||
|
else
|
||||||
|
# Non-BSD systems
|
||||||
|
stat_flags="-c %Y"
|
||||||
|
date_flags="-d@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bash_opts=()
|
||||||
|
if bash --help 2>&1 | grep -q -- '--noprofile'; then
|
||||||
|
bash_opts+=(--noprofile)
|
||||||
|
fi
|
||||||
|
if bash --help 2>&1 | grep -q -- '--norc'; then
|
||||||
|
bash_opts+=(--norc)
|
||||||
|
fi
|
||||||
|
|
||||||
|
status_opts=(--porcelain --short)
|
||||||
|
# %ct: committer date, UNIX timestamp / %at: author date, UNIX timestamp
|
||||||
|
log_opts=(--format='%ct')
|
||||||
|
if git status --help 2>&1 | grep -q -- "--no-renames"; then
|
||||||
|
status_opts+=(--no-renames)
|
||||||
|
log_opts+=(--no-renames)
|
||||||
|
fi
|
||||||
|
if git status --help 2>&1 | grep -q -- "--untracked-files"; then
|
||||||
|
status_opts+=(--untracked-files=no)
|
||||||
|
fi
|
||||||
|
if git status --help 2>&1 | grep -q -- "--ignored"; then
|
||||||
|
status_opts+=(--ignored=no)
|
||||||
|
fi
|
||||||
|
|
||||||
|
prefix="$(git rev-parse --show-prefix) "
|
||||||
|
strip="${#prefix}"
|
||||||
|
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
# shellcheck disable=SC2064
|
||||||
|
trap "rm -f '${tmpfile}'" 0
|
||||||
|
|
||||||
|
awk_flags=(
|
||||||
|
-F'\t'
|
||||||
|
-v date_flags="${date_flags}"
|
||||||
|
-v op="${op}"
|
||||||
|
-v stat_flags="${stat_flags}"
|
||||||
|
-v strip="${strip}"
|
||||||
|
-v tmpfile="${tmpfile}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# sanity check, not required:
|
||||||
|
if awk --help 2>&1 | grep -q -- '--posix'; then
|
||||||
|
awk_flags+=(--posix)
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r -d '' awk_script <<"EOF" || true
|
||||||
|
BEGIN {
|
||||||
|
seen[""]=1
|
||||||
|
print "#!/usr/bin/env bash"
|
||||||
|
print "set +e"
|
||||||
|
print "t() {"
|
||||||
|
print " test -e \"$2\" || return 0"
|
||||||
|
printf(" test \"$(stat %s \"$2\" 2>/dev/null)\" -%s \"$1\" && return 0\n", stat_flags, op)
|
||||||
|
if (date_flags == "-d@") {
|
||||||
|
print " echo \"+ touch -h -d@$1 $2\""
|
||||||
|
print " touch -h -d@$1 \"$2\""
|
||||||
|
} else {
|
||||||
|
print " t=$(date -r$1 \"+%Y%m%d%H%M.%S\")"
|
||||||
|
print " echo \"+ touch -h -t $t $2\""
|
||||||
|
print " touch -h -t $t \"$2\""
|
||||||
|
}
|
||||||
|
print "}"
|
||||||
|
}
|
||||||
|
FILENAME==tmpfile {
|
||||||
|
skip[$1]=1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
# skip blank lines
|
||||||
|
!/^$/ {
|
||||||
|
# skip deletes
|
||||||
|
if (substr($1, length($1), 1) ~ /D/) {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if (NF == 1) {
|
||||||
|
ct=$1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
$2 = substr($2, strip, length($2)- strip + 1)
|
||||||
|
if ($2 in seen) {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if ($2 in skip) {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
seen[$2]=1
|
||||||
|
# remove enclosing double quotes that git adds:
|
||||||
|
if (substr($2, 1, 1) == "\"" && substr($2, length($2), 1) == "\"") {
|
||||||
|
$2 = substr($2, 2, length($2) - 2)
|
||||||
|
# unescape remaining double quotes
|
||||||
|
gsub(/\\"/, "\"", $2)
|
||||||
|
# unescape escaped backslashes
|
||||||
|
gsub(/\\\\/, "\\", $2)
|
||||||
|
}
|
||||||
|
# escape apostrophes: ' => '\''
|
||||||
|
gsub(/'/, "'\\''", $2)
|
||||||
|
printf("t %s '%s'\n", ct, $2)
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# prefix is stripped:
|
||||||
|
git --no-pager status "${status_opts[@]}" . \
|
||||||
|
| cut -c 4- >"${tmpfile}"
|
||||||
|
|
||||||
|
# prefix is not stripped:
|
||||||
|
git --no-pager log --raw --no-merges "${log_opts[@]}" . \
|
||||||
|
| awk "${awk_flags[@]}" "${awk_script}" "${tmpfile}" - \
|
||||||
|
| BASH_ENV='' bash "${bash_opts[@]}" -
|
||||||
8
bin/git-wip
Executable file
8
bin/git-wip
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Based on scripts from git-utils:
|
||||||
|
# * https://github.com/ddollar/git-utils/blob/master/git-wip
|
||||||
|
# * https://github.com/ddollar/git-utils/blob/master/git-addremove
|
||||||
|
|
||||||
|
git add --all
|
||||||
|
git commit --all --message="WIP"
|
||||||
|
|
@ -3,10 +3,10 @@ index e49cd24..4ae28b5 100755
|
||||||
--- a/bin/git-extras
|
--- a/bin/git-extras
|
||||||
+++ b/bin/git-extras
|
+++ b/bin/git-extras
|
||||||
@@ -4,13 +4,12 @@ VERSION="4.3.0"
|
@@ -4,13 +4,12 @@ VERSION="4.3.0"
|
||||||
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/master/install.sh"
|
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
- local bin="$(which git-extras)"
|
- local bin="$(command -v git-extras)"
|
||||||
- local prefix=${bin%/*/*}
|
- local prefix=${bin%/*/*}
|
||||||
- local orig=$PWD
|
- local orig=$PWD
|
||||||
-
|
-
|
||||||
|
|
|
||||||
10
check_dependencies.sh
Executable file
10
check_dependencies.sh
Executable file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
err() {
|
||||||
|
echo >&2 "$1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! command -v column >/dev/null 2>&1; then
|
||||||
|
err "Need to install dependency 'column' before installation (can be found in bsdmainutils)"
|
||||||
|
fi
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue