summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Haller <stefan@haller-berlin.de>2024-03-12 13:30:19 +0100
committerGitHub <noreply@github.com>2024-03-12 13:30:19 +0100
commitfd18db6ba285eb280aee1cb8c381295df6556cad (patch)
treef00590f6fbdbc5af5f260f35757a0d0cfe6c9e65
parentd12ceeb1ecac2bf9783f0febb48aca335805524a (diff)
parent36fa25fa6d23e64698e81a276366f38c5dc7a046 (diff)
Show "breaking changes" message at startup (#3377)
- **PR Description** Remember which version of lazygit the user was last running, and show a list of breaking changes since that version (if any) if the user upgraded to a newer version. It's a little unobvious how to test this manually, because we don't show the popup for developer builds. You'll have to build with something like `go build -ldflags="-X 'main.version=0.41.0'"` in order to test it. This is an extremely stripped down version of #3261.
-rw-r--r--pkg/config/app_config.go2
-rw-r--r--pkg/gui/gui.go68
-rw-r--r--pkg/gui/keybindings.go10
-rw-r--r--pkg/gui/layout.go5
-rw-r--r--pkg/gui/types/version_number.go41
-rw-r--r--pkg/gui/types/version_number_test.go81
-rw-r--r--pkg/i18n/english.go29
7 files changed, 236 insertions, 0 deletions
diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go
index 67852dcd1..7f5757447 100644
--- a/pkg/config/app_config.go
+++ b/pkg/config/app_config.go
@@ -343,6 +343,7 @@ type AppState struct {
LastUpdateCheck int64
RecentRepos []string
StartupPopupVersion int
+ LastVersion string // this is the last version the user was using, for the purpose of showing release notes
// these are for custom commands typed in directly, not for custom commands in the lazygit config
CustomCommandsHistory []string
@@ -367,6 +368,7 @@ func getDefaultAppState() *AppState {
LastUpdateCheck: 0,
RecentRepos: []string{},
StartupPopupVersion: 0,
+ LastVersion: "",
DiffContextSize: 3,
LocalBranchSortOrder: "recency",
RemoteBranchSortOrder: "alphabetical",
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 4544d5f0b..ba759505a 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
+ "sort"
"strings"
"sync"
@@ -40,6 +41,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/updates"
"github.com/jesseduffield/lazygit/pkg/utils"
+ "github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
"gopkg.in/ozeidan/fuzzy-patricia.v3/patricia"
)
@@ -869,6 +871,72 @@ func (gui *Gui) showIntroPopupMessage() {
})
}
+func (gui *Gui) showBreakingChangesMessage() {
+ _, err := types.ParseVersionNumber(gui.Config.GetVersion())
+ if err != nil {
+ // We don't have a parseable version, so we'll assume it's a developer
+ // build, or a build from HEAD with a version such as 0.40.0-g1234567;
+ // in these cases we don't show release notes.
+ return
+ }
+
+ last := &types.VersionNumber{}
+ lastVersionStr := gui.c.GetAppState().LastVersion
+ // If there's no saved last version, we show all release notes. This is for
+ // people upgrading from a version before we started to save lastVersion.
+ // First time new users won't see the release notes because we show them the
+ // intro popup instead.
+ if lastVersionStr != "" {
+ last, err = types.ParseVersionNumber(lastVersionStr)
+ if err != nil {
+ // The last version was a developer build, so don't show release
+ // notes in this case either.
+ return
+ }
+ }
+
+ // Now collect all release notes texts for versions newer than lastVersion.
+ // We don't need to bother checking the current version here, because we
+ // can't possibly have texts for versions newer than current.
+ type versionAndText struct {
+ version *types.VersionNumber
+ text string
+ }
+ texts := []versionAndText{}
+ for versionStr, text := range gui.Tr.BreakingChangesByVersion {
+ v, err := types.ParseVersionNumber(versionStr)
+ if err != nil {
+ // Ignore bogus entries in the BreakingChanges map
+ continue
+ }
+ if last.IsOlderThan(v) {
+ texts = append(texts, versionAndText{version: v, text: text})
+ }
+ }
+
+ if len(texts) > 0 {
+ sort.Slice(texts, func(i, j int) bool {
+ return texts[i].version.IsOlderThan(texts[j].version)
+ })
+ message := strings.Join(lo.Map(texts, func(t versionAndText, _ int) string { return t.text }), "\n")
+
+ gui.waitForIntro.Add(1)
+ gui.c.OnUIThread(func() error {
+ onConfirm := func() error {
+ gui.waitForIntro.Done()
+ return nil
+ }
+
+ return gui.c.Confirm(types.ConfirmOpts{
+ Title: gui.Tr.BreakingChangesTitle,
+ Prompt: gui.Tr.BreakingChangesMessage + "\n\n" + message,
+ HandleConfirm: onConfirm,
+ HandleClose: onConfirm,
+ })
+ })
+ }
+}
+
// setColorScheme sets the color scheme for the app based on the user config
func (gui *Gui) setColorScheme() error {
userConfig := gui.UserConfig
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index 319576cd6..6275d5189 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -248,6 +248,16 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
Handler: self.scrollDownConfirmationPanel,
},
{
+ ViewName: "confirmation",
+ Key: gocui.MouseWheelUp,
+ Handler: self.scrollUpConfirmationPanel,
+ },
+ {
+ ViewName: "confirmation",
+ Key: gocui.MouseWheelDown,
+ Handler: self.scrollDownConfirmationPanel,
+ },
+ {
ViewName: "submodules",
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
Handler: self.handleCopySelectedSideContextItemToClipboard,
diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go
index 823dfe994..01c397628 100644
--- a/pkg/gui/layout.go
+++ b/pkg/gui/layout.go
@@ -254,9 +254,14 @@ func (gui *Gui) onInitialViewsCreation() error {
storedPopupVersion := gui.c.GetAppState().StartupPopupVersion
if storedPopupVersion < StartupPopupVersion {
gui.showIntroPopupMessage()
+ } else {
+ gui.showBreakingChangesMessage()
}
}
+ gui.c.GetAppState().LastVersion = gui.Config.GetVersion()
+ gui.c.SaveAppStateAndLogError()
+
if gui.showRecentRepos {
if err := gui.helpers.Repos.CreateRecentReposMenu(); err != nil {
return err
diff --git a/pkg/gui/types/version_number.go b/pkg/gui/types/version_number.go
new file mode 100644
index 000000000..ae51a2722
--- /dev/null
+++ b/pkg/gui/types/version_number.go
@@ -0,0 +1,41 @@
+package types
+
+import (
+ "errors"
+ "regexp"
+ "strconv"
+)
+
+type VersionNumber struct {
+ Major, Minor, Patch int
+}
+
+func (v *VersionNumber) IsOlderThan(otherVersion *VersionNumber) bool {
+ this := v.Major*1000*1000 + v.Minor*1000 + v.Patch
+ other := otherVersion.Major*1000*1000 + otherVersion.Minor*1000 + otherVersion.Patch
+ return this < other
+}
+
+func ParseVersionNumber(versionStr string) (*VersionNumber, error) {
+ re := regexp.MustCompile(`^v?(\d+)\.(\d+)(?:\.(\d+))?$`)
+ matches := re.FindStringSubmatch(versionStr)
+ if matches == nil {
+ return nil, errors.New("unexpected version format: " + versionStr)
+ }
+
+ v := &VersionNumber{}
+ var err error
+
+ if v.Major, err = strconv.Atoi(matches[1]); err != nil {
+ return nil, err
+ }
+ if v.Minor, err = strconv.Atoi(matches[2]); err != nil {
+ return nil, err
+ }
+ if len(matches[3]) > 0 {
+ if v.Patch, err = strconv.Atoi(matches[3]); err != nil {
+ return nil, err
+ }
+ }
+ return v, nil
+}
diff --git a/pkg/gui/types/version_number_test.go b/pkg/gui/types/version_number_test.go
new file mode 100644
index 000000000..407a01139
--- /dev/null
+++ b/pkg/gui/types/version_number_test.go
@@ -0,0 +1,81 @@
+package types
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestParseVersionNumber(t *testing.T) {
+ tests := []struct {
+ versionStr string
+ expected *VersionNumber
+ err error
+ }{
+ {
+ versionStr: "1.2.3",
+ expected: &VersionNumber{
+ Major: 1,
+ Minor: 2,
+ Patch: 3,
+ },
+ err: nil,
+ },
+ {
+ versionStr: "v1.2.3",
+ expected: &VersionNumber{
+ Major: 1,
+ Minor: 2,
+ Patch: 3,
+ },
+ err: nil,
+ },
+ {
+ versionStr: "12.34.56",
+ expected: &VersionNumber{
+ Major: 12,
+ Minor: 34,
+ Patch: 56,
+ },
+ err: nil,
+ },
+ {
+ versionStr: "1.2",
+ expected: &VersionNumber{
+ Major: 1,
+ Minor: 2,
+ Patch: 0,
+ },
+ err: nil,
+ },
+ {
+ versionStr: "1",
+ expected: nil,
+ err: errors.New("unexpected version format: 1"),
+ },
+ {
+ versionStr: "invalid",
+ expected: nil,
+ err: errors.New("unexpected version format: invalid"),
+ },
+ {
+ versionStr: "junk_before 1.2.3",
+ expected: nil,
+ err: errors.New("unexpected version format: junk_before 1.2.3"),
+ },
+ {
+ versionStr: "1.2.3 junk_after",
+ expected: nil,
+ err: errors.New("unexpected version format: 1.2.3 junk_after"),
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.versionStr, func(t *testing.T) {
+ actual, err := ParseVersionNumber(test.versionStr)
+ assert.Equal(t, test.expected, actual)
+ assert.Equal(t, test.err, err)
+ })
+ }
+}
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index 818783e93..1ec3af2c1 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -773,6 +773,9 @@ type TranslationSet struct {
Actions Actions
Bisect Bisect
Log Log
+ BreakingChangesTitle string
+ BreakingChangesMessage string
+ BreakingChangesByVersion map[string]string
}
type Bisect struct {
@@ -1866,5 +1869,31 @@ func EnglishTranslationSet() TranslationSet {
AppendingLineToFile: "Appending '{{.line}}' to file '{{.filename}}'",
EditRebaseFromBaseCommit: "Beginning interactive rebase from '{{.baseCommit}}' onto '{{.targetBranchName}}",
},
+ BreakingChangesTitle: "Breaking Changes",
+ BreakingChangesMessage: `You are updating to a new version of lazygit which contains breaking changes. Please review the notes below and update your configuration if necessary.
+For more information, see the full release notes at <https://github.com/jesseduffield/lazygit/releases>.`,
+ BreakingChangesByVersion: map[string]string{
+ "0.41.0": `- When you press 'g' to bring up the git reset menu, the 'mixed' option is now the first and default, rather than 'soft'. This is because 'mixed' is the most commonly used option.
+- The commit message panel now automatically hard-wraps by default (i.e. it adds newline characters when you reach the margin). You can adjust the config like so:
+
+git:
+ commit:
+ autoWrapCommitMessage: true
+ autoWrapWidth: 72
+
+- The 'v' key was already being used in the staging view to start a range select, but now you can use it to start a range select in any view. Unfortunately this clashes with the 'v' keybinding for pasting commits (cherry-pick), so now pasting commits is done via 'shift+V' and for the sake of consistency, copying commits is now done via 'shift+C' instead of just 'c'. Note that the 'v' keybinding is only one way to start a range-select: you can use shift+up/down arrow instead. So, if you want to configure the cherry-pick keybindings to get the old behaviour, set the following in your config:
+
+keybinding:
+ universal:
+ toggleRangeSelect: <something other than v>
+ commits:
+ cherryPickCopy: 'c'
+ pasteCommits: 'v'
+
+- Squashing fixups using 'shift-S' now brings up a menu, with the default option being to squash all fixup commits in the branch. The original behaviour of only squashing fixup commits above the selected commit is still available as the second option in that menu.
+- Push/pull/fetch loading statuses are now shown against the branch rather than in a popup. This allows you to e.g. fetch multiple branches in parallel and see the status for each branch.
+- The git log graph in the commits view is now always shown by default (previously it was only shown when the view was maximised). If you find this too noisy, you can change it back via ctrl+L -> 'Show git graph' -> 'when maximised'
+ `,
+ },
}
}