lazygit icon indicating copy to clipboard operation
lazygit copied to clipboard

Handle repos that use 'main' as the main branch

Open PGScully opened this issue 5 years ago • 8 comments

Is your feature request related to a problem? Please describe.

GitHub and git are now using 'main' instead of 'master' for the default branch name. lazygit only considers 'master', and colors 'main' as a branch instead of the default branch.

Describe the solution you'd like

git can now be configured to use any name as the default branch with the following config snippet:

[init]
        defaultBranch = main

lazygit should ~~use the defaultBranch option for~~ programatically determine the branch to color as master, and fall back to 'master' or 'main' if it's not present.

Describe alternatives you've considered

Additional context

https://github.com/github/renaming

git 2.28 introduced the defaultBranch config option: https://github.blog/2020-07-27-highlights-from-git-2-28/#introducing-init-defaultbranch

PGScully avatar Oct 30 '20 05:10 PGScully

Alas, the defaultBranch config option only applies to when we're creating a new repo, not when we're dealing with an existing one. We'll need to wait and see if git makes this easier for us

jesseduffield avatar Apr 06 '21 23:04 jesseduffield

I think the "99% solution" is:

  • if master exists and main does not, use master
  • if main exists, and master does not, use main

What to do if both or neither of them exist is the challenging UX corner case.

PGScully avatar Apr 07 '21 01:04 PGScully

@PGScully I agree. In the case where both exist I would use master. I feel like in repos where main has been opted for, it's less likely that we'll see a master branch compared to vice versa.

jesseduffield avatar Jun 05 '21 03:06 jesseduffield

Interestingly, Oh My Zsh's git plugin git_main_branch() function tests for main, then trunk, and if neither is found uses master.

https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/git/git.plugin.zsh#L32

PGScully avatar Jun 23 '21 05:06 PGScully

Resolved by #2020 .

mark2185 avatar Aug 07 '22 20:08 mark2185

@mark2185 This issue and #2020 are unrelated.

Ryooooooga avatar Aug 07 '22 23:08 Ryooooooga

Yep, this issue is about colouring commits based on whether they're included in the main branch, whether that's master or main. This is a harder problem to solve (efficiently) without adding a per-repo config option, given that git has no way to tell us what the main branch is.

jesseduffield avatar Aug 07 '22 23:08 jesseduffield

Ah, my bad, I only checked the coloring of the branch in the branches panel. Sorry for the noise!

mark2185 avatar Aug 08 '22 05:08 mark2185

For a solution that might work for most people, how about a config option, so that one can define an order of "prefer-default-if-exists" such as [main, trunk, master] or [master, main, trunk].

This way we can get the functionality into lazygit without having a perfect solution, and if some day git has native support for a default branch this code can just be removed and simplified to that.

kittydoor avatar Nov 02 '22 14:11 kittydoor

I see a similar and simpler suggestion in a closed earlier PR regarding handling of this: https://github.com/jesseduffield/lazygit/pull/1064#issuecomment-754497943

I don't think this is ideal, since on my machine at least there are repos with a bunch of different default branches both work and non-work, and ideally I would be able to have a prefer order or even a way to "override" on individual repos.

kittydoor avatar Nov 02 '22 14:11 kittydoor

As a final option, we can also consider using "remote origin HEAD branch" ie. refs/remotes/origin/HEAD, which you can see in the remote overview via git remote show origin. This "should" accurately provide the default branch, perhaps, though I would appreciate others to chime in on the accuracy of this statement.

kittydoor avatar Nov 02 '22 14:11 kittydoor

@kittydoor I like the idea of defining a default ordering, with the ability to specify the main branch on a per-repo basis.

So we could we have:

mainBranchPrecedence: ['main', 'master', 'trunk']
mainBranch:
  lazygit: 'master'
  otherrepo: 'main'

