mirror of
https://github.com/tj/git-extras.git
synced 2026-09-10 15:36:21 -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
|
||||
various contributors:
|
||||
|
||||
Development Lead
|
||||
````````````````
|
||||
- TJ Holowaychuk <tj@vision-media.ca>
|
||||
git-extras is written and maintained by various contributors:
|
||||
|
||||
Maintainers
|
||||
```````````
|
||||
- TJ Holowaychuk <tj@vision-media.ca>
|
||||
- Hemanth.HM <hemanth.hm@gmail.com>
|
||||
- Nimit Kalra <me@nimit.io>
|
||||
- Nicolai Skogheim <nicolai.skogheim@gmail.com>
|
||||
- spacewander <spacewanderlzx@gmail.com>
|
||||
- Edwin Kofler <edwin@kofler.dev>
|
||||
- Leroy <vanpipy@gmail.com>
|
||||
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
- Jonhnny Weslley
|
||||
- Tobias Fendin
|
||||
- LeeW
|
||||
- timfeirg
|
||||
- Niklas Schlimm
|
||||
- nickl-
|
||||
- timfeirg
|
||||
- CJ
|
||||
- Paul Wise
|
||||
- Damien Tardy-Panis
|
||||
- Mark Eissler
|
||||
- Yuriy VG
|
||||
- Leila Muhtasib
|
||||
- Richard Fearn
|
||||
- Bill Wood
|
||||
- Jesús Espino
|
||||
- Kenneth Reitz
|
||||
- David Baumgold
|
||||
- Rémy HUBSCHER
|
||||
- go2null
|
||||
- Andrew Janke
|
||||
- David Baumgold
|
||||
- Jan Schulz
|
||||
- Kenneth Reitz
|
||||
- Mark Pitman
|
||||
- Peter Benjamin
|
||||
- Günther Grill
|
||||
- Luke Childs
|
||||
- Sasha Khamkov
|
||||
- equt
|
||||
- oikarinen
|
||||
- vyas
|
||||
- Don Harper
|
||||
- Pierre Ayoub
|
||||
- Robin Winslow
|
||||
- Ross Smith II
|
||||
- Yi EungJun
|
||||
- grindhold
|
||||
- NANRI
|
||||
- Chris Hall
|
||||
- Damian Krzeminski
|
||||
- wyattscarpenter
|
||||
- Aggelos Orfanakos
|
||||
- Camille Reynders
|
||||
- Rico Sta. Cruz
|
||||
- Rémy HUBSCHER
|
||||
- Carlos Prado
|
||||
- Chris Hall
|
||||
- Damian Krzeminski
|
||||
- NANRI
|
||||
- Alexander Krasnukhin
|
||||
- Takuma Yamaguchi
|
||||
- Phally
|
||||
- Julio Napurí
|
||||
- Rico Sta. Cruz
|
||||
- Takuma Yamaguchi
|
||||
- thomas menzel
|
||||
- vr8ce
|
||||
- Brian J Brennan
|
||||
- Dominik Gedon
|
||||
- Hogan Long
|
||||
- Ivan Malopinsky
|
||||
- Brian J Brennan
|
||||
- wooorm
|
||||
- Philipp Klose
|
||||
- John Bachir
|
||||
- Jonathan "Duke" Leto
|
||||
- Julio Napurí
|
||||
- Justin Dugger
|
||||
- Nils Winkler
|
||||
- Philipp Klose
|
||||
- Raphael Boidol
|
||||
- Richard Russon
|
||||
- Sam Bostock
|
||||
- Vladimir Jimenez
|
||||
- gisphm
|
||||
- William Montgomery
|
||||
- David Rogers
|
||||
- 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
|
||||
- jacobherrington
|
||||
- overengineer
|
||||
- phigoro
|
||||
- soffolk
|
||||
- wooorm
|
||||
- Alexis GRIMALDI
|
||||
- Domenico Rotiroti
|
||||
- Devin Withers
|
||||
- Jean Jordaan
|
||||
- Matiss
|
||||
- Allan Odgaard
|
||||
- Andre Cerqueira
|
||||
- Austin Ziegler
|
||||
- Beth Skurrie
|
||||
- Brice Dutheil
|
||||
- Curtis McEnroe
|
||||
- Daniele Paolella
|
||||
- David Rogers
|
||||
- Devin Withers
|
||||
- Domenico Rotiroti
|
||||
- Evan Grim
|
||||
- Tom Vincent
|
||||
- Justin Guenther
|
||||
- Richard Littauer
|
||||
- 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
|
||||
- Stefan VanBuren
|
||||
- Stephen Mathieson
|
||||
- Steve Mao
|
||||
- Todd Wolfson
|
||||
- Tom Ashworth
|
||||
- Tony
|
||||
- TweeKane
|
||||
- Valérian Galliat
|
||||
- Xiaopei Li
|
||||
- creynders
|
||||
- William Montgomery
|
||||
- Ye Lin Aung
|
||||
- luozexuan
|
||||
- roxchgt
|
||||
- soffolk
|
||||
- 0xflotus
|
||||
- Abdullah
|
||||
- Adam Parkin
|
||||
- eszabpt
|
||||
- jykntr
|
||||
- meza
|
||||
- neydroid
|
||||
- nulltask
|
||||
- zeroDivisible
|
||||
- Étienne BERSAC
|
||||
- ☃ pitr
|
||||
- dead-horse
|
||||
- Adriaan Zonnenberg
|
||||
- Akim Demaille
|
||||
- Alessandro Pagiaro
|
||||
- Alex McHale
|
||||
- Aloxaf
|
||||
- Amir Tocker
|
||||
- Ammar Najjar
|
||||
- Amory Meltzer
|
||||
- Andreas Duering
|
||||
- Andrei Petcu
|
||||
- Andrew Griffiths
|
||||
- Andrew Marcinkevičius
|
||||
- Andrew Starr-Bochicchio
|
||||
- Andrew Sullivan Cant
|
||||
- Andrey Elizarov
|
||||
- Angel Aguilera
|
||||
- Antoine Beaupré
|
||||
- Aurélien Scoubeau
|
||||
- Balazs Nadasdi
|
||||
- Ben Parnell
|
||||
- Beni Ben Zikry
|
||||
- Brandon Zylstra
|
||||
- Brian Goad
|
||||
- Brian Murrell
|
||||
- Bruno Sutic
|
||||
- Caleb Maclennan
|
||||
- Carl Casbolt
|
||||
- Carlos Rodríguez
|
||||
- Christophe Badoit
|
||||
- Ciro Nunes
|
||||
- CleanMachine1
|
||||
- CodeByZach
|
||||
- Craig MacGregor
|
||||
- Dan Goodliffe
|
||||
- Dan Jackson
|
||||
- Dan Kilman
|
||||
- Daniel Bruder
|
||||
- Daniel Lublin
|
||||
- Daniel Schildt
|
||||
- Dave James Miller
|
||||
- David Hartmann
|
||||
- Dominic Barnes
|
||||
- Dung Quang
|
||||
- Edward Betts
|
||||
- Eli Schwartz
|
||||
- Emil Kjelsrud
|
||||
- Erik Skultety
|
||||
- François M
|
||||
- George Crabtree
|
||||
- Gerrit-K
|
||||
- Greg Allen
|
||||
- Guilhem Saurel
|
||||
- Guillermo Rauch
|
||||
- Gunnlaugur Thor Briem
|
||||
- Hasse Ramlev Hansen
|
||||
- Heiko Becker
|
||||
- Hugo Ruiz-Mireles
|
||||
- Isaac Mungai
|
||||
- J.C. Yamokoski
|
||||
- Jack O. Wasey
|
||||
- James Manning
|
||||
- James Zhu
|
||||
- Jan Krueger
|
||||
- Jared Baur
|
||||
- Jarod Stewart
|
||||
- Jason Young
|
||||
- Jens K. Mueller
|
||||
- Jeremy Lin
|
||||
- Jesse Sipprell
|
||||
- Jianjin Fan
|
||||
- Jobin Kurian
|
||||
- Johannes Ewald
|
||||
- John Evans
|
||||
- John Hoffmann
|
||||
- Jon Ander Peñalba
|
||||
- Josh McKinney
|
||||
- Joshua Appelman
|
||||
- Justin Dugger
|
||||
- José María Gutiérrez @TheTechOddBug
|
||||
- Katrin Leinweber
|
||||
- Kevin Woo
|
||||
- Konstantin Schukraft
|
||||
- Kurban Mallachiev
|
||||
- Leandro López
|
||||
- Lone
|
||||
- Luis Miguel Hernanz
|
||||
- Maarten Winter
|
||||
- Marco Glasbergen
|
||||
- Matan Rosenberg
|
||||
- Mathieu D. (MatToufoutu)
|
||||
- Matt
|
||||
- Matt Colyer
|
||||
- Matt Headley
|
||||
- Matthew Avant
|
||||
- Mattias Andersson
|
||||
- Michael Komitee
|
||||
- Michael Matuzak
|
||||
- Michele Bologna
|
||||
- Montak Oleg
|
||||
- Moritz Grauel
|
||||
- Nate Eagleson
|
||||
- Nate Fischer
|
||||
- Nathan Rajlich
|
||||
- Nick Campbell
|
||||
- Nick Payne
|
||||
- Nicolas Kosinski
|
||||
- Niklas Fiekas
|
||||
- Oliver Kopp
|
||||
- Orestis
|
||||
- Peter Bittner
|
||||
- Piotr Górski
|
||||
- Prayag Verma
|
||||
- Przemek Kitszel
|
||||
- R. Martinho Fernandes
|
||||
- Raphael Fleischlin
|
||||
- Rasmus Wriedt Larsen
|
||||
- René
|
||||
- Revisor
|
||||
- Ricardo Fernández Serrata
|
||||
- Riceball LEE
|
||||
- Rob Kennedy
|
||||
- Robin von Bülow
|
||||
- Russ
|
||||
- Ryan Bohn
|
||||
- Sachin Gupta
|
||||
- Sam Thursfield
|
||||
- Sandro
|
||||
- Sascha Andres
|
||||
- SchleimKeim
|
||||
- Sean Molenaar
|
||||
- Xavier Krantz
|
||||
- Oliver Kopp
|
||||
- Dominic Barnes
|
||||
- Edward Betts
|
||||
- Luis Miguel Hernanz
|
||||
- Sebastian Gniazdowski
|
||||
- Sebastián Mancilla
|
||||
- Simon Tate
|
||||
- Stefan VanBuren
|
||||
- Stephen Mathieson
|
||||
- Steve Mao
|
||||
- Stu Feldt
|
||||
- SukkaW
|
||||
- Tim Preston
|
||||
- Timothy Hwang
|
||||
- Marco Glasbergen
|
||||
- Daniel Lublin
|
||||
- Matt
|
||||
- Sam Bostock
|
||||
- Tin Lai
|
||||
- Todd Wolfson
|
||||
- Tom Andrade
|
||||
- Tom Ashworth
|
||||
- Tony
|
||||
- TweeKane
|
||||
- Valérian Galliat
|
||||
- Vitaly Chikunov
|
||||
- Wei Lee
|
||||
- Wei Wu
|
||||
- Wen Sun
|
||||
- Wiktor Żurawik
|
||||
- Xavier Krantz
|
||||
- Xiaopei Li
|
||||
- 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>
|
||||
* Bash 3.2+ (If you aren't sure, see [the Bash changelog](http://tldp.org/LDP/abs/html/bash2.html))
|
||||
* Git 2.1+
|
||||
Thanks for contributing! Please read this document before you make a PR.
|
||||
|
||||
<sup>*</sup>If you aren't able to test your new command on a platform,
|
||||
make that clear in your PR and someone else may be able to test it on their system.
|
||||
## Supported Platforms
|
||||
|
||||
## 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`.
|
||||
3. Don't forget to introduce it in `Commands.md`.
|
||||
4. Update `./etc/git-extras-completion.zsh`. Just follow existing code.
|
||||
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.
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
Please follow the suggestion in `./.github/PULL_REQUEST_TEMPLATE.md`.
|
||||
|
|
|
|||
453
Commands.md
453
Commands.md
|
|
@ -1,53 +1,62 @@
|
|||
|
||||
- [`git abort`](#git-abort)
|
||||
- [`git alias`](#git-alias)
|
||||
- [`git archive-file`](#git-archive-file)
|
||||
- [`git authors`](#git-authors)
|
||||
- [`git back`](#git-back)
|
||||
- [`git bug`](#git-featurerefactorbugchore)
|
||||
- [`git browse`](#git-browse)
|
||||
- [`git browse-ci`](#git-browse-ci)
|
||||
- [`git bulk`](#git-bulk)
|
||||
- [`git brv`](#git-brv)
|
||||
- [`git changelog`](#git-changelog)
|
||||
- [`git chore`](#git-featurerefactorbugchore)
|
||||
- [`git clear`](#git-clear)
|
||||
- [`git clear-soft`](#git-clear-soft)
|
||||
- [`git coauthor`](#git-coauthor)
|
||||
- [`git commits-since`](#git-commits-since)
|
||||
- [`git continue`](#git-continue)
|
||||
- [`git contrib`](#git-contrib)
|
||||
- [`git count`](#git-count)
|
||||
- [`git cp`](#git-cp)
|
||||
- [`git create-branch`](#git-create-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-squashed-branches`](#git-delete-squashed-branches)
|
||||
- [`git delete-submodule`](#git-delete-submodule)
|
||||
- [`git delete-tag`](#git-delete-tag)
|
||||
- [`git delta`](#git-delta)
|
||||
- [`git effort`](#git-effort)
|
||||
- [`git extras`](#git-extras)
|
||||
- [`git feature`](#git-featurerefactorbugchore)
|
||||
- [`git feature`](#git-feature)
|
||||
- [`git force-clone`](#git-force-clone)
|
||||
- [`git fork`](#git-fork)
|
||||
- [`git fresh-branch`](#git-fresh-branch)
|
||||
- [`git get`](#git-get)
|
||||
- [`git gh-pages`](#git-gh-pages)
|
||||
- [`git graft`](#git-graft)
|
||||
- [`git guilt`](#git-guilt)
|
||||
- [`git ignore`](#git-ignore)
|
||||
- [`git ignore-io`](#git-ignore-io)
|
||||
- [`git info`](#git-info)
|
||||
- [`git line-summary`](#git-line-summary)
|
||||
- [`git local-commits`](#git-local-commits)
|
||||
- [`git lock`](#git-lock)
|
||||
- [`git locked`](#git-locked)
|
||||
- [`git magic`](#git-magic)
|
||||
- [`git merge-into`](#git-merge-into)
|
||||
- [`git merge-repo`](#git-merge-repo)
|
||||
- [`git missing`](#git-missing)
|
||||
- [`git mr`](#git-mr)
|
||||
- [`git obliterate`](#git-obliterate)
|
||||
- [`git paste`](#git-paste)
|
||||
- [`git pr`](#git-pr)
|
||||
- [`git psykorebase`](#git-psykorebase)
|
||||
- [`git pull-request`](#git-pull-request)
|
||||
- [`git reauthor`](#git-reauthor)
|
||||
- [`git rebase-patch`](#git-rebase-patch)
|
||||
- [`git refactor`](#git-featurerefactorbugchore)
|
||||
- [`git release`](#git-release)
|
||||
- [`git rename-branch`](#git-rename-branch)
|
||||
- [`git rename-file`](#git-rename-file)
|
||||
- [`git rename-tag`](#git-rename-tag)
|
||||
- [`git rename-remote`](#git-rename-remote)
|
||||
- [`git repl`](#git-repl)
|
||||
- [`git reset-file`](#git-reset-file)
|
||||
- [`git root`](#git-root)
|
||||
|
|
@ -66,6 +75,9 @@
|
|||
- [`git touch`](#git-touch)
|
||||
- [`git undo`](#git-undo)
|
||||
- [`git unlock`](#git-unlock)
|
||||
- [`git utimes`](#git-utimes)
|
||||
- [`git unwip`](#git-unwip)
|
||||
- [`git wip`](#git-wip)
|
||||
|
||||
## git extras
|
||||
|
||||
|
|
@ -74,7 +86,7 @@ The main `git-extras` command.
|
|||
Output the current `--version`:
|
||||
|
||||
```bash
|
||||
$ git extras
|
||||
$ git extras --version
|
||||
```
|
||||
|
||||
List available commands:
|
||||
|
|
@ -90,11 +102,11 @@ $ git extras update
|
|||
```
|
||||
## 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
|
||||
$ 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.
|
||||
|
||||
All of this works with `feature`, `bug`, `chore` or `refactor`.
|
||||
|
||||
## git contrib
|
||||
|
||||
Output `author`'s contributions to a project:
|
||||
|
|
@ -142,17 +152,20 @@ visionmedia (18):
|
|||
|
||||
## git summary
|
||||
|
||||
Outputs a repo summary:
|
||||
Outputs a repo or path summary:
|
||||
|
||||
```bash
|
||||
$ git summary
|
||||
|
||||
project : git-extras
|
||||
repo age : 10 months ago
|
||||
commits : 163
|
||||
active : 60 days
|
||||
files : 93
|
||||
authors :
|
||||
project : git-extras
|
||||
repo age : 10 months ago
|
||||
branch : master
|
||||
last active : 3 weeks ago
|
||||
active on : 93 days
|
||||
commits : 163
|
||||
files : 93
|
||||
uncommitted : 3
|
||||
authors :
|
||||
97 Tj Holowaychuk 59.5%
|
||||
37 Jonhnny Weslley 22.7%
|
||||
8 Kenneth Reitz 4.9%
|
||||
|
|
@ -169,8 +182,8 @@ authors :
|
|||
1 Guillermo Rauch 0.6%
|
||||
```
|
||||
|
||||
This command can also take a *commitish*, and will print a summary for commits in
|
||||
the commitish range:
|
||||
This command can also take a *committish*, and will print a summary for commits in
|
||||
the committish range:
|
||||
|
||||
```bash
|
||||
$ git summary v42..
|
||||
|
|
@ -213,9 +226,19 @@ project : git-extras
|
|||
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
|
||||
|
||||
|
|
@ -225,8 +248,6 @@ project : git-extras
|
|||
node (master): git effort --above 15 {src,lib}/*
|
||||
```
|
||||
|
||||

|
||||
|
||||
If you wish to ignore files with commits `<=` a value you may use `--above`:
|
||||
|
||||
```
|
||||
|
|
@ -255,17 +276,40 @@ $ git effort bin/* lib/*
|
|||
|
||||
```bash
|
||||
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 --addcurrent <ws-name>
|
||||
git bulk --purge
|
||||
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
|
||||
$ 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`
|
||||
|
|
@ -279,6 +323,7 @@ $ git bulk --addcurrent personal
|
|||
```bash
|
||||
$ git bulk --listall
|
||||
bulkworkspaces.personal /Users/niklasschlimm/workspaces/personal
|
||||
|
||||
```
|
||||
|
||||
Run a git command on the repositories of the current workspace:
|
||||
|
|
@ -318,21 +363,78 @@ $ git bulk --removeworkspace personal
|
|||
$ 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 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
|
||||
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
|
||||
$ git repl
|
||||
git version 2.9.2
|
||||
git-extras version 3.0.0
|
||||
type 'ls' to ls files below current directory,
|
||||
'!command' to execute any command or just 'subcommand' to execute any git subcommand
|
||||
git version 2.34.1
|
||||
git-extras version 7.3.0
|
||||
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.
|
||||
|
||||
git (master)> ls-files
|
||||
History.md
|
||||
|
|
@ -351,6 +453,30 @@ Straight from the shell!
|
|||
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
|
||||
|
||||
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
|
||||
$ git rename-branch new-name old-name
|
||||
$ git rename-branch old-name new-name
|
||||
|
||||
# renames current branch
|
||||
$ 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
|
||||
|
||||
Rename a tag (locally and remotely).
|
||||
|
|
@ -509,6 +649,19 @@ $ git tag
|
|||
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
|
||||
|
||||
|
|
@ -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
|
||||
* `-l` or `--local` to show just the local file
|
||||
* `-p` or `--private` to show just the repository's file
|
||||
|
||||
```bash
|
||||
$ git ignore -g
|
||||
|
|
@ -620,14 +774,14 @@ build
|
|||
|
||||
## 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.
|
||||
|
||||
```bash
|
||||
$ git ignore-io vim
|
||||
|
||||
# Created by https://www.gitignore.io/api/vim
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/vim
|
||||
|
||||
### Vim ###
|
||||
[._]*.s[a-w][a-z]
|
||||
|
|
@ -688,6 +842,13 @@ $ git info
|
|||
myBranch
|
||||
* 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:
|
||||
|
||||
commit e3952df2c172c6f3eb533d8d0b1a6c77250769a7
|
||||
|
|
@ -695,8 +856,6 @@ $ git info
|
|||
|
||||
Added git-info command.
|
||||
|
||||
Type 'git log' for more commits, or 'git show <commit id>' for full commit details.
|
||||
|
||||
## Configuration (.git/config):
|
||||
|
||||
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 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
|
||||
|
||||
Create local branch `name`:
|
||||
|
|
@ -766,6 +933,21 @@ Delete local and remote tag `name`:
|
|||
$ 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
|
||||
|
||||
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
|
||||
|
||||
Create empty local branch `name`:
|
||||
Create empty local branch `docs`:
|
||||
|
||||
```bash
|
||||
$ 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
|
||||
|
||||
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.
|
||||
|
||||
Opens the file in `$EDITOR` when set.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Updating AUTHORS file:
|
||||
|
||||
|
|
@ -880,15 +1080,6 @@ nickl-
|
|||
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
|
||||
|
||||
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.
|
||||
|
||||
For example,
|
||||
```bash
|
||||
$ git sed 'this' 'that'
|
||||
$ git sed 'this' 'that' g
|
||||
$ git sed 'this' 'that' -- path/ path2/
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
|
@ -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.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
$ git missing master
|
||||
|
|
@ -1173,6 +1379,12 @@ $ git missing 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
|
||||
|
||||
Lock a local file `filename`:
|
||||
|
|
@ -1216,8 +1428,8 @@ $ git reset-file .htaccess dc82b19
|
|||
|
||||
## git mr
|
||||
|
||||
Checks out a merge request from GitLab. Usage: `git mr <ID|URL> [REMOTE]`.
|
||||
Default remote is `origin`.
|
||||
Checks out a merge request from GitLab, or a pull request from Forgejo/Codeberg.
|
||||
Usage: `git mr <ID|URL> [REMOTE]`. Default remote is `origin`.
|
||||
|
||||
``` bash
|
||||
$ git mr 51
|
||||
|
|
@ -1229,15 +1441,48 @@ Switched to branch 'mr/51'
|
|||
With full URL, the head is fetched from a temporary remote pointing to the base URL.
|
||||
|
||||
``` bash
|
||||
$ git mr https://gitlab.com/owner/repository/merge_requests/51
|
||||
$ git mr https://gitlab.com/owner/repository/merge_requests/51
|
||||
From gitlab.com:owner/repository
|
||||
* [new ref] refs/merge-requests/51/head -> 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
|
||||
`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
|
||||
|
||||
Checks out a pull request from GitHub
|
||||
|
|
@ -1386,3 +1631,99 @@ Sync local branch with origin/master
|
|||
```bash
|
||||
$ 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
|
||||
==================
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
```bash
|
||||
|
|
@ -15,6 +39,20 @@ $ sudo $apt_pref 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))
|
||||
|
||||
```bash
|
||||
|
|
@ -33,7 +71,11 @@ $ sudo apt-get install 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
|
||||
$ 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.
|
||||
|
||||
### Arch Linux
|
||||
|
||||
* [git-extras](https://aur.archlinux.org/packages/git-extras/)
|
||||
* [git-extras-git](https://aur.archlinux.org/packages/git-extras-git/)
|
||||
|
||||
### Windows
|
||||
|
||||
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.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/tj/git-extras.git
|
||||
# checkout the latest tag (optional)
|
||||
|
|
@ -63,16 +111,24 @@ in `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`
|
||||
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
|
||||
|
||||
Use the instructions to build from source below. Make sure you are using `gmake` (GNU `make`) instead of `make`.
|
||||
|
||||
## 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.
|
||||
|
||||
```bash
|
||||
|
|
@ -88,7 +144,7 @@ alternate location, specify a `PREFIX` when calling `make`.
|
|||
|
||||
```bash
|
||||
# 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
|
||||
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
|
||||
specify `SYSCONFDIR` in addition to `PREFIX`.
|
||||
|
||||
```
|
||||
```sh
|
||||
$ sudo make install PREFIX=/usr/local SYSCONFDIR=/etc
|
||||
```
|
||||
|
||||
|
|
@ -109,9 +165,26 @@ See the Makefile for details on advanced configuration options.
|
|||
One-liner:
|
||||
|
||||
```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
|
||||
|
||||
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`!
|
||||
|
||||
[1]: http://sourceforge.net/projects/msys2/
|
||||
[1]: https://sourceforge.net/projects/msys2/
|
||||
|
|
|
|||
87
Makefile
87
Makefile
|
|
@ -1,9 +1,28 @@
|
|||
PREFIX ?= /usr/local
|
||||
BINPREFIX ?= "$(PREFIX)/bin"
|
||||
MANPREFIX ?= "$(PREFIX)/share/man/man1"
|
||||
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-*)
|
||||
MANS = $(wildcard man/git-*.md)
|
||||
MAN_BINS = $(filter-out man/git-extras.md, $(MANS))
|
||||
MAN_HTML = $(MANS:.md=.html)
|
||||
MAN_PAGES = $(MANS:.md=.1)
|
||||
CODE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
|
|
@ -17,26 +36,33 @@ default: install
|
|||
|
||||
docs: $(MAN_HTML) $(MAN_PAGES)
|
||||
|
||||
install:
|
||||
check:
|
||||
@echo "Check dependencies before installation"
|
||||
@./check_dependencies.sh
|
||||
@echo
|
||||
|
||||
install: check
|
||||
@if [ "$(INSTALL_VIA)" = brew ]; then \
|
||||
git apply brew-release.patch || { echo "Can't apply brew release patch"; exit 1; } \
|
||||
fi
|
||||
@mkdir -p $(DESTDIR)$(MANPREFIX)
|
||||
@mkdir -p $(DESTDIR)$(BINPREFIX)
|
||||
@echo "... installing bins to $(DESTDIR)$(BINPREFIX)"
|
||||
@echo "... installing man pages to $(DESTDIR)$(MANPREFIX)"
|
||||
mkdir -p $(DESTDIR)$(MANPREFIX)
|
||||
mkdir -p $(DESTDIR)$(BINPREFIX)
|
||||
@echo '... installing bins to '$(DESTDIR)$(BINPREFIX)
|
||||
@echo '... installing man pages to '$(DESTDIR)$(MANPREFIX)
|
||||
$(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 775 $(TEMPFILE)
|
||||
$(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), \
|
||||
disable=''; \
|
||||
if test ! -z "$(filter $(COMMAND), $(EXISTED_ALIASES))"; then \
|
||||
read -p "$(COMMAND) conflicts with an alias, still install it and disable the alias? [y/n]" answer; \
|
||||
test "$$answer" = 'n' -o "$$answer" = 'N' && disable="true"; \
|
||||
should_install='yes'; \
|
||||
if [ ! -z "$(filter $(COMMAND), $(EXISTED_ALIASES))" ] && [ "$$SKIP_CONFLICT_CHECK" != yes ]; then \
|
||||
read -p "$(COMMAND) conflicts with an alias, still install it? [y/n]: " answer; \
|
||||
if [ "$$answer" = 'n' ] || [ "$$answer" = 'N' ]; then \
|
||||
should_install="no"; \
|
||||
fi; \
|
||||
fi; \
|
||||
if test -z "$$disable"; then \
|
||||
if [ "$$should_install" = 'yes' ]; then \
|
||||
echo "... installing $(COMMAND)"; \
|
||||
head -1 bin/$(COMMAND) > $(TEMPFILE); \
|
||||
cat $(LIB) >> $(TEMPFILE); \
|
||||
|
|
@ -56,13 +82,35 @@ install:
|
|||
cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX); \
|
||||
echo "cp -f man/git-*.1 $(DESTDIR)$(MANPREFIX)"; \
|
||||
fi
|
||||
@mkdir -p $(DESTDIR)$(SYSCONFDIR)/bash_completion.d
|
||||
cp -f etc/bash_completion.sh $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/git-extras
|
||||
@mkdir -p $(COMPL_DIR)
|
||||
cp -f etc/bash_completion.sh $(COMPL_DIR)/git-extras
|
||||
@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"
|
||||
@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 \
|
||||
--manual "Git Extras" \
|
||||
--html \
|
||||
|
|
@ -84,7 +132,7 @@ uninstall:
|
|||
echo "... uninstalling $(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
|
||||
|
||||
|
|
@ -92,4 +140,7 @@ docclean:
|
|||
rm -f man/*.1
|
||||
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
|
||||
|
||||
[](https://gitter.im/tj/git-extras)
|
||||
|
||||
Little git extras.
|
||||
|
||||
## 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
|
||||
|
||||
See [Installation](Installation.md) page.
|
||||
See the [Installation](Installation.md) page.
|
||||
|
||||
## 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
|
||||
|
||||
## 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
|
||||
|
||||
options=""
|
||||
|
||||
usage() {
|
||||
cat <<HERE
|
||||
usage: git alias # list all aliases
|
||||
or: git alias <search-pattern> # show aliases matching pattern
|
||||
or: git alias <alias-name> <command> # alias a command
|
||||
usage: git alias [options] # list all aliases
|
||||
or: git alias [options] <search-pattern> # show aliases matching pattern
|
||||
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
|
||||
}
|
||||
|
||||
if [[ "$1" == "--local" || "$1" == "--global" ]]; then
|
||||
options=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
case $# in
|
||||
0) git config --get-regexp 'alias.*' | sed 's/^alias\.//' | sed 's/[ ]/ = /' | sort ;;
|
||||
1) git alias | grep -e "$1" ;;
|
||||
2) git config --global alias."$1" "$2" ;;
|
||||
0)
|
||||
if [[ -z "$options" ]]; then
|
||||
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 ;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -1,34 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# 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
|
||||
# output filename
|
||||
ARCHIVE_NAME=$(basename $(pwd))
|
||||
ARCHIVE_NAME=$(basename "$(git rev-parse --show-toplevel)")
|
||||
|
||||
if [[ $BRANCH = tags* ]]; then
|
||||
BRANCH=$(git describe)
|
||||
echo Building for tag \"$BRANCH\"
|
||||
FILENAME=$ARCHIVE_NAME.$BRANCH.zip
|
||||
BRANCH=$(git describe)
|
||||
echo Building for tag "\"$BRANCH\""
|
||||
FILENAME=$ARCHIVE_NAME.$BRANCH.zip
|
||||
else
|
||||
echo Building archive on branch \"$BRANCH\"
|
||||
# get a version string, so archives will not be overwritten when creating
|
||||
# many of them
|
||||
VERSION=$(git describe --always --long)
|
||||
# if not on master append branch name into the filename
|
||||
if [ "$BRANCH" = "master" ]; then
|
||||
FILENAME=$ARCHIVE_NAME.$VERSION.zip
|
||||
else
|
||||
FILENAME=$ARCHIVE_NAME.$VERSION.$BRANCH.zip
|
||||
fi
|
||||
echo Building archive on branch "\"$BRANCH\""
|
||||
# get a version string, so archives will not be overwritten when creating
|
||||
# many of them
|
||||
VERSION=$(git describe --always --long)
|
||||
# if not on master append branch name into the filename
|
||||
if [ "$BRANCH" = "$(git_extra_default_branch)" ]; then
|
||||
FILENAME=$ARCHIVE_NAME.$VERSION.zip
|
||||
else
|
||||
FILENAME=$ARCHIVE_NAME.$VERSION.$BRANCH.zip
|
||||
fi
|
||||
fi
|
||||
|
||||
# rename invalid chars for the file path
|
||||
FILENAME=${FILENAME//\//-}
|
||||
FILENAME=${FILENAME//\\/-}
|
||||
# combine path and filename
|
||||
OUTPUT=$(pwd)/$FILENAME
|
||||
OUTPUT=$PWD/$FILENAME
|
||||
|
||||
# building archive
|
||||
git archive --format zip --output $OUTPUT $BRANCH
|
||||
git archive --format zip --output "$OUTPUT" "$BRANCH"
|
||||
|
||||
# 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
|
||||
NO_EMAIL=false
|
||||
FILE=""
|
||||
EDITOR=$(git var GIT_EDITOR)
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
|
|
@ -22,9 +21,9 @@ done
|
|||
|
||||
if ! $LIST; then
|
||||
FILE=$1
|
||||
if test "$FILE" = ""; then
|
||||
FILE=$(ls | egrep 'authors|contributors' -i|head -n1)
|
||||
if test "$FILE" = ""; then
|
||||
if [ -z "$FILE" ]; then
|
||||
FILE=$(find . -mindepth 1 -maxdepth 1 \( -iname '*authors*' -o -iname '*contributors*' \) | head -n1)
|
||||
if [ -z "$FILE" ]; then
|
||||
FILE='AUTHORS'
|
||||
fi
|
||||
fi
|
||||
|
|
@ -36,11 +35,11 @@ fi
|
|||
|
||||
authors() {
|
||||
if $NO_EMAIL; then
|
||||
# eamil will be used to uniq authors.
|
||||
git shortlog -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++' \
|
||||
# email will be used to uniq authors.
|
||||
git shortlog HEAD -sne | awk '{$1=""; sub(" ", ""); print}' | awk -F'<' '!x[$1]++' | awk -F'<' '!x[$2]++' \
|
||||
| awk -F'<' '{gsub(/ +$/, "", $1); print $1}'
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +50,5 @@ authors() {
|
|||
if $LIST; then
|
||||
authors
|
||||
else
|
||||
authors >> $FILE
|
||||
test -n "$EDITOR" && $EDITOR $FILE
|
||||
authors >> "$FILE"
|
||||
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 "$@"
|
||||
189
bin/git-bulk
189
bin/git-bulk
|
|
@ -1,124 +1,185 @@
|
|||
#!/usr/bin/env bash
|
||||
invers=$(tput rev)
|
||||
inverse=$(tput rev)
|
||||
reset=$(tput sgr0)
|
||||
txtbld=$(tput bold)
|
||||
bldred=${txtbld}$(tput setaf 1)
|
||||
txtbld=$(tput bold)
|
||||
bldred=${txtbld}$(tput setaf 1)
|
||||
|
||||
# default option settings
|
||||
guardedmode=false
|
||||
singlemode=false
|
||||
allwsmode=false
|
||||
quiet=false
|
||||
no_follow_symlinks=false
|
||||
no_follow_hidden=false
|
||||
|
||||
#
|
||||
# print usage message
|
||||
#
|
||||
usage() {
|
||||
echo 1>&2 "usage: git bulk [-g] ([-a]|[-w <ws-name>]) <git command>"
|
||||
echo 1>&2 " git bulk --addworkspace <ws-name> <ws-root-directory>"
|
||||
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> (--from <URL or file>)"
|
||||
echo 1>&2 " git bulk --removeworkspace <ws-name>"
|
||||
echo 1>&2 " git bulk --addcurrent <ws-name>"
|
||||
echo 1>&2 " git bulk --purge"
|
||||
echo 1>&2 " git bulk --listall"
|
||||
}
|
||||
|
||||
cdfail() {
|
||||
echo 1>&2 "failed to change directory: $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 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
|
||||
function addcurrent { git config --global bulkworkspaces."$wsname" "$PWD"; }
|
||||
addcurrent() { git config --global bulkworkspaces."$wsname" "$PWD"; }
|
||||
|
||||
# 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
|
||||
function purge { git config --global --remove-section bulkworkspaces; }
|
||||
purge() { git config --global --remove-section bulkworkspaces; }
|
||||
|
||||
# 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
|
||||
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
|
||||
echo -n "${invers}git $gitcommand${reset} -> execute here (y/n)? "
|
||||
read -n 1 -r </dev/tty; echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then atomicExecution; fi
|
||||
echo 1>&2 -n " Execute command here (y/n)? "
|
||||
read -n 1 -r </dev/tty; echo 1>&2
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
git "$@"
|
||||
fi
|
||||
else
|
||||
atomicExecution
|
||||
git "$@"
|
||||
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
|
||||
function checkGitCommand () {
|
||||
checkGitCommand () {
|
||||
if git help -a | grep -o -q "\b${corecommand}\b"; then
|
||||
echo "Core command \"$corecommand\" accepted."
|
||||
echo 1>&2 "Core command \"$corecommand\" accepted."
|
||||
else
|
||||
if git config --get-regexp alias | grep -o -q "\.${corecommand} "; then
|
||||
echo "Alias ${corecommand} accepted."
|
||||
echo 1>&2 "Alias ${corecommand} accepted."
|
||||
else
|
||||
usage && echo "error: unknown GIT command: $corecommand" && exit 1
|
||||
usage && echo 1>&2 "error: unknown GIT command: $corecommand" && exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# check if workspace name is registered
|
||||
function checkWSName () {
|
||||
while read workspace; do
|
||||
checkWSName () {
|
||||
while read -r workspace; do
|
||||
parseWsName "$workspace"
|
||||
if [[ $rwsname == "$wsname" ]]; then return; fi
|
||||
done <<< "$(echo "$(listall)")"
|
||||
done <<< "$(listall)"
|
||||
# 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
|
||||
function parseWsName () {
|
||||
parseWsName () {
|
||||
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#* }
|
||||
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%% *}
|
||||
}
|
||||
|
||||
# detects the wsname of the current directory
|
||||
function wsnameToCurrent () {
|
||||
while read workspace; do
|
||||
wsnameToCurrent () {
|
||||
while read -r workspace; do
|
||||
if [ -z "$workspace" ]; then continue; fi
|
||||
parseWsName "$workspace"
|
||||
if echo "$PWD" | grep -o -q "$rwsdir"; then wsname="$rwsname" && return; fi
|
||||
done <<< "$(echo "$(listall)")"
|
||||
done <<< "$(listall)"
|
||||
# when here then not in workspace dir
|
||||
echo "error: you are not in a workspace directory. your registered workspaces are:" && \
|
||||
wslist="$(echo "$(listall)")" && echo "${wslist:-'<no workspaces defined yet>'}" && exit 1
|
||||
echo 1>&2 "error: you are not in a workspace directory. your registered workspaces are:" && \
|
||||
wslist="$(listall)" && echo 1>&2 "${wslist:-'<no workspaces defined yet>'}" && exit 1
|
||||
}
|
||||
|
||||
# helper to check number of arguments
|
||||
function allowedargcount () {
|
||||
( test "$paramcount" -ne "$1" ) && ( usage && echo 1>&2 "error: wrong number of arguments" && exit 1 )
|
||||
# helper to check number of arguments.
|
||||
allowedargcount () {
|
||||
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
|
||||
function executBulkOp () {
|
||||
executBulkOp () {
|
||||
checkGitCommand
|
||||
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"
|
||||
if [[ -n $wsname ]] && [[ $rwsname != "$wsname" ]]; then continue; fi
|
||||
eval cd "\"$rwsdir\""
|
||||
local actual=$(pwd)
|
||||
echo "Executing bulk operation in workspace ${invers}$actual${reset}"
|
||||
eval find . -name ".git" | while read line; do
|
||||
cd "$rwsdir" || exit 1
|
||||
local actual=$PWD
|
||||
[ "${quiet?}" != "true" ] && echo 1>&2 "Executing bulk operation in workspace ${inverse}$actual${reset}"
|
||||
|
||||
# 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
|
||||
eval cd "\"$gitrepodir\"" # into git repo location
|
||||
local curdir=$(pwd)
|
||||
local leadingpath=${curdir#${actual}}
|
||||
echo "Current repository: ${leadingpath%/*}/${bldred}${curdir##*/}${reset}"
|
||||
guardedExecution
|
||||
eval cd "\"$rwsdir\"" # back to origin location of last find command
|
||||
done
|
||||
done
|
||||
cd "$gitrepodir" || exit 1 # into git repo location
|
||||
local curdir=$PWD
|
||||
local leadingpath=${curdir#"${actual}"}
|
||||
# do not execute if we do not want to consider a ".git" directory under a hidden directory
|
||||
if [ $no_follow_hidden = false ] || ! [[ "$leadingpath" =~ "/." ]]; then
|
||||
guardedExecution "$@"
|
||||
fi
|
||||
cd "$rwsdir" || exit 1 # back to origin location of last find command
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
paramcount="${#}"
|
||||
|
|
@ -129,22 +190,27 @@ if [[ $paramcount -le 0 ]]; then usage; fi
|
|||
# parse command parameters
|
||||
while [ "${#}" -ge 1 ] ; do
|
||||
case "$1" in
|
||||
--quiet|-q) quiet='true' ;;
|
||||
--listall|--purge)
|
||||
butilcommand="${1:2}" && break ;;
|
||||
--removeworkspace|--addcurrent|--addworkspace)
|
||||
butilcommand="${1:2}" && wsname="$2" && wsdir="$3" && break ;;
|
||||
-a)
|
||||
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)
|
||||
allwsmode=true ;;
|
||||
-g)
|
||||
-g)
|
||||
guardedmode=true ;;
|
||||
-w)
|
||||
-w)
|
||||
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 ;;
|
||||
*) # git core commands
|
||||
butilcommand="executBulkOp" && corecommand="$1" && gitcommand="$@" && break ;;
|
||||
butilcommand="executBulkOp" && corecommand="$1" && gitcommand="$*" && break ;;
|
||||
esac && shift
|
||||
done
|
||||
|
||||
|
|
@ -152,13 +218,14 @@ done
|
|||
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 $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
|
||||
case $butilcommand in
|
||||
listall|purge) allowedargcount 1;;
|
||||
addcurrent|removeworkspace) allowedargcount 2;;
|
||||
addworkspace) allowedargcount 3;;
|
||||
addworkspace) allowedargcount 3 5;;
|
||||
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)"
|
||||
PROGNAME="git-changelog"
|
||||
|
||||
git_editor() {
|
||||
if test -z "${GIT_EDITOR:+set}"
|
||||
then
|
||||
GIT_EDITOR="$(git var GIT_EDITOR)" || return $?
|
||||
fi
|
||||
|
||||
eval "$GIT_EDITOR" '"$@"'
|
||||
}
|
||||
|
||||
_usage() {
|
||||
cat << EOF
|
||||
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.
|
||||
|
||||
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
|
||||
-t, --tag Tag label to use for most-recent (untagged) commits
|
||||
-f, --final-tag Newest 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
|
||||
-m, --merges-only Only uses merge commits (uses both subject and body of commit)
|
||||
-p, --prune-old Replace existing Changelog entirely with new content
|
||||
|
|
@ -145,6 +155,8 @@ _fetchCommitRange() {
|
|||
local start_tag="$2"
|
||||
local final_tag="$3"
|
||||
|
||||
# This shellcheck disable is applied to the whole if-body
|
||||
# shellcheck disable=SC2086
|
||||
if [[ "$list_all" == true ]]; then
|
||||
git log $GIT_LOG_OPTS --pretty=format:"${CUR_GIT_LOG_FORMAT}"
|
||||
elif [[ -n "$final_tag" && "$start_tag" == "null" ]]; then
|
||||
|
|
@ -160,7 +172,7 @@ _formatCommitPlain() {
|
|||
local start_tag="$1"
|
||||
local final_tag="$2"
|
||||
|
||||
printf "%s" "$(_fetchCommitRange "false" "$start_tag" "$final_tag")"
|
||||
printf "%s\n" "$(_fetchCommitRange "false" "$start_tag" "$final_tag")"
|
||||
}
|
||||
|
||||
_formatCommitPretty() {
|
||||
|
|
@ -187,7 +199,8 @@ commitList() {
|
|||
local title_tag="$1"; shift
|
||||
local start_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 title_date="$(date +'%Y-%m-%d')"
|
||||
local tags_list=()
|
||||
|
|
@ -195,6 +208,19 @@ commitList() {
|
|||
local defaultIFS="$IFS"
|
||||
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:
|
||||
#
|
||||
|
|
@ -218,7 +244,7 @@ commitList() {
|
|||
# fetch our tags
|
||||
local _ref _date _tag _tab='%x09'
|
||||
local _tag_regex='tag: *'
|
||||
while IFS=$'\t' read _ref _date _tag; do
|
||||
while IFS=$'\t' read -r _ref _date _tag; do
|
||||
[[ -z "${_tag}" ]] && continue
|
||||
# strip out tags form ()
|
||||
# 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
|
||||
local __curr_tag="${tags_list_keys[$i]}"
|
||||
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##*=>}"
|
||||
|
||||
# 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
|
||||
if [[ -n "$start_tag" ]]; then
|
||||
[[ "$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
|
||||
|
||||
# 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 start_tag="$2"
|
||||
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() {
|
||||
|
|
@ -325,9 +369,11 @@ commitListPretty() {
|
|||
local title_tag="$2"
|
||||
local start_tag="$3"
|
||||
local final_tag="$4"
|
||||
local start_commit="$5"
|
||||
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() {
|
||||
|
|
@ -341,8 +387,8 @@ _exit() {
|
|||
# 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.
|
||||
# Why don't we just use the last column?
|
||||
# Because the body of CMD column may contain space and be treated as multiple fileds.
|
||||
pid_list=( $(ps -f |
|
||||
# Because the body of CMD column may contain space and be treated as multiple fields.
|
||||
pid_list=( $(ps -f |
|
||||
awk -v ppid=$$ 'NR == 1 {
|
||||
COL_NUM = NF
|
||||
}
|
||||
|
|
@ -357,7 +403,7 @@ _exit() {
|
|||
local _pid
|
||||
for _pid in "${pid_list[@]}"; do
|
||||
echo "killing: ${_pid}"
|
||||
kill -TERM ${_pid}
|
||||
kill -TERM "${_pid}"
|
||||
done
|
||||
|
||||
wait; stty sane; exit 1
|
||||
|
|
@ -374,6 +420,7 @@ main() {
|
|||
"list_style:false"
|
||||
"title_tag:$DEF_TAG_RECENT"
|
||||
"start_tag:"
|
||||
"start_commit:"
|
||||
"final_tag:"
|
||||
"output_file:"
|
||||
"use_stdout:false"
|
||||
|
|
@ -410,6 +457,10 @@ main() {
|
|||
option=( $(_setValueForKeyFakeAssocArray "start_tag" "$2" "${option[*]}") )
|
||||
shift
|
||||
;;
|
||||
--start-commit )
|
||||
option=( $(_setValueForKeyFakeAssocArray "start_commit" "$2" "${option[*]}") )
|
||||
shift
|
||||
;;
|
||||
-n | --no-merges )
|
||||
GIT_LOG_OPTS='--no-merges'
|
||||
CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
|
||||
|
|
@ -435,18 +486,45 @@ main() {
|
|||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
|
||||
# The default log format unless already set
|
||||
[[ -z "$CUR_GIT_LOG_FORMAT" ]] && CUR_GIT_LOG_FORMAT="$GIT_LOG_FORMAT"
|
||||
|
||||
|
||||
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)"
|
||||
if [[ -z "$start_tag" ]]; then
|
||||
_error "Specified start-tag does not exist!"
|
||||
return 1
|
||||
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
|
||||
|
||||
local _tag="$(_valueForKeyFakeAssocArray "final_tag" "${option[*]}")"
|
||||
|
|
@ -462,26 +540,29 @@ main() {
|
|||
#
|
||||
# generate changelog
|
||||
#
|
||||
local tmpfile="$(git_extra_mktemp)"
|
||||
local changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")"
|
||||
local title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")"
|
||||
local tmpfile changelog title_tag
|
||||
tmpfile="$(git_extra_mktemp)"
|
||||
changelog="$(_valueForKeyFakeAssocArray "output_file" "${option[*]}")"
|
||||
title_tag="$(_valueForKeyFakeAssocArray "title_tag" "${option[*]}")"
|
||||
|
||||
if [[ "$(_valueForKeyFakeAssocArray "list_style" "${option[*]}")" == true ]]; then
|
||||
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
|
||||
commitListPlain "true" >> "$tmpfile"
|
||||
else
|
||||
commitListPlain "false" "$start_tag" "$final_tag" >> "$tmpfile"
|
||||
commitListPlain "false" "$start_tag" "$final_tag" \
|
||||
"$start_commit" >> "$tmpfile"
|
||||
fi
|
||||
else
|
||||
if [[ "$(_valueForKeyFakeAssocArray "list_all" "${option[*]}")" == true ]]; then
|
||||
commitListPretty "true" "$title_tag" >> "$tmpfile"
|
||||
else
|
||||
commitListPretty "false" "$title_tag" "$start_tag" "$final_tag" >> "$tmpfile"
|
||||
commitListPretty "false" "$title_tag" "$start_tag" "$final_tag" \
|
||||
"$start_commit" >> "$tmpfile"
|
||||
fi
|
||||
fi
|
||||
|
||||
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
|
||||
changelog="History.md";
|
||||
fi
|
||||
|
|
@ -500,12 +581,11 @@ main() {
|
|||
else
|
||||
cp -f "$tmpfile" "$changelog"
|
||||
rm -f "$tmpfile"
|
||||
# Use `eval` to handle GIT_EDITOR which contains space and options,
|
||||
# like ""C:\Program Files (x86)\Notepad++\notepad++.exe" -multiInst ".
|
||||
# Thanks @JanSchulz to inspire me this solution
|
||||
[[ -n "$GIT_EDITOR" ]] && eval $GIT_EDITOR "$changelog"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
_exit
|
||||
|
||||
if [[ -n "$GIT_EDITOR" ]]; then
|
||||
git_editor "$changelog" || _exit
|
||||
else
|
||||
less "$changelog" || _exit
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
ALIAS=chore
|
||||
git feature -a $ALIAS "$@"
|
||||
|
|
@ -1,7 +1,43 @@
|
|||
#!/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]: "
|
||||
read ans
|
||||
if [ "$ans" == "y" ]
|
||||
then git clean -d -f -x && git reset --hard
|
||||
_usage() {
|
||||
cat << EOF
|
||||
usage: $PROGNAME options
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/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]: "
|
||||
read ans
|
||||
if [ "$ans" == "y" ]
|
||||
read -r answer
|
||||
if [ "$answer" = "y" ]
|
||||
then git clean -d -f && git reset --hard
|
||||
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
|
||||
|
||||
SINCE="last week"
|
||||
test $# -ne 0 && SINCE=$@
|
||||
echo "... commits since $SINCE" >&2
|
||||
git log --pretty='%an - %s' --after="@{$SINCE}"
|
||||
REF=""
|
||||
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
|
||||
|
||||
git shortlog --author="$user"
|
||||
git shortlog --format=format:"%h %s" --author="$user"
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ if test "$1" = "--all"; then
|
|||
echo
|
||||
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
|
||||
|
||||
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
|
||||
do
|
||||
case $1 in
|
||||
|
|
@ -8,33 +16,61 @@ do
|
|||
then
|
||||
REMOTE=$2
|
||||
shift
|
||||
else
|
||||
else
|
||||
REMOTE=origin
|
||||
fi
|
||||
;;
|
||||
--from)
|
||||
if [[ -n $2 ]]; then
|
||||
START_POINT=$2
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
BRANCH=$1
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -z $BRANCH ]] && [[ -n $REMOTE ]]
|
||||
# handle ambiguous `-r` option argument by shift
|
||||
if [[ -z $BRANCH ]] && [[ -n $REMOTE ]]
|
||||
then
|
||||
BRANCH=$REMOTE
|
||||
REMOTE=origin
|
||||
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 ]]
|
||||
then
|
||||
git ls-remote --exit-code $REMOTE &>/dev/null
|
||||
if [ $? = 0 ]
|
||||
stderr=$(git_extra_mktemp)
|
||||
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
|
||||
git push $REMOTE HEAD:refs/heads/$BRANCH
|
||||
git fetch $REMOTE
|
||||
git checkout --track -b $BRANCH $REMOTE/$BRANCH
|
||||
if [[ -n $START_POINT ]]; then
|
||||
git fetch "$REMOTE"
|
||||
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 $?
|
||||
else
|
||||
echo
|
||||
echo " Error connecting to remote '$REMOTE': $REMOTE_ERROR"
|
||||
echo
|
||||
exit $REMOTE_EXIT
|
||||
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
|
||||
|
||||
# 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 "$@"
|
||||
do
|
||||
remote=$(git config branch.$branch.remote)
|
||||
test -z $remote && remote="origin"
|
||||
ref=$(git config branch.$branch.merge)
|
||||
test -z $ref && ref="refs/heads/$branch"
|
||||
remote=$(git config "branch.$branch.remote" || echo "origin")
|
||||
ref=$(git config "branch.$branch.merge" || echo "refs/heads/$branch")
|
||||
|
||||
git branch -D $branch
|
||||
git branch -D "$branch" || true
|
||||
# Avoid deleting local upstream
|
||||
[ "$remote" == "." ] && continue
|
||||
git branch -d -r $remote/$branch
|
||||
git push $remote :$ref
|
||||
[ "$remote" = "." ] && continue
|
||||
git branch -d -r "$remote/$branch" || continue
|
||||
git push "$remote" ":$ref"
|
||||
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
|
||||
|
||||
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" ]
|
||||
then
|
||||
echo "$branches" | xargs -n 1 git branch -d
|
||||
echo "$branches" | xargs git branch -d
|
||||
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
|
||||
|
||||
test -z "$1" && echo "submodule required" 1>&2 && exit 1
|
||||
cd "$(git root)"
|
||||
test ! -f .gitmodules && echo ".gitmodules file not found" 1>&2 && exit 2
|
||||
abort() {
|
||||
error="$1" && shift
|
||||
echo "ERROR: $*" 1>&2
|
||||
test -z "$FORCE" && exit "$error"
|
||||
}
|
||||
|
||||
NAME="$(echo "$1" | sed 's/\/$//g')"
|
||||
test -z \
|
||||
"$(git config --file=.gitmodules submodule."$NAME".url)" \
|
||||
&& echo "submodule not found" 1>&2 && exit 3
|
||||
# Don't abort on failures. This allows to cleanup after a previous failure.
|
||||
[ "$1" = '--force' ] && FORCE=1 && shift
|
||||
|
||||
# 1. Delete the relevant section from .git/config and clean submodule files
|
||||
git submodule deinit -f "$NAME" || exit 4
|
||||
rmdir "$NAME"
|
||||
rm -rf .git/modules/"$NAME"
|
||||
# 2. Delete the relevant line from .gitmodules
|
||||
git config --file=.gitmodules --remove-section submodule."$NAME"
|
||||
git add .gitmodules
|
||||
# 3. Run git rm --cached path_to_submodule
|
||||
git rm --cached -rf "$NAME"
|
||||
# 4. Need to confirm and commit the changes for yourself
|
||||
echo
|
||||
echo "Now submodule $NAME is deleted."
|
||||
echo 'Confirm with `git submodule status` and commit the changes for yourself.'
|
||||
test -z "$1" && abort 1 'Submodule required'
|
||||
cd "$(git root)" || abort 5 'Cannot change to repository root'
|
||||
test ! -f '.gitmodules' && abort 2 '.gitmodules file not found'
|
||||
|
||||
NAME="${1%/}"
|
||||
test -z "$(git config --file='.gitmodules' "submodule.$NAME.url")" \
|
||||
&& abort 3 'Submodule not found'
|
||||
|
||||
# 1. Handle the .git directory
|
||||
# 1.a. Delete the relevant section from .git/config
|
||||
git submodule deinit -f "$NAME" || abort 4 "Failed to deinitialize $NAME"
|
||||
# 1.b. Delete empty submodule directory
|
||||
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
|
||||
|
||||
# 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
|
||||
local_tags=""
|
||||
|
|
@ -13,5 +22,7 @@ do
|
|||
done
|
||||
|
||||
# Delete all the tags
|
||||
# shellcheck disable=SC2086
|
||||
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
|
||||
|
||||
branch=master
|
||||
branch=$(git_extra_default_branch)
|
||||
filter=ACM
|
||||
|
||||
if test $# -eq 1; then
|
||||
|
|
@ -12,4 +12,4 @@ else
|
|||
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() {
|
||||
if [ "$to_tty" = true ]; then
|
||||
if [ $1 -gt 200 ]; then color="$(tputq setaf 1)$(tputq bold)"
|
||||
elif [ $1 -gt 150 ]; then color="$(tputq setaf 1)" # red
|
||||
elif [ $1 -gt 125 ]; then color="$(tputq setaf 2)$(tputq bold)"
|
||||
elif [ $1 -gt 100 ]; then color="$(tputq setaf 2)" # green
|
||||
elif [ $1 -gt 75 ]; then color="$(tputq setaf 5)$(tputq bold)"
|
||||
elif [ $1 -gt 50 ]; then color="$(tputq setaf 5)" # purplish
|
||||
elif [ $1 -gt 25 ]; then color="$(tputq setaf 3)$(tputq bold)"
|
||||
elif [ $1 -gt 10 ]; then color="$(tputq setaf 3)" # yellow
|
||||
if [ "$1" -gt 200 ]; then color="$(tputq setaf 1)$(tputq bold)"
|
||||
elif [ "$1" -gt 150 ]; then color="$(tputq setaf 1)" # red
|
||||
elif [ "$1" -gt 125 ]; then color="$(tputq setaf 2)$(tputq bold)"
|
||||
elif [ "$1" -gt 100 ]; then color="$(tputq setaf 2)" # green
|
||||
elif [ "$1" -gt 75 ]; then color="$(tputq setaf 5)$(tputq bold)"
|
||||
elif [ "$1" -gt 50 ]; then color="$(tputq setaf 5)" # purplish
|
||||
elif [ "$1" -gt 25 ]; then color="$(tputq setaf 3)$(tputq bold)"
|
||||
elif [ "$1" -gt 10 ]; then color="$(tputq setaf 3)" # yellow
|
||||
else color="$(tputq sgr0)" # default color
|
||||
fi
|
||||
else
|
||||
|
|
@ -84,37 +84,38 @@ effort() {
|
|||
local color reset_color commits len dot f_dot i msg active
|
||||
reset_color=""
|
||||
test "$to_tty" = true && reset_color="$(tputq sgr0)"
|
||||
commit_dates=`dates "$path"`
|
||||
[ $? -gt 0 ] && exit 255
|
||||
if ! commit_dates=$(dates "$path"); then
|
||||
exit 255
|
||||
fi
|
||||
|
||||
# Ensure it's not just an empty line
|
||||
if [ -z "`head -c 1 <<<$(echo $commit_dates)`" ]
|
||||
if [ -z "$(head -c 1 <<<"$commit_dates")" ]
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
commits=`wc -l <<<"$(echo "$commit_dates")"`
|
||||
commits=$(wc -l <<<"$commit_dates")
|
||||
color='90'
|
||||
|
||||
# ignore <= --above
|
||||
test $commits -le $above && exit 0
|
||||
test "$commits" -le "$above" && exit 0
|
||||
|
||||
# commits
|
||||
color_for $(( $commits - $above ))
|
||||
color_for $(( commits - above ))
|
||||
len=${#path}
|
||||
dot="."
|
||||
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
|
||||
i=$(($i+1))
|
||||
i=$((i+1))
|
||||
done
|
||||
|
||||
msg=$(printf " ${color}%s %-10d" "$f_dot" $commits)
|
||||
msg=$(printf " ${color}%s %-10d" "$f_dot" "$commits")
|
||||
|
||||
# active days
|
||||
active=`active_days "$commit_dates"`
|
||||
color_for $(( $active - $above ))
|
||||
msg="$msg $(printf "${color} %d${reset_color}\n" $active)"
|
||||
active=$(active_days "$commit_dates")
|
||||
color_for $(( active - above ))
|
||||
msg="$msg $(printf "${color} %d${reset_color}\n" "$active")"
|
||||
echo "$msg"
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +137,16 @@ sort_effort() {
|
|||
clear
|
||||
echo " "
|
||||
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=()
|
||||
|
|
@ -177,50 +187,48 @@ fi
|
|||
args_to_git_log="${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 ...]
|
||||
|
||||
if test "${#paths}" -eq 0; then
|
||||
save_ifs=$IFS
|
||||
IFS=`echo -en "\n\b"`
|
||||
paths=(`git ls-files`)
|
||||
IFS=$(echo -en "\n\b")
|
||||
paths=($(git ls-files))
|
||||
IFS=$save_ifs
|
||||
unset save_ifs
|
||||
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
|
||||
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
|
||||
|
||||
# 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
|
||||
test "$(wc -l $tmp | awk '{print $1}')" -gt 1 && sort_effort
|
||||
test "$(wc -l "$tmp" | awk '{print $1}')" -gt 1 && sort_effort
|
||||
echo
|
||||
|
||||
show_cursor_and_cleanup
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
VERSION="4.5.0"
|
||||
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/master/install.sh"
|
||||
VERSION="7.6.0-dev"
|
||||
INSTALL_SCRIPT="https://raw.githubusercontent.com/tj/git-extras/main/install.sh"
|
||||
|
||||
update() {
|
||||
local bin="$(which git-extras)"
|
||||
local bin="$(command -v git-extras)"
|
||||
local prefix=${bin%/*/*}
|
||||
local orig=$PWD
|
||||
|
||||
|
|
@ -14,10 +14,10 @@ update() {
|
|||
}
|
||||
|
||||
updateForWindows() {
|
||||
local bin="$(which git-extras)"
|
||||
local bin="$(command -v git-extras)"
|
||||
local prefix=${bin%/*/*}
|
||||
local orig=$PWD
|
||||
|
||||
|
||||
# we need to clean up /tmp manually on windows
|
||||
cd /tmp \
|
||||
&& rm -rf ./git-extras \
|
||||
|
|
|
|||
136
bin/git-feature
136
bin/git-feature
|
|
@ -1,55 +1,105 @@
|
|||
#!/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
|
||||
while test $# != 0
|
||||
do
|
||||
case $1 in
|
||||
-a|--alias )
|
||||
if [[ -n $2 ]]
|
||||
then
|
||||
shift # shift -a|-alias
|
||||
branch_prefix=$1
|
||||
else
|
||||
argv+=($1) # treat tail '-a' as <name>
|
||||
fi
|
||||
;;
|
||||
-r|--remote )
|
||||
if [[ -n $2 ]]
|
||||
then
|
||||
remote=$2
|
||||
shift
|
||||
else
|
||||
remote="origin"
|
||||
fi
|
||||
;;
|
||||
* )
|
||||
argv+=($1)
|
||||
;;
|
||||
esac
|
||||
case $1 in
|
||||
-a|--alias )
|
||||
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||
then
|
||||
shift # shift -a|-alias
|
||||
branch_prefix=$1
|
||||
else
|
||||
echo >&2 "option $1 requires a value"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
-r|--remote )
|
||||
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||
then
|
||||
remote=$2
|
||||
shift
|
||||
else
|
||||
remote="origin"
|
||||
fi
|
||||
;;
|
||||
-s|--separator )
|
||||
if [[ -n $2 ]] && [[ $2 != -- ]]
|
||||
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
|
||||
;;
|
||||
-- )
|
||||
# terminate argument parsing
|
||||
shift
|
||||
argv+=("$@")
|
||||
break
|
||||
;;
|
||||
finish )
|
||||
finish=true
|
||||
;;
|
||||
* )
|
||||
argv+=("$1")
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
concatargs(){
|
||||
str=$(IFS='-'; echo "$*")
|
||||
branch="$branch_prefix"/$str
|
||||
concatargs() {
|
||||
str=$(IFS='-'; echo "$*")
|
||||
branch="$branch_prefix$branch_separator$str"
|
||||
}
|
||||
|
||||
if test "${argv[0]}" = "finish"; then
|
||||
test -z "${argv[1]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
|
||||
branch="$branch_prefix"/"${argv[1]}"
|
||||
git merge --no-ff "$branch" && git delete-branch "$branch"
|
||||
test -z "${argv[0]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
|
||||
|
||||
concatargs "${argv[@]}"
|
||||
|
||||
if "${finish}"
|
||||
then
|
||||
git merge ${merge_mode} "$branch" && git delete-branch "$branch"
|
||||
else
|
||||
test -z "${argv[0]}" && echo "$branch_prefix" "<name> required." 1>&2 && exit 1
|
||||
if test -n "${argv[1]}"; then
|
||||
concatargs "${argv[@]}"
|
||||
else
|
||||
branch="$branch_prefix"/"${argv[0]}"
|
||||
fi
|
||||
if [[ -n $remote ]]
|
||||
then
|
||||
git create-branch -r $remote $branch
|
||||
else
|
||||
git create-branch $branch
|
||||
fi
|
||||
if [[ -n $remote ]] && [[ -z $start_point ]]
|
||||
then
|
||||
git create-branch -r "$remote" "$branch"
|
||||
fi
|
||||
|
||||
if [[ -z $remote ]] && [[ -z $start_point ]]
|
||||
then
|
||||
git create-branch "$branch"
|
||||
fi
|
||||
|
||||
if [[ -n $remote ]] && [[ -n $start_point ]]
|
||||
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
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ repository from scratch.
|
|||
changed files will be reset, local branches and other remotes will be removed.
|
||||
|
||||
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
|
||||
"
|
||||
}
|
||||
|
|
@ -63,21 +63,21 @@ main() {
|
|||
|
||||
if [ -d "${destination_path}/.git" ]; then
|
||||
(
|
||||
cd ${destination_path}
|
||||
cd "${destination_path}"
|
||||
|
||||
# Delete all remotes
|
||||
for remote in `git remote`; do
|
||||
git remote rm ${remote}
|
||||
for remote in $(git remote); do
|
||||
git remote rm "${remote}"
|
||||
done
|
||||
|
||||
# Add origin
|
||||
git remote add origin ${remote_url}
|
||||
git remote add origin "${remote_url}"
|
||||
git fetch origin
|
||||
|
||||
# Set default branch
|
||||
if [ -z "${branch:-}" ]; then
|
||||
branch=`LC_ALL=C git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$'`
|
||||
git remote set-head origin ${branch}
|
||||
branch=$(LC_ALL=C git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$')
|
||||
git remote set-head origin "${branch}"
|
||||
else
|
||||
git remote set-head origin -a
|
||||
fi
|
||||
|
|
@ -87,19 +87,20 @@ main() {
|
|||
git reset --hard HEAD
|
||||
|
||||
# Get on the desired branch
|
||||
git checkout ${branch}
|
||||
git reset --hard origin/${branch}
|
||||
git checkout "${branch}"
|
||||
git reset --hard "origin/${branch}"
|
||||
|
||||
# Delete all other branches
|
||||
branches=`git branch | grep -v \* | xargs`
|
||||
# shellcheck disable=SC2063
|
||||
branches=$(git branch | grep -v '*' | xargs)
|
||||
if [ -n "${branches}" ]; then
|
||||
git branch -D ${branches}
|
||||
git branch -D "${branches}"
|
||||
fi
|
||||
)
|
||||
elif [ -n "${branch:-}" ]; then
|
||||
git clone -b ${branch} ${remote_url} ${destination_path}
|
||||
git clone -b "${branch}" "${remote_url}" "${destination_path}"
|
||||
else
|
||||
git clone ${remote_url} ${destination_path}
|
||||
git clone "${remote_url}" "${destination_path}"
|
||||
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
|
||||
echo "Enter your github username"
|
||||
read user
|
||||
read -r user
|
||||
[ -n "$user" ] || abort "git username required"
|
||||
echo "Enter github two-factor authentication code (leave blank if not set up)"
|
||||
read MFA_CODE
|
||||
# personal access token
|
||||
# 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
|
||||
project=${url##*/}
|
||||
owner=${url%/$project}
|
||||
owner=${url%/"$project"}
|
||||
project=${project%.git}
|
||||
if [[ $owner == git@* ]]; then
|
||||
owner=${owner##*:}
|
||||
|
|
@ -28,19 +32,20 @@ else
|
|||
fi
|
||||
|
||||
# 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
|
||||
curl -qsf \
|
||||
if ! curl -qsf \
|
||||
-X POST \
|
||||
-u "$user" \
|
||||
-u "$user:$github_personal_access_token" \
|
||||
-H "X-GitHub-OTP: $MFA_CODE" \
|
||||
"https://api.github.com/repos/$owner/$project/forks"
|
||||
|
||||
[ $? = 0 ] || abort "fork failed"
|
||||
then
|
||||
abort "fork failed"
|
||||
fi
|
||||
|
||||
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
|
||||
if [ -n "$use_ssh" ] && ssh -T git@github.com 2>&1 | grep -qi 'success'; then
|
||||
remote_prefix="git@github.com:"
|
||||
|
|
@ -56,7 +61,7 @@ else
|
|||
# clone forked repo into current dir
|
||||
git clone "${remote_prefix}${user}/${project}.git" "$project"
|
||||
# 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 fetch upstream
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@
|
|||
|
||||
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()
|
||||
{
|
||||
git symbolic-ref HEAD refs/heads/$branch
|
||||
git symbolic-ref HEAD "refs/heads/$branch"
|
||||
rm .git/index
|
||||
git clean -fdx
|
||||
}
|
||||
|
||||
if [ ! -z "$changes" ]; then
|
||||
read -p "All untracked changes will be lost. Continue [y/N]? " res
|
||||
if [ -n "$changes" ]; then
|
||||
read -rp "All untracked changes will be lost. Continue [y/N]? " res
|
||||
case $res in
|
||||
[Yy]* ) ;;
|
||||
* ) 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 'Tell me your github account username: '
|
||||
read username
|
||||
read -r username
|
||||
|
||||
echo 'Now, tell me your repository name: '
|
||||
read repository
|
||||
read -r repository
|
||||
|
||||
git stash \
|
||||
&& git checkout -b 'gh-pages' \
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
src=$1
|
||||
dst=$2
|
||||
|
||||
test -z $src && echo "source branch required." 1>&2 && exit 1
|
||||
test -z $dst && echo "destination 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
|
||||
|
||||
git checkout $dst \
|
||||
&& git merge --no-ff $src \
|
||||
&& git branch -d $src
|
||||
git checkout "$dst" \
|
||||
&& git merge --no-ff "$src" \
|
||||
&& git branch -d "$src"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
for param in $*
|
||||
for param in "$@"
|
||||
do
|
||||
case $param in
|
||||
-h)
|
||||
|
|
@ -31,7 +31,7 @@ do
|
|||
esac
|
||||
done
|
||||
|
||||
cd "$(git-root)" # cd for git blame
|
||||
cd "$(git-root)" || exit # cd for git blame
|
||||
MERGED_LOG=$(git_extra_mktemp)
|
||||
if [[ $EMAIL == '-e' ]]
|
||||
then
|
||||
|
|
@ -44,11 +44,13 @@ for file in $(git diff --name-only "$@")
|
|||
do
|
||||
test -n "$DEBUG" && echo "git blame $file"
|
||||
# $1 - since $2 - until
|
||||
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null |
|
||||
sed -n "$PATTERN" | sort | uniq -c | sed 's/^\(.\)/- \1/' >> $MERGED_LOG
|
||||
# shellcheck disable=SC2086
|
||||
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null |
|
||||
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"
|
||||
# shellcheck disable=SC2086
|
||||
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
|
||||
|
||||
DEBUG="$DEBUG" awk '
|
||||
|
|
@ -71,20 +73,15 @@ END {
|
|||
printf("%d %s\n", contributors[people], people)
|
||||
}
|
||||
}
|
||||
}' $MERGED_LOG | sort -nr | # only gawk supports built-in sort function
|
||||
while read line
|
||||
}' "$MERGED_LOG" | sort -nr | # only gawk supports built-in sort function
|
||||
while read -r line
|
||||
do
|
||||
people=${line#* }
|
||||
num=${line%% *}
|
||||
|
||||
if [[ $num -gt 0 ]]
|
||||
then
|
||||
if [[ ${#people} -ge 29 ]]
|
||||
then
|
||||
printf "%-29s \033[00;32m" "$people"
|
||||
else
|
||||
printf "%-29s\t\033[00;32m" "$people"
|
||||
fi
|
||||
printf "%-29s \033[00;32m" "$people"
|
||||
if [[ $num -ge 50 ]]
|
||||
then
|
||||
len=${#num}
|
||||
|
|
@ -92,7 +89,7 @@ do
|
|||
do
|
||||
printf "+"
|
||||
done
|
||||
printf "(%s)" $num
|
||||
printf "(%s)" "$num"
|
||||
else
|
||||
for (( i = 0; i < num; i++ ))
|
||||
do
|
||||
|
|
@ -100,12 +97,7 @@ do
|
|||
done
|
||||
fi
|
||||
else
|
||||
if [[ ${#people} -ge 29 ]]
|
||||
then
|
||||
printf "%-29s \033[00;31m" "$people"
|
||||
else
|
||||
printf "%-29s\t\033[00;31m" "$people"
|
||||
fi
|
||||
printf "%-29s \033[00;31m" "$people"
|
||||
if [[ $num -le -50 ]]
|
||||
then
|
||||
len=${#num}
|
||||
|
|
@ -113,7 +105,7 @@ do
|
|||
do
|
||||
printf "-"
|
||||
done
|
||||
printf "(%s)" $num
|
||||
printf "(%s)" "$num"
|
||||
else
|
||||
for (( i = 0; i > num; i-- ))
|
||||
do
|
||||
|
|
|
|||
269
bin/git-ignore
269
bin/git-ignore
|
|
@ -1,6 +1,28 @@
|
|||
#!/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}"
|
||||
if [ -f "$file" ]; then
|
||||
echo "$1 gitignore: $2" && cat "$file"
|
||||
|
|
@ -9,58 +31,255 @@ function show_contents {
|
|||
fi
|
||||
}
|
||||
|
||||
function show_global {
|
||||
show_contents Global `git config --global core.excludesfile`
|
||||
cd_to_git_root() {
|
||||
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 {
|
||||
local global_gitignore=$(git config --global core.excludesfile)
|
||||
global_ignore() {
|
||||
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
|
||||
echo "Can't find global .gitignore."
|
||||
echo ""
|
||||
echo "Use 'git config --global --add core.excludesfile ~/.gitignore-global' to set the path to your global gitignore file to '~/.gitignore-global'."
|
||||
echo ""
|
||||
else
|
||||
add_patterns `git config --global core.excludesfile` "$@"
|
||||
add_patterns "$global_gitignore" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
function show_local {
|
||||
cd "$(git root)"
|
||||
show_local() {
|
||||
cd_to_git_root --warn
|
||||
show_contents Local .gitignore
|
||||
}
|
||||
|
||||
function add_local {
|
||||
cd "$(git root)"
|
||||
add_local() {
|
||||
cd_to_git_root --warn
|
||||
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"
|
||||
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
|
||||
echo "... adding '$pattern'"
|
||||
(test -f "$file" && test "$pattern" && grep -q -F -x -- "$pattern" "$file") || echo "$pattern" >> "$file"
|
||||
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
|
||||
show_global
|
||||
echo "---------------------------------"
|
||||
show_local
|
||||
echo "---------------------------------"
|
||||
show_private
|
||||
else
|
||||
case "$1" in
|
||||
-l|--local)
|
||||
test $# -gt 1 && add_local "${@:2}" && echo
|
||||
show_local
|
||||
;;
|
||||
-g|--global)
|
||||
test $# -gt 1 && add_global "${@:2}" && echo
|
||||
show_global
|
||||
;;
|
||||
*)
|
||||
add_local "$@"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
# parse flags and args
|
||||
level="none"
|
||||
action="none"
|
||||
declare -a args
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
_usage
|
||||
exit 0
|
||||
;;
|
||||
-l|--local)
|
||||
level="local"
|
||||
;;
|
||||
-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
|
||||
|
||||
gitignore_io_url="https://www.gitignore.io/api/"
|
||||
gitignore_io_url="https://www.toptal.com/developers/gitignore/api/"
|
||||
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() {
|
||||
curl -L -s "${gitignore_io_url}/list" > ~/.gi_list
|
||||
curl -L -s "${gitignore_io_url}/list" > "$default_path"
|
||||
}
|
||||
|
||||
print_in_alphabetical_order() {
|
||||
|
|
@ -13,7 +17,7 @@ print_in_alphabetical_order() {
|
|||
for ignorable in $(echo "$gi_list" | sort);
|
||||
do
|
||||
first_character=${ignorable:0:1}
|
||||
if [[ $first_character = $previous_first_character ]]; then
|
||||
if [[ $first_character = "$previous_first_character" ]]; then
|
||||
printf " %s" "$ignorable"
|
||||
elif [[ $first = true ]]; then
|
||||
previous_first_character=$first_character
|
||||
|
|
@ -42,12 +46,11 @@ search() {
|
|||
}
|
||||
|
||||
print_last_modified_time() {
|
||||
gi_list_date=$(stat -c "%y" "$default_path" 2> /dev/null)
|
||||
if [ "$?" -ne 0 ]; then
|
||||
gi_list_date=$(stat -f "%t%Sm" "$default_path" 2> /dev/null)
|
||||
if [ "$?" -ne 0 ]; then
|
||||
gi_list_date=$(date -r "$default_path" +%s 2> /dev/null)
|
||||
[ "$?" -ne 0 ] && gi_list_date=0
|
||||
if ! gi_list_date=$(stat -c "%y" "$default_path" 2> /dev/null); then
|
||||
if ! gi_list_date=$(stat -f "%t%Sm" "$default_path" 2> /dev/null); then
|
||||
if ! gi_list_date=$(date -r "$default_path" +%s 2> /dev/null); then
|
||||
gi_list_date=0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
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-alphabetically] Print available types in alphabetical order "
|
||||
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 " [-u|--update-list] Update ~/.gi_list"
|
||||
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 $default_path"
|
||||
}
|
||||
|
||||
|
||||
check_list_exist() {
|
||||
if ! [ -f "$default_path" ]; then
|
||||
echo "-----Initial gitignore.io list----"
|
||||
update_gi_list &
|
||||
update_gi_list
|
||||
echo "-----Save to $default_path-----"
|
||||
echo
|
||||
fi
|
||||
|
|
|
|||
60
bin/git-info
60
bin/git-info
|
|
@ -1,11 +1,40 @@
|
|||
#!/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() {
|
||||
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() {
|
||||
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() {
|
||||
|
|
@ -21,27 +50,30 @@ remote_urls() {
|
|||
}
|
||||
|
||||
echon() {
|
||||
echo "$@"
|
||||
echo
|
||||
echo "$@"
|
||||
echo
|
||||
}
|
||||
|
||||
# Show info similar to svn
|
||||
|
||||
echo
|
||||
echon "## Remote URLs:"
|
||||
echon "${COLOR_TITLE}## Remote URLs:${NORMAL}"
|
||||
echon "$(remote_urls)"
|
||||
|
||||
echon "## Remote Branches:"
|
||||
echon "${COLOR_TITLE}## Remote Branches:${NORMAL}"
|
||||
echon "$(remote_branches)"
|
||||
|
||||
echon "## Local Branches:"
|
||||
echon "${COLOR_TITLE}## Local Branches:${NORMAL}"
|
||||
echon "$(local_branches)"
|
||||
|
||||
echon "## Most Recent Commit:"
|
||||
echon "$(most_recent_commit)"
|
||||
echon "Type 'git log' for more commits, or 'git show <commit id>' for full commit details."
|
||||
SUBMODULES_LOG=$(submodules)
|
||||
if [ -n "$SUBMODULES_LOG" ]; then
|
||||
echon "${COLOR_TITLE}## Submodule(s):${NORMAL}"
|
||||
echon "$SUBMODULES_LOG"
|
||||
fi
|
||||
|
||||
if test "$1" != "--no-config"; then
|
||||
echon "## Configuration (.git/config):"
|
||||
echon "${COLOR_TITLE}## Most Recent Commit:${NORMAL}"
|
||||
echon "$(most_recent_commit)"
|
||||
|
||||
if [ -n "$HIDE_CONFIG" ]; then
|
||||
echon "${COLOR_TITLE}## Configuration (.git/config):${NORMAL}"
|
||||
echon "$(get_config)"
|
||||
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
|
||||
|
||||
ref=$(git symbolic-ref HEAD)
|
||||
branch=${ref#refs/heads/}
|
||||
|
||||
git log origin/${branch}..${branch} $*
|
||||
git log "@{upstream}..@" "$@"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
filename=$1
|
||||
test -z $filename && echo "filename required." 1>&2 && exit 1
|
||||
git update-index --skip-worktree $filename
|
||||
filename="$1"
|
||||
test -z "$filename" && echo "filename required." 1>&2 && exit 1
|
||||
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)
|
||||
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
|
||||
2 ) # dest --ff
|
||||
git push "$(git rev-parse --show-toplevel)" "$cur_branch":"$1";;
|
||||
|
|
@ -32,3 +41,8 @@ else
|
|||
usage
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ $stashed -eq 1 ];
|
||||
then
|
||||
git stash pop;
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ branch=$2
|
|||
prefix=$3
|
||||
flat=0
|
||||
|
||||
if test $prefix = "."; then
|
||||
if test "$prefix" = "."; then
|
||||
prefix=$(mktemp -u 'git-merge-repo.XXXXXXX')
|
||||
flat=1
|
||||
fi
|
||||
|
|
@ -16,10 +16,10 @@ message=$(git log -1 --pretty=%B)
|
|||
|
||||
if test $flat -eq 1; then
|
||||
git stash -u
|
||||
mv -i $prefix/* ./
|
||||
mv -i "$prefix"/* ./
|
||||
git undo
|
||||
git add .
|
||||
git commit -am "$message"
|
||||
git stash apply
|
||||
rm -drf $prefix
|
||||
rm -drf "$prefix"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
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 ]
|
||||
|
|
@ -12,9 +12,20 @@ fi
|
|||
|
||||
declare -a git_log_args=()
|
||||
declare -a branches=()
|
||||
declare -a pathspec=()
|
||||
declare parse_path=false
|
||||
|
||||
for arg in "$@" ; do
|
||||
|
||||
if [[ $parse_path == true ]]; then
|
||||
pathspec+=("$@")
|
||||
break
|
||||
fi
|
||||
|
||||
case "$arg" in
|
||||
--)
|
||||
parse_path=true
|
||||
;;
|
||||
--*)
|
||||
git_log_args+=( "$arg" )
|
||||
;;
|
||||
|
|
@ -38,4 +49,4 @@ else
|
|||
exit 1
|
||||
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
|
||||
fi
|
||||
|
||||
remote_ref_kind=merge-requests
|
||||
|
||||
if test "$1" = "clean"; then
|
||||
git for-each-ref refs/heads/mr/* --format='%(refname)' | while read ref; do
|
||||
git branch -D ${ref#refs/heads/}
|
||||
git for-each-ref refs/heads/mr/* --format='%(refname)' | while read -r ref; do
|
||||
git branch -D "${ref#refs/heads/}"
|
||||
done
|
||||
exit 0
|
||||
elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then
|
||||
remote=${BASH_REMATCH[1]}.git
|
||||
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
|
||||
id=$1
|
||||
remote=${2:-origin}
|
||||
fi
|
||||
|
||||
branch=mr/$id
|
||||
remote_ref=refs/merge-requests/$id/head
|
||||
git fetch -fu $remote $remote_ref:$branch
|
||||
git checkout $branch
|
||||
git config --local --replace branch.$branch.merge $remote_ref
|
||||
git config --local --replace branch.$branch.remote $remote
|
||||
remote_ref=refs/$remote_ref_kind/$id/head
|
||||
git fetch -fu "$remote" "$remote_ref:$branch"
|
||||
git checkout "$branch"
|
||||
git config --local --replace "branch.$branch.merge" "$remote_ref"
|
||||
git config --local --replace "branch.$branch.remote" "$remote"
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ do
|
|||
file="$file"' '"$i"
|
||||
shift
|
||||
done
|
||||
test -n "$*" && range="$*"
|
||||
test -n "$*" && range=("$@")
|
||||
|
||||
test -z "$file" && echo "file required." 1>&2 && exit 1
|
||||
if [ -z "$range" ]
|
||||
if [ -z "${range[*]}" ]
|
||||
then
|
||||
git filter-branch -f --index-filter "git rm -r --cached ""$file"" --ignore-unmatch" \
|
||||
--prune-empty --tag-name-filter cat -- --all
|
||||
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" \
|
||||
--prune-empty --tag-name-filter cat -- $range
|
||||
--prune-empty --tag-name-filter cat -- "${range[@]}"
|
||||
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
|
||||
# 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
|
||||
git for-each-ref refs/heads/pr/* --format='%(refname)' | while read ref; do
|
||||
git branch -D ${ref#refs/heads/}
|
||||
git for-each-ref refs/heads/pr/* --format='%(refname)' | while read -r ref; do
|
||||
git branch -D "${ref#refs/heads/}"
|
||||
done
|
||||
exit 0
|
||||
elif [[ $1 =~ ^(https?://[^/]+/(.+))/pull/([0-9]+).*$ ]]; then
|
||||
remote=${BASH_REMATCH[1]}.git
|
||||
id=${BASH_REMATCH[3]}
|
||||
branch=pr/$id
|
||||
else
|
||||
test -z $1 && echo "pr number required." 1>&2 && exit 1
|
||||
remote=${2:-origin}
|
||||
|
||||
elif [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
remote_pref=${2:-$(git config --get git-extras.pr.remote)}
|
||||
remote=${remote_pref:-origin}
|
||||
id=$1
|
||||
branch=pr/$id
|
||||
pull "$remote" "$id" "$branch"
|
||||
|
||||
else
|
||||
rc=1
|
||||
while [ "$1" != "" ]; do
|
||||
pull_pr_if_matched "$1"
|
||||
rc=$?
|
||||
shift
|
||||
done
|
||||
|
||||
exit "$rc"
|
||||
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"
|
||||
CONTINUE="no"
|
||||
|
||||
function current_branch() {
|
||||
current_branch() {
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
}
|
||||
|
||||
|
|
@ -19,10 +19,10 @@ function usage()
|
|||
echo " -c|--continue: Continue after the user updates conflicts."
|
||||
}
|
||||
|
||||
while [[ $# > 0 ]]
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
key="$1"
|
||||
|
||||
|
||||
case $key in
|
||||
--no-ff)
|
||||
FF="--no-ff"
|
||||
|
|
@ -50,19 +50,17 @@ fi
|
|||
|
||||
if [[ "$CONTINUE" == "yes" ]]; then
|
||||
TARGET_BRANCH=$(current_branch)
|
||||
set $(echo "$TARGET_BRANCH" | sed -e "s/-rebased-on-top-of-/\n/g")
|
||||
if [[ $# != 2 ]]; then
|
||||
SECONDARY_BRANCH=${TARGET_BRANCH%"-rebased-on-top-of-"*}
|
||||
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}"
|
||||
exit 30 # Impossible to detect PRIMARY_BRANCH AND SECONDARY_BRANCH
|
||||
fi
|
||||
|
||||
SECONDARY_BRANCH=$1
|
||||
PRIMARY_BRANCH=$2
|
||||
|
||||
echo "Continuing rebasing of $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
|
||||
git commit || exit 51
|
||||
git branch -d ${SECONDARY_BRANCH} || exit 52
|
||||
git branch -m ${TARGET_BRANCH} ${SECONDARY_BRANCH} || exit 53
|
||||
git branch -d "${SECONDARY_BRANCH}" || exit 52
|
||||
git branch -m "${TARGET_BRANCH}" "${SECONDARY_BRANCH}" || exit 53
|
||||
|
||||
elif [[ "$PRIMARY_BRANCH" == "" ]]; then
|
||||
usage
|
||||
|
|
@ -71,14 +69,13 @@ else
|
|||
echo "Rebasing $SECONDARY_BRANCH on top of $PRIMARY_BRANCH"
|
||||
TARGET_BRANCH="${SECONDARY_BRANCH}-rebased-on-top-of-${PRIMARY_BRANCH}"
|
||||
|
||||
git checkout ${PRIMARY_BRANCH} || exit 41
|
||||
git checkout -b ${TARGET_BRANCH} || exit 42
|
||||
git merge ${SECONDARY_BRANCH} ${FF} \
|
||||
-m "Psycho-rebased branch ${SECONDARY_BRANCH} on top of ${PRIMARY_BRANCH}"
|
||||
git checkout "${PRIMARY_BRANCH}" || exit 41
|
||||
git checkout -b "${TARGET_BRANCH}" || exit 42
|
||||
|
||||
if [[ $? == 0 ]]; then
|
||||
git branch -d ${SECONDARY_BRANCH} || exit 43
|
||||
git branch -m ${TARGET_BRANCH} ${SECONDARY_BRANCH} || exit 44
|
||||
if git merge "${SECONDARY_BRANCH}" ${FF} \
|
||||
-m "Psycho-rebased branch ${SECONDARY_BRANCH} on top of ${PRIMARY_BRANCH}"; then
|
||||
git branch -d "${SECONDARY_BRANCH}" || exit 43
|
||||
git branch -m "${TARGET_BRANCH}" "${SECONDARY_BRANCH}" || exit 44
|
||||
else
|
||||
echo "Resolve the conflict and run ``${PROGRAM} --continue``."
|
||||
exit 1
|
||||
|
|
|
|||
|
|
@ -28,15 +28,12 @@ json() {
|
|||
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
|
||||
user="$EMAIL"
|
||||
fi
|
||||
|
||||
test -z "$user" && abort "git config user.email required"
|
||||
test -z "$github_personal_access_token" && abort "GITHUB_TOKEN or git config git-extras.github-personal-access-token required"
|
||||
|
||||
# branch
|
||||
|
||||
|
|
@ -46,7 +43,7 @@ if [ -z "$remote" ]; then
|
|||
echo 'no upstream found, push to origin as default'
|
||||
remote="origin"
|
||||
fi
|
||||
[ "$remote" == "." ] && abort "the upstream should be a remote branch."
|
||||
[ "$remote" = "." ] && abort "the upstream should be a remote branch."
|
||||
|
||||
# make sure it's pushed
|
||||
|
||||
|
|
@ -67,7 +64,7 @@ echo " create pull-request for $project '$branch'"
|
|||
echo
|
||||
printf " title: " && read -r title
|
||||
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
|
||||
echo
|
||||
|
||||
|
|
@ -75,12 +72,14 @@ echo
|
|||
|
||||
if [ -z "$base" ]
|
||||
then
|
||||
base="master"
|
||||
base="$(git_extra_default_branch)"
|
||||
fi
|
||||
|
||||
body=$(json "$title" "$body" "$branch" "$base")
|
||||
|
||||
curl -u "$user" \
|
||||
-H "X-GitHub-OTP: $mfa_code" \
|
||||
curl \
|
||||
-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"
|
||||
|
||||
|
|
|
|||
|
|
@ -17,26 +17,26 @@ fi
|
|||
# Use a temporary index.
|
||||
index=$(git_extra_mktemp)
|
||||
cleanup() {
|
||||
rm $index
|
||||
rm "$index"
|
||||
exit 2
|
||||
}
|
||||
trap cleanup 2
|
||||
trap cleanup INT
|
||||
|
||||
# Go back in history while parent commits are available.
|
||||
echo "Trying to find a commit the patch applies to..."
|
||||
rev=$(git rev-parse HEAD)
|
||||
while [ $? = 0 ]
|
||||
do
|
||||
GIT_INDEX_FILE=$index git read-tree $rev
|
||||
GIT_INDEX_FILE=$index git read-tree "$rev"
|
||||
|
||||
# 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=$?
|
||||
|
||||
# Do it again, but show the error, if the problem is the patch itself.
|
||||
if [ $patch_failed = 128 ]
|
||||
then
|
||||
GIT_INDEX_FILE=$index git apply --index --check $1
|
||||
GIT_INDEX_FILE=$index git apply --index --check "$1"
|
||||
exit $patch_failed
|
||||
fi
|
||||
|
||||
|
|
@ -45,19 +45,19 @@ do
|
|||
then
|
||||
# Manufacture a commit.
|
||||
tree=$(GIT_INDEX_FILE=$index git write-tree)
|
||||
commit=$(git commit-tree $tree -p $rev -m "$1")
|
||||
rm $index
|
||||
commit=$(git commit-tree "$tree" -p "$rev" -m "$1")
|
||||
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 $?
|
||||
fi
|
||||
|
||||
rev=$(git rev-parse --verify -q $rev^)
|
||||
rev=$(git rev-parse --verify -q "$rev^")
|
||||
done
|
||||
|
||||
# No compatible commit found. Restore.
|
||||
echo "Failed to find a commit the patch applies to."
|
||||
rm $index
|
||||
rm "$index"
|
||||
exit 1
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
ALIAS=refactor
|
||||
git feature -a $ALIAS "$@"
|
||||
|
|
@ -4,17 +4,17 @@ set -e
|
|||
hook() {
|
||||
local hook=.git/hooks/$1.sh
|
||||
# compat without extname
|
||||
if test ! -f $hook; then
|
||||
if test ! -f "$hook"; then
|
||||
hook=.git/hooks/$1
|
||||
fi
|
||||
|
||||
if test -f $hook; then
|
||||
if test -f "$hook"; then
|
||||
echo "... $1"
|
||||
shift
|
||||
if test -x $hook; then
|
||||
if test -x "$hook"; then
|
||||
$hook "$@"
|
||||
else
|
||||
. $hook "$@"
|
||||
. "$hook" "$@"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
|
@ -34,12 +34,29 @@ if test $# -gt 0; then
|
|||
-c) need_changelog=true;;
|
||||
-r) remote=$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)
|
||||
test -z "$2" &&
|
||||
exit_with_msg "major/minor/patch required for --semver option"
|
||||
semver=$2
|
||||
shift
|
||||
;;
|
||||
--prefix)
|
||||
test -z "$2" &&
|
||||
exit_with_msg "prefix string required for --prefix option"
|
||||
prefix="$2"
|
||||
shift
|
||||
;;
|
||||
--no-empty-commit) no_empty_commit=true;;
|
||||
--) shift; hook_args="$hook_args $*"; break;;
|
||||
*) 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)")
|
||||
|
||||
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
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -75,9 +92,9 @@ if test $# -gt 0; then
|
|||
(( ++version ))
|
||||
|
||||
case "$semver" in
|
||||
major ) version="${BASH_REMATCH[1]}$version.${BASH_REMATCH[3]}.${BASH_REMATCH[4]}${BASH_REMATCH[5]}" ;;
|
||||
minor ) version="${BASH_REMATCH[1]}${BASH_REMATCH[2]}.$version.${BASH_REMATCH[4]}${BASH_REMATCH[5]}" ;;
|
||||
patch ) version="${BASH_REMATCH[1]}${BASH_REMATCH[2]}.${BASH_REMATCH[3]}.$version${BASH_REMATCH[5]}" ;;
|
||||
major ) version="$prefix${BASH_REMATCH[1]}$version.0.0${BASH_REMATCH[5]}" ;;
|
||||
minor ) version="$prefix${BASH_REMATCH[1]}${BASH_REMATCH[2]}.$version.0${BASH_REMATCH[5]}" ;;
|
||||
patch ) version="$prefix${BASH_REMATCH[1]}${BASH_REMATCH[2]}.${BASH_REMATCH[3]}.$version${BASH_REMATCH[5]}" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
|
@ -101,8 +118,17 @@ if test $# -gt 0; then
|
|||
git commit -a -m "$msg" --allow-empty
|
||||
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
|
||||
git tag $version -a -m "$msg" \
|
||||
git tag "${sign_args[@]}" $version -a -m "$msg" \
|
||||
&& git push $remote --tags \
|
||||
&& git push $remote \
|
||||
&& hook post-release $hook_args \
|
||||
|
|
|
|||
|
|
@ -2,11 +2,17 @@
|
|||
set -e
|
||||
|
||||
# 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"
|
||||
old_branch="${2-$(git symbolic-ref --short -q HEAD)}"
|
||||
remote=$(git config branch."$old_branch".remote)
|
||||
if [ -z "$2" ]; then
|
||||
new_branch="$1"
|
||||
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"
|
||||
# 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
|
||||
new=$2
|
||||
|
||||
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 "$old" && echo "old 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 -d $old
|
||||
git push origin $new
|
||||
git push origin :refs/tags/$old
|
||||
git tag "$new" "$old"
|
||||
git tag -d "$old"
|
||||
git push origin "$new"
|
||||
git push origin ":refs/tags/$old"
|
||||
|
|
|
|||
82
bin/git-repl
82
bin/git-repl
|
|
@ -1,43 +1,99 @@
|
|||
#!/usr/bin/env bash
|
||||
shopt -s extglob
|
||||
|
||||
git version
|
||||
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
|
||||
# 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
|
||||
if test -n "$cur"; then
|
||||
prompt="git ($cur)> "
|
||||
cur_string=" ($cur)"
|
||||
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
|
||||
|
||||
# Readline
|
||||
read -e -r -p "$prompt" cmd
|
||||
prompt_base="$prefix$project_name$cur_string$es_string$prompt_character"
|
||||
prompt_base_stripped=$(echo "$prompt_base" | awk '{$1=$1};1')
|
||||
prompt="$prompt_base_stripped "
|
||||
|
||||
# EOF
|
||||
test $? -ne 0 && break
|
||||
# Use arguments as a command if any are provided.
|
||||
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
|
||||
history -s "$cmd"
|
||||
# Add command to history if it is not all whitespace
|
||||
if [[ ! "$cmd" =~ ^[[:space:]]*$ ]]; then
|
||||
history -s "$cmd"
|
||||
fi
|
||||
|
||||
# Built-in commands
|
||||
case $cmd in
|
||||
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
|
||||
|
||||
history -a
|
||||
|
||||
if [[ $cmd == !* ]]; then
|
||||
eval ${cmd:1}
|
||||
# shellcheck disable=SC2086
|
||||
eval ${cmd:1}
|
||||
elif [[ $cmd == git* ]]; then
|
||||
# shellcheck disable=SC2086
|
||||
eval $cmd
|
||||
else
|
||||
eval git "$cmd"
|
||||
fi
|
||||
exit_status=$?
|
||||
done
|
||||
|
||||
echo
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ file="$1"
|
|||
commit="$2"
|
||||
|
||||
if [[ -f $file ]]; then
|
||||
git rm --cached -q -f -- $file
|
||||
if [[ -z $commit ]]; then
|
||||
git checkout HEAD -- $file
|
||||
git rm --cached -q -f -- "$file"
|
||||
if [[ -z $commit ]]; then
|
||||
git checkout HEAD -- "$file"
|
||||
echo "Reset '$1' to HEAD"
|
||||
else
|
||||
git checkout $commit -- $file
|
||||
git checkout "$commit" -- "$file"
|
||||
echo "Reset '$1' to $commit"
|
||||
fi
|
||||
else
|
||||
echo "File '$1' not found in `pwd`"
|
||||
else
|
||||
echo "File '$1' not found in $PWD"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -6,7 +6,13 @@ git_root() {
|
|||
|
||||
# get the relative path of current path according to root of repo
|
||||
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
|
||||
|
|
|
|||
171
bin/git-scp
171
bin/git-scp
|
|
@ -18,7 +18,7 @@ function _test_git_scp()
|
|||
function set_remote()
|
||||
{
|
||||
remote=$1
|
||||
if [ $(git remote | grep -c -- ^$remote$) -eq 0 ]
|
||||
if [ "$(git remote | grep -c -- "^$remote$")" -eq 0 ]
|
||||
then
|
||||
COLOR_RED
|
||||
echo "Remote $remote does not exist in your git config"
|
||||
|
|
@ -39,8 +39,9 @@ function php_lint()
|
|||
test ! -f "$i" && continue
|
||||
case "$i" in
|
||||
*\.php|*\.phtml)
|
||||
php -l "$i" > /dev/null
|
||||
[ $? -gt 0 ] && error_count[${#error_count[@]}]="$i"
|
||||
if ! php -l "$i" > /dev/null; then
|
||||
error_count[${#error_count[@]}]="$i"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -58,17 +59,17 @@ function php_lint()
|
|||
|
||||
function _dos2unix()
|
||||
{
|
||||
command -v dos2unix > /dev/null && dos2unix $@
|
||||
command -v dos2unix > /dev/null && dos2unix "$@"
|
||||
return 0
|
||||
}
|
||||
|
||||
function _sanitize()
|
||||
{
|
||||
git config --get-all extras.scp.sanitize | while read i
|
||||
git config --get-all extras.scp.sanitize | while read -r i
|
||||
do
|
||||
case $i in
|
||||
php_lint) php_lint $@;; # git config --global --add extras.scp.sanitize php_lint
|
||||
dos2unix) _dos2unix $@;; # git config --global --add extras.scp.sanitize dos2unix
|
||||
php_lint) php_lint "$@";; # git config --global --add extras.scp.sanitize php_lint
|
||||
dos2unix) _dos2unix "$@";; # git config --global --add extras.scp.sanitize dos2unix
|
||||
esac
|
||||
done
|
||||
return $?
|
||||
|
|
@ -76,14 +77,26 @@ function _sanitize()
|
|||
|
||||
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
|
||||
|
||||
local refhead="$(git rev-parse --quiet --verify $1)"
|
||||
local refhead
|
||||
refhead="$(git rev-parse --quiet --verify "$1")"
|
||||
if [ -n "$refhead" ]
|
||||
then
|
||||
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"
|
||||
fi
|
||||
|
||||
|
|
@ -92,46 +105,117 @@ function scp_and_stage
|
|||
list=$(git ls-files "$@")" "$(git ls-files -o "$@")
|
||||
elif [ -n "$refhead" ]
|
||||
then
|
||||
git diff --stat $refhead
|
||||
list=$(git diff $refhead --name-only)
|
||||
[ "$verbose" -eq 1 ] && git --no-pager diff --stat "$refhead"
|
||||
list=$(git diff "$refhead" --name-only)
|
||||
else
|
||||
git diff
|
||||
[ "$verbose" -eq 1 ] && git --no-pager diff
|
||||
list=$(git diff --name-only)
|
||||
fi
|
||||
|
||||
deleted=$(for i in $list; do [ -f $i ] || echo $i; done)
|
||||
list=$(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)
|
||||
|
||||
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" ]
|
||||
then
|
||||
local _TMP=${0///}
|
||||
echo "$list" > $_TMP &&
|
||||
_sanitize $list &&
|
||||
_info Pushing to $remote \($(git config remote.$remote.url)\) &&
|
||||
rsync -rlDv --files-from=$_TMP ./ "$(git config remote.$remote.url)/" &&
|
||||
git add --force $list &&
|
||||
rm $_TMP
|
||||
# shellcheck disable=SC2086
|
||||
echo "$list" > "$_TMP"
|
||||
if [ "$dry_run" -eq 1 ] || _sanitize $list
|
||||
then
|
||||
_info "Pushing to $remote ($(git config "remote.$remote.url"))"
|
||||
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
|
||||
|
||||
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" ] &&
|
||||
COLOR_RED &&
|
||||
echo Deleted remote files &&
|
||||
ssh $(git config remote.$remote.url | cut -d: -f1) -t "rm $deleted" &&
|
||||
echo "$deleted"
|
||||
COLOR_RESET
|
||||
if [ -n "$deleted" ]
|
||||
then
|
||||
COLOR_RED
|
||||
echo Deleted remote files
|
||||
if [ "$dry_run" -eq 1 ]
|
||||
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()
|
||||
{
|
||||
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
|
||||
|
||||
local _TMP=${0///}
|
||||
echo $@ > $_TMP &&
|
||||
rsync -rlDv --files-from=$_TMP "$(git config remote.$remote.url)/" ./ &&
|
||||
rm $_TMP
|
||||
echo "$@" > "$_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()
|
||||
|
|
@ -145,13 +229,18 @@ function _usage()
|
|||
{
|
||||
echo "Usage:
|
||||
git scp -h|help|?
|
||||
git scp <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 rscp <remote> [<file|directory>] # copy <remote> files to current working directory
|
||||
git scp [options] <remote> [ref|file..] # scp and stage your files to specified remote
|
||||
git scp [options] <remote> [<ref>] # show diff relative to <ref> and upload unstaged files to <remote>
|
||||
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
|
||||
-v|verbose|--verbose) grep -A100 '^#* OPTIONS #*$' $0;;
|
||||
-v|verbose|--verbose) grep -A100 '^#* OPTIONS #*$' "$0" ;;
|
||||
esac
|
||||
exit
|
||||
}
|
||||
|
|
@ -161,18 +250,18 @@ function _error()
|
|||
[ $# -eq 0 ] && _usage && exit 0
|
||||
|
||||
echo
|
||||
echo ERROR: "$@"
|
||||
echo "ERROR: $*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
### OPTIONS ###
|
||||
case $(basename $0) in
|
||||
case $(basename "$0") in
|
||||
git-scp)
|
||||
case $1 in
|
||||
''|-h|'?'|help|--help) shift; _test_git_scp; _usage $@;;
|
||||
*) scp_and_stage $@;;
|
||||
''|-h|'?'|help|--help) shift; _test_git_scp; _usage "$@";;
|
||||
*) scp_and_stage "$@";;
|
||||
esac
|
||||
;;
|
||||
git-rscp) reverse_scp $@;;
|
||||
git-rscp) reverse_scp "$@";;
|
||||
esac
|
||||
|
|
|
|||
51
bin/git-sed
51
bin/git-sed
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
usage() {
|
||||
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
|
||||
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.
|
||||
EOF
|
||||
|
|
@ -16,7 +16,8 @@ do_commit() {
|
|||
true
|
||||
}
|
||||
|
||||
while [ "X$1" != "X" ]; do
|
||||
pathspec=
|
||||
while [ "$1" != "" ]; do
|
||||
case "$1" in
|
||||
-c|--commit)
|
||||
if git status --porcelain | grep .; then
|
||||
|
|
@ -32,7 +33,7 @@ actual command:
|
|||
}
|
||||
;;
|
||||
-f|--flags)
|
||||
if [ "X$2" = "X" ]; then
|
||||
if [ "$2" = "" ]; then
|
||||
usage
|
||||
echo "missing argument for $1"
|
||||
exit 1
|
||||
|
|
@ -44,16 +45,22 @@ actual command:
|
|||
usage
|
||||
exit
|
||||
;;
|
||||
--)
|
||||
pathspec="$*"
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
echo "unknown flag: $1"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ "X$search" = "X" ]; then
|
||||
if [ "$search" = "" ]; then
|
||||
search="$1"
|
||||
elif [ "X$replacement" = "X" ]; then
|
||||
elif [ "$replacement" = "" ]; then
|
||||
replacement="$1"
|
||||
elif [ "$flags" = "" ]; then
|
||||
flags="$1"
|
||||
else
|
||||
usage
|
||||
echo "too many arguments: $1"
|
||||
|
|
@ -64,6 +71,34 @@ actual command:
|
|||
shift
|
||||
done
|
||||
|
||||
command="git grep -lz '$search' | xargs -0 sed -i 's/$search/$replacement/$flags'"
|
||||
git grep -lz "$search" | xargs -0 sed -i "s/$search/$replacement/$flags"
|
||||
all="$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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
COMMIT_MESSAGE='Initial commit'
|
||||
|
||||
if [ "$1" == "-m" ]; then
|
||||
if [ "$1" = "-m" ]; then
|
||||
COMMIT_MESSAGE=$2
|
||||
shift; shift
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
#!/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
|
||||
|
||||
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
|
||||
|
||||
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"
|
||||
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() {
|
||||
git show-ref --verify --quiet "refs/heads/$src"
|
||||
|
|
@ -12,7 +31,8 @@ is_commit_reference() {
|
|||
}
|
||||
|
||||
is_on_current_branch() {
|
||||
local commit_sha=`git rev-parse "$src"`
|
||||
local commit_sha
|
||||
commit_sha=$(git rev-parse "$src")
|
||||
git rev-list HEAD |
|
||||
grep -q -- "$commit_sha"
|
||||
}
|
||||
|
|
@ -23,8 +43,8 @@ commit_if_msg_provided() {
|
|||
fi
|
||||
}
|
||||
|
||||
prompt_continuation_if_squashing_master() {
|
||||
if [[ $src =~ ^master$ ]]; then
|
||||
prompt_continuation_if_squashing_default_branch() {
|
||||
if [[ $src == $(git_extra_default_branch) ]]; then
|
||||
read -p "Warning: squashing '$src'! Continue [y/N]? " -r
|
||||
if ! [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Exiting"
|
||||
|
|
@ -34,19 +54,26 @@ prompt_continuation_if_squashing_master() {
|
|||
}
|
||||
|
||||
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
|
||||
commit_if_msg_provided
|
||||
}
|
||||
|
||||
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
|
||||
commit_if_msg_provided
|
||||
}
|
||||
|
||||
if `is_branch`; then
|
||||
if is_branch; then
|
||||
squash_branch
|
||||
elif `is_commit_reference` && `is_on_current_branch`; then
|
||||
elif is_commit_reference && is_on_current_branch; then
|
||||
squash_current_branch
|
||||
else
|
||||
echo "Source branch or commit reference required." 1>&2 && exit 1
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ EOF
|
|||
|
||||
error() {
|
||||
if [[ -n "$1" ]]; then
|
||||
local msg=$( echo "error: $1" )
|
||||
echo -e "${msg}" >&2
|
||||
echo "error: $1" >&2
|
||||
fi
|
||||
usage
|
||||
exit 1
|
||||
|
|
@ -34,7 +33,7 @@ stamp() {
|
|||
local commit_msg=$( git log -1 --pretty=%B )
|
||||
local stamp_msg
|
||||
[[ -n "${MSG}" ]] && stamp_msg="${ID} ${MSG}" || stamp_msg="${ID}"
|
||||
|
||||
|
||||
if ${REPLACE}; then
|
||||
# remove previous stamps with same ID from the commit message
|
||||
commit_msg=$(
|
||||
|
|
@ -71,9 +70,9 @@ parse_options() {
|
|||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
ID="$1"
|
||||
MSG="${@:2}"
|
||||
MSG="${*:2}"
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
117
bin/git-standup
117
bin/git-standup
|
|
@ -5,17 +5,21 @@
|
|||
usage() {
|
||||
cat <<EOS
|
||||
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
|
||||
-m - Specify the depth of 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 date format for "git log" (default: relative)
|
||||
-h - Display this help screen
|
||||
-g - Show if commit is GPG signed (G) or not (N)
|
||||
-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:
|
||||
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
|
||||
# supports them.
|
||||
if which tput >/dev/null 2>&1; then
|
||||
if command -v tput &>/dev/null; then
|
||||
ncolors=$(tput colors)
|
||||
fi
|
||||
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)
|
||||
UNDERLINE=$(tput smul)
|
||||
NORMAL=$(tput sgr0)
|
||||
COLOR=always
|
||||
else
|
||||
RED=""
|
||||
GREEN=""
|
||||
|
|
@ -52,13 +57,18 @@ else
|
|||
BOLD=""
|
||||
UNDERLINE=""
|
||||
NORMAL=""
|
||||
COLOR=never
|
||||
fi
|
||||
|
||||
# Only enable exit-on-error after the non-critical colorization stuff,
|
||||
# which may fail on systems lacking tput or terminfo
|
||||
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
|
||||
h)
|
||||
usage
|
||||
|
|
@ -72,7 +82,8 @@ while getopts "hgfd:a:w:m:D:L" opt; do
|
|||
fi
|
||||
;;
|
||||
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
|
||||
>&2 echo "Specify days less than one is invalid"
|
||||
exit 1
|
||||
|
|
@ -80,10 +91,11 @@ while getopts "hgfd:a:w:m:D:L" opt; do
|
|||
SINCE="$OPTARG days ago"
|
||||
;;
|
||||
w)
|
||||
if [ -n "$SINCE" ]; then
|
||||
if [ -n "$RANGE_SPECIFIED" ]; then
|
||||
warn "-w option is conflict with -d"
|
||||
continue
|
||||
fi
|
||||
RANGE_SPECIFIED=yes
|
||||
|
||||
week_range=${OPTARG}
|
||||
week_start="${week_range%%-*}"
|
||||
|
|
@ -115,7 +127,29 @@ while getopts "hgfd:a:w:m:D:L" opt; do
|
|||
GIT_DATE_FORMAT=${OPTARG}
|
||||
;;
|
||||
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
|
||||
|
|
@ -127,7 +161,7 @@ done
|
|||
shift $((OPTIND-1))
|
||||
|
||||
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
|
||||
usage
|
||||
exit 1
|
||||
|
|
@ -137,30 +171,76 @@ if [[ $# -gt 0 ]]; then
|
|||
UNTIL=$3
|
||||
fi
|
||||
|
||||
AUTHOR=${AUTHOR:="$(git config user.name)"}
|
||||
SINCE=${SINCE:=yesterday}
|
||||
UNTIL=${UNTIL:=today}
|
||||
AUTHOR=${AUTHOR:="$(git config user.name || echo '')"}
|
||||
if [ -z "${AUTHOR}" ]; then
|
||||
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}
|
||||
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}
|
||||
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 \
|
||||
--all
|
||||
--no-merges
|
||||
--since \"$SINCE\"
|
||||
--until \"$UNTIL\"
|
||||
--author=\"$AUTHOR\"
|
||||
--abbrev-commit
|
||||
--oneline
|
||||
--color=$COLOR
|
||||
--pretty=format:'$GIT_PRETTY_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
|
||||
if [[ $in_git_repo != 0 ]]; then
|
||||
## Set delimiter to newline for the loop
|
||||
IFS=$'\n'
|
||||
## Recursively search for git repositories
|
||||
# shellcheck disable=SC2086
|
||||
PROJECT_DIRS=$(find $INCLUDE_LINKS . -maxdepth "$MAXDEPTH" -mindepth 0 -name .git)
|
||||
|
||||
# Fetch the latest commits, if required
|
||||
|
|
@ -188,11 +268,12 @@ if [[ $in_git_repo != 0 ]]; then
|
|||
pushd "$DIR" > /dev/null
|
||||
## Show the detail only if it is a git repository
|
||||
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
|
||||
if [[ ! -z "$GITOUT" ]] ; then
|
||||
if [[ -n "$GITOUT" ]] ; then
|
||||
echo "${BOLD}${UNDERLINE}${YELLOW}$(basename "$DIR")${NORMAL}"
|
||||
echo "$GITOUT"
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
echo "Repository under $DIR could not be queried." >&2
|
||||
|
|
@ -206,8 +287,8 @@ else
|
|||
git fetch --all > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if GITOUT=$(eval ${GIT_LOG_COMMAND}); then
|
||||
if [[ ! -z "$GITOUT" ]] ; then
|
||||
if GITOUT=$(git_output); then
|
||||
if [[ -n "$GITOUT" ]] ; then
|
||||
echo "$GITOUT"
|
||||
else
|
||||
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; }
|
||||
|
||||
commit=""
|
||||
test $# -ne 0 && commit=$@
|
||||
project=${PWD##*/}
|
||||
PROJECT_FULL_PATH=
|
||||
SUMMARY_BY_LINE=
|
||||
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>
|
||||
#
|
||||
|
||||
date() {
|
||||
git log --pretty='format: %ai' $1 | cut -d ' ' -f 2
|
||||
commit_date() {
|
||||
# the $1 can be empty
|
||||
# shellcheck disable=SC2086
|
||||
git log $MERGES_ARG --pretty='format: %ai' "$1" | cut -d ' ' -f 2
|
||||
}
|
||||
|
||||
#
|
||||
# get active days for the given <commit>
|
||||
#
|
||||
|
||||
active_days() {
|
||||
date $1 | sort -r | uniq | awk '
|
||||
commit_date "$1" | sort -r | uniq | awk '
|
||||
{ sum += 1 }
|
||||
END { print sum }
|
||||
'
|
||||
|
|
@ -29,28 +84,66 @@ active_days() {
|
|||
#
|
||||
# get the commit total
|
||||
#
|
||||
|
||||
commit_count() {
|
||||
git log --oneline $commit | wc -l | tr -d ' '
|
||||
# shellcheck disable=SC2086
|
||||
git rev-list $MERGES_ARG --count "$commit"
|
||||
}
|
||||
|
||||
#
|
||||
# total file count
|
||||
#
|
||||
|
||||
file_count() {
|
||||
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
|
||||
#
|
||||
|
||||
authors() {
|
||||
format_authors() {
|
||||
# a rare unicode character is used as separator to avoid conflicting with
|
||||
# author name. However, Linux column utility will escape tab if separator
|
||||
# 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 }
|
||||
END {
|
||||
for (i = 1; i <= NR; ++i) {
|
||||
|
|
@ -63,29 +156,104 @@ authors() {
|
|||
#
|
||||
# fetch repository age from oldest commit
|
||||
#
|
||||
|
||||
repository_age() {
|
||||
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
|
||||
git line-summary "$@"
|
||||
echo
|
||||
else
|
||||
#
|
||||
# list the last modified author for each line
|
||||
#
|
||||
single_file() {
|
||||
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
|
||||
echo " project : $project"
|
||||
echo " repo age :" $(repository_age)
|
||||
echo " active :" $(active_days) days
|
||||
echo " commits :" $(commit_count)
|
||||
if test "$commit" = ""; then
|
||||
echo " files :" $(file_count)
|
||||
current_branch_name() {
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
}
|
||||
|
||||
#
|
||||
# list the author for all file
|
||||
#
|
||||
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
|
||||
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
|
||||
|
|
|
|||
42
bin/git-sync
42
bin/git-sync
|
|
@ -7,10 +7,12 @@ function _usage()
|
|||
Usage:
|
||||
${command} [<remote> <branch>]
|
||||
${command} -h | --help
|
||||
${command} -s | --soft
|
||||
${command} -f | --force
|
||||
|
||||
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.
|
||||
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:
|
||||
Sync with upstream of local branch:
|
||||
|
|
@ -18,6 +20,12 @@ Examples:
|
|||
|
||||
Sync with origin/master:
|
||||
${command} origin master
|
||||
|
||||
Sync without cleaning untracked files:
|
||||
${command} -s
|
||||
|
||||
Sync without interaction:
|
||||
${command} -f
|
||||
EOS
|
||||
}
|
||||
|
||||
|
|
@ -29,6 +37,12 @@ function main()
|
|||
_usage
|
||||
exit
|
||||
;;
|
||||
-s | --soft)
|
||||
local soft="true"
|
||||
;;
|
||||
-f | --force)
|
||||
local force="YES"
|
||||
;;
|
||||
* )
|
||||
if [ "${remote}" = "" ]; then
|
||||
local remote="$1"
|
||||
|
|
@ -45,14 +59,14 @@ function main()
|
|||
done
|
||||
|
||||
local remote_branch
|
||||
if [ "${remote}" = "" ]; then
|
||||
if ! remote_branch="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null)"; then
|
||||
if [ -z "${remote}" ]; 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."
|
||||
exit 1
|
||||
fi
|
||||
local branch="$(git rev-parse --abbrev-ref --symbolic-full-name @)"
|
||||
local remote=$(git config "branch.${branch}.remote")
|
||||
elif [ "${branch}" = "" ]; then
|
||||
elif [ -z "${branch}" ]; then
|
||||
echo -e "Error: too few arguments.\n"
|
||||
_usage
|
||||
exit 1
|
||||
|
|
@ -60,12 +74,22 @@ function main()
|
|||
remote_branch="${remote}/${branch}"
|
||||
fi
|
||||
|
||||
echo -n "Are you sure you want to clean all changes & sync with '${remote_branch}'? [y/N]: "
|
||||
local res
|
||||
read res
|
||||
case "${res}" in
|
||||
if [ "${force}" != "YES" ]; then
|
||||
if [ "${soft}" = "true" ]; then
|
||||
echo -n "Are you sure you want to sync with '${remote_branch}'? [y/N]: "
|
||||
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" )
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ USAGE(){
|
|||
|
||||
test $# -lt 1 && USAGE
|
||||
|
||||
for filename in $@; do
|
||||
for filename in "$@"; do
|
||||
touch "$filename" \
|
||||
&& git add "$filename"
|
||||
done
|
||||
|
|
|
|||
55
bin/git-undo
55
bin/git-undo
|
|
@ -1,6 +1,27 @@
|
|||
#!/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
|
||||
-h)
|
||||
|
|
@ -12,9 +33,8 @@ EOL
|
|||
read -r res
|
||||
case "${res}" in
|
||||
"Y" | "y")
|
||||
test $2 -gt 1 > /dev/null 2>&1 && back="~$2"
|
||||
git reset --hard HEAD$back
|
||||
exit $?
|
||||
parm1=hard
|
||||
parm2=${2:-1}
|
||||
;;
|
||||
* )
|
||||
exit 0
|
||||
|
|
@ -22,16 +42,31 @@ EOL
|
|||
esac
|
||||
;;
|
||||
--hard)
|
||||
test $2 -gt 1 > /dev/null 2>&1 && back="~$2"
|
||||
git reset --hard HEAD$back && exit 0;
|
||||
parm1=hard
|
||||
parm2=${2:-1}
|
||||
;;
|
||||
-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
|
||||
|
||||
git reset --soft HEAD$back
|
||||
git reset
|
||||
if [[ ! $parm2 =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo "Invalid undo count: $parm2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
_undo "$parm1" "$parm2" "$parm3"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
filename=$1
|
||||
test -z $filename && echo "filename required." 1>&2 && exit 1
|
||||
git update-index --no-skip-worktree $filename
|
||||
filename="$1"
|
||||
test -z "$filename" && echo "filename required." 1>&2 && exit 1
|
||||
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
|
||||
+++ b/bin/git-extras
|
||||
@@ -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() {
|
||||
- local bin="$(which git-extras)"
|
||||
- local bin="$(command -v git-extras)"
|
||||
- local prefix=${bin%/*/*}
|
||||
- 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