Conversation
mislav
left a comment
There was a problem hiding this comment.
Love the cmd stubber extraction into a new internal package, but I think we need to tweak our approach still to detecting the branch name in a no-commits scenario.
git/git.go
Outdated
|
|
||
| // CurrentBranch reads the checked-out branch for the git repository | ||
| func CurrentBranch() (string, error) { | ||
| err := run.PrepareCmd(GitCommand("log")).Run() |
There was a problem hiding this comment.
Consider that git log command will, with no extra arguments, list all the commits reachable from current HEAD. In large repositories, this is an expensive operation.
We could potentially limit the output by adding -1, but we could also explore options to use a lower-level approach than git log.
git/git.go
Outdated
| err := run.PrepareCmd(GitCommand("log")).Run() | ||
| if err != nil { | ||
| // this is a hack. | ||
| errRe := regexp.MustCompile("your current branch '([^']+)' does not have any commits yet") |
There was a problem hiding this comment.
I would suggest that we avoid ever matching text in error messages. I have two main reasons:
- Forward-compatibility: future git versions might decide to tweak an error message;
- Localization-sensitivity: not everybody has git in English.
$ LANG=es git rev-list -1 HEAD
fatal: argumento ambiguo 'HEAD': revisi'on desconocida o ruta fuera del 'arbol de trabajo.
Use '--' para separar las rutas de las revisiones, de esta manera:
'git <comando> [<revisi'on>...] -- [<archivo>...]'
If we wanted to detect whether the current branch has any commits, I think we could do git rev-list -1 HEAD, silence stderr, and see if there is a line on stdout.
So this leaves a problem on detecting the branch name when we're on a branch that has no commits yet. We have several options:
git branch --show-current: works, but not present in the oldest git version we support (1.9);- Scanning the branch name from
git logerror message: brittle; - Parsing the branch name from
.git/HEADfile manually: possible, but not straightforward to pull off since.gitcan be a regular file in case of git worktrees; - Avoiding reading the current branch name and just failing with
git: not on any branch, basically equating the no-commits scenario with detached HEAD scenario; git symbolic-ref --short HEAD: might do the job, but we'd have to check how far back it is supported, otherwise this is in the same basket as (1).
I'm leaning to (3) as the most "correct" approach, (5) as something to look into, and (4) as a lazy approach.
There was a problem hiding this comment.
All very fair criticisms; I knew it was a bad solution but figured you'd have a good suggestion on a different approach ^_^() I just don't have enough lower level git knowledge yet.
git symbolic-ref exists as far back as git 1.9 and I like using it more than the other options. I was going to suggest we switch to that by default instead of the existing rev-parse invocation but it errors on detached HEAD :(
I'll clean up CurrentBranch to use both, I guess.
| Calls []*exec.Cmd | ||
| } | ||
|
|
||
| func UseTempGitRepo() *TempGitRepo { |
mislav
left a comment
There was a problem hiding this comment.
This is looking great! One last thought about simplifying git invocations
git/git.go
Outdated
| // we avoid using `git branch --show-current` for compatibility with git < 2.22 | ||
| branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") | ||
| output, err := run.PrepareCmd(branchCmd).Output() | ||
| output, err := run.PrepareCmd(GitCommand("rev-parse", "--abbrev-ref", "HEAD")).Output() |
There was a problem hiding this comment.
This approach looks like it might cover all the cases, but I wonder if we could get away with using just a single invocation without fallbacks? It looks like git symbolic-ref --quiet --short HEAD could be just what we need:
- exit code == 0 + lines on stdout: the branch name was determined successfully
- exit code != 0 + no stderr: detached HEAD (no current branch)
- exit code != 0 + lines on stderr: some other error with which we should abort the process
There was a problem hiding this comment.
great thought, implemented.
git/git.go
Outdated
| output, err = run.PrepareCmd(GitCommand("symbolic-ref", "--short", "HEAD")).Output() | ||
| if err != nil { | ||
| return "", noBranchError | ||
| ce := err.(*run.CmdError) |
There was a problem hiding this comment.
This is going to panic if err is not of type *run.CmdError.
As a rule of thumb, we should never assume that an error is guaranteed to be of any specific type, but always check the type:
var cmdErr *run.CmdError
if errors.As(err, &cmdErr) {
cmdErr.Stderr.Len()
}There was a problem hiding this comment.
hm; I opted for the panic because it seemed like err was always supposed to be a *run.CmdError and panicking would signal to a developer that they were changing that contract. If we silently ignore the failed cast, doesn't that mean we can let a bug go unacknowledged?
There was a problem hiding this comment.
ah, re-reading more closely i see it's possible to not get a CmdError out of a prepared command. I'll fix the type assertion.
test/helpers.go
Outdated
| type OutputStub struct { | ||
| Out []byte | ||
| Out []byte | ||
| Error *run.CmdError |
There was a problem hiding this comment.
Should this be of type error so we can return any error type that we want, instead of just *run.CmdError?
There was a problem hiding this comment.
like i said above I misread the code as always using CmdError and didn't see the need for that flexibility; i'll update it.
mislav
left a comment
There was a problem hiding this comment.
Who would have thought that reading the current branch name could be so tricky 😣 I hope that in the future we can revise our minimum git support and, after all popular OSs ship with a git version newer than 1.9, we can migrate to git branch --show-current.
Thanks for being patient and working with me through all the iterations! 🌟
closes #682
This adds a kind of gross hack to enable branch detection on a repo with no commits. You are "on" abranch; but without commits
rev-parsefails.branchis also not helpful. i noticed thatlogtells you the branch you are on (that is lacking commits), so I extracted the branch name from the
error.
This adds a fallback to
symbolic-refwhen doing branch detection so that it works on an empty repository.symbolic-refis defined as far back as git 1.9.testing changes
I took this opportunity to touch
utils.RunnableandCmdStubbersome more:Runnableinterface) into its own package,internal/runStubErrortoCmdStubber. This is primitive in that it's working with a rawerrorinstead of
CmdErrbut I figure it was enough for now.