Skip to content

Reduce memory consumption of graph (pipe sets) #4498

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Apr 29, 2025
Merged

Conversation

stefanhaller
Copy link
Collaborator

  • PR Description

As a followup to #2533, reduce the memory consumption some more by optimizing the storage of the pipe sets used for the commit graph.

Some coarse measurements (taken by opening the respective repo, typing 4 >, and waiting for all commits to be loaded, and then reading the Memory column in Activity Monitor):

master this PR
git 2.5 GB 1.0 GB
our work repo 2.3 GB 1.3 GB
linux kernel 94.8 GB 38.0 GB

It's still not really usable for the linux kernel, but for all other repos that I come across in my daily use of lazygit, it works quite well now.

@stefanhaller stefanhaller added the enhancement New feature or request label Apr 20, 2025
Copy link

codacy-production bot commented Apr 20, 2025

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
Report missing for aa688851 93.42%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (aa68885) Report Missing Report Missing Report Missing
Head commit (38ca388) 55874 48469 86.75%

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#4498) 228 213 93.42%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

Footnotes

  1. Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

Copy link
Owner

@jesseduffield jesseduffield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some substantial memory saving which is great. I'll need to test this out a bit because I'm worried about nil pointer exceptions.

It's a shame this change requires so many sweeping changes but I can't think of a way to not require sweeping changes of some form. I did some investigating into alternative approaches e.g.:

  • using [20]byte instead of string (much better than string, but doesn't address duplicates)
  • Having an immutable commit struct which itself lives in a pool. This would represent the actual commit and nothing independent of it like tags. Then each commit in the state can have a pointer to the corresponding immutable commit. You could have the immutable commit have its parents as just pointers to other immutable commits.
  • Storing a reference to the pool inside the commit and using .Hash() to encapsulate how to resolve the actual string. Unfortunately means bloating each commit with an 8 byte pointer.

Did you have any thoughts on the above?

@stefanhaller
Copy link
Collaborator Author

It's a shame this change requires so many sweeping changes but I can't think of a way to not require sweeping changes of some form.

I did consider making the Hash property of Commit private (lowercase) and exposing a getter. When doing this in an extra commit, the actual change of turning it into a pointer would require much fewer changes. I didn't do that just because currently all fields are public, but I'm totally open to changing this either just for this field, or for all.

  • Having an immutable commit struct which itself lives in a pool.

I don't see how this addresses the problem of the duplicate hash strings; unless you change the Pipes to have pointers to commits instead of pointers to hashes, is that what you meant? But then we need a map of commits by hash, so that we can look up the commit if we only have the hash. I'm not sure I like this direction.

  • Storing a reference to the pool inside the commit and using .Hash() to encapsulate how to resolve the actual string. Unfortunately means bloating each commit with an 8 byte pointer.

I'm not sure what you mean by "resolve the actual string", and why you need a reference to the pool for that. The .Hash() getter would just return the pointer; that's basically what I said above.

@jesseduffield
Copy link
Owner

I did consider making the Hash property of Commit private (lowercase) and exposing a getter. When doing this in an extra commit, the actual change of turning it into a pointer would require much fewer changes. I didn't do that just because currently all fields are public, but I'm totally open to changing this either just for this field, or for all.

I'm not sure what you mean by "resolve the actual string", and why you need a reference to the pool for that. The .Hash() getter would just return the pointer; that's basically what I said above.

I meant that the .Hash() method could just return the string directly so that you didn't need to de-reference it afterwards. But that would require holding a reference to the pool. On reflection I don't actually think it's a good idea.

I don't see how this addresses the problem of the duplicate hash strings; unless you change the Pipes to have pointers to commits instead of pointers to hashes, is that what you meant? But then we need a map of commits by hash, so that we can look up the commit if we only have the hash. I'm not sure I like this direction.

Yep that's what I meant: a map of hash to commit. I like the idea because it would allow us to do some more stuff in memory without having to shell out to git but it's a big piece of work and I'm not clear on the overall value.

@stefanhaller
Copy link
Collaborator Author

I'm not sure what you mean by "resolve the actual string", and why you need a reference to the pool for that. The .Hash() getter would just return the pointer; that's basically what I said above.

I meant that the .Hash() method could just return the string directly so that you didn't need to de-reference it afterwards. But that would require holding a reference to the pool. On reflection I don't actually think it's a good idea.

Still don't get it. We'd have a hash *string field and a Hash() string method that dereferences the field and returns it. Why do we need the pool for that?

@stefanhaller
Copy link
Collaborator Author

When I said above "The .Hash() getter would just return the pointer", I meant the dereferenced pointer of course.

@jesseduffield
Copy link
Owner

Still don't get it. We'd have a hash *string field and a Hash() string method that dereferences the field and returns it. Why do we need the pool for that?

Ah yes, I mistakenly thought you'd need to access the pool to deference. Well in that case I actually very much like the idea of having a Hash() method.

Copy link

codacy-production bot commented Apr 27, 2025

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
Report missing for d87bdaf1 94.53%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (d87bdaf) Report Missing Report Missing Report Missing
Head commit (6ca627d) 55965 48566 86.78%

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#4498) 274 259 94.53%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

Footnotes

  1. Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

@stefanhaller
Copy link
Collaborator Author

@jesseduffield I reworked the branch completely, please have another look. I'm quite happy with this now.

Copy link
Owner

@jesseduffield jesseduffield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Massive improvement using the Hash() method. Nice one. Just one question

@@ -329,7 +322,7 @@ func renderPipeSet(
// we don't want to highlight two commits if they're contiguous. We only want
// to highlight multiple things if there's an actual visible pipe involved.
highlight := true
if prevCommit != nil && equalHashes(prevCommit.Hash, selectedCommitHash) {
if prevCommit != nil && equalHashes(prevCommit.HashPtr(), selectedCommitHash) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit confusing to read that we're comparing a hash pointer with a hash, when in fact selectedCommitHash is not a hash: it's a hash pointer. Although that's clear enough when you're looking at the argument's type in the function signature, it's not clear when you read it in isolation e.g. in this line here. I think we should have a rule that if a variable contains a hash pointer, it should be end in HashPtr instead of just Hash.

Playing devils advocate against myself: often we pass around pointers to structs without needing to specify that it's a pointer in the variable name, so why the double standard when it comes to hashes? I think the answer is that scalar values (e.g. strings, integers, etc) are just expected not to be pointers and if they actually are pointers the default assumption is that they're only pointers for the sake of being possibly null.

Having said that, does it actually make a measurable difference to compare the pointers vs comparing the values themselves? Cos it would make the code simpler if we could fully encapsulate the pointer stuff inside the Hash() method and not have to sometimes pass hash pointers through functions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very interesting discussion. At my workplace we have the convention to name all pointer variables with a "p" prefix, i.e. pSelectedCommitHash, this would solve it. Some of my coworkers object to that though, and find it redundant. I kind of like it myself, but I'm not proposing we adopt the convention in lazygit. 😄

But I'm happy to rename selectedCommitHash to selectedCommitHashPtr (see b438e07).

Having said that, does it actually make a measurable difference to compare the pointers vs comparing the values themselves? Cos it would make the code simpler if we could fully encapsulate the pointer stuff inside the Hash() method and not have to sometimes pass hash pointers through functions.

I would agree if equalHashes were a public function, but it's a private implementation detail, so I don't think it matters as much. And it is sometimes called with pipe.toHash as an argument, which is a pointer, so we would have to dereference it then; I don't feel this would be an improvement.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I'm happy to rename selectedCommitHash to selectedCommitHashPtr (see b438e07).

Looks good to me. I much prefer having it as a 'Ptr' suffix to a 'p' prefix.

I would agree if equalHashes were a public function, but it's a private implementation detail, so I don't think it matters as much. And it is sometimes called with pipe.toHash as an argument, which is a pointer, so we would have to dereference it then; I don't feel this would be an improvement.

Although that's true, RenderAux and RenderCommitGraph are public and they require a pointer for the sake of passing to equalHashes meaning we do get some blast radius from this performance (as we see in your commit). It's not so bad, but I hope there is a good performance gain we're getting in return.

Copy link
Owner

@jesseduffield jesseduffield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

These are not the expected commits.
The "// merge commit" comment was plain wrong, this is any commit that has a
parent, merge or not. The "else if" condition was unnecessary, a plain "else"
would have been enough. But the code in the two blocks was almost identical, so
extract the one thing that was different and unify it.

And while we're at it, use IsFirstCommit() instead of counting parents.
We want to unexport Parents in a later commit.
This is in preparation for turning the hash into pointer to a string.
This in itself is not an improvement, because hashes are unique (they are shared
between real commits and rebase todos, but there are so few of those that it
doesn't matter). However, it becomes an improvement once we also store parent
hashes in the same pool; but the real motivation for this change is to also
reuse the hash pointers in Pipe objects later in the branch. This will be a big
win because in a merge-heavy git repo there are many more Pipe instances than
commits.
This is exactly the same as what we did for Hash earlier. And for the same
reason: we want to turn the parents field into a slice of pointers.
We need to pass %P instead of %p in the format string of the git log command, so
that the parent hashes have the full length and can be shared with the real
hashes.
Change the base type of some of our enums from int to uint8, and reorder fields
for better packing. This reduces the size of models.Commit from 152 to 132 bytes
on my machine.

This doesn't improve overall memory usage significantly, but why not save a
little bit of memory if it's easy.
Now that commit hashes are stored in a pool and referenced by pointer by the
commits, we can use those same pointers in the pipes.
Now that all hashes that we deal with are stored in the same pool, we can simply
compare their addresses.
This saves some memory at the cost of a slight performance increase (I suppose
reallocting the slice when adding new Pipes is slightly more expensive now).

Performance of the BenchmarkRenderCommitGraph benchmark is 130μs before, 175μs
after. I'm guessing this is still acceptable.
The instances are held by the AuthorStyle cache.
…king

Hopefully, graphs will never get wider than 32768 characters. (They would get
kind of hard to navigate if they did...)

This reduces the size of the Pipe struct from 48 to 32 bytes, which makes a
significant difference when there are many millions of instances.
@stefanhaller stefanhaller enabled auto-merge April 29, 2025 12:58
@stefanhaller stefanhaller merged commit 66caa25 into master Apr 29, 2025
14 checks passed
@stefanhaller stefanhaller deleted the reduce-pipe-memory branch April 29, 2025 12:59
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request May 10, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [jesseduffield/lazygit](https://github.com/jesseduffield/lazygit) | minor | `v0.48.0` -> `v0.50.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>jesseduffield/lazygit (jesseduffield/lazygit)</summary>

### [`v0.50.0`](https://github.com/jesseduffield/lazygit/releases/tag/v0.50.0)

[Compare Source](jesseduffield/lazygit@v0.49.0...v0.50.0)

<!-- Release notes generated using configuration in .github/release.yml at v0.50.0 -->

#### What's Changed

##### Enhancements 🔥

-   Continue/abort a conflicted cherry-pick or revert by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4441
-   Show todo items for pending cherry-picks and reverts by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4442
-   Use "git cherry-pick" for implementing copy/paste of commits by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4443
-   Allow reverting a range of commits by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4444
-   Section headers for rebase todos and actual commits by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4463
-   Focus the main view by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4429
-   Auto-forward main branches after fetching by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4493
-   Add new command "Move commits to new branch" by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#3876
-   Strip the '+' and '-' characters when copying parts of a diff to the clipboard by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4519
-   Reduce memory consumption of graph (pipe sets) by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4498

##### Fixes 🔧

-   Fix truncation of branches when scrolling branches panel to the left by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4483
-   Fix nvim-remote commands for fish shell by [@&#8203;SavingFrame](https://github.com/SavingFrame) in jesseduffield/lazygit#4506
-   Disallow creating custom patches when the diff context size is 0 by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4522
-   Remove double space between rebase todo and author columns by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4520

##### Maintenance ⚙️

-   Allow closing issues via github actions by [@&#8203;jesseduffield](https://github.com/jesseduffield) in jesseduffield/lazygit#4515

##### Docs 📖

-   Add Debian installation instructions alongside Ubuntu by [@&#8203;jmkim](https://github.com/jmkim) in jesseduffield/lazygit#4501
-   fix wording of random tip by [@&#8203;dawedawe](https://github.com/dawedawe) in jesseduffield/lazygit#4488

#### New Contributors

-   [@&#8203;jmkim](https://github.com/jmkim) made their first contribution in jesseduffield/lazygit#4501
-   [@&#8203;SavingFrame](https://github.com/SavingFrame) made their first contribution in jesseduffield/lazygit#4506
-   [@&#8203;dawedawe](https://github.com/dawedawe) made their first contribution in jesseduffield/lazygit#4488

**Full Changelog**: jesseduffield/lazygit@v0.49.0...v0.50.0

### [`v0.49.0`](https://github.com/jesseduffield/lazygit/releases/tag/v0.49.0)

[Compare Source](jesseduffield/lazygit@v0.48.0...v0.49.0)

<!-- Release notes generated using configuration in .github/release.yml at v0.49.0 -->

#### What's Changed

##### Enhancements 🔥

-   Support fish when running shell command by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4350
-   Add acme editor preset by [@&#8203;rakoo](https://github.com/rakoo) in jesseduffield/lazygit#4360
-   Support home and end as alternatives to '<' and '>' by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4396
-   Drop the git config cache when getting focus by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4376
-   Add a "Content of selected file" entry to the copy menu for commit files by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4341
-   Add root node in file tree by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4346
-   \[FEAT] Add Recursive Bulk Initialize and Update for Submodules by [@&#8203;cesarandr](https://github.com/cesarandr) in jesseduffield/lazygit#4259
-   Commit without pre-commit hooks action is independent on prefix by [@&#8203;kschweiger](https://github.com/kschweiger) in jesseduffield/lazygit#4374
-   Let users define custom icons and color for files on the config file by [@&#8203;hasecilu](https://github.com/hasecilu) in jesseduffield/lazygit#4395
-   Add "Absolute path" item to the file view's copy menu by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4410
-   Allow range drop stashes by [@&#8203;gaogao-qwq](https://github.com/gaogao-qwq) in jesseduffield/lazygit#4451
-   More navigation keybindings for confirmation panel by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4404
-   Resolve non-inline merge conflicts by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4431
-   Add `runCommand` function to Go template syntax + add support for templates in git `branchPrefix` setting by [@&#8203;ruudk](https://github.com/ruudk) in jesseduffield/lazygit#4438
-   Show "(hooks disabled)" in title bar of commit message editor by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4467
-   Add a command to select all commits of the current branch by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4448

##### Fixes 🔧

-   Use a waiting status for rewording a non-head commit by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4343
-   Fix layout of options view for non-english languages by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4359
-   Fix changing language while lazygit is running by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4361
-   URL encode gitlab brackets to make consistent with branch names by [@&#8203;ChrisMcD1](https://github.com/ChrisMcD1) in jesseduffield/lazygit#4336
-   Fix commitPrefix setting with empty pattern by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4381
-   Commit only tracked files in tracked filter view by [@&#8203;parthokunda](https://github.com/parthokunda) in jesseduffield/lazygit#4386
-   Revert [#&#8203;4313](jesseduffield/lazygit#4313) (Skip post-checkout hook when discarding changes) by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4407
-   Enhance support for GPG signed tags by [@&#8203;ChrisMcD1](https://github.com/ChrisMcD1) in jesseduffield/lazygit#4394
-   Fix checking out a file from a range selection of commits by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4423
-   Fix discarding submodule changes in nested folders by [@&#8203;brandondong](https://github.com/brandondong) in jesseduffield/lazygit#4317
-   Better support for shell aliases by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4385
-   Fix hyperlinks in last line of confirmation popups by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4454
-   Fix wrong main view content after pressing `e` in a stack of branches by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4450
-   fix: update vscode color to logo color by [@&#8203;PeterCardenas](https://github.com/PeterCardenas) in jesseduffield/lazygit#4459
-   Fix crash with drag selecting and staging by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4480
-   Escape special characters in filenames when git-ignoring files by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4475

##### Maintenance ⚙️

-   Fix linter warnings by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4352
-   Fix release schedule again by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4364
-   Update to go 1.24 by [@&#8203;radsaq](https://github.com/radsaq) in jesseduffield/lazygit#4377
-   Add an integration test for a config with a negative refspec by [@&#8203;radsaq](https://github.com/radsaq) in jesseduffield/lazygit#4382
-   Specify a go release minor version by [@&#8203;radsaq](https://github.com/radsaq) in jesseduffield/lazygit#4393
-   Fix flaky integration test by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4432
-   Some code cleanup by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4449
-   Bump the minimum required git version to 2.22 by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4439
-   Bump go-git, and get rid of github.com/imdario/mergo by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4460
-   Skip date check when release worfklow is manually invoked by [@&#8203;jesseduffield](https://github.com/jesseduffield) in jesseduffield/lazygit#4484

##### Docs 📖

-   Include empty arrays and maps in config docs by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4413
-   Filter out deprecated user config fields from generated Config.md by [@&#8203;karimkhaleel](https://github.com/karimkhaleel) in jesseduffield/lazygit#4416
-   Corrected interactive rebase keybind example in README.md by [@&#8203;NewtonChutney](https://github.com/NewtonChutney) in jesseduffield/lazygit#4426
-   Update kidpix link in README to active url by [@&#8203;ChrisMcD1](https://github.com/ChrisMcD1) in jesseduffield/lazygit#4425

##### I18n 🌎

-   Update translation files from Crowdin by [@&#8203;stefanhaller](https://github.com/stefanhaller) in jesseduffield/lazygit#4473

#### New Contributors

-   [@&#8203;rakoo](https://github.com/rakoo) made their first contribution in jesseduffield/lazygit#4360
-   [@&#8203;radsaq](https://github.com/radsaq) made their first contribution in jesseduffield/lazygit#4377
-   [@&#8203;cesarandr](https://github.com/cesarandr) made their first contribution in jesseduffield/lazygit#4259
-   [@&#8203;kschweiger](https://github.com/kschweiger) made their first contribution in jesseduffield/lazygit#4374
-   [@&#8203;NewtonChutney](https://github.com/NewtonChutney) made their first contribution in jesseduffield/lazygit#4426
-   [@&#8203;gaogao-qwq](https://github.com/gaogao-qwq) made their first contribution in jesseduffield/lazygit#4451
-   [@&#8203;ruudk](https://github.com/ruudk) made their first contribution in jesseduffield/lazygit#4438

**Full Changelog**: jesseduffield/lazygit@v0.48.0...v0.49.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC4xMS4yIiwidXBkYXRlZEluVmVyIjoiNDAuMTEuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90Il19-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants