mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-03 12:36:57 -04:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7357c1daea | ||
|
|
75488f1041 | ||
|
|
3db8175bb5 | ||
|
|
5f8bca6508 | ||
|
|
3b677cbcfe | ||
|
|
658dbd2bdf | ||
|
|
a9a13619fd | ||
|
|
5d452122b3 | ||
|
|
f70b0a214b | ||
|
|
b81b6e3c2e | ||
|
|
4bed4b8965 | ||
|
|
4fab224ccd | ||
|
|
95005999e6 | ||
|
|
f84cff3ba5 | ||
|
|
2ae87d3d0f | ||
|
|
a2b2fa7a5e |
@@ -481,6 +481,10 @@ git:
|
||||
# to 40 to disable truncation.
|
||||
truncateCopiedCommitHashesTo: 12
|
||||
|
||||
# If true and if `gh` is installed and on version >=2, we will use `gh` to
|
||||
# display pull requests against branches.
|
||||
enableGithubCli: true
|
||||
|
||||
# Periodic update checks
|
||||
update:
|
||||
# One of: 'prompt' (default) | 'background' | 'never'
|
||||
@@ -666,7 +670,6 @@ keybinding:
|
||||
checkForUpdate: u
|
||||
recentRepos: <enter>
|
||||
allBranchesLogGraph: a
|
||||
allBranchesLogGraphReverse: A
|
||||
files:
|
||||
commitChanges: c
|
||||
commitChangesWithoutHook: w
|
||||
|
||||
@@ -60,7 +60,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | Copy path to clipboard | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | Checkout | Checkout file. This replaces the file in your working tree with the version from the selected commit. |
|
||||
| `` d `` | Discard | 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. |
|
||||
| `` d `` | Remove | 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. |
|
||||
| `` o `` | Open file | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
@@ -84,7 +84,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -275,7 +275,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
@@ -353,14 +353,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` u `` | Check for update | |
|
||||
| `` <enter> `` | Switch to a recent repo | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Sub-commits
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
|
||||
@@ -64,7 +64,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
|
||||
| `` <c-r> `` | コピーされた(チェリーピックされた)コミットの選択をリセット | |
|
||||
| `` b `` | bisectオプションを表示 | |
|
||||
| `` s `` | スカッシュ | 選択したコミットをその下のコミットにスカッシュします。スカッシュとは複数のコミットを1つにまとめる操作です。選択したコミットのメッセージが下のコミットに追加されます。 |
|
||||
@@ -108,7 +108,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` <c-o> `` | パスをクリップボードにコピー | |
|
||||
| `` y `` | クリップボードにコピー | |
|
||||
| `` c `` | チェックアウト(ブランチの切り替え) | ファイルをチェックアウトします。これにより、作業ツリー内のファイルが選択したコミットのバージョンに置き換えられます。 |
|
||||
| `` d `` | 破棄 | このコミットのこのファイルへの変更を破棄します。これはバックグラウンドで対話的なリベースを実行するため、後のコミットでもこのファイルが変更されている場合、マージコンフリクトが発生する可能性があります。 |
|
||||
| `` d `` | 削除 | このコミットのこのファイルへの変更を破棄します。これはバックグラウンドで対話的なリベースを実行するため、後のコミットでもこのファイルが変更されている場合、マージコンフリクトが発生する可能性があります。 |
|
||||
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
|
||||
| `` e `` | 編集 | 外部エディタでファイルを開きます。 |
|
||||
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
|
||||
@@ -132,7 +132,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
|
||||
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したコミットをデタッチドヘッド(特定のブランチに属さない状態)としてチェックアウトします。 |
|
||||
| `` y `` | コミット属性をクリップボードにコピー | コミット属性をクリップボードにコピーします(例:ハッシュ、URL、差分、メッセージ、作者)。 |
|
||||
| `` o `` | ブラウザでコミットを開く | |
|
||||
@@ -185,7 +185,6 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` u `` | 更新を確認 | |
|
||||
| `` <enter> `` | 最近のリポジトリをチェックアウト | |
|
||||
| `` a `` | ブランチログの表示モードを順に切り替え | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | メインビューにフォーカス | |
|
||||
|
||||
## セカンダリ
|
||||
@@ -321,7 +320,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
|
||||
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したコミットをデタッチドヘッド(特定のブランチに属さない状態)としてチェックアウトします。 |
|
||||
| `` y `` | コミット属性をクリップボードにコピー | コミット属性をクリップボードにコピーします(例:ハッシュ、URL、差分、メッセージ、作者)。 |
|
||||
| `` o `` | ブラウザでコミットを開く | |
|
||||
|
||||
@@ -64,7 +64,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
|
||||
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | 브라우저에서 커밋 열기 | |
|
||||
@@ -106,7 +106,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
|
||||
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | 브라우저에서 커밋 열기 | |
|
||||
@@ -243,7 +243,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` u `` | 업데이트 확인 | |
|
||||
| `` <enter> `` | 최근에 사용한 저장소로 전환 | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## 서브모듈
|
||||
@@ -295,7 +294,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
|
||||
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
|
||||
| `` b `` | Bisect 옵션 보기 | |
|
||||
| `` s `` | 스쿼시 | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -339,7 +338,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | 파일명을 클립보드에 복사 | |
|
||||
| `` y `` | 클립보드에 복사 | |
|
||||
| `` c `` | 체크아웃 | Checkout file |
|
||||
| `` d `` | View 'discard changes' options | Discard this commit's changes to this file |
|
||||
| `` d `` | Remove | Discard this commit's changes to this file |
|
||||
| `` o `` | 파일 닫기 | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
|
||||
@@ -138,7 +138,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | Kopieer de bestandsnaam naar het klembord | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | Uitchecken | Bestand uitchecken |
|
||||
| `` d `` | Bekijk 'veranderingen ongedaan maken' opties | Uitsluit deze commit zijn veranderingen aan dit bestand |
|
||||
| `` d `` | Remove | Uitsluit deze commit zijn veranderingen aan dit bestand |
|
||||
| `` o `` | Open bestand | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
@@ -155,7 +155,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopieer commit hash naar klembord | |
|
||||
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -253,7 +253,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopieer commit hash naar klembord | |
|
||||
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
@@ -353,14 +353,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` u `` | Check voor updates | |
|
||||
| `` <enter> `` | Wissel naar een recente repo | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Sub-commits
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopieer commit hash naar klembord | |
|
||||
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
|
||||
@@ -57,7 +57,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopiuj hash commita do schowka | |
|
||||
| `` <c-r> `` | Resetuj wybrane (cherry-picked) commity | |
|
||||
| `` b `` | Zobacz opcje bisect | |
|
||||
| `` s `` | Scal | Scal wybrany commit z commitami poniżej. Wiadomość wybranego commita zostanie dołączona do commita poniżej. |
|
||||
@@ -269,7 +269,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` <c-o> `` | Kopiuj ścieżkę do schowka | |
|
||||
| `` y `` | Kopiuj do schowka | |
|
||||
| `` c `` | Przełącz | Przełącz plik. Zastępuje plik w twoim drzewie roboczym wersją z wybranego commita. |
|
||||
| `` d `` | Odrzuć | Odrzuć zmiany w tym pliku z tego commita. Uruchamia interaktywny rebase w tle, więc możesz otrzymać konflikt scalania, jeśli późniejszy commit również zmienia ten plik. |
|
||||
| `` d `` | Usuń | Odrzuć zmiany w tym pliku z tego commita. Uruchamia interaktywny rebase w tle, więc możesz otrzymać konflikt scalania, jeśli późniejszy commit również zmienia ten plik. |
|
||||
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
|
||||
| `` e `` | Edytuj | Otwórz plik w zewnętrznym edytorze. |
|
||||
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
|
||||
@@ -293,7 +293,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopiuj hash commita do schowka | |
|
||||
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
|
||||
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
|
||||
| `` o `` | Otwórz commit w przeglądarce | |
|
||||
@@ -332,14 +332,13 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` u `` | Sprawdź aktualizacje | |
|
||||
| `` <enter> `` | Przełącz na ostatnie repozytorium | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Sub-commity
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopiuj hash commita do schowka | |
|
||||
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
|
||||
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
|
||||
| `` o `` | Otwórz commit w przeglądarce | |
|
||||
|
||||
@@ -142,7 +142,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | Copy path to clipboard | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | Verificar | Arquivo de check-out. Isso substitui o arquivo em sua árvore de trabalho com a versão do commit selecionado. |
|
||||
| `` d `` | Descartar | Descartar as alterações desse commit para este arquivo. Isso executa uma rebase interativa em segundo plano, então você pode ter um conflito de merge se um commit posterior também alterar este arquivo. |
|
||||
| `` d `` | Remover | Descartar as alterações desse commit para este arquivo. Isso executa uma rebase interativa em segundo plano, então você pode ter um conflito de merge se um commit posterior também alterar este arquivo. |
|
||||
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
|
||||
| `` e `` | Editar | Abrir arquivo no editor externo. |
|
||||
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
|
||||
@@ -159,7 +159,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` s `` | Squash | Squash o commit selecionado no commit abaixo dele. A mensagem do commit selecionado será anexada ao commit abaixo dele. |
|
||||
@@ -303,7 +303,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <space> `` | Verificar | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
@@ -362,14 +362,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` u `` | Verificar atualização | |
|
||||
| `` <enter> `` | Mudar para um repositório recente | |
|
||||
| `` a `` | Mostrar/ciclo todos os logs de filiais | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Sub-commits
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <space> `` | Verificar | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
|
||||
@@ -146,7 +146,7 @@ _Связки клавиш_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
|
||||
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Открыть коммит в браузере | |
|
||||
@@ -166,7 +166,7 @@ _Связки клавиш_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
|
||||
| `` <c-r> `` | Сбросить отобранную (скопированную \| cherry-picked) выборку коммитов | |
|
||||
| `` b `` | Просмотреть параметры бинарного поиска | |
|
||||
| `` s `` | Объединить коммиты (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -253,7 +253,7 @@ _Связки клавиш_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
|
||||
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Открыть коммит в браузере | |
|
||||
@@ -297,7 +297,7 @@ _Связки клавиш_
|
||||
| `` <c-o> `` | Скопировать название файла в буфер обмена | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | Переключить | Переключить файл |
|
||||
| `` d `` | Просмотреть параметры «отмены изменении» | Отменить изменения коммита в этом файле |
|
||||
| `` d `` | Remove | Отменить изменения коммита в этом файле |
|
||||
| `` o `` | Открыть файл | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
@@ -319,7 +319,6 @@ _Связки клавиш_
|
||||
| `` u `` | Проверить обновления | |
|
||||
| `` <enter> `` | Переключиться на последний репозиторий | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Теги
|
||||
|
||||
@@ -57,7 +57,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
|
||||
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(如hash、URL、diff、消息、作者)。 |
|
||||
| `` o `` | 在浏览器中打开提交 | |
|
||||
@@ -101,7 +101,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
|
||||
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(如hash、URL、diff、消息、作者)。 |
|
||||
| `` o `` | 在浏览器中打开提交 | |
|
||||
@@ -121,7 +121,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
|
||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||
| `` b `` | 查看二分查找选项 | |
|
||||
| `` s `` | 压缩(Squash) | 将已选提交压缩到该提交之下。这些选定的提交的消息会附加到该提交的消息之下。 |
|
||||
@@ -172,7 +172,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` <c-o> `` | 复制路径到剪贴板 | |
|
||||
| `` y `` | 复制到剪贴板 | |
|
||||
| `` c `` | 检出 | 检出文件 |
|
||||
| `` d `` | 查看'放弃变更'选项 | 放弃对此文件的提交变更 |
|
||||
| `` d `` | 删除 | 放弃对此文件的提交变更 |
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` e `` | 编辑(Edit) | 使用外部编辑器打开文件 |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
@@ -345,7 +345,6 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` u `` | 检查更新 | |
|
||||
| `` <enter> `` | 切换到最近的仓库 | |
|
||||
| `` a `` | 显示/循环所有分支日志 | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | 聚焦主视图 | |
|
||||
|
||||
## 确认面板
|
||||
|
||||
@@ -136,7 +136,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
|
||||
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | 在瀏覽器中開啟提交 | |
|
||||
@@ -180,7 +180,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
|
||||
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
|
||||
| `` b `` | 查看二分選項 | |
|
||||
| `` s `` | 壓縮 (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -231,7 +231,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
|
||||
| `` y `` | 複製到剪貼簿 | |
|
||||
| `` c `` | 檢出 | 檢出檔案 |
|
||||
| `` d `` | 捨棄 | 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. |
|
||||
| `` d `` | Remove | 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. |
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` e `` | 編輯 | 使用外部編輯器開啟 |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
@@ -262,7 +262,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
|
||||
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | 在瀏覽器中開啟提交 | |
|
||||
@@ -374,7 +374,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` u `` | 檢查更新 | |
|
||||
| `` <enter> `` | 切換到最近使用的版本庫 | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## 確認面板
|
||||
|
||||
+4
-1
@@ -481,6 +481,10 @@ git:
|
||||
# to 40 to disable truncation.
|
||||
truncateCopiedCommitHashesTo: 12
|
||||
|
||||
# If true and if if `gh` is installed and on version >=2, we will use `gh` to
|
||||
# display pull requests against branches.
|
||||
enableGithubCli: true
|
||||
|
||||
# Periodic update checks
|
||||
update:
|
||||
# One of: 'prompt' (default) | 'background' | 'never'
|
||||
@@ -666,7 +670,6 @@ keybinding:
|
||||
checkForUpdate: u
|
||||
recentRepos: <enter>
|
||||
allBranchesLogGraph: a
|
||||
allBranchesLogGraphReverse: A
|
||||
files:
|
||||
commitChanges: c
|
||||
commitChangesWithoutHook: w
|
||||
|
||||
@@ -60,7 +60,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | Copy path to clipboard | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | Checkout | Checkout file. This replaces the file in your working tree with the version from the selected commit. |
|
||||
| `` d `` | Discard | 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. |
|
||||
| `` d `` | Remove | 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. |
|
||||
| `` o `` | Open file | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
@@ -71,7 +71,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
|
||||
## Commit summary
|
||||
|
||||
@@ -84,7 +84,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -160,7 +160,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
|
||||
## Input prompt
|
||||
|
||||
@@ -237,7 +237,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` o `` | Open file | Open file in default application. |
|
||||
| `` e `` | Edit file | Open file in external editor. |
|
||||
| `` <space> `` | Toggle lines in patch | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | Exit custom patch builder | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
|
||||
@@ -275,7 +274,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
@@ -353,14 +352,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` u `` | Check for update | |
|
||||
| `` <enter> `` | Switch to a recent repo | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Sub-commits
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
|
||||
@@ -64,7 +64,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
|
||||
| `` <c-r> `` | コピーされた(チェリーピックされた)コミットの選択をリセット | |
|
||||
| `` b `` | bisectオプションを表示 | |
|
||||
| `` s `` | スカッシュ | 選択したコミットをその下のコミットにスカッシュします。スカッシュとは複数のコミットを1つにまとめる操作です。選択したコミットのメッセージが下のコミットに追加されます。 |
|
||||
@@ -108,7 +108,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` <c-o> `` | パスをクリップボードにコピー | |
|
||||
| `` y `` | クリップボードにコピー | |
|
||||
| `` c `` | チェックアウト(ブランチの切り替え) | ファイルをチェックアウトします。これにより、作業ツリー内のファイルが選択したコミットのバージョンに置き換えられます。 |
|
||||
| `` d `` | 破棄 | このコミットのこのファイルへの変更を破棄します。これはバックグラウンドで対話的なリベースを実行するため、後のコミットでもこのファイルが変更されている場合、マージコンフリクトが発生する可能性があります。 |
|
||||
| `` d `` | 削除 | このコミットのこのファイルへの変更を破棄します。これはバックグラウンドで対話的なリベースを実行するため、後のコミットでもこのファイルが変更されている場合、マージコンフリクトが発生する可能性があります。 |
|
||||
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
|
||||
| `` e `` | 編集 | 外部エディタでファイルを開きます。 |
|
||||
| `` <c-t> `` | 外部差分ツールを開く(git difftool) | |
|
||||
@@ -119,7 +119,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` - `` | すべてのファイルを折りたたむ | ファイルツリー内のすべてのディレクトリを折りたたみます |
|
||||
| `` = `` | すべてのファイルを展開 | ファイルツリー内のすべてのディレクトリを展開します |
|
||||
| `` 0 `` | メインビューにフォーカス | |
|
||||
| `` / `` | 現在のビューをテキストでフィルタリング | |
|
||||
| `` / `` | 現在のビューをテキストで検索 | |
|
||||
|
||||
## コミット概要
|
||||
|
||||
@@ -132,7 +132,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
|
||||
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したコミットをデタッチドヘッド(特定のブランチに属さない状態)としてチェックアウトします。 |
|
||||
| `` y `` | コミット属性をクリップボードにコピー | コミット属性をクリップボードにコピーします(例:ハッシュ、URL、差分、メッセージ、作者)。 |
|
||||
| `` o `` | ブラウザでコミットを開く | |
|
||||
@@ -185,7 +185,6 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` u `` | 更新を確認 | |
|
||||
| `` <enter> `` | 最近のリポジトリをチェックアウト | |
|
||||
| `` a `` | ブランチログの表示モードを順に切り替え | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | メインビューにフォーカス | |
|
||||
|
||||
## セカンダリ
|
||||
@@ -243,7 +242,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` - `` | すべてのファイルを折りたたむ | ファイルツリー内のすべてのディレクトリを折りたたみます |
|
||||
| `` = `` | すべてのファイルを展開 | ファイルツリー内のすべてのディレクトリを展開します |
|
||||
| `` 0 `` | メインビューにフォーカス | |
|
||||
| `` / `` | 現在のビューをテキストでフィルタリング | |
|
||||
| `` / `` | 現在のビューをテキストで検索 | |
|
||||
|
||||
## メインパネル(ステージング)
|
||||
|
||||
@@ -279,7 +278,6 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
| `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 |
|
||||
| `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 |
|
||||
| `` <space> `` | パッチ内の行を切り替え | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | カスタムパッチビルダーを終了 | |
|
||||
| `` / `` | 現在のビューをテキストで検索 | |
|
||||
|
||||
@@ -321,7 +319,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | コミットハッシュをクリップボードにコピー | |
|
||||
| `` <space> `` | チェックアウト(ブランチの切り替え) | 選択したコミットをデタッチドヘッド(特定のブランチに属さない状態)としてチェックアウトします。 |
|
||||
| `` y `` | コミット属性をクリップボードにコピー | コミット属性をクリップボードにコピーします(例:ハッシュ、URL、差分、メッセージ、作者)。 |
|
||||
| `` o `` | ブラウザでコミットを開く | |
|
||||
|
||||
@@ -64,7 +64,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
|
||||
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | 브라우저에서 커밋 열기 | |
|
||||
@@ -106,7 +106,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
|
||||
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | 브라우저에서 커밋 열기 | |
|
||||
@@ -178,7 +178,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` o `` | 파일 닫기 | Open file in default application. |
|
||||
| `` e `` | 파일 편집 | Open file in external editor. |
|
||||
| `` <space> `` | Line(s)을 패치에 추가/삭제 | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | Exit custom patch builder | |
|
||||
| `` / `` | 검색 시작 | |
|
||||
|
||||
@@ -243,7 +242,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` u `` | 업데이트 확인 | |
|
||||
| `` <enter> `` | 최근에 사용한 저장소로 전환 | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## 서브모듈
|
||||
@@ -295,7 +293,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
|
||||
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
|
||||
| `` b `` | Bisect 옵션 보기 | |
|
||||
| `` s `` | 스쿼시 | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -339,7 +337,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | 파일명을 클립보드에 복사 | |
|
||||
| `` y `` | 클립보드에 복사 | |
|
||||
| `` c `` | 체크아웃 | Checkout file |
|
||||
| `` d `` | View 'discard changes' options | Discard this commit's changes to this file |
|
||||
| `` d `` | Remove | Discard this commit's changes to this file |
|
||||
| `` o `` | 파일 닫기 | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
@@ -350,7 +348,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | 검색 시작 | |
|
||||
|
||||
## 커밋메시지
|
||||
|
||||
@@ -406,7 +404,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | 검색 시작 | |
|
||||
|
||||
## 확인 패널
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | Start met zoeken | |
|
||||
|
||||
## Bevestigingspaneel
|
||||
|
||||
@@ -138,7 +138,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | Kopieer de bestandsnaam naar het klembord | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | Uitchecken | Bestand uitchecken |
|
||||
| `` d `` | Bekijk 'veranderingen ongedaan maken' opties | Uitsluit deze commit zijn veranderingen aan dit bestand |
|
||||
| `` d `` | Remove | Uitsluit deze commit zijn veranderingen aan dit bestand |
|
||||
| `` o `` | Open bestand | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
@@ -149,13 +149,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | Start met zoeken | |
|
||||
|
||||
## Commits
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopieer commit hash naar klembord | |
|
||||
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -245,7 +245,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` o `` | Open bestand | Open file in default application. |
|
||||
| `` e `` | Verander bestand | Open file in external editor. |
|
||||
| `` <space> `` | Voeg toe/verwijder lijn(en) in patch | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | Sluit lijn-bij-lijn modus | |
|
||||
| `` / `` | Start met zoeken | |
|
||||
|
||||
@@ -253,7 +252,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopieer commit hash naar klembord | |
|
||||
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
@@ -353,14 +352,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` u `` | Check voor updates | |
|
||||
| `` <enter> `` | Wissel naar een recente repo | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Sub-commits
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopieer commit hash naar klembord | |
|
||||
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
|
||||
@@ -57,7 +57,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopiuj hash commita do schowka | |
|
||||
| `` <c-r> `` | Resetuj wybrane (cherry-picked) commity | |
|
||||
| `` b `` | Zobacz opcje bisect | |
|
||||
| `` s `` | Scal | Scal wybrany commit z commitami poniżej. Wiadomość wybranego commita zostanie dołączona do commita poniżej. |
|
||||
@@ -124,7 +124,6 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
|
||||
| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. |
|
||||
| `` <space> `` | Przełącz linie w łatce | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | Wyjdź z budowniczego niestandardowej łatki | |
|
||||
| `` / `` | Szukaj w bieżącym widoku po tekście | |
|
||||
|
||||
@@ -260,7 +259,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filtruj bieżący widok po tekście | |
|
||||
| `` / `` | Szukaj w bieżącym widoku po tekście | |
|
||||
|
||||
## Pliki commita
|
||||
|
||||
@@ -269,7 +268,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` <c-o> `` | Kopiuj ścieżkę do schowka | |
|
||||
| `` y `` | Kopiuj do schowka | |
|
||||
| `` c `` | Przełącz | Przełącz plik. Zastępuje plik w twoim drzewie roboczym wersją z wybranego commita. |
|
||||
| `` d `` | Odrzuć | Odrzuć zmiany w tym pliku z tego commita. Uruchamia interaktywny rebase w tle, więc możesz otrzymać konflikt scalania, jeśli późniejszy commit również zmienia ten plik. |
|
||||
| `` d `` | Usuń | Odrzuć zmiany w tym pliku z tego commita. Uruchamia interaktywny rebase w tle, więc możesz otrzymać konflikt scalania, jeśli późniejszy commit również zmienia ten plik. |
|
||||
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
|
||||
| `` e `` | Edytuj | Otwórz plik w zewnętrznym edytorze. |
|
||||
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
|
||||
@@ -280,7 +279,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filtruj bieżący widok po tekście | |
|
||||
| `` / `` | Szukaj w bieżącym widoku po tekście | |
|
||||
|
||||
## Podsumowanie commita
|
||||
|
||||
@@ -293,7 +292,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopiuj hash commita do schowka | |
|
||||
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
|
||||
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
|
||||
| `` o `` | Otwórz commit w przeglądarce | |
|
||||
@@ -332,14 +331,13 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
||||
| `` u `` | Sprawdź aktualizacje | |
|
||||
| `` <enter> `` | Przełącz na ostatnie repozytorium | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Sub-commity
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Kopiuj hash commita do schowka | |
|
||||
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
|
||||
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
|
||||
| `` o `` | Otwórz commit w przeglądarce | |
|
||||
|
||||
@@ -84,7 +84,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
|
||||
| `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
|
||||
## Branches locais
|
||||
|
||||
@@ -142,7 +142,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` <c-o> `` | Copy path to clipboard | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | Verificar | Arquivo de check-out. Isso substitui o arquivo em sua árvore de trabalho com a versão do commit selecionado. |
|
||||
| `` d `` | Descartar | Descartar as alterações desse commit para este arquivo. Isso executa uma rebase interativa em segundo plano, então você pode ter um conflito de merge se um commit posterior também alterar este arquivo. |
|
||||
| `` d `` | Remover | Descartar as alterações desse commit para este arquivo. Isso executa uma rebase interativa em segundo plano, então você pode ter um conflito de merge se um commit posterior também alterar este arquivo. |
|
||||
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
|
||||
| `` e `` | Editar | Abrir arquivo no editor externo. |
|
||||
| `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) | |
|
||||
@@ -153,13 +153,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
|
||||
| `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
|
||||
## Commits
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
|
||||
| `` b `` | View bisect options | |
|
||||
| `` s `` | Squash | Squash o commit selecionado no commit abaixo dele. A mensagem do commit selecionado será anexada ao commit abaixo dele. |
|
||||
@@ -295,7 +295,6 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. |
|
||||
| `` e `` | Editar arquivo | Abrir arquivo no editor externo. |
|
||||
| `` <space> `` | Alternar linhas no caminho | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | Sair do construtor de patch personalizado | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
|
||||
@@ -303,7 +302,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <space> `` | Verificar | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
@@ -362,14 +361,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
| `` u `` | Verificar atualização | |
|
||||
| `` <enter> `` | Mudar para um repositório recente | |
|
||||
| `` a `` | Mostrar/ciclo todos os logs de filiais | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Sub-commits
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Copy commit hash to clipboard | |
|
||||
| `` <space> `` | Verificar | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Open commit in browser | |
|
||||
|
||||
@@ -138,7 +138,6 @@ _Связки клавиш_
|
||||
| `` o `` | Открыть файл | Open file in default application. |
|
||||
| `` e `` | Редактировать файл | Open file in external editor. |
|
||||
| `` <space> `` | Добавить/удалить строку(и) для патча | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | Выйти из сборщика пользовательских патчей | |
|
||||
| `` / `` | Найти | |
|
||||
|
||||
@@ -146,7 +145,7 @@ _Связки клавиш_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
|
||||
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Открыть коммит в браузере | |
|
||||
@@ -166,7 +165,7 @@ _Связки клавиш_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
|
||||
| `` <c-r> `` | Сбросить отобранную (скопированную \| cherry-picked) выборку коммитов | |
|
||||
| `` b `` | Просмотреть параметры бинарного поиска | |
|
||||
| `` s `` | Объединить коммиты (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -253,7 +252,7 @@ _Связки клавиш_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
|
||||
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | Открыть коммит в браузере | |
|
||||
@@ -297,7 +296,7 @@ _Связки клавиш_
|
||||
| `` <c-o> `` | Скопировать название файла в буфер обмена | |
|
||||
| `` y `` | Copy to clipboard | |
|
||||
| `` c `` | Переключить | Переключить файл |
|
||||
| `` d `` | Просмотреть параметры «отмены изменении» | Отменить изменения коммита в этом файле |
|
||||
| `` d `` | Remove | Отменить изменения коммита в этом файле |
|
||||
| `` o `` | Открыть файл | Open file in default application. |
|
||||
| `` e `` | Edit | Open file in external editor. |
|
||||
| `` <c-t> `` | Open external diff tool (git difftool) | |
|
||||
@@ -308,7 +307,7 @@ _Связки клавиш_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | Найти | |
|
||||
|
||||
## Статус
|
||||
|
||||
@@ -319,7 +318,6 @@ _Связки клавиш_
|
||||
| `` u `` | Проверить обновления | |
|
||||
| `` <enter> `` | Переключиться на последний репозиторий | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## Теги
|
||||
@@ -400,7 +398,7 @@ _Связки клавиш_
|
||||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
| `` / `` | Найти | |
|
||||
|
||||
## Хранилище
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
|
||||
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(如hash、URL、diff、消息、作者)。 |
|
||||
| `` o `` | 在浏览器中打开提交 | |
|
||||
@@ -101,7 +101,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
|
||||
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
|
||||
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(如hash、URL、diff、消息、作者)。 |
|
||||
| `` o `` | 在浏览器中打开提交 | |
|
||||
@@ -121,7 +121,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 复制提交哈希到剪贴板 | |
|
||||
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
|
||||
| `` b `` | 查看二分查找选项 | |
|
||||
| `` s `` | 压缩(Squash) | 将已选提交压缩到该提交之下。这些选定的提交的消息会附加到该提交的消息之下。 |
|
||||
@@ -172,7 +172,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` <c-o> `` | 复制路径到剪贴板 | |
|
||||
| `` y `` | 复制到剪贴板 | |
|
||||
| `` c `` | 检出 | 检出文件 |
|
||||
| `` d `` | 查看'放弃变更'选项 | 放弃对此文件的提交变更 |
|
||||
| `` d `` | 删除 | 放弃对此文件的提交变更 |
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` e `` | 编辑(Edit) | 使用外部编辑器打开文件 |
|
||||
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
|
||||
@@ -183,7 +183,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
|
||||
| `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
|
||||
| `` 0 `` | 聚焦主视图 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
## 文件
|
||||
|
||||
@@ -216,7 +216,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
|
||||
| `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
|
||||
| `` 0 `` | 聚焦主视图 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
## 本地分支
|
||||
|
||||
@@ -260,7 +260,6 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
|
||||
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
|
||||
| `` <space> `` | 添加/移除 行到补丁 | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | 退出逐行模式 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
|
||||
@@ -345,7 +344,6 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
||||
| `` u `` | 检查更新 | |
|
||||
| `` <enter> `` | 切换到最近的仓库 | |
|
||||
| `` a `` | 显示/循环所有分支日志 | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | 聚焦主视图 | |
|
||||
|
||||
## 确认面板
|
||||
|
||||
@@ -72,7 +72,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
|
||||
| `` <space> `` | 向 (或從) 補丁中添加/刪除行 | |
|
||||
| `` d `` | Remove lines from commit | Remove the selected lines from this commit. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes these lines. |
|
||||
| `` <esc> `` | 退出自訂補丁建立器 | |
|
||||
| `` / `` | 搜尋 | |
|
||||
|
||||
@@ -136,7 +135,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
|
||||
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | 在瀏覽器中開啟提交 | |
|
||||
@@ -180,7 +179,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
|
||||
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
|
||||
| `` b `` | 查看二分選項 | |
|
||||
| `` s `` | 壓縮 (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
|
||||
@@ -231,7 +230,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
|
||||
| `` y `` | 複製到剪貼簿 | |
|
||||
| `` c `` | 檢出 | 檢出檔案 |
|
||||
| `` d `` | 捨棄 | 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. |
|
||||
| `` d `` | Remove | 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. |
|
||||
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
|
||||
| `` e `` | 編輯 | 使用外部編輯器開啟 |
|
||||
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
|
||||
@@ -262,7 +261,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
|
||||
| Key | Action | Info |
|
||||
|-----|--------|-------------|
|
||||
| `` <c-o> `` | Copy abbreviated commit hash to clipboard | |
|
||||
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
|
||||
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
|
||||
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
|
||||
| `` o `` | 在瀏覽器中開啟提交 | |
|
||||
@@ -374,7 +373,6 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
||||
| `` u `` | 檢查更新 | |
|
||||
| `` <enter> `` | 切換到最近使用的版本庫 | |
|
||||
| `` a `` | Show/cycle all branch logs | |
|
||||
| `` A `` | Show/cycle all branch logs (reverse) | |
|
||||
| `` 0 `` | Focus main view | |
|
||||
|
||||
## 確認面板
|
||||
|
||||
@@ -10,6 +10,7 @@ require (
|
||||
github.com/adrg/xdg v0.4.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aybabtme/humanlog v0.4.1
|
||||
github.com/cli/go-gh/v2 v2.9.0
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/gdamore/tcell/v2 v2.13.8
|
||||
@@ -18,7 +19,7 @@ require (
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c
|
||||
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20260308162933-5e45e57b5564
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20260128194906-9d8c3cdfac18
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
|
||||
@@ -39,7 +40,7 @@ require (
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.42.0
|
||||
golang.org/x/sys v0.40.0
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -49,7 +50,8 @@ require (
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/cli/safeexec v1.0.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
@@ -66,8 +68,8 @@ require (
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
@@ -78,8 +80,8 @@ require (
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
golang.org/x/term v0.39.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
|
||||
@@ -64,9 +64,13 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cli/go-gh/v2 v2.9.0 h1:D3lTjEneMYl54M+WjZ+kRPrR5CEJ5BHS05isBPOV3LI=
|
||||
github.com/cli/go-gh/v2 v2.9.0/go.mod h1:MeRoKzXff3ygHu7zP+NVTT+imcHW6p3tpuxHAzRM2xE=
|
||||
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
|
||||
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
@@ -189,8 +193,8 @@ github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c h1:tC2Paiis
|
||||
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c/go.mod h1:F2fEBk0ddf6ixrBrJjY7phfQ3hL9rXG0uSjvwYe50bE=
|
||||
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd h1:ViKj6qth8FgcIWizn9KiACWwPemWSymx62OPN0tHT+Q=
|
||||
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd/go.mod h1:lRhCiBr6XjQrvcQVa+UYsy/99d3wMXn/a0nSQlhnhlA=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20260308162933-5e45e57b5564 h1:aB/Ytu+OCEpjft/BehqbH8/PTdgLREqbCvjK1JXctoo=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20260308162933-5e45e57b5564/go.mod h1:lQCd2TvvNXVKFBowy4A7xxZbUp+1KEiGs4j0Q5Zt9gQ=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20260128194906-9d8c3cdfac18 h1:+Q17GqvNaGzuvIR1JCdIS0khVjMzdwrhXBBDxnsdN8Y=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20260128194906-9d8c3cdfac18/go.mod h1:lQCd2TvvNXVKFBowy4A7xxZbUp+1KEiGs4j0Q5Zt9gQ=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
@@ -223,13 +227,14 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
@@ -451,20 +456,20 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
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.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
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=
|
||||
@@ -475,8 +480,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
+47
-41
@@ -18,27 +18,29 @@ import (
|
||||
|
||||
// GitCommand is our main git interface
|
||||
type GitCommand struct {
|
||||
Blame *git_commands.BlameCommands
|
||||
Branch *git_commands.BranchCommands
|
||||
Commit *git_commands.CommitCommands
|
||||
Config *git_commands.ConfigCommands
|
||||
Custom *git_commands.CustomCommands
|
||||
Diff *git_commands.DiffCommands
|
||||
File *git_commands.FileCommands
|
||||
Flow *git_commands.FlowCommands
|
||||
Patch *git_commands.PatchCommands
|
||||
Rebase *git_commands.RebaseCommands
|
||||
Remote *git_commands.RemoteCommands
|
||||
Stash *git_commands.StashCommands
|
||||
Status *git_commands.StatusCommands
|
||||
Submodule *git_commands.SubmoduleCommands
|
||||
Sync *git_commands.SyncCommands
|
||||
Tag *git_commands.TagCommands
|
||||
WorkingTree *git_commands.WorkingTreeCommands
|
||||
Bisect *git_commands.BisectCommands
|
||||
Worktree *git_commands.WorktreeCommands
|
||||
Version *git_commands.GitVersion
|
||||
RepoPaths *git_commands.RepoPaths
|
||||
Blame *git_commands.BlameCommands
|
||||
Branch *git_commands.BranchCommands
|
||||
Commit *git_commands.CommitCommands
|
||||
Config *git_commands.ConfigCommands
|
||||
Custom *git_commands.CustomCommands
|
||||
Diff *git_commands.DiffCommands
|
||||
File *git_commands.FileCommands
|
||||
Flow *git_commands.FlowCommands
|
||||
Patch *git_commands.PatchCommands
|
||||
Rebase *git_commands.RebaseCommands
|
||||
Remote *git_commands.RemoteCommands
|
||||
Stash *git_commands.StashCommands
|
||||
Status *git_commands.StatusCommands
|
||||
Submodule *git_commands.SubmoduleCommands
|
||||
Sync *git_commands.SyncCommands
|
||||
Tag *git_commands.TagCommands
|
||||
WorkingTree *git_commands.WorkingTreeCommands
|
||||
Bisect *git_commands.BisectCommands
|
||||
Worktree *git_commands.WorktreeCommands
|
||||
Version *git_commands.GitVersion
|
||||
RepoPaths *git_commands.RepoPaths
|
||||
GitHub *git_commands.GitHubCommands
|
||||
HostingService *git_commands.HostingService
|
||||
|
||||
Loaders Loaders
|
||||
}
|
||||
@@ -137,6 +139,8 @@ func NewGitCommandAux(
|
||||
bisectCommands := git_commands.NewBisectCommands(gitCommon)
|
||||
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
||||
blameCommands := git_commands.NewBlameCommands(gitCommon)
|
||||
gitHubCommands := git_commands.NewGitHubCommand(gitCommon)
|
||||
hostingServiceCommands := git_commands.NewHostingServiceCommand(gitCommon)
|
||||
|
||||
branchLoader := git_commands.NewBranchLoader(cmn, gitCommon, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||
@@ -148,26 +152,28 @@ func NewGitCommandAux(
|
||||
tagLoader := git_commands.NewTagLoader(cmn, cmd)
|
||||
|
||||
return &GitCommand{
|
||||
Blame: blameCommands,
|
||||
Branch: branchCommands,
|
||||
Commit: commitCommands,
|
||||
Config: configCommands,
|
||||
Custom: customCommands,
|
||||
Diff: diffCommands,
|
||||
File: fileCommands,
|
||||
Flow: flowCommands,
|
||||
Patch: patchCommands,
|
||||
Rebase: rebaseCommands,
|
||||
Remote: remoteCommands,
|
||||
Stash: stashCommands,
|
||||
Status: statusCommands,
|
||||
Submodule: submoduleCommands,
|
||||
Sync: syncCommands,
|
||||
Tag: tagCommands,
|
||||
Bisect: bisectCommands,
|
||||
WorkingTree: workingTreeCommands,
|
||||
Worktree: worktreeCommands,
|
||||
Version: version,
|
||||
Blame: blameCommands,
|
||||
Branch: branchCommands,
|
||||
Commit: commitCommands,
|
||||
Config: configCommands,
|
||||
Custom: customCommands,
|
||||
Diff: diffCommands,
|
||||
File: fileCommands,
|
||||
Flow: flowCommands,
|
||||
Patch: patchCommands,
|
||||
Rebase: rebaseCommands,
|
||||
Remote: remoteCommands,
|
||||
Stash: stashCommands,
|
||||
Status: statusCommands,
|
||||
Submodule: submoduleCommands,
|
||||
Sync: syncCommands,
|
||||
Tag: tagCommands,
|
||||
Bisect: bisectCommands,
|
||||
WorkingTree: workingTreeCommands,
|
||||
Worktree: worktreeCommands,
|
||||
Version: version,
|
||||
GitHub: gitHubCommands,
|
||||
HostingService: hostingServiceCommands,
|
||||
Loaders: Loaders{
|
||||
BranchLoader: branchLoader,
|
||||
CommitFileLoader: commitFileLoader,
|
||||
|
||||
@@ -317,12 +317,6 @@ func (self *BranchCommands) RotateAllBranchesLogIdx() {
|
||||
self.allBranchesLogCmdIndex = (i + 1) % n
|
||||
}
|
||||
|
||||
func (self *BranchCommands) RotateAllBranchesLogIdxBackward() {
|
||||
n := len(self.allBranchesLogCandidates())
|
||||
i := self.allBranchesLogCmdIndex
|
||||
self.allBranchesLogCmdIndex = (i - 1 + n) % n
|
||||
}
|
||||
|
||||
func (self *BranchCommands) GetAllBranchesLogIdxAndCount() (int, int) {
|
||||
n := len(self.allBranchesLogCandidates())
|
||||
i := self.allBranchesLogCmdIndex
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cli/go-gh/v2/pkg/auth"
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type GitHubCommands struct {
|
||||
*GitCommon
|
||||
}
|
||||
|
||||
func NewGitHubCommand(gitCommon *GitCommon) *GitHubCommands {
|
||||
return &GitHubCommands{
|
||||
GitCommon: gitCommon,
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/cli/cli/issues/2300
|
||||
func (self *GitHubCommands) BaseRepo() error {
|
||||
cmdArgs := NewGitCmd("config").
|
||||
Arg("--local", "--get-regexp", ".gh-resolved").
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).DontLog().Run()
|
||||
}
|
||||
|
||||
// Ex: git config --local --add "remote.origin.gh-resolved" "jesseduffield/lazygit"
|
||||
func (self *GitHubCommands) SetBaseRepo(repository string) (string, error) {
|
||||
cmdArgs := NewGitCmd("config").
|
||||
Arg("--local", "--add", "remote.origin.gh-resolved", repository).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Data RepositoryQuery `json:"data"`
|
||||
}
|
||||
|
||||
type RepositoryQuery struct {
|
||||
Repository map[string]PullRequest `json:"repository"`
|
||||
}
|
||||
|
||||
type PullRequest struct {
|
||||
Edges []PullRequestEdge `json:"edges"`
|
||||
}
|
||||
|
||||
type PullRequestEdge struct {
|
||||
Node PullRequestNode `json:"node"`
|
||||
}
|
||||
|
||||
type PullRequestNode struct {
|
||||
Title string `json:"title"`
|
||||
HeadRefName string `json:"headRefName"`
|
||||
Number int `json:"number"`
|
||||
Url string `json:"url"`
|
||||
HeadRepositoryOwner GithubRepositoryOwner `json:"headRepositoryOwner"`
|
||||
State string `json:"state"`
|
||||
IsDraft bool `json:"isDraft"`
|
||||
}
|
||||
|
||||
type GithubRepositoryOwner struct {
|
||||
Login string `json:"login"`
|
||||
}
|
||||
|
||||
func fetchPullRequestsQuery(branches []string, owner string, repo string) string {
|
||||
queries := make([]string, 0, len(branches))
|
||||
for i, branch := range branches {
|
||||
// We're making a sub-query per branch, and arbitrarily labelling each subquery
|
||||
// as a1, a2, etc.
|
||||
fieldName := fmt.Sprintf("a%d", i+1)
|
||||
// We fetch a few PRs per branch name because multiple forks may have PRs
|
||||
// with the same head ref name. The mapping logic filters by owner later.
|
||||
queries = append(queries, fmt.Sprintf(`%s: pullRequests(first: 5, headRefName: "%s", orderBy: {field: CREATED_AT, direction: DESC}) {
|
||||
edges {
|
||||
node {
|
||||
title
|
||||
headRefName
|
||||
state
|
||||
number
|
||||
url
|
||||
isDraft
|
||||
headRepositoryOwner {
|
||||
login
|
||||
}
|
||||
}
|
||||
}
|
||||
}`, fieldName, branch))
|
||||
}
|
||||
|
||||
queryString := fmt.Sprintf(`{
|
||||
repository(owner: "%s", name: "%s") {
|
||||
%s
|
||||
}
|
||||
}`, owner, repo, strings.Join(queries, "\n"))
|
||||
|
||||
return queryString
|
||||
}
|
||||
|
||||
// FetchRecentPRs fetches recent pull requests using GraphQL.
|
||||
func (self *GitHubCommands) FetchRecentPRs(branches []string) ([]*models.GithubPullRequest, error) {
|
||||
repoOwner, repoName, err := self.GetBaseRepoOwnerAndName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
|
||||
var g errgroup.Group
|
||||
results := make(chan []*models.GithubPullRequest)
|
||||
|
||||
// We want at most 5 concurrent requests, but no less than 10 branches per request
|
||||
concurrency := 5
|
||||
minBranchesPerRequest := 10
|
||||
branchesPerRequest := max(len(branches)/concurrency, minBranchesPerRequest)
|
||||
for i := 0; i < len(branches); i += branchesPerRequest {
|
||||
end := i + branchesPerRequest
|
||||
if end > len(branches) {
|
||||
end = len(branches)
|
||||
}
|
||||
branchChunk := branches[i:end]
|
||||
|
||||
// Launch a goroutine for each chunk of branches
|
||||
g.Go(func() error {
|
||||
prs, err := self.FetchRecentPRsAux(repoOwner, repoName, branchChunk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results <- prs
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Close the results channel when all goroutines are done
|
||||
go func() {
|
||||
_ = g.Wait()
|
||||
close(results)
|
||||
}()
|
||||
|
||||
// Collect results from all goroutines
|
||||
var allPRs []*models.GithubPullRequest
|
||||
for prs := range results {
|
||||
allPRs = append(allPRs, prs...)
|
||||
}
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.Log.Warnf("Fetched PRs in %s", time.Since(t))
|
||||
|
||||
return allPRs, nil
|
||||
}
|
||||
|
||||
func (self *GitHubCommands) FetchRecentPRsAux(repoOwner string, repoName string, branches []string) ([]*models.GithubPullRequest, error) {
|
||||
queryString := fetchPullRequestsQuery(branches, repoOwner, repoName)
|
||||
escapedQueryString := strconv.Quote(queryString)
|
||||
|
||||
body := fmt.Sprintf(`{"query": %s}`, escapedQueryString)
|
||||
req, err := http.NewRequest("POST", "https://api.github.com/graphql", bytes.NewBuffer([]byte(body)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defaultHost, _ := auth.DefaultHost()
|
||||
token, _ := auth.TokenForHost(defaultHost)
|
||||
if token == "" {
|
||||
return nil, fmt.Errorf("No token found for GitHub")
|
||||
}
|
||||
req.Header.Set("Authorization", "token "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyStr := new(bytes.Buffer)
|
||||
_, _ = bodyStr.ReadFrom(resp.Body)
|
||||
return nil, fmt.Errorf("GraphQL query failed with status: %s. Body: %s", resp.Status, bodyStr.String())
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result Response
|
||||
err = json.Unmarshal(bodyBytes, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prs := []*models.GithubPullRequest{}
|
||||
for _, repoQuery := range result.Data.Repository {
|
||||
for _, edge := range repoQuery.Edges {
|
||||
node := edge.Node
|
||||
pr := &models.GithubPullRequest{
|
||||
HeadRefName: node.HeadRefName,
|
||||
Number: node.Number,
|
||||
Title: node.Title,
|
||||
State: lo.Ternary(node.IsDraft && node.State != "CLOSED", "DRAFT", node.State),
|
||||
Url: node.Url,
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{
|
||||
Login: node.HeadRepositoryOwner.Login,
|
||||
},
|
||||
}
|
||||
prs = append(prs, pr)
|
||||
}
|
||||
}
|
||||
|
||||
return prs, nil
|
||||
}
|
||||
|
||||
// returns a map from branch name to pull request
|
||||
func GenerateGithubPullRequestMap(
|
||||
prs []*models.GithubPullRequest,
|
||||
branches []*models.Branch,
|
||||
remotes []*models.Remote,
|
||||
) map[string]*models.GithubPullRequest {
|
||||
res := map[string]*models.GithubPullRequest{}
|
||||
|
||||
if len(prs) == 0 {
|
||||
return res
|
||||
}
|
||||
|
||||
remotesToOwnersMap := getRemotesToOwnersMap(remotes)
|
||||
|
||||
// A PR can be identified by two things: the owner e.g. 'jesseduffield' and the
|
||||
// branch name e.g. 'feature/my-feature'. The owner might be different
|
||||
// to the owner of the repo if the PR is from a fork of that repo.
|
||||
type prKey struct {
|
||||
owner string
|
||||
branchName string
|
||||
}
|
||||
|
||||
prByKey := map[prKey]models.GithubPullRequest{}
|
||||
|
||||
for _, pr := range prs {
|
||||
key := prKey{owner: pr.UserName(), branchName: pr.BranchName()}
|
||||
// PRs are returned newest-first from the API, so the first one we
|
||||
// see for each key is the most recent and therefore the most relevant.
|
||||
if _, exists := prByKey[key]; !exists {
|
||||
prByKey[key] = *pr
|
||||
}
|
||||
}
|
||||
|
||||
for _, branch := range branches {
|
||||
if !branch.IsTrackingRemote() {
|
||||
continue
|
||||
}
|
||||
|
||||
owner, foundRemoteOwner := remotesToOwnersMap[branch.UpstreamRemote]
|
||||
if !foundRemoteOwner {
|
||||
// UpstreamRemote may be a full URL rather than a remote name;
|
||||
// try parsing the owner directly from it.
|
||||
repoInfo, err := hosting_service.GetRepoInfoFromURL(branch.UpstreamRemote)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
owner = repoInfo.Owner
|
||||
}
|
||||
|
||||
pr, hasPr := prByKey[prKey{owner: owner, branchName: branch.UpstreamBranch}]
|
||||
|
||||
if !hasPr {
|
||||
continue
|
||||
}
|
||||
|
||||
res[branch.Name] = &pr
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func getRemotesToOwnersMap(remotes []*models.Remote) map[string]string {
|
||||
res := map[string]string{}
|
||||
for _, remote := range remotes {
|
||||
if len(remote.Urls) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
repoInfo, err := hosting_service.GetRepoInfoFromURL(remote.Urls[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
res[remote.Name] = repoInfo.Owner
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// return <installed>, <valid version>
|
||||
func (self *GitHubCommands) DetermineGitHubCliState() (bool, bool) {
|
||||
output, err := self.cmd.New([]string{"gh", "--version"}).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
// assuming a failure here means that it's not installed
|
||||
return false, false
|
||||
}
|
||||
|
||||
if !isGhVersionValid(output) {
|
||||
return true, false
|
||||
}
|
||||
|
||||
return true, true
|
||||
}
|
||||
|
||||
func isGhVersionValid(versionStr string) bool {
|
||||
// output should be something like:
|
||||
// gh version 2.0.0 (2021-08-23)
|
||||
// https://github.com/cli/cli/releases/tag/v2.0.0
|
||||
re := regexp.MustCompile(`[^\d]+([\d\.]+)`)
|
||||
matches := re.FindStringSubmatch(versionStr)
|
||||
|
||||
if len(matches) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
ghVersion := matches[1]
|
||||
majorVersion, err := strconv.Atoi(ghVersion[0:1])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if majorVersion < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *GitHubCommands) InGithubRepo() bool {
|
||||
remotes, err := self.repo.Remotes()
|
||||
if err != nil {
|
||||
self.Log.Error(err)
|
||||
return false
|
||||
}
|
||||
|
||||
if len(remotes) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
remote := GetMainRemote(remotes)
|
||||
|
||||
if len(remote.Config().URLs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
url := remote.Config().URLs[0]
|
||||
return strings.Contains(url, "github.com")
|
||||
}
|
||||
|
||||
func GetMainRemote(remotes []*gogit.Remote) *gogit.Remote {
|
||||
for _, remote := range remotes {
|
||||
if remote.Config().Name == "origin" {
|
||||
return remote
|
||||
}
|
||||
}
|
||||
|
||||
// need to sort remotes by name so that this is deterministic
|
||||
return lo.MinBy(remotes, func(a, b *gogit.Remote) bool {
|
||||
return a.Config().Name < b.Config().Name
|
||||
})
|
||||
}
|
||||
|
||||
func GetSuggestedRemoteName(remotes []*models.Remote) string {
|
||||
if len(remotes) == 0 {
|
||||
return "origin"
|
||||
}
|
||||
|
||||
for _, remote := range remotes {
|
||||
if remote.Name == "origin" {
|
||||
return remote.Name
|
||||
}
|
||||
}
|
||||
|
||||
return remotes[0].Name
|
||||
}
|
||||
|
||||
func (self *GitHubCommands) GetBaseRepoOwnerAndName() (string, string, error) {
|
||||
remotes, err := self.repo.Remotes()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if len(remotes) == 0 {
|
||||
return "", "", fmt.Errorf("No remotes found")
|
||||
}
|
||||
|
||||
originRemote, ok := lo.Find(remotes, func(remote *gogit.Remote) bool {
|
||||
return remote.Config().Name == "origin"
|
||||
})
|
||||
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("Origin remote not found")
|
||||
}
|
||||
|
||||
if len(originRemote.Config().URLs) == 0 {
|
||||
return "", "", fmt.Errorf("No URLs found for remote")
|
||||
}
|
||||
|
||||
url := originRemote.Config().URLs[0]
|
||||
|
||||
repoInfo, err := hosting_service.GetRepoInfoFromURL(url)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return repoInfo.Owner, repoInfo.Repository, nil
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsValidGhVersion(t *testing.T) {
|
||||
type scenario struct {
|
||||
versionStr string
|
||||
expectedResult bool
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"",
|
||||
false,
|
||||
},
|
||||
{
|
||||
`gh version 1.0.0 (2020-08-23)
|
||||
https://github.com/cli/cli/releases/tag/v1.0.0`,
|
||||
false,
|
||||
},
|
||||
{
|
||||
`gh version 2.0.0 (2021-08-23)
|
||||
https://github.com/cli/cli/releases/tag/v2.0.0`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`gh version 1.1.0 (2021-10-14)
|
||||
https://github.com/cli/cli/releases/tag/v1.1.0
|
||||
|
||||
A new release of gh is available: 1.1.0 → v2.2.0
|
||||
To upgrade, run: brew update && brew upgrade gh
|
||||
https://github.com/cli/cli/releases/tag/v2.2.0`,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.versionStr, func(t *testing.T) {
|
||||
result := isGhVersionValid(s.versionStr)
|
||||
assert.Equal(t, result, s.expectedResult)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSuggestedRemoteName(t *testing.T) {
|
||||
cases := []struct {
|
||||
remotes []*models.Remote
|
||||
expected string
|
||||
}{
|
||||
{mkRemoteList(), "origin"},
|
||||
{mkRemoteList("upstream", "origin", "foo"), "origin"},
|
||||
{mkRemoteList("upstream", "foo", "bar"), "upstream"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
result := GetSuggestedRemoteName(c.remotes)
|
||||
assert.EqualValues(t, c.expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRepoInfoFromURL(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
url string
|
||||
expected hosting_service.RepoInformation
|
||||
}{
|
||||
{
|
||||
name: "SSH URL",
|
||||
url: "git@github.com:jesseduffield/lazygit.git",
|
||||
expected: hosting_service.RepoInformation{
|
||||
Owner: "jesseduffield",
|
||||
Repository: "lazygit",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "HTTPS URL",
|
||||
url: "https://github.com/jesseduffield/lazygit.git",
|
||||
expected: hosting_service.RepoInformation{
|
||||
Owner: "jesseduffield",
|
||||
Repository: "lazygit",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "HTTPS URL without .git",
|
||||
url: "https://github.com/jesseduffield/lazygit",
|
||||
expected: hosting_service.RepoInformation{
|
||||
Owner: "jesseduffield",
|
||||
Repository: "lazygit",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SSH URL with org nesting",
|
||||
url: "git@github.com:my-org/sub-group/lazygit.git",
|
||||
expected: hosting_service.RepoInformation{
|
||||
Owner: "my-org/sub-group",
|
||||
Repository: "lazygit",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
result, err := hosting_service.GetRepoInfoFromURL(c.url)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateGithubPullRequestMap(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
prs []*models.GithubPullRequest
|
||||
branches []*models.Branch
|
||||
remotes []*models.Remote
|
||||
expected map[string]*models.GithubPullRequest
|
||||
}{
|
||||
{
|
||||
name: "empty inputs",
|
||||
prs: []*models.GithubPullRequest{},
|
||||
branches: []*models.Branch{},
|
||||
remotes: []*models.Remote{},
|
||||
expected: map[string]*models.GithubPullRequest{},
|
||||
},
|
||||
{
|
||||
name: "matches PR to branch tracking origin",
|
||||
prs: []*models.GithubPullRequest{
|
||||
{
|
||||
HeadRefName: "feature-branch",
|
||||
Number: 42,
|
||||
Title: "Add feature",
|
||||
State: "OPEN",
|
||||
Url: "https://github.com/jesseduffield/lazygit/pull/42",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{
|
||||
Name: "feature-branch",
|
||||
UpstreamRemote: "origin",
|
||||
UpstreamBranch: "feature-branch",
|
||||
},
|
||||
},
|
||||
remotes: []*models.Remote{
|
||||
{
|
||||
Name: "origin",
|
||||
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
|
||||
},
|
||||
},
|
||||
expected: map[string]*models.GithubPullRequest{
|
||||
"feature-branch": {
|
||||
HeadRefName: "feature-branch",
|
||||
Number: 42,
|
||||
Title: "Add feature",
|
||||
State: "OPEN",
|
||||
Url: "https://github.com/jesseduffield/lazygit/pull/42",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "does not match branch without upstream",
|
||||
prs: []*models.GithubPullRequest{
|
||||
{
|
||||
HeadRefName: "feature-branch",
|
||||
Number: 42,
|
||||
Title: "Add feature",
|
||||
State: "OPEN",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{
|
||||
Name: "feature-branch",
|
||||
// no upstream set
|
||||
},
|
||||
},
|
||||
remotes: []*models.Remote{
|
||||
{
|
||||
Name: "origin",
|
||||
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
|
||||
},
|
||||
},
|
||||
expected: map[string]*models.GithubPullRequest{},
|
||||
},
|
||||
{
|
||||
name: "matches fork PR to branch tracking fork remote",
|
||||
prs: []*models.GithubPullRequest{
|
||||
{
|
||||
HeadRefName: "fix-bug",
|
||||
Number: 99,
|
||||
Title: "Fix bug",
|
||||
State: "OPEN",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
|
||||
},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{
|
||||
Name: "fix-bug",
|
||||
UpstreamRemote: "contributor",
|
||||
UpstreamBranch: "fix-bug",
|
||||
},
|
||||
},
|
||||
remotes: []*models.Remote{
|
||||
{
|
||||
Name: "origin",
|
||||
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
|
||||
},
|
||||
{
|
||||
Name: "contributor",
|
||||
Urls: []string{"git@github.com:contributor/lazygit.git"},
|
||||
},
|
||||
},
|
||||
expected: map[string]*models.GithubPullRequest{
|
||||
"fix-bug": {
|
||||
HeadRefName: "fix-bug",
|
||||
Number: 99,
|
||||
Title: "Fix bug",
|
||||
State: "OPEN",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "does not match when owner differs",
|
||||
prs: []*models.GithubPullRequest{
|
||||
{
|
||||
HeadRefName: "feature-branch",
|
||||
Number: 42,
|
||||
Title: "Add feature",
|
||||
State: "OPEN",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "someone-else"},
|
||||
},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{
|
||||
Name: "feature-branch",
|
||||
UpstreamRemote: "origin",
|
||||
UpstreamBranch: "feature-branch",
|
||||
},
|
||||
},
|
||||
remotes: []*models.Remote{
|
||||
{
|
||||
Name: "origin",
|
||||
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
|
||||
},
|
||||
},
|
||||
expected: map[string]*models.GithubPullRequest{},
|
||||
},
|
||||
{
|
||||
name: "matches when UpstreamRemote is a full URL",
|
||||
prs: []*models.GithubPullRequest{
|
||||
{
|
||||
HeadRefName: "my-branch",
|
||||
Number: 55,
|
||||
Title: "Full URL upstream",
|
||||
State: "OPEN",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
|
||||
},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{
|
||||
Name: "my-branch",
|
||||
UpstreamRemote: "git@github.com:contributor/lazygit.git",
|
||||
UpstreamBranch: "my-branch",
|
||||
},
|
||||
},
|
||||
remotes: []*models.Remote{
|
||||
{
|
||||
Name: "origin",
|
||||
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
|
||||
},
|
||||
},
|
||||
expected: map[string]*models.GithubPullRequest{
|
||||
"my-branch": {
|
||||
HeadRefName: "my-branch",
|
||||
Number: 55,
|
||||
Title: "Full URL upstream",
|
||||
State: "OPEN",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "uses first PR when branch name is reused (API returns newest first)",
|
||||
prs: []*models.GithubPullRequest{
|
||||
// API returns newest first (CREATED_AT DESC)
|
||||
{
|
||||
HeadRefName: "update-sponsors",
|
||||
Number: 50,
|
||||
Title: "Newest PR",
|
||||
State: "CLOSED",
|
||||
Url: "https://github.com/jesseduffield/lazygit/pull/50",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
{
|
||||
HeadRefName: "update-sponsors",
|
||||
Number: 30,
|
||||
Title: "Middle PR",
|
||||
State: "OPEN",
|
||||
Url: "https://github.com/jesseduffield/lazygit/pull/30",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
{
|
||||
HeadRefName: "update-sponsors",
|
||||
Number: 10,
|
||||
Title: "Oldest PR",
|
||||
State: "CLOSED",
|
||||
Url: "https://github.com/jesseduffield/lazygit/pull/10",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{
|
||||
Name: "update-sponsors",
|
||||
UpstreamRemote: "origin",
|
||||
UpstreamBranch: "update-sponsors",
|
||||
},
|
||||
},
|
||||
remotes: []*models.Remote{
|
||||
{
|
||||
Name: "origin",
|
||||
Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
|
||||
},
|
||||
},
|
||||
expected: map[string]*models.GithubPullRequest{
|
||||
"update-sponsors": {
|
||||
HeadRefName: "update-sponsors",
|
||||
Number: 50,
|
||||
Title: "Newest PR",
|
||||
State: "CLOSED",
|
||||
Url: "https://github.com/jesseduffield/lazygit/pull/50",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "matches with HTTPS remote URL",
|
||||
prs: []*models.GithubPullRequest{
|
||||
{
|
||||
HeadRefName: "my-pr",
|
||||
Number: 10,
|
||||
Title: "My PR",
|
||||
State: "MERGED",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
},
|
||||
branches: []*models.Branch{
|
||||
{
|
||||
Name: "my-pr",
|
||||
UpstreamRemote: "origin",
|
||||
UpstreamBranch: "my-pr",
|
||||
},
|
||||
},
|
||||
remotes: []*models.Remote{
|
||||
{
|
||||
Name: "origin",
|
||||
Urls: []string{"https://github.com/jesseduffield/lazygit.git"},
|
||||
},
|
||||
},
|
||||
expected: map[string]*models.GithubPullRequest{
|
||||
"my-pr": {
|
||||
HeadRefName: "my-pr",
|
||||
Number: 10,
|
||||
Title: "My PR",
|
||||
State: "MERGED",
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
result := GenerateGithubPullRequestMap(c.prs, c.branches, c.remotes)
|
||||
assert.Equal(t, c.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mkRemoteList(names ...string) []*models.Remote {
|
||||
return lo.Map(names, func(name string, _ int) *models.Remote {
|
||||
return &models.Remote{Name: name}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package git_commands
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
|
||||
|
||||
// a hosting service is something like github, gitlab, bitbucket etc
|
||||
type HostingService struct {
|
||||
*GitCommon
|
||||
}
|
||||
|
||||
func NewHostingServiceCommand(gitCommon *GitCommon) *HostingService {
|
||||
return &HostingService{
|
||||
GitCommon: gitCommon,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *HostingService) GetPullRequestURL(from string, to string) (string, error) {
|
||||
return self.getHostingServiceMgr(self.config.GetRemoteURL()).GetPullRequestURL(from, to)
|
||||
}
|
||||
|
||||
func (self *HostingService) GetCommitURL(commitSha string) (string, error) {
|
||||
return self.getHostingServiceMgr(self.config.GetRemoteURL()).GetCommitURL(commitSha)
|
||||
}
|
||||
|
||||
func (self *HostingService) GetRepoNameFromRemoteURL(remoteURL string) (string, error) {
|
||||
return self.getHostingServiceMgr(remoteURL).GetRepoName()
|
||||
}
|
||||
|
||||
// getting this on every request rather than storing it in state in case our remoteURL changes
|
||||
// from one invocation to the next. Note however that we're currently caching config
|
||||
// results so we might want to invalidate the cache here if it becomes a problem.
|
||||
func (self *HostingService) getHostingServiceMgr(remoteURL string) *hosting_service.HostingServiceMgr {
|
||||
configServices := self.UserConfig().Services
|
||||
return hosting_service.NewHostingServiceMgr(self.Log, self.Tr, remoteURL, configServices)
|
||||
}
|
||||
@@ -246,11 +246,7 @@ func (self *WorkingTreeCommands) Ignore(filename string) error {
|
||||
|
||||
// Exclude adds a file to the .git/info/exclude for the repo
|
||||
func (self *WorkingTreeCommands) Exclude(filename string) error {
|
||||
infoDir := filepath.Join(self.repoPaths.repoGitDirPath, "info")
|
||||
if err := os.MkdirAll(infoDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
excludeFile := filepath.Join(infoDir, "exclude")
|
||||
excludeFile := filepath.Join(self.repoPaths.repoGitDirPath, "info", "exclude")
|
||||
return self.os.AppendLineToFile(excludeFile, escapeFilename(filename))
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,6 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
||||
// we can parallelize the calls to git rev-parse
|
||||
GitDir: "",
|
||||
}
|
||||
} else if strings.HasPrefix(splitLine, "HEAD ") {
|
||||
current.Head = strings.SplitN(splitLine, " ", 2)[1]
|
||||
} else if strings.HasPrefix(splitLine, "branch ") {
|
||||
branch := strings.SplitN(splitLine, " ", 2)[1]
|
||||
current.Branch = strings.TrimPrefix(branch, "refs/heads/")
|
||||
|
||||
@@ -46,7 +46,6 @@ branch refs/heads/mybranch
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git",
|
||||
Branch: "mybranch",
|
||||
Head: "d85cc9d281fa6ae1665c68365fc70e75e82a042d",
|
||||
Name: "repo",
|
||||
},
|
||||
},
|
||||
@@ -87,7 +86,6 @@ branch refs/heads/mybranch-worktree
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git",
|
||||
Branch: "mybranch",
|
||||
Head: "d85cc9d281fa6ae1665c68365fc70e75e82a042d",
|
||||
Name: "repo",
|
||||
},
|
||||
{
|
||||
@@ -97,7 +95,6 @@ branch refs/heads/mybranch-worktree
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git/worktrees/repo-worktree",
|
||||
Branch: "mybranch-worktree",
|
||||
Head: "775955775e79b8f5b4c4b56f82fbf657e2d5e4de",
|
||||
Name: "repo-worktree",
|
||||
},
|
||||
},
|
||||
@@ -127,7 +124,6 @@ branch refs/heads/missingbranch
|
||||
IsPathMissing: true,
|
||||
GitDir: "",
|
||||
Branch: "missingbranch",
|
||||
Head: "775955775e79b8f5b4c4b56f82fbf657e2d5e4de",
|
||||
Name: "worktree",
|
||||
},
|
||||
},
|
||||
@@ -168,7 +164,6 @@ branch refs/heads/mybranch-worktree
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git/worktrees/repo-worktree",
|
||||
Branch: "mybranch-worktree",
|
||||
Head: "775955775e79b8f5b4c4b56f82fbf657e2d5e4de",
|
||||
Name: "repo-worktree",
|
||||
},
|
||||
{
|
||||
@@ -178,62 +173,11 @@ branch refs/heads/mybranch-worktree
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git",
|
||||
Branch: "mybranch",
|
||||
Head: "d85cc9d281fa6ae1665c68365fc70e75e82a042d",
|
||||
Name: "repo",
|
||||
},
|
||||
},
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
testName: "Detached HEAD worktree",
|
||||
repoPaths: &RepoPaths{
|
||||
repoPath: "/path/to/repo",
|
||||
worktreePath: "/path/to/repo",
|
||||
},
|
||||
before: func(runner *oscommands.FakeCmdObjRunner, fs afero.Fs, getRevParseArgs argFn) {
|
||||
runner.ExpectGitArgs([]string{"worktree", "list", "--porcelain"},
|
||||
`worktree /path/to/repo
|
||||
HEAD d85cc9d281fa6ae1665c68365fc70e75e82a042d
|
||||
branch refs/heads/mybranch
|
||||
|
||||
worktree /path/to/repo-worktree
|
||||
HEAD 775955775e79b8f5b4c4b56f82fbf657e2d5e4de
|
||||
detached
|
||||
`,
|
||||
nil)
|
||||
gitArgsMainWorktree := append(append([]string{"-C", "/path/to/repo"}, getRevParseArgs()...), "--absolute-git-dir")
|
||||
runner.ExpectGitArgs(gitArgsMainWorktree, "/path/to/repo/.git", nil)
|
||||
gitArgsLinkedWorktree := append(append([]string{"-C", "/path/to/repo-worktree"}, getRevParseArgs()...), "--absolute-git-dir")
|
||||
runner.ExpectGitArgs(gitArgsLinkedWorktree, "/path/to/repo/.git/worktrees/repo-worktree", nil)
|
||||
|
||||
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
|
||||
_ = fs.MkdirAll("/path/to/repo-worktree", 0o755)
|
||||
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/repo-worktree", 0o755)
|
||||
},
|
||||
expectedWorktrees: []*models.Worktree{
|
||||
{
|
||||
IsMain: true,
|
||||
IsCurrent: true,
|
||||
Path: "/path/to/repo",
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git",
|
||||
Branch: "mybranch",
|
||||
Head: "d85cc9d281fa6ae1665c68365fc70e75e82a042d",
|
||||
Name: "repo",
|
||||
},
|
||||
{
|
||||
IsMain: false,
|
||||
IsCurrent: false,
|
||||
Path: "/path/to/repo-worktree",
|
||||
IsPathMissing: false,
|
||||
GitDir: "/path/to/repo/.git/worktrees/repo-worktree",
|
||||
Branch: "",
|
||||
Head: "775955775e79b8f5b4c4b56f82fbf657e2d5e4de",
|
||||
Name: "repo-worktree",
|
||||
},
|
||||
},
|
||||
expectedErr: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
@@ -6,7 +6,11 @@ var defaultUrlRegexStrings = []string{
|
||||
`^(?:https?|ssh)://[^/]+/(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
|
||||
`^(.*?@)?.*:/*(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
|
||||
}
|
||||
var defaultRepoURLTemplate = "https://{{.webDomain}}/{{.owner}}/{{.repo}}"
|
||||
|
||||
var (
|
||||
defaultRepoURLTemplate = "https://{{.webDomain}}/{{.owner}}/{{.repo}}"
|
||||
defaultRepoNameTemplate = "{{.owner}}/{{.repo}}"
|
||||
)
|
||||
|
||||
// we've got less type safety using go templates but this lends itself better to
|
||||
// users adding custom service definitions in their config
|
||||
@@ -17,6 +21,7 @@ var githubServiceDef = ServiceDefinition{
|
||||
commitURL: "/commit/{{.CommitHash}}",
|
||||
regexStrings: defaultUrlRegexStrings,
|
||||
repoURLTemplate: defaultRepoURLTemplate,
|
||||
repoNameTemplate: defaultRepoNameTemplate,
|
||||
}
|
||||
|
||||
var bitbucketServiceDef = ServiceDefinition{
|
||||
@@ -28,7 +33,8 @@ var bitbucketServiceDef = ServiceDefinition{
|
||||
`^(?:https?|ssh)://.*/(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
|
||||
`^.*@.*:/*(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
|
||||
},
|
||||
repoURLTemplate: defaultRepoURLTemplate,
|
||||
repoURLTemplate: defaultRepoURLTemplate,
|
||||
repoNameTemplate: defaultRepoNameTemplate,
|
||||
}
|
||||
|
||||
var gitLabServiceDef = ServiceDefinition{
|
||||
@@ -38,6 +44,7 @@ var gitLabServiceDef = ServiceDefinition{
|
||||
commitURL: "/-/commit/{{.CommitHash}}",
|
||||
regexStrings: defaultUrlRegexStrings,
|
||||
repoURLTemplate: defaultRepoURLTemplate,
|
||||
repoNameTemplate: defaultRepoNameTemplate,
|
||||
}
|
||||
|
||||
var azdoServiceDef = ServiceDefinition{
|
||||
@@ -52,6 +59,8 @@ var azdoServiceDef = ServiceDefinition{
|
||||
`^https://.*/(?P<org>.*?)/(?P<project>.*?)/_git/(?P<repo>.*?)(?:\.git)?$`,
|
||||
},
|
||||
repoURLTemplate: "https://{{.webDomain}}/{{.org}}/{{.project}}/_git/{{.repo}}",
|
||||
// TODO: verify this is actually correct
|
||||
repoNameTemplate: "{{.org}}/{{.project}}/{{.repo}}",
|
||||
}
|
||||
|
||||
var bitbucketServerServiceDef = ServiceDefinition{
|
||||
@@ -64,6 +73,8 @@ var bitbucketServerServiceDef = ServiceDefinition{
|
||||
`^https://.*/scm/(?P<project>.*)/(?P<repo>.*?)(?:\.git)?$`,
|
||||
},
|
||||
repoURLTemplate: "https://{{.webDomain}}/projects/{{.project}}/repos/{{.repo}}",
|
||||
// TODO: verify this is actually correct
|
||||
repoNameTemplate: "{{.project}}/{{.repo}}",
|
||||
}
|
||||
|
||||
var giteaServiceDef = ServiceDefinition{
|
||||
|
||||
@@ -61,6 +61,18 @@ func (self *HostingServiceMgr) GetCommitURL(commitHash string) (string, error) {
|
||||
return pullRequestURL, nil
|
||||
}
|
||||
|
||||
// e.g. 'jesseduffield/lazygit'
|
||||
func (self *HostingServiceMgr) GetRepoName() (string, error) {
|
||||
gitService, err := self.getService()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
repoName := gitService.repoName
|
||||
|
||||
return repoName, nil
|
||||
}
|
||||
|
||||
func (self *HostingServiceMgr) getService() (*Service, error) {
|
||||
serviceDomain, err := self.getServiceDomain(self.remoteURL)
|
||||
if err != nil {
|
||||
@@ -72,8 +84,14 @@ func (self *HostingServiceMgr) getService() (*Service, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoName, err := serviceDomain.serviceDefinition.getRepoNameFromRemoteURL(self.remoteURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
repoURL: repoURL,
|
||||
repoName: repoName,
|
||||
ServiceDefinition: serviceDomain.serviceDefinition,
|
||||
}, nil
|
||||
}
|
||||
@@ -144,24 +162,68 @@ type ServiceDefinition struct {
|
||||
regexStrings []string
|
||||
|
||||
// can expect 'webdomain' to be passed in. Otherwise, you get to pick what we match in the regex
|
||||
repoURLTemplate string
|
||||
repoURLTemplate string
|
||||
repoNameTemplate string
|
||||
}
|
||||
|
||||
func (self ServiceDefinition) getRepoURLFromRemoteURL(url string, webDomain string) (string, error) {
|
||||
matches, err := self.parseRemoteUrl(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
matches["webDomain"] = webDomain
|
||||
return utils.ResolvePlaceholderString(self.repoURLTemplate, matches), nil
|
||||
}
|
||||
|
||||
func (self ServiceDefinition) getRepoNameFromRemoteURL(url string) (string, error) {
|
||||
matches, err := self.parseRemoteUrl(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return utils.ResolvePlaceholderString(self.repoNameTemplate, matches), nil
|
||||
}
|
||||
|
||||
func (self ServiceDefinition) parseRemoteUrl(url string) (map[string]string, error) {
|
||||
for _, regexStr := range self.regexStrings {
|
||||
re := regexp.MustCompile(regexStr)
|
||||
input := utils.FindNamedMatches(re, url)
|
||||
if input != nil {
|
||||
input["webDomain"] = webDomain
|
||||
return utils.ResolvePlaceholderString(self.repoURLTemplate, input), nil
|
||||
matches := utils.FindNamedMatches(re, url)
|
||||
if matches != nil {
|
||||
return matches, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("Failed to parse repo information from url")
|
||||
return nil, errors.New("Failed to parse repo information from url")
|
||||
}
|
||||
|
||||
// RepoInformation holds the owner and repository name parsed from a remote URL.
|
||||
type RepoInformation struct {
|
||||
Owner string
|
||||
Repository string
|
||||
}
|
||||
|
||||
// GetRepoInfoFromURL parses a remote URL (SSH or HTTPS) and extracts the
|
||||
// owner and repository name using the default URL regex patterns.
|
||||
func GetRepoInfoFromURL(url string) (RepoInformation, error) {
|
||||
for _, regexStr := range defaultUrlRegexStrings {
|
||||
re := regexp.MustCompile(regexStr)
|
||||
matches := utils.FindNamedMatches(re, url)
|
||||
if matches != nil {
|
||||
return RepoInformation{
|
||||
Owner: matches["owner"],
|
||||
Repository: matches["repo"],
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return RepoInformation{}, errors.New("Failed to parse repo information from url")
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
repoURL string
|
||||
// e.g. 'jesseduffield/lazygit'
|
||||
repoName string
|
||||
ServiceDefinition
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package models
|
||||
|
||||
// TODO: see if I need to store the head repo name in case it differs from the base repo
|
||||
type GithubPullRequest struct {
|
||||
HeadRefName string `json:"headRefName"`
|
||||
Number int `json:"number"`
|
||||
Title string `json:"title"`
|
||||
State string `json:"state"` // "MERGED", "OPEN", "CLOSED", "DRAFT"
|
||||
Url string `json:"url"`
|
||||
HeadRepositoryOwner GithubRepositoryOwner `json:"headRepositoryOwner"`
|
||||
}
|
||||
|
||||
func (pr *GithubPullRequest) UserName() string {
|
||||
// e.g. 'jesseduffield'
|
||||
return pr.HeadRepositoryOwner.Login
|
||||
}
|
||||
|
||||
func (pr *GithubPullRequest) BranchName() string {
|
||||
// e.g. 'feature/my-feature'
|
||||
return pr.HeadRefName
|
||||
}
|
||||
|
||||
type GithubRepositoryOwner struct {
|
||||
Login string `json:"login"`
|
||||
}
|
||||
@@ -19,9 +19,6 @@ type Worktree struct {
|
||||
// * the worktree is mid-rebase on the branch
|
||||
// * the worktree is mid-bisect on the branch
|
||||
Branch string
|
||||
// The HEAD sha of the worktree. Always populated (even when Branch is set).
|
||||
// Used for display when Branch is empty (detached HEAD state).
|
||||
Head string
|
||||
// based on the path, but uniquified. Not the same name that git uses in the worktrees/ folder (no good reason for this,
|
||||
// I just prefer my naming convention better)
|
||||
Name string
|
||||
|
||||
@@ -704,10 +704,27 @@ type AppState struct {
|
||||
ShellCommandsHistory []string `yaml:"customcommandshistory"`
|
||||
|
||||
HideCommandLog bool
|
||||
|
||||
// Cache of GitHub pull requests per repo path, so that PR info can be
|
||||
// shown instantly on startup before the async refresh completes.
|
||||
GithubPullRequests map[string][]CachedPullRequest `yaml:"githubPullRequests"`
|
||||
}
|
||||
|
||||
// CachedPullRequest stores the essential fields of a GitHub pull request
|
||||
// for persisting in the app state cache.
|
||||
type CachedPullRequest struct {
|
||||
HeadRefName string `yaml:"headRefName"`
|
||||
Number int `yaml:"number"`
|
||||
Title string `yaml:"title"`
|
||||
State string `yaml:"state"`
|
||||
Url string `yaml:"url"`
|
||||
HeadRepositoryOwner string `yaml:"headRepositoryOwner"`
|
||||
}
|
||||
|
||||
func getDefaultAppState() *AppState {
|
||||
return &AppState{}
|
||||
return &AppState{
|
||||
GithubPullRequests: make(map[string][]CachedPullRequest),
|
||||
}
|
||||
}
|
||||
|
||||
func LogPath() (string, error) {
|
||||
|
||||
@@ -321,6 +321,8 @@ type GitConfig struct {
|
||||
RemoteBranchSortOrder string `yaml:"remoteBranchSortOrder" jsonschema:"enum=date,enum=alphabetical"`
|
||||
// When copying commit hashes to the clipboard, truncate them to this length. Set to 40 to disable truncation.
|
||||
TruncateCopiedCommitHashesTo int `yaml:"truncateCopiedCommitHashesTo"`
|
||||
// If true and if `gh` is installed and on version >=2, we will use `gh` to display pull requests against branches.
|
||||
EnableGithubCli bool `yaml:"enableGithubCli"`
|
||||
}
|
||||
|
||||
type PagerType string
|
||||
@@ -493,10 +495,9 @@ type KeybindingUniversalConfig struct {
|
||||
}
|
||||
|
||||
type KeybindingStatusConfig struct {
|
||||
CheckForUpdate string `yaml:"checkForUpdate"`
|
||||
RecentRepos string `yaml:"recentRepos"`
|
||||
AllBranchesLogGraph string `yaml:"allBranchesLogGraph"`
|
||||
AllBranchesLogGraphReverse string `yaml:"allBranchesLogGraphReverse"`
|
||||
CheckForUpdate string `yaml:"checkForUpdate"`
|
||||
RecentRepos string `yaml:"recentRepos"`
|
||||
AllBranchesLogGraph string `yaml:"allBranchesLogGraph"`
|
||||
}
|
||||
|
||||
type KeybindingFilesConfig struct {
|
||||
@@ -858,6 +859,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
BranchPrefix: "",
|
||||
ParseEmoji: false,
|
||||
TruncateCopiedCommitHashesTo: 12,
|
||||
EnableGithubCli: true,
|
||||
},
|
||||
Refresher: RefresherConfig{
|
||||
RefreshInterval: 10,
|
||||
@@ -956,10 +958,9 @@ func GetDefaultConfig() *UserConfig {
|
||||
OpenDiffTool: "<c-t>",
|
||||
},
|
||||
Status: KeybindingStatusConfig{
|
||||
CheckForUpdate: "u",
|
||||
RecentRepos: "<enter>",
|
||||
AllBranchesLogGraph: "a",
|
||||
AllBranchesLogGraphReverse: "A",
|
||||
CheckForUpdate: "u",
|
||||
RecentRepos: "<enter>",
|
||||
AllBranchesLogGraph: "a",
|
||||
},
|
||||
Files: KeybindingFilesConfig{
|
||||
CommitChanges: "c",
|
||||
|
||||
+13
-17
@@ -52,7 +52,7 @@ func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
||||
}
|
||||
|
||||
if self.gui.Config.GetDebug() {
|
||||
self.goEvery(time.Second*time.Duration(10), self.gui.stopChan, func(_ bool) error {
|
||||
self.goEvery(time.Second*time.Duration(10), self.gui.stopChan, func() error {
|
||||
formatBytes := func(b uint64) string {
|
||||
const unit = 1000
|
||||
if b < unit {
|
||||
@@ -78,7 +78,7 @@ func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
||||
func (self *BackgroundRoutineMgr) startBackgroundFetch() {
|
||||
self.gui.waitForIntro.Wait()
|
||||
|
||||
fetch := func(firstTimeOrRetriggered bool) error {
|
||||
fetch := func() error {
|
||||
// Do this on the UI thread so that we don't have to deal with synchronization around the
|
||||
// access of the repo state.
|
||||
self.gui.onUIThread(func() error {
|
||||
@@ -89,18 +89,14 @@ func (self *BackgroundRoutineMgr) startBackgroundFetch() {
|
||||
return nil
|
||||
})
|
||||
|
||||
if self.gui.UserConfig().Gui.ShowBottomLine || firstTimeOrRetriggered {
|
||||
return self.gui.helpers.AppStatus.WithWaitingStatusImpl(self.gui.Tr.FetchingStatus, func(gocui.Task) error {
|
||||
return self.backgroundFetch()
|
||||
}, nil)
|
||||
}
|
||||
|
||||
return self.backgroundFetch()
|
||||
return self.gui.helpers.AppStatus.WithWaitingStatusImpl(self.gui.Tr.FetchingStatus, func(gocui.Task) error {
|
||||
return self.backgroundFetch()
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// We want an immediate fetch at startup, and since goEvery starts by
|
||||
// waiting for the interval, we need to trigger one manually first
|
||||
_ = fetch(true)
|
||||
_ = fetch()
|
||||
|
||||
userConfig := self.gui.UserConfig()
|
||||
self.triggerFetch = self.goEvery(userConfig.Refresher.FetchIntervalDuration(), self.gui.stopChan, fetch)
|
||||
@@ -110,25 +106,25 @@ func (self *BackgroundRoutineMgr) startBackgroundFilesRefresh() {
|
||||
self.gui.waitForIntro.Wait()
|
||||
|
||||
userConfig := self.gui.UserConfig()
|
||||
self.goEvery(userConfig.Refresher.RefreshIntervalDuration(), self.gui.stopChan, func(_ bool) error {
|
||||
self.goEvery(userConfig.Refresher.RefreshIntervalDuration(), self.gui.stopChan, func() error {
|
||||
self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// returns a channel that can be used to trigger the callback immediately
|
||||
func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan struct{}, function func(bool) error) chan struct{} {
|
||||
func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan struct{}, function func() error) chan struct{} {
|
||||
done := make(chan struct{})
|
||||
retrigger := make(chan struct{})
|
||||
go utils.Safe(func() {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
doit := func(retriggered bool) {
|
||||
doit := func() {
|
||||
if self.pauseBackgroundRefreshes {
|
||||
return
|
||||
}
|
||||
self.gui.c.OnWorker(func(gocui.Task) error {
|
||||
_ = function(retriggered)
|
||||
_ = function()
|
||||
done <- struct{}{}
|
||||
return nil
|
||||
})
|
||||
@@ -140,10 +136,10 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
doit(false)
|
||||
doit()
|
||||
case <-retrigger:
|
||||
ticker.Reset(interval)
|
||||
doit(true)
|
||||
doit()
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
@@ -155,7 +151,7 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
|
||||
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
|
||||
err = self.gui.git.Sync.FetchBackground()
|
||||
|
||||
self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.SYNC})
|
||||
self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS, types.PULL_REQUESTS}, Mode: types.SYNC})
|
||||
|
||||
if err == nil {
|
||||
err = self.gui.helpers.BranchesHelper.AutoForwardBranches()
|
||||
|
||||
@@ -28,6 +28,8 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
return presentation.GetBranchListDisplayStrings(
|
||||
viewModel.GetItems(),
|
||||
c.State().GetItemOperation,
|
||||
c.Model().PullRequests,
|
||||
c.Model().Remotes,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().Diffing.Ref,
|
||||
c.Views().Branches.InnerWidth()+c.Views().Branches.OriginX(),
|
||||
|
||||
@@ -68,6 +68,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
||||
mergeConflictsHelper,
|
||||
worktreeHelper,
|
||||
searchHelper,
|
||||
suggestionsHelper,
|
||||
)
|
||||
diffHelper := helpers.NewDiffHelper(helperCommon)
|
||||
cherryPickHelper := helpers.NewCherryPickHelper(
|
||||
|
||||
@@ -3,12 +3,16 @@ package controllers
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
@@ -192,7 +196,27 @@ func (self *BranchesController) GetOnRenderToMain() func() {
|
||||
} else {
|
||||
cmdObj := self.c.Git().Branch.GetGraphCmdObj(branch.FullRefName())
|
||||
|
||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
ptyTask := types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
task = ptyTask
|
||||
|
||||
// Shouldn't we hold on to the map for longer instead of generating it every time?
|
||||
// It is also generated every time we render the branches list.
|
||||
prs := git_commands.GenerateGithubPullRequestMap(
|
||||
self.c.Model().PullRequests,
|
||||
self.c.Model().Branches,
|
||||
self.c.Model().Remotes,
|
||||
)
|
||||
|
||||
if pr, ok := prs[branch.Name]; ok {
|
||||
icon := lo.Ternary(icons.IsIconEnabled(), icons.IconForRemoteUrl(pr.Url)+" ", "")
|
||||
ptyTask.Prefix = style.PrintHyperlink(fmt.Sprintf("%s%s %s %s\n",
|
||||
icon,
|
||||
coloredStateText(pr.State),
|
||||
pr.Title,
|
||||
style.FgCyan.Sprintf("#%d", pr.Number)),
|
||||
pr.Url)
|
||||
ptyTask.Prefix += strings.Repeat("─", self.c.Contexts().Normal.GetView().InnerWidth()) + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
@@ -206,6 +230,77 @@ func (self *BranchesController) GetOnRenderToMain() func() {
|
||||
}
|
||||
}
|
||||
|
||||
func stateText(state string) string {
|
||||
if icons.IsIconEnabled() {
|
||||
switch state {
|
||||
case "OPEN":
|
||||
return " Open"
|
||||
case "CLOSED":
|
||||
return " Closed"
|
||||
case "MERGED":
|
||||
return " Merged"
|
||||
case "DRAFT":
|
||||
return " Draft"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
switch state {
|
||||
case "OPEN":
|
||||
return "Open"
|
||||
case "CLOSED":
|
||||
return "Closed"
|
||||
case "MERGED":
|
||||
return "Merged"
|
||||
case "DRAFT":
|
||||
return "Draft"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func coloredStateText(state string) string {
|
||||
if icons.IsIconEnabled() {
|
||||
return fmt.Sprintf("%s%s%s",
|
||||
withPrFgColor(state, ""),
|
||||
withPrBgColor(state, style.FgWhite.Sprint(stateText(state))),
|
||||
withPrFgColor(state, ""))
|
||||
}
|
||||
|
||||
return withPrFgColor(state, stateText(state))
|
||||
}
|
||||
|
||||
func withPrFgColor(state string, text string) string {
|
||||
switch state {
|
||||
case "OPEN":
|
||||
return style.FgGreen.Sprint(text)
|
||||
case "CLOSED":
|
||||
return style.FgRed.Sprint(text)
|
||||
case "MERGED":
|
||||
return style.FgMagenta.Sprint(text)
|
||||
case "DRAFT":
|
||||
return color.RGB(0x66, 0x66, 0x66, false).Sprint(text)
|
||||
default:
|
||||
return style.FgDefault.Sprint(text)
|
||||
}
|
||||
}
|
||||
|
||||
func withPrBgColor(state string, text string) string {
|
||||
switch state {
|
||||
case "OPEN":
|
||||
return style.BgGreen.Sprint(text)
|
||||
case "CLOSED":
|
||||
return style.BgRed.Sprint(text)
|
||||
case "MERGED":
|
||||
return style.BgMagenta.Sprint(text)
|
||||
case "DRAFT":
|
||||
return color.RGB(0x66, 0x66, 0x66, true).Sprint(text)
|
||||
default:
|
||||
return style.BgDefault.Sprint(text)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branch) error {
|
||||
upstream := lo.Ternary(selectedBranch.RemoteBranchStoredLocally(),
|
||||
selectedBranch.ShortUpstreamRefName(),
|
||||
|
||||
@@ -62,8 +62,8 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.withItems(self.discard),
|
||||
GetDisabledReason: self.require(self.itemsSelected(self.canDiscardFileChanges)),
|
||||
Description: self.c.Tr.Discard,
|
||||
GetDisabledReason: self.require(self.itemsSelected()),
|
||||
Description: self.c.Tr.Remove,
|
||||
Tooltip: self.c.Tr.DiscardOldFileChangeTooltip,
|
||||
DisplayOnScreen: true,
|
||||
},
|
||||
@@ -302,13 +302,18 @@ func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) discard(selectedNodes []*filetree.CommitFileNode) error {
|
||||
prompt := lo.Ternary(self.c.Git().Patch.PatchBuilder.Active(),
|
||||
self.c.Tr.DiscardFileChangesPromptResetPatch,
|
||||
self.c.Tr.DiscardFileChangesPrompt)
|
||||
parentContext := self.c.Context().Current().GetParentContext()
|
||||
if parentContext == nil || parentContext.GetKey() != context.LOCAL_COMMITS_CONTEXT_KEY {
|
||||
return errors.New(self.c.Tr.CanOnlyDiscardFromLocalCommits)
|
||||
}
|
||||
|
||||
if ok, err := self.c.Helpers().PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
var filePaths []string
|
||||
@@ -343,32 +348,6 @@ func (self *CommitFilesController) discard(selectedNodes []*filetree.CommitFileN
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) canDiscardFileChanges(nodes []*filetree.CommitFileNode) *types.DisabledReason {
|
||||
parentContext := self.c.Context().Current().GetParentContext()
|
||||
if parentContext == nil || parentContext.GetKey() != context.LOCAL_COMMITS_CONTEXT_KEY {
|
||||
return &types.DisabledReason{
|
||||
Text: self.c.Tr.CanOnlyDiscardFromLocalCommits,
|
||||
ShowErrorInPanel: true,
|
||||
}
|
||||
}
|
||||
|
||||
if self.c.Contexts().LocalCommits.AreMultipleItemsSelected() {
|
||||
return &types.DisabledReason{
|
||||
Text: self.c.Tr.CannotDiscardFromMultipleCommits,
|
||||
ShowErrorInPanel: true,
|
||||
}
|
||||
}
|
||||
|
||||
if self.c.Git().Status.WorkingTreeState().Any() {
|
||||
return &types.DisabledReason{
|
||||
Text: self.c.Tr.CantPatchWhileRebasingError,
|
||||
ShowErrorInPanel: true,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) open(node *filetree.CommitFileNode) error {
|
||||
return self.c.Helpers().Files.OpenFile(node.GetPath())
|
||||
}
|
||||
@@ -578,7 +557,7 @@ func (self *CommitFilesController) GetOnClickFocusedMainView() func(mainViewName
|
||||
func (self *CommitFilesController) pathsForDiff(node *filetree.CommitFileNode) []string {
|
||||
if !node.IsFile() && self.context().IsFiltering() {
|
||||
var paths []string
|
||||
_ = node.ForEachFile(func(file *models.CommitFile) error {
|
||||
node.ForEachFile(func(file *models.CommitFile) error {
|
||||
paths = append(paths, file.Path)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -7,10 +7,25 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// This controller lets you change the context size for diffs. The 'context' in 'context size' refers to the conventional meaning of the word 'context' in a diff, as opposed to lazygit's own idea of a 'context'.
|
||||
|
||||
var CONTEXT_KEYS_SHOWING_DIFFS = []types.ContextKey{
|
||||
context.FILES_CONTEXT_KEY,
|
||||
context.COMMIT_FILES_CONTEXT_KEY,
|
||||
context.STASH_CONTEXT_KEY,
|
||||
context.LOCAL_COMMITS_CONTEXT_KEY,
|
||||
context.SUB_COMMITS_CONTEXT_KEY,
|
||||
context.STAGING_MAIN_CONTEXT_KEY,
|
||||
context.STAGING_SECONDARY_CONTEXT_KEY,
|
||||
context.PATCH_BUILDING_MAIN_CONTEXT_KEY,
|
||||
context.PATCH_BUILDING_SECONDARY_CONTEXT_KEY,
|
||||
context.NORMAL_MAIN_CONTEXT_KEY,
|
||||
context.NORMAL_SECONDARY_CONTEXT_KEY,
|
||||
}
|
||||
|
||||
type ContextLinesController struct {
|
||||
baseController
|
||||
c *ControllerCommon
|
||||
@@ -51,31 +66,39 @@ func (self *ContextLinesController) Context() types.Context {
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) Increase() error {
|
||||
if err := self.checkCanChangeContext(); err != nil {
|
||||
return err
|
||||
if self.isShowingDiff() {
|
||||
if err := self.checkCanChangeContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if self.c.UserConfig().Git.DiffContextSize < math.MaxUint64 {
|
||||
self.c.UserConfig().Git.DiffContextSize++
|
||||
}
|
||||
return self.applyChange()
|
||||
}
|
||||
|
||||
if self.c.UserConfig().Git.DiffContextSize < math.MaxUint64 {
|
||||
self.c.UserConfig().Git.DiffContextSize++
|
||||
}
|
||||
return self.applyChange()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) Decrease() error {
|
||||
if err := self.checkCanChangeContext(); err != nil {
|
||||
return err
|
||||
if self.isShowingDiff() {
|
||||
if err := self.checkCanChangeContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if self.c.UserConfig().Git.DiffContextSize > 0 {
|
||||
self.c.UserConfig().Git.DiffContextSize--
|
||||
}
|
||||
return self.applyChange()
|
||||
}
|
||||
|
||||
if self.c.UserConfig().Git.DiffContextSize > 0 {
|
||||
self.c.UserConfig().Git.DiffContextSize--
|
||||
}
|
||||
return self.applyChange()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) applyChange() error {
|
||||
self.c.Toast(fmt.Sprintf(self.c.Tr.DiffContextSizeChanged, self.c.UserConfig().Git.DiffContextSize))
|
||||
|
||||
currentContext := self.c.Context().CurrentSide()
|
||||
currentContext := self.currentSidePanel()
|
||||
switch currentContext.GetKey() {
|
||||
// we make an exception for our staging and patch building contexts because they actually need to refresh their state afterwards.
|
||||
case context.PATCH_BUILDING_MAIN_CONTEXT_KEY:
|
||||
@@ -95,3 +118,22 @@ func (self *ContextLinesController) checkCanChangeContext() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) isShowingDiff() bool {
|
||||
return lo.Contains(
|
||||
CONTEXT_KEYS_SHOWING_DIFFS,
|
||||
self.currentSidePanel().GetKey(),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) currentSidePanel() types.Context {
|
||||
currentContext := self.c.Context().CurrentStatic()
|
||||
if currentContext.GetKey() == context.NORMAL_MAIN_CONTEXT_KEY ||
|
||||
currentContext.GetKey() == context.NORMAL_SECONDARY_CONTEXT_KEY {
|
||||
if sidePanelContext := self.c.Context().NextInStack(currentContext); sidePanelContext != nil {
|
||||
return sidePanelContext
|
||||
}
|
||||
}
|
||||
|
||||
return currentContext
|
||||
}
|
||||
|
||||
@@ -123,6 +123,13 @@ func (self *CustomPatchOptionsMenuAction) getPatchCommitIndex() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) validateNormalWorkingTreeState() (bool, error) {
|
||||
if self.c.Git().Status.WorkingTreeState().Any() {
|
||||
return false, errors.New(self.c.Tr.CantPatchWhileRebasingError)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) returnFocusFromPatchExplorerIfNecessary() {
|
||||
if self.c.Context().Current().GetKey() == self.c.Contexts().CustomPatchBuilder.GetKey() {
|
||||
self.c.Helpers().PatchBuilding.Escape()
|
||||
@@ -130,6 +137,10 @@ func (self *CustomPatchOptionsMenuAction) returnFocusFromPatchExplorerIfNecessar
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handleDeletePatchFromCommit() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
self.returnFocusFromPatchExplorerIfNecessary()
|
||||
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
|
||||
@@ -141,6 +152,10 @@ func (self *CustomPatchOptionsMenuAction) handleDeletePatchFromCommit() error {
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handleMovePatchToSelectedCommit() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
self.returnFocusFromPatchExplorerIfNecessary()
|
||||
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
|
||||
@@ -152,6 +167,10 @@ func (self *CustomPatchOptionsMenuAction) handleMovePatchToSelectedCommit() erro
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
self.returnFocusFromPatchExplorerIfNecessary()
|
||||
|
||||
mustStash := self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules()
|
||||
@@ -170,6 +189,10 @@ func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
self.returnFocusFromPatchExplorerIfNecessary()
|
||||
|
||||
commitIndex := self.getPatchCommitIndex()
|
||||
@@ -201,6 +224,10 @@ func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommitBefore() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
self.returnFocusFromPatchExplorerIfNecessary()
|
||||
|
||||
commitIndex := self.getPatchCommitIndex()
|
||||
|
||||
@@ -441,7 +441,7 @@ func (self *FilesController) pressWithLock(selectedNodes []*filetree.FileNode) e
|
||||
if self.context().IsFiltering() {
|
||||
var paths []string
|
||||
for _, node := range nodes {
|
||||
_ = node.ForEachFile(func(file *models.File) error {
|
||||
node.ForEachFile(func(file *models.File) error {
|
||||
paths = append(paths, file.Path)
|
||||
return nil
|
||||
})
|
||||
@@ -529,7 +529,7 @@ func (self *FilesController) press(nodes []*filetree.FileNode) error {
|
||||
func (self *FilesController) pathOverridesForDiff(node *filetree.FileNode) []string {
|
||||
if !node.IsFile() && self.context().IsFiltering() {
|
||||
var paths []string
|
||||
_ = node.ForEachFile(func(file *models.File) error {
|
||||
node.ForEachFile(func(file *models.File) error {
|
||||
paths = append(paths, file.Path)
|
||||
return nil
|
||||
})
|
||||
@@ -543,7 +543,7 @@ func (self *FilesController) pathOverridesForDiff(node *filetree.FileNode) []str
|
||||
func (self *FilesController) unstageFilteredFiles(nodes []*filetree.FileNode) error {
|
||||
var trackedPaths, untrackedPaths []string
|
||||
for _, node := range nodes {
|
||||
_ = node.ForEachFile(func(file *models.File) error {
|
||||
node.ForEachFile(func(file *models.File) error {
|
||||
if file.Tracked || file.HasStagedChanges {
|
||||
trackedPaths = append(trackedPaths, file.Path)
|
||||
} else {
|
||||
@@ -713,7 +713,7 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
||||
if self.context().IsFiltering() {
|
||||
// When filtering, only stage visible files
|
||||
var paths []string
|
||||
_ = root.ForEachFile(func(file *models.File) error {
|
||||
root.ForEachFile(func(file *models.File) error {
|
||||
paths = append(paths, file.Path)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -102,13 +102,11 @@ func getMessageHeight(wrap bool, editable bool, message string, width int, tabWi
|
||||
return len(wrappedLines)
|
||||
}
|
||||
|
||||
func (self *ConfirmationHelper) getPopupPanelDimensionsForContentHeight(contentWidth, contentHeight int, parentPopupContext types.Context) (int, int, int, int) {
|
||||
return self.getPopupPanelDimensionsAux(contentWidth, contentHeight, parentPopupContext)
|
||||
func (self *ConfirmationHelper) getPopupPanelDimensionsForContentHeight(panelWidth, contentHeight int, parentPopupContext types.Context) (int, int, int, int) {
|
||||
return self.getPopupPanelDimensionsAux(panelWidth, contentHeight, parentPopupContext)
|
||||
}
|
||||
|
||||
func (self *ConfirmationHelper) getPopupPanelDimensionsAux(contentWidth int, contentHeight int, parentPopupContext types.Context) (int, int, int, int) {
|
||||
panelWidth := contentWidth + 2 // plus 2 for the frame
|
||||
panelHeight := contentHeight + 2
|
||||
func (self *ConfirmationHelper) getPopupPanelDimensionsAux(panelWidth int, panelHeight int, parentPopupContext types.Context) (int, int, int, int) {
|
||||
width, height := self.c.GocuiGui().Size()
|
||||
if panelHeight > height*3/4 {
|
||||
panelHeight = height * 3 / 4
|
||||
@@ -119,19 +117,14 @@ func (self *ConfirmationHelper) getPopupPanelDimensionsAux(contentWidth int, con
|
||||
x0, y0, _, _ := parentPopupContext.GetView().Dimensions()
|
||||
x0 += 2
|
||||
y0 += 1
|
||||
return x0, y0, x0 + panelWidth - 1, y0 + panelHeight - 1
|
||||
return x0, y0, x0 + panelWidth, y0 + panelHeight + 1
|
||||
}
|
||||
return width/2 - panelWidth/2,
|
||||
height/2 - panelHeight/2 - panelHeight%2,
|
||||
// Currently, X1/Y1 of a gocui view is one less than you would expect based on its
|
||||
// width/height, so we need to subtract 1 here. See
|
||||
// https://github.com/jesseduffield/lazygit/commit/f6f2a52dee8bba3ebd7e3b34b4b7c7d3e3795f3e
|
||||
width/2 + panelWidth/2 - 1,
|
||||
height/2 + panelHeight/2 - 1
|
||||
height/2 - panelHeight/2 - panelHeight%2 - 1,
|
||||
width/2 + panelWidth/2,
|
||||
height/2 + panelHeight/2
|
||||
}
|
||||
|
||||
// Returns the outer width of the view, including its frame. To decide how to wrap text, subtract 2.
|
||||
// Also, note that X1-X0 of the view is one less than this.
|
||||
func (self *ConfirmationHelper) getPopupPanelWidth(maxWidth int) int {
|
||||
width, _ := self.c.GocuiGui().Size()
|
||||
// we want a minimum width up to a point, then we do it based on ratio, but only up to the given max width
|
||||
@@ -338,7 +331,7 @@ func (self *ConfirmationHelper) resizeMenu(parentPopupContext types.Context) {
|
||||
panelWidth := self.getPopupPanelWidth(90)
|
||||
contentWidth := panelWidth - 2 // minus 2 for the frame
|
||||
promptLinesCount := self.layoutMenuPrompt(contentWidth)
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(contentWidth, itemCount+offset+promptLinesCount, parentPopupContext)
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(panelWidth, itemCount+offset+promptLinesCount, parentPopupContext)
|
||||
menuBottom := y1 - offset
|
||||
_, _ = self.c.GocuiGui().SetView(self.c.Views().Menu.Name(), x0, y0, x1, menuBottom, 0)
|
||||
|
||||
@@ -386,8 +379,8 @@ func (self *ConfirmationHelper) resizeConfirmationPanel(parentPopupContext types
|
||||
contentWidth := panelWidth - 2 // minus 2 for the frame
|
||||
confirmationView := self.c.Views().Confirmation
|
||||
prompt := confirmationView.Buffer()
|
||||
contentHeight := getMessageHeight(true, false, prompt, contentWidth, confirmationView.TabWidth)
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(contentWidth, contentHeight, parentPopupContext)
|
||||
panelHeight := getMessageHeight(true, false, prompt, contentWidth, confirmationView.TabWidth)
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
|
||||
_, _ = self.c.GocuiGui().SetView(confirmationView.Name(), x0, y0, x1, y1, 0)
|
||||
}
|
||||
|
||||
@@ -400,8 +393,8 @@ func (self *ConfirmationHelper) resizePromptPanel(parentPopupContext types.Conte
|
||||
contentWidth := panelWidth - 2 // minus 2 for the frame
|
||||
promptView := self.c.Views().Prompt
|
||||
prompt := promptView.TextArea.GetContent()
|
||||
contentHeight := getMessageHeight(false, true, prompt, contentWidth, promptView.TabWidth) + suggestionsViewHeight
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(contentWidth, contentHeight, parentPopupContext)
|
||||
panelHeight := getMessageHeight(false, true, prompt, contentWidth, promptView.TabWidth) + suggestionsViewHeight
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
|
||||
promptViewBottom := y1 - suggestionsViewHeight
|
||||
_, _ = self.c.GocuiGui().SetView(promptView.Name(), x0, y0, x1, promptViewBottom, 0)
|
||||
|
||||
@@ -417,17 +410,14 @@ func (self *ConfirmationHelper) ResizeCommitMessagePanels(parentPopupContext typ
|
||||
maxWidth = self.c.UserConfig().Git.Commit.AutoWrapWidth + 25
|
||||
}
|
||||
panelWidth := self.getPopupPanelWidth(maxWidth)
|
||||
contentWidth := panelWidth - 2 // minus 2 for the frame
|
||||
content := self.c.Views().CommitDescription.TextArea.GetContent()
|
||||
summaryViewHeight := 3
|
||||
// The width we pass to getMessageHeight is irrelevant because we don't wrap the text to the
|
||||
// view width. If we did, we'd have to subtract 2 for the view frame.
|
||||
contentHeight := getMessageHeight(false, true, content, contentWidth, self.c.Views().CommitDescription.TabWidth)
|
||||
panelHeight := getMessageHeight(false, true, content, panelWidth, self.c.Views().CommitDescription.TabWidth)
|
||||
minHeight := 7
|
||||
if contentHeight < minHeight {
|
||||
contentHeight = minHeight
|
||||
if panelHeight < minHeight {
|
||||
panelHeight = minHeight
|
||||
}
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(contentWidth, contentHeight, parentPopupContext)
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
|
||||
|
||||
_, _ = self.c.GocuiGui().SetView(self.c.Views().CommitMessage.Name(), x0, y0, x1, y0+summaryViewHeight-1, 0)
|
||||
_, _ = self.c.GocuiGui().SetView(self.c.Views().CommitDescription.Name(), x0, y0+summaryViewHeight, x1, y1+summaryViewHeight, 0)
|
||||
|
||||
@@ -27,7 +27,6 @@ type Helpers struct {
|
||||
MergeAndRebase *MergeAndRebaseHelper
|
||||
MergeConflicts *MergeConflictsHelper
|
||||
CherryPick *CherryPickHelper
|
||||
Host *HostHelper
|
||||
PatchBuilding *PatchBuildingHelper
|
||||
Staging *StagingHelper
|
||||
GPG *GpgHelper
|
||||
@@ -53,6 +52,7 @@ type Helpers struct {
|
||||
Search *SearchHelper
|
||||
Worktree *WorktreeHelper
|
||||
SubCommits *SubCommitsHelper
|
||||
Host *HostHelper
|
||||
}
|
||||
|
||||
func NewStubHelpers() *Helpers {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
@@ -21,6 +22,13 @@ func NewPatchBuildingHelper(
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PatchBuildingHelper) ValidateNormalWorkingTreeState() (bool, error) {
|
||||
if self.c.Git().Status.WorkingTreeState().Any() {
|
||||
return false, errors.New(self.c.Tr.CantPatchWhileRebasingError)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *PatchBuildingHelper) ShowHunkStagingHint() {
|
||||
if !self.c.AppState.DidShowHunkStagingHint && self.c.UserConfig().Gui.UseHunkModeInStagingView {
|
||||
self.c.AppState.DidShowHunkStagingHint = true
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||
@@ -27,6 +29,7 @@ type RefreshHelper struct {
|
||||
mergeConflictsHelper *MergeConflictsHelper
|
||||
worktreeHelper *WorktreeHelper
|
||||
searchHelper *SearchHelper
|
||||
suggestionsHelper *SuggestionsHelper
|
||||
}
|
||||
|
||||
func NewRefreshHelper(
|
||||
@@ -38,6 +41,7 @@ func NewRefreshHelper(
|
||||
mergeConflictsHelper *MergeConflictsHelper,
|
||||
worktreeHelper *WorktreeHelper,
|
||||
searchHelper *SearchHelper,
|
||||
suggestionsHelper *SuggestionsHelper,
|
||||
) *RefreshHelper {
|
||||
return &RefreshHelper{
|
||||
c: c,
|
||||
@@ -48,6 +52,7 @@ func NewRefreshHelper(
|
||||
mergeConflictsHelper: mergeConflictsHelper,
|
||||
worktreeHelper: worktreeHelper,
|
||||
searchHelper: searchHelper,
|
||||
suggestionsHelper: suggestionsHelper,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +96,7 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) {
|
||||
types.STATUS,
|
||||
types.BISECT_INFO,
|
||||
types.STAGING,
|
||||
types.PULL_REQUESTS,
|
||||
})
|
||||
} else {
|
||||
scopeSet = set.NewFromSlice(options.Scope)
|
||||
@@ -117,6 +123,10 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
if scopeSet.Includes(types.PULL_REQUESTS) {
|
||||
refresh("pull requests", func() { _ = self.refreshGithubPullRequests() })
|
||||
}
|
||||
|
||||
includeWorktreesWithBranches := false
|
||||
if scopeSet.Includes(types.COMMITS) || scopeSet.Includes(types.BRANCHES) || scopeSet.Includes(types.REFLOG) || scopeSet.Includes(types.BISECT_INFO) {
|
||||
// whenever we change commits, we should update branches because the upstream/downstream
|
||||
@@ -757,3 +767,188 @@ func (self *RefreshHelper) refreshView(context types.Context) {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) refreshGithubPullRequests() error {
|
||||
self.c.Mutexes().RefreshingPullRequestsMutex.Lock()
|
||||
defer self.c.Mutexes().RefreshingPullRequestsMutex.Unlock()
|
||||
|
||||
if !self.c.UserConfig().Git.EnableGithubCli {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !self.c.Git().GitHub.InGithubRepo() {
|
||||
self.c.Model().PullRequests = []*models.GithubPullRequest{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load cached PRs for instant display while we fetch fresh data
|
||||
self.loadCachedPullRequests()
|
||||
|
||||
switch self.c.State().GetGitHubCliState() {
|
||||
case types.UNKNOWN:
|
||||
state := self.determineGithubCliState()
|
||||
self.c.State().SetGitHubCliState(state)
|
||||
if state != types.VALID {
|
||||
if state == types.INVALID_VERSION {
|
||||
// todo: i18n
|
||||
self.c.LogAction("gh version is too old (must be version 2 or greater), so pull requests will not be shown against branches.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case types.VALID:
|
||||
// continue on
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := self.c.Git().GitHub.BaseRepo(); err != nil {
|
||||
ok, err := self.promptForBaseGithubRepo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.setGithubPullRequests(); err != nil {
|
||||
self.c.LogAction(fmt.Sprintf("Error fetching pull requests from GitHub: %s", err.Error()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) promptForBaseGithubRepo() (bool, error) {
|
||||
err := self.refreshRemotes()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
remotes := self.c.Model().Remotes
|
||||
if len(remotes) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// If there's only one remote, or if 'origin' exists, use it automatically
|
||||
remote, hasOrigin := lo.Find(remotes, func(r *models.Remote) bool {
|
||||
return r.Name == "origin"
|
||||
})
|
||||
if !hasOrigin {
|
||||
if len(remotes) == 1 {
|
||||
remote = remotes[0]
|
||||
} else {
|
||||
// Multiple remotes with no 'origin': prompt the user
|
||||
self.c.Prompt(types.PromptOpts{
|
||||
Title: self.c.Tr.SelectRemoteRepository,
|
||||
InitialContent: "",
|
||||
FindSuggestionsFunc: self.suggestionsHelper.GetRemoteRepoSuggestionsFunc(),
|
||||
HandleConfirm: func(repository string) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcSelectingRemote, func(gocui.Task) error {
|
||||
// `repository` is something like 'jesseduffield/lazygit'
|
||||
_, err := self.c.Git().GitHub.SetBaseRepo(repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.refreshGithubPullRequests()
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(remote.Urls) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
repoName, err := self.c.Git().HostingService.GetRepoNameFromRemoteURL(remote.Urls[0])
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
_, err = self.c.Git().GitHub.SetBaseRepo(repoName)
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) determineGithubCliState() types.GitHubCliState {
|
||||
installed, validVersion := self.c.Git().GitHub.DetermineGitHubCliState()
|
||||
if validVersion {
|
||||
return types.VALID
|
||||
} else if installed {
|
||||
return types.INVALID_VERSION
|
||||
}
|
||||
return types.NOT_INSTALLED
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) setGithubPullRequests() error {
|
||||
branches := lo.Filter(self.c.Model().Branches, func(branch *models.Branch, _ int) bool {
|
||||
return branch.IsTrackingRemote()
|
||||
})
|
||||
branchNames := lo.Map(branches, func(branch *models.Branch, _ int) string {
|
||||
return branch.UpstreamBranch
|
||||
})
|
||||
|
||||
prs, err := self.c.Git().GitHub.FetchRecentPRs(branchNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.c.Model().PullRequests = prs
|
||||
self.savePullRequestsToCache(prs)
|
||||
|
||||
self.c.PostRefreshUpdate(self.c.Contexts().Branches)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) loadCachedPullRequests() {
|
||||
repoPath := self.c.Git().RepoPaths.RepoPath()
|
||||
cachedPRs := self.c.GetAppState().GithubPullRequests[repoPath]
|
||||
if len(cachedPRs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
prs := lo.Map(cachedPRs, func(cached config.CachedPullRequest, _ int) *models.GithubPullRequest {
|
||||
return &models.GithubPullRequest{
|
||||
HeadRefName: cached.HeadRefName,
|
||||
Number: cached.Number,
|
||||
Title: cached.Title,
|
||||
State: cached.State,
|
||||
Url: cached.Url,
|
||||
HeadRepositoryOwner: models.GithubRepositoryOwner{
|
||||
Login: cached.HeadRepositoryOwner,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
self.c.Model().PullRequests = prs
|
||||
self.c.PostRefreshUpdate(self.c.Contexts().Branches)
|
||||
}
|
||||
|
||||
func (self *RefreshHelper) savePullRequestsToCache(prs []*models.GithubPullRequest) {
|
||||
repoPath := self.c.Git().RepoPaths.RepoPath()
|
||||
cached := lo.Map(prs, func(pr *models.GithubPullRequest, _ int) config.CachedPullRequest {
|
||||
return config.CachedPullRequest{
|
||||
HeadRefName: pr.HeadRefName,
|
||||
Number: pr.Number,
|
||||
Title: pr.Title,
|
||||
State: pr.State,
|
||||
Url: pr.Url,
|
||||
HeadRepositoryOwner: pr.HeadRepositoryOwner.Login,
|
||||
}
|
||||
})
|
||||
|
||||
appState := self.c.GetAppState()
|
||||
if appState.GithubPullRequests == nil {
|
||||
appState.GithubPullRequests = make(map[string][]config.CachedPullRequest)
|
||||
}
|
||||
appState.GithubPullRequests[repoPath] = cached
|
||||
self.c.SaveAppStateAndLogError()
|
||||
}
|
||||
|
||||
@@ -54,7 +54,24 @@ func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions
|
||||
// loading a heap of commits is slow so we limit them whenever doing a reset
|
||||
self.c.Contexts().LocalCommits.SetLimitCommits(true)
|
||||
|
||||
self.c.Refresh(types.RefreshOptions{Mode: types.BLOCK_UI, KeepBranchSelectionIndex: true})
|
||||
self.c.Refresh(types.RefreshOptions{Mode: types.BLOCK_UI, KeepBranchSelectionIndex: true, Scope: []types.RefreshableView{
|
||||
types.COMMITS,
|
||||
types.BRANCHES,
|
||||
types.FILES,
|
||||
types.STASH,
|
||||
types.REFLOG,
|
||||
types.TAGS,
|
||||
types.REMOTES,
|
||||
types.WORKTREES,
|
||||
types.STATUS,
|
||||
types.BISECT_INFO,
|
||||
types.STAGING,
|
||||
}, Then: func() {
|
||||
// Fetch PRs in the background after checkout; the new branch may
|
||||
// have a PR that we haven't fetched yet. We do this in Then so
|
||||
// that branches are loaded first (PRs are matched against branches).
|
||||
self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.PULL_REQUESTS}})
|
||||
}})
|
||||
}
|
||||
|
||||
localBranch, found := lo.Find(self.c.Model().Branches, func(branch *models.Branch) bool {
|
||||
|
||||
@@ -64,6 +64,30 @@ func (self *SuggestionsHelper) getBranchNames() []string {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SuggestionsHelper) GetRemoteRepoSuggestionsFunc() func(string) []*types.Suggestion {
|
||||
repoNames := self.getRemoteRepoNames()
|
||||
|
||||
return FilterFunc(repoNames, self.c.UserConfig().Gui.UseFuzzySearch())
|
||||
}
|
||||
|
||||
func (self *SuggestionsHelper) getRemoteRepoNames() []string {
|
||||
remotes := self.c.Model().Remotes
|
||||
result := make([]string, 0, len(remotes))
|
||||
for _, remote := range remotes {
|
||||
if len(remote.Urls) == 0 {
|
||||
continue
|
||||
}
|
||||
repoName, err := self.c.Git().HostingService.GetRepoNameFromRemoteURL(remote.Urls[0])
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
continue
|
||||
}
|
||||
result = append(result, repoName)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (self *SuggestionsHelper) GetBranchNameSuggestionsFunc() func(string) []*types.Suggestion {
|
||||
branchNames := self.getBranchNames()
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
@@ -60,19 +61,5 @@ func (self *UpstreamHelper) PromptForUpstreamWithoutInitialContent(_ *models.Bra
|
||||
}
|
||||
|
||||
func (self *UpstreamHelper) GetSuggestedRemote() string {
|
||||
return getSuggestedRemote(self.c.Model().Remotes)
|
||||
}
|
||||
|
||||
func getSuggestedRemote(remotes []*models.Remote) string {
|
||||
if len(remotes) == 0 {
|
||||
return "origin"
|
||||
}
|
||||
|
||||
for _, remote := range remotes {
|
||||
if remote.Name == "origin" {
|
||||
return remote.Name
|
||||
}
|
||||
}
|
||||
|
||||
return remotes[0].Name
|
||||
return git_commands.GetSuggestedRemoteName(self.c.Model().Remotes)
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetSuggestedRemote(t *testing.T) {
|
||||
cases := []struct {
|
||||
remotes []*models.Remote
|
||||
expected string
|
||||
}{
|
||||
{mkRemoteList(), "origin"},
|
||||
{mkRemoteList("upstream", "origin", "foo"), "origin"},
|
||||
{mkRemoteList("upstream", "foo", "bar"), "upstream"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
result := getSuggestedRemote(c.remotes)
|
||||
assert.EqualValues(t, c.expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func mkRemoteList(names ...string) []*models.Remote {
|
||||
return lo.Map(names, func(name string, _ int) *models.Remote {
|
||||
return &models.Remote{Name: name}
|
||||
})
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func (self *PatchBuildingController) GetKeybindings(opts types.KeybindingsOpts)
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||
Handler: self.discardSelection,
|
||||
Handler: self.DiscardSelection,
|
||||
GetDisabledReason: self.getDisabledReasonForDiscard,
|
||||
Description: self.c.Tr.RemoveSelectionFromPatch,
|
||||
Tooltip: self.c.Tr.RemoveSelectionFromPatchTooltip,
|
||||
@@ -181,42 +181,55 @@ func (self *PatchBuildingController) toggleSelection() error {
|
||||
|
||||
func (self *PatchBuildingController) getDisabledReasonForDiscard() *types.DisabledReason {
|
||||
if !self.c.Git().Patch.PatchBuilder.CanRebase {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CanOnlyDiscardFromLocalCommits, ShowErrorInPanel: true}
|
||||
return &types.DisabledReason{Text: self.c.Tr.CanOnlyRemoveLinesFromLocalCommits}
|
||||
}
|
||||
if self.c.Git().Status.WorkingTreeState().Any() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CantPatchWhileRebasingError, ShowErrorInPanel: true}
|
||||
}
|
||||
if self.c.UserConfig().Git.DiffContextSize == 0 {
|
||||
text := fmt.Sprintf(self.c.Tr.Actions.NotEnoughContextToRemoveLines,
|
||||
keybindings.Label(self.c.UserConfig().Keybinding.Universal.IncreaseContextInDiffView))
|
||||
return &types.DisabledReason{Text: text, ShowErrorInPanel: true}
|
||||
if !self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.MustClearPatchBeforeRemovingLines}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) discardSelection() error {
|
||||
prompt := lo.Ternary(self.c.Git().Patch.PatchBuilder.IsEmpty(),
|
||||
self.c.Tr.DiscardLinesFromCommitPrompt,
|
||||
self.c.Tr.DiscardLinesFromCommitPromptWithReset)
|
||||
func (self *PatchBuildingController) DiscardSelection() error {
|
||||
if self.c.UserConfig().Git.DiffContextSize == 0 {
|
||||
return fmt.Errorf(self.c.Tr.Actions.NotEnoughContextToRemoveLines,
|
||||
keybindings.Label(self.c.UserConfig().Keybinding.Universal.IncreaseContextInDiffView))
|
||||
}
|
||||
|
||||
if ok, err := self.c.Helpers().PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.DiscardLinesFromCommitTitle,
|
||||
Prompt: prompt,
|
||||
Title: self.c.Tr.RemoveLinesFromCommitTitle,
|
||||
Prompt: self.c.Tr.RemoveLinesFromCommitPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return self.discardSelectionFromCommit()
|
||||
return self.removeSelectionFromCommit()
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) discardSelectionFromCommit() error {
|
||||
// Reset the current patch if there is one.
|
||||
if !self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
||||
self.c.Git().Patch.PatchBuilder.Reset()
|
||||
func (self *PatchBuildingController) addSelectionToPatch() error {
|
||||
self.context().GetMutex().Lock()
|
||||
defer self.context().GetMutex().Unlock()
|
||||
|
||||
filename := self.c.Contexts().CommitFiles.GetSelectedPath()
|
||||
if filename == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := self.toggleSelection(); err != nil {
|
||||
state := self.context().GetState()
|
||||
lineIndicesToToggle := state.LineIndicesOfAddedOrDeletedLinesInSelectedPatchRange()
|
||||
if len(lineIndicesToToggle) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.c.Git().Patch.PatchBuilder.AddFileLineRange(filename, lineIndicesToToggle)
|
||||
}
|
||||
|
||||
func (self *PatchBuildingController) removeSelectionFromCommit() error {
|
||||
if err := self.addSelectionToPatch(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -224,13 +237,13 @@ func (self *PatchBuildingController) discardSelectionFromCommit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.c.WithWaitingStatusSync(self.c.Tr.RebasingStatus, func() error {
|
||||
self.c.Helpers().PatchBuilding.Escape()
|
||||
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
|
||||
commitIndex := self.getPatchCommitIndex()
|
||||
self.c.LogAction(self.c.Tr.Actions.RemovePatchFromCommit)
|
||||
err := self.c.Git().Patch.DeletePatchesFromCommit(self.c.Model().Commits, commitIndex)
|
||||
self.c.Helpers().PatchBuilding.Escape()
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
|
||||
err, types.RefreshOptions{Mode: types.SYNC})
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,20 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// This controller lets you change the similarity threshold for detecting renames.
|
||||
|
||||
var CONTEXT_KEYS_SHOWING_RENAMES = []types.ContextKey{
|
||||
context.FILES_CONTEXT_KEY,
|
||||
context.SUB_COMMITS_CONTEXT_KEY,
|
||||
context.LOCAL_COMMITS_CONTEXT_KEY,
|
||||
context.STASH_CONTEXT_KEY,
|
||||
context.NORMAL_MAIN_CONTEXT_KEY,
|
||||
context.NORMAL_SECONDARY_CONTEXT_KEY,
|
||||
}
|
||||
|
||||
type RenameSimilarityThresholdController struct {
|
||||
baseController
|
||||
c *ControllerCommon
|
||||
@@ -51,27 +61,29 @@ func (self *RenameSimilarityThresholdController) Context() types.Context {
|
||||
func (self *RenameSimilarityThresholdController) Increase() error {
|
||||
old_size := self.c.UserConfig().Git.RenameSimilarityThreshold
|
||||
|
||||
if old_size < 100 {
|
||||
if self.isShowingRenames() && old_size < 100 {
|
||||
self.c.UserConfig().Git.RenameSimilarityThreshold = min(100, old_size+5)
|
||||
return self.applyChange()
|
||||
}
|
||||
|
||||
return self.applyChange()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *RenameSimilarityThresholdController) Decrease() error {
|
||||
old_size := self.c.UserConfig().Git.RenameSimilarityThreshold
|
||||
|
||||
if old_size > 5 {
|
||||
if self.isShowingRenames() && old_size > 5 {
|
||||
self.c.UserConfig().Git.RenameSimilarityThreshold = max(5, old_size-5)
|
||||
return self.applyChange()
|
||||
}
|
||||
|
||||
return self.applyChange()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *RenameSimilarityThresholdController) applyChange() error {
|
||||
self.c.Toast(fmt.Sprintf(self.c.Tr.RenameSimilarityThresholdChanged, self.c.UserConfig().Git.RenameSimilarityThreshold))
|
||||
|
||||
currentContext := self.c.Context().CurrentSide()
|
||||
currentContext := self.currentSidePanel()
|
||||
switch currentContext.GetKey() {
|
||||
// we make an exception for our files context, because it actually need to refresh its state afterwards.
|
||||
case context.FILES_CONTEXT_KEY:
|
||||
@@ -81,3 +93,22 @@ func (self *RenameSimilarityThresholdController) applyChange() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *RenameSimilarityThresholdController) isShowingRenames() bool {
|
||||
return lo.Contains(
|
||||
CONTEXT_KEYS_SHOWING_RENAMES,
|
||||
self.currentSidePanel().GetKey(),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *RenameSimilarityThresholdController) currentSidePanel() types.Context {
|
||||
currentContext := self.c.Context().CurrentStatic()
|
||||
if currentContext.GetKey() == context.NORMAL_MAIN_CONTEXT_KEY ||
|
||||
currentContext.GetKey() == context.NORMAL_SECONDARY_CONTEXT_KEY {
|
||||
if sidePanelContext := self.c.Context().NextInStack(currentContext); sidePanelContext != nil {
|
||||
return sidePanelContext
|
||||
}
|
||||
}
|
||||
|
||||
return currentContext
|
||||
}
|
||||
|
||||
@@ -41,11 +41,6 @@ func (self *ScreenModeActions) rerenderViewsWithScreenModeDependentContent() {
|
||||
self.rerenderView(context.GetView())
|
||||
}
|
||||
}
|
||||
|
||||
// Rerender the main view; for views that display a diff this is necessary in case a custom
|
||||
// pager depends on the width of the view. For other views it isn't needed, but we don't bother
|
||||
// making a distinction here, as rerendering the main view unnecessarily is not a big deal.
|
||||
self.c.Context().CurrentSide().HandleRenderToMain()
|
||||
}
|
||||
|
||||
func (self *ScreenModeActions) rerenderView(view *gocui.View) {
|
||||
|
||||
@@ -63,11 +63,6 @@ func (self *StatusController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
||||
Handler: func() error { self.switchToOrRotateAllBranchesLogs(); return nil },
|
||||
Description: self.c.Tr.AllBranchesLogGraph,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Status.AllBranchesLogGraphReverse),
|
||||
Handler: func() error { self.switchToOrRotateAllBranchesLogsBackward(); return nil },
|
||||
Description: self.c.Tr.AllBranchesLogGraphReverse,
|
||||
},
|
||||
}
|
||||
|
||||
return bindings
|
||||
@@ -211,18 +206,6 @@ func (self *StatusController) switchToOrRotateAllBranchesLogs() {
|
||||
self.showAllBranchLogs()
|
||||
}
|
||||
|
||||
// Switches to the all branches view, or, if already on that view,
|
||||
// rotates to the previous command in the list, and then renders it.
|
||||
func (self *StatusController) switchToOrRotateAllBranchesLogsBackward() {
|
||||
// A bit of a hack to ensure we only rotate to the previous branch log command
|
||||
// if we currently are looking at a branch log. Otherwise, we should just show
|
||||
// the current index (if we are coming from the dashboard).
|
||||
if self.c.Views().Main.Title != self.c.Tr.StatusTitle {
|
||||
self.c.Git().Branch.RotateAllBranchesLogIdxBackward()
|
||||
}
|
||||
self.showAllBranchLogs()
|
||||
}
|
||||
|
||||
func (self *StatusController) showDashboard() {
|
||||
versionStr := "master"
|
||||
version, err := types.ParseVersionNumber(self.c.GetConfig().GetVersion())
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type WorktreesController struct {
|
||||
@@ -96,11 +95,7 @@ func (self *WorktreesController) GetOnRenderToMain() func() {
|
||||
var builder strings.Builder
|
||||
w := tabwriter.NewWriter(&builder, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "%s:\t%s%s\n", self.c.Tr.Name, style.FgGreen.Sprint(worktree.Name), main)
|
||||
branch := style.FgYellow.Sprint(worktree.Branch)
|
||||
if worktree.Branch == "" && worktree.Head != "" {
|
||||
branch = style.FgYellow.Sprintf("HEAD detached at %s", utils.ShortHash(worktree.Head))
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%s:\t%s\n", self.c.Tr.Branch, branch)
|
||||
_, _ = fmt.Fprintf(w, "%s:\t%s\n", self.c.Tr.Branch, style.FgYellow.Sprint(worktree.Branch))
|
||||
_, _ = fmt.Fprintf(w, "%s:\t%s%s\n", self.c.Tr.Path, style.FgCyan.Sprint(worktree.Path), missing)
|
||||
_ = w.Flush()
|
||||
|
||||
|
||||
+11
-20
@@ -148,6 +148,7 @@ type Gui struct {
|
||||
integrationTest integrationTypes.IntegrationTest
|
||||
|
||||
afterLayoutFuncs chan func() error
|
||||
gitHubCliState types.GitHubCliState
|
||||
}
|
||||
|
||||
type StateAccessor struct {
|
||||
@@ -221,6 +222,14 @@ func (self *StateAccessor) ClearItemOperation(item types.HasUrn) {
|
||||
delete(self.gui.itemOperations, item.URN())
|
||||
}
|
||||
|
||||
func (self *StateAccessor) GetGitHubCliState() types.GitHubCliState {
|
||||
return self.gui.gitHubCliState
|
||||
}
|
||||
|
||||
func (self *StateAccessor) SetGitHubCliState(value types.GitHubCliState) {
|
||||
self.gui.gitHubCliState = value
|
||||
}
|
||||
|
||||
// we keep track of some stuff from one render to the next to see if certain
|
||||
// things have changed
|
||||
type PrevLayout struct {
|
||||
@@ -377,7 +386,7 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context
|
||||
|
||||
gui.g.SetOpenHyperlinkFunc(func(url string, viewname string) error {
|
||||
if strings.HasPrefix(url, "lazygit-edit:") {
|
||||
re := regexp.MustCompile(`^lazygit-edit://(.+?)(?::(\d*))?$`)
|
||||
re := regexp.MustCompile(`^lazygit-edit://(.+?)(?::(\d+))?$`)
|
||||
matches := re.FindStringSubmatch(url)
|
||||
if matches == nil {
|
||||
return fmt.Errorf(gui.Tr.InvalidLazygitEditURL, url)
|
||||
@@ -581,6 +590,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
||||
Authors: map[string]*models.Author{},
|
||||
MainBranches: git_commands.NewMainBranches(gui.c.Common, gui.os.Cmd),
|
||||
HashPool: &utils.StringPool{},
|
||||
PullRequests: make([]*models.GithubPullRequest, 0),
|
||||
},
|
||||
Modes: &types.Modes{
|
||||
Filtering: filtering.New(startArgs.FilterPath, ""),
|
||||
@@ -842,25 +852,6 @@ func (gui *Gui) Run(startArgs appTypes.StartArgs) error {
|
||||
|
||||
g.ErrorHandler = gui.PopupHandler.ErrorHandler
|
||||
|
||||
gui.g.ShouldHandleMouseEvent = func(view *gocui.View, key gocui.Key) bool {
|
||||
if gui.helpers.Confirmation.IsPopupPanelFocused() && gui.currentViewName() != view.Name() &&
|
||||
!gocui.IsMouseScrollKey(key) {
|
||||
// we ignore click events on views that aren't popup panels, when a popup panel is focused.
|
||||
// Unless both the current view and the clicked-on view are either commit message or commit
|
||||
// description, or a prompt and the suggestions view, because we want to allow switching
|
||||
// between those two views by clicking.
|
||||
isCommitMessageOrSuggestionsView := func(viewName string) bool {
|
||||
return viewName == "commitMessage" || viewName == "commitDescription" ||
|
||||
viewName == "prompt" || viewName == "suggestions"
|
||||
}
|
||||
if !isCommitMessageOrSuggestionsView(gui.currentViewName()) || !isCommitMessageOrSuggestionsView(view.Name()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// if the deadlock package wants to report a deadlock, we first need to
|
||||
// close the gui so that we can actually read what it prints.
|
||||
deadlock.Opts.LogBuf = utils.NewOnceWriter(os.Stderr, func() {
|
||||
|
||||
@@ -499,7 +499,29 @@ func (gui *Gui) SetKeybinding(binding *types.Binding) error {
|
||||
return gui.g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, gui.wrappedHandler(handler))
|
||||
}
|
||||
|
||||
// warning: mutates the binding
|
||||
func (gui *Gui) SetMouseKeybinding(binding *gocui.ViewMouseBinding) error {
|
||||
baseHandler := binding.Handler
|
||||
newHandler := func(opts gocui.ViewMouseBindingOpts) error {
|
||||
if gui.helpers.Confirmation.IsPopupPanelFocused() && gui.currentViewName() != binding.ViewName &&
|
||||
!gocui.IsMouseScrollKey(opts.Key) {
|
||||
// we ignore click events on views that aren't popup panels, when a popup panel is focused.
|
||||
// Unless both the current view and the clicked-on view are either commit message or commit
|
||||
// description, or a prompt and the suggestions view, because we want to allow switching
|
||||
// between those two views by clicking.
|
||||
isCommitMessageOrSuggestionsView := func(viewName string) bool {
|
||||
return viewName == "commitMessage" || viewName == "commitDescription" ||
|
||||
viewName == "prompt" || viewName == "suggestions"
|
||||
}
|
||||
if !isCommitMessageOrSuggestionsView(gui.currentViewName()) || !isCommitMessageOrSuggestionsView(binding.ViewName) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return baseHandler(opts)
|
||||
}
|
||||
binding.Handler = newHandler
|
||||
|
||||
return gui.g.SetViewClickBinding(binding)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ var colorPatterns *colorMatcher
|
||||
func GetBranchListDisplayStrings(
|
||||
branches []*models.Branch,
|
||||
getItemOperation func(item types.HasUrn) types.ItemOperation,
|
||||
pullRequests []*models.GithubPullRequest,
|
||||
remotes []*models.Remote,
|
||||
fullDescription bool,
|
||||
diffName string,
|
||||
viewWidth int,
|
||||
@@ -35,9 +37,15 @@ func GetBranchListDisplayStrings(
|
||||
userConfig *config.UserConfig,
|
||||
worktrees []*models.Worktree,
|
||||
) [][]string {
|
||||
prs := git_commands.GenerateGithubPullRequestMap(
|
||||
pullRequests,
|
||||
branches,
|
||||
remotes,
|
||||
)
|
||||
|
||||
return lo.Map(branches, func(branch *models.Branch, _ int) []string {
|
||||
diffed := branch.Name == diffName
|
||||
return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, viewWidth, tr, userConfig, worktrees, time.Now())
|
||||
return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, viewWidth, tr, userConfig, worktrees, time.Now(), prs)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,11 +60,13 @@ func getBranchDisplayStrings(
|
||||
userConfig *config.UserConfig,
|
||||
worktrees []*models.Worktree,
|
||||
now time.Time,
|
||||
prs map[string]*models.GithubPullRequest,
|
||||
) []string {
|
||||
checkedOutByWorkTree := git_commands.CheckedOutByOtherWorktree(b, worktrees)
|
||||
showCommitHash := fullDescription || userConfig.Gui.ShowBranchCommitHash
|
||||
branchStatus := BranchStatus(b, itemOperation, tr, now, userConfig)
|
||||
divergence := divergenceStr(b, itemOperation, tr, userConfig)
|
||||
worktreeIcon := lo.Ternary(icons.IsIconEnabled(), icons.LINKED_WORKTREE_ICON, fmt.Sprintf("(%s)", tr.LcWorktree))
|
||||
|
||||
// Recency is always three characters, plus one for the space
|
||||
availableWidth := viewWidth - 4
|
||||
@@ -71,42 +81,17 @@ func getBranchDisplayStrings(
|
||||
}
|
||||
paddingNeededForDivergence := availableWidth
|
||||
|
||||
displayName := b.Name
|
||||
if b.DisplayName != "" {
|
||||
displayName = b.DisplayName
|
||||
if checkedOutByWorkTree {
|
||||
availableWidth -= utils.StringWidth(worktreeIcon) + 1
|
||||
}
|
||||
|
||||
if len(branchStatus) > 0 {
|
||||
availableWidth -= utils.StringWidth(utils.Decolorise(branchStatus)) + 1
|
||||
}
|
||||
|
||||
worktreeIcon := ""
|
||||
if checkedOutByWorkTree {
|
||||
if wt, ok := git_commands.WorktreeForBranch(b, worktrees); ok && wt.Name != b.Name {
|
||||
if icons.IsIconEnabled() {
|
||||
worktreeIcon = fmt.Sprintf("(%s %s)", icons.LINKED_WORKTREE_ICON, wt.Name)
|
||||
} else {
|
||||
worktreeIcon = fmt.Sprintf("(%s %s)", tr.LcWorktree, wt.Name)
|
||||
}
|
||||
|
||||
// If the worktree name doesn't fit in the available width, omit it
|
||||
remaining := availableWidth - utils.StringWidth(worktreeIcon) - 1
|
||||
if remaining < utils.StringWidth(displayName) {
|
||||
if icons.IsIconEnabled() {
|
||||
worktreeIcon = icons.LINKED_WORKTREE_ICON
|
||||
} else {
|
||||
worktreeIcon = fmt.Sprintf("(%s)", tr.LcWorktree)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if icons.IsIconEnabled() {
|
||||
worktreeIcon = icons.LINKED_WORKTREE_ICON
|
||||
} else {
|
||||
worktreeIcon = fmt.Sprintf("(%s)", tr.LcWorktree)
|
||||
}
|
||||
}
|
||||
|
||||
availableWidth -= utils.StringWidth(worktreeIcon) + 1
|
||||
displayName := b.Name
|
||||
if b.DisplayName != "" {
|
||||
displayName = b.DisplayName
|
||||
}
|
||||
|
||||
nameTextStyle := GetBranchTextStyle(b.Name)
|
||||
@@ -124,6 +109,7 @@ func getBranchDisplayStrings(
|
||||
if checkedOutByWorkTree {
|
||||
coloredName = fmt.Sprintf("%s %s", coloredName, style.FgDefault.Sprint(worktreeIcon))
|
||||
}
|
||||
|
||||
if len(branchStatus) > 0 {
|
||||
coloredName = fmt.Sprintf("%s %s", coloredName, branchStatus)
|
||||
}
|
||||
@@ -134,14 +120,22 @@ func getBranchDisplayStrings(
|
||||
}
|
||||
|
||||
res := make([]string, 0, 6)
|
||||
|
||||
res = append(res, recencyColor.Sprint(b.Recency))
|
||||
|
||||
if icons.IsIconEnabled() {
|
||||
res = append(res, nameTextStyle.Sprint(icons.IconForBranch(b)))
|
||||
}
|
||||
|
||||
if showCommitHash {
|
||||
res = append(res, utils.ShortHash(b.CommitHash))
|
||||
pr, hasPr := prs[b.Name]
|
||||
if hasPr {
|
||||
if icons.IsIconEnabled() {
|
||||
res = append(res, prColor(pr.State).Sprint(icons.IconForRemoteUrl(pr.Url)))
|
||||
} else {
|
||||
res = append(res, prColor(pr.State).Sprint("●"))
|
||||
}
|
||||
} else {
|
||||
if icons.IsIconEnabled() {
|
||||
res = append(res, style.FgDefault.Sprint(icons.IconForBranch(b)))
|
||||
} else {
|
||||
res = append(res, style.FgDefault.Sprint("●"))
|
||||
}
|
||||
}
|
||||
|
||||
if divergence != "" {
|
||||
@@ -151,8 +145,13 @@ func getBranchDisplayStrings(
|
||||
coloredName += style.FgCyan.Sprint(divergence)
|
||||
}
|
||||
}
|
||||
|
||||
res = append(res, coloredName)
|
||||
|
||||
if showCommitHash {
|
||||
res = append(res, utils.ShortHash(b.CommitHash))
|
||||
}
|
||||
|
||||
if fullDescription {
|
||||
res = append(
|
||||
res,
|
||||
@@ -252,3 +251,18 @@ func SetCustomBranches(customBranchColors map[string]string, isRegex bool) {
|
||||
isRegex: isRegex,
|
||||
}
|
||||
}
|
||||
|
||||
func prColor(state string) style.TextStyle {
|
||||
switch state {
|
||||
case "OPEN":
|
||||
return style.FgGreen
|
||||
case "CLOSED":
|
||||
return style.FgRed
|
||||
case "MERGED":
|
||||
return style.FgMagenta
|
||||
case "DRAFT":
|
||||
return style.FgBlackLighter
|
||||
default:
|
||||
return style.FgDefault
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branch_name"},
|
||||
expected: []string{"1m", "●", "branch_name"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "🍉_special_char", Recency: "1m"},
|
||||
@@ -52,7 +52,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "🍉_special_char"},
|
||||
expected: []string{"1m", "●", "🍉_special_char"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "branch_name", Recency: "1m"},
|
||||
@@ -62,7 +62,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: true,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branch_name (worktree other-worktree)"},
|
||||
expected: []string{"1m", "●", "branch_name (worktree)"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "branch_name", Recency: "1m"},
|
||||
@@ -72,7 +72,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: true,
|
||||
checkedOutByWorktree: true,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "", "branch_name ( other-worktree)"},
|
||||
expected: []string{"1m", "", "branch_name "},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -88,7 +88,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branch_name ✓"},
|
||||
expected: []string{"1m", "●", "branch_name ✓"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -104,7 +104,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: true,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branch_name (worktree other-worktree) ↓5↑3"},
|
||||
expected: []string{"1m", "●", "branch_name (worktree) ↓5↑3"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -118,7 +118,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "onlyArrow",
|
||||
expected: []string{"1m", "branch_name ↓"},
|
||||
expected: []string{"1m", "●", "branch_name ↓"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -135,7 +135,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "arrowAndNumber",
|
||||
expected: []string{"1m", "branch_name ✓ ↓2"},
|
||||
expected: []string{"1m", "●", "branch_name ✓ ↓2"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -152,7 +152,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "arrowAndNumber",
|
||||
expected: []string{"1m", "branch_name ↓5↑3 ↓2"},
|
||||
expected: []string{"1m", "●", "branch_name ↓5↑3 ↓2"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "branch_name", Recency: "1m"},
|
||||
@@ -162,7 +162,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branch_name Pushing |"},
|
||||
expected: []string{"1m", "●", "branch_name Pushing |"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -181,7 +181,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "12345678", "branch_name ✓", "origin branch_name", "commit title"},
|
||||
expected: []string{"1m", "●", "branch_name ✓", "12345678", "origin branch_name", "commit title"},
|
||||
},
|
||||
|
||||
// Now tests for how we truncate the branch name when there's not enough room:
|
||||
@@ -193,7 +193,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branch_na…"},
|
||||
expected: []string{"1m", "●", "branch_na…"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "🍉_special_char", Recency: "1m"},
|
||||
@@ -203,7 +203,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "🍉_special_ch…"},
|
||||
expected: []string{"1m", "●", "🍉_special_ch…"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "branch_name", Recency: "1m"},
|
||||
@@ -213,7 +213,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: true,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "bra… (worktree)"},
|
||||
expected: []string{"1m", "●", "bra… (worktree)"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "branch_name", Recency: "1m"},
|
||||
@@ -239,7 +239,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branch_… ✓"},
|
||||
expected: []string{"1m", "●", "branch_… ✓"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -256,7 +256,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "arrowAndNumber",
|
||||
expected: []string{"1m", "branch_n… ↓5↑3 ↓4"},
|
||||
expected: []string{"1m", "●", "branch_n… ↓5↑3 ↓4"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -272,7 +272,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: true,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branch_na… (worktree) ↓5↑3"},
|
||||
expected: []string{"1m", "●", "branch_na… (worktree) ↓5↑3"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "branch_name", Recency: "1m"},
|
||||
@@ -282,7 +282,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "branc… Pushing |"},
|
||||
expected: []string{"1m", "●", "branc… Pushing |"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "abc", Recency: "1m"},
|
||||
@@ -292,7 +292,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "abc Pushing |"},
|
||||
expected: []string{"1m", "●", "abc Pushing |"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "ab", Recency: "1m"},
|
||||
@@ -302,7 +302,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "ab Pushing |"},
|
||||
expected: []string{"1m", "●", "ab Pushing |"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{Name: "a", Recency: "1m"},
|
||||
@@ -312,7 +312,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "a Pushing |"},
|
||||
expected: []string{"1m", "●", "a Pushing |"},
|
||||
},
|
||||
{
|
||||
branch: &models.Branch{
|
||||
@@ -331,7 +331,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
useIcons: false,
|
||||
checkedOutByWorktree: false,
|
||||
showDivergenceCfg: "none",
|
||||
expected: []string{"1m", "12345678", "bran… ✓", "origin branch_name", "commit title"},
|
||||
expected: []string{"1m", "●", "bran… ✓", "12345678", "origin branch_name", "commit title"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -347,11 +347,11 @@ func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
|
||||
worktrees := []*models.Worktree{}
|
||||
if s.checkedOutByWorktree {
|
||||
worktrees = append(worktrees, &models.Worktree{Branch: s.branch.Name, Name: "other-worktree"})
|
||||
worktrees = append(worktrees, &models.Worktree{Branch: s.branch.Name})
|
||||
}
|
||||
|
||||
t.Run(fmt.Sprintf("getBranchDisplayStrings_%d", i), func(t *testing.T) {
|
||||
strings := getBranchDisplayStrings(s.branch, s.itemOperation, s.fullDescription, false, s.viewWidth, c.Tr, c.UserConfig(), worktrees, time.Time{})
|
||||
strings := getBranchDisplayStrings(s.branch, s.itemOperation, s.fullDescription, false, s.viewWidth, c.Tr, c.UserConfig(), worktrees, time.Time{}, map[string]*models.GithubPullRequest{})
|
||||
assert.Equal(t, s.expected, strings)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,6 +79,15 @@ func IconForRemote(remote *models.Remote) string {
|
||||
return DEFAULT_REMOTE_ICON
|
||||
}
|
||||
|
||||
func IconForRemoteUrl(url string) string {
|
||||
for domain, icon := range remoteIcons {
|
||||
if strings.Contains(url, domain) {
|
||||
return icon
|
||||
}
|
||||
}
|
||||
return DEFAULT_REMOTE_ICON
|
||||
}
|
||||
|
||||
func IconForStash(stash *models.StashEntry) string {
|
||||
return STASH_ICON
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
@@ -41,23 +40,12 @@ func GetWorktreeDisplayString(tr *i18n.TranslationSet, worktree *models.Worktree
|
||||
}
|
||||
|
||||
name := worktree.Name
|
||||
if worktree.IsMain {
|
||||
name += " " + tr.MainWorktree
|
||||
}
|
||||
if worktree.IsPathMissing && !icons.IsIconEnabled() {
|
||||
name += " " + tr.MissingWorktree
|
||||
}
|
||||
res = append(res, textStyle.Sprint(name))
|
||||
var branch string
|
||||
if worktree.Branch != "" {
|
||||
branch = style.FgCyan.Sprint(worktree.Branch)
|
||||
} else if worktree.Head != "" {
|
||||
branch = style.FgYellow.Sprint("HEAD detached at " + utils.ShortHash(worktree.Head))
|
||||
}
|
||||
res = append(res, branch+mainWorktreeLabel(tr, worktree))
|
||||
return res
|
||||
}
|
||||
|
||||
func mainWorktreeLabel(tr *i18n.TranslationSet, worktree *models.Worktree) string {
|
||||
if worktree.IsMain {
|
||||
return style.FgDefault.Sprint(" " + tr.MainWorktree)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xo/terminfo"
|
||||
)
|
||||
|
||||
func Test_GetWorktreeDisplayString(t *testing.T) {
|
||||
tr := &i18n.TranslationSet{
|
||||
MainWorktree: "(main worktree)",
|
||||
MissingWorktree: "(missing)",
|
||||
}
|
||||
|
||||
scenarios := []struct {
|
||||
testName string
|
||||
worktree *models.Worktree
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
testName: "Current worktree on a branch",
|
||||
worktree: &models.Worktree{
|
||||
Name: "my-worktree",
|
||||
Branch: "my-branch",
|
||||
IsCurrent: true,
|
||||
},
|
||||
expected: []string{" *", "my-worktree", "my-branch"},
|
||||
},
|
||||
{
|
||||
testName: "Non-current worktree on a branch",
|
||||
worktree: &models.Worktree{
|
||||
Name: "my-worktree",
|
||||
Branch: "feature-branch",
|
||||
},
|
||||
expected: []string{"", "my-worktree", "feature-branch"},
|
||||
},
|
||||
{
|
||||
testName: "Main worktree on a branch",
|
||||
worktree: &models.Worktree{
|
||||
Name: "repo",
|
||||
Branch: "main",
|
||||
IsMain: true,
|
||||
},
|
||||
expected: []string{"", "repo", "main (main worktree)"},
|
||||
},
|
||||
{
|
||||
testName: "Detached HEAD worktree",
|
||||
worktree: &models.Worktree{
|
||||
Name: "my-worktree",
|
||||
Head: "d85cc9d281fa6ae1665c68365fc70e75e82a042d",
|
||||
},
|
||||
expected: []string{"", "my-worktree", "HEAD detached at d85cc9d2"},
|
||||
},
|
||||
{
|
||||
testName: "Detached HEAD on main worktree",
|
||||
worktree: &models.Worktree{
|
||||
Name: "repo",
|
||||
IsMain: true,
|
||||
Head: "d85cc9d281fa6ae1665c68365fc70e75e82a042d",
|
||||
},
|
||||
expected: []string{"", "repo", "HEAD detached at d85cc9d2 (main worktree)"},
|
||||
},
|
||||
{
|
||||
testName: "Worktree with branch takes precedence over head",
|
||||
worktree: &models.Worktree{
|
||||
Name: "my-worktree",
|
||||
Branch: "my-branch",
|
||||
Head: "d85cc9d281fa6ae1665c68365fc70e75e82a042d",
|
||||
},
|
||||
expected: []string{"", "my-worktree", "my-branch"},
|
||||
},
|
||||
{
|
||||
testName: "Missing worktree",
|
||||
worktree: &models.Worktree{
|
||||
Name: "my-worktree",
|
||||
IsPathMissing: true,
|
||||
Head: "d85cc9d281fa6ae1665c68365fc70e75e82a042d",
|
||||
},
|
||||
expected: []string{"", "my-worktree (missing)", "HEAD detached at d85cc9d2"},
|
||||
},
|
||||
{
|
||||
testName: "Worktree with no branch and no head",
|
||||
worktree: &models.Worktree{
|
||||
Name: "my-worktree",
|
||||
},
|
||||
expected: []string{"", "my-worktree", ""},
|
||||
},
|
||||
{
|
||||
testName: "Main worktree with no branch and no head",
|
||||
worktree: &models.Worktree{
|
||||
Name: "my-worktree",
|
||||
IsMain: true,
|
||||
},
|
||||
expected: []string{"", "my-worktree", " (main worktree)"},
|
||||
},
|
||||
}
|
||||
|
||||
oldColorLevel := color.ForceSetColorLevel(terminfo.ColorLevelNone)
|
||||
defer color.ForceSetColorLevel(oldColorLevel)
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
result := GetWorktreeDisplayString(tr, s.worktree)
|
||||
assert.Equal(t, s.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
+23
-9
@@ -296,6 +296,7 @@ type Model struct {
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
PullRequests []*models.GithubPullRequest
|
||||
|
||||
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
||||
// When in filtering mode we only include the ones that match the given path
|
||||
@@ -326,15 +327,16 @@ type Model struct {
|
||||
}
|
||||
|
||||
type Mutexes struct {
|
||||
RefreshingFilesMutex deadlock.Mutex
|
||||
RefreshingBranchesMutex deadlock.Mutex
|
||||
RefreshingStatusMutex deadlock.Mutex
|
||||
LocalCommitsMutex deadlock.Mutex
|
||||
SubCommitsMutex deadlock.Mutex
|
||||
AuthorsMutex deadlock.Mutex
|
||||
SubprocessMutex deadlock.Mutex
|
||||
PopupMutex deadlock.Mutex
|
||||
PtyMutex deadlock.Mutex
|
||||
RefreshingFilesMutex deadlock.Mutex
|
||||
RefreshingBranchesMutex deadlock.Mutex
|
||||
RefreshingStatusMutex deadlock.Mutex
|
||||
RefreshingPullRequestsMutex deadlock.Mutex
|
||||
LocalCommitsMutex deadlock.Mutex
|
||||
SubCommitsMutex deadlock.Mutex
|
||||
AuthorsMutex deadlock.Mutex
|
||||
SubprocessMutex deadlock.Mutex
|
||||
PopupMutex deadlock.Mutex
|
||||
PtyMutex deadlock.Mutex
|
||||
}
|
||||
|
||||
// A long-running operation associated with an item. For example, we'll show
|
||||
@@ -373,6 +375,8 @@ type IStateAccessor interface {
|
||||
GetItemOperation(item HasUrn) ItemOperation
|
||||
SetItemOperation(item HasUrn, operation ItemOperation)
|
||||
ClearItemOperation(item HasUrn)
|
||||
GetGitHubCliState() GitHubCliState
|
||||
SetGitHubCliState(GitHubCliState)
|
||||
}
|
||||
|
||||
type IRepoStateAccessor interface {
|
||||
@@ -409,3 +413,13 @@ const (
|
||||
SCREEN_HALF
|
||||
SCREEN_FULL
|
||||
)
|
||||
|
||||
// for keeping track of whether our github CLI is installed and on a valid version
|
||||
type GitHubCliState int
|
||||
|
||||
const (
|
||||
UNKNOWN GitHubCliState = iota
|
||||
VALID
|
||||
NOT_INSTALLED
|
||||
INVALID_VERSION
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
COMMIT_FILES
|
||||
// not actually a view. Will refactor this later
|
||||
BISECT_INFO
|
||||
PULL_REQUESTS
|
||||
)
|
||||
|
||||
type RefreshMode int
|
||||
|
||||
+19
-15
@@ -279,7 +279,6 @@ type TranslationSet struct {
|
||||
ConfirmQuit string
|
||||
SwitchRepo string
|
||||
AllBranchesLogGraph string
|
||||
AllBranchesLogGraphReverse string
|
||||
UnsupportedGitService string
|
||||
CopyPullRequestURL string
|
||||
NoBranchOnRemote string
|
||||
@@ -437,12 +436,12 @@ type TranslationSet struct {
|
||||
CheckoutCommitFileTooltip string
|
||||
CannotCheckoutWithModifiedFilesErr string
|
||||
CanOnlyDiscardFromLocalCommits string
|
||||
CannotDiscardFromMultipleCommits string
|
||||
CanOnlyRemoveLinesFromLocalCommits string
|
||||
MustClearPatchBeforeRemovingLines string
|
||||
Remove string
|
||||
DiscardOldFileChangeTooltip string
|
||||
DiscardFileChangesTitle string
|
||||
DiscardFileChangesPrompt string
|
||||
DiscardFileChangesPromptResetPatch string
|
||||
DisabledForGPG string
|
||||
CreateRepo string
|
||||
BareRepo string
|
||||
@@ -609,6 +608,8 @@ type TranslationSet struct {
|
||||
CyclePagersDisabledReason string
|
||||
StartSearch string
|
||||
StartFilter string
|
||||
SelectRemoteRepository string
|
||||
LcSelectingRemote string
|
||||
Keybindings string
|
||||
KeybindingsLegend string
|
||||
KeybindingsMenuSectionLocal string
|
||||
@@ -691,9 +692,8 @@ type TranslationSet struct {
|
||||
BranchUnknown string
|
||||
DiscardChangeTitle string
|
||||
DiscardChangePrompt string
|
||||
DiscardLinesFromCommitTitle string
|
||||
DiscardLinesFromCommitPrompt string
|
||||
DiscardLinesFromCommitPromptWithReset string
|
||||
RemoveLinesFromCommitTitle string
|
||||
RemoveLinesFromCommitPrompt string
|
||||
CreateNewBranchFromCommit string
|
||||
BuildingPatch string
|
||||
ViewCommits string
|
||||
@@ -705,6 +705,8 @@ type TranslationSet struct {
|
||||
BackToParentRepo string
|
||||
Enter string
|
||||
CopySubmoduleNameToClipboard string
|
||||
MinGhVersionError string
|
||||
FailedToObtainGhVersionError string
|
||||
RemoveSubmodule string
|
||||
RemoveSubmoduleTooltip string
|
||||
RemoveSubmodulePrompt string
|
||||
@@ -1390,7 +1392,6 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
ConfirmQuit: `Are you sure you want to quit?`,
|
||||
SwitchRepo: `Switch to a recent repo`,
|
||||
AllBranchesLogGraph: `Show/cycle all branch logs`,
|
||||
AllBranchesLogGraphReverse: `Show/cycle all branch logs (reverse)`,
|
||||
UnsupportedGitService: `Unsupported git service`,
|
||||
CreatePullRequest: `Create pull request`,
|
||||
CopyPullRequestURL: `Copy pull request URL to clipboard`,
|
||||
@@ -1554,12 +1555,12 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
CheckoutCommitFileTooltip: "Checkout file. This replaces the file in your working tree with the version from the selected commit.",
|
||||
CannotCheckoutWithModifiedFilesErr: "You have local modifications for the file(s) you are trying to check out. You need to stash or discard these first.",
|
||||
CanOnlyDiscardFromLocalCommits: "Changes can only be discarded from local commits",
|
||||
CannotDiscardFromMultipleCommits: "Changes cannot be discarded from a multiselection of commits",
|
||||
CanOnlyRemoveLinesFromLocalCommits: "Lines can only be removed from local commits",
|
||||
MustClearPatchBeforeRemovingLines: "Clear the current custom patch first before removing lines from the commit",
|
||||
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 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.",
|
||||
DiscardFileChangesPromptResetPatch: "Are you sure you want to discard 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.\n\nNote: This will reset the active custom patch!",
|
||||
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.\n\nIf you are using a passphrase agent (e.g. gpg-agent) so that you don't have to type your passphrase when signing, you can enable this feature by adding\n\ngit:\n overrideGpg: true\n\nto your lazygit config file.",
|
||||
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) ",
|
||||
@@ -1729,6 +1730,8 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
CyclePagersDisabledReason: "No other pagers configured",
|
||||
StartSearch: "Search the current view by text",
|
||||
StartFilter: "Filter the current view by text",
|
||||
SelectRemoteRepository: "Select base repository for pull requests",
|
||||
LcSelectingRemote: "Selecting remote",
|
||||
KeybindingsLegend: "Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b",
|
||||
RenameBranch: "Rename branch",
|
||||
BranchUpstreamOptionsTitle: "Upstream options",
|
||||
@@ -1784,7 +1787,7 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
ShowingGitDiff: "Showing output for:",
|
||||
ShowingDiffForRange: "Showing diff for range",
|
||||
CommitDiff: "Commit diff",
|
||||
CopyCommitHashToClipboard: "Copy abbreviated commit hash to clipboard",
|
||||
CopyCommitHashToClipboard: "Copy commit hash to clipboard",
|
||||
CommitHash: "Commit hash",
|
||||
CommitURL: "Commit URL",
|
||||
PasteCommitMessageFromClipboard: "Paste commit message from clipboard",
|
||||
@@ -1808,9 +1811,8 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
BranchUnknown: "Branch unknown",
|
||||
DiscardChangeTitle: "Discard change",
|
||||
DiscardChangePrompt: "Are you sure you want to discard this change (git reset)? It is irreversible.\nTo disable this dialogue set the config key of 'gui.skipDiscardChangeWarning' to true",
|
||||
DiscardLinesFromCommitTitle: "Discard lines from commit",
|
||||
DiscardLinesFromCommitPrompt: "Are you sure you want to discard the selected lines from this commit?",
|
||||
DiscardLinesFromCommitPromptWithReset: "Are you sure you want to discard the selected lines from this commit?\n\nNote: This will reset the active custom patch!",
|
||||
RemoveLinesFromCommitTitle: "Remove lines from commit",
|
||||
RemoveLinesFromCommitPrompt: "Are you sure you want to remove the selected lines from this commit?",
|
||||
CreateNewBranchFromCommit: "Create new branch off of commit",
|
||||
BuildingPatch: "Building patch",
|
||||
ViewCommits: "View commits",
|
||||
@@ -1822,6 +1824,8 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
EnterSubmoduleTooltip: "Enter submodule. After entering the submodule, you can press `{{.escape}}` to escape back to the parent repo.",
|
||||
BackToParentRepo: "Back to parent repo",
|
||||
CopySubmoduleNameToClipboard: "Copy submodule name to clipboard",
|
||||
MinGhVersionError: "GH version must be at least 2.0. Please upgrade your gh version. Alternatively raise an issue at https://github.com/jesseduffield/lazygit/issues for lazygit to be more backwards compatible.",
|
||||
FailedToObtainGhVersionError: "Failed to obtain gh version. Output from running 'gh --version' was: %s",
|
||||
RemoveSubmodule: "Remove submodule",
|
||||
RemoveSubmodulePrompt: "Are you sure you want to remove submodule '%s' and its corresponding directory? This is irreversible.",
|
||||
RemoveSubmoduleTooltip: "Remove the selected submodule and its corresponding directory.",
|
||||
@@ -1990,7 +1994,7 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
CantDeleteMainWorktree: "You cannot remove the main worktree!",
|
||||
NoWorktreesThisRepo: "No worktrees",
|
||||
MissingWorktree: "(missing)",
|
||||
MainWorktree: "(main worktree)",
|
||||
MainWorktree: "(main)",
|
||||
NewWorktree: "New worktree",
|
||||
NewWorktreePath: "New worktree path",
|
||||
NewWorktreeBase: "New worktree base ref",
|
||||
|
||||
@@ -133,7 +133,6 @@
|
||||
"NewBranchFromStashTooltip": "選択したスタッシュエントリから新しいブランチを作成します。これは、スタッシュエントリが作成されたコミットをgitがチェックアウトし、そのコミットから新しいブランチを作成した後、スタッシュエントリを追加のコミットとして新しいブランチに適用することで機能します。",
|
||||
"MoveCommitsToNewBranch": "コミットを新しいブランチに移動",
|
||||
"MoveCommitsToNewBranchFromBaseItem": "ベースブランチから新規ブランチ (%s)",
|
||||
"CannotMoveCommitsBehindUpstream": "アップストリームブランチに遅れているブランチからコミットを移動することはできません",
|
||||
"CannotMoveCommitsNoUnpushedCommits": "新しいブランチに移動するプッシュされていないコミットはありません",
|
||||
"NoBranchesThisRepo": "このリポジトリにはブランチがありません",
|
||||
"CommitWithoutMessageErr": "コミットメッセージなしでコミットすることはできません",
|
||||
@@ -390,6 +389,7 @@
|
||||
"Remove": "削除",
|
||||
"DiscardOldFileChangeTooltip": "このコミットのこのファイルへの変更を破棄します。これはバックグラウンドで対話的なリベースを実行するため、後のコミットでもこのファイルが変更されている場合、マージコンフリクトが発生する可能性があります。",
|
||||
"DiscardFileChangesTitle": "ファイルの変更を破棄",
|
||||
"DiscardFileChangesPrompt": "このコミットから選択したファイルの変更を削除してよろしいですか?\n\nこのアクションはリベースを開始し、これらのファイル変更を元に戻します。後続のコミットがこれらの変更に依存している場合、コンフリクトを解決する必要があるかもしれないことに注意してください。\n注意:これにより、アクティブなカスタムパッチもリセットされます。",
|
||||
"DisabledForGPG": "GPGを使用しているユーザーには利用できない機能です。\n\nパスフレーズエージェント(gpg-agentなど)を使用して署名時にパスフレーズを入力しなくても済むようにしている場合は、lazygitの設定ファイルに\n\ngit:\n overrideGpg: true\n\nを追加することでこの機能を有効にできます。",
|
||||
"CreateRepo": "Gitリポジトリがありません。新しいgitリポジトリを作成しますか? (y/N): ",
|
||||
"BareRepo": "ベアリポジトリでLazygitを開こうとしましたが、Lazygitはまだベアリポジトリをサポートしていません。最近のリポジトリを開いてよろしいですか? (y/n) ",
|
||||
@@ -596,6 +596,7 @@
|
||||
"ShowingGitDiff": "出力を表示:",
|
||||
"ShowingDiffForRange": "範囲の差分を表示",
|
||||
"CommitDiff": "コミットの差分",
|
||||
"CopyCommitHashToClipboard": "コミットハッシュをクリップボードにコピー",
|
||||
"CommitHash": "コミットハッシュ",
|
||||
"CommitURL": "コミットURL",
|
||||
"PasteCommitMessageFromClipboard": "クリップボードからコミットメッセージを貼り付け",
|
||||
@@ -779,6 +780,7 @@
|
||||
"CantDeleteMainWorktree": "メインのワークツリーは削除できません!",
|
||||
"NoWorktreesThisRepo": "ワークツリーがありません",
|
||||
"MissingWorktree": "(見つかりません)",
|
||||
"MainWorktree": "(メイン)",
|
||||
"NewWorktree": "新しいワークツリー",
|
||||
"NewWorktreePath": "新しいワークツリーのパス",
|
||||
"NewWorktreeBase": "新しいワークツリーのベース参照",
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
"CheckoutCommitFileTooltip": "Checkout file",
|
||||
"DiscardOldFileChangeTooltip": "Discard this commit's changes to this file",
|
||||
"DiscardFileChangesTitle": "파일 변경 사항 버리기",
|
||||
"DiscardFileChangesPrompt": "Are you sure you want to discard this commit's changes to this file? If this file was created in this commit, it will be deleted",
|
||||
"Discard": "View 'discard changes' options",
|
||||
"Cancel": "취소",
|
||||
"DiscardAllChanges": "모든 변경사항 버리기",
|
||||
@@ -242,6 +243,7 @@
|
||||
"ViewDiffingOptions": "Diff 메뉴 열기",
|
||||
"OpenCommandLogMenu": "명령어 로그 메뉴 열기",
|
||||
"CommitDiff": "커밋의 iff",
|
||||
"CopyCommitHashToClipboard": "커밋 해시를 클립보드에 복사",
|
||||
"CommitHash": "커밋 해시",
|
||||
"CommitURL": "커밋 URL",
|
||||
"CommitMessage": "커밋 메시지",
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
"CheckoutCommitFileTooltip": "Bestand uitchecken",
|
||||
"DiscardOldFileChangeTooltip": "Uitsluit deze commit zijn veranderingen aan dit bestand",
|
||||
"DiscardFileChangesTitle": "Uitsluit bestand zijn veranderingen",
|
||||
"DiscardFileChangesPrompt": "Weet je zeker dat je de wijzigingen van deze commit in dit bestand wilt weggooien? Als dit bestand is gecreëerd in deze commit dan zal dit bestand worden verwijdert",
|
||||
"AutoStashPrompt": "Je moet je veranderingen stashen en poppen om ze over te brengen. Dit automatisch doen? (enter/esc)",
|
||||
"Discard": "Bekijk 'veranderingen ongedaan maken' opties",
|
||||
"Cancel": "Annuleren",
|
||||
@@ -240,6 +241,7 @@
|
||||
"SwapDiff": "Keer diff richting om",
|
||||
"ViewDiffingOptions": "Open diff menu",
|
||||
"ShowingGitDiff": "Laat output zien voor:",
|
||||
"CopyCommitHashToClipboard": "Kopieer commit hash naar klembord",
|
||||
"CopyBranchNameToClipboard": "Kopieer branch name naar klembord",
|
||||
"CopyPathToClipboard": "Kopieer de bestandsnaam naar het klembord",
|
||||
"CommitPrefixPatternError": "Fout in commitPrefix patroon",
|
||||
|
||||
@@ -299,6 +299,7 @@
|
||||
"Remove": "Usuń",
|
||||
"DiscardOldFileChangeTooltip": "Odrzuć zmiany w tym pliku z tego commita. Uruchamia interaktywny rebase w tle, więc możesz otrzymać konflikt scalania, jeśli późniejszy commit również zmienia ten plik.",
|
||||
"DiscardFileChangesTitle": "Odrzuć zmiany w pliku",
|
||||
"DiscardFileChangesPrompt": "Czy na pewno chcesz usunąć zmiany w wybranym pliku/ach z tego commita?\n\nTa akcja uruchomi rebase, cofając te zmiany w pliku. Pamiętaj, że jeśli późniejsze commity zależą od tych zmian, możesz potrzebować rozwiązać konflikty.\nUwaga: Spowoduje to również zresetowanie wszelkich aktywnych niestandardowych łatek.",
|
||||
"BareRepo": "Próbujesz otworzyć Lazygit w gołym repozytorium, ale Lazygit jeszcze nie obsługuje gołych repozytoriów. Otworzyć najnowsze repozytorium? (t/n) ",
|
||||
"InitialBranch": "Nazwa gałęzi? (pozostaw puste dla domyślnej gita): ",
|
||||
"NoRecentRepositories": "Musisz otworzyć lazygit w repozytorium git. Brak ważnych ostatnich repozytoriów. Wyjście.",
|
||||
@@ -475,6 +476,7 @@
|
||||
"OpenCommandLogMenuTooltip": "Pokaż opcje dla dziennika poleceń, np. pokazywanie/ukrywanie dziennika poleceń i skupienie na dzienniku poleceń.",
|
||||
"ShowingGitDiff": "Pokazuje wynik dla:",
|
||||
"CommitDiff": "Różnice commita",
|
||||
"CopyCommitHashToClipboard": "Kopiuj hash commita do schowka",
|
||||
"CommitHash": "hash commita",
|
||||
"CommitURL": "URL commita",
|
||||
"CommitMessage": "Wiadomość commita",
|
||||
@@ -635,6 +637,7 @@
|
||||
"CantDeleteMainWorktree": "Nie możesz usunąć głównego drzewa pracy!",
|
||||
"NoWorktreesThisRepo": "Brak drzew pracy",
|
||||
"MissingWorktree": "(brakujące)",
|
||||
"MainWorktree": "(główne)",
|
||||
"NewWorktree": "Nowe drzewo pracy",
|
||||
"NewWorktreePath": "Nowa ścieżka drzewa pracy",
|
||||
"NewWorktreeBase": "Nowa bazowa ref drzewa pracy",
|
||||
|
||||
@@ -367,6 +367,7 @@
|
||||
"Remove": "Remover",
|
||||
"DiscardOldFileChangeTooltip": "Descartar as alterações desse commit para este arquivo. Isso executa uma rebase interativa em segundo plano, então você pode ter um conflito de merge se um commit posterior também alterar este arquivo.",
|
||||
"DiscardFileChangesTitle": "Descartar alterações de arquivo",
|
||||
"DiscardFileChangesPrompt": "Você tem certeza de que deseja remover as alterações do(s) arquivo(s) selecionado(s) deste commit?",
|
||||
"BareRepo": "Você tentou abrir Lazygit em um repositório puro, mas Lazygit ainda não suporta repositórios vazios. Abrir os repositórios mais recentes? (y/n) ",
|
||||
"InitialBranch": "Nome da branch? (deixe vazio para o padrão do git): ",
|
||||
"NoRecentRepositories": "É necessário abrir lazygit em um repositório git. Nenhum repositório recente válido. Saindo do sistema.",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"CredentialsPassword": "Пароль",
|
||||
"CredentialsPassphrase": "Введите пароль для SSH ключа",
|
||||
"CredentialsPIN": "Введите PIN-код для SSH ключа",
|
||||
"CredentialsToken": "Введите токен для SSH-ключа",
|
||||
"PassUnameWrong": "Неверный пароль, кодовая фраза и/или имя пользователя",
|
||||
"Commit": "Сохранить изменения",
|
||||
"AmendLastCommit": "Правка последнего коммита",
|
||||
@@ -226,6 +225,7 @@
|
||||
"CanOnlyDiscardFromLocalCommits": "Изменения можно отменить только из локальных коммитов.",
|
||||
"DiscardOldFileChangeTooltip": "Отменить изменения коммита в этом файле",
|
||||
"DiscardFileChangesTitle": "Отменить изменения файла",
|
||||
"DiscardFileChangesPrompt": "Вы уверены, что хотите удалить изменения в выбранных файлах из этого коммита?\n\nЭто действие запустит перебазирование и отменит изменения в этих файлах. Обратите внимание, что если последующие коммиты зависят от этих изменений, вам, возможно, придется разрешить конфликты.\nПримечание: это также сбросит все активные пользовательские патчи.",
|
||||
"BareRepo": "Вы пытались открыть Lazygit в пустом репозитории, но Lazygit ещё не поддерживает пустые репозитории. Открыть последний репозиторий? (y/n)",
|
||||
"InitialBranch": "Название ветки? (оставьте пустым для git по умолчанию):",
|
||||
"NoRecentRepositories": "Необходимо открыть lazygit в git репозитории. Нет валидных последних репозиториев. Выход.",
|
||||
@@ -342,6 +342,7 @@
|
||||
"OpenCommandLogMenu": "Открыть меню журнала команд",
|
||||
"ShowingGitDiff": "Показывает вывод для:",
|
||||
"CommitDiff": "Разница коммита",
|
||||
"CopyCommitHashToClipboard": "Скопировать hash коммита в буфер обмена",
|
||||
"CommitHash": "hash коммита",
|
||||
"CommitURL": "URL коммита",
|
||||
"CommitMessage": "Полное сообщение коммита",
|
||||
|
||||
@@ -417,6 +417,7 @@
|
||||
"Remove": "删除",
|
||||
"DiscardOldFileChangeTooltip": "放弃对此文件的提交变更",
|
||||
"DiscardFileChangesTitle": "放弃文件变更",
|
||||
"DiscardFileChangesPrompt": "您确定要舍弃此提交对该文件的变更吗?如果此文件是在此提交中创建的,它将被删除",
|
||||
"DisabledForGPG": "使用GPG的用户无法使用此功能。\n\n如果您正在使用密码代理(如gpg-agent)以避免每次签名时输入密码,可以通过在lazygit配置文件中添加\n\ngit:\n overrideGpg: true\n\n来启用此功能。",
|
||||
"CreateRepo": "不在 git 仓库中。创建一个新的 git 仓库吗?(y/N): ",
|
||||
"BareRepo": "您已经尝试在空仓库中打开Lazygit,但是Lazygit还不支持空仓库。打开最近的仓库吗?(y / n) ",
|
||||
@@ -641,6 +642,7 @@
|
||||
"ShowingGitDiff": "显示输出:",
|
||||
"ShowingDiffForRange": "显示范围差异",
|
||||
"CommitDiff": "比较提交差异",
|
||||
"CopyCommitHashToClipboard": "复制提交哈希到剪贴板",
|
||||
"CommitHash": "提交的 hash",
|
||||
"CommitURL": "提交URL",
|
||||
"PasteCommitMessageFromClipboard": "粘贴提交信息自剪贴板",
|
||||
@@ -844,6 +846,7 @@
|
||||
"CantDeleteMainWorktree": "您不能移除主工作树!",
|
||||
"NoWorktreesThisRepo": "没有工作区",
|
||||
"MissingWorktree": "(缺失)",
|
||||
"MainWorktree": "(主要)",
|
||||
"NewWorktree": "新建工作树",
|
||||
"NewWorktreePath": "新建工作树路径",
|
||||
"NewWorktreeBase": "新建工作树基于ref",
|
||||
|
||||
@@ -258,6 +258,7 @@
|
||||
"CommitFilesTitle": "提交檔案",
|
||||
"CheckoutCommitFileTooltip": "檢出檔案",
|
||||
"DiscardFileChangesTitle": "捨棄檔案更改",
|
||||
"DiscardFileChangesPrompt": "是否捨棄此提交?如果這個檔案是在此提交中創建的,它將被刪除",
|
||||
"BareRepo": "你嘗試在裸版本庫中開啟 Lazygit,但 Lazygit 尚未支援裸版本庫。是否開啟最新版本庫? (y/n) ",
|
||||
"InitialBranch": "分支名稱?(留空使用 git 的預設值):",
|
||||
"NoRecentRepositories": "必須在 git 版本庫中開啟 lazygit。沒有有效的最近版本庫。退出。",
|
||||
@@ -396,6 +397,7 @@
|
||||
"OpenCommandLogMenu": "開啟命令記錄選單",
|
||||
"ShowingGitDiff": "顯示輸出:",
|
||||
"CommitDiff": "提交差異",
|
||||
"CopyCommitHashToClipboard": "複製提交 hash 到剪貼簿",
|
||||
"CommitHash": "提交 hash",
|
||||
"CommitURL": "提交 URL",
|
||||
"CommitMessage": "提交訊息",
|
||||
@@ -537,6 +539,7 @@
|
||||
"CantDeleteMainWorktree": "無法刪除主要工作目錄!",
|
||||
"NoWorktreesThisRepo": "無工作目錄",
|
||||
"MissingWorktree": "(失蹤)",
|
||||
"MainWorktree": "(主要)",
|
||||
"NewWorktreePath": "工作目錄路徑",
|
||||
"NewWorktreeBase": "工作目錄來源",
|
||||
"NewBranchName": "分支名稱",
|
||||
|
||||
@@ -20,7 +20,7 @@ var CreateTag = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
MatchesRegexp(`\*\s*new-branch`).IsSelected(),
|
||||
MatchesRegexp(`\*.*new-branch`).IsSelected(),
|
||||
MatchesRegexp(`master`),
|
||||
).
|
||||
SelectNextItem().
|
||||
|
||||
@@ -31,7 +31,7 @@ var DetachedHead = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
MatchesRegexp(`\* new-branch`).IsSelected(),
|
||||
MatchesRegexp(`\*.*new-branch`).IsSelected(),
|
||||
MatchesRegexp(`master`),
|
||||
)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ var Checkout = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Branches().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("* (HEAD detached at").IsSelected(),
|
||||
MatchesRegexp(`\*.*\(HEAD detached at`).IsSelected(),
|
||||
Contains("branch1"),
|
||||
Contains("branch2"),
|
||||
Contains("master"),
|
||||
|
||||
@@ -57,7 +57,7 @@ var DiscardOldFileChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Equals("Are you sure you want to discard 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.")).
|
||||
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().
|
||||
@@ -99,7 +99,7 @@ var DiscardOldFileChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Equals("Are you sure you want to discard 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.")).
|
||||
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().
|
||||
@@ -157,7 +157,7 @@ var DiscardOldFileChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Equals("Are you sure you want to discard 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.\n\nNote: This will reset the active custom patch!")).
|
||||
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
|
||||
|
||||
@@ -45,7 +45,7 @@ var DiscardSubmoduleChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard file changes")).
|
||||
Content(Contains("Are you sure you want to discard changes to the selected file(s) from this commit?")).
|
||||
Content(Contains("Are you sure you want to remove changes to the selected file(s) from this commit?")).
|
||||
Confirm()
|
||||
|
||||
t.Shell().RunCommand([]string{"git", "submodule", "update"})
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var ExcludeWithoutInfoDir = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Exclude a file when .git/info directory does not exist",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
// Remove .git/info directory to reproduce #5302
|
||||
shell.RunCommand([]string{"rm", "-rf", ".git/info"})
|
||||
shell.CreateFile("toExclude", "")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Focus().
|
||||
NavigateToLine(Contains("toExclude")).
|
||||
Press(keys.Files.IgnoreFile).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Menu().Title(Equals("Ignore or exclude file")).Select(Contains("Add to .git/info/exclude")).Confirm()
|
||||
|
||||
// Should succeed without error, creating .git/info/ directory automatically
|
||||
t.FileSystem().FileContent(".git/info/exclude", Contains("/toExclude"))
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -28,25 +28,25 @@ var FilterCommitFilesToggleDirectory = NewIntegrationTest(NewIntegrationTestArgs
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ dir1").IsSelected(),
|
||||
Equals(" A apple-grape"),
|
||||
Equals(" A apple-orange"),
|
||||
Equals(" A grape-orange"),
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("apple-grape"),
|
||||
Contains("apple-orange"),
|
||||
Contains("grape-orange"),
|
||||
).
|
||||
// Filter to show only "apple" files (staying in tree view)
|
||||
FilterOrSearch("apple").
|
||||
Lines(
|
||||
// first item is always selected after filtering
|
||||
Equals("▼ dir1").IsSelected(),
|
||||
Equals(" A apple-grape"),
|
||||
Equals(" A apple-orange"),
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("apple-grape"),
|
||||
Contains("apple-orange"),
|
||||
).
|
||||
// dir1 is already selected; toggle for patch
|
||||
PressPrimaryAction().
|
||||
Lines(
|
||||
Equals("▼ dir1").IsSelected(),
|
||||
Equals(" ● apple-grape"),
|
||||
Equals(" ● apple-orange"),
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("● apple-grape"),
|
||||
Contains("● apple-orange"),
|
||||
)
|
||||
|
||||
t.Views().Information().Content(Contains("Building patch"))
|
||||
|
||||
@@ -20,18 +20,18 @@ var FilterFilesStageAll = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
Lines(
|
||||
Equals("▼ dir1").IsSelected(),
|
||||
Equals(" ?? apple-grape"),
|
||||
Equals(" ?? apple-orange"),
|
||||
Equals(" ?? grape-orange"),
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("apple-grape"),
|
||||
Contains("apple-orange"),
|
||||
Contains("grape-orange"),
|
||||
).
|
||||
// Filter to show only "apple" files
|
||||
FilterOrSearch("apple").
|
||||
Lines(
|
||||
// first item is always selected after filtering
|
||||
Equals("▼ dir1").IsSelected(),
|
||||
Equals(" ?? apple-grape"),
|
||||
Equals(" ?? apple-orange"),
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("apple-grape"),
|
||||
Contains("apple-orange"),
|
||||
).
|
||||
// Stage all visible files
|
||||
Press(keys.Files.ToggleStagedAll).
|
||||
@@ -41,10 +41,10 @@ var FilterFilesStageAll = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ dir1").IsSelected(),
|
||||
Equals(" A apple-grape"),
|
||||
Equals(" A apple-orange"),
|
||||
Equals(" ?? grape-orange"),
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("A apple-grape"),
|
||||
Contains("A apple-orange"),
|
||||
Contains("?? grape-orange"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -20,18 +20,18 @@ var FilterFilesStageDirectory = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
Lines(
|
||||
Equals("▼ dir1").IsSelected(),
|
||||
Equals(" ?? apple-grape"),
|
||||
Equals(" ?? apple-orange"),
|
||||
Equals(" ?? grape-orange"),
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("apple-grape"),
|
||||
Contains("apple-orange"),
|
||||
Contains("grape-orange"),
|
||||
).
|
||||
// Filter to show only "apple" files
|
||||
FilterOrSearch("apple").
|
||||
Lines(
|
||||
// first item is always selected after filtering
|
||||
Equals("▼ dir1").IsSelected(),
|
||||
Equals(" ?? apple-grape"),
|
||||
Equals(" ?? apple-orange"),
|
||||
Contains("dir1").IsSelected(),
|
||||
Contains("apple-grape"),
|
||||
Contains("apple-orange"),
|
||||
).
|
||||
// dir1 is already selected; stage it
|
||||
PressPrimaryAction().
|
||||
@@ -41,10 +41,10 @@ var FilterFilesStageDirectory = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ dir1"),
|
||||
Equals(" A apple-grape"),
|
||||
Equals(" A apple-orange"),
|
||||
Equals(" ?? grape-orange"),
|
||||
Contains("dir1"),
|
||||
Contains("A apple-grape"),
|
||||
Contains("A apple-orange"),
|
||||
Contains("?? grape-orange"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package filter_and_search
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var FilterPreservesSelectionOnModelChange = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Verify that when a filter is active and the model changes, the selection is preserved",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.NewBranch("branch-alpha")
|
||||
shell.NewBranch("branch-beta")
|
||||
shell.NewBranch("branch-gamma")
|
||||
shell.NewBranch("checked-out-branch")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("checked-out-branch").IsSelected(),
|
||||
Contains("branch-alpha"),
|
||||
Contains("branch-beta"),
|
||||
Contains("branch-gamma"),
|
||||
Contains("master"),
|
||||
).
|
||||
FilterOrSearch("branch-").
|
||||
Lines(
|
||||
Contains("branch-alpha").IsSelected(),
|
||||
Contains("branch-beta"),
|
||||
Contains("branch-gamma"),
|
||||
).
|
||||
// Move cursor to a non-zero position
|
||||
SelectNextItem().
|
||||
SelectNextItem().
|
||||
Lines(
|
||||
Contains("branch-alpha"),
|
||||
Contains("branch-beta"),
|
||||
Contains("branch-gamma").IsSelected(),
|
||||
)
|
||||
|
||||
// Trigger a model update while staying on the Branches view.
|
||||
// Using a shell command that creates a new branch sorting after
|
||||
// branch-gamma, so the selection index still points to the same item.
|
||||
t.GlobalPress(keys.Universal.ExecuteShellCommand)
|
||||
t.ExpectPopup().Prompt().
|
||||
Title(Equals("Shell command:")).
|
||||
Type("git branch branch-zeta").
|
||||
Confirm()
|
||||
|
||||
// Verify that the selection is still on branch-gamma (not reset to 0)
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
Contains("branch-alpha"),
|
||||
Contains("branch-beta"),
|
||||
Contains("branch-gamma").IsSelected(),
|
||||
Contains("branch-zeta"),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -70,7 +70,7 @@ var NestedFilter = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
FilterOrSearch("grape").
|
||||
Lines(
|
||||
Equals("A grape").IsSelected(),
|
||||
Contains("grape").IsSelected(),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
@@ -88,7 +88,7 @@ var NestedFilter = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("A grape").IsSelected(),
|
||||
Contains("grape").IsSelected(),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Views().Search().IsVisible().Content(Contains("matches for 'grape'"))
|
||||
|
||||
@@ -75,7 +75,7 @@ var NestedFilterTransient = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
FilterOrSearch("two").
|
||||
Lines(
|
||||
Equals("A file-two").IsSelected(),
|
||||
Contains("file-two").IsSelected(),
|
||||
)
|
||||
|
||||
t.Views().Branches().
|
||||
|
||||
+4
-4
@@ -5,8 +5,8 @@ import (
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var DiscardLinesFromCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Discard specific lines from a commit using the 'd' shortcut in the patch building view",
|
||||
var RemoveLinesFromCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Remove specific lines from a commit using the 'd' shortcut in the patch building view",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
@@ -42,8 +42,8 @@ var DiscardLinesFromCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Universal.Remove)
|
||||
|
||||
t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Discard lines from commit")).
|
||||
Content(Equals("Are you sure you want to discard the selected lines from this commit?")).
|
||||
Title(Equals("Remove lines from commit")).
|
||||
Content(Equals("Are you sure you want to remove the selected lines from this commit?")).
|
||||
Confirm()
|
||||
|
||||
// After the rebase, we should be back at the commit files view
|
||||
@@ -10,11 +10,7 @@ var LogCmdStatusPanelAllBranchesLog = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Git.AllBranchesLogCmds = []string{
|
||||
`echo "view1"`,
|
||||
`echo "view2"`,
|
||||
`echo "view3"`,
|
||||
}
|
||||
config.GetUserConfig().Git.AllBranchesLogCmds = []string{`echo "view1"`, `echo "view2"`}
|
||||
config.GetUserConfig().Gui.StatusPanelView = "allBranchesLog"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {},
|
||||
@@ -29,30 +25,14 @@ var LogCmdStatusPanelAllBranchesLog = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Status().
|
||||
Focus()
|
||||
|
||||
t.Views().Main().Content(Contains("view1"))
|
||||
t.Views().Main().Content(Contains("view1").DoesNotContain("view2"))
|
||||
|
||||
t.Views().Status().
|
||||
Press(keys.Status.AllBranchesLogGraph)
|
||||
t.Views().Main().Content(Contains("view2"))
|
||||
t.Views().Main().Content(Contains("view2").DoesNotContain("view1"))
|
||||
|
||||
t.Views().Status().
|
||||
Press(keys.Status.AllBranchesLogGraph)
|
||||
t.Views().Main().Content(Contains("view3"))
|
||||
|
||||
t.Views().Status().
|
||||
Press(keys.Status.AllBranchesLogGraph)
|
||||
t.Views().Main().Content(Contains("view1"))
|
||||
|
||||
t.Views().Status().
|
||||
Press(keys.Status.AllBranchesLogGraphReverse)
|
||||
t.Views().Main().Content(Contains("view3"))
|
||||
|
||||
t.Views().Status().
|
||||
Press(keys.Status.AllBranchesLogGraphReverse)
|
||||
t.Views().Main().Content(Contains("view2"))
|
||||
|
||||
t.Views().Status().
|
||||
Press(keys.Status.AllBranchesLogGraphReverse)
|
||||
t.Views().Main().Content(Contains("view1"))
|
||||
t.Views().Main().Content(Contains("view1").DoesNotContain("view2"))
|
||||
},
|
||||
})
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ var FetchAndAutoForwardBranchesAllBranchesCheckedOutInOtherWorktree = NewIntegra
|
||||
Contains("checked-out").IsSelected(),
|
||||
Contains("diverged ↓2↑1"),
|
||||
Contains("feature ↓2").DoesNotContain("↑"),
|
||||
Contains("master (worktree linked-worktree) ↓1").DoesNotContain("↑"),
|
||||
Contains("master (worktree) ↓1").DoesNotContain("↑"),
|
||||
)
|
||||
|
||||
t.Views().Files().
|
||||
@@ -51,7 +51,7 @@ var FetchAndAutoForwardBranchesAllBranchesCheckedOutInOtherWorktree = NewIntegra
|
||||
Contains("checked-out").IsSelected(),
|
||||
Contains("diverged ↓2↑1"),
|
||||
Contains("feature ✓"),
|
||||
Contains("master (worktree linked-worktree) ↓1"),
|
||||
Contains("master (worktree) ↓1"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -34,14 +34,14 @@ var FetchWhenSortedByDate = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
Contains("* branch1").IsSelected(),
|
||||
MatchesRegexp(`\*.*branch1`).IsSelected(),
|
||||
Contains("branch2"),
|
||||
Contains("master ↓1"),
|
||||
).
|
||||
NavigateToLine(Contains("master")).
|
||||
Press(keys.Branches.FetchRemote).
|
||||
Lines(
|
||||
Contains("* branch1"),
|
||||
MatchesRegexp(`\*.*branch1`),
|
||||
Contains("master").IsSelected(),
|
||||
Contains("branch2"),
|
||||
)
|
||||
|
||||
@@ -221,7 +221,6 @@ var tests = []*components.IntegrationTest{
|
||||
file.DiscardUnstagedRangeSelect,
|
||||
file.DiscardVariousChanges,
|
||||
file.DiscardVariousChangesRangeSelect,
|
||||
file.ExcludeWithoutInfoDir,
|
||||
file.Gitignore,
|
||||
file.GitignoreSpecialCharacters,
|
||||
file.RememberCommitMessageAfterFail,
|
||||
@@ -242,7 +241,6 @@ var tests = []*components.IntegrationTest{
|
||||
filter_and_search.FilterMenuByKeybinding,
|
||||
filter_and_search.FilterMenuCancelFilterWithEscape,
|
||||
filter_and_search.FilterMenuWithNoKeybindings,
|
||||
filter_and_search.FilterPreservesSelectionOnModelChange,
|
||||
filter_and_search.FilterRemoteBranches,
|
||||
filter_and_search.FilterRemotes,
|
||||
filter_and_search.FilterSearchHistory,
|
||||
@@ -333,7 +331,6 @@ var tests = []*components.IntegrationTest{
|
||||
patch_building.ApplyInReverseWithConflict,
|
||||
patch_building.ApplyWithModifiedFileConflict,
|
||||
patch_building.ApplyWithModifiedFileNoConflict,
|
||||
patch_building.DiscardLinesFromCommit,
|
||||
patch_building.EditLineInPatchBuildingPanel,
|
||||
patch_building.MoveRangeToIndex,
|
||||
patch_building.MoveToEarlierCommit,
|
||||
@@ -354,6 +351,7 @@ var tests = []*components.IntegrationTest{
|
||||
patch_building.MoveToNewCommitInLastCommitOfStackedBranch,
|
||||
patch_building.MoveToNewCommitPartialHunk,
|
||||
patch_building.RemoveFromCommit,
|
||||
patch_building.RemoveLinesFromCommit,
|
||||
patch_building.RemovePartsOfAddedFile,
|
||||
patch_building.ResetWithEscape,
|
||||
patch_building.SelectAllFiles,
|
||||
|
||||
@@ -42,7 +42,7 @@ var AddFromBranch = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("newbranch").IsSelected(),
|
||||
Contains("mybranch (worktree repo)"),
|
||||
Contains("mybranch (worktree)"),
|
||||
).
|
||||
NavigateToLine(Contains("mybranch")).
|
||||
Press(keys.Universal.Select).
|
||||
@@ -54,7 +54,7 @@ var AddFromBranch = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
}).
|
||||
Lines(
|
||||
Contains("mybranch").IsSelected(),
|
||||
Contains("newbranch (worktree linked-worktree)"),
|
||||
Contains("newbranch (worktree)"),
|
||||
).
|
||||
// Confirm the files view is still showing in the files window
|
||||
Press(keys.Universal.PrevBlock)
|
||||
|
||||
@@ -37,7 +37,7 @@ var AddFromBranchDetached = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("(no branch)").IsSelected(),
|
||||
Contains("mybranch (worktree repo)"),
|
||||
Contains("mybranch (worktree)"),
|
||||
)
|
||||
|
||||
t.Views().Status().
|
||||
|
||||
@@ -50,7 +50,7 @@ var AddFromCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("newbranch").IsSelected(),
|
||||
Contains("mybranch (worktree repo)"),
|
||||
Contains("mybranch (worktree)"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -30,7 +30,7 @@ var AssociateBranchBisect = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("mybranch").IsSelected(),
|
||||
Contains("newbranch (worktree linked-worktree)"),
|
||||
Contains("newbranch (worktree)"),
|
||||
)
|
||||
|
||||
// start a bisect on the main worktree
|
||||
@@ -70,7 +70,7 @@ var AssociateBranchBisect = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
}).
|
||||
Lines(
|
||||
Contains("newbranch").IsSelected(),
|
||||
Contains("mybranch (worktree repo)"),
|
||||
Contains("mybranch (worktree)"),
|
||||
)
|
||||
|
||||
// switch back to main worktree
|
||||
|
||||
@@ -31,7 +31,7 @@ var AssociateBranchRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("mybranch").IsSelected(),
|
||||
Contains("newbranch (worktree linked-worktree)"),
|
||||
Contains("newbranch (worktree)"),
|
||||
)
|
||||
|
||||
// start a rebase on the main worktree
|
||||
@@ -57,7 +57,7 @@ var AssociateBranchRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
}).
|
||||
Lines(
|
||||
Contains("newbranch").IsSelected(),
|
||||
Contains("mybranch (worktree repo)"),
|
||||
Contains("mybranch (worktree)"),
|
||||
)
|
||||
|
||||
// start a rebase on the linked worktree
|
||||
@@ -83,7 +83,7 @@ var AssociateBranchRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("(no branch").IsSelected(),
|
||||
Contains("mybranch"),
|
||||
// even though the linked worktree is rebasing, we still associate it with the branch
|
||||
Contains("newbranch (worktree linked-worktree)"),
|
||||
Contains("newbranch (worktree)"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ var Crud = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Worktrees().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("(main worktree)"),
|
||||
Contains("repo (main)"),
|
||||
).
|
||||
Press(keys.Universal.New).
|
||||
Tap(func() {
|
||||
@@ -55,7 +55,7 @@ var Crud = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
}).
|
||||
Lines(
|
||||
Contains("linked-worktree").IsSelected(),
|
||||
Contains("(main worktree)"),
|
||||
Contains("repo (main)"),
|
||||
).
|
||||
// confirm we're still in the same view
|
||||
IsFocused()
|
||||
@@ -82,7 +82,7 @@ var Crud = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Confirm()
|
||||
}).
|
||||
// confirm we cannot remove the main worktree
|
||||
NavigateToLine(Contains("(main worktree)")).
|
||||
NavigateToLine(Contains("repo (main)")).
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
@@ -93,7 +93,7 @@ var Crud = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
// switch back to main worktree
|
||||
Press(keys.Universal.Select).
|
||||
Lines(
|
||||
Contains("(main worktree)").IsSelected(),
|
||||
Contains("repo (main)").IsSelected(),
|
||||
Contains("linked-worktree"),
|
||||
)
|
||||
|
||||
@@ -114,7 +114,7 @@ var Crud = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
Contains("(main worktree)").IsSelected(),
|
||||
Contains("repo (main)").IsSelected(),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -28,13 +28,13 @@ var CustomCommand = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Worktrees().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("(main worktree)"),
|
||||
Contains("repo (main)"),
|
||||
Contains("linked-worktree"),
|
||||
).
|
||||
NavigateToLine(Contains("linked-worktree")).
|
||||
Press("d").
|
||||
Lines(
|
||||
Contains("(main worktree)"),
|
||||
Contains("repo (main)"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user