mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-29 02:35:39 -04:00
Compare commits
85
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44d30173c9 | ||
|
|
6bc85e6a21 | ||
|
|
271ca55512 | ||
|
|
ac4767bb2a | ||
|
|
d1f8c45099 | ||
|
|
41a68f7c4a | ||
|
|
944d82028f | ||
|
|
379a6f1922 | ||
|
|
cede021400 | ||
|
|
99ad6005e8 | ||
|
|
dc9ee186f4 | ||
|
|
bb26979420 | ||
|
|
c6d20c876e | ||
|
|
3e3b902228 | ||
|
|
dfb45ba893 | ||
|
|
314efe2539 | ||
|
|
bbc680266b | ||
|
|
40232440b7 | ||
|
|
44f553b609 | ||
|
|
3b723282cb | ||
|
|
db4f12929e | ||
|
|
ea87912a74 | ||
|
|
ddcd916301 | ||
|
|
85a6a42bff | ||
|
|
ad017459d2 | ||
|
|
cfaf4b29d0 | ||
|
|
8d9e5d1258 | ||
|
|
212ec7e8e5 | ||
|
|
0f14065245 | ||
|
|
eb0f7e3d02 | ||
|
|
e9b04b8cfc | ||
|
|
253a0096f9 | ||
|
|
aa81c456bb | ||
|
|
ee5533f9bf | ||
|
|
416a40b0e6 | ||
|
|
418b316fab | ||
|
|
fd8ce7d779 | ||
|
|
ed34ddc04d | ||
|
|
581161ac34 | ||
|
|
69c9477783 | ||
|
|
98d6504d1d | ||
|
|
afb9980ad9 | ||
|
|
e3809f3111 | ||
|
|
fe7d1847b7 | ||
|
|
ad0394aebe | ||
|
|
af56065dd6 | ||
|
|
503422a72e | ||
|
|
329b434915 | ||
|
|
9362aede8f | ||
|
|
dfabe8db70 | ||
|
|
b0f3bb7a9a | ||
|
|
c7c8776371 | ||
|
|
7cef4f4e33 | ||
|
|
2c0520bc4b | ||
|
|
2118ecdf8e | ||
|
|
4302018437 | ||
|
|
236f42879c | ||
|
|
d329c92554 | ||
|
|
afbaf82395 | ||
|
|
a2ff2e6dd9 | ||
|
|
9f0b4d0000 | ||
|
|
649048c336 | ||
|
|
57ac9c2189 | ||
|
|
c4eedec9d5 | ||
|
|
2b9cb3a640 | ||
|
|
1081c45397 | ||
|
|
6c6201ab04 | ||
|
|
ea42275f06 | ||
|
|
5b567f3774 | ||
|
|
7f4a05debf | ||
|
|
e354a9bb48 | ||
|
|
b01bad7fad | ||
|
|
3b7f32db95 | ||
|
|
d3804d313c | ||
|
|
8746c3d9e0 | ||
|
|
c431698dba | ||
|
|
d138f7ce86 | ||
|
|
62a0c895b4 | ||
|
|
15d5261933 | ||
|
|
beb730a9e6 | ||
|
|
b1d05b6371 | ||
|
|
0c5212ae0b | ||
|
|
bc6616d511 | ||
|
|
2c82b3f8dd | ||
|
|
dc40fb5267 |
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.21.x
|
||||
- name: Run goreleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Continuous Integration
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.20
|
||||
GO_VERSION: 1.21
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.21.x
|
||||
- name: Test code
|
||||
# we're passing -short so that we skip the integration tests, which will be run in parallel below
|
||||
run: |
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.21.x
|
||||
- name: Print git version
|
||||
run: git --version
|
||||
- name: Test code
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.21.x
|
||||
- name: Build linux binary
|
||||
run: |
|
||||
GOOS=linux go build
|
||||
@@ -142,7 +142,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.21.x
|
||||
- name: Check Vendor Directory
|
||||
# ensure our vendor directory matches up with our go modules
|
||||
run: |
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
go-version: 1.21.x
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v3.7.0
|
||||
with:
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
# see https://github.com/JamesIves/github-sponsors-readme-action
|
||||
name: Generate Sponsors README
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request: # Add this if you want to run checks on PRs as well
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: master # This ensures the checkout step fetches the master branch
|
||||
|
||||
- name: Switch to master branch
|
||||
run: |
|
||||
git fetch origin master:refs/remotes/origin/master
|
||||
git checkout master
|
||||
|
||||
- name: Generate Sponsors 💖
|
||||
uses: JamesIves/github-sponsors-readme-action@v1.2.2
|
||||
@@ -24,5 +33,7 @@ jobs:
|
||||
git config --global user.email "actions@users.noreply.github.com"
|
||||
git config --global user.name "README-bot"
|
||||
git add README.md
|
||||
git commit -m "Updated README.md" || exit 0
|
||||
git push
|
||||
git commit -m "Updated README.md [skip ci]" || exit 0
|
||||
git push origin master # Explicitly push to master branch
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }}
|
||||
|
||||
+1
-1
@@ -30,5 +30,5 @@ linters-settings:
|
||||
max-func-lines: 0
|
||||
|
||||
run:
|
||||
go: '1.20'
|
||||
go: '1.21'
|
||||
timeout: 10m
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
# docker build -t lazygit .
|
||||
# docker run -it lazygit:latest /bin/sh
|
||||
|
||||
FROM golang:1.20 as build
|
||||
FROM golang:1.21 as build
|
||||
WORKDIR /go/src/github.com/jesseduffield/lazygit/
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
+8
-2
@@ -4,7 +4,7 @@ Default path for the config file:
|
||||
|
||||
- Linux: `~/.config/lazygit/config.yml`
|
||||
- MacOS: `~/Library/Application\ Support/lazygit/config.yml`
|
||||
- Windows: `%APPDATA%\lazygit\config.yml`
|
||||
- Windows: `%LOCALAPPDATA%\lazygit\config.yml` (default location, but it will also be found in `%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):
|
||||
|
||||
@@ -92,6 +92,8 @@ git:
|
||||
useConfig: false
|
||||
commit:
|
||||
signOff: false
|
||||
autoWrapCommitMessage: true # automatic WYSIWYG wrapping of the commit message as you type
|
||||
autoWrapWidth: 72 # if autoWrapCommitMessage is true, the width to wrap to
|
||||
merging:
|
||||
# only applicable to unix users
|
||||
manualCommit: false
|
||||
@@ -101,10 +103,14 @@ git:
|
||||
# one of date-order, author-date-order, topo-order or default.
|
||||
# topo-order makes it easier to read the git log graph, but commits may not
|
||||
# appear chronologically. See https://git-scm.com/docs/git-log#_commit_ordering
|
||||
#
|
||||
# Deprecated: Configure this with `Log menu -> Commit sort order` (<c-l> in the commits window by default).
|
||||
order: 'topo-order'
|
||||
# one of always, never, when-maximised
|
||||
# this determines whether the git graph is rendered in the commits panel
|
||||
showGraph: 'when-maximised'
|
||||
#
|
||||
# Deprecated: Configure this with `Log menu -> Show git graph` (<c-l> in the commits window by default).
|
||||
showGraph: 'always'
|
||||
# displays the whole git graph by default in the commits panel (equivalent to passing the `--all` argument to `git log`)
|
||||
showWholeGraph: false
|
||||
skipHookPrefix: WIP
|
||||
|
||||
@@ -23,7 +23,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` + `` | Next screen mode (normal/half/fullscreen) | |
|
||||
| `` _ `` | Prev screen mode | |
|
||||
| `` ? `` | Open keybindings menu | |
|
||||
| `` <c-s> `` | View filter-by-path options | View options for filtering the commit log by a file path, so that only commits relating to that path are shown. |
|
||||
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` q `` | Quit | |
|
||||
@@ -230,6 +230,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` c `` | Commit | Commit staged changes. |
|
||||
| `` w `` | Commit changes without pre-commit hook | |
|
||||
| `` C `` | Commit changes using git editor | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | Search the current view by text | |
|
||||
|
||||
## Menu
|
||||
|
||||
@@ -23,7 +23,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` + `` | 次のスクリーンモード (normal/half/fullscreen) | |
|
||||
| `` _ `` | 前のスクリーンモード | |
|
||||
| `` ? `` | メニューを開く | |
|
||||
| `` <c-s> `` | View filter-by-path options | View options for filtering the commit log by a file path, so that only commits relating to that path are shown. |
|
||||
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | 差分メニューを開く | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | 差分メニューを開く | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` q `` | 終了 | |
|
||||
@@ -300,6 +300,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` c `` | 変更をコミット | Commit staged changes. |
|
||||
| `` w `` | pre-commitフックを実行せずに変更をコミット | |
|
||||
| `` C `` | gitエディタを使用して変更をコミット | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | 検索を開始 | |
|
||||
|
||||
## メニュー
|
||||
|
||||
@@ -23,7 +23,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` + `` | 다음 스크린 모드 (normal/half/fullscreen) | |
|
||||
| `` _ `` | 이전 스크린 모드 | |
|
||||
| `` ? `` | 매뉴 열기 | |
|
||||
| `` <c-s> `` | View filter-by-path options | View options for filtering the commit log by a file path, so that only commits relating to that path are shown. |
|
||||
| `` <c-s> `` | View filter-by-path options | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` q `` | 종료 | |
|
||||
@@ -171,6 +171,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` c `` | 커밋 변경내용 | Commit staged changes. |
|
||||
| `` w `` | Commit changes without pre-commit hook | |
|
||||
| `` C `` | Git 편집기를 사용하여 변경 내용을 커밋합니다. | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | 검색 시작 | |
|
||||
|
||||
## 브랜치
|
||||
|
||||
@@ -23,7 +23,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` + `` | Volgende scherm modus (normaal/half/groot) | |
|
||||
| `` _ `` | Vorige scherm modus | |
|
||||
| `` ? `` | Open menu | |
|
||||
| `` <c-s> `` | Bekijk scoping opties | View options for filtering the commit log by a file path, so that only commits relating to that path are shown. |
|
||||
| `` <c-s> `` | Bekijk scoping opties | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` q `` | Quit | |
|
||||
@@ -283,6 +283,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` c `` | Commit veranderingen | Commit staged changes. |
|
||||
| `` w `` | Commit veranderingen zonder pre-commit hook | |
|
||||
| `` C `` | Commit veranderingen met de git editor | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | Start met zoeken | |
|
||||
|
||||
## Stash
|
||||
|
||||
@@ -23,7 +23,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` + `` | Next screen mode (normal/half/fullscreen) | |
|
||||
| `` _ `` | Prev screen mode | |
|
||||
| `` ? `` | Open keybindings menu | |
|
||||
| `` <c-s> `` | View filter-by-path options | View options for filtering the commit log by a file path, so that only commits relating to that path are shown. |
|
||||
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` q `` | Quit | |
|
||||
@@ -215,6 +215,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` c `` | Zatwierdź zmiany | Commit staged changes. |
|
||||
| `` w `` | Zatwierdź zmiany bez skryptu pre-commit | |
|
||||
| `` C `` | Zatwierdź zmiany używając edytora | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | Search the current view by text | |
|
||||
|
||||
## Reflog
|
||||
|
||||
@@ -23,7 +23,7 @@ _Связки клавиш_
|
||||
| `` + `` | Следующий режим экрана (нормальный/полуэкранный/полноэкранный) | |
|
||||
| `` _ `` | Предыдущий режим экрана | |
|
||||
| `` ? `` | Открыть меню | |
|
||||
| `` <c-s> `` | Просмотреть параметры фильтрации по пути | View options for filtering the commit log by a file path, so that only commits relating to that path are shown. |
|
||||
| `` <c-s> `` | Просмотреть параметры фильтрации по пути | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` q `` | Выйти | |
|
||||
@@ -78,6 +78,7 @@ _Связки клавиш_
|
||||
| `` c `` | Сохранить изменения | Commit staged changes. |
|
||||
| `` w `` | Закоммитить изменения без предварительного хука коммита | |
|
||||
| `` C `` | Сохранить изменения с помощью редактора git | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | Найти | |
|
||||
|
||||
## Главная панель (Обычный)
|
||||
|
||||
@@ -23,7 +23,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` + `` | 下一屏模式(正常/半屏/全屏) | |
|
||||
| `` _ `` | 上一屏模式 | |
|
||||
| `` ? `` | 打开菜单 | |
|
||||
| `` <c-s> `` | 查看按路径过滤选项 | View options for filtering the commit log by a file path, so that only commits relating to that path are shown. |
|
||||
| `` <c-s> `` | 查看按路径过滤选项 | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | 打开 diff 菜单 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | 打开 diff 菜单 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` q `` | 退出 | |
|
||||
@@ -287,6 +287,7 @@ If you would instead like to start an interactive rebase from the selected commi
|
||||
| `` c `` | 提交更改 | Commit staged changes. |
|
||||
| `` w `` | 提交更改而无需预先提交钩子 | |
|
||||
| `` C `` | 提交更改(使用编辑器编辑提交信息) | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
## 正常
|
||||
|
||||
@@ -23,7 +23,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
| `` + `` | 下一個螢幕模式(常規/半螢幕/全螢幕) | |
|
||||
| `` _ `` | 上一個螢幕模式 | |
|
||||
| `` ? `` | 開啟選單 | |
|
||||
| `` <c-s> `` | 檢視篩選路徑選項 | View options for filtering the commit log by a file path, so that only commits relating to that path are shown. |
|
||||
| `` <c-s> `` | 檢視篩選路徑選項 | View options for filtering the commit log, so that only commits matching the filter are shown. |
|
||||
| `` W `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` <c-e> `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
|
||||
| `` q `` | 結束 | |
|
||||
@@ -118,6 +118,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B_
|
||||
| `` c `` | 提交變更 | Commit staged changes. |
|
||||
| `` w `` | 沒有預提交 hook 就提交更改 | |
|
||||
| `` C `` | 使用 git 編輯器提交變更 | |
|
||||
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
|
||||
| `` / `` | 開始搜尋 | |
|
||||
|
||||
## 主面板 (補丁生成)
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
module github.com/jesseduffield/lazygit
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/OpenPeeDeeP/xdg v1.0.0
|
||||
github.com/adrg/xdg v0.4.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aybabtme/humanlog v0.4.1
|
||||
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/gdamore/tcell/v2 v2.7.1-0.20240121011954-0393f5eb0b1a
|
||||
github.com/gdamore/tcell/v2 v2.7.4
|
||||
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.20240129213945-26fc8669eb5b
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240309085756-86e0d5a312de
|
||||
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
|
||||
@@ -68,14 +68,14 @@ require (
|
||||
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.6 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // 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-20220722155217-630584e8d5aa // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
@@ -38,8 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
|
||||
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/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
@@ -89,9 +89,8 @@ github.com/fsmiamoto/git-todo-parser v0.0.5/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLI
|
||||
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.7.1-0.20240103180601-96e29905643b/go.mod h1:hl/KtAANGBecfIPxk+FzKvThTqI84oplgbPEmVX60b8=
|
||||
github.com/gdamore/tcell/v2 v2.7.1-0.20240121011954-0393f5eb0b1a h1:IgatwqPZL0RPblLezzibmx8GgARDjOQOvrLpCWLmZak=
|
||||
github.com/gdamore/tcell/v2 v2.7.1-0.20240121011954-0393f5eb0b1a/go.mod h1:hl/KtAANGBecfIPxk+FzKvThTqI84oplgbPEmVX60b8=
|
||||
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
|
||||
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
|
||||
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=
|
||||
@@ -188,8 +187,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
|
||||
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.20240129213945-26fc8669eb5b h1:QASuIUc76BuFmSuzzqwzjpsn23r8ybfDqbKsY2WzTrE=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240129213945-26fc8669eb5b/go.mod h1:9zkyjnUmdL3+sUknJrQy/3HweUu8mVln/3J2wRF/l8M=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240309085756-86e0d5a312de h1:2ww1SWgakihE8hFxZ7L3agVeGpA6qwW5vdnhFUXKMQo=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20240309085756-86e0d5a312de/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
|
||||
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=
|
||||
@@ -264,8 +263,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
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.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
||||
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/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=
|
||||
@@ -286,7 +285,6 @@ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKk
|
||||
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/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=
|
||||
@@ -299,6 +297,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
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=
|
||||
@@ -467,18 +466,19 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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-20211025201205-69cdffdb9359/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.5.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/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.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-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.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/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
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=
|
||||
|
||||
@@ -210,7 +210,7 @@ type MergeOpts struct {
|
||||
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
|
||||
cmdArgs := NewGitCmd("merge").
|
||||
Arg("--no-edit").
|
||||
ArgIf(self.UserConfig.Git.Merging.Args != "", self.UserConfig.Git.Merging.Args).
|
||||
Arg(strings.Fields(self.UserConfig.Git.Merging.Args)...).
|
||||
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||
Arg(branchName).
|
||||
ToArgv()
|
||||
|
||||
@@ -127,6 +127,19 @@ func TestBranchMerge(t *testing.T) {
|
||||
branchName: "mybranch",
|
||||
expected: []string{"merge", "--no-edit", "--merging-args", "mybranch"},
|
||||
},
|
||||
{
|
||||
testName: "multiple merging args",
|
||||
userConfig: &config.UserConfig{
|
||||
Git: config.GitConfig{
|
||||
Merging: config.MergingConfig{
|
||||
Args: "--arg1 --arg2", // it's up to the user what they put here
|
||||
},
|
||||
},
|
||||
},
|
||||
opts: MergeOpts{},
|
||||
branchName: "mybranch",
|
||||
expected: []string{"merge", "--no-edit", "--arg1", "--arg2", "mybranch"},
|
||||
},
|
||||
{
|
||||
testName: "fast forward only",
|
||||
userConfig: &config.UserConfig{},
|
||||
|
||||
@@ -142,7 +142,7 @@ func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
|
||||
ToArgv()
|
||||
|
||||
message, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
return strings.TrimSpace(message), err
|
||||
return strings.ReplaceAll(strings.TrimSpace(message), "\r\n", "\n"), err
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitSubject(commitSha string) (string, error) {
|
||||
@@ -239,6 +239,7 @@ func (self *CommitCommands) ShowCmdObj(sha string, filterPath string) oscommands
|
||||
|
||||
extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Config("diff.noprefix=false").
|
||||
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
|
||||
ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
|
||||
@@ -24,6 +24,7 @@ func NewCommitFileLoader(common *common.Common, cmd oscommands.ICmdObjBuilder) *
|
||||
// GetFilesInDiff get the specified commit files
|
||||
func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) {
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
Config("diff.noprefix=false").
|
||||
Arg("--submodule").
|
||||
Arg("--no-ext-diff").
|
||||
Arg("--name-status").
|
||||
|
||||
@@ -64,6 +64,7 @@ func NewCommitLoader(
|
||||
type GetCommitsOptions struct {
|
||||
Limit bool
|
||||
FilterPath string
|
||||
FilterAuthor string
|
||||
IncludeRebaseCommits bool
|
||||
RefName string // e.g. "HEAD" or "my_branch"
|
||||
RefForPushedStatus string // the ref to use for determining pushed/unpushed status
|
||||
@@ -253,10 +254,7 @@ func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode) ([]*models.Commit, error) {
|
||||
commits, err := self.getRebasingCommits(rebaseMode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commits := self.getRebasingCommits(rebaseMode)
|
||||
|
||||
if len(commits) == 0 {
|
||||
return nil, nil
|
||||
@@ -277,7 +275,7 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
||||
).DontLog()
|
||||
|
||||
fullCommits := map[string]*models.Commit{}
|
||||
err = cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
||||
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
||||
commit := self.extractCommitFromLine(line, false)
|
||||
fullCommits[commit.Sha] = commit
|
||||
return false, nil
|
||||
@@ -313,73 +311,20 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
||||
}
|
||||
|
||||
// getRebasingCommits obtains the commits that we're in the process of rebasing
|
||||
func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) ([]*models.Commit, error) {
|
||||
switch rebaseMode {
|
||||
case enums.REBASE_MODE_MERGING:
|
||||
return self.getNormalRebasingCommits()
|
||||
case enums.REBASE_MODE_INTERACTIVE:
|
||||
return self.getInteractiveRebasingCommits()
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) {
|
||||
rewrittenCount := 0
|
||||
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply/rewritten"))
|
||||
if err == nil {
|
||||
content := string(bytesContent)
|
||||
rewrittenCount = len(strings.Split(content, "\n"))
|
||||
}
|
||||
|
||||
// we know we're rebasing, so lets get all the files whose names have numbers
|
||||
commits := []*models.Commit{}
|
||||
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
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
re := regexp.MustCompile(`^\d+$`)
|
||||
if !re.MatchString(f.Name()) {
|
||||
return nil
|
||||
}
|
||||
bytesContent, err := self.readFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content := string(bytesContent)
|
||||
commit := self.commitFromPatch(content)
|
||||
commits = append([]*models.Commit{commit}, commits...)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
// git-rebase-todo example:
|
||||
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
|
||||
// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
|
||||
func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) []*models.Commit {
|
||||
if rebaseMode != enums.REBASE_MODE_INTERACTIVE {
|
||||
return nil
|
||||
}
|
||||
|
||||
// git-rebase-todo.backup example:
|
||||
// pick 49cbba374296938ea86bbd4bf4fee2f6ba5cccf6 third commit on master
|
||||
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 blah commit on master
|
||||
// pick afb893148791a2fbd8091aeb81deba4930c73031 fourth commit on master
|
||||
|
||||
// getInteractiveRebasingCommits takes our git-rebase-todo and our git-rebase-todo.backup files
|
||||
// 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.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
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
commits := []*models.Commit{}
|
||||
@@ -387,7 +332,7 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
|
||||
todos, err := todo.Parse(bytes.NewBuffer(bytesContent), self.config.GetCoreCommentChar())
|
||||
if err != nil {
|
||||
self.Log.Error(fmt.Sprintf("error occurred while parsing git-rebase-todo file: %s", err.Error()))
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// See if the current commit couldn't be applied because it conflicted; if
|
||||
@@ -416,7 +361,7 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
|
||||
})
|
||||
}
|
||||
|
||||
return commits, nil
|
||||
return commits
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) string {
|
||||
@@ -506,22 +451,6 @@ func (self *CommitLoader) getConflictedCommitImpl(todos []todo.Todo, doneTodos [
|
||||
return lastTodo.Commit
|
||||
}
|
||||
|
||||
// assuming the file starts like this:
|
||||
// From e93d4193e6dd45ca9cf3a5a273d7ba6cd8b8fb20 Mon Sep 17 00:00:00 2001
|
||||
// From: Lazygit Tester <test@example.com>
|
||||
// Date: Wed, 5 Dec 2018 21:03:23 +1100
|
||||
// Subject: second commit on master
|
||||
func (self *CommitLoader) commitFromPatch(content string) *models.Commit {
|
||||
lines := strings.Split(content, "\n")
|
||||
sha := strings.Split(lines[0], " ")[1]
|
||||
name := strings.TrimPrefix(lines[3], "Subject: ")
|
||||
return &models.Commit{
|
||||
Sha: sha,
|
||||
Name: name,
|
||||
Status: models.StatusRebasing,
|
||||
}
|
||||
}
|
||||
|
||||
func setCommitMergedStatuses(ancestor string, commits []*models.Commit) {
|
||||
if ancestor == "" {
|
||||
return
|
||||
@@ -650,7 +579,7 @@ func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
|
||||
|
||||
// getLog gets the git log.
|
||||
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||
config := self.UserConfig.Git.Log
|
||||
gitLogOrder := self.AppState.GitLogOrder
|
||||
|
||||
refSpec := opts.RefName
|
||||
if opts.RefToShowDivergenceFrom != "" {
|
||||
@@ -659,11 +588,12 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||
|
||||
cmdArgs := NewGitCmd("log").
|
||||
Arg(refSpec).
|
||||
ArgIf(config.Order != "default", "--"+config.Order).
|
||||
ArgIf(gitLogOrder != "default", "--"+gitLogOrder).
|
||||
ArgIf(opts.All, "--all").
|
||||
Arg("--oneline").
|
||||
Arg(prettyFormat).
|
||||
Arg("--abbrev=40").
|
||||
ArgIf(opts.FilterAuthor != "", "--author="+opts.FilterAuthor).
|
||||
ArgIf(opts.Limit, "-300").
|
||||
ArgIf(opts.FilterPath != "", "--follow").
|
||||
Arg("--no-show-signature").
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -305,7 +306,8 @@ func TestGetCommits(t *testing.T) {
|
||||
scenario := scenario
|
||||
t.Run(scenario.testName, func(t *testing.T) {
|
||||
common := utils.NewDummyCommon()
|
||||
common.UserConfig.Git.Log.Order = scenario.logOrder
|
||||
common.AppState = &config.AppState{}
|
||||
common.AppState.GitLogOrder = scenario.logOrder
|
||||
|
||||
builder := &CommitLoader{
|
||||
Common: common,
|
||||
|
||||
@@ -197,7 +197,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
},
|
||||
{
|
||||
testName: "Default case with filter path",
|
||||
@@ -205,7 +205,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
@@ -213,7 +213,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 77,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff, ignoring whitespace",
|
||||
@@ -221,7 +221,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 77,
|
||||
ignoreWhitespace: true,
|
||||
extDiffCmd: "",
|
||||
expected: []string{"-C", "/path/to/worktree", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
|
||||
},
|
||||
{
|
||||
testName: "Show diff with external diff command",
|
||||
@@ -229,7 +229,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
extDiffCmd: "difft --color=always",
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
|
||||
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
||||
return self.cmd.New(
|
||||
NewGitCmd("diff").
|
||||
Config("diff.noprefix=false").
|
||||
Arg("--submodule", "--no-ext-diff", "--color").
|
||||
Arg(diffArgs...).
|
||||
Dir(self.repoPaths.worktreePath).
|
||||
@@ -24,6 +25,7 @@ func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
|
||||
|
||||
func (self *DiffCommands) internalDiffCmdObj(diffArgs ...string) *GitCommandBuilder {
|
||||
return NewGitCmd("diff").
|
||||
Config("diff.noprefix=false").
|
||||
Arg("--no-ext-diff", "--no-color").
|
||||
Arg(diffArgs...).
|
||||
Dir(self.repoPaths.worktreePath)
|
||||
@@ -87,6 +89,7 @@ func (self *DiffCommands) OpenDiffToolCmdObj(opts DiffToolCmdOptions) oscommands
|
||||
func (self *DiffCommands) DiffIndexCmdObj(diffArgs ...string) oscommands.ICmdObj {
|
||||
return self.cmd.New(
|
||||
NewGitCmd("diff-index").
|
||||
Config("diff.noprefix=false").
|
||||
Arg("--submodule", "--no-ext-diff", "--no-color", "--patch").
|
||||
Arg(diffArgs...).ToArgv(),
|
||||
)
|
||||
|
||||
@@ -60,6 +60,14 @@ func (self *GitCommandBuilder) Dir(path string) *GitCommandBuilder {
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) DirIf(condition bool, path string) *GitCommandBuilder {
|
||||
if condition {
|
||||
return self.Dir(path)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -321,7 +321,11 @@ func (self *PatchCommands) PullPatchIntoNewCommit(
|
||||
// only some lines of a range of adjacent added lines. To solve this, we
|
||||
// get the diff of HEAD and the original commit and then apply that.
|
||||
func (self *PatchCommands) diffHeadAgainstCommit(commit *models.Commit) (string, error) {
|
||||
cmdArgs := NewGitCmd("diff").Arg("HEAD.." + commit.Sha).ToArgv()
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
Config("diff.noprefix=false").
|
||||
Arg("--no-ext-diff").
|
||||
Arg("HEAD.." + commit.Sha).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).RunWithOutput()
|
||||
}
|
||||
|
||||
@@ -423,23 +423,25 @@ func (self *RebaseCommands) runSkipEditorCommand(cmdObj oscommands.ICmdObj) erro
|
||||
}
|
||||
|
||||
// DiscardOldFileChanges discards changes to a file from an old commit
|
||||
func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, commitIndex int, fileName string) error {
|
||||
func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, commitIndex int, filePaths []string) error {
|
||||
if err := self.BeginInteractiveRebaseForCommit(commits, commitIndex, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
||||
cmdArgs := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+fileName).ToArgv()
|
||||
for _, filePath := range filePaths {
|
||||
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
||||
cmdArgs := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+filePath).ToArgv()
|
||||
|
||||
if err := self.cmd.New(cmdArgs).Run(); err != nil {
|
||||
if err := self.os.Remove(fileName); err != nil {
|
||||
if err := self.cmd.New(cmdArgs).Run(); err != nil {
|
||||
if err := self.os.Remove(filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := self.workingTree.StageFile(filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := self.workingTree.CheckoutFile("HEAD^", filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := self.workingTree.StageFile(fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := self.workingTree.CheckoutFile("HEAD^", fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// amend the commit
|
||||
|
||||
@@ -111,7 +111,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||
gitConfigMockResponses map[string]string
|
||||
commits []*models.Commit
|
||||
commitIndex int
|
||||
fileName string
|
||||
fileName []string
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
test func(error)
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||
gitConfigMockResponses: nil,
|
||||
commits: []*models.Commit{},
|
||||
commitIndex: 0,
|
||||
fileName: "test999.txt",
|
||||
fileName: []string{"test999.txt"},
|
||||
runner: oscommands.NewFakeRunner(t),
|
||||
test: func(err error) {
|
||||
assert.Error(t, err)
|
||||
@@ -133,7 +133,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||
gitConfigMockResponses: map[string]string{"commit.gpgsign": "true"},
|
||||
commits: []*models.Commit{{Name: "commit", Sha: "123456"}},
|
||||
commitIndex: 0,
|
||||
fileName: "test999.txt",
|
||||
fileName: []string{"test999.txt"},
|
||||
runner: oscommands.NewFakeRunner(t),
|
||||
test: func(err error) {
|
||||
assert.Error(t, err)
|
||||
@@ -147,7 +147,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||
{Name: "commit2", Sha: "abcdef"},
|
||||
},
|
||||
commitIndex: 0,
|
||||
fileName: "test999.txt",
|
||||
fileName: []string{"test999.txt"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "abcdef"}, "", nil).
|
||||
ExpectGitArgs([]string{"cat-file", "-e", "HEAD^:test999.txt"}, "", nil).
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewReflogCommitLoader(common *common.Common, cmd oscommands.ICmdObjBuilder)
|
||||
|
||||
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
|
||||
// if none is passed (i.e. it's value is nil) then we get all the reflog commits
|
||||
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string) ([]*models.Commit, bool, error) {
|
||||
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string, filterAuthor string) ([]*models.Commit, bool, error) {
|
||||
commits := make([]*models.Commit, 0)
|
||||
|
||||
cmdArgs := NewGitCmd("log").
|
||||
@@ -31,6 +31,7 @@ func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit
|
||||
Arg("-g").
|
||||
Arg("--abbrev=40").
|
||||
Arg("--format=%h%x00%ct%x00%gs%x00%p").
|
||||
ArgIf(filterAuthor != "", "--author="+filterAuthor).
|
||||
ArgIf(filterPath != "", "--follow", "--", filterPath).
|
||||
ToArgv()
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ func TestGetReflogCommits(t *testing.T) {
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
lastReflogCommit *models.Commit
|
||||
filterPath string
|
||||
filterAuthor string
|
||||
expectedCommits []*models.Commit
|
||||
expectedOnlyObtainedNew bool
|
||||
expectedError error
|
||||
@@ -136,6 +137,31 @@ func TestGetReflogCommits(t *testing.T) {
|
||||
expectedOnlyObtainedNew: true,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "when passing filterAuthor",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--author=John Doe <john@doe.com>"}, reflogOutput, nil),
|
||||
|
||||
lastReflogCommit: &models.Commit{
|
||||
Sha: "c3c4b66b64c97ffeecde",
|
||||
Name: "checkout: moving from B to A",
|
||||
Status: models.StatusReflog,
|
||||
UnixTimestamp: 1643150483,
|
||||
Parents: []string{"51baa8c1"},
|
||||
},
|
||||
filterAuthor: "John Doe <john@doe.com>",
|
||||
expectedCommits: []*models.Commit{
|
||||
{
|
||||
Sha: "c3c4b66b64c97ffeecde",
|
||||
Name: "checkout: moving from A to B",
|
||||
Status: models.StatusReflog,
|
||||
UnixTimestamp: 1643150483,
|
||||
Parents: []string{"51baa8c1"},
|
||||
},
|
||||
},
|
||||
expectedOnlyObtainedNew: true,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "when command returns error",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
@@ -157,7 +183,7 @@ func TestGetReflogCommits(t *testing.T) {
|
||||
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
||||
}
|
||||
|
||||
commits, onlyObtainednew, err := builder.GetReflogCommits(scenario.lastReflogCommit, scenario.filterPath)
|
||||
commits, onlyObtainednew, err := builder.GetReflogCommits(scenario.lastReflogCommit, scenario.filterPath, scenario.filterAuthor)
|
||||
assert.Equal(t, scenario.expectedOnlyObtainedNew, onlyObtainednew)
|
||||
assert.Equal(t, scenario.expectedError, err)
|
||||
t.Logf("actual commits: \n%s", litter.Sdump(commits))
|
||||
|
||||
@@ -26,8 +26,12 @@ func NewSubmoduleCommands(gitCommon *GitCommon) *SubmoduleCommands {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) GetConfigs() ([]*models.SubmoduleConfig, error) {
|
||||
file, err := os.Open(".gitmodules")
|
||||
func (self *SubmoduleCommands) GetConfigs(parentModule *models.SubmoduleConfig) ([]*models.SubmoduleConfig, error) {
|
||||
gitModulesPath := ".gitmodules"
|
||||
if parentModule != nil {
|
||||
gitModulesPath = filepath.Join(parentModule.FullPath(), gitModulesPath)
|
||||
}
|
||||
file, err := os.Open(gitModulesPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
@@ -51,21 +55,27 @@ func (self *SubmoduleCommands) GetConfigs() ([]*models.SubmoduleConfig, error) {
|
||||
}
|
||||
|
||||
configs := []*models.SubmoduleConfig{}
|
||||
lastConfigIdx := -1
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
if name, ok := firstMatch(line, `\[submodule "(.*)"\]`); ok {
|
||||
configs = append(configs, &models.SubmoduleConfig{Name: name})
|
||||
configs = append(configs, &models.SubmoduleConfig{
|
||||
Name: name, ParentModule: parentModule,
|
||||
})
|
||||
lastConfigIdx = len(configs) - 1
|
||||
continue
|
||||
}
|
||||
|
||||
if len(configs) > 0 {
|
||||
lastConfig := configs[len(configs)-1]
|
||||
|
||||
if lastConfigIdx != -1 {
|
||||
if path, ok := firstMatch(line, `\s*path\s*=\s*(.*)\s*`); ok {
|
||||
lastConfig.Path = path
|
||||
configs[lastConfigIdx].Path = path
|
||||
nestedConfigs, err := self.GetConfigs(configs[lastConfigIdx])
|
||||
if err == nil {
|
||||
configs = append(configs, nestedConfigs...)
|
||||
}
|
||||
} else if url, ok := firstMatch(line, `\s*url\s*=\s*(.*)\s*`); ok {
|
||||
lastConfig.Url = url
|
||||
configs[lastConfigIdx].Url = url
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,12 +87,12 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
|
||||
// if the path does not exist then it hasn't yet been initialized so we'll swallow the error
|
||||
// because the intention here is to have no dirty worktree state
|
||||
if _, err := os.Stat(submodule.Path); os.IsNotExist(err) {
|
||||
self.Log.Infof("submodule path %s does not exist, returning", submodule.Path)
|
||||
self.Log.Infof("submodule path %s does not exist, returning", submodule.FullPath())
|
||||
return nil
|
||||
}
|
||||
|
||||
cmdArgs := NewGitCmd("stash").
|
||||
Dir(submodule.Path).
|
||||
Dir(submodule.FullPath()).
|
||||
Arg("--include-untracked").
|
||||
ToArgv()
|
||||
|
||||
@@ -90,8 +100,13 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Reset(submodule *models.SubmoduleConfig) error {
|
||||
parentDir := ""
|
||||
if submodule.ParentModule != nil {
|
||||
parentDir = submodule.ParentModule.FullPath()
|
||||
}
|
||||
cmdArgs := NewGitCmd("submodule").
|
||||
Arg("update", "--init", "--force", "--", submodule.Path).
|
||||
DirIf(parentDir != "", parentDir).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
@@ -107,6 +122,20 @@ func (self *SubmoduleCommands) UpdateAll() error {
|
||||
func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
||||
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
|
||||
|
||||
if submodule.ParentModule != nil {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chdir(submodule.ParentModule.FullPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = os.Chdir(wd) }()
|
||||
}
|
||||
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("submodule").
|
||||
Arg("deinit", "--force", "--", submodule.Path).ToArgv(),
|
||||
@@ -141,7 +170,7 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
||||
|
||||
// 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))
|
||||
return os.RemoveAll(submodule.GitDirPath(self.repoPaths.repoGitDirPath))
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
|
||||
@@ -158,10 +187,24 @@ func (self *SubmoduleCommands) Add(name string, path string, url string) error {
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string) error {
|
||||
func (self *SubmoduleCommands) UpdateUrl(submodule *models.SubmoduleConfig, newUrl string) error {
|
||||
if submodule.ParentModule != nil {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chdir(submodule.ParentModule.FullPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = os.Chdir(wd) }()
|
||||
}
|
||||
|
||||
setUrlCmdStr := NewGitCmd("config").
|
||||
Arg(
|
||||
"--file", ".gitmodules", "submodule."+name+".url", newUrl,
|
||||
"--file", ".gitmodules", "submodule."+submodule.Name+".url", newUrl,
|
||||
).
|
||||
ToArgv()
|
||||
|
||||
@@ -170,7 +213,7 @@ func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string
|
||||
return err
|
||||
}
|
||||
|
||||
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", path).
|
||||
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", submodule.Path).
|
||||
ToArgv()
|
||||
|
||||
if err := self.cmd.New(syncCmdStr).Run(); err != nil {
|
||||
|
||||
@@ -291,6 +291,7 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
|
||||
useExtDiff := extDiffCmd != "" && !plain
|
||||
|
||||
cmdArgs := NewGitCmd("diff").
|
||||
Config("diff.noprefix=false").
|
||||
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
|
||||
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
|
||||
Arg("--submodule").
|
||||
@@ -342,7 +343,7 @@ func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
|
||||
|
||||
// ResetAndClean removes all unstaged changes and removes all untracked files
|
||||
func (self *WorkingTreeCommands) ResetAndClean() error {
|
||||
submoduleConfigs, err := self.submodule.GetConfigs()
|
||||
submoduleConfigs, err := self.submodule.GetConfigs(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
@@ -359,7 +359,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
||||
ignoreWhitespace: false,
|
||||
contextSize: 123,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
{
|
||||
testName: "Default case (ignore whitespace)",
|
||||
@@ -370,7 +370,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
|
||||
ignoreWhitespace: true,
|
||||
contextSize: 3,
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,33 @@
|
||||
package models
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
type SubmoduleConfig struct {
|
||||
Name string
|
||||
Path string
|
||||
Url string
|
||||
|
||||
ParentModule *SubmoduleConfig // nil if top-level
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) FullName() string {
|
||||
if r.ParentModule != nil {
|
||||
return r.ParentModule.FullName() + "/" + r.Name
|
||||
}
|
||||
|
||||
return r.Name
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) FullPath() string {
|
||||
if r.ParentModule != nil {
|
||||
return r.ParentModule.FullPath() + "/" + r.Path
|
||||
}
|
||||
|
||||
return r.Path
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) RefName() string {
|
||||
return r.Name
|
||||
return r.FullName()
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) ID() string {
|
||||
@@ -17,3 +37,12 @@ func (r *SubmoduleConfig) ID() string {
|
||||
func (r *SubmoduleConfig) Description() string {
|
||||
return r.RefName()
|
||||
}
|
||||
|
||||
func (r *SubmoduleConfig) GitDirPath(repoGitDirPath string) string {
|
||||
parentPath := repoGitDirPath
|
||||
if r.ParentModule != nil {
|
||||
parentPath = r.ParentModule.GitDirPath(repoGitDirPath)
|
||||
}
|
||||
|
||||
return filepath.Join(parentPath, "modules", r.Name)
|
||||
}
|
||||
|
||||
+60
-23
@@ -6,7 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenPeeDeeP/xdg"
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
|
||||
yaml "github.com/jesseduffield/yaml"
|
||||
)
|
||||
@@ -80,6 +80,17 @@ func NewAppConfig(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Temporary: the defaults for these are set to empty strings in
|
||||
// getDefaultAppState so that we can migrate them from userConfig (which is
|
||||
// now deprecated). Once we remove the user configs, we can remove this code
|
||||
// and set the proper defaults in getDefaultAppState.
|
||||
if appState.GitLogOrder == "" {
|
||||
appState.GitLogOrder = userConfig.Git.Log.Order
|
||||
}
|
||||
if appState.GitLogShowGraph == "" {
|
||||
appState.GitLogShowGraph = userConfig.Git.Log.ShowGraph
|
||||
}
|
||||
|
||||
appConfig := &AppConfig{
|
||||
Name: name,
|
||||
Version: version,
|
||||
@@ -102,21 +113,9 @@ func isCustomConfigFile(path string) bool {
|
||||
}
|
||||
|
||||
func ConfigDir() string {
|
||||
legacyConfigDirectory := configDirForVendor("jesseduffield")
|
||||
if _, err := os.Stat(legacyConfigDirectory); !os.IsNotExist(err) {
|
||||
return legacyConfigDirectory
|
||||
}
|
||||
configDirectory := configDirForVendor("")
|
||||
return configDirectory
|
||||
}
|
||||
_, filePath := findConfigFile("config.yml")
|
||||
|
||||
func configDirForVendor(vendor string) string {
|
||||
envConfigDir := os.Getenv("CONFIG_DIR")
|
||||
if envConfigDir != "" {
|
||||
return envConfigDir
|
||||
}
|
||||
configDirs := xdg.New(vendor, "lazygit")
|
||||
return configDirs.ConfigHome()
|
||||
return filepath.Dir(filePath)
|
||||
}
|
||||
|
||||
func findOrCreateConfigDir() (string, error) {
|
||||
@@ -244,17 +243,42 @@ func (c *AppConfig) GetTempDir() string {
|
||||
return c.TempDir
|
||||
}
|
||||
|
||||
func configFilePath(filename string) (string, error) {
|
||||
folder, err := findOrCreateConfigDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
// findConfigFile looks for a possibly existing config file.
|
||||
// This function does NOT create any folders or files.
|
||||
func findConfigFile(filename string) (exists bool, path string) {
|
||||
if envConfigDir := os.Getenv("CONFIG_DIR"); envConfigDir != "" {
|
||||
return true, filepath.Join(envConfigDir, filename)
|
||||
}
|
||||
|
||||
return filepath.Join(folder, filename), nil
|
||||
// look for jesseduffield/lazygit/filename in XDG_CONFIG_HOME and XDG_CONFIG_DIRS
|
||||
legacyConfigPath, err := xdg.SearchConfigFile(filepath.Join("jesseduffield", "lazygit", filename))
|
||||
if err == nil {
|
||||
return true, legacyConfigPath
|
||||
}
|
||||
|
||||
// look for lazygit/filename in XDG_CONFIG_HOME and XDG_CONFIG_DIRS
|
||||
configFilepath, err := xdg.SearchConfigFile(filepath.Join("lazygit", filename))
|
||||
if err == nil {
|
||||
return true, configFilepath
|
||||
}
|
||||
|
||||
return false, filepath.Join(xdg.ConfigHome, "lazygit", filename)
|
||||
}
|
||||
|
||||
var ConfigFilename = "config.yml"
|
||||
|
||||
// stateFilePath looks for a possibly existing state file.
|
||||
// if none exist, the default path is returned and all parent directories are created.
|
||||
func stateFilePath(filename string) (string, error) {
|
||||
exists, legacyStateFile := findConfigFile(filename)
|
||||
if exists {
|
||||
return legacyStateFile, nil
|
||||
}
|
||||
|
||||
// looks for XDG_STATE_HOME/lazygit/filename
|
||||
return xdg.StateFile(filepath.Join("lazygit", filename))
|
||||
}
|
||||
|
||||
// ConfigFilename returns the filename of the default config file
|
||||
func (c *AppConfig) ConfigFilename() string {
|
||||
return filepath.Join(c.UserConfigDir, ConfigFilename)
|
||||
@@ -267,7 +291,7 @@ func (c *AppConfig) SaveAppState() error {
|
||||
return err
|
||||
}
|
||||
|
||||
filepath, err := configFilePath("state.yml")
|
||||
filepath, err := stateFilePath(stateFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -281,11 +305,13 @@ func (c *AppConfig) SaveAppState() error {
|
||||
return err
|
||||
}
|
||||
|
||||
var stateFileName = "state.yml"
|
||||
|
||||
// loadAppState loads recorded AppState from file
|
||||
func loadAppState() (*AppState, error) {
|
||||
appState := getDefaultAppState()
|
||||
|
||||
filepath, err := configFilePath("state.yml")
|
||||
filepath, err := stateFilePath(stateFileName)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
// apparently when people have read-only permissions they prefer us to fail silently
|
||||
@@ -325,6 +351,15 @@ type AppState struct {
|
||||
DiffContextSize int
|
||||
LocalBranchSortOrder string
|
||||
RemoteBranchSortOrder string
|
||||
|
||||
// One of: 'date-order' | 'author-date-order' | 'topo-order' | 'default'
|
||||
// 'topo-order' makes it easier to read the git log graph, but commits may not
|
||||
// appear chronologically. See https://git-scm.com/docs/
|
||||
GitLogOrder string
|
||||
|
||||
// This determines whether the git graph is rendered in the commits panel
|
||||
// One of 'always' | 'never' | 'when-maximised'
|
||||
GitLogShowGraph string
|
||||
}
|
||||
|
||||
func getDefaultAppState() *AppState {
|
||||
@@ -335,6 +370,8 @@ func getDefaultAppState() *AppState {
|
||||
DiffContextSize: 3,
|
||||
LocalBranchSortOrder: "recency",
|
||||
RemoteBranchSortOrder: "alphabetical",
|
||||
GitLogOrder: "", // should be "topo-order" eventually
|
||||
GitLogShowGraph: "", // should be "always" eventually
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,5 +380,5 @@ func LogPath() (string, error) {
|
||||
return os.Getenv("LAZYGIT_LOG_PATH"), nil
|
||||
}
|
||||
|
||||
return configFilePath("development.log")
|
||||
return stateFilePath("development.log")
|
||||
}
|
||||
|
||||
@@ -65,6 +65,13 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
|
||||
"nano": standardTerminalEditorPreset("nano"),
|
||||
"kakoune": standardTerminalEditorPreset("kak"),
|
||||
"helix": {
|
||||
editTemplate: "helix -- {{filename}}",
|
||||
editAtLineTemplate: "helix -- {{filename}}:{{line}}",
|
||||
editAtLineAndWaitTemplate: "helix -- {{filename}}:{{line}}",
|
||||
openDirInEditorTemplate: "helix -- {{dir}}",
|
||||
suspend: true,
|
||||
},
|
||||
"helix (hx)": {
|
||||
editTemplate: "hx -- {{filename}}",
|
||||
editAtLineTemplate: "hx -- {{filename}}:{{line}}",
|
||||
editAtLineAndWaitTemplate: "hx -- {{filename}}:{{line}}",
|
||||
@@ -103,11 +110,12 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
|
||||
|
||||
// Some of our presets have a different name than the editor they are using.
|
||||
editorToPreset := map[string]string{
|
||||
"kak": "kakoune",
|
||||
"hx": "helix",
|
||||
"code": "vscode",
|
||||
"subl": "sublime",
|
||||
"xed": "xcode",
|
||||
"kak": "kakoune",
|
||||
"helix": "helix",
|
||||
"hx": "helix (hx)",
|
||||
"code": "vscode",
|
||||
"subl": "sublime",
|
||||
"xed": "xcode",
|
||||
}
|
||||
|
||||
presetName := osConfig.EditPreset
|
||||
|
||||
@@ -236,6 +236,10 @@ type PagingConfig struct {
|
||||
type CommitConfig struct {
|
||||
// If true, pass '--signoff' flag when committing
|
||||
SignOff bool `yaml:"signOff"`
|
||||
// Automatic WYSIWYG wrapping of the commit message as you type
|
||||
AutoWrapCommitMessage bool `yaml:"autoWrapCommitMessage"`
|
||||
// If autoWrapCommitMessage is true, the width to wrap to
|
||||
AutoWrapWidth int `yaml:"autoWrapWidth"`
|
||||
}
|
||||
|
||||
type MergingConfig struct {
|
||||
@@ -247,13 +251,17 @@ type MergingConfig struct {
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
// One of: 'date-order' | 'author-date-order' | 'topo-order | default'
|
||||
// One of: 'date-order' | 'author-date-order' | 'topo-order' | 'default'
|
||||
// 'topo-order' makes it easier to read the git log graph, but commits may not
|
||||
// appear chronologically. See https://git-scm.com/docs/
|
||||
Order string `yaml:"order" jsonschema:"enum=date-order,enum=author-date-order,enum=topo-order,enum=default"`
|
||||
//
|
||||
// Deprecated: Configure this with `Log menu -> Commit sort order` (<c-l> in the commits window by default).
|
||||
Order string `yaml:"order" jsonschema:"deprecated,enum=date-order,enum=author-date-order,enum=topo-order,enum=default,deprecated"`
|
||||
// This determines whether the git graph is rendered in the commits panel
|
||||
// One of 'always' | 'never' | 'when-maximised'
|
||||
ShowGraph string `yaml:"showGraph" jsonschema:"enum=always,enum=never,enum=when-maximised"`
|
||||
//
|
||||
// Deprecated: Configure this with `Log menu -> Show git graph` (<c-l> in the commits window by default).
|
||||
ShowGraph string `yaml:"showGraph" jsonschema:"deprecated,enum=always,enum=never,enum=when-maximised"`
|
||||
// displays the whole git graph by default in the commits view (equivalent to passing the `--all` argument to `git log`)
|
||||
ShowWholeGraph bool `yaml:"showWholeGraph"`
|
||||
}
|
||||
@@ -654,7 +662,9 @@ func GetDefaultConfig() *UserConfig {
|
||||
ExternalDiffCommand: "",
|
||||
},
|
||||
Commit: CommitConfig{
|
||||
SignOff: false,
|
||||
SignOff: false,
|
||||
AutoWrapCommitMessage: true,
|
||||
AutoWrapWidth: 72,
|
||||
},
|
||||
Merging: MergingConfig{
|
||||
ManualCommit: false,
|
||||
@@ -662,7 +672,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
},
|
||||
Log: LogConfig{
|
||||
Order: "topo-order",
|
||||
ShowGraph: "when-maximised",
|
||||
ShowGraph: "always",
|
||||
ShowWholeGraph: false,
|
||||
},
|
||||
SkipHookPrefix: "WIP",
|
||||
|
||||
@@ -22,6 +22,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
func(branch *models.Branch) []string {
|
||||
return []string{branch.Name}
|
||||
},
|
||||
func() bool { return c.AppState.LocalBranchSortOrder != "alphabetical" },
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -16,14 +17,21 @@ type FilteredList[T any] struct {
|
||||
getFilterFields func(T) []string
|
||||
filter string
|
||||
|
||||
// Normally, filtered items are presented sorted by best match. If this
|
||||
// function returns true, they retain their original sort order instead;
|
||||
// this is useful for lists that show items sorted by date, for example.
|
||||
// Leaving this nil is equivalent to returning false.
|
||||
shouldRetainSortOrder func() bool
|
||||
|
||||
mutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string) *FilteredList[T] {
|
||||
func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string, shouldRetainSortOrder func() bool) *FilteredList[T] {
|
||||
return &FilteredList[T]{
|
||||
getList: getList,
|
||||
getFilterFields: getFilterFields,
|
||||
mutex: &deadlock.Mutex{},
|
||||
getList: getList,
|
||||
getFilterFields: getFilterFields,
|
||||
shouldRetainSortOrder: shouldRetainSortOrder,
|
||||
mutex: &deadlock.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +100,9 @@ func (self *FilteredList[T]) applyFilter() {
|
||||
self.filteredIndices = lo.Map(matches, func(match fuzzy.Match, _ int) int {
|
||||
return match.Index
|
||||
})
|
||||
if self.shouldRetainSortOrder != nil && self.shouldRetainSortOrder() {
|
||||
slices.Sort(self.filteredIndices)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ type FilteredListViewModel[T HasID] struct {
|
||||
*SearchHistory
|
||||
}
|
||||
|
||||
func NewFilteredListViewModel[T HasID](getList func() []T, getFilterFields func(T) []string) *FilteredListViewModel[T] {
|
||||
filteredList := NewFilteredList(getList, getFilterFields)
|
||||
func NewFilteredListViewModel[T HasID](getList func() []T, getFilterFields func(T) []string, shouldRetainSortOrder func() bool) *FilteredListViewModel[T] {
|
||||
filteredList := NewFilteredList(getList, getFilterFields, shouldRetainSortOrder)
|
||||
|
||||
self := &FilteredListViewModel[T]{
|
||||
FilteredList: filteredList,
|
||||
|
||||
@@ -38,14 +38,14 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
}
|
||||
|
||||
showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == enums.REBASE_MODE_REBASING
|
||||
showBranchMarkerForHeadCommit := c.Git().Config.GetRebaseUpdateRefs()
|
||||
hasRebaseUpdateRefsConfig := c.Git().Config.GetRebaseUpdateRefs()
|
||||
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
c.Common,
|
||||
c.Model().Commits,
|
||||
c.Model().Branches,
|
||||
c.Model().CheckedOutBranch,
|
||||
showBranchMarkerForHeadCommit,
|
||||
hasRebaseUpdateRefsConfig,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().CherryPicking.SelectedShaSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
@@ -156,7 +156,8 @@ func shouldShowGraph(c *ContextCommon) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
value := c.UserConfig.Git.Log.ShowGraph
|
||||
value := c.GetAppState().GitLogShowGraph
|
||||
|
||||
switch value {
|
||||
case "always":
|
||||
return true
|
||||
|
||||
@@ -61,6 +61,10 @@ func NewMenuViewModel(c *ContextCommon) *MenuViewModel {
|
||||
self.FilteredListViewModel = NewFilteredListViewModel(
|
||||
func() []*types.MenuItem { return self.menuItems },
|
||||
func(item *types.MenuItem) []string { return item.LabelColumns },
|
||||
// The only menu that the user is likely to filter in is the keybindings
|
||||
// menu; retain the sort order in that one because this allows us to
|
||||
// keep the section headers while filtering:
|
||||
func() bool { return true },
|
||||
)
|
||||
|
||||
return self
|
||||
@@ -88,33 +92,13 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
|
||||
return displayStrings
|
||||
}
|
||||
|
||||
// These keys are used for general navigation so we'll strike them out to
|
||||
// avoid confusion
|
||||
reservedKeys := []string{
|
||||
self.c.UserConfig.Keybinding.Universal.Confirm,
|
||||
self.c.UserConfig.Keybinding.Universal.Select,
|
||||
self.c.UserConfig.Keybinding.Universal.Return,
|
||||
self.c.UserConfig.Keybinding.Universal.StartSearch,
|
||||
}
|
||||
keyLabel := keybindings.LabelFromKey(item.Key)
|
||||
keyStyle := style.FgCyan
|
||||
if lo.Contains(reservedKeys, keyLabel) {
|
||||
keyStyle = style.FgDefault.SetStrikethrough()
|
||||
}
|
||||
|
||||
displayStrings = utils.Prepend(displayStrings, keyStyle.Sprint(keyLabel))
|
||||
displayStrings = utils.Prepend(displayStrings, style.FgCyan.Sprint(keyLabel))
|
||||
return displayStrings
|
||||
})
|
||||
}
|
||||
|
||||
func (self *MenuViewModel) GetNonModelItems() []*NonModelItem {
|
||||
// Don't display section headers when we are filtering. The reason is that
|
||||
// filtering changes the order of the items (they are sorted by best match),
|
||||
// so all the sections would be messed up.
|
||||
if self.FilteredListViewModel.IsFiltering() {
|
||||
return []*NonModelItem{}
|
||||
}
|
||||
|
||||
result := []*NonModelItem{}
|
||||
menuItems := self.FilteredListViewModel.GetItems()
|
||||
var prevSection *types.MenuSection = nil
|
||||
|
||||
@@ -24,6 +24,7 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
||||
func(commit *models.Commit) []string {
|
||||
return []string{commit.ShortSha(), commit.Name}
|
||||
},
|
||||
func() bool { return true },
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
|
||||
@@ -26,6 +26,7 @@ func NewRemoteBranchesContext(
|
||||
func(remoteBranch *models.RemoteBranch) []string {
|
||||
return []string{remoteBranch.Name}
|
||||
},
|
||||
func() bool { return c.AppState.RemoteBranchSortOrder != "alphabetical" },
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
|
||||
@@ -22,6 +22,7 @@ func NewRemotesContext(c *ContextCommon) *RemotesContext {
|
||||
func(remote *models.Remote) []string {
|
||||
return []string{remote.Name}
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
|
||||
@@ -24,6 +24,7 @@ func NewStashContext(
|
||||
func(stashEntry *models.StashEntry) []string {
|
||||
return []string{stashEntry.Name}
|
||||
},
|
||||
func() bool { return true },
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
|
||||
@@ -55,13 +55,13 @@ func NewSubCommitsContext(
|
||||
if viewModel.GetShowBranchHeads() {
|
||||
branches = c.Model().Branches
|
||||
}
|
||||
showBranchMarkerForHeadCommit := c.Git().Config.GetRebaseUpdateRefs()
|
||||
hasRebaseUpdateRefsConfig := c.Git().Config.GetRebaseUpdateRefs()
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
c.Common,
|
||||
c.Model().SubCommits,
|
||||
branches,
|
||||
viewModel.GetRef().RefName(),
|
||||
showBranchMarkerForHeadCommit,
|
||||
hasRebaseUpdateRefsConfig,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().CherryPicking.SelectedShaSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
|
||||
@@ -17,8 +17,9 @@ func NewSubmodulesContext(c *ContextCommon) *SubmodulesContext {
|
||||
viewModel := NewFilteredListViewModel(
|
||||
func() []*models.SubmoduleConfig { return c.Model().Submodules },
|
||||
func(submodule *models.SubmoduleConfig) []string {
|
||||
return []string{submodule.Name}
|
||||
return []string{submodule.FullName()}
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
|
||||
@@ -24,6 +24,7 @@ func NewTagsContext(
|
||||
func(tag *models.Tag) []string {
|
||||
return []string{tag.Name, tag.Message}
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
|
||||
@@ -19,6 +19,7 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext {
|
||||
func(Worktree *models.Worktree) []string {
|
||||
return []string{Worktree.Name}
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
|
||||
@@ -38,10 +38,14 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
getCommitDescription := func() string {
|
||||
return strings.TrimSpace(gui.Views.CommitDescription.TextArea.GetContent())
|
||||
}
|
||||
getUnwrappedCommitDescription := func() string {
|
||||
return strings.TrimSpace(gui.Views.CommitDescription.TextArea.GetUnwrappedContent())
|
||||
}
|
||||
commitsHelper := helpers.NewCommitsHelper(helperCommon,
|
||||
getCommitSummary,
|
||||
setCommitSummary,
|
||||
getCommitDescription,
|
||||
getUnwrappedCommitDescription,
|
||||
setCommitDescription,
|
||||
)
|
||||
|
||||
|
||||
@@ -84,9 +84,10 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
DisplayOnScreen: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.CherryPickCopy),
|
||||
Handler: self.withItem(self.copyRange),
|
||||
Description: self.c.Tr.CherryPickCopy,
|
||||
Key: opts.GetKey(opts.Config.Commits.CherryPickCopy),
|
||||
Handler: self.withItem(self.copyRange),
|
||||
GetDisabledReason: self.require(self.itemRangeSelected(self.canCopyCommits)),
|
||||
Description: self.c.Tr.CherryPickCopy,
|
||||
Tooltip: utils.ResolvePlaceholderString(self.c.Tr.CherryPickCopyTooltip,
|
||||
map[string]string{
|
||||
"paste": keybindings.Label(opts.Config.Commits.PasteCommits),
|
||||
@@ -292,6 +293,20 @@ func (self *BasicCommitsController) copyRange(*models.Commit) error {
|
||||
return self.c.Helpers().CherryPick.CopyRange(self.context.GetCommits(), self.context)
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) canCopyCommits(selectedCommits []*models.Commit, startIdx int, endIdx int) *types.DisabledReason {
|
||||
for _, commit := range selectedCommits {
|
||||
if commit.Sha == "" {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotCherryPickNonCommit, ShowErrorInPanel: true}
|
||||
}
|
||||
|
||||
if commit.IsMerge() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CannotCherryPickMergeCommit, ShowErrorInPanel: true}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) handleOldCherryPickKey() error {
|
||||
msg := utils.ResolvePlaceholderString(self.c.Tr.OldCherryPickKeyWarning,
|
||||
map[string]string{
|
||||
|
||||
@@ -3,6 +3,7 @@ package controllers
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -100,7 +101,7 @@ func (self *CommitMessageController) handleCommitIndexChange(value int) error {
|
||||
self.c.Helpers().Commits.SetMessageAndDescriptionInView(self.context().GetHistoryMessage())
|
||||
return nil
|
||||
} else if currentIndex == context.NoCommitIndex {
|
||||
self.context().SetHistoryMessage(self.c.Helpers().Commits.JoinCommitMessageAndDescription())
|
||||
self.context().SetHistoryMessage(self.c.Helpers().Commits.JoinCommitMessageAndUnwrappedDescription())
|
||||
}
|
||||
|
||||
validCommit, err := self.setCommitMessageAtIndex(newIndex)
|
||||
@@ -119,6 +120,9 @@ func (self *CommitMessageController) setCommitMessageAtIndex(index int) (bool, e
|
||||
}
|
||||
return false, self.c.ErrorMsg(self.c.Tr.CommitWithoutMessageErr)
|
||||
}
|
||||
if self.c.UserConfig.Git.Commit.AutoWrapCommitMessage {
|
||||
commitMessage = helpers.TryRemoveHardLineBreaks(commitMessage, self.c.UserConfig.Git.Commit.AutoWrapWidth)
|
||||
}
|
||||
self.c.Helpers().Commits.UpdateCommitPanelView(commitMessage)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.withItem(self.discard),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Handler: self.withItems(self.discard),
|
||||
GetDisabledReason: self.require(self.itemsSelected()),
|
||||
Description: self.c.Tr.Remove,
|
||||
Tooltip: self.c.Tr.DiscardOldFileChangeTooltip,
|
||||
DisplayOnScreen: true,
|
||||
@@ -176,40 +176,51 @@ func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error {
|
||||
func (self *CommitFilesController) discard(selectedNodes []*filetree.CommitFileNode) error {
|
||||
parentContext, ok := self.c.CurrentContext().GetParentContext()
|
||||
if !ok || parentContext.GetKey() != context.LOCAL_COMMITS_CONTEXT_KEY {
|
||||
return self.c.ErrorMsg(self.c.Tr.CanOnlyDiscardFromLocalCommits)
|
||||
}
|
||||
|
||||
if node.File == nil {
|
||||
return self.c.ErrorMsg(self.c.Tr.DiscardNotSupportedForDirectory)
|
||||
}
|
||||
|
||||
if ok, err := self.c.Helpers().PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
prompt := self.c.Tr.DiscardFileChangesPrompt
|
||||
if node.File.Added() {
|
||||
prompt = self.c.Tr.DiscardAddedFileChangesPrompt
|
||||
} else if node.File.Deleted() {
|
||||
prompt = self.c.Tr.DiscardDeletedFileChangesPrompt
|
||||
}
|
||||
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.DiscardFileChangesTitle,
|
||||
Prompt: prompt,
|
||||
Prompt: self.c.Tr.DiscardFileChangesPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DiscardOldFileChange)
|
||||
if err := self.c.Git().Rebase.DiscardOldFileChanges(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), node.GetPath()); err != nil {
|
||||
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {
|
||||
var filePaths []string
|
||||
selectedNodes = normalisedSelectedCommitFileNodes(selectedNodes)
|
||||
|
||||
// Reset the current patch if there is one.
|
||||
if self.c.Git().Patch.PatchBuilder.Active() {
|
||||
self.c.Git().Patch.PatchBuilder.Reset()
|
||||
if err := self.c.Refresh(types.RefreshOptions{Mode: types.BLOCK_UI}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.BLOCK_UI})
|
||||
for _, node := range selectedNodes {
|
||||
err := node.ForEachFile(func(file *models.CommitFile) error {
|
||||
filePaths = append(filePaths, file.GetPath())
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
err := self.c.Git().Rebase.DiscardOldFileChanges(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), filePaths)
|
||||
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if self.context().RangeSelectEnabled() {
|
||||
self.context().GetList().CancelRangeSelect()
|
||||
}
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.SYNC})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -20,15 +20,12 @@ func (self *CustomCommandAction) Call() error {
|
||||
HandleConfirm: func(command string) error {
|
||||
if self.shouldSaveCommand(command) {
|
||||
self.c.GetAppState().CustomCommandsHistory = utils.Limit(
|
||||
lo.Uniq(append(self.c.GetAppState().CustomCommandsHistory, command)),
|
||||
lo.Uniq(append([]string{command}, self.c.GetAppState().CustomCommandsHistory...)),
|
||||
1000,
|
||||
)
|
||||
}
|
||||
|
||||
err := self.c.SaveAppState()
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
self.c.SaveAppStateAndLogError()
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CustomCommand)
|
||||
return self.c.RunSubprocessAndRefresh(
|
||||
@@ -39,8 +36,7 @@ func (self *CustomCommandAction) Call() error {
|
||||
}
|
||||
|
||||
func (self *CustomCommandAction) GetCustomCommandsHistorySuggestionsFunc() func(string) []*types.Suggestion {
|
||||
// reversing so that we display the latest command first
|
||||
history := lo.Reverse(self.c.GetAppState().CustomCommandsHistory)
|
||||
history := self.c.GetAppState().CustomCommandsHistory
|
||||
|
||||
return helpers.FuzzySearchFunc(history)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type FilteringMenuAction struct {
|
||||
|
||||
func (self *FilteringMenuAction) Call() error {
|
||||
fileName := ""
|
||||
author := ""
|
||||
switch self.c.CurrentSideContext() {
|
||||
case self.c.Contexts().Files:
|
||||
node := self.c.Contexts().Files.GetSelected()
|
||||
@@ -24,16 +25,36 @@ func (self *FilteringMenuAction) Call() error {
|
||||
if node != nil {
|
||||
fileName = node.GetPath()
|
||||
}
|
||||
case self.c.Contexts().LocalCommits:
|
||||
commit := self.c.Contexts().LocalCommits.GetSelected()
|
||||
if commit != nil {
|
||||
author = fmt.Sprintf("%s <%s>", commit.AuthorName, commit.AuthorEmail)
|
||||
}
|
||||
}
|
||||
|
||||
menuItems := []*types.MenuItem{}
|
||||
tooltip := ""
|
||||
if self.c.Modes().Filtering.Active() {
|
||||
tooltip = self.c.Tr.WillCancelExistingFilterTooltip
|
||||
}
|
||||
|
||||
if fileName != "" {
|
||||
menuItems = append(menuItems, &types.MenuItem{
|
||||
Label: fmt.Sprintf("%s '%s'", self.c.Tr.FilterBy, fileName),
|
||||
OnPress: func() error {
|
||||
return self.setFiltering(fileName)
|
||||
return self.setFilteringPath(fileName)
|
||||
},
|
||||
Tooltip: tooltip,
|
||||
})
|
||||
}
|
||||
|
||||
if author != "" {
|
||||
menuItems = append(menuItems, &types.MenuItem{
|
||||
Label: fmt.Sprintf("%s '%s'", self.c.Tr.FilterBy, author),
|
||||
OnPress: func() error {
|
||||
return self.setFilteringAuthor(author)
|
||||
},
|
||||
Tooltip: tooltip,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,10 +65,25 @@ func (self *FilteringMenuAction) Call() error {
|
||||
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetFilePathSuggestionsFunc(),
|
||||
Title: self.c.Tr.EnterFileName,
|
||||
HandleConfirm: func(response string) error {
|
||||
return self.setFiltering(strings.TrimSpace(response))
|
||||
return self.setFilteringPath(strings.TrimSpace(response))
|
||||
},
|
||||
})
|
||||
},
|
||||
Tooltip: tooltip,
|
||||
})
|
||||
|
||||
menuItems = append(menuItems, &types.MenuItem{
|
||||
Label: self.c.Tr.FilterAuthorOption,
|
||||
OnPress: func() error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc(),
|
||||
Title: self.c.Tr.EnterAuthor,
|
||||
HandleConfirm: func(response string) error {
|
||||
return self.setFilteringAuthor(strings.TrimSpace(response))
|
||||
},
|
||||
})
|
||||
},
|
||||
Tooltip: tooltip,
|
||||
})
|
||||
|
||||
if self.c.Modes().Filtering.Active() {
|
||||
@@ -60,9 +96,19 @@ func (self *FilteringMenuAction) Call() error {
|
||||
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.FilteringMenuTitle, Items: menuItems})
|
||||
}
|
||||
|
||||
func (self *FilteringMenuAction) setFiltering(path string) error {
|
||||
func (self *FilteringMenuAction) setFilteringPath(path string) error {
|
||||
self.c.Modes().Filtering.Reset()
|
||||
self.c.Modes().Filtering.SetPath(path)
|
||||
return self.setFiltering()
|
||||
}
|
||||
|
||||
func (self *FilteringMenuAction) setFilteringAuthor(author string) error {
|
||||
self.c.Modes().Filtering.Reset()
|
||||
self.c.Modes().Filtering.SetAuthor(author)
|
||||
return self.setFiltering()
|
||||
}
|
||||
|
||||
func (self *FilteringMenuAction) setFiltering() error {
|
||||
repoState := self.c.State().GetRepoState()
|
||||
if repoState.GetScreenMode() == types.SCREEN_NORMAL {
|
||||
repoState.SetScreenMode(types.SCREEN_HALF)
|
||||
|
||||
@@ -31,21 +31,6 @@ func (self *CherryPickHelper) getData() *cherrypicking.CherryPicking {
|
||||
return self.c.Modes().CherryPicking
|
||||
}
|
||||
|
||||
func (self *CherryPickHelper) Copy(commit *models.Commit, commitsList []*models.Commit, context types.Context) error {
|
||||
if err := self.resetIfNecessary(context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// we will un-copy it if it's already copied
|
||||
if self.getData().SelectedShaSet().Includes(commit.Sha) {
|
||||
self.getData().Remove(commit, commitsList)
|
||||
} else {
|
||||
self.getData().Add(commit, commitsList)
|
||||
}
|
||||
|
||||
return self.rerender()
|
||||
}
|
||||
|
||||
func (self *CherryPickHelper) CopyRange(commitsList []*models.Commit, context types.IListContext) error {
|
||||
startIdx, endIdx := context.GetList().GetSelectionRange()
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
@@ -16,10 +17,11 @@ type ICommitsHelper interface {
|
||||
type CommitsHelper struct {
|
||||
c *HelperCommon
|
||||
|
||||
getCommitSummary func() string
|
||||
setCommitSummary func(string)
|
||||
getCommitDescription func() string
|
||||
setCommitDescription func(string)
|
||||
getCommitSummary func() string
|
||||
setCommitSummary func(string)
|
||||
getCommitDescription func() string
|
||||
getUnwrappedCommitDescription func() string
|
||||
setCommitDescription func(string)
|
||||
}
|
||||
|
||||
var _ ICommitsHelper = &CommitsHelper{}
|
||||
@@ -29,14 +31,16 @@ func NewCommitsHelper(
|
||||
getCommitSummary func() string,
|
||||
setCommitSummary func(string),
|
||||
getCommitDescription func() string,
|
||||
getUnwrappedCommitDescription func() string,
|
||||
setCommitDescription func(string),
|
||||
) *CommitsHelper {
|
||||
return &CommitsHelper{
|
||||
c: c,
|
||||
getCommitSummary: getCommitSummary,
|
||||
setCommitSummary: setCommitSummary,
|
||||
getCommitDescription: getCommitDescription,
|
||||
setCommitDescription: setCommitDescription,
|
||||
c: c,
|
||||
getCommitSummary: getCommitSummary,
|
||||
setCommitSummary: setCommitSummary,
|
||||
getCommitDescription: getCommitDescription,
|
||||
getUnwrappedCommitDescription: getUnwrappedCommitDescription,
|
||||
setCommitDescription: setCommitDescription,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,11 +57,36 @@ func (self *CommitsHelper) SetMessageAndDescriptionInView(message string) {
|
||||
self.c.Contexts().CommitMessage.RenderCommitLength()
|
||||
}
|
||||
|
||||
func (self *CommitsHelper) JoinCommitMessageAndDescription() string {
|
||||
if len(self.getCommitDescription()) == 0 {
|
||||
func (self *CommitsHelper) JoinCommitMessageAndUnwrappedDescription() string {
|
||||
if len(self.getUnwrappedCommitDescription()) == 0 {
|
||||
return self.getCommitSummary()
|
||||
}
|
||||
return self.getCommitSummary() + "\n" + self.getCommitDescription()
|
||||
return self.getCommitSummary() + "\n" + self.getUnwrappedCommitDescription()
|
||||
}
|
||||
|
||||
func TryRemoveHardLineBreaks(message string, autoWrapWidth int) string {
|
||||
messageRunes := []rune(message)
|
||||
lastHardLineStart := 0
|
||||
for i, r := range messageRunes {
|
||||
if r == '\n' {
|
||||
// Try to make this a soft linebreak by turning it into a space, and
|
||||
// checking whether it still wraps to the same result then.
|
||||
messageRunes[i] = ' '
|
||||
|
||||
_, cursorMapping := gocui.AutoWrapContent(messageRunes[lastHardLineStart:], autoWrapWidth)
|
||||
|
||||
// Look at the cursorMapping to check whether auto-wrapping inserted
|
||||
// a line break. If it did, there will be a cursorMapping entry with
|
||||
// Orig pointing to the position after the inserted line break.
|
||||
if len(cursorMapping) == 0 || cursorMapping[0].Orig != i-lastHardLineStart+1 {
|
||||
// It didn't, so change it back to a newline
|
||||
messageRunes[i] = '\n'
|
||||
}
|
||||
lastHardLineStart = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
return string(messageRunes)
|
||||
}
|
||||
|
||||
func (self *CommitsHelper) SwitchToEditor() error {
|
||||
@@ -154,7 +183,7 @@ func (self *CommitsHelper) HandleCommitConfirm() error {
|
||||
|
||||
func (self *CommitsHelper) CloseCommitMessagePanel() error {
|
||||
if self.c.Contexts().CommitMessage.GetPreserveMessage() {
|
||||
message := self.JoinCommitMessageAndDescription()
|
||||
message := self.JoinCommitMessageAndUnwrappedDescription()
|
||||
|
||||
self.c.Contexts().CommitMessage.SetPreservedMessage(message)
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTryRemoveHardLineBreaks(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
message string
|
||||
autoWrapWidth int
|
||||
expectedResult string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
message: "",
|
||||
autoWrapWidth: 7,
|
||||
expectedResult: "",
|
||||
},
|
||||
{
|
||||
name: "all line breaks are needed",
|
||||
message: "abc\ndef\n\nxyz",
|
||||
autoWrapWidth: 7,
|
||||
expectedResult: "abc\ndef\n\nxyz",
|
||||
},
|
||||
{
|
||||
name: "some can be unwrapped",
|
||||
message: "123\nabc def\nghi jkl\nmno\n456\n",
|
||||
autoWrapWidth: 7,
|
||||
expectedResult: "123\nabc def ghi jkl mno\n456\n",
|
||||
},
|
||||
}
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
actualResult := TryRemoveHardLineBreaks(s.message, s.autoWrapWidth)
|
||||
assert.Equal(t, s.expectedResult, actualResult)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -72,11 +72,12 @@ func (self *ModeHelper) Statuses() []ModeStatus {
|
||||
{
|
||||
IsActive: self.c.Modes().Filtering.Active,
|
||||
Description: func() string {
|
||||
filterContent := lo.Ternary(self.c.Modes().Filtering.GetPath() != "", self.c.Modes().Filtering.GetPath(), self.c.Modes().Filtering.GetAuthor())
|
||||
return self.withResetButton(
|
||||
fmt.Sprintf(
|
||||
"%s '%s'",
|
||||
self.c.Tr.FilteringBy,
|
||||
self.c.Modes().Filtering.GetPath(),
|
||||
filterContent,
|
||||
),
|
||||
style.FgRed,
|
||||
)
|
||||
|
||||
@@ -79,9 +79,6 @@ func (self *PatchBuildingHelper) RefreshPatchBuildingPanel(opts types.OnFocusOpt
|
||||
}
|
||||
|
||||
secondaryDiff := self.c.Git().Patch.PatchBuilder.RenderPatchForFile(path, false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context := self.c.Contexts().CustomPatchBuilder
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ func (self *RefreshHelper) refreshCommitsAndCommitFiles() {
|
||||
// or perhaps we could just pop that context off the stack whenever cycling windows.
|
||||
// For now the awkwardness remains.
|
||||
commit := self.c.Contexts().LocalCommits.GetSelected()
|
||||
if commit != nil {
|
||||
if commit != nil && commit.RefName() != "" {
|
||||
self.c.Contexts().CommitFiles.SetRef(commit)
|
||||
self.c.Contexts().CommitFiles.SetTitleRef(commit.RefName())
|
||||
_ = self.refreshCommitFilesContext()
|
||||
@@ -317,6 +317,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error {
|
||||
git_commands.GetCommitsOptions{
|
||||
Limit: self.c.Contexts().LocalCommits.GetLimitCommits(),
|
||||
FilterPath: self.c.Modes().Filtering.GetPath(),
|
||||
FilterAuthor: self.c.Modes().Filtering.GetAuthor(),
|
||||
IncludeRebaseCommits: true,
|
||||
RefName: self.refForLog(),
|
||||
RefForPushedStatus: checkedOutBranchName,
|
||||
@@ -342,6 +343,7 @@ func (self *RefreshHelper) refreshSubCommitsWithLimit() error {
|
||||
git_commands.GetCommitsOptions{
|
||||
Limit: self.c.Contexts().SubCommits.GetLimitCommits(),
|
||||
FilterPath: self.c.Modes().Filtering.GetPath(),
|
||||
FilterAuthor: self.c.Modes().Filtering.GetAuthor(),
|
||||
IncludeRebaseCommits: false,
|
||||
RefName: self.c.Contexts().SubCommits.GetRef().FullRefName(),
|
||||
RefToShowDivergenceFrom: self.c.Contexts().SubCommits.GetRefToShowDivergenceFrom(),
|
||||
@@ -413,7 +415,7 @@ func (self *RefreshHelper) refreshTags() error {
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshStateSubmoduleConfigs() error {
|
||||
configs, err := self.c.Git().Submodule.GetConfigs()
|
||||
configs, err := self.c.Git().Submodule.GetConfigs(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -438,7 +440,7 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele
|
||||
// which allows us to order them correctly. So if we're filtering we'll just
|
||||
// manually load all the reflog commits here
|
||||
var err error
|
||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "")
|
||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "", "")
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
@@ -597,9 +599,9 @@ func (self *RefreshHelper) refreshReflogCommits() error {
|
||||
lastReflogCommit = model.ReflogCommits[0]
|
||||
}
|
||||
|
||||
refresh := func(stateCommits *[]*models.Commit, filterPath string) error {
|
||||
refresh := func(stateCommits *[]*models.Commit, filterPath string, filterAuthor string) error {
|
||||
commits, onlyObtainedNewReflogCommits, err := self.c.Git().Loaders.ReflogCommitLoader.
|
||||
GetReflogCommits(lastReflogCommit, filterPath)
|
||||
GetReflogCommits(lastReflogCommit, filterPath, filterAuthor)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
@@ -612,12 +614,12 @@ func (self *RefreshHelper) refreshReflogCommits() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := refresh(&model.ReflogCommits, ""); err != nil {
|
||||
if err := refresh(&model.ReflogCommits, "", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if self.c.Modes().Filtering.Active() {
|
||||
if err := refresh(&model.FilteredReflogCommits, self.c.Modes().Filtering.GetPath()); err != nil {
|
||||
if err := refresh(&model.FilteredReflogCommits, self.c.Modes().Filtering.GetPath(), self.c.Modes().Filtering.GetAuthor()); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -166,12 +166,13 @@ func (self *RefsHelper) CreateGitResetMenu(ref string) error {
|
||||
strength string
|
||||
label string
|
||||
key types.Key
|
||||
tooltip string
|
||||
}
|
||||
strengths := []strengthWithKey{
|
||||
// not i18'ing because it's git terminology
|
||||
{strength: "soft", label: "Soft reset", key: 's'},
|
||||
{strength: "mixed", label: "Mixed reset", key: 'm'},
|
||||
{strength: "hard", label: "Hard reset", key: 'h'},
|
||||
{strength: "mixed", label: "Mixed reset", key: 'm', tooltip: self.c.Tr.ResetMixedTooltip},
|
||||
{strength: "soft", label: "Soft reset", key: 's', tooltip: self.c.Tr.ResetSoftTooltip},
|
||||
{strength: "hard", label: "Hard reset", key: 'h', tooltip: self.c.Tr.ResetHardTooltip},
|
||||
}
|
||||
|
||||
menuItems := lo.Map(strengths, func(row strengthWithKey, _ int) *types.MenuItem {
|
||||
@@ -184,7 +185,8 @@ func (self *RefsHelper) CreateGitResetMenu(ref string) error {
|
||||
self.c.LogAction("Reset")
|
||||
return self.ResetToRef(ref, row.strength, []string{})
|
||||
},
|
||||
Key: row.key,
|
||||
Key: row.key,
|
||||
Tooltip: row.tooltip,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ func (self *ReposHelper) EnterSubmodule(submodule *models.SubmoduleConfig) error
|
||||
}
|
||||
self.c.State().GetRepoPathStack().Push(wd)
|
||||
|
||||
return self.DispatchSwitchToRepo(submodule.Path, context.NO_CONTEXT)
|
||||
return self.DispatchSwitchToRepo(submodule.FullPath(), context.NO_CONTEXT)
|
||||
}
|
||||
|
||||
func (self *ReposHelper) getCurrentBranch(path string) string {
|
||||
|
||||
@@ -39,6 +39,7 @@ func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error {
|
||||
git_commands.GetCommitsOptions{
|
||||
Limit: true,
|
||||
FilterPath: self.c.Modes().Filtering.GetPath(),
|
||||
FilterAuthor: self.c.Modes().Filtering.GetAuthor(),
|
||||
IncludeRebaseCommits: false,
|
||||
RefName: opts.Ref.FullRefName(),
|
||||
RefForPushedStatus: opts.Ref.FullRefName(),
|
||||
|
||||
@@ -2,6 +2,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/go-errors/errors"
|
||||
@@ -115,7 +116,6 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
{
|
||||
Key: opts.GetKey(editCommitKey),
|
||||
Handler: self.withItems(self.edit),
|
||||
// TODO: have disabled reason ensure that if we're not rebasing, we only select one commit
|
||||
GetDisabledReason: self.require(
|
||||
self.itemRangeSelected(self.midRebaseCommandEnabled),
|
||||
),
|
||||
@@ -354,7 +354,9 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
if self.c.UserConfig.Git.Commit.AutoWrapCommitMessage {
|
||||
commitMessage = helpers.TryRemoveHardLineBreaks(commitMessage, self.c.UserConfig.Git.Commit.AutoWrapWidth)
|
||||
}
|
||||
return self.c.Helpers().Commits.OpenCommitMessagePanel(
|
||||
&helpers.OpenCommitMessagePanelOpts{
|
||||
CommitIndex: self.context().GetSelectedLineIdx(),
|
||||
@@ -457,15 +459,7 @@ func (self *LocalCommitsController) edit(selectedCommits []*models.Commit) error
|
||||
return self.updateTodos(todo.Edit, selectedCommits)
|
||||
}
|
||||
|
||||
// TODO: support range select here (start a rebase and set the selected commits
|
||||
// to 'edit' in the todo file)
|
||||
if len(selectedCommits) > 1 {
|
||||
return self.c.ErrorMsg(self.c.Tr.RangeSelectNotSupported)
|
||||
}
|
||||
|
||||
selectedCommit := selectedCommits[0]
|
||||
|
||||
return self.startInteractiveRebaseWithEdit(selectedCommit)
|
||||
return self.startInteractiveRebaseWithEdit(selectedCommits)
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) quickStartInteractiveRebase() error {
|
||||
@@ -474,11 +468,11 @@ func (self *LocalCommitsController) quickStartInteractiveRebase() error {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.startInteractiveRebaseWithEdit(commitToEdit)
|
||||
return self.startInteractiveRebaseWithEdit([]*models.Commit{commitToEdit})
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) startInteractiveRebaseWithEdit(
|
||||
commitToEdit *models.Commit,
|
||||
commitsToEdit []*models.Commit,
|
||||
) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.EditCommit)
|
||||
@@ -486,10 +480,24 @@ func (self *LocalCommitsController) startInteractiveRebaseWithEdit(
|
||||
commits := self.c.Model().Commits
|
||||
selectedSha := commits[selectedIdx].Sha
|
||||
rangeStartSha := commits[rangeStartIdx].Sha
|
||||
err := self.c.Git().Rebase.EditRebase(commitToEdit.Sha)
|
||||
err := self.c.Git().Rebase.EditRebase(commitsToEdit[len(commitsToEdit)-1].Sha)
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
|
||||
err,
|
||||
types.RefreshOptions{Mode: types.BLOCK_UI, Then: func() {
|
||||
todos := make([]*models.Commit, 0, len(commitsToEdit)-1)
|
||||
for _, c := range commitsToEdit[:len(commitsToEdit)-1] {
|
||||
// Merge commits can't be set to "edit", so just skip them
|
||||
if !c.IsMerge() {
|
||||
todos = append(todos, &models.Commit{Sha: c.Sha, Action: todo.Pick})
|
||||
}
|
||||
}
|
||||
if len(todos) > 0 {
|
||||
err := self.updateTodos(todo.Edit, todos)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// We need to select the same commit range again because after starting a rebase,
|
||||
// new lines can be added for update-ref commands in the TODO file, due to
|
||||
// stacked branches. So the selected commits may be in different positions in the list.
|
||||
@@ -806,11 +814,14 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err
|
||||
HandleConfirm: func() error {
|
||||
return self.c.Helpers().WorkingTree.WithEnsureCommitableFiles(func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CreateFixupCommit)
|
||||
if err := self.c.Git().Commit.CreateFixupCommit(commit.Sha); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
return self.c.WithWaitingStatusSync(self.c.Tr.CreatingFixupCommitStatus, func() error {
|
||||
if err := self.c.Git().Commit.CreateFixupCommit(commit.Sha); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
self.context().MoveSelectedLine(1)
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.SYNC})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -839,37 +850,89 @@ func (self *LocalCommitsController) squashFixupCommits() error {
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) squashAllFixupsAboveSelectedCommit(commit *models.Commit) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
|
||||
err := self.c.Git().Rebase.SquashAllAboveFixupCommits(commit)
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||
})
|
||||
return self.squashFixupsImpl(commit, self.context().GetSelectedLineIdx())
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) squashAllFixupsInCurrentBranch() error {
|
||||
commit, err := self.findCommitForSquashFixupsInCurrentBranch()
|
||||
commit, rebaseStartIdx, err := self.findCommitForSquashFixupsInCurrentBranch()
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func(gocui.Task) error {
|
||||
return self.squashFixupsImpl(commit, rebaseStartIdx)
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) squashFixupsImpl(commit *models.Commit, rebaseStartIdx int) error {
|
||||
selectionOffset := countSquashableCommitsAbove(self.c.Model().Commits, self.context().GetSelectedLineIdx(), rebaseStartIdx)
|
||||
return self.c.WithWaitingStatusSync(self.c.Tr.SquashingStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
|
||||
err := self.c.Git().Rebase.SquashAllAboveFixupCommits(commit)
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||
self.context().MoveSelectedLine(-selectionOffset)
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
|
||||
err, types.RefreshOptions{Mode: types.SYNC})
|
||||
})
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) findCommitForSquashFixupsInCurrentBranch() (*models.Commit, error) {
|
||||
func (self *LocalCommitsController) findCommitForSquashFixupsInCurrentBranch() (*models.Commit, int, error) {
|
||||
commits := self.c.Model().Commits
|
||||
_, index, ok := lo.FindIndexOf(commits, func(c *models.Commit) bool {
|
||||
return c.IsMerge() || c.Status == models.StatusMerged
|
||||
})
|
||||
|
||||
if !ok || index == 0 {
|
||||
return nil, errors.New(self.c.Tr.CannotSquashCommitsInCurrentBranch)
|
||||
return nil, -1, errors.New(self.c.Tr.CannotSquashCommitsInCurrentBranch)
|
||||
}
|
||||
|
||||
return commits[index-1], nil
|
||||
return commits[index-1], index - 1, nil
|
||||
}
|
||||
|
||||
// Anticipate how many commits above the selectedIdx are going to get squashed
|
||||
// by the SquashAllAboveFixupCommits call, so that we can adjust the selection
|
||||
// afterwards. Let's hope we're matching git's behavior correctly here.
|
||||
func countSquashableCommitsAbove(commits []*models.Commit, selectedIdx int, rebaseStartIdx int) int {
|
||||
result := 0
|
||||
|
||||
// For each commit _above_ the selection, ...
|
||||
for i, commit := range commits[0:selectedIdx] {
|
||||
// ... see if it is a fixup commit, and get the base subject it applies to
|
||||
if baseSubject, isFixup := isFixupCommit(commit.Name); isFixup {
|
||||
// Then, for each commit after the fixup, up to and including the
|
||||
// rebase start commit, see if we find the base commit
|
||||
for _, baseCommit := range commits[i+1 : rebaseStartIdx+1] {
|
||||
if strings.HasPrefix(baseCommit.Name, baseSubject) {
|
||||
result++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Check whether the given subject line is the subject of a fixup commit, and
|
||||
// returns (trimmedSubject, true) if so (where trimmedSubject is the subject
|
||||
// with all fixup prefixes removed), or (subject, false) if not.
|
||||
func isFixupCommit(subject string) (string, bool) {
|
||||
prefixes := []string{"fixup! ", "squash! ", "amend! "}
|
||||
trimPrefix := func(s string) (string, bool) {
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
return strings.TrimPrefix(s, prefix), true
|
||||
}
|
||||
}
|
||||
return s, false
|
||||
}
|
||||
|
||||
if subject, wasTrimmed := trimPrefix(subject); wasTrimmed {
|
||||
for {
|
||||
// handle repeated prefixes like "fixup! amend! fixup! Subject"
|
||||
if subject, wasTrimmed = trimPrefix(subject); !wasTrimmed {
|
||||
break
|
||||
}
|
||||
}
|
||||
return subject, true
|
||||
}
|
||||
|
||||
return subject, false
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) createTag(commit *models.Commit) error {
|
||||
@@ -928,8 +991,12 @@ func (self *LocalCommitsController) handleOpenLogMenu() error {
|
||||
OnPress: func() error {
|
||||
onPress := func(value string) func() error {
|
||||
return func() error {
|
||||
self.c.UserConfig.Git.Log.ShowGraph = value
|
||||
return nil
|
||||
self.c.GetAppState().GitLogShowGraph = value
|
||||
self.c.SaveAppStateAndLogError()
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits); err != nil {
|
||||
return err
|
||||
}
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
|
||||
}
|
||||
}
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
@@ -957,7 +1024,8 @@ func (self *LocalCommitsController) handleOpenLogMenu() error {
|
||||
OnPress: func() error {
|
||||
onPress := func(value string) func() error {
|
||||
return func() error {
|
||||
self.c.UserConfig.Git.Log.Order = value
|
||||
self.c.GetAppState().GitLogOrder = value
|
||||
self.c.SaveAppStateAndLogError()
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LoadingCommits, func(gocui.Task) error {
|
||||
return self.c.Refresh(
|
||||
types.RefreshOptions{
|
||||
@@ -1057,7 +1125,7 @@ func (self *LocalCommitsController) canFindCommitForQuickStart() *types.Disabled
|
||||
}
|
||||
|
||||
func (self *LocalCommitsController) canFindCommitForSquashFixupsInCurrentBranch() *types.DisabledReason {
|
||||
if _, err := self.findCommitForSquashFixupsInCurrentBranch(); err != nil {
|
||||
if _, _, err := self.findCommitForSquashFixupsInCurrentBranch(); err != nil {
|
||||
return &types.DisabledReason{Text: err.Error()}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_countSquashableCommitsAbove(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
commits []*models.Commit
|
||||
selectedIdx int
|
||||
rebaseStartIdx int
|
||||
expectedResult int
|
||||
}{
|
||||
{
|
||||
name: "no squashable commits",
|
||||
commits: []*models.Commit{
|
||||
{Name: "abc"},
|
||||
{Name: "def"},
|
||||
{Name: "ghi"},
|
||||
},
|
||||
selectedIdx: 2,
|
||||
rebaseStartIdx: 2,
|
||||
expectedResult: 0,
|
||||
},
|
||||
{
|
||||
name: "some squashable commits, including for the selected commit",
|
||||
commits: []*models.Commit{
|
||||
{Name: "fixup! def"},
|
||||
{Name: "fixup! ghi"},
|
||||
{Name: "abc"},
|
||||
{Name: "def"},
|
||||
{Name: "ghi"},
|
||||
},
|
||||
selectedIdx: 4,
|
||||
rebaseStartIdx: 4,
|
||||
expectedResult: 2,
|
||||
},
|
||||
{
|
||||
name: "base commit is below rebase start",
|
||||
commits: []*models.Commit{
|
||||
{Name: "fixup! def"},
|
||||
{Name: "abc"},
|
||||
{Name: "def"},
|
||||
},
|
||||
selectedIdx: 1,
|
||||
rebaseStartIdx: 1,
|
||||
expectedResult: 0,
|
||||
},
|
||||
{
|
||||
name: "base commit does not exist at all",
|
||||
commits: []*models.Commit{
|
||||
{Name: "fixup! xyz"},
|
||||
{Name: "abc"},
|
||||
{Name: "def"},
|
||||
},
|
||||
selectedIdx: 2,
|
||||
rebaseStartIdx: 2,
|
||||
expectedResult: 0,
|
||||
},
|
||||
{
|
||||
name: "selected commit is in the middle of fixups",
|
||||
commits: []*models.Commit{
|
||||
{Name: "fixup! def"},
|
||||
{Name: "abc"},
|
||||
{Name: "fixup! ghi"},
|
||||
{Name: "def"},
|
||||
{Name: "ghi"},
|
||||
},
|
||||
selectedIdx: 1,
|
||||
rebaseStartIdx: 4,
|
||||
expectedResult: 1,
|
||||
},
|
||||
{
|
||||
name: "selected commit is after rebase start",
|
||||
commits: []*models.Commit{
|
||||
{Name: "fixup! def"},
|
||||
{Name: "abc"},
|
||||
{Name: "def"},
|
||||
{Name: "ghi"},
|
||||
},
|
||||
selectedIdx: 3,
|
||||
rebaseStartIdx: 2,
|
||||
expectedResult: 1,
|
||||
},
|
||||
}
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
assert.Equal(t, s.expectedResult, countSquashableCommitsAbove(s.commits, s.selectedIdx, s.rebaseStartIdx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isFixupCommit(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
subject string
|
||||
expectedTrimmedSubject string
|
||||
expectedIsFixup bool
|
||||
}{
|
||||
{
|
||||
subject: "Bla",
|
||||
expectedTrimmedSubject: "Bla",
|
||||
expectedIsFixup: false,
|
||||
},
|
||||
{
|
||||
subject: "fixup Bla",
|
||||
expectedTrimmedSubject: "fixup Bla",
|
||||
expectedIsFixup: false,
|
||||
},
|
||||
{
|
||||
subject: "fixup! Bla",
|
||||
expectedTrimmedSubject: "Bla",
|
||||
expectedIsFixup: true,
|
||||
},
|
||||
{
|
||||
subject: "fixup! fixup! Bla",
|
||||
expectedTrimmedSubject: "Bla",
|
||||
expectedIsFixup: true,
|
||||
},
|
||||
{
|
||||
subject: "amend! squash! Bla",
|
||||
expectedTrimmedSubject: "Bla",
|
||||
expectedIsFixup: true,
|
||||
},
|
||||
{
|
||||
subject: "fixup!",
|
||||
expectedTrimmedSubject: "fixup!",
|
||||
expectedIsFixup: false,
|
||||
},
|
||||
}
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.subject, func(t *testing.T) {
|
||||
trimmedSubject, isFixupCommit := isFixupCommit(s.subject)
|
||||
assert.Equal(t, s.expectedTrimmedSubject, trimmedSubject)
|
||||
assert.Equal(t, s.expectedIsFixup, isFixupCommit)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,12 @@ func (self *StagingController) GetKeybindings(opts types.KeybindingsOpts) []*typ
|
||||
Handler: self.c.Helpers().WorkingTree.HandleCommitEditorPress,
|
||||
Description: self.c.Tr.CommitChangesWithEditor,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.FindBaseCommitForFixup),
|
||||
Handler: self.c.Helpers().FixupHelper.HandleFindBaseCommitForFixupPress,
|
||||
Description: self.c.Tr.FindBaseCommitForFixup,
|
||||
Tooltip: self.c.Tr.FindBaseCommitForFixupTooltip,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,8 +116,8 @@ func (self *SubmodulesController) GetOnRenderToMain() func() error {
|
||||
} else {
|
||||
prefix := fmt.Sprintf(
|
||||
"Name: %s\nPath: %s\nUrl: %s\n\n",
|
||||
style.FgGreen.Sprint(submodule.Name),
|
||||
style.FgYellow.Sprint(submodule.Path),
|
||||
style.FgGreen.Sprint(submodule.FullName()),
|
||||
style.FgYellow.Sprint(submodule.FullPath()),
|
||||
style.FgCyan.Sprint(submodule.Url),
|
||||
)
|
||||
|
||||
@@ -178,12 +178,12 @@ func (self *SubmodulesController) add() error {
|
||||
|
||||
func (self *SubmodulesController) editURL(submodule *models.SubmoduleConfig) error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: fmt.Sprintf(self.c.Tr.UpdateSubmoduleUrl, submodule.Name),
|
||||
Title: fmt.Sprintf(self.c.Tr.UpdateSubmoduleUrl, submodule.FullName()),
|
||||
InitialContent: submodule.Url,
|
||||
HandleConfirm: func(newUrl string) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.UpdatingSubmoduleUrlStatus, func(gocui.Task) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.UpdateSubmoduleUrl)
|
||||
err := self.c.Git().Submodule.UpdateUrl(submodule.Name, submodule.Path, newUrl)
|
||||
err := self.c.Git().Submodule.UpdateUrl(submodule, newUrl)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
@@ -272,7 +272,7 @@ func (self *SubmodulesController) update(submodule *models.SubmoduleConfig) erro
|
||||
func (self *SubmodulesController) remove(submodule *models.SubmoduleConfig) error {
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.RemoveSubmodule,
|
||||
Prompt: fmt.Sprintf(self.c.Tr.RemoveSubmodulePrompt, submodule.Name),
|
||||
Prompt: fmt.Sprintf(self.c.Tr.RemoveSubmodulePrompt, submodule.FullName()),
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RemoveSubmodule)
|
||||
if err := self.c.Git().Submodule.Delete(submodule); err != nil {
|
||||
|
||||
@@ -51,7 +51,7 @@ func (self *SwitchToDiffFilesController) GetKeybindings(opts types.KeybindingsOp
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.GoInto),
|
||||
Handler: self.withItem(self.enter),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
GetDisabledReason: self.require(self.singleItemSelected(self.itemRepresentsCommit)),
|
||||
Description: self.c.Tr.ViewItemFiles,
|
||||
},
|
||||
}
|
||||
@@ -91,3 +91,11 @@ func (self *SwitchToDiffFilesController) viewFiles(opts SwitchToCommitFilesConte
|
||||
|
||||
return self.c.PushContext(diffFilesContext)
|
||||
}
|
||||
|
||||
func (self *SwitchToDiffFilesController) itemRepresentsCommit(ref types.Ref) *types.DisabledReason {
|
||||
if ref.RefName() == "" {
|
||||
return &types.DisabledReason{Text: self.c.Tr.SelectedItemDoesNotHaveFiles}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -386,7 +386,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
||||
Authors: map[string]*models.Author{},
|
||||
},
|
||||
Modes: &types.Modes{
|
||||
Filtering: filtering.New(startArgs.FilterPath),
|
||||
Filtering: filtering.New(startArgs.FilterPath, ""),
|
||||
CherryPicking: cherrypicking.New(),
|
||||
Diffing: diffing.New(),
|
||||
MarkedBaseCommit: marked_base_commit.New(),
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
package filtering
|
||||
|
||||
type Filtering struct {
|
||||
path string // the filename that gets passed to git log
|
||||
path string // the filename that gets passed to git log
|
||||
author string // the author that gets passed to git log
|
||||
}
|
||||
|
||||
func New(path string) Filtering {
|
||||
return Filtering{path: path}
|
||||
func New(path string, author string) Filtering {
|
||||
return Filtering{path: path, author: author}
|
||||
}
|
||||
|
||||
func (m *Filtering) Active() bool {
|
||||
return m.path != ""
|
||||
return m.path != "" || m.author != ""
|
||||
}
|
||||
|
||||
func (m *Filtering) Reset() {
|
||||
m.path = ""
|
||||
m.author = ""
|
||||
}
|
||||
|
||||
func (m *Filtering) SetPath(path string) {
|
||||
@@ -23,3 +25,11 @@ func (m *Filtering) SetPath(path string) {
|
||||
func (m *Filtering) GetPath() string {
|
||||
return m.path
|
||||
}
|
||||
|
||||
func (m *Filtering) SetAuthor(author string) {
|
||||
m.author = author
|
||||
}
|
||||
|
||||
func (m *Filtering) GetAuthor() string {
|
||||
return m.author
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func GetCommitListDisplayStrings(
|
||||
commits []*models.Commit,
|
||||
branches []*models.Branch,
|
||||
currentBranchName string,
|
||||
showBranchMarkerForHeadCommit bool,
|
||||
hasRebaseUpdateRefsConfig bool,
|
||||
fullDescription bool,
|
||||
cherryPickedCommitShaSet *set.Set[string],
|
||||
diffName string,
|
||||
@@ -99,7 +99,7 @@ func GetCommitListDisplayStrings(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getGraphLine = func(idx int) string { return "" }
|
||||
getGraphLine = func(int) string { return "" }
|
||||
}
|
||||
|
||||
// Determine the hashes of the local branches for which we want to show a
|
||||
@@ -123,7 +123,7 @@ func GetCommitListDisplayStrings(
|
||||
!lo.Contains(common.UserConfig.Git.MainBranches, b.Name) &&
|
||||
// Don't show a marker for the head commit unless the
|
||||
// rebase.updateRefs config is on
|
||||
(showBranchMarkerForHeadCommit || b.CommitHash != commits[0].Sha)
|
||||
(hasRebaseUpdateRefsConfig || b.CommitHash != commits[0].Sha)
|
||||
}))
|
||||
|
||||
lines := make([][]string, 0, len(filteredCommits))
|
||||
@@ -145,6 +145,7 @@ func GetCommitListDisplayStrings(
|
||||
common,
|
||||
commit,
|
||||
branchHeadsToVisualize,
|
||||
hasRebaseUpdateRefsConfig,
|
||||
cherryPickedCommitShaSet,
|
||||
isMarkedBaseCommit,
|
||||
willBeRebased,
|
||||
@@ -296,6 +297,7 @@ func displayCommit(
|
||||
common *common.Common,
|
||||
commit *models.Commit,
|
||||
branchHeadsToVisualize *set.Set[string],
|
||||
hasRebaseUpdateRefsConfig bool,
|
||||
cherryPickedCommitShaSet *set.Set[string],
|
||||
isMarkedBaseCommit bool,
|
||||
willBeRebased bool,
|
||||
@@ -329,7 +331,11 @@ func displayCommit(
|
||||
tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(commit.Tags, " ")) + " "
|
||||
}
|
||||
|
||||
if branchHeadsToVisualize.Includes(commit.Sha) && commit.Status != models.StatusMerged {
|
||||
if branchHeadsToVisualize.Includes(commit.Sha) &&
|
||||
// Don't show branch head on commits that are already merged to a main branch
|
||||
commit.Status != models.StatusMerged &&
|
||||
// Don't show branch head on a "pick" todo if the rebase.updateRefs config is on
|
||||
!(commit.IsTODO() && hasRebaseUpdateRefsConfig) {
|
||||
tagString = style.FgCyan.SetBold().Sprint(
|
||||
lo.Ternary(icons.IsIconEnabled(), icons.BRANCH_ICON, "*") + " " + tagString)
|
||||
}
|
||||
|
||||
@@ -13,5 +13,15 @@ func GetSubmoduleListDisplayStrings(submodules []*models.SubmoduleConfig) [][]st
|
||||
}
|
||||
|
||||
func getSubmoduleDisplayStrings(s *models.SubmoduleConfig) []string {
|
||||
return []string{theme.DefaultTextColor.Sprint(s.Name)}
|
||||
name := s.Name
|
||||
if s.ParentModule != nil {
|
||||
indentation := ""
|
||||
for p := s.ParentModule; p != nil; p = p.ParentModule {
|
||||
indentation += " "
|
||||
}
|
||||
|
||||
name = indentation + "- " + s.Name
|
||||
}
|
||||
|
||||
return []string{theme.DefaultTextColor.Sprint(name)}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func (gui *Gui) linesToReadFromCmdTask(v *gocui.View) tasks.LinesToRead {
|
||||
// We want to read as many lines initially as necessary to let the
|
||||
// scrollbar go to its minimum height, so that the scrollbar thumb doesn't
|
||||
// change size as you scroll down.
|
||||
minScrollbarHeight := 2
|
||||
minScrollbarHeight := 1
|
||||
linesToReadForAccurateScrollbar := height*(height-1)/minScrollbarHeight + oy
|
||||
|
||||
// However, cap it at some arbitrary max limit, so that we don't get
|
||||
|
||||
@@ -168,6 +168,8 @@ func (gui *Gui) createAllViews() error {
|
||||
gui.Views.CommitDescription.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.CommitDescription.Editable = true
|
||||
gui.Views.CommitDescription.Editor = gocui.EditorFunc(gui.commitDescriptionEditor)
|
||||
gui.Views.CommitDescription.TextArea.AutoWrap = gui.c.UserConfig.Git.Commit.AutoWrapCommitMessage
|
||||
gui.Views.CommitDescription.TextArea.AutoWrapWidth = gui.c.UserConfig.Git.Commit.AutoWrapWidth
|
||||
|
||||
gui.Views.Confirmation.Visible = false
|
||||
gui.Views.Confirmation.Editor = gocui.EditorFunc(gui.promptEditor)
|
||||
|
||||
+27
-10
@@ -302,6 +302,8 @@ type TranslationSet struct {
|
||||
PasteCommits string
|
||||
SureCherryPick string
|
||||
CherryPick string
|
||||
CannotCherryPickNonCommit string
|
||||
CannotCherryPickMergeCommit string
|
||||
Donate string
|
||||
AskQuestion string
|
||||
PrevLine string
|
||||
@@ -339,6 +341,7 @@ type TranslationSet struct {
|
||||
CheckingOutStatus string
|
||||
CommittingStatus string
|
||||
RevertingStatus string
|
||||
CreatingFixupCommitStatus string
|
||||
CommitFiles string
|
||||
SubCommitsDynamicTitle string
|
||||
CommitFilesDynamicTitle string
|
||||
@@ -352,9 +355,6 @@ type TranslationSet struct {
|
||||
DiscardOldFileChangeTooltip string
|
||||
DiscardFileChangesTitle string
|
||||
DiscardFileChangesPrompt string
|
||||
DiscardAddedFileChangesPrompt string
|
||||
DiscardDeletedFileChangesPrompt string
|
||||
DiscardNotSupportedForDirectory string
|
||||
DisabledForGPG string
|
||||
CreateRepo string
|
||||
BareRepo string
|
||||
@@ -399,6 +399,9 @@ type TranslationSet struct {
|
||||
CommitChangesWithoutHook string
|
||||
SkipHookPrefixNotConfigured string
|
||||
ResetTo string
|
||||
ResetSoftTooltip string
|
||||
ResetMixedTooltip string
|
||||
ResetHardTooltip string
|
||||
PressEnterToReturn string
|
||||
ViewStashOptions string
|
||||
ViewStashOptionsTooltip string
|
||||
@@ -418,6 +421,7 @@ type TranslationSet struct {
|
||||
ScrollRight string
|
||||
DiscardPatch string
|
||||
DiscardPatchConfirm string
|
||||
DiscardPatchSameCommitConfirm string
|
||||
CantPatchWhileRebasingError string
|
||||
ToggleAddToPatch string
|
||||
ToggleAddToPatchTooltip string
|
||||
@@ -533,9 +537,13 @@ type TranslationSet struct {
|
||||
OpenFilteringMenuTooltip string
|
||||
FilterBy string
|
||||
ExitFilterMode string
|
||||
ExitFilterModeAuthor string
|
||||
FilterPathOption string
|
||||
FilterAuthorOption string
|
||||
EnterFileName string
|
||||
EnterAuthor string
|
||||
FilteringMenuTitle string
|
||||
WillCancelExistingFilterTooltip string
|
||||
MustExitFilterModeTitle string
|
||||
MustExitFilterModePrompt string
|
||||
Diff string
|
||||
@@ -756,6 +764,7 @@ type TranslationSet struct {
|
||||
RangeSelectNotSupported string
|
||||
NoItemSelected string
|
||||
SelectedItemIsNotABranch string
|
||||
SelectedItemDoesNotHaveFiles string
|
||||
RangeSelectNotSupportedForSubmodules string
|
||||
OldCherryPickKeyWarning string
|
||||
Actions Actions
|
||||
@@ -1242,6 +1251,8 @@ func EnglishTranslationSet() TranslationSet {
|
||||
PasteCommits: "Paste (cherry-pick)",
|
||||
SureCherryPick: "Are you sure you want to cherry-pick the copied commits onto this branch?",
|
||||
CherryPick: "Cherry-pick",
|
||||
CannotCherryPickNonCommit: "Cannot cherry-pick this kind of todo item",
|
||||
CannotCherryPickMergeCommit: "Cherry-picking merge commits is not supported",
|
||||
Donate: "Donate",
|
||||
AskQuestion: "Ask Question",
|
||||
PrevLine: "Select previous line",
|
||||
@@ -1279,6 +1290,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
CheckingOutStatus: "Checking out",
|
||||
CommittingStatus: "Committing",
|
||||
RevertingStatus: "Reverting",
|
||||
CreatingFixupCommitStatus: "Creating fixup commit",
|
||||
CommitFiles: "Commit files",
|
||||
SubCommitsDynamicTitle: "Commits (%s)",
|
||||
CommitFilesDynamicTitle: "Diff files (%s)",
|
||||
@@ -1291,10 +1303,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
Remove: "Remove",
|
||||
DiscardOldFileChangeTooltip: "Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file.",
|
||||
DiscardFileChangesTitle: "Discard file changes",
|
||||
DiscardFileChangesPrompt: "Are you sure you want to discard this commit's changes to this file?",
|
||||
DiscardAddedFileChangesPrompt: "Are you sure you want to discard this commit's changes to this file? The file was added in this commit, so it will be deleted again.",
|
||||
DiscardDeletedFileChangesPrompt: "Are you sure you want to discard this commit's changes to this file? The file was deleted in this commit, so it will reappear.",
|
||||
DiscardNotSupportedForDirectory: "Discarding changes is not supported for entire directories. Please use a custom patch for this.",
|
||||
DiscardFileChangesPrompt: "Are you sure you want to remove changes to the selected file(s) from this commit?\n\nThis action will start a rebase, reverting these file changes. Be aware that if subsequent commits depend on these changes, you may need to resolve conflicts.\nNote: This will also reset any active custom patches.",
|
||||
DisabledForGPG: "Feature not available for users using GPG",
|
||||
CreateRepo: "Not in a git repository. Create a new git repository? (y/n): ",
|
||||
BareRepo: "You've attempted to open Lazygit in a bare repo but Lazygit does not yet support bare repos. Open most recent repo? (y/n) ",
|
||||
@@ -1320,6 +1329,9 @@ func EnglishTranslationSet() TranslationSet {
|
||||
Delete: "Delete",
|
||||
Reset: "Reset",
|
||||
ResetTooltip: "View reset options (soft/mixed/hard) for resetting onto selected item.",
|
||||
ResetSoftTooltip: "Reset HEAD to the chosen commit, and keep the changes between the current and chosen commit as staged changes.",
|
||||
ResetMixedTooltip: "Reset HEAD to the chosen commit, and keep the changes between the current and chosen commit as unstaged changes.",
|
||||
ResetHardTooltip: "Reset HEAD to the chosen commit, and discard all changes between the current and chosen commit, as well as all current modifications in the working tree.",
|
||||
ViewResetOptions: `Reset`,
|
||||
FileResetOptionsTooltip: "View reset options for working tree (e.g. nuking the working tree).",
|
||||
FixupTooltip: "Meld the selected commit into the commit below it. Similar to fixup, but the selected commit's message will be discarded.",
|
||||
@@ -1359,6 +1371,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
ScrollRight: "Scroll right",
|
||||
DiscardPatch: "Discard patch",
|
||||
DiscardPatchConfirm: "You can only build a patch from one commit/stash-entry at a time. Discard current patch?",
|
||||
DiscardPatchSameCommitConfirm: "You currently have changes added to a patch for this commit. Discard current patch?",
|
||||
CantPatchWhileRebasingError: "You cannot build a patch or run patch commands while in a merging or rebasing state",
|
||||
ToggleAddToPatch: "Toggle file included in patch",
|
||||
ToggleAddToPatchTooltip: "Toggle whether the file is included in the custom patch. See {{.doc}}.",
|
||||
@@ -1468,13 +1481,16 @@ func EnglishTranslationSet() TranslationSet {
|
||||
GotoBottom: "Scroll to bottom",
|
||||
FilteringBy: "Filtering by",
|
||||
ResetInParentheses: "(Reset)",
|
||||
OpenFilteringMenu: "View filter-by-path options",
|
||||
OpenFilteringMenuTooltip: "View options for filtering the commit log by a file path, so that only commits relating to that path are shown.",
|
||||
OpenFilteringMenu: "View filter options",
|
||||
OpenFilteringMenuTooltip: "View options for filtering the commit log, so that only commits matching the filter are shown.",
|
||||
FilterBy: "Filter by",
|
||||
ExitFilterMode: "Stop filtering by path",
|
||||
ExitFilterMode: "Stop filtering",
|
||||
FilterPathOption: "Enter path to filter by",
|
||||
FilterAuthorOption: "Enter author to filter by",
|
||||
EnterFileName: "Enter path:",
|
||||
EnterAuthor: "Enter author:",
|
||||
FilteringMenuTitle: "Filtering",
|
||||
WillCancelExistingFilterTooltip: "Note: this will cancel the existing filter",
|
||||
MustExitFilterModeTitle: "Command not available",
|
||||
MustExitFilterModePrompt: "Command not available in filter-by-path mode. Exit filter-by-path mode?",
|
||||
Diff: "Diff",
|
||||
@@ -1693,6 +1709,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
RangeSelectNotSupported: "Action does not support range selection, please select a single item",
|
||||
NoItemSelected: "No item selected",
|
||||
SelectedItemIsNotABranch: "Selected item is not a branch",
|
||||
SelectedItemDoesNotHaveFiles: "Selected item does not have files to view",
|
||||
RangeSelectNotSupportedForSubmodules: "Range select not supported for submodules",
|
||||
OldCherryPickKeyWarning: "The 'c' key is no longer the default key for copying commits to cherry pick. Please use `{{.copy}}` instead (and `{{.paste}}` to paste). The reason for this change is that the 'v' key for selecting a range of lines when staging is now also used for selecting a range of lines in any list view, meaning that we needed to find a new key for pasting commits, and if we're going to now use `{{.paste}}` for pasting commits, we may as well use `{{.copy}}` for copying them. If you want to configure the keybindings to get the old behaviour, set the following in your config:\n\nkeybinding:\n universal:\n toggleRangeSelect: <something other than v>\n commits:\n cherryPickCopy: 'c'\n pasteCommits: 'v'",
|
||||
|
||||
|
||||
+1
-4
@@ -289,10 +289,7 @@ func RussianTranslationSet() TranslationSet {
|
||||
CanOnlyDiscardFromLocalCommits: "Изменения можно отменить только из локальных коммитов.",
|
||||
DiscardOldFileChangeTooltip: "Отменить изменения коммита в этом файле",
|
||||
DiscardFileChangesTitle: "Отменить изменения файла",
|
||||
DiscardFileChangesPrompt: "Вы уверены, что хотите отменить изменения коммита в этом файле? Если файл был создан в этом коммите, он будет удалён",
|
||||
DiscardAddedFileChangesPrompt: "Вы уверены, что хотите отменить изменения, внесённые в этот файл коммитом? Файл был добавлен в этот коммит, поэтому он снова будет удален.",
|
||||
DiscardDeletedFileChangesPrompt: "Вы уверены, что хотите отменить изменения, внесённые в этот файл коммитом? Файл был удалён в этом коммите, поэтому он снова появится.",
|
||||
DiscardNotSupportedForDirectory: "Отмена изменений не поддерживается для всех каталогов. Используйте для этого специальный патч.",
|
||||
DiscardFileChangesPrompt: "Вы уверены, что хотите удалить изменения в выбранных файлах из этого коммита?\n\nЭто действие запустит перебазирование и отменит изменения в этих файлах. Обратите внимание, что если последующие коммиты зависят от этих изменений, вам, возможно, придется разрешить конфликты.\nПримечание: это также сбросит все активные пользовательские патчи.",
|
||||
DisabledForGPG: "Функция недоступна для пользователей, использующих GPG",
|
||||
CreateRepo: "Не в git репозитории. Создать новый git репозиторий? (y/n):",
|
||||
BareRepo: "Вы пытались открыть Lazygit в пустом репозитории, но Lazygit ещё не поддерживает пустые репозитории. Открыть последний репозиторий? (y/n)",
|
||||
|
||||
@@ -31,6 +31,16 @@ func (self *CommitDescriptionPanelDriver) AddNewline() *CommitDescriptionPanelDr
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *CommitDescriptionPanelDriver) GoToBeginning() *CommitDescriptionPanelDriver {
|
||||
numLines := len(self.getViewDriver().getView().BufferLines())
|
||||
for i := 0; i < numLines; i++ {
|
||||
self.t.pressFast("<up>")
|
||||
}
|
||||
|
||||
self.t.pressFast("<c-a>")
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *CommitDescriptionPanelDriver) Title(expected *TextMatcher) *CommitDescriptionPanelDriver {
|
||||
self.getViewDriver().Title(expected)
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
)
|
||||
|
||||
type Git struct {
|
||||
@@ -44,3 +47,11 @@ func (self *Git) expect(cmdArgs []string, condition func(string) (bool, string))
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Git) Version() *git_commands.GitVersion {
|
||||
version, err := getGitVersion()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get git version: %v", err)
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
@@ -345,9 +345,9 @@ func (self *Shell) CloneIntoRemote(name string) *Shell {
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Shell) CloneIntoSubmodule(submoduleName string) *Shell {
|
||||
func (self *Shell) CloneIntoSubmodule(submoduleName string, submodulePath string) *Shell {
|
||||
self.Clone("other_repo")
|
||||
self.RunCommand([]string{"git", "submodule", "add", "../other_repo", submoduleName})
|
||||
self.RunCommand([]string{"git", "submodule", "add", "--name", submoduleName, "../other_repo", submodulePath})
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
NewBranch("mybranch").
|
||||
CreateNCommits(10)
|
||||
},
|
||||
SetupConfig: func(cfg *config.AppConfig) {},
|
||||
SetupConfig: func(cfg *config.AppConfig) {
|
||||
cfg.AppState.GitLogShowGraph = "never"
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
markCommitAsBad := func() {
|
||||
t.Views().Commits().
|
||||
|
||||
@@ -14,7 +14,9 @@ var ChooseTerms = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
NewBranch("mybranch").
|
||||
CreateNCommits(10)
|
||||
},
|
||||
SetupConfig: func(cfg *config.AppConfig) {},
|
||||
SetupConfig: func(cfg *config.AppConfig) {
|
||||
cfg.AppState.GitLogShowGraph = "never"
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
markCommitAsFixed := func() {
|
||||
t.Views().Commits().
|
||||
|
||||
@@ -13,7 +13,9 @@ var Skip = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
shell.
|
||||
CreateNCommits(10)
|
||||
},
|
||||
SetupConfig: func(cfg *config.AppConfig) {},
|
||||
SetupConfig: func(cfg *config.AppConfig) {
|
||||
cfg.AppState.GitLogShowGraph = "never"
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
|
||||
@@ -9,7 +9,9 @@ var CherryPickDuringRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Cherry pick commits from the subcommits view during a rebase",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.AppState.GitLogShowGraph = "never"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
EmptyCommit("base").
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var AutoWrapMessage = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Commit, and test how the commit message body is auto-wrapped",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
// Use a ridiculously small width so that we don't have to use so much test data
|
||||
config.UserConfig.Git.Commit.AutoWrapWidth = 20
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFile("file", "file content")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
IsEmpty()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
PressPrimaryAction(). // stage file
|
||||
Press(keys.Files.CommitChanges)
|
||||
|
||||
t.ExpectPopup().CommitMessagePanel().
|
||||
Type("subject").
|
||||
SwitchToDescription().
|
||||
Type("Lorem ipsum dolor sit amet, consectetur adipiscing elit.").
|
||||
// See how it automatically inserted line feeds to wrap the text:
|
||||
Content(Equals("Lorem ipsum dolor \nsit amet, \nconsectetur \nadipiscing elit.")).
|
||||
SwitchToSummary().
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
Contains("subject"),
|
||||
).
|
||||
Focus().
|
||||
Tap(func() {
|
||||
t.Views().Main().Content(Contains(
|
||||
"subject\n \n Lorem ipsum dolor\n sit amet,\n consectetur\n adipiscing elit."))
|
||||
}).
|
||||
Press(keys.Commits.RenameCommit)
|
||||
|
||||
// Test that when rewording, the hard line breaks are turned back into
|
||||
// soft ones, so that we can insert text at the beginning and have the
|
||||
// paragraph reflow nicely.
|
||||
t.ExpectPopup().CommitMessagePanel().
|
||||
InitialText(Equals("subject")).
|
||||
SwitchToDescription().
|
||||
Content(Equals("Lorem ipsum dolor \nsit amet, \nconsectetur \nadipiscing elit.")).
|
||||
GoToBeginning().
|
||||
Type("More text. ").
|
||||
Content(Equals("More text. Lorem \nipsum dolor sit \namet, consectetur \nadipiscing elit."))
|
||||
},
|
||||
})
|
||||
@@ -1,57 +0,0 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DiscardOldFileChange = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Discarding a single file from an old commit (does rebase in background to remove the file but retain the other one)",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("file0", "file0")
|
||||
shell.Commit("first commit")
|
||||
|
||||
shell.CreateFileAndAdd("file1", "file2")
|
||||
shell.CreateFileAndAdd("fileToRemove", "fileToRemove")
|
||||
shell.Commit("commit to change")
|
||||
|
||||
shell.CreateFileAndAdd("file3", "file3")
|
||||
shell.Commit("third commit")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("third commit").IsSelected(),
|
||||
Contains("commit to change"),
|
||||
Contains("first commit"),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("file1").IsSelected(),
|
||||
Contains("fileToRemove"),
|
||||
).
|
||||
SelectNextItem().
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Equals("Are you sure you want to discard this commit's changes to this file? The file was added in this commit, so it will be deleted again.")).
|
||||
Confirm()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("file1").IsSelected(),
|
||||
)
|
||||
|
||||
t.FileSystem().PathNotPresent("fileToRemove")
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,170 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DiscardOldFileChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Discarding a range of files from an old commit.",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("dir1/d1_file0", "file0\n")
|
||||
shell.CreateFileAndAdd("dir1/subd1/subfile0", "file1\n")
|
||||
shell.CreateFileAndAdd("dir2/d2_file1", "d2f1 content\n")
|
||||
shell.CreateFileAndAdd("dir2/d2_file2", "d2f4 content\n")
|
||||
shell.Commit("remove one file from this commit")
|
||||
|
||||
shell.UpdateFileAndAdd("dir2/d2_file1", "d2f1 content\nsecond line\n")
|
||||
shell.DeleteFileAndAdd("dir2/d2_file2")
|
||||
shell.CreateFileAndAdd("dir2/d2_file3", "d2f3 content\n")
|
||||
shell.CreateFileAndAdd("dir2/d2_file4", "d2f2 content\n")
|
||||
shell.Commit("remove four files from this commit")
|
||||
|
||||
shell.CreateFileAndAdd("dir1/fileToRemove", "file to remove content\n")
|
||||
shell.CreateFileAndAdd("dir1/multiLineFile", "this file has\ncontent on\nthree lines\n")
|
||||
shell.CreateFileAndAdd("dir1/subd1/file2ToRemove", "file2 to remove content\n")
|
||||
shell.Commit("remove changes in multiple dirs from this commit")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("remove changes in multiple dirs from this commit").IsSelected(),
|
||||
Contains("remove four files from this commit"),
|
||||
Contains("remove one file from this commit"),
|
||||
).
|
||||
NavigateToLine(Contains("remove one file from this commit")).
|
||||
PressEnter()
|
||||
|
||||
// Check removing a single file from an old commit
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("subd1"),
|
||||
Contains("subfile0"),
|
||||
Contains("d1_file0"),
|
||||
Contains("dir2"),
|
||||
Contains("d2_file1"),
|
||||
Contains("d2_file2"),
|
||||
).
|
||||
NavigateToLine(Contains("d1_file0")).
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Equals("Are you sure you want to remove changes to the selected file(s) from this commit?\n\nThis action will start a rebase, reverting these file changes. Be aware that if subsequent commits depend on these changes, you may need to resolve conflicts.\nNote: This will also reset any active custom patches.")).
|
||||
Confirm()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("dir1/subd1"),
|
||||
Contains("subfile0"),
|
||||
Contains("dir2"),
|
||||
Contains("d2_file1").IsSelected(),
|
||||
Contains("d2_file2"),
|
||||
).
|
||||
PressEscape()
|
||||
|
||||
// Check removing 4 files in the same directory
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("remove changes in multiple dirs from this commit"),
|
||||
Contains("remove four files from this commit"),
|
||||
Contains("remove one file from this commit").IsSelected(),
|
||||
).
|
||||
NavigateToLine(Contains("remove four files from this commit")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("dir2").IsSelected(),
|
||||
Contains("d2_file1"),
|
||||
Contains("d2_file2"),
|
||||
Contains("d2_file3"),
|
||||
Contains("d2_file4"),
|
||||
).
|
||||
NavigateToLine(Contains("d2_file1")).
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
NavigateToLine(Contains("d2_file4")).
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Equals("Are you sure you want to remove changes to the selected file(s) from this commit?\n\nThis action will start a rebase, reverting these file changes. Be aware that if subsequent commits depend on these changes, you may need to resolve conflicts.\nNote: This will also reset any active custom patches.")).
|
||||
Confirm()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("(none)"),
|
||||
).
|
||||
PressEscape()
|
||||
|
||||
// Check removing multiple files from 2 directories w/ a custom patch.
|
||||
// This checks node selection logic & if the custom patch is getting reset.
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("remove changes in multiple dirs from this commit"),
|
||||
Contains("remove four files from this commit").IsSelected(),
|
||||
Contains("remove one file from this commit"),
|
||||
).
|
||||
NavigateToLine(Contains("remove changes in multiple dirs from this commit")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("subd1"),
|
||||
Contains("file2ToRemove"),
|
||||
Contains("fileToRemove"),
|
||||
Contains("multiLineFile"),
|
||||
).
|
||||
NavigateToLine(Contains("multiLineFile")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().PatchBuilding().
|
||||
IsFocused().
|
||||
SelectedLine(
|
||||
Contains("+this file has"),
|
||||
).
|
||||
PressPrimaryAction().
|
||||
PressEscape()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("dir1"),
|
||||
Contains("subd1"),
|
||||
Contains("file2ToRemove"),
|
||||
Contains("fileToRemove"),
|
||||
Contains("multiLineFile").IsSelected(),
|
||||
).
|
||||
NavigateToLine(Contains("dir1")).
|
||||
Press(keys.Universal.ToggleRangeSelect).
|
||||
NavigateToLine(Contains("subd1")).
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Equals("Are you sure you want to remove changes to the selected file(s) from this commit?\n\nThis action will start a rebase, reverting these file changes. Be aware that if subsequent commits depend on these changes, you may need to resolve conflicts.\nNote: This will also reset any active custom patches.")).
|
||||
Confirm()
|
||||
|
||||
// "Building patch" will still be in this view if the patch isn't reset properly
|
||||
t.Views().Information().Content(DoesNotContain("Building patch"))
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("(none)"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -10,7 +10,7 @@ var Highlight = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.Log.ShowGraph = "always"
|
||||
config.AppState.GitLogShowGraph = "always"
|
||||
config.GetUserConfig().Gui.AuthorColors = map[string]string{
|
||||
"CI": "red",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package custom_commands
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var History = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Test that the custom commands history is saved correctly",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupRepo: func(shell *Shell) {},
|
||||
SetupConfig: func(cfg *config.AppConfig) {},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.GlobalPress(keys.Universal.ExecuteCustomCommand)
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Custom command:")).
|
||||
Type("echo 1").
|
||||
Confirm()
|
||||
|
||||
t.GlobalPress(keys.Universal.ExecuteCustomCommand)
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Custom command:")).
|
||||
SuggestionLines(Contains("1")).
|
||||
Type("echo 2").
|
||||
Confirm()
|
||||
|
||||
t.GlobalPress(keys.Universal.ExecuteCustomCommand)
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Custom command:")).
|
||||
SuggestionLines(
|
||||
// "echo 2" was typed last, so it should come first
|
||||
Contains("2"),
|
||||
Contains("1"),
|
||||
).
|
||||
Type("echo 3").
|
||||
Confirm()
|
||||
|
||||
t.GlobalPress(keys.Universal.ExecuteCustomCommand)
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Custom command:")).
|
||||
SuggestionLines(
|
||||
Contains("3"),
|
||||
Contains("2"),
|
||||
Contains("1"),
|
||||
).
|
||||
Type("echo 1").
|
||||
Confirm()
|
||||
|
||||
// Executing a command again should move it to the front:
|
||||
t.GlobalPress(keys.Universal.ExecuteCustomCommand)
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Custom command:")).
|
||||
SuggestionLines(
|
||||
Contains("1"),
|
||||
Contains("3"),
|
||||
Contains("2"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -26,6 +26,7 @@ var FilterMenu = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Filter("Ignore").
|
||||
Lines(
|
||||
// menu has filtered down to the one item that matches the filter
|
||||
Contains(`--- Local ---`),
|
||||
Contains(`Ignore`).IsSelected(),
|
||||
).
|
||||
Confirm()
|
||||
|
||||
@@ -20,6 +20,7 @@ var FilterMenuCancelFilterWithEscape = NewIntegrationTest(NewIntegrationTestArgs
|
||||
Filter("Ignore").
|
||||
Lines(
|
||||
// menu has filtered down to the one item that matches the filter
|
||||
Contains(`--- Local ---`),
|
||||
Contains(`Ignore`).IsSelected(),
|
||||
)
|
||||
|
||||
|
||||
@@ -27,9 +27,10 @@ var FilterUpdatesWhenModelChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
FilterOrSearch("branch").
|
||||
Lines(
|
||||
Contains("branch-to-delete").IsSelected(),
|
||||
Contains("checked-out-branch"),
|
||||
Contains("checked-out-branch").IsSelected(),
|
||||
Contains("branch-to-delete"),
|
||||
).
|
||||
SelectNextItem().
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package filter_by_author
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var SelectAuthor = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Filter commits using the currently highlighted commit's author when the commit view is active",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.AppState.GitLogShowGraph = "never"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
commonSetup(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
SelectedLineIdx(0).
|
||||
Press(keys.Universal.FilteringMenu)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Filtering")).
|
||||
Select(Contains("Filter by 'Paul Oberstein <paul.oberstein@email.com>'")).
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("commit 7"),
|
||||
Contains("commit 6"),
|
||||
Contains("commit 5"),
|
||||
Contains("commit 4"),
|
||||
Contains("commit 3"),
|
||||
Contains("commit 2"),
|
||||
Contains("commit 1"),
|
||||
Contains("commit 0"),
|
||||
)
|
||||
|
||||
t.Views().Information().Content(Contains("Filtering by 'Paul Oberstein <paul.oberstein@email.com>'"))
|
||||
|
||||
t.Views().Commits().
|
||||
Press(keys.Universal.FilteringMenu)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Filtering")).
|
||||
Select(Contains("Stop filtering")).
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
NavigateToLine(Contains("SK commit 0")).
|
||||
Press(keys.Universal.FilteringMenu)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Filtering")).
|
||||
Select(Contains("Filter by 'Siegfried Kircheis <siegfried.kircheis@email.com>'")).
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("commit 0"),
|
||||
)
|
||||
|
||||
t.Views().Information().Content(Contains("Filtering by 'Siegfried Kircheis <siegfried.kircheis@email.com>'"))
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
package filter_by_author
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
type AuthorInfo struct {
|
||||
name string
|
||||
numberOfCommits int
|
||||
}
|
||||
|
||||
func commonSetup(shell *Shell) {
|
||||
authors := []AuthorInfo{{"Yang Wen-li", 3}, {"Siegfried Kircheis", 1}, {"Paul Oberstein", 8}}
|
||||
totalCommits := 0
|
||||
repoStartDaysAgo := 100
|
||||
|
||||
for _, authorInfo := range authors {
|
||||
for i := 0; i < authorInfo.numberOfCommits; i++ {
|
||||
authorEmail := strings.ToLower(strings.ReplaceAll(authorInfo.name, " ", ".")) + "@email.com"
|
||||
commitMessage := fmt.Sprintf("commit %d", i)
|
||||
|
||||
shell.SetAuthor(authorInfo.name, authorEmail)
|
||||
shell.EmptyCommitDaysAgo(commitMessage, repoStartDaysAgo-totalCommits)
|
||||
totalCommits++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package filter_by_author
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var TypeAuthor = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Filter commits by author using the typed in author",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
commonSetup(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Status().
|
||||
Focus().
|
||||
Press(keys.Universal.FilteringMenu)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Filtering")).
|
||||
Select(Contains("Enter author to filter by")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Enter author:")).
|
||||
Type("Yang").
|
||||
SuggestionLines(Equals("Yang Wen-li <yang.wen-li@email.com>")).
|
||||
ConfirmFirstSuggestion()
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("commit 2"),
|
||||
Contains("commit 1"),
|
||||
Contains("commit 0"),
|
||||
)
|
||||
|
||||
t.Views().Information().Content(Contains("Filtering by 'Yang Wen-li <yang.wen-li@email.com>'"))
|
||||
|
||||
t.Views().Status().
|
||||
Focus().
|
||||
Press(keys.Universal.FilteringMenu)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Filtering")).
|
||||
Select(Contains("Enter author to filter by")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Enter author:")).
|
||||
Type("Siegfried").
|
||||
SuggestionLines(Equals("Siegfried Kircheis <siegfried.kircheis@email.com>")).
|
||||
ConfirmFirstSuggestion()
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("commit 0"),
|
||||
)
|
||||
|
||||
t.Views().Information().Content(Contains("Filtering by 'Siegfried Kircheis <siegfried.kircheis@email.com>'"))
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
package interactive_rebase
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DontShowBranchHeadsForTodoItems = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Check that branch heads are shown for normal commits during interactive rebase, but not for todo items",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
GitVersion: AtLeast("2.38.0"),
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.AppState.GitLogShowGraph = "never"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
NewBranch("branch1").
|
||||
CreateNCommits(2).
|
||||
NewBranch("branch2").
|
||||
CreateNCommitsStartingAt(4, 3).
|
||||
NewBranch("branch3").
|
||||
CreateNCommitsStartingAt(3, 7)
|
||||
|
||||
shell.SetConfig("rebase.updateRefs", "true")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("CI commit 09"),
|
||||
Contains("CI commit 08"),
|
||||
Contains("CI commit 07"),
|
||||
Contains("CI * commit 06"),
|
||||
Contains("CI commit 05"),
|
||||
Contains("CI commit 04"),
|
||||
Contains("CI commit 03"),
|
||||
Contains("CI * commit 02"),
|
||||
Contains("CI commit 01"),
|
||||
).
|
||||
NavigateToLine(Contains("commit 04")).
|
||||
Press(keys.Universal.Edit).
|
||||
Lines(
|
||||
Contains("pick").Contains("CI commit 09"),
|
||||
Contains("pick").Contains("CI commit 08"),
|
||||
Contains("pick").Contains("CI commit 07"),
|
||||
Contains("update-ref").Contains("branch2"),
|
||||
Contains("pick").Contains("CI commit 06"), // no star on this entry, even though branch2 points to it
|
||||
Contains("pick").Contains("CI commit 05"),
|
||||
Contains("CI <-- YOU ARE HERE --- commit 04"),
|
||||
Contains("CI commit 03"),
|
||||
Contains("CI * commit 02"), // this star is fine though
|
||||
Contains("CI commit 01"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -12,6 +12,7 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
GitVersion: AtLeast("2.38.0"),
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.MainBranches = []string{"master"}
|
||||
config.AppState.GitLogShowGraph = "never"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
@@ -43,7 +44,7 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("pick").Contains("CI commit 06"),
|
||||
Contains("pick").Contains("CI commit 05"),
|
||||
Contains("update-ref").Contains("branch1").DoesNotContain("*"),
|
||||
Contains("pick").Contains("CI * commit 04"),
|
||||
Contains("pick").Contains("CI commit 04"),
|
||||
Contains("pick").Contains("CI commit 03"),
|
||||
Contains("<-- YOU ARE HERE --- commit 02").IsSelected(),
|
||||
Contains("CI commit 01"),
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package interactive_rebase
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
var EditRangeSelectOutsideRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Select a range of commits to edit outside of a rebase",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
GitVersion: AtLeast("2.22.0"), // first version that supports the --rebase-merges option
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shared.CreateMergeCommit(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
TopLines(
|
||||
Contains("Merge branch 'second-change-branch' into first-change-branch").IsSelected(),
|
||||
).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
Lines(
|
||||
Contains("CI ⏣─╮ Merge branch 'second-change-branch' into first-change-branch").IsSelected(),
|
||||
Contains("CI │ ◯ * second-change-branch unrelated change").IsSelected(),
|
||||
Contains("CI │ ◯ second change").IsSelected(),
|
||||
Contains("CI ◯ │ first change").IsSelected(),
|
||||
Contains("CI ◯─╯ * original").IsSelected(),
|
||||
Contains("CI ◯ three").IsSelected(),
|
||||
Contains("CI ◯ two"),
|
||||
Contains("CI ◯ one"),
|
||||
).
|
||||
Press(keys.Universal.Edit).
|
||||
Lines(
|
||||
Contains("merge CI Merge branch 'second-change-branch' into first-change-branch").IsSelected(),
|
||||
Contains("edit CI first change").IsSelected(),
|
||||
Contains("edit CI * second-change-branch unrelated change").IsSelected(),
|
||||
Contains("edit CI second change").IsSelected(),
|
||||
Contains("edit CI * original").IsSelected(),
|
||||
Contains(" CI ◯ <-- YOU ARE HERE --- three").IsSelected(),
|
||||
Contains(" CI ◯ two"),
|
||||
Contains(" CI ◯ one"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -20,27 +20,6 @@ var MidRebaseRangeSelect = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
TopLines(
|
||||
Contains("commit 10").IsSelected(),
|
||||
).
|
||||
NavigateToLine(Contains("commit 07")).
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
TopLines(
|
||||
Contains("commit 10"),
|
||||
Contains("commit 09"),
|
||||
Contains("commit 08"),
|
||||
Contains("commit 07").IsSelected(),
|
||||
Contains("commit 06").IsSelected(),
|
||||
Contains("commit 05"),
|
||||
Contains("commit 04"),
|
||||
).
|
||||
// Verify we can't perform an edit on multiple commits (it's not supported
|
||||
// yet)
|
||||
Press(keys.Universal.Edit).
|
||||
Tap(func() {
|
||||
// This ought to be a toast but I'm too lazy to implement that right now.
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Error")).
|
||||
Content(Contains("Action does not support range selection, please select a single item")).
|
||||
Confirm()
|
||||
}).
|
||||
NavigateToLine(Contains("commit 05")).
|
||||
// Start a rebase
|
||||
Press(keys.Universal.Edit).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user