Compare commits
571 Commits
worktree-s
...
release-no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a191b913f | ||
|
|
e9962b98a7 | ||
|
|
a1ce6029c1 | ||
|
|
8c716184c1 | ||
|
|
5c888a0b47 | ||
|
|
498092a8ec | ||
|
|
caf6a3629d | ||
|
|
1ca96bbe5b | ||
|
|
cb5d0bca1c | ||
|
|
ef3d6f4a32 | ||
|
|
b470442a46 | ||
|
|
bf01c0b00e | ||
|
|
76e39af76f | ||
|
|
5b91cd0cc8 | ||
|
|
6255728e63 | ||
|
|
b657fc4f0b | ||
|
|
d294d51791 | ||
|
|
b35f8776e1 | ||
|
|
8ca78412ac | ||
|
|
33f933ba21 | ||
|
|
d70dd5123d | ||
|
|
15da702140 | ||
|
|
cd50c79ae4 | ||
|
|
3ebba5f32c | ||
|
|
9a423c388d | ||
|
|
daf9b8cfa9 | ||
|
|
b6a9220343 | ||
|
|
aeb017e029 | ||
|
|
a6174271aa | ||
|
|
517e0f8248 | ||
|
|
c1cb95db6f | ||
|
|
966e5f5337 | ||
|
|
f244ec8251 | ||
|
|
2b97f0fb43 | ||
|
|
af5e25cfb5 | ||
|
|
93cae68e94 | ||
|
|
6e6fe6a489 | ||
|
|
2c2436574d | ||
|
|
0cdca9ac2c | ||
|
|
4172be6bc8 | ||
|
|
d97b37a178 | ||
|
|
21334fa889 | ||
|
|
36a29f225b | ||
|
|
1e85c4379f | ||
|
|
470632b97a | ||
|
|
50044dd5e0 | ||
|
|
bc330b8ff3 | ||
|
|
7f36494eb2 | ||
|
|
85c48ba887 | ||
|
|
1e3935cbaf | ||
|
|
3fe491fcb2 | ||
|
|
66b608b2f9 | ||
|
|
4ee01d153b | ||
|
|
79e04fad9a | ||
|
|
6778bc04a3 | ||
|
|
d548f857a4 | ||
|
|
f99c59b6d5 | ||
|
|
0fd4983c66 | ||
|
|
240948b882 | ||
|
|
cf82e69bbe | ||
|
|
653e59a3d5 | ||
|
|
e342860ef1 | ||
|
|
569adae6a2 | ||
|
|
79fe885dcd | ||
|
|
a46f26e148 | ||
|
|
ca4b8b25f0 | ||
|
|
dad2c5fa52 | ||
|
|
8a08abcd35 | ||
|
|
b96befa250 | ||
|
|
8cc820668a | ||
|
|
ccb1ee04a5 | ||
|
|
d00936fb4e | ||
|
|
e84d5acd23 | ||
|
|
a8a4211d2b | ||
|
|
6907816af9 | ||
|
|
38db574de9 | ||
|
|
c7012528fc | ||
|
|
2162e5ff64 | ||
|
|
f40f5710f0 | ||
|
|
97940cae10 | ||
|
|
1555503493 | ||
|
|
1a035db4c8 | ||
|
|
df5b3693d6 | ||
|
|
b123719107 | ||
|
|
6ec109c15a | ||
|
|
dee1ff007d | ||
|
|
02b739743a | ||
|
|
f5361fdcb4 | ||
|
|
b86d5bd688 | ||
|
|
4bfffa69ff | ||
|
|
f9b4bcde38 | ||
|
|
aaecd6cc40 | ||
|
|
7e5f25e415 | ||
|
|
d8059d7f7d | ||
|
|
0f2b79a1d4 | ||
|
|
bb87642aee | ||
|
|
4a5b3eaa52 | ||
|
|
1b4e76797f | ||
|
|
58971182ca | ||
|
|
9427a85805 | ||
|
|
d145e818d0 | ||
|
|
e0fc8fe25b | ||
|
|
bb705d91a4 | ||
|
|
738fa286b2 | ||
|
|
1d1b8cc01f | ||
|
|
c357284e1a | ||
|
|
232596b83d | ||
|
|
1c1d558cc7 | ||
|
|
dffb16ed96 | ||
|
|
df8ce76bd6 | ||
|
|
4d18b62c54 | ||
|
|
8193731a82 | ||
|
|
1a6d062192 | ||
|
|
1ff13cdfc6 | ||
|
|
19e8cafe41 | ||
|
|
cbb5fe6007 | ||
|
|
c4fc30c243 | ||
|
|
117aa1dcc6 | ||
|
|
c550737a4f | ||
|
|
9e37ae3f5d | ||
|
|
c89ef8b84a | ||
|
|
23befdd13a | ||
|
|
58a83b0862 | ||
|
|
d5b4f7bb3e | ||
|
|
3cee4de39c | ||
|
|
f609a04671 | ||
|
|
7ffb6ffb0f | ||
|
|
3691856021 | ||
|
|
b5ca6a3add | ||
|
|
ecaa4846f1 | ||
|
|
787f9966ec | ||
|
|
013cfc77a1 | ||
|
|
16f7b01fec | ||
|
|
421c6565f9 | ||
|
|
d02deeefd8 | ||
|
|
235f5bb221 | ||
|
|
be3b4bd791 | ||
|
|
67d6447e12 | ||
|
|
fd9d7cb7bb | ||
|
|
3d6965ccbb | ||
|
|
707fa37160 | ||
|
|
7075b66bc6 | ||
|
|
9d55d71fdd | ||
|
|
cc9a20c4ab | ||
|
|
c39fafe6ec | ||
|
|
2a11725749 | ||
|
|
deed9eb18e | ||
|
|
50b49ebeb1 | ||
|
|
a74c6eef40 | ||
|
|
a3759c65e4 | ||
|
|
519760077b | ||
|
|
8390622f70 | ||
|
|
a04fbafcfd | ||
|
|
4fe8dee40f | ||
|
|
6fd80565c7 | ||
|
|
5ebd8ac7fe | ||
|
|
3cda1d03d9 | ||
|
|
d4eb02fac0 | ||
|
|
9c72d8a2b0 | ||
|
|
4f3127ccb8 | ||
|
|
877c27722f | ||
|
|
0dda8b58d1 | ||
|
|
e997d1ae5d | ||
|
|
3e2ef84d56 | ||
|
|
b2eccd6fe8 | ||
|
|
cc25a3b8c5 | ||
|
|
e93d945dba | ||
|
|
246e08096f | ||
|
|
860a52a736 | ||
|
|
18d8b29461 | ||
|
|
7af371701d | ||
|
|
5ccc95b76f | ||
|
|
cdb1d76484 | ||
|
|
9e423e949c | ||
|
|
e1ceb6892a | ||
|
|
40b8557608 | ||
|
|
92e107f52d | ||
|
|
2f6a87df98 | ||
|
|
2c29577e3c | ||
|
|
e72559bb27 | ||
|
|
6da1cf87b1 | ||
|
|
508b869773 | ||
|
|
59cc6843e6 | ||
|
|
f108fd2236 | ||
|
|
8081b59a02 | ||
|
|
26d180a50a | ||
|
|
10fe872c71 | ||
|
|
c74448f00d | ||
|
|
41ab7c44a0 | ||
|
|
3589a43682 | ||
|
|
ad70341139 | ||
|
|
ea93df571d | ||
|
|
bc21921001 | ||
|
|
07cbb62510 | ||
|
|
fc868cdda5 | ||
|
|
864a9ada57 | ||
|
|
f3e9d50d94 | ||
|
|
cdad0b998e | ||
|
|
64012d67a9 | ||
|
|
33d9d4dbf1 | ||
|
|
4c5e250ed8 | ||
|
|
1359fa14c1 | ||
|
|
c222a618a8 | ||
|
|
05c32e292e | ||
|
|
fb4f742416 | ||
|
|
276438b601 | ||
|
|
edec116ceb | ||
|
|
1aae1772ce | ||
|
|
a642395e9f | ||
|
|
70bfeddc90 | ||
|
|
e2a966443b | ||
|
|
0b13c3ca87 | ||
|
|
8b6766de79 | ||
|
|
c9371f812f | ||
|
|
e592d81b60 | ||
|
|
f2f50ccf75 | ||
|
|
75aed98c35 | ||
|
|
7f9818cfa2 | ||
|
|
679148449a | ||
|
|
bc00aeb10d | ||
|
|
4d258bd981 | ||
|
|
c465b0f2ff | ||
|
|
25160b671e | ||
|
|
20bfa42792 | ||
|
|
5a740e34c7 | ||
|
|
0aad599a9b | ||
|
|
b6c892a08a | ||
|
|
28d12e4e5d | ||
|
|
5d5e24a48e | ||
|
|
de598e55a6 | ||
|
|
0fd8392067 | ||
|
|
67ac2c5d9b | ||
|
|
7626fd7df3 | ||
|
|
db409fa69f | ||
|
|
5b8a8d356c | ||
|
|
d7e2ca3f10 | ||
|
|
387fbf6ab6 | ||
|
|
917eb88617 | ||
|
|
005827395d | ||
|
|
7f9fa64074 | ||
|
|
ab06a1c85c | ||
|
|
37048911ca | ||
|
|
2b7b6f71ee | ||
|
|
47d422bb8a | ||
|
|
c3ca77d6bf | ||
|
|
a3cd1e93be | ||
|
|
4cf8d81155 | ||
|
|
7774fe0ab3 | ||
|
|
f7db17f7d0 | ||
|
|
1106981827 | ||
|
|
1dac4158e9 | ||
|
|
18c5780485 | ||
|
|
e895052437 | ||
|
|
ade8d78c63 | ||
|
|
2e74b7177e | ||
|
|
6266e19623 | ||
|
|
c67aa49856 | ||
|
|
3a54089859 | ||
|
|
e2c447b8d0 | ||
|
|
843e12286f | ||
|
|
e60936e964 | ||
|
|
b9cfd89b80 | ||
|
|
de4224bbe4 | ||
|
|
ce91de76e4 | ||
|
|
acfce07aa0 | ||
|
|
81216189e4 | ||
|
|
2b26b380b6 | ||
|
|
1ba8d3060b | ||
|
|
f561007fb7 | ||
|
|
a63d5891e2 | ||
|
|
df38e954f4 | ||
|
|
ea532b4729 | ||
|
|
572d1b5920 | ||
|
|
e8fac6ca73 | ||
|
|
4de3fadb00 | ||
|
|
12f24aa8b4 | ||
|
|
911aa7774b | ||
|
|
1fb0e1e151 | ||
|
|
94bf279b5a | ||
|
|
37381b610d | ||
|
|
6d4df57393 | ||
|
|
3df01aaff0 | ||
|
|
4ee4f6f34b | ||
|
|
7953f7fa86 | ||
|
|
7a8df7795c | ||
|
|
aa493d3a9e | ||
|
|
72731f2c16 | ||
|
|
f680b6e82e | ||
|
|
ec22570eac | ||
|
|
198ead7c14 | ||
|
|
297a020abf | ||
|
|
473d989cde | ||
|
|
061bfce835 | ||
|
|
996e30e5d9 | ||
|
|
ecd2a14a15 | ||
|
|
8dfa8dc48c | ||
|
|
f7c183a429 | ||
|
|
473f86fcaa | ||
|
|
28353da61b | ||
|
|
efd49f07fd | ||
|
|
b8e183acc4 | ||
|
|
821c4b05fe | ||
|
|
4272d84fa2 | ||
|
|
e95505453e | ||
|
|
6dc34d19ea | ||
|
|
a670fbb33c | ||
|
|
98e6c119f5 | ||
|
|
c718a73d0a | ||
|
|
d74c817fd8 | ||
|
|
93d19db158 | ||
|
|
d7b611aa05 | ||
|
|
fc6008fdff | ||
|
|
a65d3119c0 | ||
|
|
51ecf4089b | ||
|
|
0c0fe8997b | ||
|
|
f86309dd03 | ||
|
|
c31fcb7134 | ||
|
|
91ec42f3f8 | ||
|
|
8f628296ad | ||
|
|
61bd3e8dd2 | ||
|
|
7263630967 | ||
|
|
16711c6f1a | ||
|
|
b2023f10b6 | ||
|
|
e915488a83 | ||
|
|
b2d629b50a | ||
|
|
125d4fa9dc | ||
|
|
527a1596f3 | ||
|
|
51d9f70f9e | ||
|
|
83727d48b5 | ||
|
|
8efc175340 | ||
|
|
6a6cb25d7e | ||
|
|
fd782ff568 | ||
|
|
dbfb469bad | ||
|
|
888a976fc0 | ||
|
|
2073730186 | ||
|
|
7a4a0c85c4 | ||
|
|
382ecb6bfe | ||
|
|
03694f7502 | ||
|
|
ee308a4994 | ||
|
|
a2b2336173 | ||
|
|
d7b1deb465 | ||
|
|
dd01639f57 | ||
|
|
525932fbf2 | ||
|
|
b1314349d7 | ||
|
|
9671f549a1 | ||
|
|
a1738a77ae | ||
|
|
689deb72bd | ||
|
|
84372cfad9 | ||
|
|
e429415ed4 | ||
|
|
7402be98b6 | ||
|
|
341b9725d4 | ||
|
|
8f164f7bc5 | ||
|
|
79c11a0458 | ||
|
|
4a4afc4639 | ||
|
|
ebdfd8046a | ||
|
|
d99e983236 | ||
|
|
1c6cfafba7 | ||
|
|
83d642b74f | ||
|
|
b1bc437d1b | ||
|
|
26989ce0ee | ||
|
|
e6356ce10c | ||
|
|
b92da0dc54 | ||
|
|
70e668bfcf | ||
|
|
8dd517870d | ||
|
|
f1753f36c8 | ||
|
|
3ea81d4a6f | ||
|
|
0df5cb1286 | ||
|
|
c43830b027 | ||
|
|
08624b8ef7 | ||
|
|
73b68927af | ||
|
|
bf699d3a79 | ||
|
|
f2e8d549ba | ||
|
|
4ffb6c0fea | ||
|
|
54776052a1 | ||
|
|
9c5eedf748 | ||
|
|
ca08956f77 | ||
|
|
aa74239f05 | ||
|
|
9e7018db8a | ||
|
|
cdea5b4873 | ||
|
|
4b0432423d | ||
|
|
2607580a09 | ||
|
|
ebd56bd8d5 | ||
|
|
ed1547e0cb | ||
|
|
5e388e21c8 | ||
|
|
4c89f9fba5 | ||
|
|
8e7958f78b | ||
|
|
579791e7bc | ||
|
|
595e28d335 | ||
|
|
0551f29de9 | ||
|
|
82b5c20050 | ||
|
|
ecaf1e9002 | ||
|
|
69ff621494 | ||
|
|
f32fe84c9b | ||
|
|
2ded352f3a | ||
|
|
c877a8528f | ||
|
|
d1754a9490 | ||
|
|
33a96a3dd7 | ||
|
|
cd889adfa0 | ||
|
|
77447ea6c3 | ||
|
|
fb05ee369f | ||
|
|
213da4fddd | ||
|
|
e78a09ebab | ||
|
|
7ccb871a45 | ||
|
|
9cc18bd7e6 | ||
|
|
8e2ecd17a9 | ||
|
|
78676dcff6 | ||
|
|
7847203d6e | ||
|
|
af3101c7f2 | ||
|
|
ec7e9f9228 | ||
|
|
ab5875c78f | ||
|
|
f6af4c29d4 | ||
|
|
996ad5bf26 | ||
|
|
096628e366 | ||
|
|
b432fb5efe | ||
|
|
c390c9d58e | ||
|
|
e25403c650 | ||
|
|
5c9a9e777c | ||
|
|
39d24fdeb7 | ||
|
|
bbb2b32816 | ||
|
|
0152639ef0 | ||
|
|
c43195efb6 | ||
|
|
17db918cba | ||
|
|
befd7fe63e | ||
|
|
de28391052 | ||
|
|
92f0aa23cc | ||
|
|
c8520fbe78 | ||
|
|
8ce60662f2 | ||
|
|
c5acbb6c7c | ||
|
|
4aca854b59 | ||
|
|
f7bf125037 | ||
|
|
c92ed07082 | ||
|
|
df0c3b3ea8 | ||
|
|
280b0429c3 | ||
|
|
cb22db675a | ||
|
|
bb21034972 | ||
|
|
ec5bc93576 | ||
|
|
f43fd7af79 | ||
|
|
a200fccba9 | ||
|
|
37dc31b815 | ||
|
|
1712ad08ae | ||
|
|
53f0c1eb4f | ||
|
|
c53adbbb84 | ||
|
|
e932aaaeaf | ||
|
|
b44d0c4bde | ||
|
|
917cfd7586 | ||
|
|
c7e6a73512 | ||
|
|
b7ba06fa5b | ||
|
|
51e205ce11 | ||
|
|
6984a4f4d4 | ||
|
|
4baecec7bf | ||
|
|
b262e7f9b8 | ||
|
|
b92c294059 | ||
|
|
07d03df8df | ||
|
|
9cc1d65280 | ||
|
|
893c1cab75 | ||
|
|
25d57350df | ||
|
|
eb68591396 | ||
|
|
6de9e230a0 | ||
|
|
668d29736c | ||
|
|
be667682f0 | ||
|
|
045bce5dfc | ||
|
|
3827479643 | ||
|
|
d41a195ee6 | ||
|
|
71d2fd37e2 | ||
|
|
66de981e91 | ||
|
|
375451785c | ||
|
|
a6af31a4cb | ||
|
|
94daf7bddc | ||
|
|
4eb73393bb | ||
|
|
f5c9764dd2 | ||
|
|
6dc25d796b | ||
|
|
0c07963a2e | ||
|
|
092d5dd608 | ||
|
|
e9bbd816de | ||
|
|
eb6f089a2a | ||
|
|
cb240081a8 | ||
|
|
9c57444adc | ||
|
|
7b8ebfa27e | ||
|
|
8ab05d6834 | ||
|
|
774df817fd | ||
|
|
18903a7dde | ||
|
|
7cfbfb7183 | ||
|
|
975d2bedb6 | ||
|
|
5c78394299 | ||
|
|
e33fe37a99 | ||
|
|
ea54cb6e9c | ||
|
|
08f0e28e55 | ||
|
|
2d4706e96e | ||
|
|
d30eef69b1 | ||
|
|
91909331b5 | ||
|
|
f31e213edc | ||
|
|
7b302d8c29 | ||
|
|
a1fae41051 | ||
|
|
b16bb409fc | ||
|
|
a77d24fdc6 | ||
|
|
eecf07cd12 | ||
|
|
06be88aef7 | ||
|
|
4c5b1574f1 | ||
|
|
de57cfd6ff | ||
|
|
2b24c15938 | ||
|
|
e874f94cf8 | ||
|
|
0a410141ae | ||
|
|
e356b29b4a | ||
|
|
bc4ce26cef | ||
|
|
ae66f720f5 | ||
|
|
9c15ba0c0b | ||
|
|
b3060065d9 | ||
|
|
a313b16704 | ||
|
|
b93b9dae88 | ||
|
|
277142fc4b | ||
|
|
18a508b29c | ||
|
|
cdfad864ae | ||
|
|
fb4453c18a | ||
|
|
b5ff55e538 | ||
|
|
81a9133261 | ||
|
|
ca6f9c4155 | ||
|
|
7569180cac | ||
|
|
eeec373728 | ||
|
|
0604e43813 | ||
|
|
27ade502ee | ||
|
|
3a7468ecac | ||
|
|
7b05dacb98 | ||
|
|
b73efb2c22 | ||
|
|
a06a5cadee | ||
|
|
3cd2d6fa5c | ||
|
|
ec839e9e96 | ||
|
|
6f2f9f6677 | ||
|
|
2082fdf84a | ||
|
|
894485190b | ||
|
|
87b2455dbb | ||
|
|
2e68967e02 | ||
|
|
71422a8549 | ||
|
|
9c69a5df69 | ||
|
|
9b24995990 | ||
|
|
142f06357f | ||
|
|
18ea68c23a | ||
|
|
4b2622d93b | ||
|
|
ae0193698e | ||
|
|
6b4a638415 | ||
|
|
4b3c9f5b35 | ||
|
|
6c69549ced | ||
|
|
5868750aba | ||
|
|
ab3052f642 | ||
|
|
53f4ccb809 | ||
|
|
077ae99438 | ||
|
|
fe8adf9eb8 | ||
|
|
c713d550c0 | ||
|
|
d19d89ea9d | ||
|
|
e8ec41fb0f | ||
|
|
7682ec029b | ||
|
|
03f726038e | ||
|
|
796945aad0 | ||
|
|
db02c13bf6 | ||
|
|
1ce9a87544 | ||
|
|
35e6e6347a | ||
|
|
54708233ac | ||
|
|
c679fd1924 | ||
|
|
9a79154d05 | ||
|
|
60872c91e6 | ||
|
|
afc4aedd4f | ||
|
|
f8ba899b87 | ||
|
|
52447e5d46 | ||
|
|
6794149ec8 | ||
|
|
a4772ae606 | ||
|
|
f30e09856c | ||
|
|
9b7f978e3e | ||
|
|
f17417219a | ||
|
|
d7f84aed8a | ||
|
|
deecbadda9 | ||
|
|
1431ffcebf | ||
|
|
1a2a301aa2 |
7
.github/pull_request_template.md
vendored
7
.github/pull_request_template.md
vendored
@@ -2,9 +2,14 @@
|
||||
|
||||
- **Please check if the PR fulfills these requirements**
|
||||
|
||||
* [ ] Cheatsheets are up-to-date (run `go run scripts/cheatsheet/main.go generate`)
|
||||
* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
|
||||
* [ ] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
|
||||
* [ ] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide)
|
||||
* [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
|
||||
* [ ] Docs (specifically `docs/Config.md`) have been updated if necessary
|
||||
* [ ] You've read through your own file changes for silly mistakes etc
|
||||
|
||||
<!--
|
||||
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
|
||||
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for examples
|
||||
-->
|
||||
|
||||
6
.github/workflows/cd.yml
vendored
6
.github/workflows/cd.yml
vendored
@@ -14,11 +14,11 @@ jobs:
|
||||
- name: Unshallow repo
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
go-version: 1.20.x
|
||||
- name: Run goreleaser
|
||||
uses: goreleaser/goreleaser-action@v1
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: v1.17.2
|
||||
|
||||
129
.github/workflows/ci.yml
vendored
129
.github/workflows/ci.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Continuous Integration
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.18
|
||||
GO_VERSION: 1.20
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -30,22 +30,20 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
${{matrix.cache_path}}
|
||||
~/go/pkg/mod
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||
restore-keys: |
|
||||
${{runner.os}}-go-
|
||||
go-version: 1.20.x
|
||||
- name: Test code
|
||||
# we're passing -short so that we skip the integration tests, which will be run in parallel below
|
||||
run: |
|
||||
go test ./... -short
|
||||
mkdir -p /tmp/code_coverage
|
||||
go test ./... -short -cover -args "-test.gocoverdir=/tmp/code_coverage"
|
||||
- name: Upload code coverage artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage-unit-${{ matrix.os }}-${{ github.run_id }}
|
||||
path: /tmp/code_coverage
|
||||
|
||||
integration-tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -89,23 +87,23 @@ jobs:
|
||||
path: ~/git-${{matrix.git-version}}
|
||||
key: ${{runner.os}}-git-${{matrix.git-version}}
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||
restore-keys: |
|
||||
${{runner.os}}-go-
|
||||
go-version: 1.20.x
|
||||
- name: Print git version
|
||||
run: git --version
|
||||
- name: Test code
|
||||
env:
|
||||
# See https://go.dev/blog/integration-test-coverage
|
||||
LAZYGIT_GOCOVERDIR: /tmp/code_coverage
|
||||
run: |
|
||||
mkdir -p /tmp/code_coverage
|
||||
./scripts/run_integration_tests.sh
|
||||
- name: Upload code coverage artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage-integration-${{ matrix.git-version }}-${{ github.run_id }}
|
||||
path: /tmp/code_coverage
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@@ -115,18 +113,9 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
|
||||
restore-keys: |
|
||||
${{runner.os}}-go-
|
||||
go-version: 1.20.x
|
||||
- name: Build linux binary
|
||||
run: |
|
||||
GOOS=linux go build
|
||||
@@ -151,29 +140,21 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
|
||||
restore-keys: |
|
||||
${{runner.os}}-go-
|
||||
- name: Check Cheatsheet
|
||||
run: |
|
||||
go run scripts/cheatsheet/main.go check
|
||||
go-version: 1.20.x
|
||||
- name: Check Vendor Directory
|
||||
# ensure our vendor directory matches up with our go modules
|
||||
run: |
|
||||
go mod vendor && git diff --exit-code || (echo "Unexpected change to vendor directory. Run 'go mod vendor' locally and commit the changes" && exit 1)
|
||||
- name: Check Integration Test List
|
||||
# ensure our integration test list is up to date
|
||||
- name: Check go.mod file
|
||||
# ensure our go.mod file is clean
|
||||
run: |
|
||||
go generate pkg/integration/tests/tests.go && git diff --exit-code || (echo "Integration test list not up to date. Run 'go generate pkg/integration/tests/tests.go' locally and commit the changes" && exit 1)
|
||||
go mod tidy && git diff --exit-code || (echo "go.mod file is not clean. Run 'go mod tidy' locally and commit the changes" && exit 1)
|
||||
- name: Check All Auto-Generated Files
|
||||
# ensure all our auto-generated files are up to date
|
||||
run: |
|
||||
go generate ./... && git diff --quiet || (git status -s; echo "Auto-generated files not up to date. Run 'go generate ./...' locally and commit the changes" && exit 1)
|
||||
shell: bash # needed so that we get "-o pipefail"
|
||||
- name: Check Filenames
|
||||
run: scripts/check_filenames.sh
|
||||
@@ -185,20 +166,11 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||
restore-keys: |
|
||||
${{runner.os}}-go-
|
||||
go-version: 1.20.x
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v3.1.0
|
||||
uses: golangci/golangci-lint-action@v3.7.0
|
||||
with:
|
||||
version: latest
|
||||
- name: errors
|
||||
@@ -206,9 +178,38 @@ jobs:
|
||||
if: ${{ failure() }}
|
||||
check-required-label:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref != 'refs/heads/master'
|
||||
steps:
|
||||
- uses: mheap/github-action-required-labels@v5
|
||||
with:
|
||||
mode: exactly
|
||||
count: 1
|
||||
labels: "ignore-for-release, feature, enhancement, bug, maintenance, docs, i18n"
|
||||
upload-coverage:
|
||||
# List all jobs that produce coverage files
|
||||
needs: [unit-tests, integration-tests]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download all coverage artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: /tmp/code_coverage
|
||||
|
||||
- name: Combine coverage files
|
||||
run: |
|
||||
# Find all directories in /tmp/code_coverage and create a comma-separated list
|
||||
COVERAGE_DIRS=$(find /tmp/code_coverage -mindepth 1 -maxdepth 1 -type d -printf '/tmp/code_coverage/%f,' | sed 's/,$//')
|
||||
echo "Coverage directories: $COVERAGE_DIRS"
|
||||
# Run the combine command with the generated list
|
||||
go tool covdata textfmt -i=$COVERAGE_DIRS -o coverage.out
|
||||
echo "Combined coverage:"
|
||||
go tool cover -func coverage.out | tail -1 | awk '{print $3}'
|
||||
|
||||
- name: Upload to Codacy
|
||||
run: |
|
||||
CODACY_PROJECT_TOKEN=${{ secrets.CODACY_PROJECT_TOKEN }} \
|
||||
bash <(curl -Ls https://coverage.codacy.com/get.sh) report \
|
||||
--force-coverage-parser go -r coverage.out
|
||||
|
||||
2
.github/workflows/sponsors.yml
vendored
2
.github/workflows/sponsors.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Generate Sponsors 💖
|
||||
uses: JamesIves/github-sponsors-readme-action@v1.0.8
|
||||
uses: JamesIves/github-sponsors-readme-action@v1.2.2
|
||||
with:
|
||||
token: ${{ secrets.SPONSORS_TOKEN }}
|
||||
file: 'README.md'
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -35,7 +35,12 @@ lazygit.exe
|
||||
|
||||
test/git_server/data
|
||||
|
||||
test/results/**
|
||||
test/_results/**
|
||||
|
||||
oryxBuildBinary
|
||||
__debug_bin
|
||||
|
||||
.worktrees
|
||||
demo/output/*
|
||||
|
||||
coverage.out
|
||||
|
||||
@@ -20,10 +20,15 @@ linters:
|
||||
linters-settings:
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
|
||||
staticcheck:
|
||||
# SA1019 is for checking that we're not using fields marked as deprecated
|
||||
# in a comment. It decides this in a loose way so I'm silencing it. Also because
|
||||
# it's tripping on our own structs.
|
||||
checks: ["all", "-SA1019"]
|
||||
nakedret:
|
||||
# the gods will judge me but I just don't like naked returns at all
|
||||
max-func-lines: 0
|
||||
|
||||
run:
|
||||
go: 1.18
|
||||
go: '1.20'
|
||||
timeout: 10m
|
||||
|
||||
39
.vscode/launch.json
vendored
39
.vscode/launch.json
vendored
@@ -7,7 +7,11 @@
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "main.go",
|
||||
"args": ["--debug", "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"],
|
||||
"args": [
|
||||
"--debug",
|
||||
"--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
|
||||
],
|
||||
"hideSystemGoroutines": true,
|
||||
"console": "integratedTerminal",
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
@@ -19,17 +23,44 @@
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "main.go",
|
||||
"args": ["--logs", "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"],
|
||||
"args": [
|
||||
"--logs",
|
||||
"--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Attach to a running Lazygit",
|
||||
"type": "go",
|
||||
"request": "attach",
|
||||
"mode": "local",
|
||||
"processId": "lazygit",
|
||||
"hideSystemGoroutines": true,
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
// To use this, first start an integration test with the "cli" runner and
|
||||
// use the -debug option; e.g.
|
||||
// $ make integration-test-cli -- -debug tag/reset.go
|
||||
"name": "Attach to integration test runner",
|
||||
"type": "go",
|
||||
"request": "attach",
|
||||
"mode": "local",
|
||||
"processId": "test_lazygit",
|
||||
"hideSystemGoroutines": true,
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Run with logs",
|
||||
"configurations": ["Tail Lazygit logs", "Debug Lazygit"],
|
||||
"configurations": [
|
||||
"Tail Lazygit logs",
|
||||
"Debug Lazygit"
|
||||
],
|
||||
"stopAll": true
|
||||
}
|
||||
]
|
||||
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"gopls": {
|
||||
"formatting.gofumpt": true,
|
||||
},
|
||||
}
|
||||
@@ -10,6 +10,15 @@ before making a change.
|
||||
|
||||
[This video](https://www.youtube.com/watch?v=kNavnhzZHtk) walks through the process of adding a small feature to lazygit. If you have no idea where to start, watching that video is a good first step.
|
||||
|
||||
## Codebase guide
|
||||
|
||||
[This doc](./docs/dev/Codebase_Guide.md) explains:
|
||||
* what the different packages in the codebase are for
|
||||
* where important files live
|
||||
* important concepts in the code
|
||||
* how the event loop works
|
||||
* other useful information
|
||||
|
||||
## All code changes happen through Pull Requests
|
||||
|
||||
Pull requests are the best way to propose changes to the codebase. We actively
|
||||
@@ -21,6 +30,8 @@ welcome your pull requests:
|
||||
4. Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||
5. Issue that pull request!
|
||||
|
||||
Please do not raise pull request from your fork's master branch: make a feature branch instead. Lazygit maintainers will sometimes push changes to your branch when reviewing a PR and we often can't do this if you use your master branch.
|
||||
|
||||
If you've never written Go in your life, then join the club! Lazygit was the maintainer's first Go program, and most contributors have never used Go before. Go is widely considered an easy-to-learn language, so if you're looking for an open source project to gain dev experience, you've come to the right place.
|
||||
|
||||
## Running in a VSCode dev container
|
||||
@@ -97,6 +108,10 @@ To run gofumpt from your terminal go:
|
||||
go install mvdan.cc/gofumpt@latest && gofumpt -l -w .
|
||||
```
|
||||
|
||||
## Programming Font
|
||||
|
||||
Lazygit supports [Nerd Fonts](https://www.nerdfonts.com) to render certain icons. Sometimes we use some of these icons verbatim in string literals in the code (mainly in tests), so you need to set your development environment to use a nerd font to see these.
|
||||
|
||||
## Internationalisation
|
||||
|
||||
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `self.c.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# docker build -t lazygit .
|
||||
# docker run -it lazygit:latest /bin/sh
|
||||
|
||||
FROM golang:1.18 as build
|
||||
FROM golang:1.20 as build
|
||||
WORKDIR /go/src/github.com/jesseduffield/lazygit/
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
68
Makefile
Normal file
68
Makefile
Normal file
@@ -0,0 +1,68 @@
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -gcflags='all=-N -l'
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
go install
|
||||
|
||||
.PHONY: run
|
||||
run: build
|
||||
./lazygit
|
||||
|
||||
# Run `make run-debug` in one terminal tab and `make print-log` in another to view the program and its log output side by side
|
||||
.PHONY: run-debug
|
||||
run-debug:
|
||||
go run main.go -debug
|
||||
|
||||
.PHONY: print-log
|
||||
print-log:
|
||||
go run main.go --logs
|
||||
|
||||
.PHONY: unit-test
|
||||
unit-test:
|
||||
go test ./... -short
|
||||
|
||||
.PHONY: test
|
||||
test: unit-test integration-test-all
|
||||
|
||||
# Generate all our auto-generated files (test list, cheatsheets, maybe other things in the future)
|
||||
.PHONY: generate
|
||||
generate:
|
||||
go generate ./...
|
||||
|
||||
.PHONY: format
|
||||
format:
|
||||
gofumpt -l -w .
|
||||
|
||||
# For more details about integration test, see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md.
|
||||
.PHONY: integration-test-tui
|
||||
integration-test-tui:
|
||||
go run cmd/integration_test/main.go tui $(filter-out $@,$(MAKECMDGOALS))
|
||||
|
||||
.PHONY: integration-test-cli
|
||||
integration-test-cli:
|
||||
go run cmd/integration_test/main.go cli $(filter-out $@,$(MAKECMDGOALS))
|
||||
|
||||
.PHONY: integration-test-all
|
||||
integration-test-all:
|
||||
go test pkg/integration/clients/*.go
|
||||
|
||||
.PHONY: bump-gocui
|
||||
bump-gocui:
|
||||
scripts/bump_gocui.sh
|
||||
|
||||
.PHONY: bump-lazycore
|
||||
bump-lazycore:
|
||||
scripts/bump_lazycore.sh
|
||||
|
||||
.PHONY: record-demo
|
||||
record-demo:
|
||||
demo/record_demo.sh $(filter-out $@,$(MAKECMDGOALS))
|
||||
|
||||
.PHONY: vendor
|
||||
vendor:
|
||||
go mod vendor && go mod tidy
|
||||
@@ -16,7 +16,7 @@ Usage:
|
||||
> go run cmd/integration_test/main.go cli [--slow] [--sandbox] <test1> <test2> ...
|
||||
If you pass no test names, it runs all tests
|
||||
Accepted environment variables:
|
||||
KEY_PRESS_DELAY (e.g. 200): the number of milliseconds to wait between keypresses
|
||||
INPUT_DELAY (e.g. 200): the number of milliseconds to wait between keypresses or mouse clicks
|
||||
|
||||
TUI mode:
|
||||
> go run cmd/integration_test/main.go tui
|
||||
@@ -26,6 +26,29 @@ Usage:
|
||||
> go run cmd/integration_test/main.go help
|
||||
`
|
||||
|
||||
type flagInfo struct {
|
||||
name string // name of the flag; can be used with "-" or "--"
|
||||
flag *bool // a pointer to the variable that should be set to true when this flag is passed
|
||||
}
|
||||
|
||||
// Takes the args that you want to parse (excluding the program name and any
|
||||
// subcommands), and returns the remaining args with the flags removed
|
||||
func parseFlags(args []string, flags []flagInfo) []string {
|
||||
outer:
|
||||
for len(args) > 0 {
|
||||
for _, f := range flags {
|
||||
if args[0] == "-"+f.name || args[0] == "--"+f.name {
|
||||
*f.flag = true
|
||||
args = args[1:]
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatal(usage)
|
||||
@@ -35,23 +58,26 @@ func main() {
|
||||
case "help":
|
||||
fmt.Println(usage)
|
||||
case "cli":
|
||||
testNames := os.Args[2:]
|
||||
slow := false
|
||||
sandbox := false
|
||||
// get the next arg if it's --slow
|
||||
if len(os.Args) > 2 {
|
||||
if os.Args[2] == "--slow" || os.Args[2] == "-slow" {
|
||||
testNames = os.Args[3:]
|
||||
slow = true
|
||||
} else if os.Args[2] == "--sandbox" || os.Args[2] == "-sandbox" {
|
||||
testNames = os.Args[3:]
|
||||
sandbox = true
|
||||
}
|
||||
}
|
||||
|
||||
clients.RunCLI(testNames, slow, sandbox)
|
||||
waitForDebugger := false
|
||||
raceDetector := false
|
||||
testNames := parseFlags(os.Args[2:], []flagInfo{
|
||||
{"slow", &slow},
|
||||
{"sandbox", &sandbox},
|
||||
{"debug", &waitForDebugger},
|
||||
{"race", &raceDetector},
|
||||
})
|
||||
clients.RunCLI(testNames, slow, sandbox, waitForDebugger, raceDetector)
|
||||
case "tui":
|
||||
clients.RunTUI()
|
||||
raceDetector := false
|
||||
remainingArgs := parseFlags(os.Args[2:], []flagInfo{
|
||||
{"race", &raceDetector},
|
||||
})
|
||||
if len(remainingArgs) > 0 {
|
||||
log.Fatal("tui only supports the -race argument.")
|
||||
}
|
||||
clients.RunTUI(raceDetector)
|
||||
default:
|
||||
log.Fatal(usage)
|
||||
}
|
||||
|
||||
2
demo/README.md
Normal file
2
demo/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
This directory contains stuff for recording lazygit demos.
|
||||
|
||||
112
demo/config.yml
Normal file
112
demo/config.yml
Normal file
@@ -0,0 +1,112 @@
|
||||
# Specify a command to be executed
|
||||
# like `/bin/bash -l`, `ls`, or any other commands
|
||||
# the default is bash for Linux
|
||||
# or powershell.exe for Windows
|
||||
command: echo "YOU NEED TO SPECIFY YOUR OWN COMMAND WITH THE -d ARG"
|
||||
|
||||
# Specify the current working directory path
|
||||
# the default is the current working directory path
|
||||
cwd: null
|
||||
|
||||
# Export additional ENV variables
|
||||
env:
|
||||
recording: true
|
||||
|
||||
# Explicitly set the number of columns
|
||||
# or use `auto` to take the current
|
||||
# number of columns of your shell
|
||||
cols: 120 # 100
|
||||
|
||||
# Explicitly set the number of rows
|
||||
# or use `auto` to take the current
|
||||
# number of rows of your shell
|
||||
rows: 35 # 30
|
||||
|
||||
# Amount of times to repeat GIF
|
||||
# If value is -1, play once
|
||||
# If value is 0, loop indefinitely
|
||||
# If value is a positive number, loop n times
|
||||
repeat: 0
|
||||
|
||||
# Quality
|
||||
# 1 - 100
|
||||
# Higher quality seems to make no difference, but running it through
|
||||
# gifsicle ends up with a much better compressed version.
|
||||
quality: 100
|
||||
|
||||
# Delay between frames in ms
|
||||
# If the value is `auto` use the actual recording delays
|
||||
frameDelay: auto
|
||||
|
||||
# Maximum delay between frames in ms
|
||||
# Ignored if the `frameDelay` isn't set to `auto`
|
||||
# Set to `auto` to prevent limiting the max idle time
|
||||
maxIdleTime: 2000
|
||||
|
||||
# The surrounding frame box
|
||||
# The `type` can be null, window, floating, or solid`
|
||||
# To hide the title use the value null
|
||||
# Don't forget to add a backgroundColor style with a null as type
|
||||
frameBox:
|
||||
type: floating
|
||||
title: Lazygit
|
||||
style:
|
||||
border: 0px black solid
|
||||
backgroundColor: "#1d1d1d"
|
||||
margin: -5px
|
||||
|
||||
# Add a watermark image to the rendered gif
|
||||
# You need to specify an absolute path for
|
||||
# the image on your machine or a URL, and you can also
|
||||
# add your own CSS styles
|
||||
watermark:
|
||||
imagePath: null
|
||||
style:
|
||||
position: absolute
|
||||
right: 15px
|
||||
bottom: 15px
|
||||
width: 100px
|
||||
opacity: 0.9
|
||||
|
||||
# Cursor style can be one of
|
||||
# `block`, `underline`, or `bar`
|
||||
cursorStyle: block
|
||||
|
||||
# Font family
|
||||
# You can use any font that is installed on your machine
|
||||
# in CSS-like syntax
|
||||
# Download from:
|
||||
# https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/DejaVuSansMono.zip
|
||||
# Not using the mono font because it makes icons too small.
|
||||
fontFamily: "DejaVuSansM Nerd Font"
|
||||
|
||||
# The size of the font
|
||||
fontSize: 8
|
||||
|
||||
# The height of lines
|
||||
lineHeight: 1
|
||||
|
||||
# The spacing between letters
|
||||
letterSpacing: 0
|
||||
|
||||
# Theme
|
||||
theme:
|
||||
background: "transparent"
|
||||
foreground: "#dddad6"
|
||||
cursor: "#c7c7c7"
|
||||
black: "#7a7a7a"
|
||||
red: "#fc4384"
|
||||
green: "#b3e33b"
|
||||
yellow: "#ffa727"
|
||||
blue: "#102895"
|
||||
magenta: "#c930c7"
|
||||
cyan: "#00c5c7"
|
||||
white: "#c7c7c7"
|
||||
brightBlack: "#676767"
|
||||
brightRed: "#ff7fac"
|
||||
brightGreen: "#c8ed71"
|
||||
brightYellow: "#ebdf86"
|
||||
brightBlue: "#6871ff"
|
||||
brightMagenta: "#ff76ff"
|
||||
brightCyan: "#5ffdff"
|
||||
brightWhite: "#fffefe"
|
||||
81
demo/record_demo.sh
Executable file
81
demo/record_demo.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
TYPE=$1
|
||||
TEST=$2
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [gif|mp4] <test path>"
|
||||
echo "e.g. using full path: $0 gif pkg/integration/tests/demo/nuke_working_tree.go"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ "$#" -ne 2 ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ "$TYPE" != "gif" ] && [ "$TYPE" != "mp4" ]
|
||||
then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$TEST" ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
|
||||
WORKTREE_PATH=$(git worktree list | grep assets | awk '{print $1}')
|
||||
|
||||
if [ -z "$WORKTREE_PATH" ]
|
||||
then
|
||||
echo "Could not find assets worktree. You'll need to create a worktree for the assets branch using the following command:"
|
||||
echo "git worktree add .worktrees/assets assets"
|
||||
echo "The assets branch has no shared history with the main branch: it exists to store assets which are too large to store in the main branch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUTPUT_DIR="$WORKTREE_PATH/demo"
|
||||
|
||||
if ! command -v terminalizer &> /dev/null
|
||||
then
|
||||
echo "terminalizer could not be found"
|
||||
echo "Install it with: npm install -g terminalizer"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v "gifsicle" &> /dev/null
|
||||
then
|
||||
echo "gifsicle could not be found"
|
||||
echo "Install it with: npm install -g gifsicle"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get last part of the test path and set that as the output name
|
||||
# example test path: pkg/integration/tests/01_basic_test.go
|
||||
# For that we want: NAME=01_basic_test
|
||||
NAME=$(echo "$TEST" | sed -e 's/.*\///' | sed -e 's/\..*//')
|
||||
|
||||
# Add the demo to the tests list (if missing) so that it can be run
|
||||
go generate pkg/integration/tests/tests.go
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# First we record the demo into a yaml representation
|
||||
terminalizer -c demo/config.yml record --skip-sharing -d "go run cmd/integration_test/main.go cli --slow $TEST" "$OUTPUT_DIR/$NAME"
|
||||
# Then we render it into a gif
|
||||
terminalizer render "$OUTPUT_DIR/$NAME" -o "$OUTPUT_DIR/$NAME.gif"
|
||||
|
||||
# Then we convert it to either an mp4 or gif based on the command line argument
|
||||
if [ "$TYPE" = "mp4" ]
|
||||
then
|
||||
COMPRESSED_PATH="$OUTPUT_DIR/$NAME.mp4"
|
||||
ffmpeg -y -i "$OUTPUT_DIR/$NAME.gif" -movflags faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" "$COMPRESSED_PATH"
|
||||
else
|
||||
COMPRESSED_PATH="$OUTPUT_DIR/$NAME-compressed.gif"
|
||||
gifsicle --colors 256 --use-col=web -O3 < "$OUTPUT_DIR/$NAME.gif" > "$COMPRESSED_PATH"
|
||||
fi
|
||||
|
||||
echo "Demo recorded to $COMPRESSED_PATH"
|
||||
100
docs/Config.md
100
docs/Config.md
@@ -3,7 +3,7 @@
|
||||
Default path for the config file:
|
||||
|
||||
- Linux: `~/.config/lazygit/config.yml`
|
||||
- MacOS: `~/Library/Application Support/lazygit/config.yml`
|
||||
- MacOS: `~/Library/Application\ Support/lazygit/config.yml`
|
||||
- Windows: `%APPDATA%\lazygit\config.yml`
|
||||
|
||||
For old installations (slightly embarrassing: I didn't realise at the time that you didn't need to supply a vendor name to the path so I just used my name):
|
||||
@@ -16,6 +16,17 @@ If you want to change the config directory:
|
||||
|
||||
- MacOS: `export XDG_CONFIG_HOME="$HOME/.config"`
|
||||
|
||||
JSON schema is available for `config.yml` so that IntelliSense in Visual Studio Code (completion and error checking) is automatically enabled when the [YAML Red Hat][yaml] extension is installed. However, note that automatic schema detection only works if your config file is in one of the standard paths mentioned above. If you override the path to the file, you can still make IntelliSense work by adding
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/lazygit.json
|
||||
```
|
||||
|
||||
to the top of your config file or via [Visual Studio Code settings.json config][settings].
|
||||
|
||||
[yaml]: https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml
|
||||
[settings]: https://github.com/redhat-developer/vscode-yaml#associating-a-schema-to-a-glob-pattern-via-yamlschemas
|
||||
|
||||
## Default
|
||||
|
||||
```yaml
|
||||
@@ -24,10 +35,13 @@ gui:
|
||||
windowSize: 'normal' # one of 'normal' | 'half' | 'full' default is 'normal'
|
||||
scrollHeight: 2 # how many lines you scroll by
|
||||
scrollPastBottom: true # enable scrolling past the bottom
|
||||
scrollOffMargin: 2 # how many lines to keep before/after the cursor when it reaches the top/bottom of the view; see 'Scroll-off Margin' section below
|
||||
scrollOffBehavior: 'margin' # one of 'margin' | 'jump'; see 'Scroll-off Margin' section below
|
||||
sidePanelWidth: 0.3333 # number from 0 to 1
|
||||
expandFocusedSidePanel: false
|
||||
mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical'
|
||||
language: 'auto' # one of 'auto' | 'en' | 'zh' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
enlargedSideViewLocation: 'left' # one of 'left' | 'top'
|
||||
language: 'auto' # one of 'auto' | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
|
||||
timeFormat: '02 Jan 06' # https://pkg.go.dev/time#Time.Format
|
||||
shortTimeFormat: '3:04PM'
|
||||
theme:
|
||||
@@ -62,15 +76,17 @@ gui:
|
||||
showListFooter: true # for seeing the '5 of 20' message in list panels
|
||||
showRandomTip: true
|
||||
showBranchCommitHash: false # show commit hashes alongside branch names
|
||||
experimentalShowBranchHeads: false # visualize branch heads with (*) in commits list
|
||||
showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you)
|
||||
showPanelJumps: true # for showing the jump-to-panel keybindings as panel subtitles
|
||||
showCommandLog: true
|
||||
showIcons: false # deprecated: use nerdFontsVersion instead
|
||||
nerdFontsVersion: "" # nerd fonts version to use ("2" or "3"); empty means don't show nerd font icons
|
||||
commandLogSize: 8
|
||||
splitDiff: 'auto' # one of 'auto' | 'always'
|
||||
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
|
||||
border: 'single' # one of 'single' | 'double' | 'rounded' | 'hidden'
|
||||
border: 'rounded' # one of 'single' | 'double' | 'rounded' | 'hidden'
|
||||
animateExplosion: true # shows an explosion animation when nuking the working tree
|
||||
portraitMode: 'auto' # one of 'auto' | 'never' | 'always'
|
||||
git:
|
||||
paging:
|
||||
colorArg: always
|
||||
@@ -104,8 +120,8 @@ git:
|
||||
overrideGpg: false # prevents lazygit from spawning a separate process when using GPG
|
||||
disableForcePushing: false
|
||||
parseEmoji: false
|
||||
diffContextSize: 3 # how many lines of context are shown around a change in diffs
|
||||
os:
|
||||
copyToClipboardCmd: '' # See 'Custom Command for Copying to Clipboard' section
|
||||
editPreset: '' # see 'Configuring File Editing' section
|
||||
edit: ''
|
||||
editAtLine: ''
|
||||
@@ -148,7 +164,7 @@ keybinding:
|
||||
jumpToBlock: ['1', '2', '3', '4', '5'] # goto the Nth block / panel
|
||||
nextMatch: 'n'
|
||||
prevMatch: 'N'
|
||||
optionMenu: null # show help menu
|
||||
optionMenu: <disabled> # show help menu
|
||||
optionMenu-alt1: '?' # show help menu
|
||||
select: '<space>'
|
||||
goInto: '<enter>'
|
||||
@@ -193,6 +209,8 @@ keybinding:
|
||||
commitChangesWithoutHook: 'w' # commit changes without pre-commit hook
|
||||
amendLastCommit: 'A'
|
||||
commitChangesWithEditor: 'C'
|
||||
findBaseCommitForFixup: '<c-f>'
|
||||
confirmDiscard: 'x'
|
||||
ignoreFile: 'i'
|
||||
refreshFiles: 'r'
|
||||
stashAllChanges: 's'
|
||||
@@ -278,29 +296,36 @@ os:
|
||||
open: 'open {{filename}}'
|
||||
```
|
||||
|
||||
### Configuring File Editing
|
||||
## Custom Command for Copying to Clipboard
|
||||
```yaml
|
||||
os:
|
||||
copyToClipboardCmd: ''
|
||||
```
|
||||
Specify an external command to invoke when copying to clipboard is requested. `{{text}` will be replaced by text to be copied. Default is to copy to system clipboard.
|
||||
|
||||
There are two commands for opening files, `o` for "open" and `e` for "edit". `o`
|
||||
acts as if the file was double-clicked in the Finder/Explorer, so it also works
|
||||
for non-text files, whereas `e` opens the file in an editor. `e` can also jump
|
||||
to the right line in the file if you invoke it from the staging panel, for
|
||||
example.
|
||||
If you are working on a terminal that supports OSC52, the following command will let you take advantage of it:
|
||||
```
|
||||
os:
|
||||
copyToClipboardCmd: printf "\033]52;c;$(printf {{text}} | base64)\a" > /dev/tty
|
||||
```
|
||||
|
||||
To tell lazygit which editor to use for the `e` command, the easiest way to do
|
||||
that is to provide an editPreset config, e.g.
|
||||
|
||||
## Configuring File Editing
|
||||
|
||||
There are two commands for opening files, `o` for "open" and `e` for "edit". `o` acts as if the file was double-clicked in the Finder/Explorer, so it also works for non-text files, whereas `e` opens the file in an editor. `e` can also jump to the right line in the file if you invoke it from the staging panel, for example.
|
||||
|
||||
To tell lazygit which editor to use for the `e` command, the easiest way to do that is to provide an editPreset config, e.g.
|
||||
|
||||
```yaml
|
||||
os:
|
||||
editPreset: 'vscode'
|
||||
```
|
||||
|
||||
Supported presets are `vim`, `nvim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`,
|
||||
`kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset
|
||||
from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
Supported presets are `vim`, `nvim`, `nvim-remote`, `lvim`, `emacs`, `nano`, `micro`, `vscode`, `sublime`, `bbedit`, `kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
|
||||
If for some reason you are not happy with the default commands from a preset, or
|
||||
there simply is no preset for your editor, you can customize the commands by
|
||||
setting the `edit`, `editAtLine`, and `editAtLineAndWait` options, e.g.:
|
||||
`nvim-remote` is an experimental preset for when you have invoked lazygit from within a neovim process, allowing lazygit to open the file from within the parent process rather than spawning a new one.
|
||||
|
||||
If for some reason you are not happy with the default commands from a preset, or there simply is no preset for your editor, you can customize the commands by setting the `edit`, `editAtLine`, and `editAtLineAndWait` options, e.g.:
|
||||
|
||||
```yaml
|
||||
os:
|
||||
@@ -308,15 +333,14 @@ os:
|
||||
editAtLine: 'myeditor --line={{line}} {{filename}}'
|
||||
editAtLineAndWait: 'myeditor --block --line={{line}} {{filename}}'
|
||||
editInTerminal: true
|
||||
openDirInEditor: 'myeditor {{dir}}'
|
||||
```
|
||||
|
||||
The `editInTerminal` option is used to decide whether lazygit needs to suspend
|
||||
itself to the background before calling the editor.
|
||||
The `editInTerminal` option is used to decide whether lazygit needs to suspend itself to the background before calling the editor. It should really be named `suspend` because for some cases like when lazygit is opened from within a neovim session and you're using the `nvim-remote` preset, you're technically still in a terminal. Nonetheless we're sticking with the name `editInTerminal` for backwards compatibility.
|
||||
|
||||
Contributions of new editor presets are welcome; see the `getPreset` function in
|
||||
[`editor_presets.go`](https://github.com/jesseduffield/lazygit/blob/master/pkg/config/editor_presets.go).
|
||||
Contributions of new editor presets are welcome; see the `getPreset` function in [`editor_presets.go`](https://github.com/jesseduffield/lazygit/blob/master/pkg/config/editor_presets.go).
|
||||
|
||||
### Overriding default config file location
|
||||
## Overriding default config file location
|
||||
|
||||
To override the default config directory, use `CONFIG_DIR="$HOME/.config/lazygit"`. This directory contains the config file in addition to some other files lazygit uses to keep track of state across sessions.
|
||||
|
||||
@@ -330,6 +354,14 @@ or
|
||||
LG_CONFIG_FILE="$HOME/.base_lg_conf,$HOME/.light_theme_lg_conf" lazygit
|
||||
```
|
||||
|
||||
## Scroll-off Margin
|
||||
|
||||
When the selected line gets close to the bottom of the window and you hit down-arrow, there's a feature called "scroll-off margin" that lets the view scroll a little earlier so that you can see a bit of what's coming in the direction that you are moving. This is controlled by the `gui.scrollOffMargin` setting (default: 2), so it keeps 2 lines below the selection visible as you scroll down. It can be set to 0 to scroll only when the selection reaches the bottom of the window.
|
||||
|
||||
That's the behavior when `gui.scrollOffBehavior` is set to "margin" (the default). If you set `gui.scrollOffBehavior` to "jump", then upon reaching the last line of a view and hitting down-arrow the view will scroll by half a page so that the selection ends up in the middle of the view. This may feel a little jarring because the cursor jumps around when continuously moving down, but it has the advantage that the view doesn't scroll as often.
|
||||
|
||||
This setting applies both to all list views (e.g. commits and branches etc), and to the staging view.
|
||||
|
||||
## Color Attributes
|
||||
|
||||
For color attributes you can choose an array of attributes (with max one color attribute)
|
||||
@@ -426,19 +458,18 @@ gui:
|
||||
nerdFontsVersion: "3"
|
||||
```
|
||||
|
||||
Supported versions are "2" and "3". The deprecated config `showIcons` sets the
|
||||
version to "2" for backwards compatibility.
|
||||
Supported versions are "2" and "3". The deprecated config `showIcons` sets the version to "2" for backwards compatibility.
|
||||
|
||||
## Keybindings
|
||||
|
||||
For all possible keybinding options, check [Custom_Keybindings.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md)
|
||||
|
||||
You can disable certain key bindings by specifying `null`.
|
||||
You can disable certain key bindings by specifying `<disabled>`.
|
||||
|
||||
```yaml
|
||||
keybinding:
|
||||
universal:
|
||||
edit: null # disable 'edit file'
|
||||
edit: <disabled> # disable 'edit file'
|
||||
```
|
||||
|
||||
### Example Keybindings For Colemak Users
|
||||
@@ -475,9 +506,7 @@ keybinding:
|
||||
|
||||
## Custom pull request URLs
|
||||
|
||||
Some git provider setups (e.g. on-premises GitLab) can have distinct URLs for git-related calls and
|
||||
the web interface/API itself. To work with those, Lazygit needs to know where it needs to create
|
||||
the pull request. You can do so on your `config.yml` file using the following syntax:
|
||||
Some git provider setups (e.g. on-premises GitLab) can have distinct URLs for git-related calls and the web interface/API itself. To work with those, Lazygit needs to know where it needs to create the pull request. You can do so on your `config.yml` file using the following syntax:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -492,8 +521,7 @@ Where:
|
||||
|
||||
## Predefined commit message prefix
|
||||
|
||||
In situations where certain naming pattern is used for branches and commits, pattern can be used to populate
|
||||
commit message with prefix that is parsed from the branch name.
|
||||
In situations where certain naming pattern is used for branches and commits, pattern can be used to populate commit message with prefix that is parsed from the branch name.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -523,9 +551,7 @@ Result:
|
||||
|
||||
## Launching not in a repository behaviour
|
||||
|
||||
By default, when launching lazygit from a directory that is not a repository,
|
||||
you will be prompted to choose if you would like to initialize a repo. You can
|
||||
override this behaviour in the config with one of the following:
|
||||
By default, when launching lazygit from a directory that is not a repository, you will be prompted to choose if you would like to initialize a repo. You can override this behaviour in the config with one of the following:
|
||||
|
||||
```yaml
|
||||
# for default prompting behaviour
|
||||
|
||||
@@ -74,6 +74,7 @@ The permitted contexts are:
|
||||
| -------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| status | The 'Status' tab |
|
||||
| files | The 'Files' tab |
|
||||
| worktrees | The 'Worktrees' tab |
|
||||
| localBranches | The 'Local Branches' tab |
|
||||
| remotes | The 'Remotes' tab |
|
||||
| remoteBranches | The context you get when pressing enter on a remote in the remotes tab |
|
||||
@@ -300,6 +301,7 @@ SelectedRemote
|
||||
SelectedTag
|
||||
SelectedStashEntry
|
||||
SelectedCommitFile
|
||||
SelectedWorktree
|
||||
CheckedOutBranch
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Custom Pagers
|
||||
|
||||
Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `o` in the Status panel).
|
||||
Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `e` in the Status panel).
|
||||
|
||||
Support does not extend to Windows users, because we're making use of a package which doesn't have Windows support.
|
||||
|
||||
@@ -62,3 +62,25 @@ git:
|
||||
```
|
||||
|
||||
If you set `useConfig: true`, lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
|
||||
|
||||
## Using external diff commands
|
||||
|
||||
Some diff tools can't work as a simple pager like the ones above do, because they need access to the entire diff, so just post-processing git's diff is not enough for them. The most notable example is probably [difftastic](https://difftastic.wilfred.me.uk).
|
||||
|
||||
These can be used in lazygit by using the `externalDiffCommand` config; in the case of difftastic, that could be
|
||||
|
||||
```yaml
|
||||
git:
|
||||
paging:
|
||||
externalDiffCommand: difft --color=always
|
||||
```
|
||||
|
||||
The `colorArg`, `pager`, and `useConfig` options are not used in this case.
|
||||
|
||||
You can add whatever extra arguments you prefer for your difftool; for instance
|
||||
|
||||
```yaml
|
||||
git:
|
||||
paging:
|
||||
externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off
|
||||
```
|
||||
|
||||
64
docs/Fixup_Commits.md
Normal file
64
docs/Fixup_Commits.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Fixup Commits
|
||||
|
||||
## Background
|
||||
|
||||
There's this common scenario that you have a PR in review, the reviewer is
|
||||
requesting some changes, and you make those changes and would normally simply
|
||||
squash them into the original commit that they came from. If you do that,
|
||||
however, there's no way for the reviewer to see what you changed. You could just
|
||||
make a separate commit with those changes at the end of the branch, but this is
|
||||
not ideal because it results in a git history that is not very clean.
|
||||
|
||||
To help with this, git has a concept of fixup commits: you do make a separate
|
||||
commit, but the subject of this commit is the string "fixup! " followed by the
|
||||
original commit subject. This both tells the reviewer what's going on (you are
|
||||
making a change that you later will squash into the designated commit), and it
|
||||
provides an easy way to actually perform this squash operation when you are
|
||||
ready to do that (before merging).
|
||||
|
||||
## Creating fixup commits
|
||||
|
||||
You could of course create fixup commits manually by typing in the commit
|
||||
message with the prefix yourself. But lazygit has an easier way to do that:
|
||||
in the Commits view, select the commit that you want to create a fixup for, and
|
||||
press shift-F (for "Create fixup commit for this commit"). This automatically
|
||||
creates a commit with the appropriate subject line.
|
||||
|
||||
Don't confuse this with the lowercase "f" command ("Fixup commit"); that one
|
||||
squashes the selected commit into its parent, this is not what we want here.
|
||||
|
||||
## Squashing fixup commits
|
||||
|
||||
When you're ready to merge the branch and want to squash all these fixup commits
|
||||
that you created, that's very easy to do: select the first commit of your branch
|
||||
and hit shift-S (for "Squash all 'fixup!' commits above selected commit
|
||||
(autosquash)"). Boom, done.
|
||||
|
||||
## Finding the commit to create a fixup for
|
||||
|
||||
When you are making changes to code that you changed earlier in a long branch,
|
||||
it can be tedious to find the commit to squash it into. Lazygit has a command to
|
||||
help you with this, too: in the Files view, press ctrl-f to select the right
|
||||
base commit in the Commits view automatically. From there, you can either press
|
||||
shift-F to create a fixup commit for it, or shift-A to amend your changes into
|
||||
the commit if you haven't published your branch yet.
|
||||
|
||||
This command works in many cases, and when it does it almost feels like magic,
|
||||
but it's important to understand its limitations because it doesn't always work.
|
||||
The way it works is that it looks at the deleted lines of your current
|
||||
modifications, blames them to find out which commit those lines come from, and
|
||||
if they all come from the same commit, it selects it. So here are cases where it
|
||||
doesn't work:
|
||||
|
||||
- Your current diff has only added lines, but no deleted lines. In this case
|
||||
there's no way for lazygit to know which commit you want to add them to.
|
||||
- The deleted lines belong to multiple different commits. In this case you can
|
||||
help lazygit by staging a set of files or hunks that all belong to the same
|
||||
commit; if some changes are staged, the ctrl-f command works only on those.
|
||||
- The found commit is already on master; in this case, lazygit refuses to select
|
||||
it, because it doesn't make sense to create fixups for it, let alone amend to
|
||||
it.
|
||||
|
||||
To sum it up: the command works great if you are changing code again that you
|
||||
changed or added earlier in the same branch. This is a common enough case to
|
||||
make the command useful.
|
||||
@@ -6,4 +6,5 @@
|
||||
* [Keybindings](./keybindings)
|
||||
* [Undo/Redo](./Undoing.md)
|
||||
* [Searching/Filtering](./Searching.md)
|
||||
* [Stacked Branches](./Stacked_Branches.md)
|
||||
* [Dev docs](./dev)
|
||||
|
||||
18
docs/Stacked_Branches.md
Normal file
18
docs/Stacked_Branches.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Working with stacked branches
|
||||
|
||||
When working on a large branch it can often be useful to break it down into
|
||||
smaller pieces, and it can help to create separate branches for each independent
|
||||
chunk of changes. For example, you could have one branch for preparatory
|
||||
refactorings, one for backend changes, and one for frontend changes. Those
|
||||
branches would then all be stacked onto each other.
|
||||
|
||||
Git has support for rebasing such a stack as a whole; you can enable it by
|
||||
setting the git config `rebase.updateRefs` to true. If you then rebase the
|
||||
topmost branch of the stack, the other ones in the stack will follow. This
|
||||
includes interactive rebases, so for example amending a commit in the first
|
||||
branch of the stack will "just work" in the sense that it keeps the other
|
||||
branches properly stacked onto it.
|
||||
|
||||
Lazygit visualizes the invidual branch heads in the stack by marking them with a
|
||||
cyan asterisk (or a cyan branch symbol if you are using [nerd
|
||||
fonts](Config.md#display-nerd-fonts-icons)).
|
||||
@@ -1,9 +1,9 @@
|
||||
# Undo/Redo in lazygit
|
||||
|
||||

|
||||
You can undo the last action by pressing 'z' and redo with `ctrl+z`. Here we drop a couple of commits and then undo the actions.
|
||||
Undo uses the reflog which is specific to commits and branches so we can't undo changes to the working tree or stash.
|
||||
|
||||
## Keybindings:
|
||||
'z' to undo, 'ctrl+z' to redo
|
||||

|
||||
|
||||
## How it works
|
||||
|
||||
|
||||
91
docs/dev/Codebase_Guide.md
Normal file
91
docs/dev/Codebase_Guide.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Lazygit Codebase Guide
|
||||
|
||||
## Packages
|
||||
|
||||
* `pkg/app`: Contains startup code, inititalises a bunch of stuff like logging, the user config, etc, before starting the gui. Catches and handles some errors that the gui raises.
|
||||
* `pkg/app/daemon`: Contains code relating to the lazygit daemon. This could be better named: it's is not a daemon in the sense that it's a long-running background process; rather it's a short-lived background process that we pass to git for certain tasks, like GIT_EDITOR for when we want to set the TODO file for an interactive rebase.
|
||||
* `pkg/cheatsheet`: Generates the keybinding cheatsheets in `docs/keybindings`.
|
||||
* `pkg/commands/git_commands`: All communication to the git binary happens here. So for example there's a `Checkout` method which calls `git checkout`.
|
||||
* `pkg/commands/oscommands`: Contains code for talking to the OS, and for invoking commands in general
|
||||
* `pkg/commands/git_config`: Reading of the git config all happens here.
|
||||
* `pkg/commands/hosting_service`: Contains code that is specific to git hosting services (aka forges).
|
||||
* `pkg/commands/models`: Contains model structs that represent commits, branches, files, etc.
|
||||
* `pkg/commands/patch`: Contains code for parsing and working with git patches
|
||||
* `pkg/common`: Contains the `Common` struct which holds common dependencies like the logger, i18n, and the user config. Most structs in the code will have a field named `c` which holds a common struct (or a derivative of the common struct).
|
||||
* `pkg/config`: Contains code relating to the Lazygit user config. Specifically `pkg/config/user_config/go` defines the user config struct and its default values.
|
||||
* `pkg/constants`: Contains some constant strings (e.g. links to docs)
|
||||
* `pkg/env`: Contains code relating to setting/getting environment variables
|
||||
* `pkg/i18n`: Contains internationalised strings
|
||||
* `pkg/integration`: Contains end-to-end tests
|
||||
* `pkg/jsonschema`: Contains generator for user config JSON schema.
|
||||
* `pkg/logs`: Contains code for instantiating the logger and for tailing the logs via `lazygit --logs`
|
||||
* `pkg/tasks`: Contains code for running asynchronous tasks: mostly related to efficiently rendering command output to the main window.
|
||||
* `pkg/theme`: Contains code related to colour themes.
|
||||
* `pkg/updates`: Contains code related to Lazygit updates (checking for update, download and installing the update)
|
||||
* `pkg/utils`: Contains lots of low-level helper functions
|
||||
* `pkg/gui`: Contains code related to the gui. We've still got a God Struct in the form of our Gui struct, but over time code has been moved out into contexts, controllers, and helpers, and we intend to continue moving more code out over time.
|
||||
* `pkg/gui/context`: Contains code relating to contexts. There is a context for each view e.g. a branches context, a tags context, etc. Contexts manage state related to the view and receive keypresses.
|
||||
* `pkg/gui/controllers`: Contains code relating to controllers. Controllers define a list of keybindings and their associated handlers. One controller can be assigned to multiple contexts, and one context can contain multiple controllers.
|
||||
* `pkg/gui/controllers/helpers`: Contains code that is shared between multiple controllers.
|
||||
* `pkg/gui/filetree`: Contains code relating to the representation of filetrees.
|
||||
* `pkg/gui/keybindings`: Contains code for mapping between keybindings and their labels
|
||||
* `pkg/gui/mergeconflicts`: Contains code relating to the handling of merge conflicts
|
||||
* `pkg/gui/modes`: Contains code relating to the state of different modes e.g. cherry picking mode, rebase mode.
|
||||
* `pkg/gui/patch_exploring`: Contains code relating to the state of patch-oriented views like the staging view.
|
||||
* `pkg/gui/popup`: Contains code that lets you easily raise popups
|
||||
* `pkg/gui/presentation`: Contains presentation code i.e. code concerned with rendering content inside views
|
||||
* `pkg/gui/services/custom_commands`: Contains code related to user-defined custom commands.
|
||||
* `pkg/gui/status`: Contains code for invoking loaders and toasts
|
||||
* `pkg/gui/style`: Contains code for specifying text styles (colour, bold, etc)
|
||||
* `pkg/gui/types`: Contains various gui-specific types and interfaces. Lots of code lives here to avoid circular dependencies
|
||||
* `vendor/github.com/jesseduffield/gocui`: Gocui is the underlying library used for handling the gui event loop, handling keypresses, and rendering the UI. It defines the View struct which our own context structs build upon.
|
||||
|
||||
## Important files
|
||||
|
||||
* `pkg/config/user_config.go`: defines the user config and default values
|
||||
* `pkg/gui/keybindings.go`: defines keybindings which have not yet been moved into a controller (originally all keybindings were defined here)
|
||||
* `pkg/gui/controllers.go`: links up controllers with contexts
|
||||
* `pkg/gui/controllers/helpers/helpers.go`: defines all the different helper structs
|
||||
* `pkg/commands/git.go`: defines all the different git command structs
|
||||
* `pkg/gui/gui.go`: defines the top-level gui state and gui initialisation/run code
|
||||
* `pkg/gui/layout.go`: defines what happens on each render
|
||||
* `pkg/gui/controllers/helpers/window_arrangement_helper.go`: defines the layout of the UI and the size/position of each window
|
||||
* `pkg/gui/context/context.go`: defines the different contexts
|
||||
* `pkg/gui/context/setup.go`: defines initialisation code for all contexts
|
||||
* `pkg/gui/context.go`: manages the lifecycle of contexts, the context stack, and focus changes.
|
||||
* `pkg/gui/types/views.go`: defines views
|
||||
* `pkg/gui/views.go`: defines the ordering of views (front to back) and their initialisation code
|
||||
* `pkg/gui/gui_common.go`: defines gui-specific methods that all controllers and helpers have access to
|
||||
* `pkg/i18n/english.go`: defines the set of i18n strings and their English values
|
||||
* `pkg/gui/controllers/helpers/refresh_helper.go`: manages refreshing of models. The refresh helper is typically invoked at the end of an action to re-load affected models from git (e.g. re-load branches after doing a git pull)
|
||||
* `vendor/github.com/jesseduffield/gocui/gui.go`: defines the gocui gui struct
|
||||
* `vendor/github.com/jesseduffield/gocui/view.go`: defines the gocui view struct
|
||||
|
||||
## Concepts
|
||||
|
||||
* **View**: Views are defined in the gocui package, and they maintain an internal buffer of content which is rendered each time the screen is drawn.
|
||||
* **Context**: A context is tied to a view and contains some additional state and logic specific to that view e.g. the branches context has code relating specifically to branches, and writes the list of branches to the branches view. Views and contexts share some responsibilities for historical reasons.
|
||||
* **Controller**: A controller defined keybindings with associated handlers. One controller can be assigned to multiple contexts and one context can have multiple controllers. For example the list controller handles keybindings relating to navigating a list, and is assigned to all list contexts (e.g. the branches context).
|
||||
* **Helper**: A helper defines shared code used by controllers, or used by some other parts of the application. Often a controller will have a method that ends up needing to be used by another controller, so in that case we move the method out into a helper so that both controllers can use it. We need to do this because controllers cannot refer to other controllers' methods.
|
||||
|
||||
In terms of dependencies, controllers sit at the highest level, so they can refer to helpers, contexts, and views (although it's preferable for view-specific code to live in contexts). Helpers can refer to contexts and views, and contexts can only refer to views. Views can't refer to contexts, controllers, or helpers.
|
||||
|
||||
* **Window**: A window is a section of the screen which will render a view. Windows are named after the default view that appears there, so for example there is a 'stash' window that is so named because by default the stash view appears there. But if you press enter on a stash entry, the stash entry's files will be shown in a different view, but in the same window.
|
||||
* **Panel**: The term 'panel' is still used in a few places to refer to either a view or a window, and it's a term that is now deprecated in favour of 'view' and 'window'.
|
||||
* **Tab**: Each tab in a window (e.g. Files, Worktrees, Submodules) actually has a corresponding view which we bring to the front upon changing tabs.
|
||||
* **Model**: Representation of a git object e.g. commits, branches, files.
|
||||
* **ViewModel**: Used by a context to maintain state related to the view.
|
||||
* **Common structs**: Most structs have a field named `c` which contains a 'common' struct: a struct containing a bag of dependencies that most structs of the same layer require. For example if you want to access a helper from a controller you can do so with `self.c.Helpers.MyHelper`.
|
||||
|
||||
## Event loop and threads
|
||||
|
||||
The event loop is managed in the `MainLoop` function of `vendor/github.com/jesseduffield/gocui/gui.go`. Any time there is an event like a key press or a window resize, the event will be processed and then the screen will be redrawn. This involves calling the `layout` function defined in `pkg/gui/layout.go`, which lays out the windows and invokes some on-render hooks.
|
||||
|
||||
Often, as part of handling a keypress, we'll want to run some code asynchronously so that it doesn't block the UI thread. For this we'll typically run `self.c.OnWorker(myFunc)`. If the worker wants to then do something on the UI thread again it can call `self.c.OnUIThread(myOtherFunc)`.
|
||||
|
||||
## Legacy code structure
|
||||
|
||||
Before we had controllers and contexts, all the code lived directly in the gui package under a gui God Struct. This was fairly bloated and so we split things out to have a better separation of concerns. Nonetheless, it's a big effort to migrate all the code so we still have some logic in the gui struct that ought to live somewhere else. Likewise, we have some keybindings defined in `pkg/gui/keybindings.go` that ought to live on a controller (all keybindings used to be defined in that one file).
|
||||
|
||||
The new structure has its own problems: we don't have a clear guide on whether code should live in a controller or helper. The current approach is to put code in a controller until it's needed by another controller, and to then extract it out into a helper. We may be better off just putting code in helpers to start with and leaving controllers super-thin, with the responsibility of just pairing keys with corresponding helper functions. But it's not clear to me if that would be better than the current approach.
|
||||
|
||||
82
docs/dev/Demo_Recordings.md
Normal file
82
docs/dev/Demo_Recordings.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Demo Recordings
|
||||
|
||||
We want our demo recordings to be consistent and easy to update if we make changes to Lazygit's UI. Luckily for us, we have an existing recording system for the sake of our integration tests, so we can piggyback on that.
|
||||
|
||||
You'll want to familiarise yourself with how integration tests are written: see [here](../../pkg/integration/README.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Ideally we'd run this whole thing through docker but we haven't got that working. So you will need:
|
||||
```
|
||||
# for recording
|
||||
npm i -g terminalizer
|
||||
# for gif compression
|
||||
npm i -g gifsicle
|
||||
# for mp4 conversion
|
||||
brew install ffmpeg
|
||||
|
||||
# font with icons
|
||||
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/DejaVuSansMono.tar.xz && \
|
||||
tar -xf DejaVuSansMono.tar.xz -C /usr/local/share/fonts && \
|
||||
rm DejaVuSansMono.tar.xz
|
||||
```
|
||||
|
||||
## Creating a demo
|
||||
|
||||
Demos are found in `pkg/integration/tests/demo/`. They are like regular integration tests but have `IsDemo: true` which has a few effects:
|
||||
* The bottom row of the UI is quieter so that we can render captions
|
||||
* Fetch/Push/Pull have artificial latency to mimic a network request
|
||||
* The loader at the bottom-right does not appear
|
||||
|
||||
In demos, we don't need to be as strict in our assertions as we are in tests. But it's still good to have some basic assertions so that if we automate the process of updating demos we'll know if one of them has broken.
|
||||
|
||||
You can use the same flow as we use with integration tests when you're writing a demo:
|
||||
* Setup the repo
|
||||
* Run the demo in sandbox mode to get a feel of what needs to happen
|
||||
* Come back and write the code to make it happen
|
||||
|
||||
### Adding captions
|
||||
|
||||
It's good to add captions explaining what task if being performed. Use the existing demos as a guide.
|
||||
|
||||
### Setting up the assets worktree
|
||||
|
||||
We store assets (which includes demo recordings) in the `assets` branch, which is a branch that shares no history with the main branch and exists purely for storing assets. Storing them separately means we don't clog up the code branches with large binaries.
|
||||
|
||||
The scripts and demo definitions live in the code branches but the output lives in the assets branch so to be able to create a video from a demo you'll need to create a linked worktree for the assets branch which you can do with:
|
||||
|
||||
```sh
|
||||
git worktree add .worktrees/assets assets
|
||||
```
|
||||
|
||||
Outputs will be stored in `.worktrees/assets/demos/`. We'll store three separate things:
|
||||
* the yaml of the recording
|
||||
* the original gif
|
||||
* either the compressed gif or the mp4 depending on the output you chose (see below)
|
||||
|
||||
### Recording the demo
|
||||
|
||||
Once you're happy with your demo you can record it using:
|
||||
```sh
|
||||
scripts/record_demo.sh [gif|mp4] <path>
|
||||
# e.g.
|
||||
scripts/record_demo.sh gif pkg/integration/tests/demo/interactive_rebase.go
|
||||
```
|
||||
|
||||
~~The gif format is for use in the first video of the readme (it has a larger size but has auto-play and looping)~~
|
||||
~~The mp4 format is for everything else (no looping, requires clicking, but smaller size).~~
|
||||
|
||||
Turns out that you can't store mp4s in a repo and link them from a README so we're gonna just use gifs across the board for now.
|
||||
|
||||
### Including demos in README/docs
|
||||
|
||||
If you've followed the above steps you'll end up with your output in your assets worktree.
|
||||
|
||||
Within that worktree, stage all three output files and raise a PR against the assets branch.
|
||||
|
||||
Then back in the code branch, in the doc, you can embed the recording like so:
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
This means we can update assets without needing to update the docs that embed them.
|
||||
@@ -1,4 +1,6 @@
|
||||
# Dev Documentation Overview
|
||||
|
||||
* [Busy/Idle tracking](./Busy.md).
|
||||
* [Codebase Guide](./Codebase_Guide.md)
|
||||
* [Busy/Idle Tracking](./Busy.md)
|
||||
* [Integration Tests](../../pkg/integration/README.md)
|
||||
* [Demo Recordings](./Demo_Recordings.md)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit Keybindings
|
||||
|
||||
@@ -52,6 +52,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||
<kbd>o</kbd>: Open file
|
||||
<kbd>e</kbd>: Edit file
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle file included in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
@@ -84,11 +85,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: Move commit down one
|
||||
<kbd><c-k></kbd>: Move commit up one
|
||||
<kbd>v</kbd>: Paste commits (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Amend commit with staged changes
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: Revert commit
|
||||
<kbd>T</kbd>: Tag commit
|
||||
<kbd><c-l></kbd>: Open log menu
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -96,6 +99,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
</pre>
|
||||
@@ -114,10 +118,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: View 'discard changes' options
|
||||
<kbd><space></kbd>: Toggle staged
|
||||
<kbd><c-b></kbd>: Filter files by status
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: Commit changes
|
||||
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||
<kbd>A</kbd>: Amend last commit
|
||||
<kbd>C</kbd>: Commit changes using git editor
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Edit file
|
||||
<kbd>o</kbd>: Open file
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
@@ -129,6 +135,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View upstream reset options
|
||||
<kbd>D</kbd>: View reset options
|
||||
<kbd>`</kbd>: Toggle file tree view
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||
<kbd>f</kbd>: Fetch
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
@@ -144,16 +151,18 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Create pull request
|
||||
<kbd>O</kbd>: Create pull request options
|
||||
<kbd><c-y></kbd>: Copy pull request URL to clipboard
|
||||
<kbd>c</kbd>: Checkout by name
|
||||
<kbd>c</kbd>: Checkout by name, enter '-' to switch to last
|
||||
<kbd>F</kbd>: Force checkout
|
||||
<kbd>d</kbd>: Delete branch
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||
<kbd>M</kbd>: Merge into currently checked out branch
|
||||
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||
<kbd>T</kbd>: Create tag
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>R</kbd>: Rename branch
|
||||
<kbd>u</kbd>: Set/Unset upstream
|
||||
<kbd>u</kbd>: View upstream options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -231,6 +240,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -239,6 +249,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -251,9 +262,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>n</kbd>: New branch
|
||||
<kbd>M</kbd>: Merge into currently checked out branch
|
||||
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||
<kbd>d</kbd>: Delete branch
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -276,6 +289,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Drop
|
||||
<kbd>n</kbd>: New branch
|
||||
<kbd>r</kbd>: Rename stash
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -294,6 +308,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -302,6 +317,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: Copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: Copy commit range (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
</pre>
|
||||
@@ -311,6 +327,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Copy submodule name to clipboard
|
||||
<kbd><enter></kbd>: Enter submodule
|
||||
<kbd><space></kbd>: Enter submodule
|
||||
<kbd>d</kbd>: Remove submodule
|
||||
<kbd>u</kbd>: Update submodule
|
||||
<kbd>n</kbd>: Add new submodule
|
||||
@@ -324,10 +341,22 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><space></kbd>: Checkout
|
||||
<kbd>d</kbd>: Delete tag
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>P</kbd>: Push tag
|
||||
<kbd>n</kbd>: Create tag
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## Worktrees
|
||||
|
||||
<pre>
|
||||
<kbd>n</kbd>: Create worktree
|
||||
<kbd><space></kbd>: Switch to worktree
|
||||
<kbd><enter></kbd>: Switch to worktree
|
||||
<kbd>o</kbd>: Open in editor
|
||||
<kbd>d</kbd>: Remove worktree
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit キーバインド
|
||||
|
||||
@@ -52,6 +52,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Drop
|
||||
<kbd>n</kbd>: 新しいブランチを作成
|
||||
<kbd>r</kbd>: Stashを変更
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -60,6 +61,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: コミットのSHAをクリップボードにコピー
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: コミットをチェックアウト
|
||||
<kbd>y</kbd>: コミットの情報をコピー
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
@@ -68,10 +70,22 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
</pre>
|
||||
|
||||
## Worktrees
|
||||
|
||||
<pre>
|
||||
<kbd>n</kbd>: Create worktree
|
||||
<kbd><space></kbd>: Switch to worktree
|
||||
<kbd><enter></kbd>: Switch to worktree
|
||||
<kbd>o</kbd>: Open in editor
|
||||
<kbd>d</kbd>: Remove worktree
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## コミット
|
||||
|
||||
<pre>
|
||||
@@ -90,11 +104,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: コミットを1つ下に移動
|
||||
<kbd><c-k></kbd>: コミットを1つ上に移動
|
||||
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: ステージされた変更でamendコミット
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: コミットをrevert
|
||||
<kbd>T</kbd>: タグを作成
|
||||
<kbd><c-l></kbd>: ログメニューを開く
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: コミットをチェックアウト
|
||||
<kbd>y</kbd>: コミットの情報をコピー
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
@@ -102,6 +118,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
</pre>
|
||||
@@ -114,6 +131,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle file included in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
@@ -133,6 +151,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: サブモジュール名をクリップボードにコピー
|
||||
<kbd><enter></kbd>: サブモジュールを開く
|
||||
<kbd><space></kbd>: サブモジュールを開く
|
||||
<kbd>d</kbd>: サブモジュールを削除
|
||||
<kbd>u</kbd>: サブモジュールを更新
|
||||
<kbd>n</kbd>: サブモジュールを新規追加
|
||||
@@ -156,10 +175,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><space></kbd>: チェックアウト
|
||||
<kbd>d</kbd>: タグを削除
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>P</kbd>: タグをpush
|
||||
<kbd>n</kbd>: タグを作成
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -171,10 +191,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: View 'discard changes' options
|
||||
<kbd><space></kbd>: ステージ/アンステージ
|
||||
<kbd><c-b></kbd>: ファイルをフィルタ (ステージ/アンステージ)
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: 変更をコミット
|
||||
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
|
||||
<kbd>A</kbd>: 最新のコミットにamend
|
||||
<kbd>C</kbd>: gitエディタを使用して変更をコミット
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>i</kbd>: ファイルをignore
|
||||
@@ -186,6 +208,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View upstream reset options
|
||||
<kbd>D</kbd>: View reset options
|
||||
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Git mergetoolを開く
|
||||
<kbd>f</kbd>: Fetch
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
@@ -201,16 +224,18 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>o</kbd>: Pull Requestを作成
|
||||
<kbd>O</kbd>: Create pull request options
|
||||
<kbd><c-y></kbd>: Pull RequestのURLをクリップボードにコピー
|
||||
<kbd>c</kbd>: Checkout by name
|
||||
<kbd>c</kbd>: Checkout by name, enter '-' to switch to last
|
||||
<kbd>F</kbd>: Force checkout
|
||||
<kbd>d</kbd>: ブランチを削除
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||
<kbd>M</kbd>: 現在のブランチにマージ
|
||||
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||
<kbd>T</kbd>: タグを作成
|
||||
<kbd>s</kbd>: 並び替え
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>R</kbd>: ブランチ名を変更
|
||||
<kbd>u</kbd>: Set/Unset upstream
|
||||
<kbd>u</kbd>: View upstream options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -302,9 +327,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>n</kbd>: 新しいブランチを作成
|
||||
<kbd>M</kbd>: 現在のブランチにマージ
|
||||
<kbd>r</kbd>: Rebase checked-out branch onto this branch
|
||||
<kbd>d</kbd>: ブランチを削除
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||
<kbd>s</kbd>: 並び替え
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -313,6 +340,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: コミットのSHAをクリップボードにコピー
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: コミットをチェックアウト
|
||||
<kbd>y</kbd>: コミットの情報をコピー
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
@@ -321,6 +349,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit 키 바인딩
|
||||
|
||||
@@ -48,6 +48,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 커밋 SHA를 클립보드에 복사
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 커밋을 체크아웃
|
||||
<kbd>y</kbd>: 커밋 attribute 복사
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
@@ -56,6 +57,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -68,6 +70,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Drop
|
||||
<kbd>n</kbd>: 새 브랜치 생성
|
||||
<kbd>r</kbd>: Rename stash
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -76,6 +79,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 커밋 SHA를 클립보드에 복사
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 커밋을 체크아웃
|
||||
<kbd>y</kbd>: 커밋 attribute 복사
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
@@ -84,10 +88,22 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
</pre>
|
||||
|
||||
## Worktrees
|
||||
|
||||
<pre>
|
||||
<kbd>n</kbd>: Create worktree
|
||||
<kbd><space></kbd>: Switch to worktree
|
||||
<kbd><enter></kbd>: Switch to worktree
|
||||
<kbd>o</kbd>: Open in editor
|
||||
<kbd>d</kbd>: Remove worktree
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## 메뉴
|
||||
|
||||
<pre>
|
||||
@@ -169,14 +185,16 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-y></kbd>: 풀 리퀘스트 URL을 클립보드에 복사
|
||||
<kbd>c</kbd>: 이름으로 체크아웃
|
||||
<kbd>F</kbd>: 강제 체크아웃
|
||||
<kbd>d</kbd>: 브랜치 삭제
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
|
||||
<kbd>M</kbd>: 현재 브랜치에 병합
|
||||
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||
<kbd>T</kbd>: 태그를 생성
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>R</kbd>: 브랜치 이름 변경
|
||||
<kbd>u</kbd>: Set/Unset upstream
|
||||
<kbd>u</kbd>: View upstream options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -196,6 +214,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 서브모듈 이름을 클립보드에 복사
|
||||
<kbd><enter></kbd>: 서브모듈 열기
|
||||
<kbd><space></kbd>: 서브모듈 열기
|
||||
<kbd>d</kbd>: 서브모듈 삭제
|
||||
<kbd>u</kbd>: 서브모듈 업데이트
|
||||
<kbd>n</kbd>: 새로운 서브모듈 추가
|
||||
@@ -223,9 +242,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>n</kbd>: 새 브랜치 생성
|
||||
<kbd>M</kbd>: 현재 브랜치에 병합
|
||||
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
|
||||
<kbd>d</kbd>: 브랜치 삭제
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -248,11 +269,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: 커밋을 1개 아래로 이동
|
||||
<kbd><c-k></kbd>: 커밋을 1개 위로 이동
|
||||
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Amend commit with staged changes
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: 커밋 되돌리기
|
||||
<kbd>T</kbd>: Tag commit
|
||||
<kbd><c-l></kbd>: 로그 메뉴 열기
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 커밋을 체크아웃
|
||||
<kbd>y</kbd>: 커밋 attribute 복사
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
@@ -260,6 +283,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View selected item's files
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
</pre>
|
||||
@@ -272,6 +296,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Discard this commit's changes to this file
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle file included in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
@@ -290,10 +315,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><space></kbd>: 체크아웃
|
||||
<kbd>d</kbd>: 태그 삭제
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>P</kbd>: 태그를 push
|
||||
<kbd>n</kbd>: 태그를 생성
|
||||
<kbd>g</kbd>: View reset options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -305,10 +331,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: View 'discard changes' options
|
||||
<kbd><space></kbd>: Staged 전환
|
||||
<kbd><c-b></kbd>: 파일을 필터하기 (Staged/unstaged)
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: 커밋 변경내용
|
||||
<kbd>w</kbd>: Commit changes without pre-commit hook
|
||||
<kbd>A</kbd>: 마지맛 커밋 수정
|
||||
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>i</kbd>: Ignore file
|
||||
@@ -320,6 +348,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View upstream reset options
|
||||
<kbd>D</kbd>: View reset options
|
||||
<kbd>`</kbd>: 파일 트리뷰로 전환
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Git mergetool를 열기
|
||||
<kbd>f</kbd>: Fetch
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit Sneltoetsen
|
||||
|
||||
@@ -51,10 +51,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Bekijk 'veranderingen ongedaan maken' opties
|
||||
<kbd><space></kbd>: Toggle staged
|
||||
<kbd><c-b></kbd>: Filter files by status
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: Commit veranderingen
|
||||
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
|
||||
<kbd>A</kbd>: Wijzig laatste commit
|
||||
<kbd>C</kbd>: Commit veranderingen met de git editor
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Verander bestand
|
||||
<kbd>o</kbd>: Open bestand
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
@@ -66,6 +68,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: Bekijk upstream reset opties
|
||||
<kbd>D</kbd>: Bekijk reset opties
|
||||
<kbd>`</kbd>: Toggle bestandsboom weergave
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||
<kbd>f</kbd>: Fetch
|
||||
<kbd>/</kbd>: Start met zoeken
|
||||
@@ -90,14 +93,16 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-y></kbd>: Kopieer de URL van het pull-verzoek naar het klembord
|
||||
<kbd>c</kbd>: Uitchecken bij naam
|
||||
<kbd>F</kbd>: Forceer checkout
|
||||
<kbd>d</kbd>: Verwijder branch
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: Rebase branch
|
||||
<kbd>M</kbd>: Merge in met huidige checked out branch
|
||||
<kbd>f</kbd>: Fast-forward deze branch vanaf zijn upstream
|
||||
<kbd>T</kbd>: Creëer tag
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>R</kbd>: Hernoem branch
|
||||
<kbd>u</kbd>: Set/Unset upstream
|
||||
<kbd>u</kbd>: View upstream options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Bekijk commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -117,6 +122,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Uitsluit deze commit zijn veranderingen aan dit bestand
|
||||
<kbd>o</kbd>: Open bestand
|
||||
<kbd>e</kbd>: Verander bestand
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle bestand inbegrepen in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter bestand om geselecteerde regels toe te voegen aan de patch
|
||||
@@ -142,11 +148,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: Verplaats commit 1 naar beneden
|
||||
<kbd><c-k></kbd>: Verplaats commit 1 naar boven
|
||||
<kbd>v</kbd>: Plak commits (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Wijzig commit met staged veranderingen
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: Commit ongedaan maken
|
||||
<kbd>T</kbd>: Tag commit
|
||||
<kbd><c-l></kbd>: Open log menu
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -154,6 +162,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Bekijk gecommite bestanden
|
||||
<kbd>/</kbd>: Start met zoeken
|
||||
</pre>
|
||||
@@ -209,6 +218,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Kopieer commit SHA naar klembord
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -217,6 +227,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Bekijk commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -229,9 +240,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>n</kbd>: Nieuwe branch
|
||||
<kbd>M</kbd>: Merge in met huidige checked out branch
|
||||
<kbd>r</kbd>: Rebase branch
|
||||
<kbd>d</kbd>: Verwijder branch
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Stel in als upstream van uitgecheckte branch
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Bekijk commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -276,6 +289,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Laten vallen
|
||||
<kbd>n</kbd>: Nieuwe branch
|
||||
<kbd>r</kbd>: Rename stash
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Bekijk gecommite bestanden
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -294,6 +308,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Kopieer commit SHA naar klembord
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -302,6 +317,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: Kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Bekijk gecommite bestanden
|
||||
<kbd>/</kbd>: Start met zoeken
|
||||
</pre>
|
||||
@@ -311,6 +327,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Kopieer submodule naam naar klembord
|
||||
<kbd><enter></kbd>: Enter submodule
|
||||
<kbd><space></kbd>: Enter submodule
|
||||
<kbd>d</kbd>: Remove submodule
|
||||
<kbd>u</kbd>: Update submodule
|
||||
<kbd>n</kbd>: Voeg nieuwe submodule toe
|
||||
@@ -324,10 +341,22 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><space></kbd>: Uitchecken
|
||||
<kbd>d</kbd>: Verwijder tag
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>P</kbd>: Push tag
|
||||
<kbd>n</kbd>: Creëer tag
|
||||
<kbd>g</kbd>: Bekijk reset opties
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Bekijk commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## Worktrees
|
||||
|
||||
<pre>
|
||||
<kbd>n</kbd>: Create worktree
|
||||
<kbd><space></kbd>: Switch to worktree
|
||||
<kbd><enter></kbd>: Switch to worktree
|
||||
<kbd>o</kbd>: Open in editor
|
||||
<kbd>d</kbd>: Remove worktree
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit Keybindings
|
||||
|
||||
@@ -69,11 +69,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: Przenieś commit 1 w dół
|
||||
<kbd><c-k></kbd>: Przenieś commit 1 w górę
|
||||
<kbd>v</kbd>: Wklej commity (przebieranie)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Popraw commit zmianami z poczekalni
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: Odwróć commit
|
||||
<kbd>T</kbd>: Tag commit
|
||||
<kbd><c-l></kbd>: Open log menu
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -81,6 +83,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Przeglądaj pliki commita
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
</pre>
|
||||
@@ -104,14 +107,16 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-y></kbd>: Skopiuj adres URL żądania pobrania do schowka
|
||||
<kbd>c</kbd>: Przełącz używając nazwy
|
||||
<kbd>F</kbd>: Wymuś przełączenie
|
||||
<kbd>d</kbd>: Usuń gałąź
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: Zmiana bazy gałęzi
|
||||
<kbd>M</kbd>: Scal do obecnej gałęzi
|
||||
<kbd>f</kbd>: Fast-forward this branch from its upstream
|
||||
<kbd>T</kbd>: Create tag
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>R</kbd>: Rename branch
|
||||
<kbd>u</kbd>: Set/Unset upstream
|
||||
<kbd>u</kbd>: View upstream options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -147,10 +152,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Pokaż opcje porzucania zmian
|
||||
<kbd><space></kbd>: Przełącz stan poczekalni
|
||||
<kbd><c-b></kbd>: Filter files by status
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: Zatwierdź zmiany
|
||||
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
|
||||
<kbd>A</kbd>: Zmień ostatni commit
|
||||
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Edytuj plik
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
<kbd>i</kbd>: Ignore or exclude file
|
||||
@@ -162,6 +169,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: View upstream reset options
|
||||
<kbd>D</kbd>: Wyświetl opcje resetu
|
||||
<kbd>`</kbd>: Toggle file tree view
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Open external merge tool (git mergetool)
|
||||
<kbd>f</kbd>: Pobierz
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
@@ -175,6 +183,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Porzuć zmiany commita dla tego pliku
|
||||
<kbd>o</kbd>: Otwórz plik
|
||||
<kbd>e</kbd>: Edytuj plik
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Toggle file included in patch
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
@@ -208,6 +217,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -216,6 +226,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -228,9 +239,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>n</kbd>: Nowa gałąź
|
||||
<kbd>M</kbd>: Scal do obecnej gałęzi
|
||||
<kbd>r</kbd>: Zmiana bazy gałęzi
|
||||
<kbd>d</kbd>: Usuń gałąź
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Set as upstream of checked-out branch
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -269,6 +282,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: Porzuć
|
||||
<kbd>n</kbd>: Nowa gałąź
|
||||
<kbd>r</kbd>: Rename stash
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Przeglądaj pliki commita
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -287,6 +301,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Copy commit SHA to clipboard
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Checkout commit
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: Open commit in browser
|
||||
@@ -295,6 +310,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: Kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
|
||||
<kbd><c-r></kbd>: Reset cherry-picked (copied) commits selection
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Przeglądaj pliki commita
|
||||
<kbd>/</kbd>: Search the current view by text
|
||||
</pre>
|
||||
@@ -304,6 +320,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Copy submodule name to clipboard
|
||||
<kbd><enter></kbd>: Enter submodule
|
||||
<kbd><space></kbd>: Enter submodule
|
||||
<kbd>d</kbd>: Remove submodule
|
||||
<kbd>u</kbd>: Update submodule
|
||||
<kbd>n</kbd>: Add new submodule
|
||||
@@ -317,14 +334,26 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><space></kbd>: Przełącz
|
||||
<kbd>d</kbd>: Delete tag
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>P</kbd>: Push tag
|
||||
<kbd>n</kbd>: Create tag
|
||||
<kbd>g</kbd>: Wyświetl opcje resetu
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: View commits
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## Worktrees
|
||||
|
||||
<pre>
|
||||
<kbd>n</kbd>: Create worktree
|
||||
<kbd><space></kbd>: Switch to worktree
|
||||
<kbd><enter></kbd>: Switch to worktree
|
||||
<kbd>o</kbd>: Open in editor
|
||||
<kbd>d</kbd>: Remove worktree
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## Zwykłe
|
||||
|
||||
<pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit Связки клавиш
|
||||
|
||||
@@ -44,6 +44,17 @@ _Связки клавиш_
|
||||
<kbd>[</kbd>: Предыдущая вкладка
|
||||
</pre>
|
||||
|
||||
## Worktrees
|
||||
|
||||
<pre>
|
||||
<kbd>n</kbd>: Create worktree
|
||||
<kbd><space></kbd>: Switch to worktree
|
||||
<kbd><enter></kbd>: Switch to worktree
|
||||
<kbd>o</kbd>: Open in editor
|
||||
<kbd>d</kbd>: Remove worktree
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## Главная панель (Индексирование)
|
||||
|
||||
<pre>
|
||||
@@ -109,6 +120,7 @@ _Связки клавиш_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Скопировать SHA коммита в буфер обмена
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Переключить коммит
|
||||
<kbd>y</kbd>: Скопировать атрибут коммита
|
||||
<kbd>o</kbd>: Открыть коммит в браузере
|
||||
@@ -117,6 +129,7 @@ _Связки клавиш_
|
||||
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||
<kbd><c-r></kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Просмотреть коммиты
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -139,11 +152,13 @@ _Связки клавиш_
|
||||
<kbd><c-j></kbd>: Переместить коммит вниз на один
|
||||
<kbd><c-k></kbd>: Переместить коммит вверх на один
|
||||
<kbd>v</kbd>: Вставить отобранные коммиты (cherry-pick)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: Править последний коммит с проиндексированными изменениями
|
||||
<kbd>a</kbd>: Установить/убрать автора коммита
|
||||
<kbd>t</kbd>: Отменить коммит
|
||||
<kbd>T</kbd>: Пометить коммит тегом
|
||||
<kbd><c-l></kbd>: Открыть меню журнала
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Переключить коммит
|
||||
<kbd>y</kbd>: Скопировать атрибут коммита
|
||||
<kbd>o</kbd>: Открыть коммит в браузере
|
||||
@@ -151,6 +166,7 @@ _Связки клавиш_
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Просмотреть файлы выбранного элемента
|
||||
<kbd>/</kbd>: Найти
|
||||
</pre>
|
||||
@@ -167,14 +183,16 @@ _Связки клавиш_
|
||||
<kbd><c-y></kbd>: Скопировать URL запроса на принятие изменений в буфер обмена
|
||||
<kbd>c</kbd>: Переключить по названию
|
||||
<kbd>F</kbd>: Принудительное переключение
|
||||
<kbd>d</kbd>: Удалить ветку
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
|
||||
<kbd>M</kbd>: Слияние с текущей переключённой веткой
|
||||
<kbd>f</kbd>: Перемотать эту ветку вперёд из её upstream-ветки
|
||||
<kbd>T</kbd>: Создать тег
|
||||
<kbd>s</kbd>: Порядок сортировки
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>R</kbd>: Переименовать ветку
|
||||
<kbd>u</kbd>: Установить/убрать upstream-ветку
|
||||
<kbd>u</kbd>: View upstream options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Просмотреть коммиты
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -198,6 +216,7 @@ _Связки клавиш_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Скопировать SHA коммита в буфер обмена
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: Переключить коммит
|
||||
<kbd>y</kbd>: Скопировать атрибут коммита
|
||||
<kbd>o</kbd>: Открыть коммит в браузере
|
||||
@@ -206,6 +225,7 @@ _Связки клавиш_
|
||||
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
|
||||
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
|
||||
<kbd><c-r></kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: Просмотреть файлы выбранного элемента
|
||||
<kbd>/</kbd>: Найти
|
||||
</pre>
|
||||
@@ -215,6 +235,7 @@ _Связки клавиш_
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: Скопировать название подмодуля в буфер обмена
|
||||
<kbd><enter></kbd>: Ввести подмодуль
|
||||
<kbd><space></kbd>: Ввести подмодуль
|
||||
<kbd>d</kbd>: Удалить подмодуль
|
||||
<kbd>u</kbd>: Обновить подмодуль
|
||||
<kbd>n</kbd>: Добавить новый подмодуль
|
||||
@@ -239,6 +260,7 @@ _Связки клавиш_
|
||||
<kbd>d</kbd>: Отменить изменения коммита в этом файле
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
<kbd>e</kbd>: Редактировать файл
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: Переключить файлы включённые в патч
|
||||
<kbd>a</kbd>: Переключить все файлы, включённые в патч
|
||||
<kbd><enter></kbd>: Введите файл, чтобы добавить выбранные строки в патч (или свернуть каталог переключения)
|
||||
@@ -260,10 +282,11 @@ _Связки клавиш_
|
||||
|
||||
<pre>
|
||||
<kbd><space></kbd>: Переключить
|
||||
<kbd>d</kbd>: Удалить тег
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>P</kbd>: Отправить тег
|
||||
<kbd>n</kbd>: Создать тег
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Просмотреть коммиты
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -276,9 +299,11 @@ _Связки клавиш_
|
||||
<kbd>n</kbd>: Новая ветка
|
||||
<kbd>M</kbd>: Слияние с текущей переключённой веткой
|
||||
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
|
||||
<kbd>d</kbd>: Удалить ветку
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: Установить как upstream-ветку переключённую ветку
|
||||
<kbd>s</kbd>: Порядок сортировки
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Просмотреть коммиты
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -300,10 +325,12 @@ _Связки клавиш_
|
||||
<kbd>d</kbd>: Просмотреть параметры «отмены изменении»
|
||||
<kbd><space></kbd>: Переключить индекс
|
||||
<kbd><c-b></kbd>: Фильтровать файлы (проиндексированные/непроиндексированные)
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: Сохранить изменения
|
||||
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
|
||||
<kbd>A</kbd>: Правка последнего коммита
|
||||
<kbd>C</kbd>: Сохранить изменения с помощью редактора git
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: Редактировать файл
|
||||
<kbd>o</kbd>: Открыть файл
|
||||
<kbd>i</kbd>: Игнорировать или исключить файл
|
||||
@@ -315,6 +342,7 @@ _Связки клавиш_
|
||||
<kbd>g</kbd>: Просмотреть параметры сброса upstream-ветки
|
||||
<kbd>D</kbd>: Просмотреть параметры сброса
|
||||
<kbd>`</kbd>: Переключить вид дерева файлов
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: Открыть внешний инструмент слияния (git mergetool)
|
||||
<kbd>f</kbd>: Получить изменения
|
||||
<kbd>/</kbd>: Найти
|
||||
@@ -328,6 +356,7 @@ _Связки клавиш_
|
||||
<kbd>d</kbd>: Удалить припрятанные изменения из хранилища
|
||||
<kbd>n</kbd>: Новая ветка
|
||||
<kbd>r</kbd>: Переименовать хранилище
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: Просмотреть файлы выбранного элемента
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit 按键绑定
|
||||
|
||||
@@ -48,6 +48,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 将提交的 SHA 复制到剪贴板
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 检出提交
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
@@ -56,10 +57,22 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## Worktrees
|
||||
|
||||
<pre>
|
||||
<kbd>n</kbd>: Create worktree
|
||||
<kbd><space></kbd>: Switch to worktree
|
||||
<kbd><enter></kbd>: Switch to worktree
|
||||
<kbd>o</kbd>: Open in editor
|
||||
<kbd>d</kbd>: Remove worktree
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## 分支页面
|
||||
|
||||
<pre>
|
||||
@@ -72,14 +85,16 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-y></kbd>: 将抓取请求 URL 复制到剪贴板
|
||||
<kbd>c</kbd>: 按名称检出
|
||||
<kbd>F</kbd>: 强制检出
|
||||
<kbd>d</kbd>: 删除分支
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: 将已检出的分支变基到该分支
|
||||
<kbd>M</kbd>: 合并到当前检出的分支
|
||||
<kbd>f</kbd>: 从上游快进此分支
|
||||
<kbd>T</kbd>: 创建标签
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>R</kbd>: 重命名分支
|
||||
<kbd>u</kbd>: Set/Unset upstream
|
||||
<kbd>u</kbd>: View upstream options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -88,6 +103,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 将提交的 SHA 复制到剪贴板
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 检出提交
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
@@ -96,6 +112,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 查看提交的文件
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
</pre>
|
||||
@@ -105,6 +122,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 将子模块名称复制到剪贴板
|
||||
<kbd><enter></kbd>: 输入子模块
|
||||
<kbd><space></kbd>: 输入子模块
|
||||
<kbd>d</kbd>: 删除子模块
|
||||
<kbd>u</kbd>: 更新子模块
|
||||
<kbd>n</kbd>: 添加新的子模块
|
||||
@@ -132,11 +150,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd><c-j></kbd>: 下移提交
|
||||
<kbd><c-k></kbd>: 上移提交
|
||||
<kbd>v</kbd>: 粘贴提交(拣选)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: 用已暂存的更改来修补提交
|
||||
<kbd>a</kbd>: Set/Reset commit author
|
||||
<kbd>t</kbd>: 还原提交
|
||||
<kbd>T</kbd>: 标签提交
|
||||
<kbd><c-l></kbd>: 打开日志菜单
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 检出提交
|
||||
<kbd>y</kbd>: Copy commit attribute
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
@@ -144,6 +164,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 查看提交的文件
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
</pre>
|
||||
@@ -156,6 +177,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: 放弃对此文件的提交更改
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: 补丁中包含的切换文件
|
||||
<kbd>a</kbd>: Toggle all files included in patch
|
||||
<kbd><enter></kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
|
||||
@@ -177,10 +199,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: 查看'放弃更改'选项
|
||||
<kbd><space></kbd>: 切换暂存状态
|
||||
<kbd><c-b></kbd>: Filter files by status
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: 提交更改
|
||||
<kbd>w</kbd>: 提交更改而无需预先提交钩子
|
||||
<kbd>A</kbd>: 修补最后一次提交
|
||||
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>i</kbd>: 忽略文件
|
||||
@@ -192,6 +216,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>g</kbd>: 查看上游重置选项
|
||||
<kbd>D</kbd>: 查看重置选项
|
||||
<kbd>`</kbd>: 切换文件树视图
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
|
||||
<kbd>f</kbd>: 抓取
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
@@ -217,10 +242,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
<pre>
|
||||
<kbd><space></kbd>: 检出
|
||||
<kbd>d</kbd>: 删除标签
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>P</kbd>: 推送标签
|
||||
<kbd>n</kbd>: 创建标签
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -303,6 +329,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>d</kbd>: 删除
|
||||
<kbd>n</kbd>: 新分支
|
||||
<kbd>r</kbd>: Rename stash
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 查看提交的文件
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -315,9 +342,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
<kbd>n</kbd>: 新分支
|
||||
<kbd>M</kbd>: 合并到当前检出的分支
|
||||
<kbd>r</kbd>: 将已检出的分支变基到该分支
|
||||
<kbd>d</kbd>: 删除分支
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: 设置为检出分支的上游
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
|
||||
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
|
||||
|
||||
# Lazygit 鍵盤快捷鍵
|
||||
|
||||
@@ -48,6 +48,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 複製提交 SHA 到剪貼簿
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 檢出提交
|
||||
<kbd>y</kbd>: 複製提交屬性
|
||||
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||
@@ -56,10 +57,22 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>c</kbd>: 複製提交 (揀選)
|
||||
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||
<kbd><c-r></kbd>: 重設選定的揀選 (複製) 提交
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 檢視提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## Worktrees
|
||||
|
||||
<pre>
|
||||
<kbd>n</kbd>: Create worktree
|
||||
<kbd><space></kbd>: Switch to worktree
|
||||
<kbd><enter></kbd>: Switch to worktree
|
||||
<kbd>o</kbd>: Open in editor
|
||||
<kbd>d</kbd>: Remove worktree
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
## 主視窗 (一般)
|
||||
|
||||
<pre>
|
||||
@@ -133,6 +146,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 複製提交 SHA 到剪貼簿
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 檢出提交
|
||||
<kbd>y</kbd>: 複製提交屬性
|
||||
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||
@@ -141,6 +155,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>c</kbd>: 複製提交 (揀選)
|
||||
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||
<kbd><c-r></kbd>: 重設選定的揀選 (複製) 提交
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 檢視所選項目的檔案
|
||||
<kbd>/</kbd>: 開始搜尋
|
||||
</pre>
|
||||
@@ -150,6 +165,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<pre>
|
||||
<kbd><c-o></kbd>: 複製子模組名稱到剪貼簿
|
||||
<kbd><enter></kbd>: 進入子模組
|
||||
<kbd><space></kbd>: 進入子模組
|
||||
<kbd>d</kbd>: 移除子模組
|
||||
<kbd>u</kbd>: 更新子模組
|
||||
<kbd>n</kbd>: 新增子模組
|
||||
@@ -177,11 +193,13 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd><c-j></kbd>: 向下移動提交
|
||||
<kbd><c-k></kbd>: 向上移動提交
|
||||
<kbd>v</kbd>: 貼上提交 (揀選)
|
||||
<kbd>B</kbd>: Mark commit as base commit for rebase
|
||||
<kbd>A</kbd>: 使用已預存的更改修正提交
|
||||
<kbd>a</kbd>: 設置/重設提交作者
|
||||
<kbd>t</kbd>: 還原提交
|
||||
<kbd>T</kbd>: 打標籤到提交
|
||||
<kbd><c-l></kbd>: 開啟記錄選單
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><space></kbd>: 檢出提交
|
||||
<kbd>y</kbd>: 複製提交屬性
|
||||
<kbd>o</kbd>: 在瀏覽器中開啟提交
|
||||
@@ -189,6 +207,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>c</kbd>: 複製提交 (揀選)
|
||||
<kbd>C</kbd>: 複製提交範圍 (揀選)
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><enter></kbd>: 檢視所選項目的檔案
|
||||
<kbd>/</kbd>: 開始搜尋
|
||||
</pre>
|
||||
@@ -208,6 +227,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>d</kbd>: 捨棄此提交對此檔案的更改
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
<kbd>e</kbd>: 編輯檔案
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd><space></kbd>: 切換檔案是否包含在補丁中
|
||||
<kbd>a</kbd>: 切換所有檔案是否包含在補丁中
|
||||
<kbd><enter></kbd>: 輸入檔案以將選定的行添加至補丁(或切換目錄折疊)
|
||||
@@ -223,6 +243,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>d</kbd>: 捨棄
|
||||
<kbd>n</kbd>: 新分支
|
||||
<kbd>r</kbd>: 重新命名收藏
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 檢視所選項目的檔案
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -239,14 +260,16 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd><c-y></kbd>: 複製拉取請求的 URL 到剪貼板
|
||||
<kbd>c</kbd>: 根據名稱檢出
|
||||
<kbd>F</kbd>: 強制檢出
|
||||
<kbd>d</kbd>: 刪除分支
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>r</kbd>: 將已檢出的分支變基至此分支
|
||||
<kbd>M</kbd>: 合併到當前檢出的分支
|
||||
<kbd>f</kbd>: 從上游快進此分支
|
||||
<kbd>T</kbd>: 建立標籤
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>R</kbd>: 重新命名分支
|
||||
<kbd>u</kbd>: 設定/取消設定上游
|
||||
<kbd>u</kbd>: View upstream options
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 檢視提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -255,10 +278,11 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
|
||||
<pre>
|
||||
<kbd><space></kbd>: 檢出
|
||||
<kbd>d</kbd>: 刪除標籤
|
||||
<kbd>d</kbd>: View delete options
|
||||
<kbd>P</kbd>: 推送標籤
|
||||
<kbd>n</kbd>: 建立標籤
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 檢視提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
@@ -270,10 +294,12 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>d</kbd>: 檢視“捨棄更改”的選項
|
||||
<kbd><space></kbd>: 切換預存
|
||||
<kbd><c-b></kbd>: 篩選檔案 (預存/未預存)
|
||||
<kbd>y</kbd>: Copy to clipboard
|
||||
<kbd>c</kbd>: 提交變更
|
||||
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
|
||||
<kbd>A</kbd>: 修正上次提交
|
||||
<kbd>C</kbd>: 使用 git 編輯器提交變更
|
||||
<kbd><c-f></kbd>: Find base commit for fixup
|
||||
<kbd>e</kbd>: 編輯檔案
|
||||
<kbd>o</kbd>: 開啟檔案
|
||||
<kbd>i</kbd>: 忽略或排除檔案
|
||||
@@ -285,6 +311,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>g</kbd>: 檢視上游重設選項
|
||||
<kbd>D</kbd>: 檢視重設選項
|
||||
<kbd>`</kbd>: 切換檔案樹狀視圖
|
||||
<kbd><c-t></kbd>: Open external diff tool (git difftool)
|
||||
<kbd>M</kbd>: 開啟外部合併工具 (git mergetool)
|
||||
<kbd>f</kbd>: 擷取
|
||||
<kbd>/</kbd>: 開始搜尋
|
||||
@@ -325,9 +352,11 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
<kbd>n</kbd>: 新分支
|
||||
<kbd>M</kbd>: 合併到當前檢出的分支
|
||||
<kbd>r</kbd>: 將已檢出的分支變基至此分支
|
||||
<kbd>d</kbd>: 刪除分支
|
||||
<kbd>d</kbd>: Delete remote tag
|
||||
<kbd>u</kbd>: 將此分支設為當前分支之上游
|
||||
<kbd>s</kbd>: Sort order
|
||||
<kbd>g</kbd>: 檢視重設選項
|
||||
<kbd>w</kbd>: View worktree options
|
||||
<kbd><enter></kbd>: 檢視提交
|
||||
<kbd>/</kbd>: Filter the current view by text
|
||||
</pre>
|
||||
|
||||
52
go.mod
52
go.mod
@@ -1,48 +1,58 @@
|
||||
module github.com/jesseduffield/lazygit
|
||||
|
||||
go 1.18
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.0
|
||||
|
||||
require (
|
||||
github.com/OpenPeeDeeP/xdg v1.0.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aybabtme/humanlog v0.4.1
|
||||
github.com/cli/safeexec v1.0.0
|
||||
github.com/charmbracelet/glamour v0.6.0
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/fsmiamoto/git-todo-parser v0.0.5
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/gdamore/tcell/v2 v2.6.0
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/gdamore/tcell/v2 v2.7.1-0.20240103180601-96e29905643b
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230723014157-03e858e46144
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240103192639-2874168c14db
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
|
||||
github.com/kyokomi/emoji/v2 v2.2.8
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/mattn/go-runewidth v0.0.14
|
||||
github.com/mattn/go-runewidth v0.0.15
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
github.com/samber/lo v1.31.0
|
||||
github.com/sanity-io/litter v1.5.2
|
||||
github.com/sasha-s/go-deadlock v0.3.1
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spf13/afero v1.9.5
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma v0.10.0 // indirect
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/fatih/color v1.9.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
@@ -51,25 +61,35 @@ require (
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/invopop/jsonschema v0.10.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.21 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.13.0 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
github.com/onsi/gomega v1.7.1 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/term v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
github.com/yuin/goldmark v1.5.2 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
508
go.sum
508
go.sum
@@ -1,7 +1,49 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OpenPeeDeeP/xdg v1.0.0 h1:UDLmNjCGFZZCaVMB74DqYEtXkHxnTxcr4FeJVF9uCn8=
|
||||
github.com/OpenPeeDeeP/xdg v1.0.0/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
@@ -11,10 +53,26 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn
|
||||
github.com/aybabtme/humanlog v0.4.1 h1:D8d9um55rrthJsP8IGSHBcti9lTb/XknmDAX6Zy8tek=
|
||||
github.com/aybabtme/humanlog v0.4.1/go.mod h1:B0bnQX4FTSU3oftPMTTPvENCy8LqixLDvYJA9TUCAGo=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
|
||||
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
|
||||
github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@@ -22,58 +80,127 @@ github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsmiamoto/git-todo-parser v0.0.5 h1:Bhzd/vz/6Qm3udfkd6NO9fWfD3TpwR9ucp3N75/J5I8=
|
||||
github.com/fsmiamoto/git-todo-parser v0.0.5/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg=
|
||||
github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y=
|
||||
github.com/gdamore/tcell/v2 v2.7.1-0.20240103180601-96e29905643b h1:VkiXff8uJUkhjcxcLwwzQLYBCc+k5tJeOVx4W0qMYTk=
|
||||
github.com/gdamore/tcell/v2 v2.7.1-0.20240103180601-96e29905643b/go.mod h1:hl/KtAANGBecfIPxk+FzKvThTqI84oplgbPEmVX60b8=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
|
||||
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
|
||||
github.com/invopop/jsonschema v0.10.0 h1:c1ktzNLBun3LyQQhyty5WE3lulbOdIIyOVlkmDLehcE=
|
||||
github.com/invopop/jsonschema v0.10.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8TIcC6Y4RI+1ZbJDOHfGJ570tPeYVCqo7/tws=
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230723014157-03e858e46144 h1:gwy5JzP6+PhcPFG1obkUSLGcTkUY88sLKlCPOFjwtak=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230723014157-03e858e46144/go.mod h1:dJ/BEUt3OWtaRg/PmuJWendRqREhre9JQ1SLvqrVJ8s=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240103192639-2874168c14db h1:ihJdYk85/XQLGiG3b6m8P2z+RUohRMtPmX74YR9IT8s=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240103192639-2874168c14db/go.mod h1:9zkyjnUmdL3+sUknJrQy/3HweUu8mVln/3J2wRF/l8M=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
||||
@@ -83,13 +210,20 @@ github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible/go.mod h1:w0xGhOSIJCGYYW+hnFPTutCy5aACpkcwbmORt5axGqk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3 h1:s995u+gNQADMaixtNOs+jilRC/Q78q0UXSI7+4T0cDE=
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3/go.mod h1:MCbEh21gjOzxc31udr3u4QM9DAdf8TFJCZz3u5hYIxA=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -101,9 +235,10 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqUE=
|
||||
github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
@@ -111,17 +246,31 @@ github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
|
||||
github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
|
||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -129,16 +278,20 @@ github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
|
||||
@@ -151,88 +304,377 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
||||
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
|
||||
github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
|
||||
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
|
||||
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0 h1:KzcWKJ0nMAmGoBhYVMnkWc1rXjB42lKy5aIys4TdLOA=
|
||||
@@ -248,3 +690,13 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
@@ -62,6 +62,7 @@ func Run(
|
||||
|
||||
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
|
||||
userConfig := config.GetUserConfig()
|
||||
appState := config.GetAppState()
|
||||
|
||||
var err error
|
||||
log := newLogger(config)
|
||||
@@ -74,7 +75,9 @@ func NewCommon(config config.AppConfigurer) (*common.Common, error) {
|
||||
Log: log,
|
||||
Tr: tr,
|
||||
UserConfig: userConfig,
|
||||
AppState: appState,
|
||||
Debug: config.GetDebug(),
|
||||
Fs: afero.NewOsFs(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -265,7 +268,11 @@ func (app *App) Run(startArgs appTypes.StartArgs) error {
|
||||
|
||||
// Close closes any resources
|
||||
func (app *App) Close() error {
|
||||
return slices.TryForEach(app.closers, func(closer io.Closer) error {
|
||||
return closer.Close()
|
||||
})
|
||||
for _, closer := range app.closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
@@ -92,7 +92,7 @@ func getDaemonKind() DaemonKind {
|
||||
}
|
||||
|
||||
func getCommentChar() byte {
|
||||
cmd := secureexec.Command("git", "config", "--get", "--null", "core.commentChar")
|
||||
cmd := exec.Command("git", "config", "--get", "--null", "core.commentChar")
|
||||
if output, err := cmd.Output(); err == nil && len(output) == 2 {
|
||||
return output[0]
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type TodoLine struct {
|
||||
@@ -26,11 +26,11 @@ func (self *TodoLine) ToString() string {
|
||||
}
|
||||
|
||||
func TodoLinesToString(todoLines []TodoLine) string {
|
||||
lines := slices.Map(todoLines, func(todoLine TodoLine) string {
|
||||
lines := lo.Map(todoLines, func(todoLine TodoLine, _ int) string {
|
||||
return todoLine.ToString()
|
||||
})
|
||||
|
||||
return strings.Join(slices.Reverse(lines), "")
|
||||
return strings.Join(lo.Reverse(lines), "")
|
||||
}
|
||||
|
||||
type ChangeTodoAction struct {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
@@ -17,7 +18,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/logs/tail"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -63,8 +63,17 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
||||
log.Fatal(absRepoPath + " is not a valid git repository.")
|
||||
}
|
||||
|
||||
cliArgs.WorkTree = absRepoPath
|
||||
cliArgs.GitDir = filepath.Join(absRepoPath, ".git")
|
||||
err = os.Chdir(absRepoPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to change directory to %s: %v", absRepoPath, err)
|
||||
}
|
||||
} else if cliArgs.WorkTree != "" {
|
||||
env.SetWorkTreeEnv(cliArgs.WorkTree)
|
||||
|
||||
if err := os.Chdir(cliArgs.WorkTree); err != nil {
|
||||
log.Fatalf("Failed to change directory to %s: %v", cliArgs.WorkTree, err)
|
||||
}
|
||||
}
|
||||
|
||||
if cliArgs.CustomConfigFile != "" {
|
||||
@@ -75,10 +84,6 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
||||
os.Setenv("CONFIG_DIR", cliArgs.UseConfigDir)
|
||||
}
|
||||
|
||||
if cliArgs.WorkTree != "" {
|
||||
env.SetGitWorkTreeEnv(cliArgs.WorkTree)
|
||||
}
|
||||
|
||||
if cliArgs.GitDir != "" {
|
||||
env.SetGitDirEnv(cliArgs.GitDir)
|
||||
}
|
||||
@@ -115,12 +120,6 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if cliArgs.WorkTree != "" {
|
||||
if err := os.Chdir(cliArgs.WorkTree); err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp("", "lazygit-*")
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
@@ -181,10 +180,10 @@ func parseCliArgsAndEnvVars() *cliArgs {
|
||||
useConfigDir := ""
|
||||
flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory")
|
||||
|
||||
workTree := ""
|
||||
workTree := os.Getenv("GIT_WORK_TREE")
|
||||
flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument")
|
||||
|
||||
gitDir := ""
|
||||
gitDir := os.Getenv("GIT_DIR")
|
||||
flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument")
|
||||
|
||||
customConfigFile := ""
|
||||
@@ -277,7 +276,7 @@ func mergeBuildInfo(buildInfo *BuildInfo) {
|
||||
}
|
||||
|
||||
func getGitVersionInfo() string {
|
||||
cmd := secureexec.Command("git", "--version")
|
||||
cmd := exec.Command("git", "--version")
|
||||
stdout, _ := cmd.Output()
|
||||
gitVersion := strings.Trim(strings.TrimPrefix(string(stdout), "git version "), " \r\n")
|
||||
return gitVersion
|
||||
|
||||
@@ -3,8 +3,8 @@ package app
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type errorMapping struct {
|
||||
@@ -18,7 +18,7 @@ func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
|
||||
|
||||
knownErrorMessages := []string{tr.MinGitVersionError}
|
||||
|
||||
if slices.Contains(knownErrorMessages, errorMessage) {
|
||||
if lo.Contains(knownErrorMessages, errorMessage) {
|
||||
return errorMessage, true
|
||||
}
|
||||
|
||||
@@ -27,9 +27,13 @@ func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
|
||||
originalError: "fatal: not a git repository",
|
||||
newError: tr.NotARepository,
|
||||
},
|
||||
{
|
||||
originalError: "getwd: no such file or directory",
|
||||
newError: tr.WorkingDirectoryDoesNotExist,
|
||||
},
|
||||
}
|
||||
|
||||
if mapping, ok := slices.Find(mappings, func(mapping errorMapping) bool {
|
||||
if mapping, ok := lo.Find(mappings, func(mapping errorMapping) bool {
|
||||
return strings.Contains(errorMessage, mapping.originalError)
|
||||
}); ok {
|
||||
return mapping.newError, true
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package cheatsheet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
)
|
||||
|
||||
func Check() {
|
||||
dir := GetKeybindingsDir()
|
||||
tmpDir := filepath.Join(os.TempDir(), "lazygit_cheatsheet")
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
err = os.Mkdir(tmpDir, 0o700)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
|
||||
generateAtDir(tmpDir)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
actualContent := obtainContent(dir)
|
||||
expectedContent := obtainContent(tmpDir)
|
||||
|
||||
if expectedContent == "" {
|
||||
log.Fatal("empty expected content")
|
||||
}
|
||||
|
||||
if actualContent != expectedContent {
|
||||
err := difflib.WriteUnifiedDiff(os.Stdout, difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(expectedContent),
|
||||
B: difflib.SplitLines(actualContent),
|
||||
FromFile: "Expected",
|
||||
FromDate: "",
|
||||
ToFile: "Actual",
|
||||
ToDate: "",
|
||||
Context: 1,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
fmt.Printf("\nCheatsheets are out of date. Please run `%s` at the project root and commit the changes. If you run the script and no keybindings files are updated as a result, try rebasing onto master and trying again.\n", CommandToRun())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("\nCheatsheets are up to date")
|
||||
}
|
||||
|
||||
func obtainContent(dir string) string {
|
||||
re := regexp.MustCompile(`Keybindings_\w+\.md$`)
|
||||
|
||||
content := ""
|
||||
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if re.MatchString(path) {
|
||||
bytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
content += fmt.Sprintf("\n%s\n\n", filepath.Base(path))
|
||||
content += string(bytes)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
// This "script" generates a file called Keybindings_{{.LANG}}.md
|
||||
// in current working directory.
|
||||
//go:generate go run generator.go
|
||||
|
||||
// This "script" generates files called Keybindings_{{.LANG}}.md
|
||||
// in the docs/keybindings directory.
|
||||
//
|
||||
// The content of this generated file is a keybindings cheatsheet.
|
||||
// The content of these generated files is a keybindings cheatsheet.
|
||||
//
|
||||
// To generate cheatsheet in english run:
|
||||
// go run scripts/generate_cheatsheet.go
|
||||
// To generate the cheatsheets, run:
|
||||
// go generate pkg/cheatsheet/generate.go
|
||||
|
||||
package cheatsheet
|
||||
|
||||
@@ -15,7 +17,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/maps"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||
"github.com/jesseduffield/lazygit/pkg/app"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type bindingSection struct {
|
||||
@@ -42,7 +44,7 @@ type headerWithBindings struct {
|
||||
}
|
||||
|
||||
func CommandToRun() string {
|
||||
return "go run scripts/cheatsheet/main.go generate"
|
||||
return "go generate ./..."
|
||||
}
|
||||
|
||||
func GetKeybindingsDir() string {
|
||||
@@ -116,6 +118,7 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
|
||||
"stash": tr.StashTitle,
|
||||
"suggestions": tr.SuggestionsCheatsheetTitle,
|
||||
"extras": tr.ExtrasTitle,
|
||||
"worktrees": tr.WorktreesTitle,
|
||||
}
|
||||
|
||||
title, ok := contextTitleMap[str]
|
||||
@@ -128,7 +131,7 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
|
||||
|
||||
func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*bindingSection {
|
||||
excludedViews := []string{"stagingSecondary", "patchBuildingSecondary"}
|
||||
bindingsToDisplay := slices.Filter(bindings, func(binding *types.Binding) bool {
|
||||
bindingsToDisplay := lo.Filter(bindings, func(binding *types.Binding, _ int) bool {
|
||||
if lo.Contains(excludedViews, binding.ViewName) {
|
||||
return false
|
||||
}
|
||||
@@ -161,7 +164,7 @@ func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*b
|
||||
return a.header.title < b.header.title
|
||||
})
|
||||
|
||||
return slices.Map(bindingGroups, func(hb headerWithBindings) *bindingSection {
|
||||
return lo.Map(bindingGroups, func(hb headerWithBindings, _ int) *bindingSection {
|
||||
return &bindingSection{
|
||||
title: hb.header.title,
|
||||
bindings: hb.bindings,
|
||||
|
||||
14
pkg/cheatsheet/generator.go
Normal file
14
pkg/cheatsheet/generator.go
Normal file
@@ -0,0 +1,14 @@
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/cheatsheet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Generating cheatsheets in %s...\n", cheatsheet.GetKeybindingsDir())
|
||||
cheatsheet.Generate()
|
||||
}
|
||||
@@ -2,11 +2,12 @@ package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
|
||||
// GitCommand is our main git interface
|
||||
type GitCommand struct {
|
||||
Blame *git_commands.BlameCommands
|
||||
Branch *git_commands.BranchCommands
|
||||
Commit *git_commands.CommitCommands
|
||||
Config *git_commands.ConfigCommands
|
||||
@@ -37,6 +39,9 @@ type GitCommand struct {
|
||||
Tag *git_commands.TagCommands
|
||||
WorkingTree *git_commands.WorkingTreeCommands
|
||||
Bisect *git_commands.BisectCommands
|
||||
Worktree *git_commands.WorktreeCommands
|
||||
Version *git_commands.GitVersion
|
||||
RepoPaths *git_commands.RepoPaths
|
||||
|
||||
Loaders Loaders
|
||||
}
|
||||
@@ -50,6 +55,7 @@ type Loaders struct {
|
||||
RemoteLoader *git_commands.RemoteLoader
|
||||
StashLoader *git_commands.StashLoader
|
||||
TagLoader *git_commands.TagLoader
|
||||
Worktrees *git_commands.WorktreeLoader
|
||||
}
|
||||
|
||||
func NewGitCommand(
|
||||
@@ -57,19 +63,50 @@ func NewGitCommand(
|
||||
version *git_commands.GitVersion,
|
||||
osCommand *oscommands.OSCommand,
|
||||
gitConfig git_config.IGitConfig,
|
||||
syncMutex *deadlock.Mutex,
|
||||
) (*GitCommand, error) {
|
||||
if err := navigateToRepoRootDirectory(os.Stat, os.Chdir); err != nil {
|
||||
return nil, err
|
||||
currentPath, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, utils.WrapError(err)
|
||||
}
|
||||
|
||||
repo, err := setupRepository(gogit.PlainOpenWithOptions, gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true}, cmn.Tr.GitconfigParseErr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// converting to forward slashes for the sake of windows (which uses backwards slashes). We want everything
|
||||
// to have forward slashes internally
|
||||
currentPath = filepath.ToSlash(currentPath)
|
||||
|
||||
gitDir := env.GetGitDirEnv()
|
||||
if gitDir != "" {
|
||||
// we've been given the git directory explicitly so no need to navigate to it
|
||||
_, err := cmn.Fs.Stat(gitDir)
|
||||
if err != nil {
|
||||
return nil, utils.WrapError(err)
|
||||
}
|
||||
} else {
|
||||
// we haven't been given the git dir explicitly so we assume it's in the current working directory as `.git/` (or an ancestor directory)
|
||||
|
||||
rootDirectory, err := findWorktreeRoot(cmn.Fs, currentPath)
|
||||
if err != nil {
|
||||
return nil, utils.WrapError(err)
|
||||
}
|
||||
currentPath = rootDirectory
|
||||
err = os.Chdir(rootDirectory)
|
||||
if err != nil {
|
||||
return nil, utils.WrapError(err)
|
||||
}
|
||||
}
|
||||
|
||||
dotGitDir, err := findDotGitDir(os.Stat, os.ReadFile)
|
||||
repoPaths, err := git_commands.GetRepoPaths(cmn.Fs, currentPath)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Error getting repo paths: %v", err)
|
||||
}
|
||||
|
||||
repository, err := gogit.PlainOpenWithOptions(
|
||||
repoPaths.WorktreeGitDirPath(),
|
||||
&gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true},
|
||||
)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
|
||||
return nil, errors.New(cmn.Tr.GitconfigParseErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -78,9 +115,8 @@ func NewGitCommand(
|
||||
version,
|
||||
osCommand,
|
||||
gitConfig,
|
||||
dotGitDir,
|
||||
repo,
|
||||
syncMutex,
|
||||
repoPaths,
|
||||
repository,
|
||||
), nil
|
||||
}
|
||||
|
||||
@@ -89,9 +125,8 @@ func NewGitCommandAux(
|
||||
version *git_commands.GitVersion,
|
||||
osCommand *oscommands.OSCommand,
|
||||
gitConfig git_config.IGitConfig,
|
||||
dotGitDir string,
|
||||
repoPaths *git_commands.RepoPaths,
|
||||
repo *gogit.Repository,
|
||||
syncMutex *deadlock.Mutex,
|
||||
) *GitCommand {
|
||||
cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd)
|
||||
|
||||
@@ -102,9 +137,9 @@ func NewGitCommandAux(
|
||||
// common ones are: cmn, osCommand, dotGitDir, configCommands
|
||||
configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo)
|
||||
|
||||
fileLoader := git_commands.NewFileLoader(cmn, cmd, configCommands)
|
||||
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands)
|
||||
|
||||
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, dotGitDir, repo, configCommands, syncMutex)
|
||||
fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
|
||||
statusCommands := git_commands.NewStatusCommands(gitCommon)
|
||||
flowCommands := git_commands.NewFlowCommands(gitCommon)
|
||||
remoteCommands := git_commands.NewRemoteCommands(gitCommon)
|
||||
@@ -121,22 +156,24 @@ func NewGitCommandAux(
|
||||
stashCommands := git_commands.NewStashCommands(gitCommon, fileLoader, workingTreeCommands)
|
||||
patchBuilder := patch.NewPatchBuilder(cmn.Log,
|
||||
func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
|
||||
// TODO: make patch builder take Gui.IgnoreWhitespaceInDiffView into
|
||||
// account. For now we just pass false.
|
||||
return workingTreeCommands.ShowFileDiff(from, to, reverse, filename, plain, false)
|
||||
return workingTreeCommands.ShowFileDiff(from, to, reverse, filename, plain)
|
||||
})
|
||||
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
||||
bisectCommands := git_commands.NewBisectCommands(gitCommon)
|
||||
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
||||
blameCommands := git_commands.NewBlameCommands(gitCommon)
|
||||
|
||||
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode, gitCommon)
|
||||
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.RebaseMode, gitCommon)
|
||||
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
||||
remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes)
|
||||
worktreeLoader := git_commands.NewWorktreeLoader(gitCommon)
|
||||
stashLoader := git_commands.NewStashLoader(cmn, cmd)
|
||||
tagLoader := git_commands.NewTagLoader(cmn, cmd)
|
||||
|
||||
return &GitCommand{
|
||||
Blame: blameCommands,
|
||||
Branch: branchCommands,
|
||||
Commit: commitCommands,
|
||||
Config: configCommands,
|
||||
@@ -154,6 +191,8 @@ func NewGitCommandAux(
|
||||
Tag: tagCommands,
|
||||
Bisect: bisectCommands,
|
||||
WorkingTree: workingTreeCommands,
|
||||
Worktree: worktreeCommands,
|
||||
Version: version,
|
||||
Loaders: Loaders{
|
||||
BranchLoader: branchLoader,
|
||||
CommitFileLoader: commitFileLoader,
|
||||
@@ -161,121 +200,40 @@ func NewGitCommandAux(
|
||||
FileLoader: fileLoader,
|
||||
ReflogCommitLoader: reflogCommitLoader,
|
||||
RemoteLoader: remoteLoader,
|
||||
Worktrees: worktreeLoader,
|
||||
StashLoader: stashLoader,
|
||||
TagLoader: tagLoader,
|
||||
},
|
||||
RepoPaths: repoPaths,
|
||||
}
|
||||
}
|
||||
|
||||
func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error {
|
||||
gitDir := env.GetGitDirEnv()
|
||||
if gitDir != "" {
|
||||
// we've been given the git directory explicitly so no need to navigate to it
|
||||
_, err := stat(gitDir)
|
||||
if err != nil {
|
||||
return utils.WrapError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// we haven't been given the git dir explicitly so we assume it's in the current working directory as `.git/` (or an ancestor directory)
|
||||
|
||||
// this returns the root of the current worktree. So if you start lazygit from within
|
||||
// a subdirectory of the worktree, it will start in the context of the root of that worktree
|
||||
func findWorktreeRoot(fs afero.Fs, currentPath string) (string, error) {
|
||||
for {
|
||||
_, err := stat(".git")
|
||||
// we don't care if .git is a directory or a file: either is okay.
|
||||
_, err := fs.Stat(path.Join(currentPath, ".git"))
|
||||
|
||||
if err == nil {
|
||||
return nil
|
||||
return currentPath, nil
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
return utils.WrapError(err)
|
||||
return "", utils.WrapError(err)
|
||||
}
|
||||
|
||||
if err = chdir(".."); err != nil {
|
||||
return utils.WrapError(err)
|
||||
}
|
||||
currentPath = path.Dir(currentPath)
|
||||
|
||||
currentPath, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
atRoot := currentPath == filepath.Dir(currentPath)
|
||||
atRoot := currentPath == path.Dir(currentPath)
|
||||
if atRoot {
|
||||
// we should never really land here: the code that creates GitCommand should
|
||||
// verify we're in a git directory
|
||||
return errors.New("Must open lazygit in a git repository")
|
||||
return "", errors.New("Must open lazygit in a git repository")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolvePath takes a path containing a symlink and returns the true path
|
||||
func resolvePath(path string) (string, error) {
|
||||
l, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if l.Mode()&os.ModeSymlink == 0 {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
return filepath.EvalSymlinks(path)
|
||||
}
|
||||
|
||||
func setupRepository(openGitRepository func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error), options gogit.PlainOpenOptions, gitConfigParseErrorStr string) (*gogit.Repository, error) {
|
||||
unresolvedPath := env.GetGitDirEnv()
|
||||
if unresolvedPath == "" {
|
||||
var err error
|
||||
unresolvedPath, err = os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
path, err := resolvePath(unresolvedPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repository, err := openGitRepository(path, &options)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
|
||||
return nil, errors.New(gitConfigParseErrorStr)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return repository, err
|
||||
}
|
||||
|
||||
func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filename string) ([]byte, error)) (string, error) {
|
||||
if env.GetGitDirEnv() != "" {
|
||||
return env.GetGitDirEnv(), nil
|
||||
}
|
||||
|
||||
f, err := stat(".git")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
return ".git", nil
|
||||
}
|
||||
|
||||
fileBytes, err := readFile(".git")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fileContent := string(fileBytes)
|
||||
if !strings.HasPrefix(fileContent, "gitdir: ") {
|
||||
return "", errors.New(".git is a file which suggests we are in a submodule or a worktree but the file's contents do not contain a gitdir pointing to the actual .git directory")
|
||||
}
|
||||
return strings.TrimSpace(strings.TrimPrefix(fileContent, "gitdir: ")), nil
|
||||
}
|
||||
|
||||
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
|
||||
return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run()
|
||||
}
|
||||
|
||||
@@ -19,12 +19,16 @@ func NewBisectCommands(gitCommon *GitCommon) *BisectCommands {
|
||||
// This command is pretty cheap to run so we're not storing the result anywhere.
|
||||
// But if it becomes problematic we can chang that.
|
||||
func (self *BisectCommands) GetInfo() *BisectInfo {
|
||||
return self.GetInfoForGitDir(self.repoPaths.WorktreeGitDirPath())
|
||||
}
|
||||
|
||||
func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
|
||||
var err error
|
||||
info := &BisectInfo{started: false, log: self.Log, newTerm: "bad", oldTerm: "good"}
|
||||
// we return nil if we're not in a git bisect session.
|
||||
// we know we're in a session by the presence of a .git/BISECT_START file
|
||||
|
||||
bisectStartPath := filepath.Join(self.dotGitDir, "BISECT_START")
|
||||
bisectStartPath := filepath.Join(gitDir, "BISECT_START")
|
||||
exists, err := self.os.FileExists(bisectStartPath)
|
||||
if err != nil {
|
||||
self.Log.Infof("error getting git bisect info: %s", err.Error())
|
||||
@@ -44,7 +48,7 @@ func (self *BisectCommands) GetInfo() *BisectInfo {
|
||||
info.started = true
|
||||
info.start = strings.TrimSpace(string(startContent))
|
||||
|
||||
termsContent, err := os.ReadFile(filepath.Join(self.dotGitDir, "BISECT_TERMS"))
|
||||
termsContent, err := os.ReadFile(filepath.Join(gitDir, "BISECT_TERMS"))
|
||||
if err != nil {
|
||||
// old git versions won't have this file so we default to bad/good
|
||||
} else {
|
||||
@@ -53,7 +57,7 @@ func (self *BisectCommands) GetInfo() *BisectInfo {
|
||||
info.oldTerm = splitContent[1]
|
||||
}
|
||||
|
||||
bisectRefsDir := filepath.Join(self.dotGitDir, "refs", "bisect")
|
||||
bisectRefsDir := filepath.Join(gitDir, "refs", "bisect")
|
||||
files, err := os.ReadDir(bisectRefsDir)
|
||||
if err != nil {
|
||||
self.Log.Infof("error getting git bisect info: %s", err.Error())
|
||||
@@ -85,7 +89,7 @@ func (self *BisectCommands) GetInfo() *BisectInfo {
|
||||
info.statusMap[sha] = status
|
||||
}
|
||||
|
||||
currentContent, err := os.ReadFile(filepath.Join(self.dotGitDir, "BISECT_EXPECTED_REV"))
|
||||
currentContent, err := os.ReadFile(filepath.Join(gitDir, "BISECT_EXPECTED_REV"))
|
||||
if err != nil {
|
||||
self.Log.Infof("error getting git bisect info: %s", err.Error())
|
||||
return info
|
||||
@@ -121,6 +125,15 @@ func (self *BisectCommands) Start() error {
|
||||
return self.cmd.New(cmdArgs).StreamOutput().Run()
|
||||
}
|
||||
|
||||
func (self *BisectCommands) StartWithTerms(oldTerm string, newTerm string) error {
|
||||
cmdArgs := NewGitCmd("bisect").Arg("start").
|
||||
Arg("--term-old=" + oldTerm).
|
||||
Arg("--term-new=" + newTerm).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).StreamOutput().Run()
|
||||
}
|
||||
|
||||
// tells us whether we've found our problem commit(s). We return a string slice of
|
||||
// commit sha's if we're done, and that slice may have more that one item if
|
||||
// skipped commits are involved.
|
||||
|
||||
@@ -2,7 +2,7 @@ package git_commands
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/maps"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -97,5 +97,5 @@ func (self *BisectInfo) Bisecting() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return slices.Contains(maps.Values(self.statusMap), BisectStatusOld)
|
||||
return lo.Contains(maps.Values(self.statusMap), BisectStatusOld)
|
||||
}
|
||||
|
||||
33
pkg/commands/git_commands/blame.go
Normal file
33
pkg/commands/git_commands/blame.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type BlameCommands struct {
|
||||
*GitCommon
|
||||
}
|
||||
|
||||
func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
|
||||
return &BlameCommands{
|
||||
GitCommon: gitCommon,
|
||||
}
|
||||
}
|
||||
|
||||
// Blame a range of lines. For each line, output the hash of the commit where
|
||||
// the line last changed, then a space, then a description of the commit (author
|
||||
// and date), another space, and then the line. For example:
|
||||
//
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 11) func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 12) return &BlameCommands{
|
||||
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 13) GitCommon: gitCommon,
|
||||
func (self *BlameCommands) BlameLineRange(filename string, commit string, firstLine int, numLines int) (string, error) {
|
||||
cmdArgs := NewGitCmd("blame").
|
||||
Arg("-l").
|
||||
Arg(fmt.Sprintf("-L%d,+%d", firstLine, numLines)).
|
||||
Arg(commit).
|
||||
Arg("--").
|
||||
Arg(filename)
|
||||
|
||||
return self.cmd.New(cmdArgs.ToArgv()).RunWithOutput()
|
||||
}
|
||||
@@ -70,8 +70,23 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Delete delete branch
|
||||
func (self *BranchCommands) Delete(branch string, force bool) error {
|
||||
// CurrentBranchName get name of current branch
|
||||
func (self *BranchCommands) CurrentBranchName() (string, error) {
|
||||
cmdArgs := NewGitCmd("rev-parse").
|
||||
Arg("--abbrev-ref").
|
||||
Arg("--verify").
|
||||
Arg("HEAD").
|
||||
ToArgv()
|
||||
|
||||
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err == nil {
|
||||
return strings.TrimSpace(output), nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// LocalDelete delete branch locally
|
||||
func (self *BranchCommands) LocalDelete(branch string, force bool) error {
|
||||
cmdArgs := NewGitCmd("branch").
|
||||
ArgIfElse(force, "-D", "-d").
|
||||
Arg(branch).
|
||||
|
||||
@@ -3,16 +3,17 @@ package git_commands
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/go-git/v5/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// context:
|
||||
@@ -62,33 +63,40 @@ func NewBranchLoader(
|
||||
func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch, error) {
|
||||
branches := self.obtainBranches()
|
||||
|
||||
reflogBranches := self.obtainReflogBranches(reflogCommits)
|
||||
|
||||
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
|
||||
branchesWithRecency := make([]*models.Branch, 0)
|
||||
outer:
|
||||
for _, reflogBranch := range reflogBranches {
|
||||
for j, branch := range branches {
|
||||
if branch.Head {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(reflogBranch.Name, branch.Name) {
|
||||
branch.Recency = reflogBranch.Recency
|
||||
branchesWithRecency = append(branchesWithRecency, branch)
|
||||
branches = slices.Remove(branches, j)
|
||||
continue outer
|
||||
if self.AppState.LocalBranchSortOrder == "recency" {
|
||||
reflogBranches := self.obtainReflogBranches(reflogCommits)
|
||||
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
|
||||
branchesWithRecency := make([]*models.Branch, 0)
|
||||
outer:
|
||||
for _, reflogBranch := range reflogBranches {
|
||||
for j, branch := range branches {
|
||||
if branch.Head {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(reflogBranch.Name, branch.Name) {
|
||||
branch.Recency = reflogBranch.Recency
|
||||
branchesWithRecency = append(branchesWithRecency, branch)
|
||||
branches = utils.Remove(branches, j)
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
branches = slices.Prepend(branches, branchesWithRecency...)
|
||||
// Sort branches that don't have a recency value alphabetically
|
||||
// (we're really doing this for the sake of deterministic behaviour across git versions)
|
||||
slices.SortFunc(branches, func(a *models.Branch, b *models.Branch) bool {
|
||||
return a.Name < b.Name
|
||||
})
|
||||
|
||||
branches = utils.Prepend(branches, branchesWithRecency...)
|
||||
}
|
||||
|
||||
foundHead := false
|
||||
for i, branch := range branches {
|
||||
if branch.Head {
|
||||
foundHead = true
|
||||
branch.Recency = " *"
|
||||
branches = slices.Move(branches, i, 0)
|
||||
branches = utils.Move(branches, i, 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -97,7 +105,7 @@ outer:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
branches = slices.Prepend(branches, &models.Branch{Name: info.RefName, DisplayName: info.DisplayName, Head: true, DetachedHead: info.DetachedHead, Recency: " *"})
|
||||
branches = utils.Prepend(branches, &models.Branch{Name: info.RefName, DisplayName: info.DisplayName, Head: true, DetachedHead: info.DetachedHead, Recency: " *"})
|
||||
}
|
||||
|
||||
configBranches, err := self.config.Branches()
|
||||
@@ -125,7 +133,7 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
|
||||
trimmedOutput := strings.TrimSpace(output)
|
||||
outputLines := strings.Split(trimmedOutput, "\n")
|
||||
|
||||
return slices.FilterMap(outputLines, func(line string) (*models.Branch, bool) {
|
||||
return lo.FilterMap(outputLines, func(line string, _ int) (*models.Branch, bool) {
|
||||
if line == "" {
|
||||
return nil, false
|
||||
}
|
||||
@@ -138,7 +146,8 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return obtainBranch(split), true
|
||||
storeCommitDateAsRecency := self.AppState.LocalBranchSortOrder != "recency"
|
||||
return obtainBranch(split, storeCommitDateAsRecency), true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -150,8 +159,18 @@ func (self *BranchLoader) getRawBranches() (string, error) {
|
||||
"%00",
|
||||
)
|
||||
|
||||
var sortOrder string
|
||||
switch strings.ToLower(self.AppState.LocalBranchSortOrder) {
|
||||
case "recency", "date":
|
||||
sortOrder = "-committerdate"
|
||||
case "alphabetical":
|
||||
sortOrder = "refname"
|
||||
default:
|
||||
sortOrder = "refname"
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("for-each-ref").
|
||||
Arg("--sort=-committerdate").
|
||||
Arg(fmt.Sprintf("--sort=%s", sortOrder)).
|
||||
Arg(fmt.Sprintf("--format=%s", format)).
|
||||
Arg("refs/heads").
|
||||
ToArgv()
|
||||
@@ -165,23 +184,33 @@ var branchFields = []string{
|
||||
"upstream:short",
|
||||
"upstream:track",
|
||||
"subject",
|
||||
fmt.Sprintf("objectname:short=%d", utils.COMMIT_HASH_SHORT_SIZE),
|
||||
"objectname",
|
||||
"committerdate:unix",
|
||||
}
|
||||
|
||||
// Obtain branch information from parsed line output of getRawBranches()
|
||||
func obtainBranch(split []string) *models.Branch {
|
||||
func obtainBranch(split []string, storeCommitDateAsRecency bool) *models.Branch {
|
||||
headMarker := split[0]
|
||||
fullName := split[1]
|
||||
upstreamName := split[2]
|
||||
track := split[3]
|
||||
subject := split[4]
|
||||
commitHash := split[5]
|
||||
commitDate := split[6]
|
||||
|
||||
name := strings.TrimPrefix(fullName, "heads/")
|
||||
pushables, pullables, gone := parseUpstreamInfo(upstreamName, track)
|
||||
|
||||
recency := ""
|
||||
if storeCommitDateAsRecency {
|
||||
if unixTimestamp, err := strconv.ParseInt(commitDate, 10, 64); err == nil {
|
||||
recency = utils.UnixToTimeAgo(unixTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
return &models.Branch{
|
||||
Name: name,
|
||||
Recency: recency,
|
||||
Pushables: pushables,
|
||||
Pullables: pullables,
|
||||
UpstreamGone: gone,
|
||||
|
||||
@@ -2,7 +2,9 @@ package git_commands
|
||||
|
||||
// "*|feat/detect-purge|origin/feat/detect-purge|[ahead 1]"
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -10,15 +12,21 @@ import (
|
||||
|
||||
func TestObtainBranch(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
input []string
|
||||
expectedBranch *models.Branch
|
||||
testName string
|
||||
input []string
|
||||
storeCommitDateAsRecency bool
|
||||
expectedBranch *models.Branch
|
||||
}
|
||||
|
||||
// Use a time stamp of 2 1/2 hours ago, resulting in a recency string of "2h"
|
||||
now := time.Now().Unix()
|
||||
timeStamp := strconv.Itoa(int(now - 2.5*60*60))
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "TrimHeads",
|
||||
input: []string{"", "heads/a_branch", "", "", "subject", "123"},
|
||||
testName: "TrimHeads",
|
||||
input: []string{"", "heads/a_branch", "", "", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Pushables: "?",
|
||||
@@ -29,8 +37,9 @@ func TestObtainBranch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "NoUpstream",
|
||||
input: []string{"", "a_branch", "", "", "subject", "123"},
|
||||
testName: "NoUpstream",
|
||||
input: []string{"", "a_branch", "", "", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Pushables: "?",
|
||||
@@ -41,8 +50,9 @@ func TestObtainBranch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "IsHead",
|
||||
input: []string{"*", "a_branch", "", "", "subject", "123"},
|
||||
testName: "IsHead",
|
||||
input: []string{"*", "a_branch", "", "", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Pushables: "?",
|
||||
@@ -53,8 +63,9 @@ func TestObtainBranch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "IsBehindAndAhead",
|
||||
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123"},
|
||||
testName: "IsBehindAndAhead",
|
||||
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Pushables: "3",
|
||||
@@ -65,8 +76,9 @@ func TestObtainBranch(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "RemoteBranchIsGone",
|
||||
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123"},
|
||||
testName: "RemoteBranchIsGone",
|
||||
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: false,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
UpstreamGone: true,
|
||||
@@ -77,11 +89,25 @@ func TestObtainBranch(t *testing.T) {
|
||||
CommitHash: "123",
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "WithCommitDateAsRecency",
|
||||
input: []string{"", "a_branch", "", "", "subject", "123", timeStamp},
|
||||
storeCommitDateAsRecency: true,
|
||||
expectedBranch: &models.Branch{
|
||||
Name: "a_branch",
|
||||
Recency: "2h",
|
||||
Pushables: "?",
|
||||
Pullables: "?",
|
||||
Head: false,
|
||||
Subject: "subject",
|
||||
CommitHash: "123",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
branch := obtainBranch(s.input)
|
||||
branch := obtainBranch(s.input, s.storeCommitDateAsRecency)
|
||||
assert.EqualValues(t, s.expectedBranch, branch)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func TestBranchDeleteBranch(t *testing.T) {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildBranchCommands(commonDeps{runner: s.runner})
|
||||
|
||||
s.test(instance.Delete("test", s.force))
|
||||
s.test(instance.LocalDelete("test", s.force))
|
||||
s.runner.CheckForMissingCalls()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,6 +38,22 @@ func (self *CommitCommands) SetAuthor(value string) error {
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
// Add a commit's coauthor using Github/Gitlab Co-authored-by metadata. Value is expected to be of the form 'Name <Email>'
|
||||
func (self *CommitCommands) AddCoAuthor(sha string, value string) error {
|
||||
message, err := self.GetCommitMessage(sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message = message + fmt.Sprintf("\nCo-authored-by: %s", value)
|
||||
|
||||
cmdArgs := NewGitCmd("commit").
|
||||
Arg("--allow-empty", "--amend", "--only", "-m", message).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
// ResetToCommit reset to commit
|
||||
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
|
||||
cmdArgs := NewGitCmd("reset").Arg("--"+strength, sha).ToArgv()
|
||||
@@ -68,6 +84,19 @@ func (self *CommitCommands) RewordLastCommitInEditorCmdObj() oscommands.ICmdObj
|
||||
return self.cmd.New(NewGitCmd("commit").Arg("--allow-empty", "--amend", "--only").ToArgv())
|
||||
}
|
||||
|
||||
func (self *CommitCommands) RewordLastCommitInEditorWithMessageFileCmdObj(tmpMessageFile string) oscommands.ICmdObj {
|
||||
return self.cmd.New(NewGitCmd("commit").
|
||||
Arg("--allow-empty", "--amend", "--only", "--edit", "--file="+tmpMessageFile).ToArgv())
|
||||
}
|
||||
|
||||
func (self *CommitCommands) CommitInEditorWithMessageFileCmdObj(tmpMessageFile string) oscommands.ICmdObj {
|
||||
return self.cmd.New(NewGitCmd("commit").
|
||||
Arg("--edit").
|
||||
Arg("--file="+tmpMessageFile).
|
||||
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
|
||||
ToArgv())
|
||||
}
|
||||
|
||||
// RewordLastCommit rewords the topmost commit with the given message
|
||||
func (self *CommitCommands) RewordLastCommit(summary string, description string) error {
|
||||
messageArgs := self.commitMessageArgs(summary, description)
|
||||
@@ -108,15 +137,23 @@ func (self *CommitCommands) signoffFlag() string {
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
|
||||
cmdArgs := NewGitCmd("rev-list").
|
||||
cmdArgs := NewGitCmd("log").
|
||||
Arg("--format=%B", "--max-count=1", commitSha).
|
||||
ToArgv()
|
||||
|
||||
messageWithHeader, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
|
||||
message, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
return strings.TrimSpace(message), err
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitSubject(commitSha string) (string, error) {
|
||||
cmdArgs := NewGitCmd("log").
|
||||
Arg("--format=%s", "--max-count=1", commitSha).
|
||||
ToArgv()
|
||||
|
||||
subject, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
return strings.TrimSpace(subject), err
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").Arg("--no-color", commitSha).ToArgv()
|
||||
|
||||
@@ -161,6 +198,20 @@ func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, e
|
||||
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
// Example output:
|
||||
//
|
||||
// cd50c79ae Preserve the commit message correctly even if the description has blank lines
|
||||
// 3ebba5f32 Add test demonstrating a bug with preserving the commit message
|
||||
// 9a423c388 Remove unused function
|
||||
func (self *CommitCommands) GetShasAndCommitMessagesFirstLine(shas []string) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg("--no-patch", "--pretty=format:%h %s").
|
||||
Arg(shas...).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg("--no-patch", "--oneline").
|
||||
@@ -183,10 +234,13 @@ func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New(cmdArgs)
|
||||
}
|
||||
|
||||
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||
contextSize := self.UserConfig.Git.DiffContextSize
|
||||
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string) oscommands.ICmdObj {
|
||||
contextSize := self.AppState.DiffContextSize
|
||||
|
||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
||||
cmdArgs := NewGitCmd("show").
|
||||
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
|
||||
ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
@@ -194,7 +248,7 @@ func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhit
|
||||
Arg("--decorate").
|
||||
Arg("-p").
|
||||
Arg(sha).
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||
ArgIf(filterPath != "", "--", filterPath).
|
||||
ToArgv()
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package git_commands
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
@@ -52,7 +51,7 @@ func getCommitFilesFromFilenames(filenames string) []*models.CommitFile {
|
||||
}
|
||||
|
||||
// typical result looks like 'A my_file' meaning my_file was added
|
||||
return slices.Map(lo.Chunk(lines, 2), func(chunk []string) *models.CommitFile {
|
||||
return lo.Map(lo.Chunk(lines, 2), func(chunk []string, _ int) *models.CommitFile {
|
||||
return &models.CommitFile{
|
||||
ChangeStatus: chunk[0],
|
||||
Name: chunk[1],
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
@@ -47,7 +47,6 @@ type CommitLoader struct {
|
||||
func NewCommitLoader(
|
||||
cmn *common.Common,
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
dotGitDir string,
|
||||
getRebaseMode func() (enums.RebaseMode, error),
|
||||
gitCommon *GitCommon,
|
||||
) *CommitLoader {
|
||||
@@ -57,7 +56,6 @@ func NewCommitLoader(
|
||||
getRebaseMode: getRebaseMode,
|
||||
readFile: os.ReadFile,
|
||||
walkFiles: filepath.Walk,
|
||||
dotGitDir: dotGitDir,
|
||||
mainBranches: nil,
|
||||
GitCommon: gitCommon,
|
||||
}
|
||||
@@ -68,8 +66,11 @@ type GetCommitsOptions struct {
|
||||
FilterPath string
|
||||
IncludeRebaseCommits bool
|
||||
RefName string // e.g. "HEAD" or "my_branch"
|
||||
RefForPushedStatus string // the ref to use for determining pushed/unpushed status
|
||||
// determines if we show the whole git graph i.e. pass the '--all' flag
|
||||
All bool
|
||||
// If non-empty, show divergence from this ref (left-right log)
|
||||
RefToShowDivergenceFrom string
|
||||
}
|
||||
|
||||
// GetCommits obtains the commits of the current branch
|
||||
@@ -95,22 +96,26 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||
defer wg.Done()
|
||||
|
||||
logErr = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
|
||||
commit := self.extractCommitFromLine(line)
|
||||
commit := self.extractCommitFromLine(line, opts.RefToShowDivergenceFrom != "")
|
||||
commits = append(commits, commit)
|
||||
return false, nil
|
||||
})
|
||||
})
|
||||
|
||||
var ancestor string
|
||||
var remoteAncestor string
|
||||
go utils.Safe(func() {
|
||||
defer wg.Done()
|
||||
|
||||
ancestor = self.getMergeBase(opts.RefName)
|
||||
if opts.RefToShowDivergenceFrom != "" {
|
||||
remoteAncestor = self.getMergeBase(opts.RefToShowDivergenceFrom)
|
||||
}
|
||||
})
|
||||
|
||||
passedFirstPushedCommit := false
|
||||
// I can get this before
|
||||
firstPushedCommit, err := self.getFirstPushedCommit(opts.RefName)
|
||||
firstPushedCommit, err := self.getFirstPushedCommit(opts.RefForPushedStatus)
|
||||
if err != nil {
|
||||
// must have no upstream branch so we'll consider everything as pushed
|
||||
passedFirstPushedCommit = true
|
||||
@@ -126,10 +131,12 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||
if commit.Sha == firstPushedCommit {
|
||||
passedFirstPushedCommit = true
|
||||
}
|
||||
if passedFirstPushedCommit {
|
||||
commit.Status = models.StatusPushed
|
||||
} else {
|
||||
commit.Status = models.StatusUnpushed
|
||||
if commit.Status != models.StatusRebasing {
|
||||
if passedFirstPushedCommit {
|
||||
commit.Status = models.StatusPushed
|
||||
} else {
|
||||
commit.Status = models.StatusUnpushed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +144,23 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
if ancestor != "" {
|
||||
commits = self.setCommitMergedStatuses(ancestor, commits)
|
||||
if opts.RefToShowDivergenceFrom != "" {
|
||||
sort.SliceStable(commits, func(i, j int) bool {
|
||||
// In the divergence view we want incoming commits to come first
|
||||
return commits[i].Divergence > commits[j].Divergence
|
||||
})
|
||||
|
||||
_, localSectionStart, found := lo.FindIndexOf(commits, func(commit *models.Commit) bool {
|
||||
return commit.Divergence == models.DivergenceLeft
|
||||
})
|
||||
if !found {
|
||||
localSectionStart = len(commits)
|
||||
}
|
||||
|
||||
setCommitMergedStatuses(remoteAncestor, commits[:localSectionStart])
|
||||
setCommitMergedStatuses(ancestor, commits[localSectionStart:])
|
||||
} else {
|
||||
setCommitMergedStatuses(ancestor, commits)
|
||||
}
|
||||
|
||||
return commits, nil
|
||||
@@ -179,8 +201,8 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
|
||||
// then puts them into a commit object
|
||||
// example input:
|
||||
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
|
||||
func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
|
||||
split := strings.SplitN(line, "\x00", 7)
|
||||
func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool) *models.Commit {
|
||||
split := strings.SplitN(line, "\x00", 8)
|
||||
|
||||
sha := split[0]
|
||||
unixTimestamp := split[1]
|
||||
@@ -189,6 +211,10 @@ func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
|
||||
extraInfo := strings.TrimSpace(split[4])
|
||||
parentHashes := split[5]
|
||||
message := split[6]
|
||||
divergence := models.DivergenceNone
|
||||
if showDivergence {
|
||||
divergence = lo.Ternary(split[7] == "<", models.DivergenceLeft, models.DivergenceRight)
|
||||
}
|
||||
|
||||
tags := []string{}
|
||||
|
||||
@@ -222,6 +248,7 @@ func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
|
||||
AuthorName: authorName,
|
||||
AuthorEmail: authorEmail,
|
||||
Parents: parents,
|
||||
Divergence: divergence,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +262,7 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
commitShas := slices.FilterMap(commits, func(commit *models.Commit) (string, bool) {
|
||||
commitShas := lo.FilterMap(commits, func(commit *models.Commit, _ int) (string, bool) {
|
||||
return commit.Sha, commit.Sha != ""
|
||||
})
|
||||
|
||||
@@ -251,7 +278,7 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
||||
|
||||
fullCommits := map[string]*models.Commit{}
|
||||
err = cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
||||
commit := self.extractCommitFromLine(line)
|
||||
commit := self.extractCommitFromLine(line, false)
|
||||
fullCommits[commit.Sha] = commit
|
||||
return false, nil
|
||||
})
|
||||
@@ -299,7 +326,7 @@ func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) ([]*mo
|
||||
|
||||
func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) {
|
||||
rewrittenCount := 0
|
||||
bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-apply/rewritten"))
|
||||
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply/rewritten"))
|
||||
if err == nil {
|
||||
content := string(bytesContent)
|
||||
rewrittenCount = len(strings.Split(content, "\n"))
|
||||
@@ -307,7 +334,7 @@ func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) {
|
||||
|
||||
// we know we're rebasing, so lets get all the files whose names have numbers
|
||||
commits := []*models.Commit{}
|
||||
err = self.walkFiles(filepath.Join(self.dotGitDir, "rebase-apply"), func(path string, f os.FileInfo, err error) error {
|
||||
err = self.walkFiles(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"), func(path string, f os.FileInfo, err error) error {
|
||||
if rewrittenCount > 0 {
|
||||
rewrittenCount--
|
||||
return nil
|
||||
@@ -348,7 +375,7 @@ func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) {
|
||||
// and extracts out the sha and names of commits that we still have to go
|
||||
// in the rebase:
|
||||
func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, error) {
|
||||
bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"))
|
||||
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"))
|
||||
if err != nil {
|
||||
self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))
|
||||
// we assume an error means the file doesn't exist so we just return
|
||||
@@ -381,7 +408,7 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
|
||||
// Command does not have a commit associated, skip
|
||||
continue
|
||||
}
|
||||
commits = slices.Prepend(commits, &models.Commit{
|
||||
commits = utils.Prepend(commits, &models.Commit{
|
||||
Sha: t.Commit,
|
||||
Name: t.Msg,
|
||||
Status: models.StatusRebasing,
|
||||
@@ -393,7 +420,7 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) string {
|
||||
bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-merge/done"))
|
||||
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/done"))
|
||||
if err != nil {
|
||||
self.Log.Error(fmt.Sprintf("error occurred reading rebase-merge/done: %s", err.Error()))
|
||||
return ""
|
||||
@@ -406,7 +433,7 @@ func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) string {
|
||||
}
|
||||
|
||||
amendFileExists := false
|
||||
if _, err := os.Stat(filepath.Join(self.dotGitDir, "rebase-merge/amend")); err == nil {
|
||||
if _, err := os.Stat(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/amend")); err == nil {
|
||||
amendFileExists = true
|
||||
}
|
||||
|
||||
@@ -495,10 +522,15 @@ func (self *CommitLoader) commitFromPatch(content string) *models.Commit {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitLoader) setCommitMergedStatuses(ancestor string, commits []*models.Commit) []*models.Commit {
|
||||
func setCommitMergedStatuses(ancestor string, commits []*models.Commit) {
|
||||
if ancestor == "" {
|
||||
return
|
||||
}
|
||||
|
||||
passedAncestor := false
|
||||
for i, commit := range commits {
|
||||
if strings.HasPrefix(ancestor, commit.Sha) {
|
||||
// some commits aren't really commits and don't have sha's, such as the update-ref todo
|
||||
if commit.Sha != "" && strings.HasPrefix(ancestor, commit.Sha) {
|
||||
passedAncestor = true
|
||||
}
|
||||
if commit.Status != models.StatusPushed && commit.Status != models.StatusUnpushed {
|
||||
@@ -508,7 +540,6 @@ func (self *CommitLoader) setCommitMergedStatuses(ancestor string, commits []*mo
|
||||
commits[i].Status = models.StatusMerged
|
||||
}
|
||||
}
|
||||
return commits
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getMergeBase(refName string) string {
|
||||
@@ -621,8 +652,13 @@ func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
|
||||
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||
config := self.UserConfig.Git.Log
|
||||
|
||||
refSpec := opts.RefName
|
||||
if opts.RefToShowDivergenceFrom != "" {
|
||||
refSpec += "..." + opts.RefToShowDivergenceFrom
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("log").
|
||||
Arg(opts.RefName).
|
||||
Arg(refSpec).
|
||||
ArgIf(config.Order != "default", "--"+config.Order).
|
||||
ArgIf(opts.All, "--all").
|
||||
Arg("--oneline").
|
||||
@@ -631,6 +667,7 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||
ArgIf(opts.Limit, "-300").
|
||||
ArgIf(opts.FilterPath != "", "--follow").
|
||||
Arg("--no-show-signature").
|
||||
ArgIf(opts.RefToShowDivergenceFrom != "", "--left-right").
|
||||
Arg("--").
|
||||
ArgIf(opts.FilterPath != "", opts.FilterPath).
|
||||
ToArgv()
|
||||
@@ -638,4 +675,4 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||
return self.cmd.New(cmdArgs).DontLog()
|
||||
}
|
||||
|
||||
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s`
|
||||
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m`
|
||||
|
||||
@@ -42,10 +42,10 @@ func TestGetCommits(t *testing.T) {
|
||||
testName: "should return no commits if there are none",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||
|
||||
expectedCommits: []*models.Commit{},
|
||||
expectedError: nil,
|
||||
@@ -54,10 +54,10 @@ func TestGetCommits(t *testing.T) {
|
||||
testName: "should use proper upstream name for branch",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
||||
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||
|
||||
expectedCommits: []*models.Commit{},
|
||||
expectedError: nil,
|
||||
@@ -66,13 +66,13 @@ func TestGetCommits(t *testing.T) {
|
||||
testName: "should return commits if they are present",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||
mainBranches: []string{"master", "main", "develop"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
// here it's seeing which commits are yet to be pushed
|
||||
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
// here it's actually getting all the commits in a formatted form, one per line
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
||||
// here it's testing which of the configured main branches have an upstream
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||
@@ -203,13 +203,13 @@ func TestGetCommits(t *testing.T) {
|
||||
testName: "should not call merge-base for mainBranches if none exist",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||
mainBranches: []string{"master", "main"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
// here it's seeing which commits are yet to be pushed
|
||||
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
// here it's actually getting all the commits in a formatted form, one per line
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||
// here it's testing which of the configured main branches exist; neither does
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
|
||||
@@ -240,13 +240,13 @@ func TestGetCommits(t *testing.T) {
|
||||
testName: "should call merge-base for all main branches that exist",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||
mainBranches: []string{"master", "main", "develop", "1.0-hotfixes"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
// here it's seeing which commits are yet to be pushed
|
||||
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
// here it's actually getting all the commits in a formatted form, one per line
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||
// here it's testing which of the configured main branches exist
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||
@@ -279,10 +279,10 @@ func TestGetCommits(t *testing.T) {
|
||||
testName: "should not specify order if `log.order` is `default`",
|
||||
logOrder: "default",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||
|
||||
expectedCommits: []*models.Commit{},
|
||||
expectedError: nil,
|
||||
@@ -291,10 +291,10 @@ func TestGetCommits(t *testing.T) {
|
||||
testName: "should set filter path",
|
||||
logOrder: "default",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", FilterPath: "src"},
|
||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
|
||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
|
||||
|
||||
expectedCommits: []*models.Commit{},
|
||||
expectedError: nil,
|
||||
@@ -506,3 +506,51 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitLoader_setCommitMergedStatuses(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
commits []*models.Commit
|
||||
ancestor string
|
||||
expectedCommits []*models.Commit
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "basic",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
|
||||
{Sha: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusPushed},
|
||||
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
|
||||
},
|
||||
ancestor: "67890",
|
||||
expectedCommits: []*models.Commit{
|
||||
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
|
||||
{Sha: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusMerged},
|
||||
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusMerged},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "with update-ref",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
|
||||
{Sha: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
|
||||
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
|
||||
},
|
||||
ancestor: "deadbeef",
|
||||
expectedCommits: []*models.Commit{
|
||||
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
|
||||
{Sha: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
|
||||
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.testName, func(t *testing.T) {
|
||||
expectedCommits := scenario.commits
|
||||
setCommitMergedStatuses(scenario.ancestor, expectedCommits)
|
||||
assert.Equal(t, scenario.expectedCommits, expectedCommits)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
filterPath string
|
||||
contextSize int
|
||||
ignoreWhitespace bool
|
||||
extDiffCmd string
|
||||
expected []string
|
||||
}
|
||||
|
||||
@@ -195,28 +196,40 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
filterPath: "",
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
extDiffCmd: "",
|
||||
expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
},
|
||||
{
|
||||
testName: "Default case with filter path",
|
||||
filterPath: "file.txt",
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
|
||||
extDiffCmd: "",
|
||||
expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
filterPath: "",
|
||||
contextSize: 77,
|
||||
ignoreWhitespace: false,
|
||||
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
|
||||
extDiffCmd: "",
|
||||
expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff, ignoring whitespace",
|
||||
filterPath: "",
|
||||
contextSize: 77,
|
||||
ignoreWhitespace: true,
|
||||
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
|
||||
extDiffCmd: "",
|
||||
expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff with external diff command",
|
||||
filterPath: "",
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "difft --color=always",
|
||||
expected: []string{"-c", "diff.external=difft --color=always", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -224,12 +237,15 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
userConfig := config.GetDefaultConfig()
|
||||
userConfig.Git.DiffContextSize = s.contextSize
|
||||
userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd
|
||||
appState := &config.AppState{}
|
||||
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
appState.DiffContextSize = s.contextSize
|
||||
|
||||
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
|
||||
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
|
||||
instance := buildCommitCommands(commonDeps{userConfig: userConfig, appState: appState, runner: runner})
|
||||
|
||||
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).Run())
|
||||
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath).Run())
|
||||
runner.CheckForMissingCalls()
|
||||
})
|
||||
}
|
||||
@@ -244,19 +260,17 @@ func TestGetCommitMsg(t *testing.T) {
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"empty",
|
||||
` commit deadbeef`,
|
||||
``,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"no line breaks (single line)",
|
||||
`commit deadbeef
|
||||
use generics to DRY up context code`,
|
||||
`use generics to DRY up context code`,
|
||||
`use generics to DRY up context code`,
|
||||
},
|
||||
{
|
||||
"with line breaks",
|
||||
`commit deadbeef
|
||||
Merge pull request #1750 from mark2185/fix-issue-template
|
||||
`Merge pull request #1750 from mark2185/fix-issue-template
|
||||
|
||||
'git-rev parse' should be 'git rev-parse'`,
|
||||
`Merge pull request #1750 from mark2185/fix-issue-template
|
||||
@@ -269,7 +283,7 @@ Merge pull request #1750 from mark2185/fix-issue-template
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildCommitCommands(commonDeps{
|
||||
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
|
||||
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
|
||||
})
|
||||
|
||||
output, err := instance.GetCommitMessage("deadbeef")
|
||||
@@ -290,15 +304,14 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"Empty message",
|
||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1"}, "", nil),
|
||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"log", "--format=%B", "--max-count=1"}, "", nil),
|
||||
func(output string, err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Default case to retrieve a commit in history",
|
||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "sha3 \n", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "sha3"}, `commit sha3
|
||||
use generics to DRY up context code`, nil),
|
||||
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "sha3 \n", nil).ExpectGitArgs([]string{"log", "--format=%B", "--max-count=1", "sha3"}, `use generics to DRY up context code`, nil),
|
||||
func(output string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "use generics to DRY up context code", output)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
type GitCommon struct {
|
||||
@@ -12,11 +11,9 @@ type GitCommon struct {
|
||||
version *GitVersion
|
||||
cmd oscommands.ICmdObjBuilder
|
||||
os *oscommands.OSCommand
|
||||
dotGitDir string
|
||||
repoPaths *RepoPaths
|
||||
repo *gogit.Repository
|
||||
config *ConfigCommands
|
||||
// mutex for doing things like push/pull/fetch
|
||||
syncMutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
func NewGitCommon(
|
||||
@@ -24,19 +21,17 @@ func NewGitCommon(
|
||||
version *GitVersion,
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
osCommand *oscommands.OSCommand,
|
||||
dotGitDir string,
|
||||
repoPaths *RepoPaths,
|
||||
repo *gogit.Repository,
|
||||
config *ConfigCommands,
|
||||
syncMutex *deadlock.Mutex,
|
||||
) *GitCommon {
|
||||
return &GitCommon{
|
||||
Common: cmn,
|
||||
version: version,
|
||||
cmd: cmd,
|
||||
os: osCommand,
|
||||
dotGitDir: dotGitDir,
|
||||
repoPaths: repoPaths,
|
||||
repo: repo,
|
||||
config: config,
|
||||
syncMutex: syncMutex,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (self *ConfigCommands) GetPager(width int) string {
|
||||
"columnWidth": strconv.Itoa(width/2 - 6),
|
||||
}
|
||||
|
||||
pagerTemplate := self.UserConfig.Git.Paging.Pager
|
||||
pagerTemplate := string(self.UserConfig.Git.Paging.Pager)
|
||||
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
|
||||
}
|
||||
|
||||
@@ -107,3 +107,7 @@ func (self *ConfigCommands) GetCoreCommentChar() byte {
|
||||
|
||||
return '#'
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetRebaseUpdateRefs() bool {
|
||||
return self.gitConfig.GetBool("rebase.updateRefs")
|
||||
}
|
||||
|
||||
@@ -11,18 +11,21 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
type commonDeps struct {
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
userConfig *config.UserConfig
|
||||
appState *config.AppState
|
||||
gitVersion *GitVersion
|
||||
gitConfig *git_config.FakeGitConfig
|
||||
getenv func(string) string
|
||||
removeFile func(string) error
|
||||
dotGitDir string
|
||||
common *common.Common
|
||||
cmd *oscommands.CmdObjBuilder
|
||||
fs afero.Fs
|
||||
repoPaths *RepoPaths
|
||||
}
|
||||
|
||||
func buildGitCommon(deps commonDeps) *GitCommon {
|
||||
@@ -30,7 +33,17 @@ func buildGitCommon(deps commonDeps) *GitCommon {
|
||||
|
||||
gitCommon.Common = deps.common
|
||||
if gitCommon.Common == nil {
|
||||
gitCommon.Common = utils.NewDummyCommonWithUserConfig(deps.userConfig)
|
||||
gitCommon.Common = utils.NewDummyCommonWithUserConfigAndAppState(deps.userConfig, deps.appState)
|
||||
}
|
||||
|
||||
if deps.fs != nil {
|
||||
gitCommon.Fs = deps.fs
|
||||
}
|
||||
|
||||
if deps.repoPaths != nil {
|
||||
gitCommon.repoPaths = deps.repoPaths
|
||||
} else {
|
||||
gitCommon.repoPaths = MockRepoPaths(".git")
|
||||
}
|
||||
|
||||
runner := deps.runner
|
||||
@@ -81,11 +94,6 @@ func buildGitCommon(deps commonDeps) *GitCommon {
|
||||
TempDir: os.TempDir(),
|
||||
})
|
||||
|
||||
gitCommon.dotGitDir = deps.dotGitDir
|
||||
if gitCommon.dotGitDir == "" {
|
||||
gitCommon.dotGitDir = ".git"
|
||||
}
|
||||
|
||||
return gitCommon
|
||||
}
|
||||
|
||||
@@ -96,7 +104,7 @@ func buildRepo() *gogit.Repository {
|
||||
}
|
||||
|
||||
func buildFileLoader(gitCommon *GitCommon) *FileLoader {
|
||||
return NewFileLoader(gitCommon.Common, gitCommon.cmd, gitCommon.config)
|
||||
return NewFileLoader(gitCommon, gitCommon.cmd, gitCommon.config)
|
||||
}
|
||||
|
||||
func buildSubmoduleCommands(deps commonDeps) *SubmoduleCommands {
|
||||
|
||||
@@ -17,3 +17,72 @@ func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
||||
NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *DiffCommands) internalDiffCmdObj(diffArgs ...string) *GitCommandBuilder {
|
||||
return NewGitCmd("diff").
|
||||
Arg("--no-ext-diff", "--no-color").
|
||||
Arg(diffArgs...)
|
||||
}
|
||||
|
||||
func (self *DiffCommands) GetPathDiff(path string, staged bool) (string, error) {
|
||||
return self.cmd.New(
|
||||
self.internalDiffCmdObj().
|
||||
ArgIf(staged, "--staged").
|
||||
Arg(path).
|
||||
ToArgv(),
|
||||
).RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *DiffCommands) GetAllDiff(staged bool) (string, error) {
|
||||
return self.cmd.New(
|
||||
self.internalDiffCmdObj().
|
||||
ArgIf(staged, "--staged").
|
||||
ToArgv(),
|
||||
).RunWithOutput()
|
||||
}
|
||||
|
||||
type DiffToolCmdOptions struct {
|
||||
// The path to show a diff for. Pass "." for the entire repo.
|
||||
Filepath string
|
||||
|
||||
// The commit against which to show the diff. Leave empty to show a diff of
|
||||
// the working copy.
|
||||
FromCommit string
|
||||
|
||||
// The commit to diff against FromCommit. Leave empty to diff the working
|
||||
// copy against FromCommit. Leave both FromCommit and ToCommit empty to show
|
||||
// the diff of the unstaged working copy changes against the index if Staged
|
||||
// is false, or the staged changes against HEAD if Staged is true.
|
||||
ToCommit string
|
||||
|
||||
// Whether to reverse the left and right sides of the diff.
|
||||
Reverse bool
|
||||
|
||||
// Whether the given Filepath is a directory. We'll pass --dir-diff to
|
||||
// git-difftool in that case.
|
||||
IsDirectory bool
|
||||
|
||||
// Whether to show the staged or the unstaged changes. Must be false if both
|
||||
// FromCommit and ToCommit are non-empty.
|
||||
Staged bool
|
||||
}
|
||||
|
||||
func (self *DiffCommands) OpenDiffToolCmdObj(opts DiffToolCmdOptions) oscommands.ICmdObj {
|
||||
return self.cmd.New(NewGitCmd("difftool").
|
||||
Arg("--no-prompt").
|
||||
ArgIf(opts.IsDirectory, "--dir-diff").
|
||||
ArgIf(opts.Staged, "--cached").
|
||||
ArgIf(opts.FromCommit != "", opts.FromCommit).
|
||||
ArgIf(opts.ToCommit != "", opts.ToCommit).
|
||||
ArgIf(opts.Reverse, "-R").
|
||||
Arg("--", opts.Filepath).
|
||||
ToArgv())
|
||||
}
|
||||
|
||||
func (self *DiffCommands) DiffIndexCmdObj(diffArgs ...string) oscommands.ICmdObj {
|
||||
return self.cmd.New(
|
||||
NewGitCmd("diff-index").
|
||||
Arg("--submodule", "--no-ext-diff", "--no-color", "--patch").
|
||||
Arg(diffArgs...).ToArgv(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -83,14 +83,14 @@ func (self *FileCommands) GetEditCmdStr(filename string) (string, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
template, editInTerminal := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
template, suspend := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
|
||||
templateValues := map[string]string{
|
||||
"filename": self.cmd.Quote(filename),
|
||||
}
|
||||
|
||||
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
|
||||
return cmdStr, editInTerminal
|
||||
return cmdStr, suspend
|
||||
}
|
||||
|
||||
func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (string, bool) {
|
||||
@@ -101,7 +101,7 @@ func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (
|
||||
}
|
||||
}
|
||||
|
||||
template, editInTerminal := config.GetEditAtLineTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
template, suspend := config.GetEditAtLineTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
|
||||
templateValues := map[string]string{
|
||||
"filename": self.cmd.Quote(filename),
|
||||
@@ -109,7 +109,7 @@ func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (
|
||||
}
|
||||
|
||||
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
|
||||
return cmdStr, editInTerminal
|
||||
return cmdStr, suspend
|
||||
}
|
||||
|
||||
func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber int) string {
|
||||
@@ -131,6 +131,17 @@ func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber
|
||||
return cmdStr
|
||||
}
|
||||
|
||||
func (self *FileCommands) GetOpenDirInEditorCmdStr(path string) (string, bool) {
|
||||
template, suspend := config.GetOpenDirInEditorTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||
|
||||
templateValues := map[string]string{
|
||||
"dir": self.cmd.Quote(path),
|
||||
}
|
||||
|
||||
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
|
||||
return cmdStr, suspend
|
||||
}
|
||||
|
||||
func (self *FileCommands) guessDefaultEditor() string {
|
||||
// Try to query a few places where editors get configured
|
||||
editor := self.config.GetCoreEditor()
|
||||
|
||||
@@ -2,11 +2,11 @@ package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
)
|
||||
|
||||
type FileLoaderConfig interface {
|
||||
@@ -14,15 +14,15 @@ type FileLoaderConfig interface {
|
||||
}
|
||||
|
||||
type FileLoader struct {
|
||||
*common.Common
|
||||
*GitCommon
|
||||
cmd oscommands.ICmdObjBuilder
|
||||
config FileLoaderConfig
|
||||
getFileType func(string) string
|
||||
}
|
||||
|
||||
func NewFileLoader(cmn *common.Common, cmd oscommands.ICmdObjBuilder, config FileLoaderConfig) *FileLoader {
|
||||
func NewFileLoader(gitCommon *GitCommon, cmd oscommands.ICmdObjBuilder, config FileLoaderConfig) *FileLoader {
|
||||
return &FileLoader{
|
||||
Common: cmn,
|
||||
GitCommon: gitCommon,
|
||||
cmd: cmd,
|
||||
getFileType: oscommands.FileType,
|
||||
config: config,
|
||||
@@ -58,13 +58,32 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
||||
Name: status.Name,
|
||||
PreviousName: status.PreviousName,
|
||||
DisplayString: status.StatusString,
|
||||
Type: self.getFileType(status.Name),
|
||||
}
|
||||
|
||||
models.SetStatusFields(file, status.Change)
|
||||
files = append(files, file)
|
||||
}
|
||||
|
||||
// Go through the files to see if any of these files are actually worktrees
|
||||
// so that we can render them correctly
|
||||
worktreePaths := linkedWortkreePaths(self.Fs, self.repoPaths.RepoGitDirPath())
|
||||
for _, file := range files {
|
||||
for _, worktreePath := range worktreePaths {
|
||||
absFilePath, err := filepath.Abs(file.Name)
|
||||
if err != nil {
|
||||
self.Log.Error(err)
|
||||
continue
|
||||
}
|
||||
if absFilePath == worktreePath {
|
||||
file.IsWorktree = true
|
||||
// `git status` renders this worktree as a folder with a trailing slash but we'll represent it as a singular worktree
|
||||
// If we include the slash, it will be rendered as a folder with a null file inside.
|
||||
file.Name = strings.TrimSuffix(file.Name, "/")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -41,7 +40,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "MM file1.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "MM",
|
||||
},
|
||||
{
|
||||
@@ -54,7 +52,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "A file3.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "A ",
|
||||
},
|
||||
{
|
||||
@@ -67,7 +64,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "AM file2.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "AM",
|
||||
},
|
||||
{
|
||||
@@ -80,7 +76,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "?? file4.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "??",
|
||||
},
|
||||
{
|
||||
@@ -93,7 +88,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: true,
|
||||
HasInlineMergeConflicts: true,
|
||||
DisplayString: "UU file5.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "UU",
|
||||
},
|
||||
},
|
||||
@@ -113,7 +107,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "MM a\nb.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "MM",
|
||||
},
|
||||
},
|
||||
@@ -137,7 +130,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "R before1.txt -> after1.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "R ",
|
||||
},
|
||||
{
|
||||
@@ -151,7 +143,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "RM before2.txt -> after2.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "RM",
|
||||
},
|
||||
},
|
||||
@@ -174,7 +165,6 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "?? a -> b.txt",
|
||||
Type: "file",
|
||||
ShortStatus: "??",
|
||||
},
|
||||
},
|
||||
@@ -187,7 +177,7 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||
cmd := oscommands.NewDummyCmdObjBuilder(s.runner)
|
||||
|
||||
loader := &FileLoader{
|
||||
Common: utils.NewDummyCommon(),
|
||||
GitCommon: buildGitCommon(commonDeps{}),
|
||||
cmd: cmd,
|
||||
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
|
||||
getFileType: func(string) string { return "file" },
|
||||
|
||||
@@ -179,34 +179,34 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
|
||||
|
||||
func TestEditFileCmd(t *testing.T) {
|
||||
type scenario struct {
|
||||
filename string
|
||||
osConfig config.OSConfig
|
||||
expectedCmdStr string
|
||||
expectedEditInTerminal bool
|
||||
filename string
|
||||
osConfig config.OSConfig
|
||||
expectedCmdStr string
|
||||
suspend bool
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
filename: "test",
|
||||
osConfig: config.OSConfig{},
|
||||
expectedCmdStr: `vim -- "test"`,
|
||||
expectedEditInTerminal: true,
|
||||
filename: "test",
|
||||
osConfig: config.OSConfig{},
|
||||
expectedCmdStr: `vim -- "test"`,
|
||||
suspend: true,
|
||||
},
|
||||
{
|
||||
filename: "test",
|
||||
osConfig: config.OSConfig{
|
||||
Edit: "nano {{filename}}",
|
||||
},
|
||||
expectedCmdStr: `nano "test"`,
|
||||
expectedEditInTerminal: true,
|
||||
expectedCmdStr: `nano "test"`,
|
||||
suspend: true,
|
||||
},
|
||||
{
|
||||
filename: "file/with space",
|
||||
osConfig: config.OSConfig{
|
||||
EditPreset: "sublime",
|
||||
},
|
||||
expectedCmdStr: `subl -- "file/with space"`,
|
||||
expectedEditInTerminal: false,
|
||||
expectedCmdStr: `subl -- "file/with space"`,
|
||||
suspend: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -218,28 +218,28 @@ func TestEditFileCmd(t *testing.T) {
|
||||
userConfig: userConfig,
|
||||
})
|
||||
|
||||
cmdStr, editInTerminal := instance.GetEditCmdStr(s.filename)
|
||||
cmdStr, suspend := instance.GetEditCmdStr(s.filename)
|
||||
assert.Equal(t, s.expectedCmdStr, cmdStr)
|
||||
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
|
||||
assert.Equal(t, s.suspend, suspend)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEditFileAtLineCmd(t *testing.T) {
|
||||
type scenario struct {
|
||||
filename string
|
||||
lineNumber int
|
||||
osConfig config.OSConfig
|
||||
expectedCmdStr string
|
||||
expectedEditInTerminal bool
|
||||
filename string
|
||||
lineNumber int
|
||||
osConfig config.OSConfig
|
||||
expectedCmdStr string
|
||||
suspend bool
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
filename: "test",
|
||||
lineNumber: 42,
|
||||
osConfig: config.OSConfig{},
|
||||
expectedCmdStr: `vim +42 -- "test"`,
|
||||
expectedEditInTerminal: true,
|
||||
filename: "test",
|
||||
lineNumber: 42,
|
||||
osConfig: config.OSConfig{},
|
||||
expectedCmdStr: `vim +42 -- "test"`,
|
||||
suspend: true,
|
||||
},
|
||||
{
|
||||
filename: "test",
|
||||
@@ -247,8 +247,8 @@ func TestEditFileAtLineCmd(t *testing.T) {
|
||||
osConfig: config.OSConfig{
|
||||
EditAtLine: "nano +{{line}} {{filename}}",
|
||||
},
|
||||
expectedCmdStr: `nano +35 "test"`,
|
||||
expectedEditInTerminal: true,
|
||||
expectedCmdStr: `nano +35 "test"`,
|
||||
suspend: true,
|
||||
},
|
||||
{
|
||||
filename: "file/with space",
|
||||
@@ -256,8 +256,8 @@ func TestEditFileAtLineCmd(t *testing.T) {
|
||||
osConfig: config.OSConfig{
|
||||
EditPreset: "sublime",
|
||||
},
|
||||
expectedCmdStr: `subl -- "file/with space":12`,
|
||||
expectedEditInTerminal: false,
|
||||
expectedCmdStr: `subl -- "file/with space":12`,
|
||||
suspend: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -269,9 +269,9 @@ func TestEditFileAtLineCmd(t *testing.T) {
|
||||
userConfig: userConfig,
|
||||
})
|
||||
|
||||
cmdStr, editInTerminal := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
|
||||
cmdStr, suspend := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
|
||||
assert.Equal(t, s.expectedCmdStr, cmdStr)
|
||||
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
|
||||
assert.Equal(t, s.suspend, suspend)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package git_commands
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// convenience struct for building git commands. Especially useful when
|
||||
// including conditional args
|
||||
@@ -42,9 +44,42 @@ func (self *GitCommandBuilder) Config(value string) *GitCommandBuilder {
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) RepoPath(value string) *GitCommandBuilder {
|
||||
func (self *GitCommandBuilder) ConfigIf(condition bool, ifTrue string) *GitCommandBuilder {
|
||||
if condition {
|
||||
self.Config(ifTrue)
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
// the -C arg will make git do a `cd` to the directory before doing anything else
|
||||
func (self *GitCommandBuilder) Dir(path string) *GitCommandBuilder {
|
||||
// repo path comes before the command
|
||||
self.args = append([]string{"-C", value}, self.args...)
|
||||
self.args = append([]string{"-C", path}, self.args...)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
// Note, you may prefer to use the Dir method instead of this one
|
||||
func (self *GitCommandBuilder) Worktree(path string) *GitCommandBuilder {
|
||||
// worktree arg comes before the command
|
||||
self.args = append([]string{"--work-tree", path}, self.args...)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
// Note, you may prefer to use the Dir method instead of this one
|
||||
func (self *GitCommandBuilder) GitDir(path string) *GitCommandBuilder {
|
||||
// git dir arg comes before the command
|
||||
self.args = append([]string{"--git-dir", path}, self.args...)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) GitDirIf(condition bool, path string) *GitCommandBuilder {
|
||||
if condition {
|
||||
return self.GitDir(path)
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestGitCommandBuilder(t *testing.T) {
|
||||
expected: []string{"git", "-c", "user.email=bar", "-c", "user.name=foo", "push"},
|
||||
},
|
||||
{
|
||||
input: NewGitCmd("push").RepoPath("a/b/c").ToArgv(),
|
||||
input: NewGitCmd("push").Dir("a/b/c").ToArgv(),
|
||||
expected: []string{"git", "-C", "a/b/c", "push"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type PatchCommands struct {
|
||||
@@ -80,7 +79,7 @@ func (self *PatchCommands) applyPatchFile(filepath string, opts ApplyPatchOpts)
|
||||
}
|
||||
|
||||
func (self *PatchCommands) SaveTemporaryPatch(patch string) (string, error) {
|
||||
filepath := filepath.Join(self.os.GetTempDir(), utils.GetCurrentRepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch")
|
||||
filepath := filepath.Join(self.os.GetTempDir(), self.repoPaths.RepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch")
|
||||
self.Log.Infof("saving temporary patch to %s", filepath)
|
||||
if err := self.os.CreateFileWithContent(filepath, patch); err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/app/daemon"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
@@ -80,6 +79,12 @@ func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, index int,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) AddCommitCoAuthor(commits []*models.Commit, index int, value string) error {
|
||||
return self.GenericAmend(commits, index, func() error {
|
||||
return self.commit.AddCoAuthor(commits[index].Sha, value)
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f func() error) error {
|
||||
if models.IsHeadCommit(commits, index) {
|
||||
// we've selected the top commit so no rebase is required
|
||||
@@ -105,7 +110,13 @@ func (self *RebaseCommands) MoveCommitDown(commits []*models.Commit, index int)
|
||||
|
||||
sha := commits[index].Sha
|
||||
|
||||
self.os.LogCommand(fmt.Sprintf("Moving TODO down: %s", utils.ShortSha(sha)), false)
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.MoveCommitDown,
|
||||
map[string]string{
|
||||
"shortSha": utils.ShortSha(sha),
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseShaOrRoot,
|
||||
@@ -119,7 +130,13 @@ func (self *RebaseCommands) MoveCommitUp(commits []*models.Commit, index int) er
|
||||
|
||||
sha := commits[index].Sha
|
||||
|
||||
self.os.LogCommand(fmt.Sprintf("Moving TODO up: %s", utils.ShortSha(sha)), false)
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.MoveCommitUp,
|
||||
map[string]string{
|
||||
"shortSha": utils.ShortSha(sha),
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseShaOrRoot,
|
||||
@@ -150,15 +167,37 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, index in
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) EditRebase(branchRef string) error {
|
||||
self.os.LogCommand(fmt.Sprintf("Beginning interactive rebase at '%s'", branchRef), false)
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.EditRebase,
|
||||
map[string]string{
|
||||
"ref": branchRef,
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: branchRef,
|
||||
instruction: daemon.NewInsertBreakInstruction(),
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) EditRebaseFromBaseCommit(targetBranchName string, baseCommit string) error {
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.EditRebaseFromBaseCommit,
|
||||
map[string]string{
|
||||
"baseCommit": baseCommit,
|
||||
"targetBranchName": targetBranchName,
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseCommit,
|
||||
onto: targetBranchName,
|
||||
instruction: daemon.NewInsertBreakInstruction(),
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func logTodoChanges(changes []daemon.ChangeTodoAction) string {
|
||||
changeTodoStr := strings.Join(slices.Map(changes, func(c daemon.ChangeTodoAction) string {
|
||||
changeTodoStr := strings.Join(lo.Map(changes, func(c daemon.ChangeTodoAction, _ int) string {
|
||||
return fmt.Sprintf("%s:%s", c.Sha, c.NewAction)
|
||||
}), "\n")
|
||||
return fmt.Sprintf("Changing TODO actions: %s", changeTodoStr)
|
||||
@@ -166,6 +205,7 @@ func logTodoChanges(changes []daemon.ChangeTodoAction) string {
|
||||
|
||||
type PrepareInteractiveRebaseCommandOpts struct {
|
||||
baseShaOrRoot string
|
||||
onto string
|
||||
instruction daemon.Instruction
|
||||
overrideEditor bool
|
||||
keepCommitsThatBecomeEmpty bool
|
||||
@@ -181,9 +221,10 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
|
||||
Arg("--interactive").
|
||||
Arg("--autostash").
|
||||
Arg("--keep-empty").
|
||||
ArgIf(opts.keepCommitsThatBecomeEmpty && !self.version.IsOlderThan(2, 26, 0), "--empty=keep").
|
||||
ArgIf(opts.keepCommitsThatBecomeEmpty && self.version.IsAtLeast(2, 26, 0), "--empty=keep").
|
||||
Arg("--no-autosquash").
|
||||
ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges").
|
||||
ArgIf(self.version.IsAtLeast(2, 22, 0), "--rebase-merges").
|
||||
ArgIf(opts.onto != "", "--onto", opts.onto).
|
||||
Arg(opts.baseShaOrRoot).
|
||||
ToArgv()
|
||||
|
||||
@@ -243,18 +284,18 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
||||
// EditRebaseTodo sets the action for a given rebase commit in the git-rebase-todo file
|
||||
func (self *RebaseCommands) EditRebaseTodo(commit *models.Commit, action todo.TodoCommand) error {
|
||||
return utils.EditRebaseTodo(
|
||||
filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action, self.config.GetCoreCommentChar())
|
||||
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action, self.config.GetCoreCommentChar())
|
||||
}
|
||||
|
||||
// MoveTodoDown moves a rebase todo item down by one position
|
||||
func (self *RebaseCommands) MoveTodoDown(commit *models.Commit) error {
|
||||
fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo")
|
||||
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
|
||||
return utils.MoveTodoDown(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar())
|
||||
}
|
||||
|
||||
// MoveTodoDown moves a rebase todo item down by one position
|
||||
func (self *RebaseCommands) MoveTodoUp(commit *models.Commit) error {
|
||||
fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo")
|
||||
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
|
||||
return utils.MoveTodoUp(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar())
|
||||
}
|
||||
|
||||
@@ -307,6 +348,13 @@ func (self *RebaseCommands) RebaseBranch(branchName string) error {
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{baseShaOrRoot: branchName}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) RebaseBranchFromBaseCommit(targetBranchName string, baseCommit string) error {
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: baseCommit,
|
||||
onto: targetBranchName,
|
||||
}).Run()
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd(commandType).Arg("--" + command).ToArgv()
|
||||
|
||||
@@ -395,7 +443,13 @@ func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
|
||||
commitLines := lo.Map(commits, func(commit *models.Commit, _ int) string {
|
||||
return fmt.Sprintf("%s %s", utils.ShortSha(commit.Sha), commit.Name)
|
||||
})
|
||||
self.os.LogCommand(fmt.Sprintf("Cherry-picking commits:\n%s", strings.Join(commitLines, "\n")), false)
|
||||
msg := utils.ResolvePlaceholderString(
|
||||
self.Tr.Log.CherryPickCommits,
|
||||
map[string]string{
|
||||
"commitLines": strings.Join(commitLines, "\n"),
|
||||
},
|
||||
)
|
||||
self.os.LogCommand(msg, false)
|
||||
|
||||
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
|
||||
baseShaOrRoot: "HEAD",
|
||||
@@ -403,6 +457,20 @@ func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
|
||||
}).Run()
|
||||
}
|
||||
|
||||
// CherryPickCommitsDuringRebase simply prepends the given commits to the existing git-rebase-todo file
|
||||
func (self *RebaseCommands) CherryPickCommitsDuringRebase(commits []*models.Commit) error {
|
||||
todoLines := lo.Map(commits, func(commit *models.Commit, _ int) daemon.TodoLine {
|
||||
return daemon.TodoLine{
|
||||
Action: "pick",
|
||||
Commit: commit,
|
||||
}
|
||||
})
|
||||
|
||||
todo := daemon.TodoLinesToString(todoLines)
|
||||
filePath := filepath.Join(self.repoPaths.worktreeGitDirPath, "rebase-merge/git-rebase-todo")
|
||||
return utils.PrependStrToTodoFile(filePath, []byte(todo))
|
||||
}
|
||||
|
||||
// we can't start an interactive rebase from the first commit without passing the
|
||||
// '--root' arg
|
||||
func getBaseShaOrRoot(commits []*models.Commit, index int) string {
|
||||
|
||||
@@ -2,6 +2,7 @@ package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
)
|
||||
@@ -53,7 +54,15 @@ func (self *RemoteCommands) DeleteRemoteBranch(task gocui.Task, remoteName strin
|
||||
Arg(remoteName, "--delete", branchName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
func (self *RemoteCommands) DeleteRemoteTag(task gocui.Task, remoteName string, tagName string) error {
|
||||
cmdArgs := NewGitCmd("push").
|
||||
Arg(remoteName, "--delete", tagName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
// CheckRemoteBranchExists Returns remote branch
|
||||
@@ -66,3 +75,14 @@ func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Resolve what might be a aliased URL into a full URL
|
||||
// SEE: `man -P 'less +/--get-url +n' git-ls-remote`
|
||||
func (self *RemoteCommands) GetRemoteURL(remoteName string) (string, error) {
|
||||
cmdArgs := NewGitCmd("ls-remote").
|
||||
Arg("--get-url", remoteName).
|
||||
ToArgv()
|
||||
|
||||
url, err := self.cmd.New(cmdArgs).RunWithOutput()
|
||||
return strings.TrimSpace(url), err
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type RemoteLoader struct {
|
||||
@@ -53,7 +55,7 @@ func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
|
||||
return nil, remoteBranchesErr
|
||||
}
|
||||
|
||||
remotes := slices.Map(goGitRemotes, func(goGitRemote *gogit.Remote) *models.Remote {
|
||||
remotes := lo.Map(goGitRemotes, func(goGitRemote *gogit.Remote, _ int) *models.Remote {
|
||||
remoteName := goGitRemote.Config().Name
|
||||
branches := remoteBranchesByRemoteName[remoteName]
|
||||
|
||||
@@ -82,14 +84,23 @@ func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
|
||||
func (self *RemoteLoader) getRemoteBranchesByRemoteName() (map[string][]*models.RemoteBranch, error) {
|
||||
remoteBranchesByRemoteName := make(map[string][]*models.RemoteBranch)
|
||||
|
||||
cmdArgs := NewGitCmd("branch").Arg("-r").ToArgv()
|
||||
err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) {
|
||||
// excluding lines like 'origin/HEAD -> origin/master' (there will be a separate
|
||||
// line for 'origin/master')
|
||||
if strings.Contains(line, "->") {
|
||||
return false, nil
|
||||
}
|
||||
var sortOrder string
|
||||
switch strings.ToLower(self.AppState.RemoteBranchSortOrder) {
|
||||
case "alphabetical":
|
||||
sortOrder = "refname"
|
||||
case "date":
|
||||
sortOrder = "-committerdate"
|
||||
default:
|
||||
sortOrder = "refname"
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("for-each-ref").
|
||||
Arg(fmt.Sprintf("--sort=%s", sortOrder)).
|
||||
Arg("--format=%(refname:short)").
|
||||
Arg("refs/remotes").
|
||||
ToArgv()
|
||||
|
||||
err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
split := strings.SplitN(line, "/", 2)
|
||||
|
||||
280
pkg/commands/git_commands/repo_paths.go
Normal file
280
pkg/commands/git_commands/repo_paths.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
ioFs "io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
type RepoPaths struct {
|
||||
currentPath string
|
||||
worktreePath string
|
||||
worktreeGitDirPath string
|
||||
repoPath string
|
||||
repoGitDirPath string
|
||||
repoName string
|
||||
}
|
||||
|
||||
// Current working directory of the program. Currently, this will always
|
||||
// be the same as WorktreePath(), but in future we may support running
|
||||
// lazygit from inside a subdirectory of the worktree.
|
||||
func (self *RepoPaths) CurrentPath() string {
|
||||
return self.currentPath
|
||||
}
|
||||
|
||||
// Path to the current worktree. If we're in the main worktree, this will
|
||||
// be the same as RepoPath()
|
||||
func (self *RepoPaths) WorktreePath() string {
|
||||
return self.worktreePath
|
||||
}
|
||||
|
||||
// Path of the worktree's git dir.
|
||||
// If we're in the main worktree, this will be the .git dir under the RepoPath().
|
||||
// If we're in a linked worktree, it will be the directory pointed at by the worktree's .git file
|
||||
func (self *RepoPaths) WorktreeGitDirPath() string {
|
||||
return self.worktreeGitDirPath
|
||||
}
|
||||
|
||||
// Path of the repo. If we're in a the main worktree, this will be the same as WorktreePath()
|
||||
// If we're in a bare repo, it will be the parent folder of the bare repo
|
||||
func (self *RepoPaths) RepoPath() string {
|
||||
return self.repoPath
|
||||
}
|
||||
|
||||
// path of the git-dir for the repo.
|
||||
// If this is a bare repo, it will be the location of the bare repo
|
||||
// If this is a non-bare repo, it will be the location of the .git dir in
|
||||
// the main worktree.
|
||||
func (self *RepoPaths) RepoGitDirPath() string {
|
||||
return self.repoGitDirPath
|
||||
}
|
||||
|
||||
// Name of the repo. Basename of the folder containing the repo.
|
||||
func (self *RepoPaths) RepoName() string {
|
||||
return self.repoName
|
||||
}
|
||||
|
||||
// Returns the repo paths for a typical repo
|
||||
func MockRepoPaths(currentPath string) *RepoPaths {
|
||||
return &RepoPaths{
|
||||
currentPath: currentPath,
|
||||
worktreePath: currentPath,
|
||||
worktreeGitDirPath: path.Join(currentPath, ".git"),
|
||||
repoPath: currentPath,
|
||||
repoGitDirPath: path.Join(currentPath, ".git"),
|
||||
repoName: "lazygit",
|
||||
}
|
||||
}
|
||||
|
||||
func GetRepoPaths(
|
||||
fs afero.Fs,
|
||||
currentPath string,
|
||||
) (*RepoPaths, error) {
|
||||
return getRepoPathsAux(afero.NewOsFs(), resolveSymlink, currentPath)
|
||||
}
|
||||
|
||||
func getRepoPathsAux(
|
||||
fs afero.Fs,
|
||||
resolveSymlinkFn func(string) (string, error),
|
||||
currentPath string,
|
||||
) (*RepoPaths, error) {
|
||||
worktreePath := currentPath
|
||||
repoGitDirPath, repoPath, err := getCurrentRepoGitDirPath(fs, resolveSymlinkFn, currentPath)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to get repo git dir path: %v", err)
|
||||
}
|
||||
|
||||
var worktreeGitDirPath string
|
||||
if env.GetWorkTreeEnv() != "" {
|
||||
// This env is set when you pass --work-tree to lazygit. In that case,
|
||||
// we're not dealing with a linked work-tree, we're dealing with a 'specified'
|
||||
// worktree (for lack of a better term). In this case, the worktree has no
|
||||
// .git file and it just contains a bunch of files: it has no idea it's
|
||||
// pointed to by a bare repo. As such it does not have its own git dir within
|
||||
// the bare repo's git dir. Instead, we just use the bare repo's git dir.
|
||||
worktreeGitDirPath = repoGitDirPath
|
||||
} else {
|
||||
var err error
|
||||
worktreeGitDirPath, err = getWorktreeGitDirPath(fs, currentPath)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to get worktree git dir path: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
repoName := path.Base(repoPath)
|
||||
|
||||
return &RepoPaths{
|
||||
currentPath: currentPath,
|
||||
worktreePath: worktreePath,
|
||||
worktreeGitDirPath: worktreeGitDirPath,
|
||||
repoPath: repoPath,
|
||||
repoGitDirPath: repoGitDirPath,
|
||||
repoName: repoName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Returns the path of the git-dir for the worktree. For linked worktrees, the worktree has
|
||||
// a .git file that points to the git-dir (which itself lives in the git-dir
|
||||
// of the repo)
|
||||
func getWorktreeGitDirPath(fs afero.Fs, worktreePath string) (string, error) {
|
||||
// if .git is a file, we're in a linked worktree, otherwise we're in
|
||||
// the main worktree
|
||||
dotGitPath := path.Join(worktreePath, ".git")
|
||||
gitFileInfo, err := fs.Stat(dotGitPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if gitFileInfo.IsDir() {
|
||||
return dotGitPath, nil
|
||||
}
|
||||
|
||||
return linkedWorktreeGitDirPath(fs, worktreePath)
|
||||
}
|
||||
|
||||
func linkedWorktreeGitDirPath(fs afero.Fs, worktreePath string) (string, error) {
|
||||
dotGitPath := path.Join(worktreePath, ".git")
|
||||
gitFileContents, err := afero.ReadFile(fs, dotGitPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// The file will have `gitdir: /path/to/.git/worktrees/<worktree-name>`
|
||||
gitDirLine := lo.Filter(strings.Split(string(gitFileContents), "\n"), func(line string, _ int) bool {
|
||||
return strings.HasPrefix(line, "gitdir: ")
|
||||
})
|
||||
|
||||
if len(gitDirLine) == 0 {
|
||||
return "", errors.New(fmt.Sprintf("%s is a file which suggests we are in a submodule or a worktree but the file's contents do not contain a gitdir pointing to the actual .git directory", dotGitPath))
|
||||
}
|
||||
|
||||
gitDir := strings.TrimPrefix(gitDirLine[0], "gitdir: ")
|
||||
|
||||
gitDir = filepath.Clean(gitDir)
|
||||
// For windows support
|
||||
gitDir = filepath.ToSlash(gitDir)
|
||||
|
||||
return gitDir, nil
|
||||
}
|
||||
|
||||
func getCurrentRepoGitDirPath(
|
||||
fs afero.Fs,
|
||||
resolveSymlinkFn func(string) (string, error),
|
||||
currentPath string,
|
||||
) (string, string, error) {
|
||||
var unresolvedGitPath string
|
||||
if env.GetGitDirEnv() != "" {
|
||||
unresolvedGitPath = env.GetGitDirEnv()
|
||||
} else {
|
||||
unresolvedGitPath = path.Join(currentPath, ".git")
|
||||
}
|
||||
|
||||
gitPath, err := resolveSymlinkFn(unresolvedGitPath)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// check if .git is a file or a directory
|
||||
gitFileInfo, err := fs.Stat(gitPath)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if gitFileInfo.IsDir() {
|
||||
// must be in the main worktree
|
||||
return gitPath, path.Dir(gitPath), nil
|
||||
}
|
||||
|
||||
// either in a submodule, or worktree
|
||||
worktreeGitPath, err := linkedWorktreeGitDirPath(fs, currentPath)
|
||||
if err != nil {
|
||||
return "", "", errors.Errorf("could not find git dir for %s: %v", currentPath, err)
|
||||
}
|
||||
|
||||
_, err = fs.Stat(worktreeGitPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// hardcoding error to get around windows-specific error message
|
||||
return "", "", errors.Errorf("could not find git dir for %s. %s does not exist", currentPath, worktreeGitPath)
|
||||
}
|
||||
return "", "", errors.Errorf("could not find git dir for %s: %v", currentPath, err)
|
||||
}
|
||||
|
||||
// confirm whether the next directory up is the worktrees directory
|
||||
parent := path.Dir(worktreeGitPath)
|
||||
if path.Base(parent) == "worktrees" {
|
||||
gitDirPath := path.Dir(parent)
|
||||
return gitDirPath, path.Dir(gitDirPath), nil
|
||||
}
|
||||
|
||||
// Unlike worktrees, submodules can be nested arbitrarily deep, so we check
|
||||
// if the `modules` directory is anywhere up the chain.
|
||||
if strings.Contains(worktreeGitPath, "/modules/") {
|
||||
// For submodules, we just return the path directly
|
||||
return worktreeGitPath, currentPath, nil
|
||||
}
|
||||
|
||||
// If this error causes issues, we could relax the constraint and just always
|
||||
// return the path
|
||||
return "", "", errors.Errorf("could not find git dir for %s: the path '%s' is not under `worktrees` or `modules` directories", currentPath, worktreeGitPath)
|
||||
}
|
||||
|
||||
// takes a path containing a symlink and returns the true path
|
||||
func resolveSymlink(path string) (string, error) {
|
||||
l, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if l.Mode()&os.ModeSymlink == 0 {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
return filepath.EvalSymlinks(path)
|
||||
}
|
||||
|
||||
// Returns the paths of linked worktrees
|
||||
func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
|
||||
result := []string{}
|
||||
// For each directory in this path we're going to cat the `gitdir` file and append its contents to our result
|
||||
// That file points us to the `.git` file in the worktree.
|
||||
worktreeGitDirsPath := path.Join(repoGitDirPath, "worktrees")
|
||||
|
||||
// ensure the directory exists
|
||||
_, err := fs.Stat(worktreeGitDirsPath)
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
_ = afero.Walk(fs, worktreeGitDirsPath, func(currPath string, info ioFs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
gitDirPath := path.Join(currPath, "gitdir")
|
||||
gitDirBytes, err := afero.ReadFile(fs, gitDirPath)
|
||||
if err != nil {
|
||||
// ignoring error
|
||||
return nil
|
||||
}
|
||||
trimmedGitDir := strings.TrimSpace(string(gitDirBytes))
|
||||
// removing the .git part
|
||||
worktreeDir := path.Dir(trimmedGitDir)
|
||||
result = append(result, worktreeDir)
|
||||
return nil
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
163
pkg/commands/git_commands/repo_paths_test.go
Normal file
163
pkg/commands/git_commands/repo_paths_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func mockResolveSymlinkFn(p string) (string, error) { return p, nil }
|
||||
|
||||
type Scenario struct {
|
||||
Name string
|
||||
BeforeFunc func(fs afero.Fs)
|
||||
Path string
|
||||
Expected *RepoPaths
|
||||
Err error
|
||||
}
|
||||
|
||||
func TestGetRepoPathsAux(t *testing.T) {
|
||||
scenarios := []Scenario{
|
||||
{
|
||||
Name: "typical case",
|
||||
BeforeFunc: func(fs afero.Fs) {
|
||||
// setup for main worktree
|
||||
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
|
||||
},
|
||||
Path: "/path/to/repo",
|
||||
Expected: &RepoPaths{
|
||||
currentPath: "/path/to/repo",
|
||||
worktreePath: "/path/to/repo",
|
||||
worktreeGitDirPath: "/path/to/repo/.git",
|
||||
repoPath: "/path/to/repo",
|
||||
repoGitDirPath: "/path/to/repo/.git",
|
||||
repoName: "repo",
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "linked worktree",
|
||||
BeforeFunc: func(fs afero.Fs) {
|
||||
// setup for linked worktree
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree1", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo/worktree1/.git", []byte("gitdir: /path/to/repo/.git/worktrees/worktree1"), 0o644)
|
||||
},
|
||||
Path: "/path/to/repo/worktree1",
|
||||
Expected: &RepoPaths{
|
||||
currentPath: "/path/to/repo/worktree1",
|
||||
worktreePath: "/path/to/repo/worktree1",
|
||||
worktreeGitDirPath: "/path/to/repo/.git/worktrees/worktree1",
|
||||
repoPath: "/path/to/repo",
|
||||
repoGitDirPath: "/path/to/repo/.git",
|
||||
repoName: "repo",
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "worktree with trailing separator in path",
|
||||
BeforeFunc: func(fs afero.Fs) {
|
||||
// setup for linked worktree
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree1", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo/worktree1/.git", []byte("gitdir: /path/to/repo/.git/worktrees/worktree1/"), 0o644)
|
||||
},
|
||||
Path: "/path/to/repo/worktree1",
|
||||
Expected: &RepoPaths{
|
||||
currentPath: "/path/to/repo/worktree1",
|
||||
worktreePath: "/path/to/repo/worktree1",
|
||||
worktreeGitDirPath: "/path/to/repo/.git/worktrees/worktree1",
|
||||
repoPath: "/path/to/repo",
|
||||
repoGitDirPath: "/path/to/repo/.git",
|
||||
repoName: "repo",
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "worktree .git file missing gitdir directive",
|
||||
BeforeFunc: func(fs afero.Fs) {
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree2", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo/worktree2/.git", []byte("blah"), 0o644)
|
||||
},
|
||||
Path: "/path/to/repo/worktree2",
|
||||
Expected: nil,
|
||||
Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/worktree2: /path/to/repo/worktree2/.git is a file which suggests we are in a submodule or a worktree but the file's contents do not contain a gitdir pointing to the actual .git directory"),
|
||||
},
|
||||
{
|
||||
Name: "worktree .git file gitdir directive points to a non-existing directory",
|
||||
BeforeFunc: func(fs afero.Fs) {
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree2", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo/worktree2/.git", []byte("gitdir: /nonexistant"), 0o644)
|
||||
},
|
||||
Path: "/path/to/repo/worktree2",
|
||||
Expected: nil,
|
||||
Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/worktree2. /nonexistant does not exist"),
|
||||
},
|
||||
{
|
||||
Name: "submodule",
|
||||
BeforeFunc: func(fs afero.Fs) {
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/modules/submodule1", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo/submodule1/.git", []byte("gitdir: /path/to/repo/.git/modules/submodule1"), 0o644)
|
||||
},
|
||||
Path: "/path/to/repo/submodule1",
|
||||
Expected: &RepoPaths{
|
||||
currentPath: "/path/to/repo/submodule1",
|
||||
worktreePath: "/path/to/repo/submodule1",
|
||||
worktreeGitDirPath: "/path/to/repo/.git/modules/submodule1",
|
||||
repoPath: "/path/to/repo/submodule1",
|
||||
repoGitDirPath: "/path/to/repo/.git/modules/submodule1",
|
||||
repoName: "submodule1",
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "submodule in nested directory",
|
||||
BeforeFunc: func(fs afero.Fs) {
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/modules/my/submodule1", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo/my/submodule1/.git", []byte("gitdir: /path/to/repo/.git/modules/my/submodule1"), 0o644)
|
||||
},
|
||||
Path: "/path/to/repo/my/submodule1",
|
||||
Expected: &RepoPaths{
|
||||
currentPath: "/path/to/repo/my/submodule1",
|
||||
worktreePath: "/path/to/repo/my/submodule1",
|
||||
worktreeGitDirPath: "/path/to/repo/.git/modules/my/submodule1",
|
||||
repoPath: "/path/to/repo/my/submodule1",
|
||||
repoGitDirPath: "/path/to/repo/.git/modules/my/submodule1",
|
||||
repoName: "submodule1",
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
Name: "submodule git dir not under .git/modules",
|
||||
BeforeFunc: func(fs afero.Fs) {
|
||||
_ = fs.MkdirAll("/random/submodule1", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo/my/submodule1/.git", []byte("gitdir: /random/submodule1"), 0o644)
|
||||
},
|
||||
Path: "/path/to/repo/my/submodule1",
|
||||
Expected: nil,
|
||||
Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/my/submodule1: the path '/random/submodule1' is not under `worktrees` or `modules` directories"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.Name, func(t *testing.T) {
|
||||
fs := afero.NewMemMapFs()
|
||||
|
||||
// prepare the filesystem for the scenario
|
||||
s.BeforeFunc(fs)
|
||||
|
||||
// run the function with the scenario path
|
||||
repoPaths, err := getRepoPathsAux(fs, mockResolveSymlinkFn, s.Path)
|
||||
|
||||
// check the error and the paths
|
||||
if s.Err != nil {
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, s.Err.Error())
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, s.Expected, repoPaths)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -80,13 +80,13 @@ func (self *StashCommands) Sha(index int) (string, error) {
|
||||
return strings.Trim(sha, "\r\n"), err
|
||||
}
|
||||
|
||||
func (self *StashCommands) ShowStashEntryCmdObj(index int, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||
func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("stash").Arg("show").
|
||||
Arg("-p").
|
||||
Arg("--stat").
|
||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
|
||||
Arg(fmt.Sprintf("--unified=%d", self.UserConfig.Git.DiffContextSize)).
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
Arg(fmt.Sprintf("--unified=%d", self.AppState.DiffContextSize)).
|
||||
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||
Arg(fmt.Sprintf("stash@{%d}", index)).
|
||||
ToArgv()
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type StashLoader struct {
|
||||
@@ -32,14 +32,14 @@ func (self *StashLoader) GetStashEntries(filterPath string) []*models.StashEntry
|
||||
return self.getUnfilteredStashEntries()
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "--name-only").ToArgv()
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--name-only", "--pretty=%ct|%gs").ToArgv()
|
||||
rawString, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return self.getUnfilteredStashEntries()
|
||||
}
|
||||
stashEntries := []*models.StashEntry{}
|
||||
var currentStashEntry *models.StashEntry
|
||||
lines := utils.SplitLines(rawString)
|
||||
lines := utils.SplitNul(rawString)
|
||||
isAStash := func(line string) bool { return strings.HasPrefix(line, "stash@{") }
|
||||
re := regexp.MustCompile(`stash@\{(\d+)\}`)
|
||||
|
||||
@@ -66,17 +66,32 @@ outer:
|
||||
}
|
||||
|
||||
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%gs").ToArgv()
|
||||
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%ct|%gs").ToArgv()
|
||||
|
||||
rawString, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
return slices.MapWithIndex(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
||||
return lo.Map(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
||||
return self.stashEntryFromLine(line, index)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *StashLoader) stashEntryFromLine(line string, index int) *models.StashEntry {
|
||||
return &models.StashEntry{
|
||||
model := &models.StashEntry{
|
||||
Name: line,
|
||||
Index: index,
|
||||
}
|
||||
|
||||
tstr, msg, ok := strings.Cut(line, "|")
|
||||
if !ok {
|
||||
return model
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(tstr, 10, 64)
|
||||
if err != nil {
|
||||
return model
|
||||
}
|
||||
|
||||
model.Name = msg
|
||||
model.Recency = utils.UnixToTimeAgo(t)
|
||||
|
||||
return model
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ func TestGetStashEntries(t *testing.T) {
|
||||
"No stash entries found",
|
||||
"",
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"}, "", nil),
|
||||
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%ct|%gs"}, "", nil),
|
||||
[]*models.StashEntry{},
|
||||
},
|
||||
{
|
||||
"Several stash entries found",
|
||||
"",
|
||||
oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"},
|
||||
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%ct|%gs"},
|
||||
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
|
||||
nil,
|
||||
),
|
||||
|
||||
@@ -134,10 +134,12 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
userConfig := config.GetDefaultConfig()
|
||||
userConfig.Git.DiffContextSize = s.contextSize
|
||||
instance := buildStashCommands(commonDeps{userConfig: userConfig})
|
||||
appState := &config.AppState{}
|
||||
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
appState.DiffContextSize = s.contextSize
|
||||
instance := buildStashCommands(commonDeps{userConfig: userConfig, appState: appState})
|
||||
|
||||
cmdStr := instance.ShowStashEntryCmdObj(s.index, s.ignoreWhitespace).Args()
|
||||
cmdStr := instance.ShowStashEntryCmdObj(s.index).Args()
|
||||
assert.Equal(t, s.expected, cmdStr)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -24,19 +25,16 @@ func NewStatusCommands(
|
||||
// RebaseMode returns "" for non-rebase mode, "normal" for normal rebase
|
||||
// and "interactive" for interactive rebase
|
||||
func (self *StatusCommands) RebaseMode() (enums.RebaseMode, error) {
|
||||
exists, err := self.os.FileExists(filepath.Join(self.dotGitDir, "rebase-apply"))
|
||||
if err != nil {
|
||||
return enums.REBASE_MODE_NONE, err
|
||||
}
|
||||
if exists {
|
||||
ok, err := self.IsInNormalRebase()
|
||||
if err == nil && ok {
|
||||
return enums.REBASE_MODE_NORMAL, nil
|
||||
}
|
||||
exists, err = self.os.FileExists(filepath.Join(self.dotGitDir, "rebase-merge"))
|
||||
if exists {
|
||||
ok, err = self.IsInInteractiveRebase()
|
||||
if err == nil && ok {
|
||||
return enums.REBASE_MODE_INTERACTIVE, err
|
||||
} else {
|
||||
return enums.REBASE_MODE_NONE, err
|
||||
}
|
||||
|
||||
return enums.REBASE_MODE_NONE, err
|
||||
}
|
||||
|
||||
func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
|
||||
@@ -67,7 +65,26 @@ func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
|
||||
return strconv.ParseBool(strings.TrimSpace(res))
|
||||
}
|
||||
|
||||
func (self *StatusCommands) IsInNormalRebase() (bool, error) {
|
||||
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"))
|
||||
}
|
||||
|
||||
func (self *StatusCommands) IsInInteractiveRebase() (bool, error) {
|
||||
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge"))
|
||||
}
|
||||
|
||||
// IsInMergeState states whether we are still mid-merge
|
||||
func (self *StatusCommands) IsInMergeState() (bool, error) {
|
||||
return self.os.FileExists(filepath.Join(self.dotGitDir, "MERGE_HEAD"))
|
||||
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD"))
|
||||
}
|
||||
|
||||
// Full ref (e.g. "refs/heads/mybranch") of the branch that is currently
|
||||
// being rebased, or empty string when we're not in a rebase
|
||||
func (self *StatusCommands) BranchBeingRebased() string {
|
||||
for _, dir := range []string{"rebase-merge", "rebase-apply"} {
|
||||
if bytesContent, err := os.ReadFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), dir, "head-name")); err == nil {
|
||||
return strings.TrimSpace(string(bytesContent))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("stash").
|
||||
RepoPath(submodule.Path).
|
||||
Dir(submodule.Path).
|
||||
Arg("--include-untracked").
|
||||
ToArgv()
|
||||
|
||||
@@ -139,7 +139,9 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
||||
self.Log.Error(err)
|
||||
}
|
||||
|
||||
return os.RemoveAll(filepath.Join(self.dotGitDir, "modules", submodule.Path))
|
||||
// We may in fact want to use the repo's git dir path but git docs say not to
|
||||
// mix submodules and worktrees anyway.
|
||||
return os.RemoveAll(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "modules", submodule.Path))
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
|
||||
|
||||
@@ -36,7 +36,7 @@ func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (oscommands
|
||||
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
|
||||
ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex)
|
||||
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task)
|
||||
return cmdObj, nil
|
||||
}
|
||||
|
||||
@@ -49,10 +49,16 @@ func (self *SyncCommands) Push(task gocui.Task, opts PushOpts) error {
|
||||
return cmdObj.Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) fetchCommandBuilder(fetchAll bool) *GitCommandBuilder {
|
||||
return NewGitCmd("fetch").
|
||||
ArgIf(fetchAll, "--all").
|
||||
// avoid writing to .git/FETCH_HEAD; this allows running a pull
|
||||
// concurrently without getting errors
|
||||
ArgIf(self.version.IsAtLeast(2, 29, 0), "--no-write-fetch-head")
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchCmdObj(task gocui.Task) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("fetch").
|
||||
ArgIf(self.UserConfig.Git.FetchAll, "--all").
|
||||
ToArgv()
|
||||
cmdArgs := self.fetchCommandBuilder(self.UserConfig.Git.FetchAll).ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs)
|
||||
cmdObj.PromptOnCredentialRequest(task)
|
||||
@@ -64,13 +70,10 @@ func (self *SyncCommands) Fetch(task gocui.Task) error {
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchBackgroundCmdObj() oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("fetch").
|
||||
ArgIf(self.UserConfig.Git.FetchAll, "--all").
|
||||
ToArgv()
|
||||
cmdArgs := self.fetchCommandBuilder(self.UserConfig.Git.FetchAll).ToArgv()
|
||||
|
||||
cmdObj := self.cmd.New(cmdArgs)
|
||||
cmdObj.DontLog().FailOnCredentialRequest()
|
||||
cmdObj.WithMutex(self.syncMutex)
|
||||
return cmdObj
|
||||
}
|
||||
|
||||
@@ -82,6 +85,7 @@ type PullOptions struct {
|
||||
RemoteName string
|
||||
BranchName string
|
||||
FastForwardOnly bool
|
||||
WorktreeGitDir string
|
||||
}
|
||||
|
||||
func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
|
||||
@@ -90,26 +94,32 @@ func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
|
||||
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||
ArgIf(opts.RemoteName != "", opts.RemoteName).
|
||||
ArgIf(opts.BranchName != "", opts.BranchName).
|
||||
GitDirIf(opts.WorktreeGitDir != "", opts.WorktreeGitDir).
|
||||
ToArgv()
|
||||
|
||||
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
|
||||
// has 'pull.rebase = interactive' configured.
|
||||
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FastForward(task gocui.Task, branchName string, remoteName string, remoteBranchName string) error {
|
||||
cmdArgs := NewGitCmd("fetch").
|
||||
func (self *SyncCommands) FastForward(
|
||||
task gocui.Task,
|
||||
branchName string,
|
||||
remoteName string,
|
||||
remoteBranchName string,
|
||||
) error {
|
||||
cmdArgs := self.fetchCommandBuilder(false).
|
||||
Arg(remoteName).
|
||||
Arg(remoteBranchName + ":" + branchName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchRemote(task gocui.Task, remoteName string) error {
|
||||
cmdArgs := NewGitCmd("fetch").
|
||||
cmdArgs := self.fetchCommandBuilder(false).
|
||||
Arg(remoteName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
@@ -12,16 +12,19 @@ func NewTagCommands(gitCommon *GitCommon) *TagCommands {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TagCommands) CreateLightweight(tagName string, ref string) error {
|
||||
cmdArgs := NewGitCmd("tag").Arg("--", tagName).
|
||||
func (self *TagCommands) CreateLightweight(tagName string, ref string, force bool) error {
|
||||
cmdArgs := NewGitCmd("tag").
|
||||
ArgIf(force, "--force").
|
||||
Arg("--", tagName).
|
||||
ArgIf(len(ref) > 0, ref).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string) error {
|
||||
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string, force bool) error {
|
||||
cmdArgs := NewGitCmd("tag").Arg(tagName).
|
||||
ArgIf(force, "--force").
|
||||
ArgIf(len(ref) > 0, ref).
|
||||
Arg("-m", msg).
|
||||
ToArgv()
|
||||
@@ -29,7 +32,16 @@ func (self *TagCommands) CreateAnnotated(tagName, ref, msg string) error {
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *TagCommands) Delete(tagName string) error {
|
||||
func (self *TagCommands) HasTag(tagName string) bool {
|
||||
cmdArgs := NewGitCmd("show-ref").
|
||||
Arg("--tags", "--quiet", "--verify", "--").
|
||||
Arg("refs/tags/" + tagName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run() == nil
|
||||
}
|
||||
|
||||
func (self *TagCommands) LocalDelete(tagName string) error {
|
||||
cmdArgs := NewGitCmd("tag").Arg("-d", tagName).
|
||||
ToArgv()
|
||||
|
||||
@@ -40,5 +52,5 @@ func (self *TagCommands) Push(task gocui.Task, remoteName string, tagName string
|
||||
cmdArgs := NewGitCmd("push").Arg(remoteName, "tag", tagName).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
|
||||
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package git_commands
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type TagLoader struct {
|
||||
@@ -38,7 +38,7 @@ func (self *TagLoader) GetTags() ([]*models.Tag, error) {
|
||||
|
||||
lineRegex := regexp.MustCompile(`^([^\s]+)(\s+)?(.*)$`)
|
||||
|
||||
tags := slices.Map(split, func(line string) *models.Tag {
|
||||
tags := lo.Map(split, func(line string, _ int) *models.Tag {
|
||||
matches := lineRegex.FindStringSubmatch(line)
|
||||
tagName := matches[1]
|
||||
message := ""
|
||||
|
||||
@@ -69,3 +69,11 @@ func (v *GitVersion) IsOlderThan(major, minor, patch int) bool {
|
||||
func (v *GitVersion) IsOlderThanVersion(version *GitVersion) bool {
|
||||
return v.IsOlderThan(version.Major, version.Minor, version.Patch)
|
||||
}
|
||||
|
||||
func (v *GitVersion) IsAtLeast(major, minor, patch int) bool {
|
||||
return !v.IsOlderThan(major, minor, patch)
|
||||
}
|
||||
|
||||
func (v *GitVersion) IsAtLeastVersion(version *GitVersion) bool {
|
||||
return v.IsAtLeast(version.Major, version.Minor, version.Patch)
|
||||
}
|
||||
|
||||
@@ -45,3 +45,12 @@ func TestGitVersionIsOlderThan(t *testing.T) {
|
||||
assert.True(t, (&GitVersion{2, 0, 1, ""}).IsOlderThan(2, 1, 0))
|
||||
assert.True(t, (&GitVersion{2, 0, 1, ""}).IsOlderThan(3, 0, 0))
|
||||
}
|
||||
|
||||
func TestGitVersionIsAtLeast(t *testing.T) {
|
||||
assert.True(t, (&GitVersion{2, 0, 0, ""}).IsAtLeast(1, 99, 99))
|
||||
assert.True(t, (&GitVersion{2, 0, 0, ""}).IsAtLeast(2, 0, 0))
|
||||
assert.True(t, (&GitVersion{2, 1, 0, ""}).IsAtLeast(2, 0, 9))
|
||||
|
||||
assert.False(t, (&GitVersion{2, 0, 1, ""}).IsAtLeast(2, 1, 0))
|
||||
assert.False(t, (&GitVersion{2, 0, 1, ""}).IsAtLeast(3, 0, 0))
|
||||
}
|
||||
|
||||
@@ -31,10 +31,6 @@ func (self *WorkingTreeCommands) OpenMergeToolCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New(NewGitCmd("mergetool").ToArgv())
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) OpenMergeTool() error {
|
||||
return self.OpenMergeToolCmdObj().Run()
|
||||
}
|
||||
|
||||
// StageFile stages a file
|
||||
func (self *WorkingTreeCommands) StageFile(path string) error {
|
||||
return self.StageFiles([]string{path})
|
||||
@@ -228,28 +224,31 @@ func (self *WorkingTreeCommands) Exclude(filename string) error {
|
||||
}
|
||||
|
||||
// WorktreeFileDiff returns the diff of a file
|
||||
func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool, cached bool, ignoreWhitespace bool) string {
|
||||
func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool, cached bool) string {
|
||||
// for now we assume an error means the file was deleted
|
||||
s, _ := self.WorktreeFileDiffCmdObj(file, plain, cached, ignoreWhitespace).RunWithOutput()
|
||||
s, _ := self.WorktreeFileDiffCmdObj(file, plain, cached).RunWithOutput()
|
||||
return s
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool) oscommands.ICmdObj {
|
||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
|
||||
contextSize := self.UserConfig.Git.DiffContextSize
|
||||
contextSize := self.AppState.DiffContextSize
|
||||
prevPath := node.GetPreviousPath()
|
||||
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
|
||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
||||
useExtDiff := extDiffCmd != "" && !plain
|
||||
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
||||
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
Arg("--no-ext-diff").
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
Arg(fmt.Sprintf("--color=%s", colorArg)).
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
ArgIf(!plain && self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||
ArgIf(cached, "--cached").
|
||||
ArgIf(noIndex, "--no-index").
|
||||
Arg("--").
|
||||
@@ -263,32 +262,32 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
|
||||
|
||||
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
|
||||
// but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode.
|
||||
func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool,
|
||||
ignoreWhitespace bool,
|
||||
) (string, error) {
|
||||
return self.ShowFileDiffCmdObj(from, to, reverse, fileName, plain, ignoreWhitespace).RunWithOutput()
|
||||
func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool) (string, error) {
|
||||
return self.ShowFileDiffCmdObj(from, to, reverse, fileName, plain).RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool,
|
||||
ignoreWhitespace bool,
|
||||
) oscommands.ICmdObj {
|
||||
contextSize := self.UserConfig.Git.DiffContextSize
|
||||
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) oscommands.ICmdObj {
|
||||
contextSize := self.AppState.DiffContextSize
|
||||
|
||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
|
||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
||||
useExtDiff := extDiffCmd != "" && !plain
|
||||
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
||||
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
Arg("--no-ext-diff").
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
Arg("--no-renames").
|
||||
Arg(fmt.Sprintf("--color=%s", colorArg)).
|
||||
Arg(from).
|
||||
Arg(to).
|
||||
ArgIf(reverse, "-R").
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
ArgIf(!plain && self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||
Arg("--").
|
||||
Arg(fileName).
|
||||
ToArgv()
|
||||
|
||||
@@ -231,7 +231,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "cached",
|
||||
@@ -245,7 +245,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "plain",
|
||||
@@ -259,7 +259,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "File not tracked and file has no staged changes",
|
||||
@@ -273,7 +273,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Default case (ignore whitespace)",
|
||||
@@ -287,7 +287,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
ignoreWhitespace: true,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
@@ -301,7 +301,7 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 17,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -309,10 +309,12 @@ func TestWorkingTreeDiff(t *testing.T) {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
userConfig := config.GetDefaultConfig()
|
||||
userConfig.Git.DiffContextSize = s.contextSize
|
||||
appState := &config.AppState{}
|
||||
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
appState.DiffContextSize = s.contextSize
|
||||
|
||||
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig})
|
||||
result := instance.WorktreeFileDiff(s.file, s.plain, s.cached, s.ignoreWhitespace)
|
||||
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig, appState: appState})
|
||||
result := instance.WorktreeFileDiff(s.file, s.plain, s.cached)
|
||||
assert.Equal(t, expectedResult, result)
|
||||
s.runner.CheckForMissingCalls()
|
||||
})
|
||||
@@ -343,7 +345,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
@@ -354,7 +356,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 123,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Default case (ignore whitespace)",
|
||||
@@ -365,7 +367,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
||||
ignoreWhitespace: true,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -373,11 +375,13 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
userConfig := config.GetDefaultConfig()
|
||||
userConfig.Git.DiffContextSize = s.contextSize
|
||||
appState := &config.AppState{}
|
||||
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
|
||||
appState.DiffContextSize = s.contextSize
|
||||
|
||||
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig})
|
||||
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig, appState: appState})
|
||||
|
||||
result, err := instance.ShowFileDiff(s.from, s.to, s.reverse, "test.txt", s.plain, s.ignoreWhitespace)
|
||||
result, err := instance.ShowFileDiff(s.from, s.to, s.reverse, "test.txt", s.plain)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedResult, result)
|
||||
s.runner.CheckForMissingCalls()
|
||||
|
||||
74
pkg/commands/git_commands/worktree.go
Normal file
74
pkg/commands/git_commands/worktree.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
)
|
||||
|
||||
type WorktreeCommands struct {
|
||||
*GitCommon
|
||||
}
|
||||
|
||||
func NewWorktreeCommands(gitCommon *GitCommon) *WorktreeCommands {
|
||||
return &WorktreeCommands{
|
||||
GitCommon: gitCommon,
|
||||
}
|
||||
}
|
||||
|
||||
type NewWorktreeOpts struct {
|
||||
// required. The path of the new worktree.
|
||||
Path string
|
||||
// required. The base branch/ref.
|
||||
Base string
|
||||
|
||||
// if true, ends up with a detached head
|
||||
Detach bool
|
||||
|
||||
// optional. if empty, and if detach is false, we will checkout the base
|
||||
Branch string
|
||||
}
|
||||
|
||||
func (self *WorktreeCommands) New(opts NewWorktreeOpts) error {
|
||||
if opts.Detach && opts.Branch != "" {
|
||||
panic("cannot specify branch when detaching")
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("worktree").Arg("add").
|
||||
ArgIf(opts.Detach, "--detach").
|
||||
ArgIf(opts.Branch != "", "-b", opts.Branch).
|
||||
Arg(opts.Path, opts.Base)
|
||||
|
||||
return self.cmd.New(cmdArgs.ToArgv()).Run()
|
||||
}
|
||||
|
||||
func (self *WorktreeCommands) Delete(worktreePath string, force bool) error {
|
||||
cmdArgs := NewGitCmd("worktree").Arg("remove").ArgIf(force, "-f").Arg(worktreePath).ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *WorktreeCommands) Detach(worktreePath string) error {
|
||||
cmdArgs := NewGitCmd("checkout").Arg("--detach").GitDir(filepath.Join(worktreePath, ".git")).ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func WorktreeForBranch(branch *models.Branch, worktrees []*models.Worktree) (*models.Worktree, bool) {
|
||||
for _, worktree := range worktrees {
|
||||
if worktree.Branch == branch.Name {
|
||||
return worktree, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func CheckedOutByOtherWorktree(branch *models.Branch, worktrees []*models.Worktree) bool {
|
||||
worktree, ok := WorktreeForBranch(branch, worktrees)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return !worktree.IsCurrent
|
||||
}
|
||||
256
pkg/commands/git_commands/worktree_loader.go
Normal file
256
pkg/commands/git_commands/worktree_loader.go
Normal file
@@ -0,0 +1,256 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
iofs "io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
type WorktreeLoader struct {
|
||||
*GitCommon
|
||||
}
|
||||
|
||||
func NewWorktreeLoader(gitCommon *GitCommon) *WorktreeLoader {
|
||||
return &WorktreeLoader{GitCommon: gitCommon}
|
||||
}
|
||||
|
||||
func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
||||
currentRepoPath := self.repoPaths.RepoPath()
|
||||
worktreePath := self.repoPaths.WorktreePath()
|
||||
|
||||
cmdArgs := NewGitCmd("worktree").Arg("list", "--porcelain").ToArgv()
|
||||
worktreesOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
splitLines := strings.Split(
|
||||
utils.NormalizeLinefeeds(worktreesOutput), "\n",
|
||||
)
|
||||
|
||||
var worktrees []*models.Worktree
|
||||
var current *models.Worktree
|
||||
for _, splitLine := range splitLines {
|
||||
// worktrees are defined over multiple lines and are separated by blank lines
|
||||
// so if we reach a blank line we're done with the current worktree
|
||||
if len(splitLine) == 0 && current != nil {
|
||||
worktrees = append(worktrees, current)
|
||||
current = nil
|
||||
continue
|
||||
}
|
||||
|
||||
// ignore bare repo (not sure why it's even appearing in this list: it's not a worktree)
|
||||
if splitLine == "bare" {
|
||||
current = nil
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(splitLine, "worktree ") {
|
||||
path := strings.SplitN(splitLine, " ", 2)[1]
|
||||
isMain := path == currentRepoPath
|
||||
isCurrent := path == worktreePath
|
||||
isPathMissing := self.pathExists(path)
|
||||
|
||||
var gitDir string
|
||||
gitDir, err := getWorktreeGitDirPath(self.Fs, path)
|
||||
if err != nil {
|
||||
self.Log.Warnf("Could not find git dir for worktree %s: %v", path, err)
|
||||
}
|
||||
|
||||
current = &models.Worktree{
|
||||
IsMain: isMain,
|
||||
IsCurrent: isCurrent,
|
||||
IsPathMissing: isPathMissing,
|
||||
Path: path,
|
||||
GitDir: gitDir,
|
||||
}
|
||||
} else if strings.HasPrefix(splitLine, "branch ") {
|
||||
branch := strings.SplitN(splitLine, " ", 2)[1]
|
||||
current.Branch = strings.TrimPrefix(branch, "refs/heads/")
|
||||
}
|
||||
}
|
||||
|
||||
names := getUniqueNamesFromPaths(lo.Map(worktrees, func(worktree *models.Worktree, _ int) string {
|
||||
return worktree.Path
|
||||
}))
|
||||
|
||||
for index, worktree := range worktrees {
|
||||
worktree.Name = names[index]
|
||||
}
|
||||
|
||||
// move current worktree to the top
|
||||
for i, worktree := range worktrees {
|
||||
if worktree.IsCurrent {
|
||||
worktrees = append(worktrees[:i], worktrees[i+1:]...)
|
||||
worktrees = append([]*models.Worktree{worktree}, worktrees...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Some worktrees are on a branch but are mid-rebase, and in those cases,
|
||||
// `git worktree list` will not show the branch name. We can get the branch
|
||||
// name from the `rebase-merge/head-name` file (if it exists) in the folder
|
||||
// for the worktree in the parent repo's .git/worktrees folder.
|
||||
for _, worktree := range worktrees {
|
||||
// No point checking if we already have a branch name
|
||||
if worktree.Branch != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// If we couldn't find the git directory, we can't find the branch name
|
||||
if worktree.GitDir == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
rebasedBranch, ok := self.rebasedBranch(worktree)
|
||||
if ok {
|
||||
worktree.Branch = rebasedBranch
|
||||
continue
|
||||
}
|
||||
|
||||
bisectedBranch, ok := self.bisectedBranch(worktree)
|
||||
if ok {
|
||||
worktree.Branch = bisectedBranch
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return worktrees, nil
|
||||
}
|
||||
|
||||
func (self *WorktreeLoader) pathExists(path string) bool {
|
||||
if _, err := self.Fs.Stat(path); err != nil {
|
||||
if errors.Is(err, iofs.ErrNotExist) {
|
||||
return true
|
||||
}
|
||||
self.Log.Errorf("failed to check if worktree path `%s` exists\n%v", path, err)
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *WorktreeLoader) rebasedBranch(worktree *models.Worktree) (string, bool) {
|
||||
for _, dir := range []string{"rebase-merge", "rebase-apply"} {
|
||||
if bytesContent, err := afero.ReadFile(self.Fs, filepath.Join(worktree.GitDir, dir, "head-name")); err == nil {
|
||||
headName := strings.TrimSpace(string(bytesContent))
|
||||
shortHeadName := strings.TrimPrefix(headName, "refs/heads/")
|
||||
return shortHeadName, true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (self *WorktreeLoader) bisectedBranch(worktree *models.Worktree) (string, bool) {
|
||||
bisectStartPath := filepath.Join(worktree.GitDir, "BISECT_START")
|
||||
startContent, err := afero.ReadFile(self.Fs, bisectStartPath)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(startContent)), true
|
||||
}
|
||||
|
||||
type pathWithIndexT struct {
|
||||
path string
|
||||
index int
|
||||
}
|
||||
|
||||
type nameWithIndexT struct {
|
||||
name string
|
||||
index int
|
||||
}
|
||||
|
||||
func getUniqueNamesFromPaths(paths []string) []string {
|
||||
pathsWithIndex := lo.Map(paths, func(path string, index int) pathWithIndexT {
|
||||
return pathWithIndexT{path, index}
|
||||
})
|
||||
|
||||
namesWithIndex := getUniqueNamesFromPathsAux(pathsWithIndex, 0)
|
||||
|
||||
// now sort based on index
|
||||
result := make([]string, len(namesWithIndex))
|
||||
for _, nameWithIndex := range namesWithIndex {
|
||||
result[nameWithIndex.index] = nameWithIndex.name
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func getUniqueNamesFromPathsAux(paths []pathWithIndexT, depth int) []nameWithIndexT {
|
||||
// If we have no paths, return an empty array
|
||||
if len(paths) == 0 {
|
||||
return []nameWithIndexT{}
|
||||
}
|
||||
|
||||
// If we have only one path, return the last segment of the path
|
||||
if len(paths) == 1 {
|
||||
path := paths[0]
|
||||
return []nameWithIndexT{{index: path.index, name: sliceAtDepth(path.path, depth)}}
|
||||
}
|
||||
|
||||
// group the paths by their value at the specified depth
|
||||
groups := make(map[string][]pathWithIndexT)
|
||||
for _, path := range paths {
|
||||
value := valueAtDepth(path.path, depth)
|
||||
groups[value] = append(groups[value], path)
|
||||
}
|
||||
|
||||
result := []nameWithIndexT{}
|
||||
for _, group := range groups {
|
||||
if len(group) == 1 {
|
||||
path := group[0]
|
||||
result = append(result, nameWithIndexT{index: path.index, name: sliceAtDepth(path.path, depth)})
|
||||
} else {
|
||||
result = append(result, getUniqueNamesFromPathsAux(group, depth+1)...)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// if the path is /a/b/c/d, and the depth is 0, the value is 'd'. If the depth is 1, the value is 'c', etc
|
||||
func valueAtDepth(path string, depth int) string {
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
|
||||
// Split the path into segments
|
||||
segments := strings.Split(path, "/")
|
||||
|
||||
// Get the length of segments
|
||||
length := len(segments)
|
||||
|
||||
// If the depth is greater than the length of segments, return an empty string
|
||||
if depth >= length {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Return the segment at the specified depth from the end of the path
|
||||
return segments[length-1-depth]
|
||||
}
|
||||
|
||||
// if the path is /a/b/c/d, and the depth is 0, the value is 'd'. If the depth is 1, the value is 'b/c', etc
|
||||
func sliceAtDepth(path string, depth int) string {
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
|
||||
// Split the path into segments
|
||||
segments := strings.Split(path, "/")
|
||||
|
||||
// Get the length of segments
|
||||
length := len(segments)
|
||||
|
||||
// If the depth is greater than or equal to the length of segments, return an empty string
|
||||
if depth >= length {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Join the segments from the specified depth till end of the path
|
||||
return strings.Join(segments[length-1-depth:], "/")
|
||||
}
|
||||
238
pkg/commands/git_commands/worktree_loader_test.go
Normal file
238
pkg/commands/git_commands/worktree_loader_test.go
Normal file
@@ -0,0 +1,238 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetWorktrees(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
repoPaths *RepoPaths
|
||||
before func(runner *oscommands.FakeCmdObjRunner, fs afero.Fs)
|
||||
expectedWorktrees []*models.Worktree
|
||||
expectedErr string
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "Single worktree (main)",
|
||||
repoPaths: &RepoPaths{
|
||||
repoPath: "/path/to/repo",
|
||||
worktreePath: "/path/to/repo",
|
||||
},
|
||||
before: func(runner *oscommands.FakeCmdObjRunner, fs afero.Fs) {
|
||||
runner.ExpectGitArgs([]string{"worktree", "list", "--porcelain"},
|
||||
`worktree /path/to/repo
|
||||
HEAD d85cc9d281fa6ae1665c68365fc70e75e82a042d
|
||||
branch refs/heads/mybranch
|
||||
`,
|
||||
nil)
|
||||
|
||||
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
|
||||
},
|
||||
expectedWorktrees: []*models.Worktree{
|
||||
{
|
||||
IsMain: true,
|
||||
IsCurrent: true,
|
||||
Path: "/path/to/repo",
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git",
|
||||
Branch: "mybranch",
|
||||
Name: "repo",
|
||||
},
|
||||
},
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
testName: "Multiple worktrees (main + linked)",
|
||||
repoPaths: &RepoPaths{
|
||||
repoPath: "/path/to/repo",
|
||||
worktreePath: "/path/to/repo",
|
||||
},
|
||||
before: func(runner *oscommands.FakeCmdObjRunner, fs afero.Fs) {
|
||||
runner.ExpectGitArgs([]string{"worktree", "list", "--porcelain"},
|
||||
`worktree /path/to/repo
|
||||
HEAD d85cc9d281fa6ae1665c68365fc70e75e82a042d
|
||||
branch refs/heads/mybranch
|
||||
|
||||
worktree /path/to/repo-worktree
|
||||
HEAD 775955775e79b8f5b4c4b56f82fbf657e2d5e4de
|
||||
branch refs/heads/mybranch-worktree
|
||||
`,
|
||||
nil)
|
||||
|
||||
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
|
||||
_ = fs.MkdirAll("/path/to/repo-worktree", 0o755)
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/repo-worktree", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo-worktree/.git", []byte("gitdir: /path/to/repo/.git/worktrees/repo-worktree"), 0o755)
|
||||
},
|
||||
expectedWorktrees: []*models.Worktree{
|
||||
{
|
||||
IsMain: true,
|
||||
IsCurrent: true,
|
||||
Path: "/path/to/repo",
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git",
|
||||
Branch: "mybranch",
|
||||
Name: "repo",
|
||||
},
|
||||
{
|
||||
IsMain: false,
|
||||
IsCurrent: false,
|
||||
Path: "/path/to/repo-worktree",
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git/worktrees/repo-worktree",
|
||||
Branch: "mybranch-worktree",
|
||||
Name: "repo-worktree",
|
||||
},
|
||||
},
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
testName: "Worktree missing path",
|
||||
repoPaths: &RepoPaths{
|
||||
repoPath: "/path/to/repo",
|
||||
worktreePath: "/path/to/repo",
|
||||
},
|
||||
before: func(runner *oscommands.FakeCmdObjRunner, fs afero.Fs) {
|
||||
runner.ExpectGitArgs([]string{"worktree", "list", "--porcelain"},
|
||||
`worktree /path/to/worktree
|
||||
HEAD 775955775e79b8f5b4c4b56f82fbf657e2d5e4de
|
||||
branch refs/heads/missingbranch
|
||||
`,
|
||||
nil)
|
||||
|
||||
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
|
||||
},
|
||||
expectedWorktrees: []*models.Worktree{
|
||||
{
|
||||
IsMain: false,
|
||||
IsCurrent: false,
|
||||
Path: "/path/to/worktree",
|
||||
IsPathMissing: true,
|
||||
GitDir: "",
|
||||
Branch: "missingbranch",
|
||||
Name: "worktree",
|
||||
},
|
||||
},
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
testName: "In linked worktree",
|
||||
repoPaths: &RepoPaths{
|
||||
repoPath: "/path/to/repo",
|
||||
worktreePath: "/path/to/repo-worktree",
|
||||
},
|
||||
before: func(runner *oscommands.FakeCmdObjRunner, fs afero.Fs) {
|
||||
runner.ExpectGitArgs([]string{"worktree", "list", "--porcelain"},
|
||||
`worktree /path/to/repo
|
||||
HEAD d85cc9d281fa6ae1665c68365fc70e75e82a042d
|
||||
branch refs/heads/mybranch
|
||||
|
||||
worktree /path/to/repo-worktree
|
||||
HEAD 775955775e79b8f5b4c4b56f82fbf657e2d5e4de
|
||||
branch refs/heads/mybranch-worktree
|
||||
`,
|
||||
nil)
|
||||
|
||||
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
|
||||
_ = fs.MkdirAll("/path/to/repo-worktree", 0o755)
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/repo-worktree", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/repo-worktree/.git", []byte("gitdir: /path/to/repo/.git/worktrees/repo-worktree"), 0o755)
|
||||
},
|
||||
expectedWorktrees: []*models.Worktree{
|
||||
{
|
||||
IsMain: false,
|
||||
IsCurrent: true,
|
||||
Path: "/path/to/repo-worktree",
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git/worktrees/repo-worktree",
|
||||
Branch: "mybranch-worktree",
|
||||
Name: "repo-worktree",
|
||||
},
|
||||
{
|
||||
IsMain: true,
|
||||
IsCurrent: false,
|
||||
Path: "/path/to/repo",
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git",
|
||||
Branch: "mybranch",
|
||||
Name: "repo",
|
||||
},
|
||||
},
|
||||
expectedErr: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
runner := oscommands.NewFakeRunner(t)
|
||||
fs := afero.NewMemMapFs()
|
||||
s.before(runner, fs)
|
||||
|
||||
loader := &WorktreeLoader{
|
||||
GitCommon: buildGitCommon(commonDeps{runner: runner, fs: fs, repoPaths: s.repoPaths}),
|
||||
}
|
||||
|
||||
worktrees, err := loader.GetWorktrees()
|
||||
if s.expectedErr != "" {
|
||||
assert.EqualError(t, errors.New(s.expectedErr), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, s.expectedWorktrees, worktrees)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUniqueNamesFromPaths(t *testing.T) {
|
||||
for _, scenario := range []struct {
|
||||
input []string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
input: []string{},
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
input: []string{
|
||||
"/my/path/feature/one",
|
||||
},
|
||||
expected: []string{
|
||||
"one",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: []string{
|
||||
"/my/path/feature/one/",
|
||||
},
|
||||
expected: []string{
|
||||
"one",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: []string{
|
||||
"/a/b/c/d",
|
||||
"/a/b/c/e",
|
||||
"/a/b/f/d",
|
||||
"/a/e/c/d",
|
||||
},
|
||||
expected: []string{
|
||||
"b/c/d",
|
||||
"e",
|
||||
"f/d",
|
||||
"e/c/d",
|
||||
},
|
||||
},
|
||||
} {
|
||||
actual := getUniqueNamesFromPaths(scenario.input)
|
||||
assert.EqualValues(t, scenario.expected, actual)
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
)
|
||||
|
||||
// including license from https://github.com/tcnksm/go-gitconfig because this file is an adaptation of that repo's code
|
||||
@@ -55,10 +53,10 @@ func runGitConfigCmd(cmd *exec.Cmd) (string, error) {
|
||||
|
||||
func getGitConfigCmd(key string) *exec.Cmd {
|
||||
gitArgs := []string{"config", "--get", "--null", key}
|
||||
return secureexec.Command("git", gitArgs...)
|
||||
return exec.Command("git", gitArgs...)
|
||||
}
|
||||
|
||||
func getGitConfigGeneralCmd(args string) *exec.Cmd {
|
||||
gitArgs := append([]string{"config"}, strings.Split(args, " ")...)
|
||||
return secureexec.Command("git", gitArgs...)
|
||||
return exec.Command("git", gitArgs...)
|
||||
}
|
||||
|
||||
@@ -1,305 +1,74 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type fileInfoMock struct {
|
||||
name string
|
||||
size int64
|
||||
fileMode os.FileMode
|
||||
fileModTime time.Time
|
||||
isDir bool
|
||||
sys interface{}
|
||||
}
|
||||
|
||||
// Name is a function.
|
||||
func (f fileInfoMock) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// Size is a function.
|
||||
func (f fileInfoMock) Size() int64 {
|
||||
return f.size
|
||||
}
|
||||
|
||||
// Mode is a function.
|
||||
func (f fileInfoMock) Mode() os.FileMode {
|
||||
return f.fileMode
|
||||
}
|
||||
|
||||
// ModTime is a function.
|
||||
func (f fileInfoMock) ModTime() time.Time {
|
||||
return f.fileModTime
|
||||
}
|
||||
|
||||
// IsDir is a function.
|
||||
func (f fileInfoMock) IsDir() bool {
|
||||
return f.isDir
|
||||
}
|
||||
|
||||
// Sys is a function.
|
||||
func (f fileInfoMock) Sys() interface{} {
|
||||
return f.sys
|
||||
}
|
||||
|
||||
// TestNavigateToRepoRootDirectory is a function.
|
||||
func TestNavigateToRepoRootDirectory(t *testing.T) {
|
||||
func TestFindWorktreeRoot(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
stat func(string) (os.FileInfo, error)
|
||||
chdir func(string) error
|
||||
test func(error)
|
||||
testName string
|
||||
currentPath string
|
||||
before func(fs afero.Fs)
|
||||
expectedPath string
|
||||
expectedErr string
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"Navigate to git repository",
|
||||
func(string) (os.FileInfo, error) {
|
||||
return fileInfoMock{isDir: true}, nil
|
||||
},
|
||||
func(string) error {
|
||||
return nil
|
||||
},
|
||||
func(err error) {
|
||||
assert.NoError(t, err)
|
||||
testName: "at root of worktree",
|
||||
currentPath: "/path/to/repo",
|
||||
before: func(fs afero.Fs) {
|
||||
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
|
||||
},
|
||||
expectedPath: "/path/to/repo",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
"An error occurred when getting path information",
|
||||
func(string) (os.FileInfo, error) {
|
||||
return nil, fmt.Errorf("An error occurred")
|
||||
},
|
||||
func(string) error {
|
||||
return nil
|
||||
},
|
||||
func(err error) {
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "An error occurred")
|
||||
testName: "inside worktree",
|
||||
currentPath: "/path/to/repo/subdir",
|
||||
before: func(fs afero.Fs) {
|
||||
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
|
||||
_ = fs.MkdirAll("/path/to/repo/subdir", 0o755)
|
||||
},
|
||||
expectedPath: "/path/to/repo",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
"An error occurred when trying to move one path backward",
|
||||
func(string) (os.FileInfo, error) {
|
||||
return nil, os.ErrNotExist
|
||||
},
|
||||
func(string) error {
|
||||
return fmt.Errorf("An error occurred")
|
||||
},
|
||||
func(err error) {
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "An error occurred")
|
||||
testName: "not in a git repo",
|
||||
currentPath: "/path/to/dir",
|
||||
before: func(fs afero.Fs) {},
|
||||
expectedPath: "",
|
||||
expectedErr: "Must open lazygit in a git repository",
|
||||
},
|
||||
{
|
||||
testName: "In linked worktree",
|
||||
currentPath: "/path/to/worktree",
|
||||
before: func(fs afero.Fs) {
|
||||
_ = fs.MkdirAll("/path/to/worktree", 0o755)
|
||||
_ = afero.WriteFile(fs, "/path/to/worktree/.git", []byte("blah"), 0o755)
|
||||
},
|
||||
expectedPath: "/path/to/worktree",
|
||||
expectedErr: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
s.test(navigateToRepoRootDirectory(s.stat, s.chdir))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetupRepository is a function.
|
||||
func TestSetupRepository(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
openGitRepository func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error)
|
||||
errorStr string
|
||||
options gogit.PlainOpenOptions
|
||||
test func(*gogit.Repository, error)
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"A gitconfig parsing error occurred",
|
||||
func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error) {
|
||||
return nil, fmt.Errorf(`unquoted '\' must be followed by new line`)
|
||||
},
|
||||
"error translated",
|
||||
gogit.PlainOpenOptions{},
|
||||
func(r *gogit.Repository, err error) {
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "error translated")
|
||||
},
|
||||
},
|
||||
{
|
||||
"A gogit error occurred",
|
||||
func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error) {
|
||||
return nil, fmt.Errorf("Error from inside gogit")
|
||||
},
|
||||
"",
|
||||
gogit.PlainOpenOptions{},
|
||||
func(r *gogit.Repository, err error) {
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "Error from inside gogit")
|
||||
},
|
||||
},
|
||||
{
|
||||
"Setup done properly",
|
||||
func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error) {
|
||||
assert.NoError(t, os.RemoveAll("/tmp/lazygit-test"))
|
||||
r, err := gogit.PlainInit("/tmp/lazygit-test", false)
|
||||
assert.NoError(t, err)
|
||||
return r, nil
|
||||
},
|
||||
"",
|
||||
gogit.PlainOpenOptions{},
|
||||
func(r *gogit.Repository, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, r)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
s.test(setupRepository(s.openGitRepository, s.options, s.errorStr))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewGitCommand is a function.
|
||||
func TestNewGitCommand(t *testing.T) {
|
||||
actual, err := os.Getwd()
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
assert.NoError(t, os.Chdir(actual))
|
||||
}()
|
||||
|
||||
type scenario struct {
|
||||
testName string
|
||||
setup func()
|
||||
test func(*GitCommand, error)
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"An error occurred, folder doesn't contains a git repository",
|
||||
func() {
|
||||
assert.NoError(t, os.Chdir("/tmp"))
|
||||
},
|
||||
func(gitCmd *GitCommand, err error) {
|
||||
assert.Error(t, err)
|
||||
assert.Regexp(t, `Must open lazygit in a git repository`, err.Error())
|
||||
},
|
||||
},
|
||||
{
|
||||
"New GitCommand object created",
|
||||
func() {
|
||||
assert.NoError(t, os.RemoveAll("/tmp/lazygit-test"))
|
||||
_, err := gogit.PlainInit("/tmp/lazygit-test", false)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, os.Chdir("/tmp/lazygit-test"))
|
||||
},
|
||||
func(gitCmd *GitCommand, err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
s.setup()
|
||||
s.test(
|
||||
NewGitCommand(utils.NewDummyCommon(),
|
||||
&git_commands.GitVersion{},
|
||||
oscommands.NewDummyOSCommand(),
|
||||
git_config.NewFakeGitConfig(nil),
|
||||
&deadlock.Mutex{},
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindDotGitDir(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
stat func(string) (os.FileInfo, error)
|
||||
readFile func(filename string) ([]byte, error)
|
||||
test func(string, error)
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
".git is a directory",
|
||||
func(dotGit string) (os.FileInfo, error) {
|
||||
assert.Equal(t, ".git", dotGit)
|
||||
return os.Stat("testdata/a_dir")
|
||||
},
|
||||
func(dotGit string) ([]byte, error) {
|
||||
assert.Fail(t, "readFile should not be called if .git is a directory")
|
||||
return nil, nil
|
||||
},
|
||||
func(gitDir string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ".git", gitDir)
|
||||
},
|
||||
},
|
||||
{
|
||||
".git is a file",
|
||||
func(dotGit string) (os.FileInfo, error) {
|
||||
assert.Equal(t, ".git", dotGit)
|
||||
return os.Stat("testdata/a_file")
|
||||
},
|
||||
func(dotGit string) ([]byte, error) {
|
||||
assert.Equal(t, ".git", dotGit)
|
||||
return []byte("gitdir: blah\n"), nil
|
||||
},
|
||||
func(gitDir string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "blah", gitDir)
|
||||
},
|
||||
},
|
||||
{
|
||||
"os.Stat returns an error",
|
||||
func(dotGit string) (os.FileInfo, error) {
|
||||
assert.Equal(t, ".git", dotGit)
|
||||
return nil, errors.New("error")
|
||||
},
|
||||
func(dotGit string) ([]byte, error) {
|
||||
assert.Fail(t, "readFile should not be called os.Stat returns an error")
|
||||
return nil, nil
|
||||
},
|
||||
func(gitDir string, err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
"readFile returns an error",
|
||||
func(dotGit string) (os.FileInfo, error) {
|
||||
assert.Equal(t, ".git", dotGit)
|
||||
return os.Stat("testdata/a_file")
|
||||
},
|
||||
func(dotGit string) ([]byte, error) {
|
||||
return nil, errors.New("error")
|
||||
},
|
||||
func(gitDir string, err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
s.test(findDotGitDir(s.stat, s.readFile))
|
||||
fs := afero.NewMemMapFs()
|
||||
s.before(fs)
|
||||
|
||||
root, err := findWorktreeRoot(fs, s.currentPath)
|
||||
if s.expectedErr != "" {
|
||||
assert.EqualError(t, errors.New(s.expectedErr), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, s.expectedPath, root)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// This package is for handling logic specific to a git hosting service like github, gitlab, bitbucket, gitea, etc.
|
||||
@@ -98,33 +99,30 @@ func (self *HostingServiceMgr) getCandidateServiceDomains() []ServiceDomain {
|
||||
|
||||
serviceDomains := slices.Clone(defaultServiceDomains)
|
||||
|
||||
if len(self.configServiceDomains) > 0 {
|
||||
for gitDomain, typeAndDomain := range self.configServiceDomains {
|
||||
splitData := strings.Split(typeAndDomain, ":")
|
||||
if len(splitData) != 2 {
|
||||
self.log.Errorf("Unexpected format for git service: '%s'. Expected something like 'github.com:github.com'", typeAndDomain)
|
||||
continue
|
||||
}
|
||||
for gitDomain, typeAndDomain := range self.configServiceDomains {
|
||||
provider, webDomain, success := strings.Cut(typeAndDomain, ":")
|
||||
|
||||
provider := splitData[0]
|
||||
webDomain := splitData[1]
|
||||
|
||||
serviceDefinition, ok := serviceDefinitionByProvider[provider]
|
||||
if !ok {
|
||||
providerNames := slices.Map(serviceDefinitions, func(serviceDefinition ServiceDefinition) string {
|
||||
return serviceDefinition.provider
|
||||
})
|
||||
|
||||
self.log.Errorf("Unknown git service type: '%s'. Expected one of %s", provider, strings.Join(providerNames, ", "))
|
||||
continue
|
||||
}
|
||||
|
||||
serviceDomains = append(serviceDomains, ServiceDomain{
|
||||
gitDomain: gitDomain,
|
||||
webDomain: webDomain,
|
||||
serviceDefinition: serviceDefinition,
|
||||
})
|
||||
// we allow for one ':' for specifying the TCP port
|
||||
if !success || strings.Count(webDomain, ":") > 1 {
|
||||
self.log.Errorf("Unexpected format for git service: '%s'. Expected something like 'github.com:github.com'", typeAndDomain)
|
||||
continue
|
||||
}
|
||||
|
||||
serviceDefinition, ok := serviceDefinitionByProvider[provider]
|
||||
if !ok {
|
||||
providerNames := lo.Map(serviceDefinitions, func(serviceDefinition ServiceDefinition, _ int) string {
|
||||
return serviceDefinition.provider
|
||||
})
|
||||
|
||||
self.log.Errorf("Unknown git service type: '%s'. Expected one of %s", provider, strings.Join(providerNames, ", "))
|
||||
continue
|
||||
}
|
||||
|
||||
serviceDomains = append(serviceDomains, ServiceDomain{
|
||||
gitDomain: gitDomain,
|
||||
webDomain: webDomain,
|
||||
serviceDefinition: serviceDefinition,
|
||||
})
|
||||
}
|
||||
|
||||
return serviceDomains
|
||||
|
||||
@@ -340,6 +340,30 @@ func TestGetPullRequestURL(t *testing.T) {
|
||||
},
|
||||
expectedLoggedErrors: nil,
|
||||
},
|
||||
{
|
||||
testName: "Does not log error when config service webDomain contains a port",
|
||||
from: "feature/profile-page",
|
||||
remoteUrl: "git@my.domain.test:johndoe/social_network.git",
|
||||
configServiceDomains: map[string]string{
|
||||
"my.domain.test": "gitlab:my.domain.test:1111",
|
||||
},
|
||||
test: func(url string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "https://my.domain.test:1111/johndoe/social_network/-/merge_requests/new?merge_request[source_branch]=feature%2Fprofile-page", url)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Logs error when webDomain contains more than one colon",
|
||||
from: "feature/profile-page",
|
||||
remoteUrl: "git@my.domain.test:johndoe/social_network.git",
|
||||
configServiceDomains: map[string]string{
|
||||
"my.domain.test": "gitlab:my.domain.test:1111:2222",
|
||||
},
|
||||
test: func(url string, err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
expectedLoggedErrors: []string{"Unexpected format for git service: 'gitlab:my.domain.test:1111:2222'. Expected something like 'github.com:github.com'"},
|
||||
},
|
||||
{
|
||||
testName: "Logs error when config service domain is malformed",
|
||||
from: "feature/profile-page",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package models
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Branch : A git branch
|
||||
// duplicating this for now
|
||||
type Branch struct {
|
||||
@@ -43,10 +45,30 @@ func (b *Branch) ParentRefName() string {
|
||||
return b.RefName() + "^"
|
||||
}
|
||||
|
||||
func (b *Branch) FullUpstreamRefName() string {
|
||||
if b.UpstreamRemote == "" || b.UpstreamBranch == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("refs/remotes/%s/%s", b.UpstreamRemote, b.UpstreamBranch)
|
||||
}
|
||||
|
||||
func (b *Branch) ShortUpstreamRefName() string {
|
||||
if b.UpstreamRemote == "" || b.UpstreamBranch == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/%s", b.UpstreamRemote, b.UpstreamBranch)
|
||||
}
|
||||
|
||||
func (b *Branch) ID() string {
|
||||
return b.RefName()
|
||||
}
|
||||
|
||||
func (b *Branch) URN() string {
|
||||
return "branch-" + b.ID()
|
||||
}
|
||||
|
||||
func (b *Branch) Description() string {
|
||||
return b.RefName()
|
||||
}
|
||||
|
||||
@@ -30,6 +30,17 @@ const (
|
||||
ActionConflict = todo.Comment + 1
|
||||
)
|
||||
|
||||
type Divergence int
|
||||
|
||||
// For a divergence log (left/right comparison of two refs) this is set to
|
||||
// either DivergenceLeft or DivergenceRight for each commit; for normal
|
||||
// commit views it is always DivergenceNone.
|
||||
const (
|
||||
DivergenceNone Divergence = iota
|
||||
DivergenceLeft
|
||||
DivergenceRight
|
||||
)
|
||||
|
||||
// Commit : A git commit
|
||||
type Commit struct {
|
||||
Sha string
|
||||
@@ -41,6 +52,7 @@ type Commit struct {
|
||||
AuthorName string // something like 'Jesse Duffield'
|
||||
AuthorEmail string // something like 'jessedduffield@gmail.com'
|
||||
UnixTimestamp int64
|
||||
Divergence Divergence // set to DivergenceNone unless we are showing the divergence view
|
||||
|
||||
// SHAs of parent commits (will be multiple if it's a merge commit)
|
||||
Parents []string
|
||||
|
||||
@@ -18,8 +18,10 @@ type File struct {
|
||||
HasMergeConflicts bool
|
||||
HasInlineMergeConflicts bool
|
||||
DisplayString string
|
||||
Type string // one of 'file', 'directory', and 'other'
|
||||
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
|
||||
|
||||
// If true, this must be a worktree folder
|
||||
IsWorktree bool
|
||||
}
|
||||
|
||||
// sometimes we need to deal with either a node (which contains a file) or an actual file
|
||||
|
||||
@@ -4,8 +4,9 @@ import "fmt"
|
||||
|
||||
// StashEntry : A git stash entry
|
||||
type StashEntry struct {
|
||||
Index int
|
||||
Name string
|
||||
Index int
|
||||
Recency string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (s *StashEntry) FullRefName() string {
|
||||
|
||||
@@ -24,6 +24,10 @@ func (t *Tag) ID() string {
|
||||
return t.RefName()
|
||||
}
|
||||
|
||||
func (t *Tag) URN() string {
|
||||
return "tag-" + t.ID()
|
||||
}
|
||||
|
||||
func (t *Tag) Description() string {
|
||||
return t.Message
|
||||
}
|
||||
|
||||
37
pkg/commands/models/worktree.go
Normal file
37
pkg/commands/models/worktree.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package models
|
||||
|
||||
// A git worktree
|
||||
type Worktree struct {
|
||||
// if false, this is a linked worktree
|
||||
IsMain bool
|
||||
// if true, this is the worktree that is currently checked out
|
||||
IsCurrent bool
|
||||
// path to the directory of the worktree i.e. the directory that contains all the user's files
|
||||
Path string
|
||||
// if true, the path is not found
|
||||
IsPathMissing bool
|
||||
// path of the git directory for this worktree. The equivalent of the .git directory
|
||||
// in the main worktree. For linked worktrees this would be <repo_path>/.git/worktrees/<name>
|
||||
GitDir string
|
||||
// If the worktree has a branch checked out, this field will be set to the branch name.
|
||||
// A branch is considered 'checked out' if:
|
||||
// * the worktree is directly on the branch
|
||||
// * the worktree is mid-rebase on the branch
|
||||
// * the worktree is mid-bisect on the branch
|
||||
Branch string
|
||||
// based on the path, but uniquified. Not the same name that git uses in the worktrees/ folder (no good reason for this,
|
||||
// I just prefer my naming convention better)
|
||||
Name string
|
||||
}
|
||||
|
||||
func (w *Worktree) RefName() string {
|
||||
return w.Name
|
||||
}
|
||||
|
||||
func (w *Worktree) ID() string {
|
||||
return w.Path
|
||||
}
|
||||
|
||||
func (w *Worktree) Description() string {
|
||||
return w.RefName()
|
||||
}
|
||||
@@ -24,6 +24,9 @@ type ICmdObj interface {
|
||||
AddEnvVars(...string) ICmdObj
|
||||
GetEnvVars() []string
|
||||
|
||||
// sets the working directory
|
||||
SetWd(string) ICmdObj
|
||||
|
||||
// runs the command and returns an error if any
|
||||
Run() error
|
||||
// runs the command and returns the output as a string, and an error if any
|
||||
@@ -70,10 +73,6 @@ type ICmdObj interface {
|
||||
}
|
||||
|
||||
type CmdObj struct {
|
||||
// the secureexec package will swap out the first arg with the full path to the binary,
|
||||
// so we store these args separately so that ToString() will output the original
|
||||
args []string
|
||||
|
||||
cmd *exec.Cmd
|
||||
|
||||
runner ICmdObjRunner
|
||||
@@ -118,7 +117,7 @@ func (self *CmdObj) GetCmd() *exec.Cmd {
|
||||
|
||||
func (self *CmdObj) ToString() string {
|
||||
// if a given arg contains a space, we need to wrap it in quotes
|
||||
quotedArgs := lo.Map(self.args, func(arg string, _ int) string {
|
||||
quotedArgs := lo.Map(self.cmd.Args, func(arg string, _ int) string {
|
||||
if strings.Contains(arg, " ") {
|
||||
return `"` + arg + `"`
|
||||
}
|
||||
@@ -129,7 +128,7 @@ func (self *CmdObj) ToString() string {
|
||||
}
|
||||
|
||||
func (self *CmdObj) Args() []string {
|
||||
return self.args
|
||||
return self.cmd.Args
|
||||
}
|
||||
|
||||
func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
|
||||
@@ -142,6 +141,12 @@ func (self *CmdObj) GetEnvVars() []string {
|
||||
return self.cmd.Env
|
||||
}
|
||||
|
||||
func (self *CmdObj) SetWd(wd string) ICmdObj {
|
||||
self.cmd.Dir = wd
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *CmdObj) DontLog() ICmdObj {
|
||||
self.dontLog = true
|
||||
return self
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user