However, something which hasn't yet been discussed, is that currently any branch prefixed with 'feature/' will be compared to the develop branch. That is hardcoded along with 'master' right now. Given it's a pain to make backwards-compatible changes to the config, I'd like to think about how we might support this feature more generally e.g.:

mainBranchPrecedence: ['main', 'master', 'trunk']
repos:
  lazygit: # repo name (absolute path can also be provided)
    baseBranch:
      default: 'master'
      prefix:
        'feature/': 'develop'
        'bugfix/': 'develop'

The mainBranchPrecedence could be implemented separate to the repos key.

What do you think @kittydoor ?

PS: I've got prefix as its own key so that we can also use something like exact for when we want to say branch X has branch Y as its base (which is something ideally you'd be able to configure from within lazygit)

jesseduffield avatar Nov 03 '22 04:11 jesseduffield

@jesseduffield thank you for taking the time to look into this!

I hadn't even thought about the fact that in many workflows, there is a multi-stage process before ending up in main/master/trunk/production/stable etc. Finding a way to support that would be helpful for those situations for sure! A way to define prefixes and exact matches could be helpful indeed. Perhaps globbing is also an option, e.g. 'JIRA-*/': 'develop' (or regex, whichever is easier to implement / sounds more intuitive to you).

I think the repos option sounds like a good way to provide advanced functionality, at the cost of being a bit cumbersome but tolerably so.

Could we consider a way to write this into the repository itself? For instance .lazygit? Could of course also be in order of "evaluate main config, override with local repo config if found". Or is adding more things for people to .gitignore or include in the project root something that might not be received so well? If we do this, do you see other things that might get populated there in the future, or will it really be "just" for this?

Also, could you elaborate on why you aren't considering refs/remotes/origin/HEAD for mainBranch/defaultBranch detection? Perhaps you think it's too brittle, I would love to hear more about that.

kittydoor avatar Nov 03 '22 10:11 kittydoor

@jesseduffield We can make it a lot simpler than your suggestion above. We don't need a per-repo or even per-branch config for what the base branch is. Fortunately, git merge-base supports multiple refs to compare the current one against, so you can call git merge-base HEAD master main develop 1.0-hotfixes, and it will pick the closest one. I tried this in a hack locally, it works very well.

So all we need is a baseBranches: [main, develop, v3.x] config. When unset, it would default to [master, main]. There's no precedence, because we don't need it.

One question before I productionize this: ideally this should be a per-repo config, but since lazygit doesn't have that, we have to make it a global config that lists the base branches of all the repos you are using. This is fine, as the git merge-base call doesn't seem to get any more expensive with more refs. However, we need to filter out the branches that don't exist in the current repo, otherwise the merge-base call would fail with an error, and it's unclear how to best do that:

  1. Making git calls to check the branches is probably too expensive; it might be ok on Mac or Linux (git rev-parse --verify refs/heads/master takes 3.5ms on my machine), but on Windows we should avoid it because forking a process is so expensive.
  2. Checking for the existence of {dotGitDir}/refs/heads/<branchname> works, but is not future-proof. It already fails now after you call git pack-refs --all (until the branches change again), and it might fail more often in the future if git decides to use the pack-refs format more automatically somehow.
  3. I considered querying the branches model (gui.State.Model.Branches), and this seems to work ok, except on first startup, because branches and commits seem to be loaded concurrently.
  4. One option could be to query the existing branches once after reading the config and cache them somewhere, hoping that none of the base branches get deleted while lazygit is running.

Any opinions?

stefanhaller avatar May 10 '23 06:05 stefanhaller

I made a draft PR if anybody wants to play with it: #2619. I use option 2. above in the first commit, but added a second commit that uses a combination of options 1. and 4. (simply caching the results locally in CommitLoader), which strikes me as the best compromise.

stefanhaller avatar May 10 '23 08:05 stefanhaller

This is now implemented on master (thanks to @stefanhaller ), so I'll close this off. Let me know if anybody still has issues

jesseduffield avatar May 17 '23 06:05 jesseduffield